]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/net/lwip_tcpip/v2_0/tests/nc_test_slave.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / net / lwip_tcpip / v2_0 / tests / nc_test_slave.c
1 //==========================================================================
2 //
3 //      tests/nc_test_slave.c
4 //
5 //      Network characterizations test (slave portion)
6 //
7 //==========================================================================
8 //####BSDCOPYRIGHTBEGIN####
9 //
10 // -------------------------------------------
11 //
12 // Portions of this software may have been derived from OpenBSD or other sources,
13 // and are covered by the appropriate copyright disclaimers included herein.
14 //
15 // -------------------------------------------
16 //
17 //####BSDCOPYRIGHTEND####
18 //==========================================================================
19 //#####DESCRIPTIONBEGIN####
20 //
21 // Author(s):    gthomas
22 // Contributors: gthomas
23 // Date:         2000-01-10
24 // Purpose:      
25 // Description:  
26 //              
27 //
28 //####DESCRIPTIONEND####
29 //
30 //==========================================================================
31
32 // Network characterization test code - slave portion
33 #include <cyg/kernel/kapi.h>
34 #include <cyg/hal/hal_arch.h>
35
36 #include "nc_test_framework.h"
37
38 #include <cyg/infra/diag.h>
39
40 #include <lwip/inet.h>
41 #include <lwip/arch.h>
42 #include <lwip/sys.h>
43 #define LWIP_TIMEVAL_PRIVATE 1
44 #include <lwip/sockets.h>
45 #include <cyg/infra/testcase.h>
46
47 #ifndef CYGPKG_LIBC_STDIO
48 #include <cyg/error/errno.h>
49 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
50 #else
51 #include <stdio.h>
52 #endif
53 #define STACK_SIZE               (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
54 #define MAX_LOAD_THREAD_LEVEL    20
55 #define MIN_LOAD_THREAD_LEVEL    0
56 #define NUM_LOAD_THREADS         10
57 #define CYGPKG_NET_THREAD_PRIORITY 7
58 #define IDLE_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY+3
59 #define LOAD_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY-1
60 #define MAIN_THREAD_PRIORITY     CYGPKG_NET_THREAD_PRIORITY-2
61 #define DESIRED_BACKGROUND_LOAD  20
62 #define CYGHWR_NET_DRIVERS 1
63
64 #if SO_REUSE
65 #ifdef CYGPKG_LWIP_TCP
66 #ifdef CYGPKG_LWIP_UDP
67
68
69 #if 0
70 static char         main_thread_stack[CYGHWR_NET_DRIVERS][STACK_SIZE];
71 static cyg_thread   main_thread_data[CYGHWR_NET_DRIVERS];
72 static cyg_handle_t main_thread_handle[CYGHWR_NET_DRIVERS];
73 #endif
74
75
76 static char         idle_thread_stack[STACK_SIZE];
77 static cyg_thread   idle_thread_data;
78 static cyg_handle_t idle_thread_handle;
79 static cyg_sem_t    idle_thread_sem;
80 volatile static long long    idle_thread_count;
81 static cyg_tick_count_t idle_thread_start_time;
82 static cyg_tick_count_t idle_thread_stop_time;
83 static char         load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE];
84 static cyg_thread   load_thread_data[NUM_LOAD_THREADS];
85 static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS];
86 static cyg_sem_t    load_thread_sem[NUM_LOAD_THREADS];
87 static long         load_thread_level;
88 static void calibrate_load(int load);
89 static void start_load(int load);
90 static void do_some_random_computation(int p);
91 #define abs(n) ((n) < 0 ? -(n) : (n))
92
93 #define test_param_t cyg_addrword_t
94 #ifdef CYGDBG_NET_TIMING_STATS
95 extern void show_net_times(void);
96 #endif
97
98 #define MAX_BUF 8192
99 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
100
101 extern void
102 cyg_test_exit(void);
103
104 static void
105 test_delay(int ticks)
106 {
107     cyg_thread_delay(ticks);
108 }
109
110
111 void
112 pexit(char *s)
113 {
114     perror(s);
115 #ifdef CYGDBG_NET_TIMING_STATS
116     show_net_times();
117 #endif
118     cyg_test_exit();
119 }
120
121 //
122 // Generic UDP test
123 //
124
125 static void
126 do_udp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
127 {
128     int i, s, td_len, seq, seq_errors, lost;
129     struct sockaddr_in test_chan_slave, test_chan_master;
130     fd_set fds;
131     struct timeval timeout;
132     struct nc_test_results results;
133     struct nc_test_data *tdp;
134     int nsent, nrecvd;
135     int need_recv, need_send;
136
137     need_recv = true;  need_send = true;
138     switch (ntohl(req->type)) {
139     case NC_REQUEST_UDP_SEND:
140         need_recv = false;
141         need_send = true;
142         break;
143     case NC_REQUEST_UDP_RECV:
144         need_recv = true;
145         need_send = false;
146         break;
147     case NC_REQUEST_UDP_ECHO:
148         break;
149     }
150
151     s = socket(AF_INET, SOCK_DGRAM, 0);
152     if (s < 0) {
153         pexit("datagram socket");
154     }
155
156     memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
157     test_chan_slave.sin_family = AF_INET;
158     test_chan_slave.sin_len = sizeof(test_chan_slave);
159     test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
160     test_chan_slave.sin_port = htons(ntohl(req->slave_port));
161     
162     if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
163         perror("bind");
164         close(s);
165     }
166
167     memcpy(&test_chan_master, master, sizeof(*master));
168     test_chan_master.sin_port = htons(ntohl(req->master_port));
169     nsent = 0;  nrecvd = 0;  seq = 0;  seq_errors = 0;  lost = 0;
170     for (i = 0;  i < ntohl(req->nbufs);  i++) {
171         if (need_recv) {
172             FD_ZERO(&fds);
173             FD_SET(s, &fds);
174             timeout.tv_sec = NC_TEST_TIMEOUT;
175             timeout.tv_usec = 0;
176             if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
177                 test_printf("recvfrom timeout, expecting seq #%d\n", seq);            
178                 if (++lost > MAX_ERRORS) {
179                     test_printf("... giving up\n");
180                     break;
181                 }
182             } else {
183                 nrecvd++;
184                 tdp = (struct nc_test_data *)in_buf;
185                 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
186                 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
187                     perror("recvfrom");
188                     close(s);
189                     return;
190                 }
191                 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
192                     (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
193                     if (ntohl(tdp->seq) != seq) {
194                         test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
195                                     ntohl(tdp->seq), seq);
196                         seq = ntohl(tdp->seq);
197                         seq_errors++;
198                     }
199                 } else {
200                     test_printf("Bad data packet - key: %lx/%lx, seq: %d\n",
201                                 ntohl(tdp->key1), ntohl(tdp->key2),
202                                 ntohl(tdp->seq));
203                 }
204             }
205         }
206         if (need_send) {
207             int retries = 10;
208             int  sent = false;
209             int res;
210
211             tdp = (struct nc_test_data *)out_buf;
212             tdp->key1 = htonl(NC_TEST_DATA_KEY1);
213             tdp->key2 = htonl(NC_TEST_DATA_KEY2);
214             tdp->seq = htonl(seq);
215             td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
216             tdp->len = htonl(td_len);
217             while (!sent && (--retries >= 0)) {
218                 res = sendto(s, tdp, td_len, 0, 
219                              (struct sockaddr *)&test_chan_master, sizeof(test_chan_master));
220                 if (res > 0) {
221                     sent = true;
222                     break;
223                 }
224                 if (errno == ENOBUFS) {
225                     // Saturated the system
226                     test_delay(1);   // Time for 200 500 byte 10-baseT packets 
227                 } else {
228                     // What else to do?
229                     close(s);
230                     return;
231                 }
232             }
233             if (sent) {
234                 nsent++;
235             } else {
236                 perror("sendto");
237             }
238         }
239         seq++;
240     }
241     results.key1 = htonl(NC_TEST_RESULT_KEY1);
242     results.key2 = htonl(NC_TEST_RESULT_KEY2);
243     results.seq = req->seq;
244     results.nsent = htonl(nsent);
245     results.nrecvd = htonl(nrecvd);
246     if (sendto(s, &results, sizeof(results), 0, 
247                (struct sockaddr *)&test_chan_master, sizeof(test_chan_master)) < 0) {
248         perror("sendto results");
249     }
250     close(s);
251 }
252
253 //
254 // Read data from a stream, accounting for the fact that packet 'boundaries'
255 // are not preserved.  This can also timeout (which would probably wreck the
256 // data boundaries).
257 //
258
259 int
260 do_read(int fd, void *buf, int buflen)
261 {
262     char *p = (char *)buf;
263     int len = buflen;
264     int res;
265     while (len) {
266         res = read(fd, p, len);
267         if (res < 0) {
268             perror("read");
269         } else {
270             len -= res;
271             p += res;
272             if (res == 0) {
273                 break;
274             }
275         }
276     }
277     return (buflen - len);
278 }
279
280 //
281 // Generic TCP test
282 //
283
284 static void
285 do_tcp_test(int s1, struct nc_request *req, struct sockaddr_in *master)
286 {
287     int i, s, len, td_len, seq, seq_errors, lost, test_chan, res;
288     struct sockaddr_in test_chan_slave, test_chan_master;
289     struct nc_test_results results;
290     struct nc_test_data *tdp;
291     int nsent, nrecvd;
292     int need_recv, need_send;
293     int one = 1;
294     static int slave_tcp_port = -1;
295
296     need_recv = true;  need_send = true;
297     switch (ntohl(req->type)) {
298     case NC_REQUEST_TCP_SEND:
299         need_recv = false;
300         need_send = true;
301         break;
302     case NC_REQUEST_TCP_RECV:
303         need_recv = true;
304         need_send = false;
305         break;
306     case NC_REQUEST_TCP_ECHO:
307         break;
308     }
309
310     if (slave_tcp_port < 0) {
311         s = socket(AF_INET, SOCK_STREAM, 0);
312         if (s < 0) {
313             pexit("datagram socket");
314         }
315
316         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
317             perror("setsockopt SO_REUSEADDR");
318             return;
319         }
320 #ifdef SO_REUSEPORT
321         if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
322             perror("setsockopt SO_REUSEPORT");
323             return;
324         }
325 #endif
326         memset((char *) &test_chan_slave, 0, sizeof(test_chan_slave));
327         test_chan_slave.sin_family = AF_INET;
328         test_chan_slave.sin_len = sizeof(test_chan_slave);
329         test_chan_slave.sin_addr.s_addr = htonl(INADDR_ANY);
330         test_chan_slave.sin_port = htons(ntohl(req->slave_port));
331     
332         if (bind(s, (struct sockaddr *) &test_chan_slave, sizeof(test_chan_slave)) < 0) {
333             perror("bind");
334             close(s);
335         }
336         listen(s, 128);
337         slave_tcp_port = s;
338     }
339
340     s = slave_tcp_port;
341     len = sizeof(test_chan_master);
342     if ((test_chan = accept(s, (struct sockaddr *)&test_chan_master, &len)) < 0) {
343         pexit("accept");
344     }
345     len = sizeof(test_chan_master);
346     getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len);
347 //    test_printf("connection from %s.%d\n", inet_ntoa(test_chan_master.sin_addr), 
348   //              ntohs(test_chan_master.sin_port));
349
350     nsent = 0;  nrecvd = 0;  seq = 0;  seq_errors = 0;  lost = 0;
351     for (i = 0;  i < ntohl(req->nbufs);  i++) {
352         if (need_recv) {
353             tdp = (struct nc_test_data *)in_buf;
354             td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
355             res = do_read(test_chan, tdp, td_len);
356             if (res != td_len) {
357                 test_printf("recvfrom timeout, expecting seq #%d\n", seq);            
358                 if (++lost > MAX_ERRORS) {
359                     test_printf("... giving up\n");
360                     break;
361                 }
362             } else {
363                 nrecvd++;
364                 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
365                     (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
366                     if (ntohl(tdp->seq) != seq) {
367                         test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
368                                     ntohl(tdp->seq), seq);
369                         seq = ntohl(tdp->seq);
370                         seq_errors++;
371                     }
372                 } else {
373                     test_printf("Bad data packet - key: %lx/%lx, seq: %d\n",
374                                 ntohl(tdp->key1), ntohl(tdp->key2),
375                                 ntohl(tdp->seq));
376                 }
377             }
378         }
379         if (need_send) {
380             tdp = (struct nc_test_data *)out_buf;
381             tdp->key1 = htonl(NC_TEST_DATA_KEY1);
382             tdp->key2 = htonl(NC_TEST_DATA_KEY2);
383             tdp->seq = htonl(seq);
384             td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
385             tdp->len = htonl(td_len);
386             if (write(test_chan, tdp, td_len) != td_len) {
387                 perror("write");
388                 if (errno == ENOBUFS) {
389                     // Saturated the system
390                     test_delay(25);
391                 } else {
392                     // What else to do?
393                     close(test_chan);
394                     return;
395                 }
396             } else {
397                 nsent++;
398             }
399         }
400         seq++;
401     }
402     results.key1 = htonl(NC_TEST_RESULT_KEY1);
403     results.key2 = htonl(NC_TEST_RESULT_KEY2);
404     results.seq = req->seq;
405     results.nsent = htonl(nsent);
406     results.nrecvd = htonl(nrecvd);
407     if (write(test_chan, &results, sizeof(results)) != sizeof(results)) {
408         perror("write");
409     }
410     close(test_chan);
411 }
412
413 //
414 // Protocol driver for testing slave.
415 //
416 // This function is the main routine running here, handling requests sent from
417 // the master and providing various responses.
418 //
419 static void
420 nc_slave(test_param_t param)
421 {
422     int s, masterlen;
423     struct sockaddr_in my_addr, master;
424     struct nc_request req;
425     struct nc_reply reply;
426     int done = false;
427
428     test_printf("Start test for eth%d\n", param);
429
430     s = socket(AF_INET, SOCK_DGRAM, 0);
431     if (s < 0) {
432         pexit("datagram socket");
433     }
434
435     memset((char *) &my_addr, 0, sizeof(my_addr));
436     my_addr.sin_family = AF_INET;
437     my_addr.sin_len = sizeof(my_addr);
438     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
439     my_addr.sin_port = htons(NC_SLAVE_PORT);
440     
441     if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
442         pexit("bind");
443     }
444
445     while (!done) {
446         masterlen = sizeof(master);
447         if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) {
448             pexit("recvfrom");
449         }
450 #if 0
451         test_printf("Request %d from %s:%d\n", ntohl(req.type), 
452                     inet_ntoa(master.sin_addr), ntohs(master.sin_port));
453 #endif
454         reply.response = htonl(NC_REPLY_ACK);
455         reply.seq = req.seq;
456         switch (ntohl(req.type)) {
457         case NC_REQUEST_DISCONNECT:
458             done = true;
459             break;
460         case NC_REQUEST_UDP_SEND:
461             test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
462             break;
463         case NC_REQUEST_UDP_RECV:
464             test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
465             break;
466         case NC_REQUEST_UDP_ECHO:
467             test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
468             break;
469         case NC_REQUEST_TCP_SEND:
470             test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
471             break;
472         case NC_REQUEST_TCP_RECV:
473             test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
474             break;
475         case NC_REQUEST_TCP_ECHO:
476             test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
477             break;
478         case NC_REQUEST_SET_LOAD:
479             start_load(ntohl(req.nbufs));
480             break;
481         case NC_REQUEST_START_IDLE:
482             test_printf("Start IDLE thread\n");
483             idle_thread_count = 0;
484             idle_thread_start_time = cyg_current_time();
485             cyg_semaphore_post(&idle_thread_sem);
486             break;
487         case NC_REQUEST_STOP_IDLE:
488             cyg_semaphore_wait(&idle_thread_sem);
489             idle_thread_stop_time = cyg_current_time();
490             test_printf("Stop IDLE thread\n");
491             reply.misc.idle_results.elapsed_time = htonl(idle_thread_stop_time - idle_thread_start_time);
492             reply.misc.idle_results.count[0] = htonl(idle_thread_count >> 32);
493             reply.misc.idle_results.count[1] = htonl((long)idle_thread_count);
494             break;
495         default:
496             test_printf("Unrecognized request: %d\n", ntohl(req.type));
497             reply.response = htonl(NC_REPLY_NAK);
498             reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST);
499             break;
500         }
501         if (sendto(s, &reply, sizeof(reply), 0, (struct sockaddr *)&master, masterlen) < 0) {
502             pexit("sendto");
503         }
504         if (reply.response == ntohl(NC_REPLY_NAK)) {
505             continue;
506         }
507         switch (ntohl(req.type)) {
508         case NC_REQUEST_UDP_SEND:
509         case NC_REQUEST_UDP_RECV:
510         case NC_REQUEST_UDP_ECHO:
511             do_udp_test(s, &req, &master);
512             break;
513         case NC_REQUEST_TCP_SEND:
514         case NC_REQUEST_TCP_RECV:
515         case NC_REQUEST_TCP_ECHO:
516             do_tcp_test(s, &req, &master);
517             break;
518         case NC_REQUEST_START_IDLE:
519         case NC_REQUEST_STOP_IDLE:
520         case NC_REQUEST_SET_LOAD:
521         default:
522             break;
523         }
524     }
525     close(s);
526 }
527
528 void
529 net_test(void *arg)
530 {
531     test_param_t param = (test_param_t)arg;
532 //    int i;
533     if (param == 0) {
534         test_printf("Start Network Characterization - SLAVE\n");
535         calibrate_load(DESIRED_BACKGROUND_LOAD);
536     }
537     nc_slave(param);
538 #ifdef CYGDBG_NET_TIMING_STATS
539     show_net_times();
540 #endif
541 #if LWIP_STATS_DISPLAY 
542     stats_display();
543 #endif    
544     cyg_test_exit();
545 }
546
547
548 //
549 // This function is called to calibrate the "background load" which can be
550 // applied during testing.  It will be called before any commands from the
551 // host are managed.
552 //
553 static void
554 calibrate_load(int desired_load)
555 {
556     long long no_load_idle, load_idle;
557     int percent_load;
558     int high, low;
559
560     // Set limits
561     high = MAX_LOAD_THREAD_LEVEL;
562     low = MIN_LOAD_THREAD_LEVEL;
563         test_printf("Start Network Characterization - SLAVE\n");
564
565     // Compute the "no load" idle value
566     idle_thread_count = 0;
567     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
568         test_printf("Start Network Characterization - SLAVE\n");
569     cyg_thread_delay(1*100);               // Pause for one second
570         test_printf("Start Network Characterization - SLAVE\n");
571     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
572         test_printf("Start Network Characterization - SLAVE\n");
573     no_load_idle = idle_thread_count;
574     diag_printf("No load = %d\n", (int)idle_thread_count);
575
576     // First ensure that the HIGH level is indeed higher
577     while (true) {
578         load_thread_level = high;
579         start_load(desired_load);              // Start up a given load
580         idle_thread_count = 0;
581         cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
582         cyg_thread_delay(1*100);               // Pause for one second
583         cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
584         load_idle = idle_thread_count;
585         start_load(0);                         // Shut down background load
586         percent_load = 100 - ((load_idle * 100) / no_load_idle);
587         diag_printf("High Load[%ld] = %d => %d%%\n", load_thread_level, 
588                     (int)idle_thread_count, percent_load);
589         if ( percent_load > desired_load )
590             break; // HIGH level is indeed higher
591         low = load_thread_level; // known to be lower
592         high *= 2; // else double it and try again
593     }
594
595     // Now chop down to the level required
596     while (true) {
597         load_thread_level = (high + low) / 2;
598         start_load(desired_load);              // Start up a given load
599         idle_thread_count = 0;
600         cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
601         cyg_thread_delay(1*100);               // Pause for one second
602         cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
603         load_idle = idle_thread_count;
604         start_load(0);                         // Shut down background load
605         percent_load = 100 - ((load_idle * 100) / no_load_idle);
606         diag_printf("Load[%ld] = %d => %d%%\n", load_thread_level, 
607                     (int)idle_thread_count, percent_load);
608         if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break;
609         if (percent_load < desired_load) {
610             low = load_thread_level;
611         } else {            
612             high = load_thread_level;
613         }
614     }
615
616     // Now we are within a few percent of the target; scale the load
617     // factor to get a better fit, and test it, print the answer.
618     load_thread_level *= desired_load;
619     load_thread_level /= percent_load;
620     start_load(desired_load);              // Start up a given load
621     idle_thread_count = 0;
622     cyg_semaphore_post(&idle_thread_sem);  // Start idle thread
623     cyg_thread_delay(1*100);               // Pause for one second
624     cyg_semaphore_wait(&idle_thread_sem);  // Stop idle thread
625     load_idle = idle_thread_count;
626     start_load(0);                         // Shut down background load
627     percent_load = 100 - ((load_idle * 100) / no_load_idle);
628     diag_printf("Final load[%ld] = %d => %d%%\n", load_thread_level, 
629                 (int)idle_thread_count, percent_load);
630 //    no_load_idle_count_1_second = no_load_idle;
631 }
632
633 //
634 // This function is called to set up a load level of 'load' percent (given
635 // as a whole number, e.g. start_load(20) would mean initiate a background
636 // load of 20%, leaving the cpu 80% idle).
637 //
638 static void
639 start_load(int load)
640 {
641     static int prev_load = 0;
642     int i;
643     test_printf("Set background load = %d%%\n", load);
644     if (load == 0) {
645         if (prev_load == 0) return;  // Nothing out there to stop
646         for (i = 0;  i < prev_load/10;  i++) {
647             cyg_semaphore_wait(&load_thread_sem[i]);
648         }
649         prev_load = 0;
650     } else {
651         for (i = 0;  i < load/10;  i++) {
652             cyg_semaphore_post(&load_thread_sem[i]);
653         }
654         prev_load = load;
655     }
656 }
657
658 //
659 // These thread(s) do some amount of "background" computing.  This is used
660 // to simulate a given load level.  They need to be run at a higher priority 
661 // than the network code itself.
662 //
663 // Like the "idle" thread, they run as long as their "switch" (aka semaphore)
664 // is enabled.
665 //
666 void
667 net_load(cyg_addrword_t who)
668 {
669     int i;
670     while (true) {
671         cyg_semaphore_wait(&load_thread_sem[who]);
672         for (i = 0;  i < load_thread_level;  i++) {
673             do_some_random_computation(i);
674         }
675         cyg_thread_delay(1);  // Wait until the next 'tick'
676         cyg_semaphore_post(&load_thread_sem[who]);
677     }
678 }
679
680 //
681 // Some arbitrary computation, designed to use up the CPU and cause associated
682 // cache "thrash" behaviour - part of background load modelling.
683 //
684 static void
685 do_some_random_computation(int p)
686 {
687     // Just something that might be "hard"
688     volatile double x;
689     x = ((p * 10) * 3.14159) / 180.0;  // radians
690 }
691
692 //
693 // This thread does nothing but count.  It will be allowed to count
694 // as long as the semaphore is "free".  
695 //
696 void
697 net_idle(cyg_addrword_t param)
698 {
699     while (true) {
700         cyg_semaphore_wait(&idle_thread_sem);
701         idle_thread_count++;
702         cyg_semaphore_post(&idle_thread_sem);
703     }
704 }
705 #if 0
706 void
707 cyg_start(void *n)
708 {
709     int i;
710     // Create processing threads
711     for (i = 0;  i < CYGHWR_NET_DRIVERS;  i++) {
712         cyg_thread_create(MAIN_THREAD_PRIORITY,     // Priority
713                           net_test,                 // entry
714                           i,                        // entry parameter
715                           "Network test",           // Name
716                           &main_thread_stack[i][0], // Stack
717                           STACK_SIZE,               // Size
718                           &main_thread_handle[i],   // Handle
719                           &main_thread_data[i]      // Thread data structure
720             );
721     }
722     cyg_thread_resume(main_thread_handle[0]);   // Start first one
723     // Create the idle thread environment
724     cyg_semaphore_init(&idle_thread_sem, 0);
725     cyg_thread_create(IDLE_THREAD_PRIORITY,     // Priority
726                       net_idle,                 // entry
727                       0,                        // entry parameter
728                       "Network idle",           // Name
729                       &idle_thread_stack[0],    // Stack
730                       STACK_SIZE,               // Size
731                       &idle_thread_handle,      // Handle
732                       &idle_thread_data         // Thread data structure
733             );
734     cyg_thread_resume(idle_thread_handle);      // Start it
735     // Create the load threads and their environment(s)
736     for (i = 0;  i < NUM_LOAD_THREADS;  i++) {
737         cyg_semaphore_init(&load_thread_sem[i], 0);
738         cyg_thread_create(LOAD_THREAD_PRIORITY,     // Priority
739                           net_load,                 // entry
740                           i,                        // entry parameter
741                           "Background load",        // Name
742                           &load_thread_stack[i][0], // Stack
743                           STACK_SIZE,               // Size
744                           &load_thread_handle[i],   // Handle
745                           &load_thread_data[i]      // Thread data structure
746             );
747         cyg_thread_resume(load_thread_handle[i]);   // Start it
748     }
749     cyg_scheduler_start();
750 }
751
752 #endif
753
754 void
755 tmain(cyg_addrword_t p)
756 {
757   lwip_init();
758   sys_thread_new(net_test, 0, MAIN_THREAD_PRIORITY);
759 }
760
761 static char stack[STACK_SIZE];
762 static cyg_thread thread_data;
763 static cyg_handle_t thread_handle;
764
765 void
766 nc_slave_main(void)
767 {
768         int i;  
769         
770         CYG_TEST_INIT();
771    // Create the idle thread environment
772     cyg_semaphore_init(&idle_thread_sem, 0);
773     cyg_thread_create(IDLE_THREAD_PRIORITY,     // Priority
774                       net_idle,                 // entry
775                       0,                        // entry parameter
776                       "Network idle",           // Name
777                       &idle_thread_stack[0],    // Stack
778                       STACK_SIZE,               // Size
779                       &idle_thread_handle,      // Handle
780                       &idle_thread_data         // Thread data structure
781             );
782     cyg_thread_resume(idle_thread_handle);      // Start it
783     // Create the load threads and their environment(s)
784     for (i = 0;  i < NUM_LOAD_THREADS;  i++) {
785         cyg_semaphore_init(&load_thread_sem[i], 0);
786         cyg_thread_create(LOAD_THREAD_PRIORITY,     // Priority
787                           net_load,                 // entry
788                           i,                        // entry parameter
789                           "Background load",        // Name
790                           &load_thread_stack[i][0], // Stack
791                           STACK_SIZE,               // Size
792                           &load_thread_handle[i],   // Handle
793                           &load_thread_data[i]      // Thread data structure
794             );
795         cyg_thread_resume(load_thread_handle[i]);   // Start it
796     }
797     // Create a main thread, so we can run the scheduler and have time 'pass'
798     cyg_thread_create(10,                // Priority - just a number
799                       tmain,          // entry
800                       0,                 // entry parameter
801                       "socket echo test",        // Name
802                       &stack[0],         // Stack
803                       STACK_SIZE,        // Size
804                       &thread_handle,    // Handle
805                       &thread_data       // Thread data structure
806             );
807     cyg_thread_resume(thread_handle);  // Start it
808     cyg_scheduler_start();
809     CYG_TEST_FAIL_FINISH("Not reached");
810 }
811
812
813 externC void
814 cyg_start( void )
815 {
816     nc_slave_main();
817 }
818
819 #else // def CYGPKG_LWIP_UDP
820 #define N_A_MSG "UDP support disabled"
821 #endif // def CYGPKG_LWIP_UDP
822 #else // def CYGPKG_LWIP_TCP
823 #define N_A_MSG "TCP support disabled"
824 #endif // def CYGPKG_LWIP_TCP
825 #else // SO_REUSE
826 #define N_A_MSG "SO_REUSE currently unavailable"
827 #endif // SO_REUSE
828
829 #ifdef N_A_MSG
830 externC void
831 cyg_start( void )
832 {
833     CYG_TEST_INIT();
834     CYG_TEST_NA(N_A_MSG);
835 }
836 #endif // N_A_MSG