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

4.104.114.84.95
Last change on this file since da8ae79b was da8ae79b, checked in by Joel Sherrill <joel.sherrill@…>, on 12/13/99 at 21:21:31

Warning removal patch from Philip A. Prindeville <philipp@…>.

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