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