39f7a568000f564c18b8033a823e6b3854e206e1
[karo-tx-redboot.git] / packages / net / common / v2_0 / tests / nc_test_master.c
1 //==========================================================================
2 //
3 //      tests/nc_test_master.c
4 //
5 //      Network characterizations test (master 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 - master portion
33
34 #include "nc_test_framework.h"
35
36 #ifdef __ECOS
37 #ifndef CYGPKG_LIBC_STDIO
38 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
39 #endif
40 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
41 static char stack[STACK_SIZE];
42 static cyg_thread thread_data;
43 static cyg_handle_t thread_handle;
44 #endif
45
46 struct test_params {
47     int  argc;
48     char **argv;
49 };
50
51 #define MAX_BUF 32*1024
52 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
53
54 static int test_seq = 1;
55 static long long idle_count;
56 static long      idle_ticks;
57 #define IDLE_TEST_TIME   10
58
59 struct pause {
60     int pause_ticks;
61     int pause_threshold;
62 };
63 #define LENGTH(x) (sizeof(x)/sizeof(x[0]))
64
65 #ifdef __ECOS
66 extern void
67 cyg_test_exit(void);
68 #else
69 #include <stdlib.h>
70 void
71 cyg_test_exit(void)
72 {
73     test_printf("... Done\n");
74     exit(1);
75 }
76 #endif
77
78 #ifdef __ECOS
79 static void
80 test_delay(int ticks)
81 {
82     cyg_thread_delay(ticks);
83 }
84
85 #else
86
87 static void
88 test_delay(int ticks)
89 {
90     usleep(ticks * 10000);
91 }
92 #endif
93
94 void
95 pexit(char *s)
96 {
97     perror(s);
98     cyg_test_exit();
99 }
100
101 #ifdef __ECOS
102 # ifdef CYGPKG_POSIX
103 # #include <pkgconf/posix.h>
104 # endif
105 # ifndef CYGPKG_POSIX_TIMERS
106 int
107 gettimeofday(struct timeval *tv, struct timezone *tz)
108 {
109     cyg_tick_count_t cur_time;
110     cur_time = cyg_current_time();
111     tv->tv_sec = cur_time / 100;
112     tv->tv_usec = (cur_time % 100) * 10000;
113 }
114 # endif
115 #endif
116
117 void
118 show_results(const char *msg, struct timeval *start, 
119              struct timeval *end, int nbufs, int buflen,
120              int lost, int seq_errors)
121 {
122     struct timeval tot_time;
123 #ifndef __ECOS
124     double real_time, thru;
125     long tot_bytes = nbufs * buflen;
126 #endif
127     timersub(end, start, &tot_time);
128     test_printf("%s - %d bufs of %d bytes in %d.%02d seconds",
129                 msg, nbufs, buflen, 
130                 tot_time.tv_sec, tot_time.tv_usec / 10000);
131 #ifndef __ECOS
132     real_time = tot_time.tv_sec + ((tot_time.tv_usec / 10000) * .01);
133     // Compute bytes / second (rounded up)
134     thru = tot_bytes / real_time;
135     // Convert to Mb / second
136     test_printf(" - %.2f KB/S", thru / 1024.0);
137     test_printf(" - %.4f Mbit/S (M = 10^6)", thru * 8.0 / 1000000.0);
138 #endif
139     if (lost) {
140         test_printf(", %d lost", lost);
141     }
142     if (seq_errors) {
143         test_printf(", %d out of sequence", seq_errors);
144     }
145     test_printf("\n");
146 }
147
148 void
149 new_test(void)
150 {
151     test_seq++;
152 }
153
154 int
155 nc_message(int s, struct nc_request *req, 
156            struct nc_reply *reply, struct sockaddr_in *slave)
157 {
158     int len;
159     fd_set fds;
160     struct timeval timeout;
161     len = sizeof(*slave);
162     req->seq = htonl(test_seq);
163     if (sendto(s, req, sizeof(*req), 0, (struct sockaddr *)slave, len) < 0) {
164         perror("sendto");
165         return false;
166     }
167     FD_ZERO(&fds);
168     FD_SET(s, &fds);
169     timeout.tv_sec = NC_REPLY_TIMEOUT;
170     timeout.tv_usec = 0;
171     if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
172         test_printf("No response to command\n");
173         return false;
174     }
175     if (recvfrom(s, reply, sizeof(*reply), 0, 0, 0) < 0) {
176         perror("recvfrom");
177         return false;
178     }
179     if (reply->seq != req->seq) {
180         test_printf("Response out of order - sent: %d, recvd: %d\n",
181                     ntohl(req->seq), ntohl(reply->seq));
182         return false;
183     }
184     return true;
185 }
186
187 void
188 show_test_results(struct nc_test_results *results)
189 {
190     if ((ntohl(results->key1) == NC_TEST_RESULT_KEY1) &&
191         (ntohl(results->key2) == NC_TEST_RESULT_KEY2) &&
192         (ntohl(results->seq) == test_seq)) {
193         test_printf("   slave sent %d, recvd %d\n", 
194                     ntohl(results->nsent), ntohl(results->nrecvd));
195     } else {
196         test_printf("   ... invalid results - keys: %x/%x, seq: %d/%d\n",
197                     ntohl(results->key1), ntohl(results->key2),
198                     ntohl(results->seq), test_seq);
199     }
200 }
201
202 void
203 do_udp_test(int s1, int type, struct sockaddr_in *slave,
204             int nbufs, int buflen, int pause_time, int pause_threshold)
205 {
206     int i, s, td_len, seq, seq_errors, total_packets;
207     struct sockaddr_in test_chan_master, test_chan_slave;
208     struct timeval start_time, end_time;
209     struct nc_request req;
210     struct nc_reply reply;
211     struct nc_test_results results;
212     struct nc_test_data *tdp;
213     fd_set fds;
214     struct timeval timeout;
215     int lost_packets = 0;
216     int need_send, need_recv;
217     const char *type_name;
218     int pkt_ctr = 0;
219
220     need_recv = true;  need_send = true;  type_name = "UDP echo";
221     switch (type) {
222     case NC_REQUEST_UDP_RECV:
223         need_recv = false;
224         need_send = true;
225         type_name = "UDP recv";
226         break;
227     case NC_REQUEST_UDP_SEND:
228         need_recv = true;
229         need_send = false;
230         type_name = "UDP send";
231         break;
232     case NC_REQUEST_UDP_ECHO:
233         break;
234     }
235
236     new_test();
237     req.type = htonl(type);
238     req.nbufs = htonl(nbufs);
239     req.buflen = htonl(buflen);
240     req.slave_port = htonl(NC_TESTING_SLAVE_PORT);
241     req.master_port = htonl(NC_TESTING_MASTER_PORT);
242     nc_message(s1, &req, &reply, slave);
243     if (reply.response != ntohl(NC_REPLY_ACK)) {
244         test_printf("Slave denied %s [%d,%d] test\n", type_name, nbufs, buflen);
245         return;
246     }
247
248     s = socket(AF_INET, SOCK_DGRAM, 0);
249     if (s < 0) {
250         pexit("datagram socket");
251     }
252
253     memset(&test_chan_master, 0, sizeof(test_chan_master));
254     test_chan_master.sin_family = AF_INET;
255 #ifdef __ECOS
256     test_chan_master.sin_len = sizeof(test_chan_master);
257 #endif
258     test_chan_master.sin_port = htons(ntohl(req.master_port));
259     test_chan_master.sin_addr.s_addr = INADDR_ANY;
260
261     if (bind(s, (struct sockaddr *) &test_chan_master, sizeof(test_chan_master)) < 0) {
262         perror("bind");
263         close(s);
264         return;
265     }
266     test_printf("Start %s [%d,%d]", type_name, nbufs, buflen);
267     if (pause_time) {
268         test_printf(" - %dms delay after %d packet%s\n", pause_time*10, 
269                     pause_threshold, pause_threshold > 1 ? "s" : "");
270     } else {
271         test_printf(" - no delays\n");
272     }
273
274     gettimeofday(&start_time, 0);
275     memcpy(&test_chan_slave, slave, sizeof(*slave));
276     test_chan_slave.sin_port = htons(ntohl(req.slave_port));
277     seq = 0;  seq_errors = 0;  total_packets = 0;
278     for (i = 0;  i < nbufs;  i++) {
279         td_len = buflen + sizeof(struct nc_test_data);
280         if (need_send) {
281             tdp = (struct nc_test_data *)out_buf;
282             tdp->key1 = htonl(NC_TEST_DATA_KEY1);
283             tdp->key2 = htonl(NC_TEST_DATA_KEY2);
284             tdp->seq = htonl(i);
285             tdp->len = htonl(td_len);
286             if (sendto(s, tdp, td_len, 0, 
287                        (struct sockaddr *)&test_chan_slave, sizeof(test_chan_slave)) < 0) {
288                 perror("sendto");
289                 close(s);
290                 return;
291             }
292             total_packets++;
293         }
294         if (need_recv) {
295             FD_ZERO(&fds);
296             FD_SET(s, &fds);
297             timeout.tv_sec = NC_TEST_TIMEOUT;
298             timeout.tv_usec = 0;
299             if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
300                 test_printf("Slave timed out after %d buffers\n", i);
301                 lost_packets++;
302             } else {
303                 tdp = (struct nc_test_data *)in_buf;
304                 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
305                     perror("recvfrom");
306                     close(s);
307                     return;
308                 }
309                 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
310                     (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
311                     if (ntohl(tdp->seq) != seq) {
312                         test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
313                                     ntohl(tdp->seq), seq);
314                         seq_errors++;
315                         if (!need_send) {
316                             // Reset sequence to what the slave wants
317                             seq = ntohl(tdp->seq);
318                         }
319                     }
320                 } else {
321                     test_printf("Bad data packet - key: %x/%x, seq: %d\n",
322                                 ntohl(tdp->key1), ntohl(tdp->key2),
323                                 ntohl(tdp->seq));
324                 }
325                 total_packets++;
326             }
327             seq++;
328             if (seq == nbufs) {
329                 break;
330             }
331             if (pause_time && (++pkt_ctr == pause_threshold)) {
332                 pkt_ctr = 0;
333                 test_delay(pause_time);
334             }
335         }
336     }
337     gettimeofday(&end_time, 0);
338     show_results(type_name, &start_time, &end_time, total_packets, buflen, 
339                  lost_packets, seq_errors);
340     // Fetch results record
341     FD_ZERO(&fds);
342     FD_SET(s, &fds);
343     timeout.tv_sec = NC_RESULTS_TIMEOUT;
344     timeout.tv_usec = 0;
345     if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
346         test_printf("No results record sent\n");
347     } else {
348         if (recvfrom(s, &results, sizeof(results), 0, 0, 0) < 0) {
349             perror("recvfrom");
350         }
351         show_test_results(&results);
352     }
353     close(s);
354 }
355
356 //
357 // Read data from a stream, accounting for the fact that packet 'boundaries'
358 // are not preserved.  This can also timeout (which would probably wreck the
359 // data boundaries).
360 //
361
362 int
363 do_read(int fd, void *buf, int buflen)
364 {
365     char *p = (char *)buf;
366     int len = buflen;
367     int res;
368     while (len) {
369         res = read(fd, p, len);
370         if (res < 0) {
371             perror("read");
372         } else {
373             len -= res;
374             p += res;
375             if (res == 0) {
376                 break;
377             }
378         }
379     }
380     return (buflen - len);
381 }
382
383 void
384 do_tcp_test(int s1, int type, struct sockaddr_in *slave,
385             int nbufs, int buflen, int pause_time, int pause_threshold)
386 {
387     int i, s, td_len, len, seq, seq_errors, total_packets, res;
388     struct sockaddr_in test_chan_slave;
389     struct timeval start_time, end_time;
390     struct nc_request req;
391     struct nc_reply reply;
392     struct nc_test_results results;
393     struct nc_test_data *tdp;
394     int lost_packets = 0;
395     int conn_failures = 0;
396     int need_send, need_recv;
397     const char *type_name;
398     int pkt_ctr = 0;
399
400     need_recv = true;  need_send = true;  type_name = "TCP echo";
401     switch (type) {
402     case NC_REQUEST_TCP_RECV:
403         need_recv = false;
404         need_send = true;
405         type_name = "TCP recv";
406         break;
407     case NC_REQUEST_TCP_SEND:
408         need_recv = true;
409         need_send = false;
410         type_name = "TCP send";
411         break;
412     case NC_REQUEST_TCP_ECHO:
413         break;
414     }
415
416     new_test();
417     req.type = htonl(type);
418     req.nbufs = htonl(nbufs);
419     req.buflen = htonl(buflen);
420     req.slave_port = htonl(NC_TESTING_SLAVE_PORT);
421     req.master_port = htonl(NC_TESTING_MASTER_PORT);
422     nc_message(s1, &req, &reply, slave);
423     if (reply.response != ntohl(NC_REPLY_ACK)) {
424         test_printf("Slave denied %s [%d,%d] test\n", type_name, nbufs, buflen);
425         return;
426     }
427
428     s = socket(AF_INET, SOCK_STREAM, 0);
429     if (s < 0) {
430         pexit("datagram socket");
431     }
432
433     test_printf("Start %s [%d,%d]", type_name, nbufs, buflen);
434     if (pause_time) {
435         test_printf(" - %dms delay after %d packet%s\n", pause_time*10, 
436                     pause_threshold, pause_threshold > 1 ? "s" : "");
437     } else {
438         test_printf(" - no delays\n");
439     }
440
441     test_delay(3*100);  
442     memcpy(&test_chan_slave, slave, sizeof(*slave));
443     test_chan_slave.sin_port = htons(ntohl(req.slave_port));
444     while (connect(s, (struct sockaddr *)&test_chan_slave, sizeof(*slave)) < 0) { 
445         perror("Can't connect to slave");
446         if (++conn_failures > MAX_ERRORS) {
447             test_printf("Too many connection failures - giving up\n");
448             return;
449         }
450         if (errno == ECONNREFUSED) {
451             // Give the slave a little time
452             test_delay(100);  // 1 second
453         } else {
454             return;
455         }
456     }
457
458     gettimeofday(&start_time, 0);
459     seq = 0;  seq_errors = 0;  total_packets = 0;
460     for (i = 0;  i < nbufs;  i++) {
461         td_len = buflen + sizeof(struct nc_test_data);
462         if (need_send) {
463             tdp = (struct nc_test_data *)out_buf;
464             tdp->key1 = htonl(NC_TEST_DATA_KEY1);
465             tdp->key2 = htonl(NC_TEST_DATA_KEY2);
466             tdp->seq = htonl(i);
467             tdp->len = htonl(td_len);
468             if ((len = write(s, tdp, td_len)) != td_len) {
469                 if (len < 0) {
470                     perror("write");
471                 } else {
472                     test_printf("short write - only %d or %d bytes written\n", len, td_len);
473                 }
474                 close(s);
475                 return;
476             }
477             total_packets++;
478         }
479         if (need_recv) {
480             tdp = (struct nc_test_data *)in_buf;
481             res = do_read(s, tdp, td_len);
482             if (res != td_len) {
483                 test_printf("Slave timed out after %d buffers\n", i);
484                 lost_packets++;
485             } else {
486                 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
487                     (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
488                     if (ntohl(tdp->seq) != seq) {
489                         test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
490                                     ntohl(tdp->seq), seq);
491                         seq_errors++;
492                         if (!need_send) {
493                             // Reset sequence to what the slave wants
494                             seq = ntohl(tdp->seq);
495                         }
496                     }
497                 } else {
498                     test_printf("Bad data packet - key: %x/%x, seq: %d\n",
499                                 ntohl(tdp->key1), ntohl(tdp->key2),
500                                 ntohl(tdp->seq));
501                 }
502                 total_packets++;
503             }
504             seq++;
505             if (seq == nbufs) {
506                 break;
507             }
508             if (pause_time && (++pkt_ctr == pause_threshold)) {
509                 pkt_ctr = 0;
510                 test_delay(pause_time);
511             }
512         }
513     }
514     gettimeofday(&end_time, 0);
515     show_results(type_name, &start_time, &end_time, total_packets, buflen, 
516                  lost_packets, seq_errors);
517     // Fetch results record
518     if (do_read(s, &results, sizeof(results)) != sizeof(results)) {
519         test_printf("No results record sent\n");
520     } else {
521         show_test_results(&results);
522     }
523     close(s);
524 }
525
526 int
527 do_set_load(int s, struct sockaddr_in *slave, int load_level)
528 {
529     struct nc_request req;
530     struct nc_reply reply;
531     req.type = htonl(NC_REQUEST_SET_LOAD);
532     req.nbufs = htonl(load_level);
533     nc_message(s, &req, &reply, slave);
534     return (reply.response == ntohl(NC_REPLY_ACK));
535 }
536
537 int
538 do_start_idle(int s, struct sockaddr_in *slave)
539 {
540     struct nc_request req;
541     struct nc_reply reply;
542     req.type = htonl(NC_REQUEST_START_IDLE);
543     nc_message(s, &req, &reply, slave);
544     return (reply.response == ntohl(NC_REPLY_ACK));
545 }
546
547 void
548 do_stop_idle(int s, struct sockaddr_in *slave, int calibrate)
549 {
550     struct nc_request req;
551     struct nc_reply reply;
552     long long res_idle_count;
553     long long adj_count;
554     int idle, res_idle_ticks;
555     req.type = htonl(NC_REQUEST_STOP_IDLE);
556     nc_message(s, &req, &reply, slave);
557     if (reply.response == ntohl(NC_REPLY_ACK)) {
558         res_idle_ticks = ntohl(reply.misc.idle_results.elapsed_time);
559         res_idle_count = ((long long)ntohl(reply.misc.idle_results.count[0]) << 32) |
560             ntohl(reply.misc.idle_results.count[1]);
561         test_printf("IDLE - ticks: %d, count: %ld", 
562                     res_idle_ticks, res_idle_count);
563         if (calibrate) {
564             idle_count = res_idle_count;
565             idle_ticks = res_idle_ticks;
566         } else {
567             adj_count = res_idle_count / res_idle_ticks;
568             adj_count *= idle_ticks;
569             idle = (int) ((adj_count * 100) / idle_count);
570             test_printf(", %d%% idle", idle);
571         }
572         test_printf("\n");
573     } else {
574         test_printf("Slave failed on IDLE\n");
575     }
576 }
577
578 void
579 do_disconnect(int s, struct sockaddr_in *slave)
580 {
581     struct nc_request req;
582     struct nc_reply reply;
583     req.type = htonl(NC_REQUEST_DISCONNECT);
584     nc_message(s, &req, &reply, slave);
585 }
586
587 static void
588 nc_master(struct test_params *p)
589 {
590     int s, i;
591     struct sockaddr_in slave, my_addr;
592     struct hostent *host;
593     struct pause pause_times[] = {
594         {0,0}, {1,10}, {5,10}, {10,10}, {1,1} };
595
596     if (p->argc != 2) {
597         test_printf("Need exactly 'master <host>'\n");
598         return;
599     }
600
601     s = socket(AF_INET, SOCK_DGRAM, 0);
602     if (s < 0) {
603         pexit("datagram socket");
604     }
605
606     memset(&my_addr, 0, sizeof(my_addr));
607     my_addr.sin_family = AF_INET;
608 #ifdef __ECOS
609     my_addr.sin_len = sizeof(my_addr);
610 #endif
611     my_addr.sin_port = htons(NC_MASTER_PORT);
612     my_addr.sin_addr.s_addr = INADDR_ANY;
613
614     if (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
615         pexit("bind");
616     }
617
618     host = gethostbyname(p->argv[1]);
619     if (host == (struct hostent *)NULL) {
620         pexit("gethostbyname");
621     }
622
623     memset(&slave, 0, sizeof(slave));
624     slave.sin_family = AF_INET;
625 #ifdef __ECOS
626     slave.sin_len = sizeof(slave);
627 #endif
628     slave.sin_port = htons(NC_SLAVE_PORT);
629     memcpy(&slave.sin_addr.s_addr, host->h_addr, host->h_length);
630
631     test_printf("================== No load, master at 100%% ========================\n");
632 #if 0
633     do_udp_test(s, NC_REQUEST_UDP_ECHO, &slave, 640, 1024, 0, 0);
634     do_udp_test(s, NC_REQUEST_UDP_SEND, &slave, 640, 1024, 0, 0);
635     do_udp_test(s, NC_REQUEST_UDP_RECV, &slave, 640, 1024, 0, 0);
636     do_tcp_test(s, NC_REQUEST_TCP_ECHO, &slave, 640, 1024, 0, 0);
637     do_tcp_test(s, NC_REQUEST_TCP_SEND, &slave, 640, 1024, 0, 0);
638     do_tcp_test(s, NC_REQUEST_TCP_RECV, &slave, 640, 1024, 0, 0);
639 #endif
640     do_tcp_test(s, NC_REQUEST_TCP_ECHO, &slave, 64, 10240, 0, 0);
641
642     if (do_set_load(s, &slave, 0)) {
643         test_printf("\n====================== Various slave compute loads ===================\n");
644         for (i = 0;  i < 60;  i += 10) {
645             test_printf(">>>>>>>>>>>> slave processing load at %d%%\n", i);
646             do_set_load(s, &slave, i);
647             do_udp_test(s, NC_REQUEST_UDP_ECHO, &slave, 2048, 1024, 0, 0);
648             do_tcp_test(s, NC_REQUEST_TCP_ECHO, &slave, 2048, 1024, 0, 0);
649         }
650     }
651
652     if (do_start_idle(s, &slave)) {
653         test_printf("\n====================== Various master loads ===================\n");
654         test_printf("Testing IDLE for %d seconds\n", IDLE_TEST_TIME);
655         test_delay(IDLE_TEST_TIME*100);
656         do_stop_idle(s, &slave, true);
657         for (i = 0;  i < LENGTH(pause_times);  i++) {
658             do_start_idle(s, &slave);
659             do_udp_test(s, NC_REQUEST_UDP_ECHO, &slave, 2048, 1024, 
660                         pause_times[i].pause_ticks, pause_times[i].pause_threshold);
661             do_stop_idle(s, &slave, false);
662             do_start_idle(s, &slave);
663             do_tcp_test(s, NC_REQUEST_TCP_ECHO, &slave, 2048, 1024, 
664                         pause_times[i].pause_ticks, pause_times[i].pause_threshold);
665             do_stop_idle(s, &slave, false);
666         }
667     }
668
669     do_disconnect(s, &slave);
670     close(s);
671 }
672
673 void
674 net_test(test_param_t p)
675 {
676     test_printf("Start Network Characterization - MASTER\n");
677 #ifdef __ECOS
678     init_all_network_interfaces();
679 #endif
680     nc_master((struct test_params *)p);
681     cyg_test_exit();
682 }
683
684 #ifdef __ECOS
685 void
686 cyg_start(void)
687 {
688     static struct test_params p;
689     // Create a main thread, so we can run the scheduler and have time 'pass'
690     cyg_thread_create(10,                // Priority - just a number
691                       net_test,          // entry
692                       (cyg_addrword_t)&p,// entry parameter
693                       "Network test",    // Name
694                       &stack[0],         // Stack
695                       STACK_SIZE,        // Size
696                       &thread_handle,    // Handle
697                       &thread_data       // Thread data structure
698             );
699     cyg_thread_resume(thread_handle);  // Start it
700     cyg_scheduler_start();
701 }
702
703 #else
704
705 int
706 main(int argc, char *argv[])
707 {
708     struct test_params p;
709     p.argc = argc;
710     p.argv = argv;
711     net_test(&p);
712 }
713 #endif