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

4.104.114.84.95
Last change on this file since a14e350a was a14e350a, checked in by Joel Sherrill <joel.sherrill@…>, on 05/21/04 at 14:23:15

2004-05-21 Joel Sherrill <joel@…>

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