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

4.115
Last change on this file since 441b90e was ff73cf6, checked in by Ralf Corsepius <ralf.corsepius@…>, on Dec 15, 2006 at 6:00:03 AM

Use ioctl_command_t as arg in ioctl-functions.

  • Property mode set to 100644
File size: 14.2 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 <bsp/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 *unused)
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  unsigned char *shp, *temp;
285  unsigned short *real_short_ptr;
286  rtems_event_set events;
287
288  tport = wd_softc[0].port ;
289
290  for (;;){
291
292    rtems_bsdnet_event_receive (INTERRUPT_EVENT,
293                                RTEMS_WAIT|RTEMS_EVENT_ANY,
294                                RTEMS_NO_TIMEOUT,
295                                &events);
296
297    for (;;){
298      inport_byte(tport+BNRY, start);
299
300      outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
301      inport_byte(tport+CURR, current);
302      outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
303
304      start += 1;
305      if (start >= OUTPAGE){
306        start = 0;
307      }
308
309      if (current == start)
310        break;
311
312      /* real_short_ptr avoids cast on lvalue which gcc no longer allows */
313      shp = dp->base + 1 + (SHAPAGE * start);
314      next = *shp++;
315      real_short_ptr = (unsigned short *)shp;
316      len = *(real_short_ptr)++ - 4;
317
318      if (next >= OUTPAGE){
319        next = 0;
320      }
321
322      MGETHDR (m, M_WAIT, MT_DATA);
323      MCLGET (m, M_WAIT);
324      m->m_pkthdr.rcvif = ifp;
325
326      temp = (unsigned char *) m->m_data;
327      m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
328
329      if ((i2 = (OUTPAGE - start) * SHAPAGE - 4) < len){
330        memcpy(temp, shp, i2);
331        len -= i2;
332        temp += i2;
333        shp = dp->base;
334      }
335      memcpy(temp, shp, len);
336
337      eh = mtod (m, struct ether_header *);
338      m->m_data += sizeof(struct ether_header);
339      ether_input (ifp, eh, m);
340
341      outport_byte(tport+BNRY, next-1);
342    }
343
344  /*
345   * Ring overwrite
346   */
347    if (overrun){
348      outport_byte(tport+ISR, MSK_OVW);         /* reset IR */
349      outport_byte(tport+TCR, 0);               /* out of loopback */
350      if (resend  == 1)
351        outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);    /* resend */
352      resend = 0;
353      overrun = 0;
354    }
355
356    outport_byte(tport+IMR, 0x15);  /* re-enable IT rx */
357  }
358}
359
360static void
361sendpacket (struct ifnet *ifp, struct mbuf *m)
362{
363        struct wd_softc *dp = ifp->if_softc;
364        struct mbuf *n;
365        unsigned int len, tport;
366        uint8_t *shp, txReady;
367
368        tport = dp->port;
369
370  /*
371   * Waiting for Transmitter ready
372   */
373  inport_byte(tport+CMDR, txReady);
374  while(txReady & MSK_TXP)
375    inport_byte(tport+CMDR, txReady);
376
377  len = 0;
378  shp = dp->base + (SHAPAGE * OUTPAGE);
379
380  n = m;
381
382  for (;;){
383    len += m->m_len;
384    memcpy(shp, (char *)m->m_data, m->m_len);
385    shp += m->m_len ;
386    if ((m = m->m_next) == NULL)
387      break;
388  }
389
390  m_freem(n);
391
392  if (len < ET_MINLEN) len = ET_MINLEN;
393  outport_byte(tport+TBCR0, len);
394  outport_byte(tport+TBCR1, (len >> 8) );
395  outport_byte(tport+TPSR, OUTPAGE);
396  outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);
397}
398
399/*
400 * Driver transmit daemon
401 */
402void
403wd_txDaemon (void *arg)
404{
405        struct wd_softc *sc = (struct wd_softc *)arg;
406        struct ifnet *ifp = &sc->arpcom.ac_if;
407        struct mbuf *m;
408        rtems_event_set events;
409
410        for (;;) {
411                /*
412                 * Wait for packet
413                 */
414                rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events);
415
416                /*
417                 * Send packets till queue is empty
418                 */
419                for (;;) {
420                        /*
421                         * Get the next mbuf chain to transmit.
422                         */
423                        IF_DEQUEUE(&ifp->if_snd, m);
424                        if (!m)
425                                break;
426                        sendpacket (ifp, m);
427                }
428                ifp->if_flags &= ~IFF_OACTIVE;
429        }
430}
431
432/*
433 * Send packet (caller provides header).
434 */
435static void
436wd_start (struct ifnet *ifp)
437{
438        struct wd_softc *sc = ifp->if_softc;
439
440        rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
441        ifp->if_flags |= IFF_OACTIVE;
442}
443
444/*
445 * Initialize and start the device
446 */
447static void
448wd_init (void *arg)
449{
450  struct wd_softc *sc = arg;
451  struct ifnet *ifp = &sc->arpcom.ac_if;
452
453  if (sc->txDaemonTid == 0) {
454
455    /*
456     * Set up WD hardware
457     */
458    wd8003Enet_initialize_hardware (sc);
459
460    /*
461     * Start driver tasks
462     */
463    sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, wd_txDaemon, sc);
464    sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, wd_rxDaemon, sc);
465  }
466
467  /*
468   * Tell the world that we're running.
469   */
470  ifp->if_flags |= IFF_RUNNING;
471
472}
473
474/*
475 * Stop the device
476 */
477static void
478wd_stop (struct wd_softc *sc)
479{
480  unsigned int tport;
481  unsigned char temp;
482  struct ifnet *ifp = &sc->arpcom.ac_if;
483
484  ifp->if_flags &= ~IFF_RUNNING;
485
486  /*
487   * Stop the transmitter
488   */
489  tport=wd_softc[0].port ;
490  inport_byte(tport+0x04,temp);
491  outport_byte(tport+0x04, temp & 0x7f);
492  outport_byte(tport + CMDR, MSK_STP + MSK_RD2);
493
494}
495
496/*
497 * Show interface statistics
498 */
499static void
500wd_stats (struct wd_softc *sc)
501{
502        printf ("      Rx Interrupts:%-8lu", sc->rxInterrupts);
503        printf ("       Not First:%-8lu", sc->rxNotFirst);
504        printf ("        Not Last:%-8lu\n", sc->rxNotLast);
505        printf ("              Giant:%-8lu", sc->rxGiant);
506        printf ("            Runt:%-8lu", sc->rxRunt);
507        printf ("       Non-octet:%-8lu\n", sc->rxNonOctet);
508        printf ("            Bad CRC:%-8lu", sc->rxBadCRC);
509        printf ("         Overrun:%-8lu", sc->rxOverrun);
510        printf ("       Collision:%-8lu\n", sc->rxCollision);
511
512        printf ("      Tx Interrupts:%-8lu", sc->txInterrupts);
513        printf ("        Deferred:%-8lu", sc->txDeferred);
514        printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
515        printf ("         No Carrier:%-8lu", sc->txLostCarrier);
516        printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
517        printf ("  Late Collision:%-8lu\n", sc->txLateCollision);
518        printf ("           Underrun:%-8lu", sc->txUnderrun);
519        printf (" Raw output wait:%-8lu\n", sc->txRawWait);
520}
521
522/*
523 * Driver ioctl handler
524 */
525static int
526wd_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
527{
528        struct wd_softc *sc = ifp->if_softc;
529        int error = 0;
530
531        switch (command) {
532        case SIOCGIFADDR:
533        case SIOCSIFADDR:
534                ether_ioctl (ifp, command, data);
535                break;
536
537        case SIOCSIFFLAGS:
538                switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
539                case IFF_RUNNING:
540                        wd_stop (sc);
541                        break;
542
543                case IFF_UP:
544                        wd_init (sc);
545                        break;
546
547                case IFF_UP | IFF_RUNNING:
548                        wd_stop (sc);
549                        wd_init (sc);
550                        break;
551
552                default:
553                        break;
554                }
555                break;
556
557        case SIO_RTEMS_SHOW_STATS:
558                wd_stats (sc);
559                break;
560
561        /*
562         * FIXME: All sorts of multicast commands need to be added here!
563         */
564        default:
565                error = EINVAL;
566                break;
567        }
568        return error;
569}
570
571/*
572 * Attach an WD driver to the system
573 */
574int
575rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
576{
577        struct wd_softc *sc;
578        struct ifnet *ifp;
579        int mtu;
580        int i;
581
582        /*
583         * Find a free driver
584         */
585        for (i = 0 ; i < NWDDRIVER ; i++) {
586                sc = &wd_softc[i];
587                ifp = &sc->arpcom.ac_if;
588                if (ifp->if_softc == NULL)
589                        break;
590        }
591        if (i >= NWDDRIVER) {
592                printf ("Too many WD drivers.\n");
593                return 0;
594        }
595
596        /*
597         * Process options
598         */
599        if (config->hardware_address) {
600          memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
601                  ETHER_ADDR_LEN);
602        }
603        else {
604          memset (sc->arpcom.ac_enaddr, 0x08,ETHER_ADDR_LEN);
605        }
606        if (config->mtu)
607                mtu = config->mtu;
608        else
609                mtu = ETHERMTU;
610
611        if (config->irno)
612                sc->irqInfo.name = config->irno;
613        else
614                sc->irqInfo.name = 5;
615
616        if (config->port)
617                sc->port = config->port;
618        else
619                sc->port = 0x240;
620
621        if (config->bpar) {
622                sc->bpar = config->bpar;
623                sc->base = (unsigned char*) config->bpar;
624        }
625        else {
626                sc->bpar = 0xD0000;
627                sc->base = (unsigned char*) 0xD0000;
628        }
629
630        sc->acceptBroadcast = !config->ignore_broadcast;
631
632        /*
633         * Set up network interface values
634         */
635        ifp->if_softc = sc;
636        ifp->if_unit = i + 1;
637        ifp->if_name = "wd";
638        ifp->if_mtu = mtu;
639        ifp->if_init = wd_init;
640        ifp->if_ioctl = wd_ioctl;
641        ifp->if_start = wd_start;
642        ifp->if_output = ether_output;
643        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
644        if (ifp->if_snd.ifq_maxlen == 0)
645                ifp->if_snd.ifq_maxlen = ifqmaxlen;
646
647        /*
648         * init some variables
649         */
650        overrun = 0;
651        resend = 0;
652
653        /*
654         * Attach the interface
655         */
656        if_attach (ifp);
657        ether_ifattach (ifp);
658        return 1;
659};
Note: See TracBrowser for help on using the repository browser.