source: rtems/c/src/lib/libbsp/i386/pc386/3c509/3c509.c @ 0e5db091

4.104.114.84.95
Last change on this file since 0e5db091 was 0e5db091, checked in by Joel Sherrill <joel.sherrill@…>, on 07/25/04 at 14:21:43

2004-07-25 Joel Sherrill <joel@…>

  • 3c509/3c509.c: Add <sys/errno.h>.
  • startup/linkcmds: Add .jcr section.
  • Property mode set to 100644
File size: 44.0 KB
Line 
1/**********************************************************************************
2 * $Header$
3 *
4 * Ported by Rosimildo da Silva.
5 * ConnectTel,Inc.
6 * e-mail: rdasilva@connecttel.com
7 *
8 * MODULE DESCRIPTION:
9 * RTEMS driver for 3COM 3C509 Ethernet Card.
10 * The driver has been tested on PC with a single network card.
11 *
12 *
13 * This driver was based on the FreeBSD implementation( if_ep.c ) of the 3c5x9
14 * family and on the network framework of the RTEMS network driver.
15 * ( WD80x3 by  Eric Norum ).
16 * See notes below:
17 *
18 ******************************************************************************
19 * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
20 * All rights reserved.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the above copyright
26 *    notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 *    notice, this list of conditions and the following disclaimer in the
29 *    documentation and/or other materials provided with the distribution.
30 * 3. All advertising materials mentioning features or use of this software
31 *    must display the following acknowledgement:
32 *      This product includes software developed by Herb Peyerl.
33 * 4. The name of Herb Peyerl may not be used to endorse or promote products
34 *    derived from this software without specific prior written permission.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
37 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
39 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
40 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
45 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 *
47 *******************************************************************************
48 *
49 * RTEMS driver for M68360 WD1 Ethernet
50 *
51 * W. Eric Norum
52 * Saskatchewan Accelerator Laboratory
53 * University of Saskatchewan
54 * Saskatoon, Saskatchewan, CANADA
55 * eric@skatter.usask.ca
56 *******************************************************************************
57 *
58 *
59 * MODIFICATION/HISTORY:
60 * $Log$
61 * Revision 1.4  2004/04/21 16:01:33  ralf
62 * Remove duplicate white lines.
63 *
64 * Revision 1.3  2004/04/21 10:42:43  ralf
65 * Remove stray white spaces.
66 *
67 * Revision 1.2  1999/12/13 21:21:31  joel
68 * Warning removal patch from Philip A. Prindeville <philipp@zembu.com>.
69 *
70 * Revision 1.1  1999/05/14 16:23:42  joel
71 * Added 3COM 3C509 driver from Rosimildo DaSilva <rdasilva@connecttel.com>.
72 *
73 *
74 **********************************************************************************/
75
76#include <bsp.h>
77
78#include <stdio.h>
79#include <stdarg.h>
80#include <sys/errno.h>
81#include <rtems/error.h>
82#include <rtems/rtems_bsdnet.h>
83
84#include <sys/param.h>
85#include <sys/mbuf.h>
86#include <sys/socket.h>
87#include <sys/sockio.h>
88#include <sys/libkern.h>
89
90#include <net/if.h>
91#include <netinet/in.h>
92#include <netinet/if_ether.h>
93
94#include <irq.h>
95
96/* Local includes */
97#include "3c509.h"
98#include "elink.h"
99
100/* #define      ET_MINLEN 60 */         /* minimum message length */
101
102/*
103 * Number of WDs supported by this driver
104 */
105#define NWDDRIVER       1
106
107/*
108 * Default number of buffer descriptors set aside for this driver.
109 * The number of transmit buffer descriptors has to be quite large
110 * since a single frame often uses four or more buffer descriptors.
111 */
112/*
113#define RX_BUF_COUNT     15
114#define TX_BUF_COUNT     4
115#define TX_BD_PER_BUF    4
116*/
117
118/*
119 * RTEMS event used by interrupt handler to signal driver tasks.
120 * This must not be any of the events used by the network task synchronization.
121 */
122#define INTERRUPT_EVENT                 RTEMS_EVENT_1
123
124/*
125 * RTEMS event used to start transmit daemon.
126 * This must not be the same as INTERRUPT_EVENT.
127 */
128#define START_TRANSMIT_EVENT    RTEMS_EVENT_2
129
130/*
131 * Receive buffer size -- Allow for a full ethernet packet including CRC
132 */
133
134/*
135#define RBUF_SIZE       1520
136
137#if (MCLBYTES < RBUF_SIZE)
138# error "Driver must have MCLBYTES > RBUF_SIZE"
139#endif
140*/
141
142/* network driver name */
143#define NET_DRIVER_NAME         "ep"
144
145/*
146 * Per device structure.
147 *
148 * XXX Note:  id_conflicts should either become an array of things we're
149 * specifically allowed to conflict with or be subsumed into some
150 * more powerful mechanism for detecting and dealing with multiple types
151 * of non-fatal conflict.  -jkh XXX
152 */
153struct isa_device
154{
155  int   id_id;          /* device id */
156  int   id_unit;        /* unit number */
157  int   id_iobase;      /* base i/o address */
158  u_int id_irq;         /* interrupt request */
159};
160
161struct ep_board
162{
163  int epb_addr;         /* address of this board */
164  char epb_used;                /* was this entry already used for configuring ? */
165                                        /* data from EEPROM for later use */
166  u_short eth_addr[3];  /* Ethernet address */
167  u_short prod_id;              /* product ID */
168  u_short res_cfg;              /* resource configuration */
169};
170
171/*
172 * Ethernet software status per interface.
173 */
174struct ep_softc
175{
176    struct arpcom arpcom;       /* Ethernet common part          */
177    int ep_io_addr;                     /* i/o bus address                       */
178    struct mbuf *top, *mcur;
179    short cur_len;
180    u_short ep_connectors;      /* Connectors on this card.      */
181    u_char ep_connector;        /* Configured connector.         */
182    int stat;                           /* some flags                            */
183    struct ep_board *epb;
184    int unit;
185
186    rtems_irq_connect_data   irqInfo;
187    rtems_id                     rxDaemonTid;
188    rtems_id                     txDaemonTid;
189
190    int                  acceptBroadcast;
191
192    short tx_underrun;
193    short rx_no_first;
194    short rx_no_mbuf;
195    short rx_bpf_disc;
196    short rx_overrunf;
197    short rx_overrunl;
198};
199
200/*  static unsigned long loopc; */
201static volatile unsigned long overrun;
202static volatile unsigned long resend;
203static struct ep_softc ep_softc[ NWDDRIVER ];
204static struct isa_device isa_dev[ NWDDRIVER ] =
205{
206  { 0,                  /* device id                            */
207    0,                  /* unit number                          */
208    -1,         /* base i/o address   ???       */
209    0                   /* interrupt request  ???       */
210  }
211};
212
213static  u_long  ep_unit;
214static  int         ep_boards;
215struct  ep_board ep_board[ EP_MAX_BOARDS + 1];
216static  int ep_current_tag = EP_LAST_TAG + 1;
217static  char *ep_conn_type[] = {"UTP", "AUI", "???", "BNC"};
218
219#define ep_ftst(f) (sc->stat&(f))
220#define ep_fset(f) (sc->stat|=(f))
221#define ep_frst(f) (sc->stat&=~(f))
222
223/* forward declarations for functions */
224static int ep_attach( struct ep_softc *sc );
225static int ep_isa_probe( struct isa_device *is );
226static void epinit(  struct ep_softc *sc );
227static void epread( register struct ep_softc *sc );
228static void epstart( struct ifnet *ifp );
229static void epread( register struct ep_softc *sc );
230static int ep_isa_attach( struct isa_device *is );
231static int get_eeprom_data( int id_port, int offset );
232static void ep_intr( struct ep_softc *sc );
233
234/* external functions */
235extern void Wait_X_ms( unsigned int timeToWait );  /* timer.c ??? */
236
237/**********************************************************************************
238 *
239 * DESCRIPTION: Writes a buffer of data to the I/O port. The data is sent to the
240 *                              port as 32 bits units( 4 bytes ).
241 *
242 * RETURNS: nothing.
243 *
244 **********************************************************************************/
245static __inline void outsl( unsigned short io_addr, unsigned char *out_data, int len )
246{
247   u_long *pl = ( u_long *)out_data;
248   while( len-- )
249   {
250     outport_long( io_addr, *pl );
251         pl++;
252   }
253}
254
255/**********************************************************************************
256 *
257 * DESCRIPTION: Writes a buffer of data to the I/O port. The data is sent to the
258 *                              port as 16 bits units( 2 bytes ).
259 *
260 * RETURNS:
261 *
262 **********************************************************************************/
263static __inline void outsw( unsigned short io_addr, unsigned char *out_data, int len )
264{
265   u_short *ps = ( u_short *)out_data;
266   while( len-- )
267   {
268     outport_word( io_addr, *ps );
269         ps++;
270   }
271}
272
273/**********************************************************************************
274 *
275 * DESCRIPTION: Writes a buffer of data to the I/O port. The data is sent to the
276 *                              port as 8 bits units( 1 byte ).
277 *
278 * RETURNS: nothing
279 *
280 **********************************************************************************/
281static __inline void outsb( unsigned short io_addr, unsigned char *out_data, int len )
282{
283   while( len-- )
284   {
285     outport_byte( io_addr, *out_data );
286         out_data++;
287   }
288}
289
290/**********************************************************************************
291 *
292 * DESCRIPTION: Read a buffer of data from an I/O port. The data is read as 16 bits
293 *                          units or 2 bytes.
294 *
295 * RETURNS: nothing.
296 *
297 **********************************************************************************/
298static __inline void insw( unsigned short io_addr, unsigned char *in_data, int len )
299{
300   u_short *ps = ( u_short *)in_data;
301   while( len-- )
302   {
303     inport_word( io_addr, *ps );
304         ps++;
305   }
306}
307
308/**********************************************************************************
309 *
310 * DESCRIPTION: Read a buffer of data from an I/O port. The data is read as 32 bits
311 *                          units or 4 bytes.
312 *
313 * RETURNS: nothing.
314 *
315 **********************************************************************************/
316static __inline void insl( unsigned short io_addr, unsigned char *in_data, int len )
317{
318   u_long *pl = ( u_long *)in_data;
319   while( len-- )
320   {
321     inport_long( io_addr, *pl );
322         pl++;
323   }
324}
325
326/**********************************************************************************
327 *
328 * DESCRIPTION: Read a buffer of data from an I/O port. The data is read as 8 bits
329 *                          units or 1 bytes.
330 *
331 * RETURNS: nothing.
332 *
333 **********************************************************************************/
334static __inline void insb( unsigned short io_addr, unsigned char *in_data, int len )
335{
336   while( len-- )
337   {
338     inport_byte( io_addr, *in_data++ );
339   }
340}
341
342/**********************************************************************************
343 *
344 * DESCRIPTION: Writes a word to the I/O port.
345 *
346 * RETURNS: nothing.
347 *
348 **********************************************************************************/
349/*
350 * Routine to output a word as defined in FreeBSD.
351 */
352static __inline void outw( unsigned short io_addr, unsigned short out_data )
353{
354  outport_word( io_addr, out_data );
355}
356
357/**********************************************************************************
358 *
359 * DESCRIPTION:  Routine to read a word as defined in FreeBSD.
360 *
361 * RETURNS: nothing
362 *
363 **********************************************************************************/
364static __inline unsigned short inw( unsigned short io_addr )
365{
366  unsigned short in_data;
367  inport_word( io_addr, in_data );
368  return in_data;
369}
370
371/**********************************************************************************
372 *
373 * DESCRIPTION: Routine to output a word as defined in FreeBSD.
374 *
375 * RETURNS: nothing.
376 *
377 **********************************************************************************/
378void __inline outb( unsigned short io_addr, unsigned char out_data )
379{
380  outport_byte( io_addr, out_data );
381}
382
383/**********************************************************************************
384 *
385 * DESCRIPTION: Routine to read a word as defined in FreeBSD.
386 *
387 * RETURNS: byte read.
388 *
389 **********************************************************************************/
390static __inline unsigned char inb( unsigned short io_addr )
391{
392  unsigned char in_data;
393  inport_byte( io_addr, in_data );
394  return in_data;
395}
396
397/**********************************************************************************
398 *
399 * DESCRIPTION:
400 * We get eeprom data from the id_port given an offset into the eeprom.
401 * Basically; after the ID_sequence is sent to all of the cards; they enter
402 * the ID_CMD state where they will accept command requests. 0x80-0xbf loads
403 * the eeprom data.  We then read the port 16 times and with every read; the
404 * cards check for contention (ie: if one card writes a 0 bit and another
405 * writes a 1 bit then the host sees a 0. At the end of the cycle; each card
406 * compares the data on the bus; if there is a difference then that card goes
407 * into ID_WAIT state again). In the meantime; one bit of data is returned in
408 * the AX register which is conveniently returned to us by inb().  Hence; we
409 * read 16 times getting one bit of data with each read.
410 *
411 * RETURNS: 16 bit word from the EEPROM
412 *
413 **********************************************************************************/
414static int get_eeprom_data( int id_port, int offset )
415{
416    int i, data = 0;
417    outb(id_port, 0x80 + offset);
418    Wait_X_ms( 1 );
419    for (i = 0; i < 16; i++)
420            data = (data << 1) | (inw(id_port) & 1);
421    return( data );
422}
423
424/**********************************************************************************
425 *
426 * DESCRIPTION: Waits until the EEPROM of the card is ready to be accessed.
427 *
428 * RETURNS: 0 - not ready; 1 - ok
429 *
430 **********************************************************************************/
431static int eeprom_rdy( struct ep_softc *sc )
432{
433    int i;
434
435    for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
436            continue;
437    if (i >= MAX_EEPROMBUSY)
438    {
439           printf("ep%d: eeprom failed to come ready.\n", sc->unit);
440           return (0);
441    }
442    return (1);
443}
444
445/**********************************************************************************
446 *
447 * DESCRIPTION:
448 * get_e: gets a 16 bits word from the EEPROM.
449 * We must have set the window before call this routine.
450 *
451 * RETURNS: data from EEPROM
452 *
453 **********************************************************************************/
454u_short get_e(  struct ep_softc *sc, int offset )
455{
456    if( !eeprom_rdy(sc) )
457           return (0xffff);
458    outw(BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset );
459    if( !eeprom_rdy(sc) )
460           return( 0xffff );
461    return( inw( BASE + EP_W0_EEPROM_DATA ) );
462}
463
464/**********************************************************************************
465 *
466 * DESCRIPTION:
467 * Driver interrupt handler. This routine is called by the RTEMS kernel when this
468 * interrupt is raised.
469 *
470 * RETURNS: nothing.
471 *
472 **********************************************************************************/
473static rtems_isr ap_interrupt_handler( rtems_vector_number v )
474{
475  struct ep_softc *sc = (struct ep_softc *)&ep_softc[ 0 ];
476
477  /* de-activate any pending interrrupt, and sent and event to interrupt task
478   * to process all events required by this interrupt.
479   */
480  outw( BASE + EP_COMMAND, SET_INTR_MASK );     /* disable all Ints */
481  rtems_event_send( sc->rxDaemonTid, INTERRUPT_EVENT );
482}
483
484/**********************************************************************************
485 *
486 * DESCRIPTION:
487 *
488 * RETURNS:
489 *
490 **********************************************************************************/
491static void nopOn(const rtems_irq_connect_data* notUsed)
492{
493  /* does nothing */
494}
495
496/**********************************************************************************
497 *
498 * DESCRIPTION:
499 *
500 * RETURNS:
501 *
502 **********************************************************************************/
503static int _3c509_IsOn(const rtems_irq_connect_data* irq)
504{
505  return BSP_irq_enabled_at_i8259s (irq->name);
506}
507
508/**********************************************************************************
509 *
510 * DESCRIPTION:
511 * Initializes the ethernet hardware.
512 *
513 * RETURNS: nothing.
514 *
515 **********************************************************************************/
516static void _3c509_initialize_hardware (struct ep_softc *sc)
517{
518  rtems_status_code st;
519
520  epinit( sc );
521
522  /*
523   * Set up interrupts
524   */
525  sc->irqInfo.hdl = ( rtems_irq_hdl )ap_interrupt_handler;
526  sc->irqInfo.on  = nopOn;
527  sc->irqInfo.off = nopOn;
528  sc->irqInfo.isOn = _3c509_IsOn;
529
530  printf ("3c509: IRQ with Kernel: %d\n", sc->irqInfo.name );
531  st = BSP_install_rtems_irq_handler( &sc->irqInfo );
532  if( !st )
533  {
534    rtems_panic ("Can't attach WD interrupt handler for irq %d\n", sc->irqInfo.name );
535  }
536}
537
538/**********************************************************************************
539 *
540 * DESCRIPTION: Driver interrupt daemon.
541 *
542 * RETURNS: nothing.
543 *
544 **********************************************************************************/
545static void _3c509_rxDaemon (void *arg)
546{
547  struct ep_softc *dp = (struct ep_softc *)&ep_softc[ 0 ];
548  rtems_event_set events;
549
550  printf ("3C509: RX Daemon is starting.\n");
551  for( ;; )
552  {
553    /* printk( "R-" ); */
554    rtems_bsdnet_event_receive( INTERRUPT_EVENT,
555                                                 RTEMS_WAIT | RTEMS_EVENT_ANY,
556                                                 RTEMS_NO_TIMEOUT,
557                                                 &events );
558    /* printk( "R+" ); */
559    ep_intr( dp );
560    epstart( &dp->arpcom.ac_if );
561  }
562  printf ("3C509: RX Daemon is finishing.\n");
563}
564
565/**********************************************************************************
566 *
567 * DESCRIPTION: Driver transmit daemon
568 *
569 * RETURNS:
570 *
571 **********************************************************************************/
572static void _3c509_txDaemon (void *arg)
573{
574    struct ep_softc *sc = (struct ep_softc *)&ep_softc[0];
575        struct ifnet *ifp = &sc->arpcom.ac_if;
576        rtems_event_set events;
577
578    printf ("3C509: TX Daemon is starting.\n");
579    for( ;; )
580        {
581                /*
582                 * Wait for packet
583                 */
584          /* printk( "T-\n" ); */
585                rtems_bsdnet_event_receive( START_TRANSMIT_EVENT,
586                                                                        RTEMS_EVENT_ANY | RTEMS_WAIT,
587                                                                        RTEMS_NO_TIMEOUT,
588                                                                        &events );
589                /*      printk( "T+\n" ); */
590      epstart( ifp );
591      while( ifp->if_flags & IFF_OACTIVE )
592          epstart( ifp );
593        }
594   printf ("3C509: TX Daemon is finishing.\n");
595}
596
597/**********************************************************************************
598 *
599 * DESCRIPTION: Activates the trabsmitter task...
600 *
601 * RETURNS: nothing.
602 *
603 **********************************************************************************/
604static void _3c509_start (struct ifnet *ifp)
605{
606        struct ep_softc *sc = ifp->if_softc;
607        /*    printk ("S");  */
608        ifp->if_flags |= IFF_OACTIVE;
609        rtems_event_send( sc->txDaemonTid, START_TRANSMIT_EVENT );
610}
611
612/**********************************************************************************
613 *
614 * DESCRIPTION: Initialize and start the device
615 *
616 * RETURNS:
617 *
618 **********************************************************************************/
619static void _3c509_init (void *arg)
620{
621  struct ep_softc *sc = arg;
622  struct ifnet *ifp = &sc->arpcom.ac_if;
623
624  printf ("3C509: Initialization called.\n");
625  if (sc->txDaemonTid == 0) {
626
627    /*
628     * Set up WD hardware
629     */
630    _3c509_initialize_hardware (sc);
631    printf ("3C509: starting network driver tasks..\n");
632    /*
633     * Start driver tasks
634     */
635    sc->txDaemonTid = rtems_bsdnet_newproc ("APtx", 4096, _3c509_txDaemon, sc);
636    sc->rxDaemonTid = rtems_bsdnet_newproc ("APrx", 4096, _3c509_rxDaemon, sc);
637  }
638
639  /*
640   * Tell the world that we're running.
641   */
642   ifp->if_flags |= IFF_RUNNING;
643}
644
645/**********************************************************************************
646 *
647 * DESCRIPTION: Stop the device
648 *
649 * RETURNS:
650 *
651 **********************************************************************************/
652static void _3c509_stop (struct ep_softc *sc)
653{
654  struct ifnet *ifp = &sc->arpcom.ac_if;
655  ifp->if_flags &= ~IFF_RUNNING;
656
657  printf ("3C509: stop() called.\n");
658  /*
659   * Stop the transmitter
660   */
661  outw(BASE + EP_COMMAND, RX_DISABLE);
662  outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
663  while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
664  outw(BASE + EP_COMMAND, TX_DISABLE);
665  outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
666  outw(BASE + EP_COMMAND, RX_RESET);
667  outw(BASE + EP_COMMAND, TX_RESET);
668  while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
669  outw(BASE + EP_COMMAND, C_INTR_LATCH);
670  outw(BASE + EP_COMMAND, SET_RD_0_MASK);
671  outw(BASE + EP_COMMAND, SET_INTR_MASK);
672  outw(BASE + EP_COMMAND, SET_RX_FILTER);
673}
674
675/**********************************************************************************
676 *
677 * DESCRIPTION:  Show interface statistics
678 *
679 * RETURNS: nothing.
680 *
681 **********************************************************************************/
682static void _3c509_stats (struct ep_softc *sc)
683{
684  struct ifnet *ifp = &sc->arpcom.ac_if;
685  printf ("3C509: stats() called.\n");
686  printf("\tStat: %x\n", sc->stat);
687  printf("\tIpackets=%ld, Opackets=%ld\n", ifp->if_ipackets, ifp->if_opackets);
688  printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n",
689           sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf,
690           sc->rx_overrunl, sc->tx_underrun );
691}
692
693/**********************************************************************************
694 *
695 * DESCRIPTION: Driver ioctl handler
696 *
697 * RETURNS:
698 *
699 **********************************************************************************/
700static int _3c509_ioctl (struct ifnet *ifp, int command, caddr_t data)
701{
702        struct ep_softc *sc = ifp->if_softc;
703        int error = 0;
704
705    printf ("3C509: ioctl() called.\n");
706        switch (command) {
707        case SIOCGIFADDR:
708        case SIOCSIFADDR:
709                ether_ioctl (ifp, command, data);
710                break;
711
712        case SIOCSIFFLAGS:
713                switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
714                case IFF_RUNNING:
715                        _3c509_stop (sc);
716                        break;
717
718                case IFF_UP:
719                        _3c509_init (sc);
720                        break;
721
722                case IFF_UP | IFF_RUNNING:
723                        _3c509_stop (sc);
724                        _3c509_init (sc);
725                        break;
726
727                default:
728                        break;
729                }
730                break;
731
732        case SIO_RTEMS_SHOW_STATS:
733                _3c509_stats( sc );
734                break;
735
736        /*
737         * FIXME: All sorts of multicast commands need to be added here!
738         */
739        default:
740                error = EINVAL;
741                break;
742        }
743        return error;
744}
745
746/**********************************************************************************
747 *
748 * DESCRIPTION:
749 * Attaches this network driver to the system. This function is called by the network
750 * interface during the initialization of the system.
751 *
752 * RETURNS: - 1 - success; 0 - fail to initialize
753 *
754 **********************************************************************************/
755int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *config )
756{
757        struct ep_softc *sc;
758        struct ifnet *ifp;
759        int mtu;
760        int i;
761
762    printf ("3C509: attach() called.\n");
763
764        /*
765         * init some variables
766         */
767        overrun = 0;
768        resend = 0;
769    ep_unit = 0;
770    ep_boards = 0;
771
772        /*
773         * Find a free driver
774         */
775        for (i = 0 ; i < NWDDRIVER ; i++) {
776                sc = &ep_softc[i];
777                ifp = &sc->arpcom.ac_if;
778                if (ifp->if_softc == NULL)
779                        break;
780        }
781        if (i >= NWDDRIVER)
782        {
783                printf ("Too many 3C509 drivers.\n");
784                return 0;
785        }
786
787        /*
788         * Process options
789         */
790        if( config->hardware_address )
791        {
792          memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
793        }
794        else
795        {
796          /* set it to something ... */
797          memset (sc->arpcom.ac_enaddr, 0x08,ETHER_ADDR_LEN);
798        }
799        if (config->mtu)
800                mtu = config->mtu;
801        else
802                mtu = ETHERMTU;
803
804        if (config->irno)
805                sc->irqInfo.name = config->irno;
806        else
807                sc->irqInfo.name = 10;
808
809        if (config->port)
810                sc->ep_io_addr = config->port;
811        else
812                sc->ep_io_addr = 0x300;
813
814        sc->acceptBroadcast = !config->ignore_broadcast;
815
816    printf ("3C509: isa_probe() looking for a card...\n");
817    if( !ep_isa_probe( &isa_dev[ 0 ] ) )
818        {
819           printf ("3C509: isa_probe() fail to find a board.\n");
820           return 0;
821        }
822
823        /* A board has been found, so proceed with the installation of the driver */
824    ep_isa_attach( &isa_dev[ 0 ] );
825        /*
826         * Set up network interface values
827         */
828
829        ifp->if_softc = sc;
830        ifp->if_unit = i;
831        ifp->if_name = NET_DRIVER_NAME;
832        ifp->if_mtu = mtu;
833        ifp->if_init = _3c509_init;
834        ifp->if_ioctl = _3c509_ioctl;
835        ifp->if_start = _3c509_start;
836        ifp->if_output = ether_output;
837    ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
838        if( ifp->if_snd.ifq_maxlen == 0 )
839        {
840           ifp->if_snd.ifq_maxlen = ifqmaxlen;
841        }
842        /*
843         * Attach the interface
844         */
845        if_attach (ifp);
846        ether_ifattach (ifp);
847    printf ("3C509: attach() is complete.\n");
848        return 1;
849}
850
851/**********************************************************************************
852 *
853 * DESCRIPTION:
854 * This function looks for a 3COM card 3c5x9 in an isa bus. If a board is found, it
855 * returns a structure describing the caracteristics of the card. It returns zero when
856 * card can not be found.
857 *
858 * RETURNS:     0 - fail - could not find a card...
859 *                              <>  description of the card.
860 *
861 **********************************************************************************/
862static struct ep_board *ep_look_for_board_at( struct isa_device *is )
863{
864    int data, i, j, id_port = ELINK_ID_PORT;
865    int count = 0;
866
867    if(ep_current_tag == (EP_LAST_TAG + 1) )
868    {
869        /* Come here just one time */
870                ep_current_tag--;
871
872        /* Look for the ISA boards. Init and leave them actived */
873                outb(id_port, 0);
874                outb(id_port, 0);
875
876                elink_idseq(0xCF);
877                elink_reset();
878      Wait_X_ms( 10 );  /* RPS: assuming delay in miliseconds */
879                for (i = 0; i < EP_MAX_BOARDS; i++)
880        {
881                outb(id_port, 0);
882                    outb(id_port, 0);
883                    elink_idseq(0xCF);
884
885                data = get_eeprom_data(id_port, EEPROM_MFG_ID);
886                    if (data != MFG_ID)
887                                break;
888
889                    /* resolve contention using the Ethernet address */
890                    for (j = 0; j < 3; j++)
891                        get_eeprom_data(id_port, j);
892
893                    /* and save this address for later use */
894
895                    for (j = 0; j < 3; j++)
896                           ep_board[ep_boards].eth_addr[j] = get_eeprom_data(id_port, j);
897
898                    ep_board[ep_boards].res_cfg = get_eeprom_data(id_port, EEPROM_RESOURCE_CFG);
899                    ep_board[ep_boards].prod_id = get_eeprom_data(id_port, EEPROM_PROD_ID);
900                    ep_board[ep_boards].epb_used = 0;
901#ifdef PC98
902                    ep_board[ep_boards].epb_addr =
903                                (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x100 + 0x40d0;
904#else
905                    ep_board[ep_boards].epb_addr =
906                                (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200;
907                    if (ep_board[ep_boards].epb_addr > 0x3E0)
908                                /* Board in EISA configuration mode */
909                                continue;
910#endif /* PC98 */
911
912                    outb(id_port, ep_current_tag);      /* tags board */
913                    outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG);
914                    ep_boards++;
915                count++;
916                    ep_current_tag--;
917                }
918                ep_board[ep_boards].epb_addr = 0;
919                if( count )
920                {
921                printf("%d 3C5x9 board(s) on ISA found at", count);
922                    for (j = 0; ep_board[j].epb_addr; j++)
923                           if( ep_board[j].epb_addr <= 0x3E0 )
924                               printf(" 0x%x", ep_board[j].epb_addr );
925                    printf("\n");
926                }
927    }
928
929    /* we have two cases:
930     *
931     *  1. Device was configured with 'port ?'
932     *      In this case we search for the first unused card in list
933     *
934     *  2. Device was configured with 'port xxx'
935     *      In this case we search for the unused card with that address
936     *
937     */
938
939    if (IS_BASE == -1)
940    { /* port? */
941                for (i = 0; ep_board[i].epb_addr && ep_board[i].epb_used; i++) ;
942                if (ep_board[i].epb_addr == 0)
943                      return 0;
944
945                IS_BASE = ep_board[i].epb_addr;
946                ep_board[i].epb_used = 1;
947                return &ep_board[ i ];
948    }
949    else
950    {
951                for (i = 0; ep_board[i].epb_addr && ep_board[i].epb_addr != IS_BASE;  i++ ) ;
952                if (ep_board[i].epb_used || ep_board[i].epb_addr != IS_BASE)
953                            return 0;
954                if (inw(IS_BASE + EP_W0_EEPROM_COMMAND) & EEPROM_TST_MODE)
955                {
956                printf("ep%d: 3c5x9 at 0x%x in PnP mode. Disable PnP mode!\n",
957                                is->id_unit, IS_BASE );
958                }
959                ep_board[i].epb_used = 1;
960                return &ep_board[i];
961    }
962}
963
964/**********************************************************************************
965 *
966 * DESCRIPTION:
967 * This routine checks if there card installed on the machine.
968 *
969 * RETURNS: 0 - no card founded.
970 *                      16 - size of the IO range for the card.
971 *
972 **********************************************************************************/
973static int ep_isa_probe( struct isa_device *is )
974{
975    struct ep_softc *sc;
976    struct ep_board *epb;
977    u_short k;
978
979    /* try to find a 3COM 3c5x9 .... */
980    if( (epb = ep_look_for_board_at(is)) == 0 )
981                return (0);
982
983    sc = &ep_softc[ 0 ];
984    sc->ep_io_addr = epb->epb_addr;
985    sc->epb = epb;
986
987    /*
988     * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be
989     * 0x9[0-f]50       (IBM-PC)
990     * 0x9[0-f]5[0-f]   (PC-98)
991     */
992    GO_WINDOW(0);
993    k = sc->epb->prod_id;
994#ifdef PC98
995    if ((k & 0xf0f0) != (PROD_ID & 0xf0f0))
996    {
997#else
998    if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
999    {
1000#endif
1001            printf("ep_isa_probe: ignoring model %04x\n", k );
1002/*        ep_unit--;  */
1003        return (0);
1004    }
1005    k = sc->epb->res_cfg;
1006    k >>= 12;
1007
1008    /* Now we have two cases again:
1009     *
1010     *  1. Device was configured with 'irq?'
1011     *      In this case we use irq read from the board
1012     *
1013     *  2. Device was configured with 'irq xxx'
1014     *      In this case we set up the board to use specified interrupt
1015     *
1016     */
1017
1018    if (is->id_irq == 0)
1019    {    /* irq? */
1020       is->id_irq = ( k == 2 ) ? 9 : k;
1021    }
1022
1023    sc->stat = 0;       /* 16 bit access */
1024
1025    /* By now, the adapter is already activated */
1026
1027    return (EP_IOSIZE);  /* 16 bytes of I/O space used. */
1028}
1029
1030/**********************************************************************************
1031 *
1032 * DESCRIPTION:
1033 * This routine attaches this network driver and the network interface routines.
1034 *
1035 * RETURNS: 0 - failed to attach
1036 *                      1 - success
1037 *
1038 **********************************************************************************/
1039static int ep_isa_attach( struct isa_device *is )
1040{
1041    struct ep_softc *sc = &ep_softc[ 0 ];
1042    u_short config;
1043    int irq;
1044
1045    sc->ep_connectors = 0;
1046    config = inw( IS_BASE + EP_W0_CONFIG_CTRL );
1047    if (config & IS_AUI)
1048    {
1049           sc->ep_connectors |= AUI;
1050    }
1051    if (config & IS_BNC)
1052    {
1053           sc->ep_connectors |= BNC;
1054    }
1055    if (config & IS_UTP)
1056    {
1057           sc->ep_connectors |= UTP;
1058    }
1059    if( !(sc->ep_connectors & 7) )
1060       printf( "no connectors!" );
1061    sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS;
1062
1063    /*
1064     * Write IRQ value to board
1065     */
1066
1067    irq = is->id_irq;
1068        /* update the interrupt line number to registered with kernel */
1069        sc->irqInfo.name = irq;
1070
1071    GO_WINDOW( 0 );
1072    SET_IRQ( BASE, irq );
1073
1074    printf( "3C509: I/O=0x%x, IRQ=%d, CONNECTOR=%s, ",
1075            sc->ep_io_addr, sc->irqInfo.name,ep_conn_type[ sc->ep_connector ] );
1076
1077    ep_attach( sc );
1078    return 1;
1079}
1080
1081/**********************************************************************************
1082 *
1083 * DESCRIPTION: Completes the initialization/attachement of the driver.
1084 *
1085 * RETURNS: 0 - ok.
1086 *
1087 **********************************************************************************/
1088static int ep_attach( struct ep_softc *sc )
1089{
1090    u_short *p;
1091    int i;
1092
1093    /*
1094     * Setup the station address
1095     */
1096    p = (u_short *) &sc->arpcom.ac_enaddr;
1097    GO_WINDOW(2);
1098    printf("ADDRESS=" );
1099    for (i = 0; i < 3; i++)
1100    {
1101            p[i] = htons( sc->epb->eth_addr[i] );
1102        outw( BASE + EP_W2_ADDR_0 + (i * 2), ntohs( p[i] ) );
1103        printf("%04x ", (u_short)ntohs( p[i] ) );
1104    }
1105    printf("\n" );
1106
1107    sc->rx_no_first = sc->rx_no_mbuf =
1108        sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl =
1109        sc->tx_underrun = 0;
1110
1111    ep_fset( F_RX_FIRST );
1112    sc->top = sc->mcur = 0;
1113    return 0;
1114}
1115
1116/**********************************************************************************
1117 *
1118 * DESCRIPTION:
1119 * Initializes the card.
1120 * The order in here seems important. Otherwise we may not receive interrupts. ?!
1121 *
1122 * RETURNS: nothing.
1123 *
1124 **********************************************************************************/
1125static void epinit( struct ep_softc *sc )
1126{
1127    register struct ifnet *ifp = &sc->arpcom.ac_if;
1128    int i, j;
1129
1130    while( inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS ) ;
1131    GO_WINDOW(0);
1132    outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
1133    GO_WINDOW(4);
1134    outw(BASE + EP_W4_MEDIA_TYPE, DISABLE_UTP);
1135    GO_WINDOW(0);
1136
1137    /* Disable the card */
1138    outw(BASE + EP_W0_CONFIG_CTRL, 0);
1139
1140    /* Enable the card */
1141    outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
1142
1143    GO_WINDOW(2);
1144
1145    /* Reload the ether_addr. */
1146    for (i = 0; i < 6; i++)
1147       outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]);
1148
1149    outw(BASE + EP_COMMAND, RX_RESET);
1150    outw(BASE + EP_COMMAND, TX_RESET);
1151    while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
1152
1153    /* Window 1 is operating window */
1154    GO_WINDOW(1);
1155    for (i = 0; i < 31; i++)
1156       inb(BASE + EP_W1_TX_STATUS);
1157
1158    /* get rid of stray intr's */
1159    outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
1160
1161    outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS);
1162
1163    outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS);
1164
1165    if (ifp->if_flags & IFF_PROMISC)
1166           outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
1167                                                            FIL_GROUP | FIL_BRDCST | FIL_ALL);
1168    else
1169           outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_GROUP | FIL_BRDCST);
1170
1171     /*
1172      * S.B.
1173      *
1174      * Now behavior was slightly changed:
1175      *
1176      * if any of flags link[0-2] is used and its connector is
1177      * physically present the following connectors are used:
1178      *
1179      *   link0 - AUI * highest precedence
1180      *   link1 - BNC
1181      *   link2 - UTP * lowest precedence
1182      *
1183      * If none of them is specified then
1184      * connector specified in the EEPROM is used
1185      * (if present on card or AUI if not).
1186      *
1187      */
1188
1189    /* Set the xcvr. */
1190    if (ifp->if_flags & IFF_LINK0 && sc->ep_connectors & AUI)
1191    {
1192           i = ACF_CONNECTOR_AUI;
1193    }
1194    else if (ifp->if_flags & IFF_LINK1 && sc->ep_connectors & BNC)
1195    {
1196           i = ACF_CONNECTOR_BNC;
1197    }
1198    else if (ifp->if_flags & IFF_LINK2 && sc->ep_connectors & UTP)
1199    {
1200           i = ACF_CONNECTOR_UTP;
1201    }
1202    else
1203    {
1204           i = sc->ep_connector;
1205    }
1206    GO_WINDOW(0);
1207    j = inw(BASE + EP_W0_ADDRESS_CFG) & 0x3fff;
1208    outw(BASE + EP_W0_ADDRESS_CFG, j | (i << ACF_CONNECTOR_BITS));
1209
1210    switch(i)
1211    {
1212      case ACF_CONNECTOR_UTP:
1213          if (sc->ep_connectors & UTP)
1214          {
1215            GO_WINDOW(4);
1216            outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
1217          }
1218          break;
1219
1220      case ACF_CONNECTOR_BNC:
1221           if (sc->ep_connectors & BNC)
1222           {
1223            outw(BASE + EP_COMMAND, START_TRANSCEIVER);
1224            Wait_X_ms( 1 );
1225           }
1226       break;
1227
1228      case ACF_CONNECTOR_AUI:
1229            /* nothing to do */
1230                break;
1231
1232      default:
1233           printf("ep%d: strange connector type in EEPROM: assuming AUI\n", sc->unit);
1234           break;
1235    }
1236
1237    outw(BASE + EP_COMMAND, RX_ENABLE);
1238    outw(BASE + EP_COMMAND, TX_ENABLE);
1239
1240    ifp->if_flags |= IFF_RUNNING;
1241    ifp->if_flags &= ~IFF_OACTIVE;      /* just in case */
1242
1243    sc->rx_no_first = sc->rx_no_mbuf =
1244        sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl =
1245        sc->tx_underrun = 0;
1246
1247    ep_fset(F_RX_FIRST);
1248    if( sc->top )
1249    {
1250           m_freem( sc->top );
1251           sc->top = sc->mcur = 0;
1252    }
1253    outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
1254    outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16);
1255
1256    /*
1257     * Store up a bunch of mbuf's for use later. (MAX_MBS). First we free up
1258     * any that we had in case we're being called from intr or somewhere
1259     * else.
1260     */
1261
1262    GO_WINDOW(1);
1263}
1264
1265static const char padmap[] = {0, 3, 2, 1};
1266
1267/**********************************************************************************
1268 *
1269 * DESCRIPTION: Routine to transmit frames to the card.
1270 *
1271 * RETURNS: nothing.
1272 *
1273 **********************************************************************************/
1274static void epstart( struct ifnet *ifp )
1275{
1276    register struct ep_softc *sc = ifp->if_softc;
1277    register u_int len;
1278    register struct mbuf *m;
1279    struct mbuf *top;
1280    int pad;
1281
1282    while( inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS )
1283        ;
1284startagain:
1285    /*    printk( "S-" ); */
1286
1287    /* Sneak a peek at the next packet */
1288    m = ifp->if_snd.ifq_head;
1289    if (m == 0)
1290    {
1291       ifp->if_flags &= ~IFF_OACTIVE;
1292       return;
1293    }
1294
1295    for( len = 0, top = m; m; m = m->m_next )
1296         len += m->m_len;
1297
1298    pad = padmap[ len & 3 ];
1299
1300    /*
1301     * The 3c509 automatically pads short packets to minimum ethernet length,
1302     * but we drop packets that are too large. Perhaps we should truncate
1303     * them instead?
1304     */
1305    if( len + pad > ETHER_MAX_LEN )
1306    {
1307           /* packet is obviously too large: toss it */
1308       ++ifp->if_oerrors;
1309       IF_DEQUEUE( &ifp->if_snd, m );
1310       m_freem( m );
1311           goto readcheck;
1312    }
1313    if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4)
1314    {
1315           /* no room in FIFO */
1316           outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4));
1317       /* make sure */
1318           if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4)
1319           {
1320               ifp->if_flags |= IFF_OACTIVE;
1321               return;
1322           }
1323    }
1324    IF_DEQUEUE( &ifp->if_snd, m );
1325    outw(BASE + EP_W1_TX_PIO_WR_1, len);
1326    outw(BASE + EP_W1_TX_PIO_WR_1, 0x0);        /* Second dword meaningless */
1327
1328    for (top = m; m != 0; m = m->m_next)
1329        {
1330       if( ep_ftst(F_ACCESS_32_BITS ) )
1331       {
1332              outsl( BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 4 );
1333              if( m->m_len & 3 )
1334             outsb(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t) + (m->m_len & (~3)), m->m_len & 3 );
1335       }
1336       else
1337       {
1338              outsw( BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2 );
1339              if( m->m_len & 1 )
1340                  outb( BASE + EP_W1_TX_PIO_WR_1, *(mtod(m, caddr_t) + m->m_len - 1) );
1341           }
1342        }
1343    while( pad-- )
1344        {
1345           outb(BASE + EP_W1_TX_PIO_WR_1, 0);   /* Padding */
1346        }
1347    ifp->if_timer = 2;
1348    ifp->if_opackets++;
1349    m_freem(top);
1350
1351/*    goto startagain;   */
1352    /*
1353     * Is another packet coming in? We don't want to overflow the tiny RX
1354     * fifo.
1355     */
1356readcheck:
1357    if( inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK )
1358    {
1359        /*
1360         * we check if we have packets left, in that case we prepare to come
1361         * back later
1362         */
1363           if( ifp->if_snd.ifq_head )
1364           {
1365              outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8);
1366       }
1367           return;
1368    }
1369    goto startagain;
1370}
1371
1372/**********************************************************************************
1373 *
1374 * DESCRIPTION: Routine to read frames from the card.
1375 *
1376 * RETURNS: nothing.
1377 *
1378 **********************************************************************************/
1379static void epread( register struct ep_softc *sc )
1380{
1381    struct ether_header *eh;
1382    struct mbuf *top, *mcur, *m;
1383    struct ifnet *ifp;
1384    int lenthisone;
1385
1386    short rx_fifo2, status;
1387    register short rx_fifo;
1388
1389    ifp = &sc->arpcom.ac_if;
1390    status = inw( BASE + EP_W1_RX_STATUS );
1391
1392read_again:
1393
1394    if (status & ERR_RX)
1395    {
1396           ++ifp->if_ierrors;
1397           if( status & ERR_RX_OVERRUN )
1398           {
1399            /*
1400             * we can think the rx latency is actually greather than we
1401             * expect
1402             */
1403            if( ep_ftst(F_RX_FIRST) )
1404                   sc->rx_overrunf++;
1405            else
1406                   sc->rx_overrunl++;
1407
1408           }
1409           goto out;
1410    }
1411    rx_fifo = rx_fifo2 = status & RX_BYTES_MASK;
1412
1413    if( ep_ftst( F_RX_FIRST ) )
1414    {
1415           MGETHDR( m, M_DONTWAIT, MT_DATA );
1416       if( !m )
1417              goto out;
1418           if( rx_fifo >= MINCLSIZE )
1419              MCLGET( m, M_DONTWAIT );
1420           sc->top = sc->mcur = top = m;
1421#define EROUND  ((sizeof(struct ether_header) + 3) & ~3)
1422#define EOFF    (EROUND - sizeof(struct ether_header))
1423           top->m_data += EOFF;
1424
1425           /* Read what should be the header. */
1426           insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), sizeof(struct ether_header) / 2);
1427           top->m_len = sizeof(struct ether_header);
1428           rx_fifo -= sizeof(struct ether_header);
1429           sc->cur_len = rx_fifo2;
1430    }
1431    else
1432    {
1433           /* come here if we didn't have a complete packet last time */
1434           top = sc->top;
1435           m = sc->mcur;
1436           sc->cur_len += rx_fifo2;
1437    }
1438
1439    /* Reads what is left in the RX FIFO */
1440    while (rx_fifo > 0)
1441    {
1442           lenthisone = min( rx_fifo, M_TRAILINGSPACE(m) );
1443           if( lenthisone == 0 )
1444           {    /* no room in this one */
1445               mcur = m;
1446               MGET(m, M_WAIT, MT_DATA);
1447               if (!m)
1448                      goto out;
1449               if (rx_fifo >= MINCLSIZE)
1450                       MCLGET(m, M_WAIT);
1451               m->m_len = 0;
1452               mcur->m_next = m;
1453               lenthisone = min(rx_fifo, M_TRAILINGSPACE(m));
1454           }
1455           if( ep_ftst( F_ACCESS_32_BITS ) )
1456           { /* default for EISA configured cards*/
1457              insl( BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 4);
1458              m->m_len += (lenthisone & ~3);
1459              if (lenthisone & 3)
1460                     insb(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone & 3);
1461              m->m_len += (lenthisone & 3);
1462            }
1463            else
1464            {
1465              insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 2);
1466              m->m_len += lenthisone;
1467              if( lenthisone & 1 )
1468                     *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1);
1469           }
1470           rx_fifo -= lenthisone;
1471    }
1472
1473    if( status & ERR_RX_INCOMPLETE)
1474    {   /* we haven't received the complete packet */
1475            sc->mcur = m;
1476        sc->rx_no_first++;      /* to know how often we come here */
1477        ep_frst( F_RX_FIRST );
1478            if( !((status = inw(BASE + EP_W1_RX_STATUS)) & ERR_RX_INCOMPLETE) )
1479            {
1480              /* we see if by now, the packet has completly arrived */
1481              goto read_again;
1482            }
1483            outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_NEXT_EARLY_THRESH);
1484            return;
1485    }
1486    outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
1487    ++ifp->if_ipackets;
1488    ep_fset(F_RX_FIRST);
1489    top->m_pkthdr.rcvif = &sc->arpcom.ac_if;
1490    top->m_pkthdr.len = sc->cur_len;
1491
1492    eh = mtod(top, struct ether_header *);
1493    m_adj(top, sizeof(struct ether_header));
1494    ether_input(ifp, eh, top);
1495    sc->top = 0;
1496    while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
1497          ;
1498    outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
1499    return;
1500
1501out:
1502    outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
1503    if (sc->top)
1504    {
1505           m_freem(sc->top);
1506           sc->top = 0;
1507       sc->rx_no_mbuf++;
1508    }
1509    ep_fset(F_RX_FIRST);
1510    while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ;
1511    outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
1512}
1513
1514/**********************************************************************************
1515 *
1516 * DESCRIPTION:
1517 * This routine handles interrupts. It is called from the "RX" task whenever
1518 * the ISR post an event to the task.
1519 * This is basically the "isr" from the FreeBSD driver.
1520 *
1521 * RETURNS: nothing.
1522 *
1523 **********************************************************************************/
1524static void ep_intr( struct ep_softc *sc )
1525{
1526  register int status;
1527  struct ifnet *ifp;
1528  ifp = &sc->arpcom.ac_if;
1529
1530rescan:
1531
1532  /*  printk( "I-" ); */
1533  while( ( status = inw(BASE + EP_STATUS)) & S_5_INTS )
1534  {
1535        /* first acknowledge all interrupt sources */
1536    outw( BASE + EP_COMMAND, ACK_INTR | ( status & S_MASK ) );
1537
1538        if( status & ( S_RX_COMPLETE | S_RX_EARLY ) )
1539        {
1540            epread( sc );
1541            continue;
1542        }
1543        if (status & S_TX_AVAIL)
1544        {
1545            /* we need ACK */
1546            ifp->if_timer = 0;
1547            ifp->if_flags &= ~IFF_OACTIVE;
1548            GO_WINDOW(1);
1549            inw(BASE + EP_W1_FREE_TX);
1550            epstart(ifp);
1551        }
1552        if (status & S_CARD_FAILURE)
1553        {
1554            ifp->if_timer = 0;
1555            printf("\nep%d:\n\tStatus: %x\n", sc->unit, status);
1556            GO_WINDOW(4);
1557            printf("\tFIFO Diagnostic: %x\n", inw(BASE + EP_W4_FIFO_DIAG));
1558            printf("\tStat: %x\n", sc->stat);
1559            printf("\tIpackets=%ld, Opackets=%ld\n", ifp->if_ipackets, ifp->if_opackets);
1560            printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n",
1561                   sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf,
1562                   sc->rx_overrunl, sc->tx_underrun);
1563
1564            printf("ep%d: Status: %x (input buffer overflow)\n", sc->unit, status);
1565            ++ifp->if_ierrors;
1566            epinit(sc);
1567            return;
1568        }
1569        if (status & S_TX_COMPLETE)
1570        {
1571            ifp->if_timer = 0;
1572            /* we  need ACK. we do it at the end */
1573            /*
1574             * We need to read TX_STATUS until we get a 0 status in order to
1575             * turn off the interrupt flag.
1576             */
1577            while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE)
1578            {
1579                   if (status & TXS_SUCCES_INTR_REQ)
1580                        ;
1581                   else if( status & (TXS_UNDERRUN | TXS_JABBER | TXS_MAX_COLLISION ) )
1582                   {
1583                      outw(BASE + EP_COMMAND, TX_RESET);
1584                      if (status & TXS_UNDERRUN)
1585                      {
1586                             sc->tx_underrun++;
1587                      }
1588                      else
1589                      {
1590                              if( status & TXS_JABBER )
1591                                   ;
1592                          else  /* TXS_MAX_COLLISION - we shouldn't get here */
1593                                   ++ifp->if_collisions;
1594                      }
1595                      ++ifp->if_oerrors;
1596                      outw(BASE + EP_COMMAND, TX_ENABLE);
1597                     /*
1598                      * To have a tx_avail_int but giving the chance to the
1599                      * Reception
1600                      */
1601                      if( ifp->if_snd.ifq_head )
1602                      {
1603                             outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8);
1604                      }
1605                   }
1606                   outb( BASE + EP_W1_TX_STATUS, 0x0 ); /* pops up the next status */
1607            } /* while */
1608            ifp->if_flags &= ~IFF_OACTIVE;
1609            GO_WINDOW(1);
1610            inw(BASE + EP_W1_FREE_TX);
1611            epstart( ifp );
1612        }  /* end TX_COMPLETE */
1613  }
1614  outw(BASE + EP_COMMAND, C_INTR_LATCH);        /* ACK int Latch */
1615  if( (status = inw(BASE + EP_STATUS) ) & S_5_INTS )
1616      goto rescan;
1617
1618  /* re-enable Ints */
1619  outw( BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS );
1620  /* printk( "I+" ); */
1621}
Note: See TracBrowser for help on using the repository browser.