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

4.104.114.84.95
Last change on this file since dbaf51a was dbaf51a, checked in by Joel Sherrill <joel.sherrill@…>, on 07/23/98 at 22:13:10

Patch from Emmanuel Raguet <raguet@…>:

Here is a patch for KA9Q stack.

This patch contains an Ethernet Driver for Western Digital,
some fixes for the UDP and TCP protocols (for endian conversion)
and some little programs which allow the test of UDP and TCP
sockets via Ethernet.

I have tested that on an Intel machine. If someone can test it
on a big-endian machine, ...

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