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

4.104.114.84.95
Last change on this file since 44dd8a61 was 44dd8a61, checked in by Joel Sherrill <joel.sherrill@…>, on 05/14/99 at 16:23:42

Added 3COM 3C509 driver from Rosimildo DaSilva? <rdasilva@…>.

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