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

4.104.114.84.95
Last change on this file since 6128a4a was 6128a4a, checked in by Ralf Corsepius <ralf.corsepius@…>, on 04/21/04 at 10:43:04

Remove stray white spaces.

  • 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 <rtems/error.h>
21#include <rtems/rtems_bsdnet.h>
22
23#include <sys/param.h>
24#include <sys/mbuf.h>
25#include <sys/socket.h>
26#include <sys/sockio.h>
27
28#include <net/if.h>
29
30#include <netinet/in.h>
31#include <netinet/if_ether.h>
32
33#include <irq.h>
34
35#define ET_MINLEN 60            /* minimum message length */
36
37/*
38 * Number of WDs supported by this driver
39 */
40#define NWDDRIVER       1
41
42/*
43 * Default number of buffer descriptors set aside for this driver.
44 * The number of transmit buffer descriptors has to be quite large
45 * since a single frame often uses four or more buffer descriptors.
46 */
47#define RX_BUF_COUNT     15
48#define TX_BUF_COUNT     4
49#define TX_BD_PER_BUF    4
50
51/*
52 * RTEMS event used by interrupt handler to signal driver tasks.
53 * This must not be any of the events used by the network task synchronization.
54 */
55#define INTERRUPT_EVENT RTEMS_EVENT_1
56
57/*
58 * RTEMS event used to start transmit daemon.
59 * This must not be the same as INTERRUPT_EVENT.
60 */
61#define START_TRANSMIT_EVENT    RTEMS_EVENT_2
62
63/*
64 * Receive buffer size -- Allow for a full ethernet packet including CRC
65 */
66#define RBUF_SIZE       1520
67
68#if (MCLBYTES < RBUF_SIZE)
69# error "Driver must have MCLBYTES > RBUF_SIZE"
70#endif
71
72/*
73 * Per-device data
74 */
75struct wd_softc {
76  struct arpcom                 arpcom;
77  rtems_irq_connect_data        irqInfo;
78  struct mbuf                   **rxMbuf;
79  struct mbuf                   **txMbuf;
80  int                           acceptBroadcast;
81  int                           rxBdCount;
82  int                           txBdCount;
83  int                           txBdHead;
84  int                           txBdTail;
85  int                           txBdActiveCount;
86  rtems_id                      rxDaemonTid;
87  rtems_id                      txDaemonTid;
88
89  unsigned int                  port;
90  unsigned char                 *base;
91  unsigned long                 bpar;
92
93  /*
94   * Statistics
95   */
96  unsigned long rxInterrupts;
97  unsigned long rxNotFirst;
98  unsigned long rxNotLast;
99  unsigned long rxGiant;
100  unsigned long rxNonOctet;
101  unsigned long rxRunt;
102  unsigned long rxBadCRC;
103  unsigned long rxOverrun;
104  unsigned long rxCollision;
105
106  unsigned long txInterrupts;
107  unsigned long txDeferred;
108  unsigned long txHeartbeat;
109  unsigned long txLateCollision;
110  unsigned long txRetryLimit;
111  unsigned long txUnderrun;
112  unsigned long txLostCarrier;
113  unsigned long txRawWait;
114};
115
116#define RO 0x10
117
118#define SHATOT (8*1024)         /* size of shared memory */
119#define SHAPAGE 256             /* shared memory information */
120#define MAXSIZ  1536            /*(MAXBUF - MESSH_SZ)*/
121#define OUTPAGE ((SHATOT-(MAXSIZ+SHAPAGE-1))/SHAPAGE)
122
123static volatile unsigned long overrun;
124static volatile unsigned long resend;
125static struct wd_softc wd_softc[NWDDRIVER];
126
127/*
128 * WD interrupt handler
129 */
130static void
131wd8003Enet_interrupt_handler (void)
132{
133  unsigned int tport;
134  unsigned char status, status2;
135
136  tport = wd_softc[0].port ;
137
138  /*
139   * Read status
140   */
141  inport_byte(tport+ISR, status);
142  outport_byte(tport+IMR, 0x00);
143
144  /*
145   * Ring overwrite
146   */
147
148  if (status & MSK_OVW){
149    outport_byte(tport+CMDR, MSK_STP + MSK_RD2);        /* stop 8390 */
150    Wait_X_ms(2);
151    outport_byte(tport+RBCR0, 0);                       /* clear byte count */
152    outport_byte(tport+RBCR1, 0);
153    inport_byte(tport+ISR, status2);
154    status |= (status2 & (MSK_PTX+MSK_TXE)) ;   /* TX status */
155    outport_byte(tport+TCR, MSK_LOOP);          /* loopback mode */
156    outport_byte(tport+CMDR, MSK_STA + MSK_RD2);        /* start */
157    overrun = 1 ;
158    if ((status & (MSK_PTX+MSK_TXE)) == 0)
159        resend = 1;
160  }
161
162  /*
163   * Frame received?
164   */
165  if (status & (MSK_PRX+MSK_RXE)) {
166    outport_byte(tport+ISR, status & (MSK_PRX+MSK_RXE));
167    wd_softc[0].rxInterrupts++;
168    rtems_event_send (wd_softc[0].rxDaemonTid, INTERRUPT_EVENT);
169  }
170
171}
172
173static void nopOn(const rtems_irq_connect_data* notUsed)
174{
175  /*
176   * code should be moved from wd8003Enet_initialize_hardware
177   * to this location
178   */
179}
180
181static int wdIsOn(const rtems_irq_connect_data* irq)
182{
183  return BSP_irq_enabled_at_i8259s (irq->name);
184}
185
186/*
187 * Initialize the ethernet hardware
188 */
189static void
190wd8003Enet_initialize_hardware (struct wd_softc *sc)
191{
192  int  i1, ultra;
193  char cc1, cc2;
194  unsigned char  temp;
195  rtems_status_code st;
196  unsigned int tport;
197  unsigned char *hwaddr;
198
199  tport = sc->port;
200
201  /* address from board ROM */
202  inport_byte(tport+0x04, temp);
203  outport_byte(tport+0x04, temp & 0x7f);
204
205  hwaddr = sc->arpcom.ac_enaddr;
206  for (i1=cc2=0; i1<8; i1++) {
207    inport_byte(tport + ADDROM + i1, cc1);
208    cc2 += cc1;
209    if (i1 < 6)
210      hwaddr[i1] = cc1;
211  }
212
213  inport_byte(tport+0x04, temp);
214  outport_byte(tport+0x04, temp | 0x80);        /* alternate registers */
215  outport_byte(tport+W83CREG, MSK_RESET);       /* reset board, set buffer */
216  outport_byte(tport+W83CREG, 0);
217  outport_byte(tport+W83CREG, MSK_ENASH + (int)((sc->bpar>>13)&0x3f));
218
219  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
220  cc1 = MSK_BMS + MSK_FT10; /* configure 8 or 16 bits */
221
222  inport_byte(tport+0x07, temp) ;
223
224  ultra = ((temp & 0xf0) == 0x20 || (temp & 0xf0) == 0x40);
225  if (ultra)
226    cc1 = MSK_WTS + MSK_BMS + MSK_FT10;
227  outport_byte(tport+DCR, cc1);
228  outport_byte(tport+RBCR0, 0);
229  outport_byte(tport+RBCR1, 0);
230  outport_byte(tport+RCR, MSK_MON);             /* disable the rxer */
231  outport_byte(tport+TCR, 0);                   /* normal operation */
232  outport_byte(tport+PSTOP, OUTPAGE);           /* init PSTOP */
233  outport_byte(tport+PSTART, 0);                /* init PSTART */
234  outport_byte(tport+BNRY, -1);                 /* init BNRY */
235  outport_byte(tport+ISR, -1);                  /* clear IR's */
236  outport_byte(tport+IMR, 0x15);                /* enable interrupt */
237
238  outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
239
240  for (i1=0; i1<6; i1++)                        /* initial physical addr */
241    outport_byte(tport+PAR+i1, hwaddr[i1]);
242
243  for (i1=0; i1<MARsize; i1++)                  /* clear multicast */
244    outport_byte(tport+MAR+i1, 0);
245  outport_byte(tport+CURR, 0);                  /* init current packet */
246
247  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
248  outport_byte(tport+CMDR, MSK_STA + MSK_RD2);  /* put 8390 on line */
249  outport_byte(tport+RCR, MSK_AB);              /* MSK_AB accept broadcast */
250
251  if (ultra) {
252    inport_byte(tport+0x0c, temp);
253    outport_byte(tport+0x0c, temp | 0x80);
254    outport_byte(tport+0x05, 0x80);
255    outport_byte(tport+0x06, 0x01);
256  }
257
258  /*
259   * Set up interrupts
260   */
261  sc->irqInfo.hdl = wd8003Enet_interrupt_handler;
262  sc->irqInfo.on  = nopOn;
263  sc->irqInfo.off = nopOn;
264  sc->irqInfo.isOn = wdIsOn;
265
266  st = BSP_install_rtems_irq_handler (&sc->irqInfo);
267  if (!st)
268    rtems_panic ("Can't attach WD interrupt handler for irq %d\n",
269                  sc->irqInfo.name);
270}
271
272static void
273wd_rxDaemon (void *arg)
274{
275  unsigned int tport;
276  struct ether_header *eh;
277  struct wd_softc *dp = (struct wd_softc *)&wd_softc[0];
278  struct ifnet *ifp = &dp->arpcom.ac_if;
279  struct mbuf *m;
280  unsigned int i2;
281  unsigned int len;
282  volatile unsigned char start, next, current;
283  char *shp, *temp;
284  rtems_event_set events;
285
286  tport = wd_softc[0].port ;
287
288  for (;;){
289
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/*
495 * Show interface statistics
496 */
497static void
498wd_stats (struct wd_softc *sc)
499{
500        printf ("      Rx Interrupts:%-8lu", sc->rxInterrupts);
501        printf ("       Not First:%-8lu", sc->rxNotFirst);
502        printf ("        Not Last:%-8lu\n", sc->rxNotLast);
503        printf ("              Giant:%-8lu", sc->rxGiant);
504        printf ("            Runt:%-8lu", sc->rxRunt);
505        printf ("       Non-octet:%-8lu\n", sc->rxNonOctet);
506        printf ("            Bad CRC:%-8lu", sc->rxBadCRC);
507        printf ("         Overrun:%-8lu", sc->rxOverrun);
508        printf ("       Collision:%-8lu\n", sc->rxCollision);
509
510        printf ("      Tx Interrupts:%-8lu", sc->txInterrupts);
511        printf ("        Deferred:%-8lu", sc->txDeferred);
512        printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
513        printf ("         No Carrier:%-8lu", sc->txLostCarrier);
514        printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
515        printf ("  Late Collision:%-8lu\n", sc->txLateCollision);
516        printf ("           Underrun:%-8lu", sc->txUnderrun);
517        printf (" Raw output wait:%-8lu\n", sc->txRawWait);
518}
519
520/*
521 * Driver ioctl handler
522 */
523static int
524wd_ioctl (struct ifnet *ifp, int command, caddr_t data)
525{
526        struct wd_softc *sc = ifp->if_softc;
527        int error = 0;
528
529        switch (command) {
530        case SIOCGIFADDR:
531        case SIOCSIFADDR:
532                ether_ioctl (ifp, command, data);
533                break;
534
535        case SIOCSIFFLAGS:
536                switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
537                case IFF_RUNNING:
538                        wd_stop (sc);
539                        break;
540
541                case IFF_UP:
542                        wd_init (sc);
543                        break;
544
545                case IFF_UP | IFF_RUNNING:
546                        wd_stop (sc);
547                        wd_init (sc);
548                        break;
549
550                default:
551                        break;
552                }
553                break;
554
555        case SIO_RTEMS_SHOW_STATS:
556                wd_stats (sc);
557                break;
558
559        /*
560         * FIXME: All sorts of multicast commands need to be added here!
561         */
562        default:
563                error = EINVAL;
564                break;
565        }
566        return error;
567}
568
569/*
570 * Attach an WD driver to the system
571 */
572int
573rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
574{
575        struct wd_softc *sc;
576        struct ifnet *ifp;
577        int mtu;
578        int i;
579
580        /*
581         * Find a free driver
582         */
583        for (i = 0 ; i < NWDDRIVER ; i++) {
584                sc = &wd_softc[i];
585                ifp = &sc->arpcom.ac_if;
586                if (ifp->if_softc == NULL)
587                        break;
588        }
589        if (i >= NWDDRIVER) {
590                printf ("Too many WD drivers.\n");
591                return 0;
592        }
593
594        /*
595         * Process options
596         */
597        if (config->hardware_address) {
598          memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
599                  ETHER_ADDR_LEN);
600        }
601        else {
602          memset (sc->arpcom.ac_enaddr, 0x08,ETHER_ADDR_LEN);
603        }
604        if (config->mtu)
605                mtu = config->mtu;
606        else
607                mtu = ETHERMTU;
608
609
610        if (config->irno)
611                sc->irqInfo.name = config->irno;
612        else
613                sc->irqInfo.name = 5;
614
615        if (config->port)
616                sc->port = config->port;
617        else
618                sc->port = 0x240;
619
620        if (config->bpar) {
621                sc->bpar = config->bpar;
622                sc->base = (unsigned char*) config->bpar;
623        }
624        else {
625                sc->bpar = 0xD0000;
626                sc->base = (unsigned char*) 0xD0000;
627        }
628
629        sc->acceptBroadcast = !config->ignore_broadcast;
630
631        /*
632         * Set up network interface values
633         */
634        ifp->if_softc = sc;
635        ifp->if_unit = i + 1;
636        ifp->if_name = "wd";
637        ifp->if_mtu = mtu;
638        ifp->if_init = wd_init;
639        ifp->if_ioctl = wd_ioctl;
640        ifp->if_start = wd_start;
641        ifp->if_output = ether_output;
642        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
643        if (ifp->if_snd.ifq_maxlen == 0)
644                ifp->if_snd.ifq_maxlen = ifqmaxlen;
645
646        /*
647         * init some variables
648         */
649        overrun = 0;
650        resend = 0;
651
652        /*
653         * Attach the interface
654         */
655        if_attach (ifp);
656        ether_ifattach (ifp);
657        return 1;
658};
Note: See TracBrowser for help on using the repository browser.