source: rtems/c/src/lib/libbsp/i386/pc386/network/network.c @ 06fa582

4.104.114.84.95
Last change on this file since 06fa582 was dbab462, checked in by Joel Sherrill <joel.sherrill@…>, on 07/24/98 at 16:09:51

Patch from Eric Valette <valette@…> and Emmanuel Raguet
<raguet@…> to make their patches work together.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1/*
2 * RTEMS/KA9Q driver for WD8003 Ethernet Controller
3 *
4 *
5 *  $Id$
6 */
7#include <bsp.h>
8#include <wd80x3.h>
9#include <rtems/error.h>
10#include <ka9q/rtems_ka9q.h>
11#include <ka9q/global.h>
12#include <ka9q/enet.h>
13#include <ka9q/iface.h>
14#include <ka9q/netuser.h>
15#include <ka9q/trace.h>
16#include <ka9q/commands.h>
17#include <ka9q/domain.h>
18#include <irq.h>
19
20#define ET_MINLEN 60            /* minimum message length */
21
22/*
23 * Number of SCCs supported by this driver
24 */
25#define NSCCDRIVER      1
26
27/*
28 * Default number of buffer descriptors set aside for this driver.
29 * The number of transmit buffer descriptors has to be quite large
30 * since a single frame often uses four or more buffer descriptors.
31 */
32
33#define RX_BUF_COUNT     15
34#define TX_BUF_COUNT     4
35#define TX_BD_PER_BUF    4
36
37/*
38 * RTEMS event used by interrupt handler to signal daemons.
39 * This must *not* be the same event used by the KA9Q task synchronization.
40 */
41#define INTERRUPT_EVENT RTEMS_EVENT_1
42
43/*
44 * Receive buffer size -- Allow for a full ethernet packet plus a pointer
45 */
46#define RBUF_SIZE       (1520 + sizeof (struct iface *))
47
48/*
49 * Hardware-specific storage
50 */
51typedef struct  {
52  rtems_irq_connect_data        irqInfo;
53  struct mbuf                   **rxMbuf;
54  struct mbuf                   **txMbuf;
55  unsigned int                  port;
56  unsigned char                 *base;
57  unsigned long                 bpar;
58  int                           rxBdCount;
59  int                           txBdCount;
60  int                           txBdHead;
61  int                           txBdTail;
62  int                           txBdActiveCount;
63  struct iface                  *iface;
64  rtems_id                      txWaitTid;
65 
66  /*
67   * Statistics
68   */
69  unsigned long                 rxInterrupts;
70  unsigned long                 rxNotFirst;
71  unsigned long                 rxNotLast;
72  unsigned long                 rxGiant;
73  unsigned long                 rxNonOctet;
74  unsigned long                 rxRunt;
75  unsigned long                 rxBadCRC;
76  unsigned long                 rxOverrun;
77  unsigned long                 rxCollision;
78 
79  unsigned long                 txInterrupts;
80  unsigned long                 txDeferred;
81  unsigned long                 txHeartbeat;
82  unsigned long                 txLateCollision;
83  unsigned long                 txRetryLimit;
84  unsigned long                 txUnderrun;
85  unsigned long                 txLostCarrier;
86  unsigned long                 txRawWait;
87}wd80x3EnetDriver;
88
89#define RO 0x10
90
91#define SHATOT (8*1024)         /* size of shared memory */
92#define SHAPAGE 256             /* shared memory information */
93#define MAXSIZ  1536            /*(MAXBUF - MESSH_SZ)*/
94#define OUTPAGE ((SHATOT-(MAXSIZ+SHAPAGE-1))/SHAPAGE)
95
96static unsigned long loopc;
97
98static wd80x3EnetDriver wd8003EnetDriver[NSCCDRIVER];
99
100/*
101 * WD8003 interrupt handler. The code as it is cleraly showes that
102 * only one driver is connected. In order to change this a table
103 * making the correspondance between the current irq number and
104 * the corresponding wd8003EnetDriver structure could be used...
105 */
106static void wd8003Enet_interrupt_handler ()
107{
108  unsigned int tport, nowTicks, bootTicks;
109  unsigned char status, status2;
110
111  struct iface *iface = (struct iface *)(wd8003EnetDriver[0].iface);
112  wd80x3EnetDriver *dp = (wd80x3EnetDriver *)&wd8003EnetDriver[0];
113  struct mbuf *bp;
114  unsigned int i2;
115  unsigned int len;
116  unsigned char start, next, current;
117  char *shp, *temp;
118
119  tport = wd8003EnetDriver[0].port ;
120
121  /*
122   * Drop chips interrupt
123   */
124  outport_byte(tport+IMR, 0x00);
125
126  /*
127   * Read status
128   */
129  inport_byte(tport+ISR, status);
130
131  /*
132   * Ring overwrite
133   */
134
135  if (status & MSK_OVW){
136    outport_byte(tport+CMDR, MSK_STP + MSK_RD2);        /* stop 8390 */
137    rtems_clock_get(RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &bootTicks );
138    while(nowTicks < bootTicks+loopc)               /* 2ms delay */
139      rtems_clock_get(RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &nowTicks );
140    outport_byte(tport+RBCR0, 0);                       /* clear byte count */
141    outport_byte(tport+RBCR1, 0);
142    inport_byte(tport+ISR, status2);
143    status |= (status2 & (MSK_PTX+MSK_TXE)) ;   /* TX status */
144    outport_byte(tport+TCR, MSK_LOOP);          /* loopback mode */
145    outport_byte(tport+CMDR, MSK_STA + MSK_RD2);        /* start */
146  }
147 
148  /*
149   * Frame received?
150   */       
151  while (status & (MSK_PRX+MSK_RXE)) {
152    outport_byte(tport+ISR, status & (MSK_PRX+MSK_RXE));
153    inport_byte(tport+BNRY, start);
154    start += 1;
155    shp = dp->base + 1 + (SHAPAGE * start);
156    next = *shp++;
157    len = *((short *)shp)++ - 4;
158    if (start >= OUTPAGE || next >= OUTPAGE)
159      break;
160    bp = ambufw (RBUF_SIZE);
161    bp->data += sizeof (struct iface *);
162    temp = bp->data;
163    bp->cnt = len;
164
165    if ((i2 = (OUTPAGE - start) * SHAPAGE - 4) < len){
166      memcpy(temp, shp, i2);
167      len -= i2;
168      temp += i2;
169      shp = dp->base;
170    }
171    memcpy(temp, shp, len);
172
173    net_route (iface, &bp);
174    outport_byte(tport+BNRY, next-1);
175    outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
176    inport_byte(tport+CURR, current);
177    outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
178    if (current == next)
179      break;
180  }
181
182  /*
183   * Ring overwrite
184   */
185  if (status & MSK_OVW) {     
186    outport_byte(tport+ISR, MSK_OVW);   /* reset IR */
187    outport_byte(tport+TCR, 0);         /* out of loopback */
188    if ((status & (MSK_PTX+MSK_TXE)) == 0)
189      outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);      /* resend */
190  }
191
192  /*
193   * Enable chip interrupts
194   */
195  outport_byte(tport+IMR, 0x15);
196}
197
198static void nopOn(const rtems_irq_connect_data* notUsed)
199{
200  /*
201   * code should be moved from wd8003Enet_initialize_hardware
202   * to this location
203   */
204}
205
206static int wdIsOn(const rtems_irq_connect_data* irq)
207{
208  return pc386_irq_enabled_at_i8259s (irq->name);
209}
210
211/*
212 * Initialize the ethernet hardware
213 */
214static void
215wd8003Enet_initialize_hardware (wd80x3EnetDriver *dp, int broadcastFlag)
216{
217  int  i1, ultra;
218  char cc1, cc2;
219  unsigned char  temp;
220  rtems_status_code sc;
221  unsigned int tport;
222
223  tport = dp->port;
224
225  /* address from board ROM */
226  inport_byte(tport+0x04, temp);
227  outport_byte(tport+0x04, temp & 0x7f);
228
229  for (i1=cc2=0; i1<8; i1++) {
230    inport_byte(tport + ADDROM + i1, cc1);
231    cc2 += cc1;
232    if (i1 < 6)
233      dp->iface->hwaddr[i1] = cc1;
234  }
235 
236  inport_byte(tport+0x04, temp);
237  outport_byte(tport+0x04, temp | 0x80);        /* alternate registers */
238  outport_byte(tport+W83CREG, MSK_RESET);       /* reset board, set buffer */
239  outport_byte(tport+W83CREG, 0);
240  outport_byte(tport+W83CREG, MSK_ENASH + (int)((dp->bpar>>13)&0x3f));
241
242  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
243  cc1 = MSK_BMS + MSK_FT10; /* configure 8 or 16 bits */
244
245  inport_byte(tport+0x07, temp) ;
246
247  ultra = ((temp & 0xf0) == 0x20 || (temp & 0xf0) == 0x40);
248  if (ultra)
249    cc1 = MSK_WTS + MSK_BMS + MSK_FT10;
250  outport_byte(tport+DCR, cc1);
251  outport_byte(tport+RBCR0, 0);
252  outport_byte(tport+RBCR1, 0);
253  outport_byte(tport+RCR, MSK_MON);             /* disable the rxer */
254  outport_byte(tport+TCR, 0);                   /* normal operation */
255  outport_byte(tport+PSTOP, OUTPAGE);           /* init PSTOP */
256  outport_byte(tport+PSTART, 0);                /* init PSTART */
257  outport_byte(tport+BNRY, -1);                 /* init BNRY */
258  outport_byte(tport+ISR, -1);                  /* clear IR's */
259  outport_byte(tport+IMR, 0x15);                /* 0x17 enable interrupt */
260
261  outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
262
263  for (i1=0; i1<6; i1++)                        /* initial physical addr */
264    outport_byte(tport+PAR+i1, dp->iface->hwaddr[i1]);
265
266  for (i1=0; i1<MARsize; i1++)                  /* clear multicast */
267    outport_byte(tport+MAR+i1, 0);
268  outport_byte(tport+CURR, 0);                  /* init current packet */
269
270  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
271  outport_byte(tport+CMDR, MSK_STA + MSK_RD2);  /* put 8390 on line */
272  outport_byte(tport+RCR, MSK_AB);              /* MSK_AB accept broadcast */
273 
274  if (ultra) {
275    inport_byte(tport+0x0c, temp);
276    outport_byte(tport+0x0c, temp | 0x80);
277    outport_byte(tport+0x05, 0x80);
278    outport_byte(tport+0x06, 0x01);
279  }
280 
281  /*
282   * Set up interrupts
283   */
284  dp->irqInfo.hdl = wd8003Enet_interrupt_handler;
285  dp->irqInfo.on  = nopOn;
286  dp->irqInfo.off = nopOn;
287  dp->irqInfo.isOn = wdIsOn;
288 
289  sc = pc386_install_rtems_irq_handler (&dp->irqInfo);
290  if (!sc)
291    rtems_panic ("Can't attach WD interrupt handler for irq %d\n",
292                  dp->irqInfo.name);
293}
294
295
296/*
297 * Send raw packet (caller provides header).
298 * This code runs in the context of the interface transmit
299 * task or in the context of the network task.
300 */
301static int
302wd8003Enet_raw (struct iface *iface, struct mbuf **bpp)
303{
304  wd80x3EnetDriver *dp = &wd8003EnetDriver[iface->dev];
305  struct mbuf *bp;
306  unsigned int len, tport;
307  char *shp;
308
309  tport = dp->port;
310 
311  /*
312   * Fill in some logging data
313   */
314  iface->rawsndcnt++;
315  iface->lastsent = secclock ();
316  dump (iface, IF_TRACE_OUT, *bpp);
317 
318  /*
319   * It would not do to have two tasks active in the transmit
320   * loop at the same time.
321   * The blocking is simple-minded since the odds of two tasks
322   * simultaneously attempting to use this code are low.  The only
323   * way that two tasks can try to run here is:
324   *    1) Task A enters this code and ends up having to
325   *       wait for a transmit buffer descriptor.
326   *    2) Task B  gains control and tries to transmit a packet.
327   * The RTEMS/KA9Q scheduling semaphore ensures that there
328   * are no race conditions associated with manipulating the
329   * txWaitTid variable.
330   */
331
332  if (dp->txWaitTid) {
333    dp->txRawWait++;
334    while (dp->txWaitTid)
335      rtems_ka9q_ppause (10);
336  }
337 
338  if (dp->txWaitTid == 0)               
339    rtems_task_ident (0, 0, &dp->txWaitTid);
340
341  bp = *bpp;
342  len = 0;
343  shp = dp->base + (SHAPAGE * OUTPAGE);
344
345  /*rtems_interrupt_disable(level);*/
346 
347  for (;;){
348    len += bp->cnt;
349    memcpy(shp, (char *)bp->data, bp->cnt);
350    shp += bp->cnt ;
351    if ((bp = bp->next) == NULL)
352      break;
353  }
354
355  free_p(bpp);
356
357  if (len < ET_MINLEN) len = ET_MINLEN;
358  outport_byte(tport+TBCR0, len);
359  outport_byte(tport+TBCR1, (len >> 8) );
360  outport_byte(tport+TPSR, OUTPAGE);
361  outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);
362
363  /*
364   * Show that we've finished with the packet
365   */
366  dp->txWaitTid = 0;
367  return 0;
368 
369}
370
371
372/*
373 * Shut down the interface
374 * FIXME: This is a pretty simple-minded routine.  It doesn't worry
375 * about cleaning up mbufs, shutting down daemons, etc.
376 */
377static int
378wd8003Enet_stop (struct iface *iface)
379{
380  unsigned int tport;
381  unsigned char temp;
382  /*
383   * Stop the transmitter
384   */
385  tport=wd8003EnetDriver[0].port ;
386  inport_byte(tport+0x04,temp);
387  outport_byte(tport+0x04, temp & 0x7f);
388  outport_byte(tport + CMDR, MSK_STP + MSK_RD2);
389  return 0;
390}
391
392/*
393 * Show interface statistics
394 */
395static void
396wd8003Enet_show (struct iface *iface)
397{
398  printf ("      Rx Interrupts:%-8lu", wd8003EnetDriver[0].rxInterrupts);
399  printf ("       Not First:%-8lu", wd8003EnetDriver[0].rxNotFirst);
400  printf ("        Not Last:%-8lu\n", wd8003EnetDriver[0].rxNotLast);
401  printf ("              Giant:%-8lu", wd8003EnetDriver[0].rxGiant);
402  printf ("            Runt:%-8lu", wd8003EnetDriver[0].rxRunt);
403  printf ("       Non-octet:%-8lu\n", wd8003EnetDriver[0].rxNonOctet);
404  printf ("            Bad CRC:%-8lu", wd8003EnetDriver[0].rxBadCRC);
405  printf ("         Overrun:%-8lu", wd8003EnetDriver[0].rxOverrun);
406  printf ("       Collision:%-8lu\n", wd8003EnetDriver[0].rxCollision);
407  printf ("      Tx Interrupts:%-8lu", wd8003EnetDriver[0].txInterrupts);
408  printf ("        Deferred:%-8lu", wd8003EnetDriver[0].txDeferred);
409  printf (" Missed Hearbeat:%-8lu\n", wd8003EnetDriver[0].txHeartbeat);
410  printf ("         No Carrier:%-8lu", wd8003EnetDriver[0].txLostCarrier);
411  printf ("Retransmit Limit:%-8lu", wd8003EnetDriver[0].txRetryLimit);
412  printf ("  Late Collision:%-8lu\n", wd8003EnetDriver[0].txLateCollision);
413  printf ("           Underrun:%-8lu", wd8003EnetDriver[0].txUnderrun);
414  printf (" Raw output wait:%-8lu\n", wd8003EnetDriver[0].txRawWait);
415}
416
417/*
418 * Attach an WD8003 driver to the system
419 * This is the only `extern' function in the driver.
420 *
421 * argv[0]: interface label, e.g., "rtems"
422 * The remainder of the arguemnts are key/value pairs:
423 * mtu ##                  --  maximum transmission unit, default 1500
424 * broadcast y/n           -- accept or ignore broadcast packets, default yes
425 * rbuf ##                 -- Set number of receive buffer descriptors
426 * rbuf ##                 -- Set number of transmit buffer descriptors
427 * ip ###.###.###.###      -- IP address
428 * ether ##:##:##:##:##:## -- Ethernet address
429 * irno                    -- Set controller irq
430 * port                    -- Set io port
431 * bpar                    -- Set RAM address
432 */
433int
434rtems_ka9q_driver_attach (int argc, char *argv[], void *p)
435{
436  struct iface *iface;
437  wd80x3EnetDriver *dp;
438  char *cp;
439  int i;
440  int argIndex;
441  int broadcastFlag;
442  char cbuf[30];
443 
444  /*
445   * Find a free driver
446   */
447  for (i = 0 ; i < NSCCDRIVER ; i++) {
448    if (wd8003EnetDriver[i].iface == NULL)
449      break;
450  }
451  if (i >= NSCCDRIVER) {
452    printf ("Too many SCC drivers.\n");
453    return -1;
454  }
455  if (if_lookup (argv[0]) != NULL) {
456    printf ("Interface %s already exists\n", argv[0]);
457    return -1;
458  }
459  dp = &wd8003EnetDriver[i];
460 
461  /*
462   * Create an inteface descriptor
463   */
464  iface = callocw (1, sizeof *iface);
465  iface->name = strdup (argv[0]);
466 
467  /*
468   * Set default values
469   */
470  broadcastFlag = 1;
471  dp->txWaitTid = 0;
472  dp->rxBdCount = RX_BUF_COUNT;
473  dp->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF;
474  dp->irqInfo.name = (rtems_irq_symbolic_name) 5;
475  dp->port = 0x240;
476  dp->base = (unsigned char*) 0xD0000;
477  dp->bpar = 0xD0000;
478  iface->mtu = 1500;
479  iface->addr = Ip_addr;
480  iface->hwaddr = mallocw (EADDR_LEN);
481  memset (iface->hwaddr, 0x08, EADDR_LEN);
482 
483  /*
484   * Parse arguments
485   */
486  for (argIndex = 1 ; argIndex < (argc - 1) ; argIndex++) {
487    if (strcmp ("mtu", argv[argIndex]) == 0) {
488      iface->mtu = atoi (argv[++argIndex]);
489    }
490    else if (strcmp ("broadcast", argv[argIndex]) == 0) {
491      if (*argv[++argIndex] == 'n')
492        broadcastFlag = 0;
493    }
494    else if (strcmp ("rbuf", argv[argIndex]) == 0) {
495      dp->rxBdCount = atoi (argv[++argIndex]);
496    }
497    else if (strcmp ("tbuf", argv[argIndex]) == 0) {
498      dp->txBdCount = atoi (argv[++argIndex]) * TX_BD_PER_BUF;
499    }
500    else if (strcmp ("ip", argv[argIndex]) == 0) {
501      iface->addr = resolve (argv[++argIndex]);
502    }
503    else if (strcmp ("ether", argv[argIndex]) == 0) {
504      argIndex++;
505      gether (iface->hwaddr, argv[argIndex]);
506    }
507    else if (strcmp ("irno", argv[argIndex]) == 0) {
508      dp->irqInfo.name = (rtems_irq_symbolic_name) atoi (argv[++argIndex]);
509    }
510    else if (strcmp ("port", argv[argIndex]) == 0) {
511      sscanf(argv[++argIndex], "%x", &(dp->port));
512    }
513    else if (strcmp ("bpar", argv[argIndex]) == 0) {
514      sscanf(argv[++argIndex], "%x", (unsigned) &(dp->bpar));
515      dp->base = (unsigned char *)(dp->bpar);
516    }
517    else {
518      printf ("Argument %d (%s) is invalid.\n", argIndex, argv[argIndex]);
519      return -1;
520    }
521  }
522  printf ("Ethernet address: %s\n", pether (cbuf, iface->hwaddr));
523  printf ("Internet address: %s\n", inet_ntoa(iface->addr));
524  printf ("Irno: %X, port: %X, bpar: %X, base: %X\n",dp->irqInfo.name, dp->port,
525          (unsigned) dp->bpar, (unsigned) dp->base);
526  fflush(stdout);
527  /*   
528   * Fill in remainder of interface configuration
529   */   
530  iface->dev = i;
531  iface->raw = wd8003Enet_raw;
532  iface->stop = wd8003Enet_stop;
533  iface->show = wd8003Enet_show;
534  dp->iface = iface;
535  setencap (iface, "Ethernet");
536 
537  /*
538   * Set up SCC hardware
539   */
540  wd8003Enet_initialize_hardware (dp, broadcastFlag);
541  fflush(stdout);
542 
543  /*
544   * Chain onto list of interfaces
545   */
546  iface->next = Ifaces;
547  Ifaces = iface;
548   
549  /* calibrate a delay loop for 2 milliseconds */
550  rtems_clock_get(RTEMS_CLOCK_GET_TICKS_PER_SECOND, &loopc );
551  loopc /= 500;
552 
553  /*
554   * Start I/O daemons
555   */
556  cp = if_name (iface, " tx");
557  iface->txproc = newproc (cp, 1024, if_tx, iface->dev, iface, NULL, 0);
558  free (cp);
559  return 0;
560}       
561
562
563
564
565
566
567
Note: See TracBrowser for help on using the repository browser.