source: rtems/c/src/lib/libbsp/i386/pc386/network/network.c @ 1094cf0d

4.104.114.84.95
Last change on this file since 1094cf0d was 1094cf0d, checked in by Joel Sherrill <joel.sherrill@…>, on 02/18/99 at 21:06:17

Patch from Emmanuel RAGUET <raguet@…> to add files
that were accidentally not committed earlier. The DECNet driver
is being added as its own directory to avoid forcing the driver to
have to pull in the complete set of network drivers.

  • 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.