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

Last change on this file since d8d6a08 was d8d6a08, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 27, 2018 at 10:12:44 AM

bsps: Move network define to source files

Define INSIDE_RTEMS_BSD_TCPIP_STACK in the network interface driver
source files to avoid some build system magic.

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