source: rtems/c/src/lib/libbsp/i386/pc386/wd8003/wd8003.c @ c0438add

4.104.114.84.95
Last change on this file since c0438add was c0438add, checked in by Joel Sherrill <joel.sherrill@…>, on Feb 18, 1999 at 9:09:25 PM

Renamed network to wd8003.

  • Property mode set to 100644
File size: 14.0 KB
Line 
1/*
2 * RTEMS driver for WD800x
3 *
4 *  Based on the 68360 Network Driver by:
5 *    W. Eric Norum
6 *    Saskatchewan Accelerator Laboratory
7 *    University of Saskatchewan
8 *    Saskatoon, Saskatchewan, CANADA
9 *    eric@skatter.usask.ca
10 *
11 *  $Id$
12 */
13
14#include <bsp.h>
15#include <wd80x3.h>
16
17#include <stdio.h>
18#include <stdarg.h>
19#include <rtems/error.h>
20#include <rtems/rtems_bsdnet.h>
21
22#include <sys/param.h>
23#include <sys/mbuf.h>
24#include <sys/socket.h>
25#include <sys/sockio.h>
26
27#include <net/if.h>
28
29#include <netinet/in.h>
30#include <netinet/if_ether.h>
31
32#include <irq.h>
33
34#define ET_MINLEN 60            /* minimum message length */
35
36/*
37 * Number of WDs supported by this driver
38 */
39#define NWDDRIVER       1
40
41/*
42 * Default number of buffer descriptors set aside for this driver.
43 * The number of transmit buffer descriptors has to be quite large
44 * since a single frame often uses four or more buffer descriptors.
45 */
46#define RX_BUF_COUNT     15
47#define TX_BUF_COUNT     4
48#define TX_BD_PER_BUF    4
49
50/*
51 * RTEMS event used by interrupt handler to signal driver tasks.
52 * This must not be any of the events used by the network task synchronization.
53 */
54#define INTERRUPT_EVENT RTEMS_EVENT_1
55
56/*
57 * RTEMS event used to start transmit daemon.
58 * This must not be the same as INTERRUPT_EVENT.
59 */
60#define START_TRANSMIT_EVENT    RTEMS_EVENT_2
61
62/*
63 * Receive buffer size -- Allow for a full ethernet packet including CRC
64 */
65#define RBUF_SIZE       1520
66
67#if (MCLBYTES < RBUF_SIZE)
68# error "Driver must have MCLBYTES > RBUF_SIZE"
69#endif
70
71/*
72 * Per-device data
73 */
74struct wd_softc {
75  struct arpcom                 arpcom;
76  rtems_irq_connect_data        irqInfo;
77  struct mbuf                   **rxMbuf;
78  struct mbuf                   **txMbuf;
79  int                           acceptBroadcast;
80  int                           rxBdCount;
81  int                           txBdCount;
82  int                           txBdHead;
83  int                           txBdTail;
84  int                           txBdActiveCount;
85  rtems_id                      rxDaemonTid;
86  rtems_id                      txDaemonTid;
87
88  unsigned int                  port;
89  unsigned char                 *base;
90  unsigned long                 bpar;
91 
92  /*
93   * Statistics
94   */
95  unsigned long rxInterrupts;
96  unsigned long rxNotFirst;
97  unsigned long rxNotLast;
98  unsigned long rxGiant;
99  unsigned long rxNonOctet;
100  unsigned long rxRunt;
101  unsigned long rxBadCRC;
102  unsigned long rxOverrun;
103  unsigned long rxCollision;
104 
105  unsigned long txInterrupts;
106  unsigned long txDeferred;
107  unsigned long txHeartbeat;
108  unsigned long txLateCollision;
109  unsigned long txRetryLimit;
110  unsigned long txUnderrun;
111  unsigned long txLostCarrier;
112  unsigned long txRawWait;
113};
114
115#define RO 0x10
116
117#define SHATOT (8*1024)         /* size of shared memory */
118#define SHAPAGE 256             /* shared memory information */
119#define MAXSIZ  1536            /*(MAXBUF - MESSH_SZ)*/
120#define OUTPAGE ((SHATOT-(MAXSIZ+SHAPAGE-1))/SHAPAGE)
121
122static volatile unsigned long overrun;
123static volatile unsigned long resend;
124static struct wd_softc wd_softc[NWDDRIVER];
125
126/*
127 * WD interrupt handler
128 */
129static rtems_isr
130wd8003Enet_interrupt_handler (rtems_vector_number v)
131{
132  unsigned int tport;
133  unsigned char status, status2;
134
135  tport = wd_softc[0].port ;
136
137  /*
138   * Read status
139   */
140  inport_byte(tport+ISR, status);
141  outport_byte(tport+IMR, 0x00);
142
143  /*
144   * Ring overwrite
145   */
146
147  if (status & MSK_OVW){
148    outport_byte(tport+CMDR, MSK_STP + MSK_RD2);        /* stop 8390 */
149    Wait_X_ms(2);
150    outport_byte(tport+RBCR0, 0);                       /* clear byte count */
151    outport_byte(tport+RBCR1, 0);
152    inport_byte(tport+ISR, status2);
153    status |= (status2 & (MSK_PTX+MSK_TXE)) ;   /* TX status */
154    outport_byte(tport+TCR, MSK_LOOP);          /* loopback mode */
155    outport_byte(tport+CMDR, MSK_STA + MSK_RD2);        /* start */
156    overrun = 1 ;
157    if ((status & (MSK_PTX+MSK_TXE)) == 0)
158        resend = 1;
159  }
160
161  /*
162   * Frame received?
163   */
164  if (status & (MSK_PRX+MSK_RXE)) {
165    outport_byte(tport+ISR, status & (MSK_PRX+MSK_RXE));
166    wd_softc[0].rxInterrupts++;
167    rtems_event_send (wd_softc[0].rxDaemonTid, INTERRUPT_EVENT);   
168  }
169
170}
171
172static void nopOn(const rtems_irq_connect_data* notUsed)
173{
174  /*
175   * code should be moved from wd8003Enet_initialize_hardware
176   * to this location
177   */
178}
179
180static int wdIsOn(const rtems_irq_connect_data* irq)
181{
182  return BSP_irq_enabled_at_i8259s (irq->name);
183}
184
185/*
186 * Initialize the ethernet hardware
187 */
188static void
189wd8003Enet_initialize_hardware (struct wd_softc *sc)
190{
191  int  i1, ultra;
192  char cc1, cc2;
193  unsigned char  temp;
194  rtems_status_code st;
195  unsigned int tport;
196  unsigned char *hwaddr;
197
198  tport = sc->port;
199
200  /* address from board ROM */
201  inport_byte(tport+0x04, temp);
202  outport_byte(tport+0x04, temp & 0x7f);
203
204  hwaddr = sc->arpcom.ac_enaddr;
205  for (i1=cc2=0; i1<8; i1++) {
206    inport_byte(tport + ADDROM + i1, cc1);
207    cc2 += cc1;
208    if (i1 < 6)
209      hwaddr[i1] = cc1;
210  }
211 
212  inport_byte(tport+0x04, temp); 
213  outport_byte(tport+0x04, temp | 0x80);        /* alternate registers */
214  outport_byte(tport+W83CREG, MSK_RESET);       /* reset board, set buffer */
215  outport_byte(tport+W83CREG, 0);
216  outport_byte(tport+W83CREG, MSK_ENASH + (int)((sc->bpar>>13)&0x3f));
217
218  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
219  cc1 = MSK_BMS + MSK_FT10; /* configure 8 or 16 bits */
220
221  inport_byte(tport+0x07, temp) ; 
222
223  ultra = ((temp & 0xf0) == 0x20 || (temp & 0xf0) == 0x40);
224  if (ultra)
225    cc1 = MSK_WTS + MSK_BMS + MSK_FT10;
226  outport_byte(tport+DCR, cc1);
227  outport_byte(tport+RBCR0, 0);
228  outport_byte(tport+RBCR1, 0);
229  outport_byte(tport+RCR, MSK_MON);             /* disable the rxer */
230  outport_byte(tport+TCR, 0);                   /* normal operation */
231  outport_byte(tport+PSTOP, OUTPAGE);           /* init PSTOP */
232  outport_byte(tport+PSTART, 0);                /* init PSTART */
233  outport_byte(tport+BNRY, -1);                 /* init BNRY */
234  outport_byte(tport+ISR, -1);                  /* clear IR's */
235  outport_byte(tport+IMR, 0x15);                /* enable interrupt */
236
237  outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
238
239  for (i1=0; i1<6; i1++)                        /* initial physical addr */
240    outport_byte(tport+PAR+i1, hwaddr[i1]);
241
242  for (i1=0; i1<MARsize; i1++)                  /* clear multicast */
243    outport_byte(tport+MAR+i1, 0);
244  outport_byte(tport+CURR, 0);                  /* init current packet */
245
246  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
247  outport_byte(tport+CMDR, MSK_STA + MSK_RD2);  /* put 8390 on line */
248  outport_byte(tport+RCR, MSK_AB);              /* MSK_AB accept broadcast */
249 
250  if (ultra) {
251    inport_byte(tport+0x0c, temp);
252    outport_byte(tport+0x0c, temp | 0x80);
253    outport_byte(tport+0x05, 0x80);
254    outport_byte(tport+0x06, 0x01);
255  }
256 
257  /*
258   * Set up interrupts
259   */
260  sc->irqInfo.hdl = wd8003Enet_interrupt_handler;
261  sc->irqInfo.on  = nopOn;
262  sc->irqInfo.off = nopOn;
263  sc->irqInfo.isOn = wdIsOn;
264 
265  st = BSP_install_rtems_irq_handler (&sc->irqInfo);
266  if (!st)
267    rtems_panic ("Can't attach WD interrupt handler for irq %d\n",
268                  sc->irqInfo.name);
269}
270
271static void
272wd_rxDaemon (void *arg)
273{
274  unsigned int tport;
275  struct ether_header *eh;
276  struct wd_softc *dp = (struct wd_softc *)&wd_softc[0];
277  struct ifnet *ifp = &dp->arpcom.ac_if;
278  struct mbuf *m;
279  unsigned int i2;
280  unsigned int len;
281  volatile unsigned char start, next, current;
282  char *shp, *temp;
283  rtems_event_set events;
284
285  tport = wd_softc[0].port ;
286
287  for (;;){
288
289
290    rtems_bsdnet_event_receive (INTERRUPT_EVENT,
291                                RTEMS_WAIT|RTEMS_EVENT_ANY,
292                                RTEMS_NO_TIMEOUT,
293                                &events);
294
295    for (;;){
296      inport_byte(tport+BNRY, start);
297
298      outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
299      inport_byte(tport+CURR, current);
300      outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2); 
301
302      start += 1;
303      if (start >= OUTPAGE){
304        start = 0;
305      }
306
307      if (current == start)
308        break;
309     
310      shp = dp->base + 1 + (SHAPAGE * start);
311      next = *shp++;
312      len = *((short *)shp)++ - 4;
313
314      if (next >= OUTPAGE){
315        next = 0;
316      }
317     
318      MGETHDR (m, M_WAIT, MT_DATA);
319      MCLGET (m, M_WAIT);
320      m->m_pkthdr.rcvif = ifp;
321     
322      temp = m->m_data;
323      m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
324     
325      if ((i2 = (OUTPAGE - start) * SHAPAGE - 4) < len){
326        memcpy(temp, shp, i2);
327        len -= i2;
328        temp += i2;
329        shp = dp->base;
330      }
331      memcpy(temp, shp, len);
332     
333      eh = mtod (m, struct ether_header *);
334      m->m_data += sizeof(struct ether_header);
335      ether_input (ifp, eh, m);
336     
337      outport_byte(tport+BNRY, next-1);
338    }
339   
340  /*
341   * Ring overwrite
342   */
343    if (overrun){     
344      outport_byte(tport+ISR, MSK_OVW);         /* reset IR */
345      outport_byte(tport+TCR, 0);               /* out of loopback */
346      if (resend  == 1)
347        outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);    /* resend */
348      resend = 0;
349      overrun = 0;
350    }
351   
352    outport_byte(tport+IMR, 0x15);  /* re-enable IT rx */
353  }     
354}
355
356static void
357sendpacket (struct ifnet *ifp, struct mbuf *m)
358{
359        struct wd_softc *dp = ifp->if_softc;
360        struct mbuf *n;
361        unsigned int len, tport;
362        char *shp, txReady;
363
364        tport = dp->port;
365
366  /*
367   * Waiting for Transmitter ready
368   */   
369  inport_byte(tport+CMDR, txReady); 
370  while(txReady & MSK_TXP)
371    inport_byte(tport+CMDR, txReady); 
372
373  len = 0;
374  shp = dp->base + (SHAPAGE * OUTPAGE);
375
376  n = m;
377 
378  for (;;){
379    len += m->m_len;
380    memcpy(shp, (char *)m->m_data, m->m_len);
381    shp += m->m_len ;
382    if ((m = m->m_next) == NULL)
383      break;
384  }
385
386  m_freem(n);
387 
388  if (len < ET_MINLEN) len = ET_MINLEN;
389  outport_byte(tport+TBCR0, len);
390  outport_byte(tport+TBCR1, (len >> 8) );
391  outport_byte(tport+TPSR, OUTPAGE);
392  outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);
393}
394
395/*
396 * Driver transmit daemon
397 */
398void
399wd_txDaemon (void *arg)
400{
401        struct wd_softc *sc = (struct wd_softc *)arg;
402        struct ifnet *ifp = &sc->arpcom.ac_if;
403        struct mbuf *m;
404        rtems_event_set events;
405
406        for (;;) {
407                /*
408                 * Wait for packet
409                 */
410                rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events);
411
412                /*
413                 * Send packets till queue is empty
414                 */
415                for (;;) {
416                        /*
417                         * Get the next mbuf chain to transmit.
418                         */
419                        IF_DEQUEUE(&ifp->if_snd, m);
420                        if (!m)
421                                break;
422                        sendpacket (ifp, m);
423                }
424                ifp->if_flags &= ~IFF_OACTIVE;
425        }
426}
427
428/*
429 * Send packet (caller provides header).
430 */
431static void
432wd_start (struct ifnet *ifp)
433{
434        struct wd_softc *sc = ifp->if_softc;
435
436        rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
437        ifp->if_flags |= IFF_OACTIVE;
438}
439
440/*
441 * Initialize and start the device
442 */
443static void
444wd_init (void *arg)
445{
446  struct wd_softc *sc = arg;
447  struct ifnet *ifp = &sc->arpcom.ac_if;
448
449  if (sc->txDaemonTid == 0) {
450   
451    /*
452     * Set up WD hardware
453     */
454    wd8003Enet_initialize_hardware (sc);
455   
456    /*
457     * Start driver tasks
458     */
459    sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, wd_txDaemon, sc);
460    sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, wd_rxDaemon, sc);
461  }
462
463  /*
464   * Tell the world that we're running.
465   */
466  ifp->if_flags |= IFF_RUNNING;
467
468}
469
470/*
471 * Stop the device
472 */
473static void
474wd_stop (struct wd_softc *sc)
475{
476  unsigned int tport;
477  unsigned char temp;
478  struct ifnet *ifp = &sc->arpcom.ac_if;
479
480  ifp->if_flags &= ~IFF_RUNNING;
481
482  /*
483   * Stop the transmitter
484   */
485  tport=wd_softc[0].port ;
486  inport_byte(tport+0x04,temp);
487  outport_byte(tport+0x04, temp & 0x7f);
488  outport_byte(tport + CMDR, MSK_STP + MSK_RD2);
489
490}
491
492
493/*
494 * Show interface statistics
495 */
496static void
497wd_stats (struct wd_softc *sc)
498{
499        printf ("      Rx Interrupts:%-8lu", sc->rxInterrupts);
500        printf ("       Not First:%-8lu", sc->rxNotFirst);
501        printf ("        Not Last:%-8lu\n", sc->rxNotLast);
502        printf ("              Giant:%-8lu", sc->rxGiant);
503        printf ("            Runt:%-8lu", sc->rxRunt);
504        printf ("       Non-octet:%-8lu\n", sc->rxNonOctet);
505        printf ("            Bad CRC:%-8lu", sc->rxBadCRC);
506        printf ("         Overrun:%-8lu", sc->rxOverrun);
507        printf ("       Collision:%-8lu\n", sc->rxCollision);
508
509        printf ("      Tx Interrupts:%-8lu", sc->txInterrupts);
510        printf ("        Deferred:%-8lu", sc->txDeferred);
511        printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
512        printf ("         No Carrier:%-8lu", sc->txLostCarrier);
513        printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
514        printf ("  Late Collision:%-8lu\n", sc->txLateCollision);
515        printf ("           Underrun:%-8lu", sc->txUnderrun);
516        printf (" Raw output wait:%-8lu\n", sc->txRawWait);
517}
518
519/*
520 * Driver ioctl handler
521 */
522static int
523wd_ioctl (struct ifnet *ifp, int command, caddr_t data)
524{
525        struct wd_softc *sc = ifp->if_softc;
526        int error = 0;
527
528        switch (command) {
529        case SIOCGIFADDR:
530        case SIOCSIFADDR:
531                ether_ioctl (ifp, command, data);
532                break;
533
534        case SIOCSIFFLAGS:
535                switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
536                case IFF_RUNNING:
537                        wd_stop (sc);
538                        break;
539
540                case IFF_UP:
541                        wd_init (sc);
542                        break;
543
544                case IFF_UP | IFF_RUNNING:
545                        wd_stop (sc);
546                        wd_init (sc);
547                        break;
548
549                default:
550                        break;
551                }
552                break;
553
554        case SIO_RTEMS_SHOW_STATS:
555                wd_stats (sc);
556                break;
557               
558        /*
559         * FIXME: All sorts of multicast commands need to be added here!
560         */
561        default:
562                error = EINVAL;
563                break;
564        }
565        return error;
566}
567
568/*
569 * Attach an WD driver to the system
570 */
571int
572rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *config)
573{
574        struct wd_softc *sc;
575        struct ifnet *ifp;
576        int mtu;
577        int i;
578
579        /*
580         * Find a free driver
581         */
582        for (i = 0 ; i < NWDDRIVER ; i++) {
583                sc = &wd_softc[i];
584                ifp = &sc->arpcom.ac_if;
585                if (ifp->if_softc == NULL)
586                        break;
587        }
588        if (i >= NWDDRIVER) {
589                printf ("Too many WD drivers.\n");
590                return 0;
591        }
592
593        /*
594         * Process options
595         */
596        if (config->hardware_address) {
597          memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
598                  ETHER_ADDR_LEN);
599        }
600        else {
601          memset (sc->arpcom.ac_enaddr, 0x08,ETHER_ADDR_LEN);
602        }
603        if (config->mtu)
604                mtu = config->mtu;
605        else
606                mtu = ETHERMTU;
607
608
609        if (config->irno)
610                sc->irqInfo.name = config->irno;
611        else
612                sc->irqInfo.name = 5;
613
614        if (config->port)
615                sc->port = config->port;
616        else
617                sc->port = 0x240;
618
619        if (config->bpar) {
620                sc->bpar = config->bpar;
621                sc->base = (unsigned char*) config->bpar;
622        }
623        else {
624                sc->bpar = 0xD0000;
625                sc->base = (unsigned char*) 0xD0000;
626        }
627       
628        sc->acceptBroadcast = !config->ignore_broadcast;
629
630        /*
631         * Set up network interface values
632         */
633        ifp->if_softc = sc;
634        ifp->if_unit = i + 1;
635        ifp->if_name = "wd";
636        ifp->if_mtu = mtu;
637        ifp->if_init = wd_init;
638        ifp->if_ioctl = wd_ioctl;
639        ifp->if_start = wd_start;
640        ifp->if_output = ether_output;
641        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
642        if (ifp->if_snd.ifq_maxlen == 0)
643                ifp->if_snd.ifq_maxlen = ifqmaxlen;
644
645        /*
646         * init some variables
647         */
648        overrun = 0;
649        resend = 0;
650
651        /*
652         * Attach the interface
653         */
654        if_attach (ifp);
655        ether_ifattach (ifp);
656        return 1;
657};
Note: See TracBrowser for help on using the repository browser.