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