Mercurial > ecos-v2_0-branch
comparison packages/net/tcpip/current/tests/tcp_echo.c @ 97:ced4577552cd ecos-sw-2000-06-06
Merge from eCos master repository on 2000-06-06-08:44:00-BST
| author | jlarmour |
|---|---|
| date | Tue, 06 Jun 2000 08:39:36 +0000 |
| parents | |
| children | 5a0cc6c243a9 |
comparison
equal
deleted
inserted
replaced
| 96:b15c60e34c84 | 97:ced4577552cd |
|---|---|
| 1 //========================================================================== | |
| 2 // | |
| 3 // tests/tcp_echo.c | |
| 4 // | |
| 5 // Simple TCP throughput test - echo component | |
| 6 // | |
| 7 //========================================================================== | |
| 8 //####COPYRIGHTBEGIN#### | |
| 9 // | |
| 10 // ------------------------------------------- | |
| 11 // The contents of this file are subject to the Red Hat eCos Public License | |
| 12 // Version 1.1 (the "License"); you may not use this file except in | |
| 13 // compliance with the License. You may obtain a copy of the License at | |
| 14 // http://www.redhat.com/ | |
| 15 // | |
| 16 // Software distributed under the License is distributed on an "AS IS" | |
| 17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the | |
| 18 // License for the specific language governing rights and limitations under | |
| 19 // the License. | |
| 20 // | |
| 21 // The Original Code is eCos - Embedded Configurable Operating System, | |
| 22 // released September 30, 1998. | |
| 23 // | |
| 24 // The Initial Developer of the Original Code is Red Hat. | |
| 25 // Portions created by Red Hat are | |
| 26 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc. | |
| 27 // All Rights Reserved. | |
| 28 // ------------------------------------------- | |
| 29 // | |
| 30 //####COPYRIGHTEND#### | |
| 31 //####BSDCOPYRIGHTBEGIN#### | |
| 32 // | |
| 33 // ------------------------------------------- | |
| 34 // | |
| 35 // Portions of this software may have been derived from OpenBSD or other sources, | |
| 36 // and are covered by the appropriate copyright disclaimers included herein. | |
| 37 // | |
| 38 // ------------------------------------------- | |
| 39 // | |
| 40 //####BSDCOPYRIGHTEND#### | |
| 41 //========================================================================== | |
| 42 //#####DESCRIPTIONBEGIN#### | |
| 43 // | |
| 44 // Author(s): gthomas | |
| 45 // Contributors: gthomas | |
| 46 // Date: 2000-01-10 | |
| 47 // Purpose: | |
| 48 // Description: This is the middle part of a three part test. The idea is | |
| 49 // to test the throughput of box in a configuration like this: | |
| 50 // | |
| 51 // +------+ port +----+ port +----+ | |
| 52 // |SOURCE|=========>|ECHO|============>|SINK| | |
| 53 // +------+ 9990 +----+ 9991 +----+ | |
| 54 // | |
| 55 // | |
| 56 //####DESCRIPTIONEND#### | |
| 57 // | |
| 58 //========================================================================== | |
| 59 | |
| 60 // Network throughput test code | |
| 61 | |
| 62 #include <lib/libkern/libkern.h> | |
| 63 | |
| 64 #include <network.h> | |
| 65 | |
| 66 #define SOURCE_PORT 9990 | |
| 67 #define SINK_PORT 9991 | |
| 68 | |
| 69 #define MAX_BUF 8192 | |
| 70 static unsigned char data_buf[MAX_BUF]; | |
| 71 | |
| 72 struct test_params { | |
| 73 long nbufs; | |
| 74 long bufsize; | |
| 75 long load; | |
| 76 }; | |
| 77 | |
| 78 struct test_status { | |
| 79 long ok; | |
| 80 }; | |
| 81 | |
| 82 #define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL | |
| 83 static char stack[STACK_SIZE]; | |
| 84 static cyg_thread thread_data; | |
| 85 static cyg_handle_t thread_handle; | |
| 86 | |
| 87 // Background load stuff | |
| 88 #define NUM_LOAD_THREADS 20 // Get 5% granularity | |
| 89 #define IDLE_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY+3 | |
| 90 #define LOAD_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-1 | |
| 91 #define MAIN_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-2 | |
| 92 #define DESIRED_BACKGROUND_LOAD 50 // should be accurate enough over range | |
| 93 | |
| 94 // starting points for load calculation | |
| 95 #define MAX_LOAD_THREAD_LEVEL 100 | |
| 96 #define MIN_LOAD_THREAD_LEVEL 0 | |
| 97 | |
| 98 static char idle_thread_stack[STACK_SIZE]; | |
| 99 static cyg_thread idle_thread_data; | |
| 100 static cyg_handle_t idle_thread_handle; | |
| 101 static cyg_sem_t idle_thread_sem; | |
| 102 volatile static long long idle_thread_count; | |
| 103 static char load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE]; | |
| 104 static cyg_thread load_thread_data[NUM_LOAD_THREADS]; | |
| 105 static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS]; | |
| 106 static cyg_sem_t load_thread_sem[NUM_LOAD_THREADS]; | |
| 107 static long load_thread_level; | |
| 108 static void calibrate_load(int load); | |
| 109 static void start_load(int load); | |
| 110 static void do_some_random_computation(int p,int id); | |
| 111 #define abs(n) ((n) < 0 ? -(n) : (n)) | |
| 112 | |
| 113 static long long no_load_idle_count_1_second; | |
| 114 | |
| 115 extern void | |
| 116 cyg_test_exit(void); | |
| 117 | |
| 118 void | |
| 119 pexit(char *s) | |
| 120 { | |
| 121 perror(s); | |
| 122 cyg_test_exit(); | |
| 123 } | |
| 124 | |
| 125 int | |
| 126 do_read(int s, void *_buf, int len) | |
| 127 { | |
| 128 int total, slen, rlen; | |
| 129 unsigned char *buf = (unsigned char *)_buf; | |
| 130 total = 0; | |
| 131 rlen = len; | |
| 132 while (total < len) { | |
| 133 slen = read(s, buf, rlen); | |
| 134 if (slen != rlen) { | |
| 135 if (slen < 0) { | |
| 136 diag_printf("Error after reading %d bytes\n", total); | |
| 137 return -1; | |
| 138 } | |
| 139 rlen -= slen; | |
| 140 buf += slen; | |
| 141 } | |
| 142 total += slen; | |
| 143 } | |
| 144 return total; | |
| 145 } | |
| 146 | |
| 147 int | |
| 148 do_write(int s, void *_buf, int len) | |
| 149 { | |
| 150 int total, slen, rlen; | |
| 151 unsigned char *buf = (unsigned char *)_buf; | |
| 152 total = 0; | |
| 153 rlen = len; | |
| 154 while (total < len) { | |
| 155 slen = write(s, buf, rlen); | |
| 156 if (slen != rlen) { | |
| 157 if (slen < 0) { | |
| 158 diag_printf("Error after writing %d bytes\n", total); | |
| 159 return -1; | |
| 160 } | |
| 161 rlen -= slen; | |
| 162 buf += slen; | |
| 163 } | |
| 164 total += slen; | |
| 165 } | |
| 166 return total; | |
| 167 } | |
| 168 | |
| 169 // | |
| 170 // This function is called to calibrate the "background load" which can be | |
| 171 // applied during testing. It will be called before any commands from the | |
| 172 // host are managed. | |
| 173 // | |
| 174 static void | |
| 175 calibrate_load(int desired_load) | |
| 176 { | |
| 177 long long no_load_idle, load_idle; | |
| 178 int percent_load; | |
| 179 int high, low; | |
| 180 | |
| 181 // Set limits | |
| 182 high = MAX_LOAD_THREAD_LEVEL; | |
| 183 low = MIN_LOAD_THREAD_LEVEL; | |
| 184 | |
| 185 // Compute the "no load" idle value | |
| 186 idle_thread_count = 0; | |
| 187 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 188 cyg_thread_delay(1*100); // Pause for one second | |
| 189 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 190 no_load_idle = idle_thread_count; | |
| 191 diag_printf("No load = %d\n", (int)idle_thread_count); | |
| 192 | |
| 193 // First ensure that the HIGH level is indeed higher | |
| 194 while (true) { | |
| 195 load_thread_level = high; | |
| 196 start_load(desired_load); // Start up a given load | |
| 197 idle_thread_count = 0; | |
| 198 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 199 cyg_thread_delay(1*100); // Pause for one second | |
| 200 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 201 load_idle = idle_thread_count; | |
| 202 start_load(0); // Shut down background load | |
| 203 percent_load = 100 - ((load_idle * 100) / no_load_idle); | |
| 204 diag_printf("High Load[%d] = %d => %d%%\n", load_thread_level, | |
| 205 (int)idle_thread_count, percent_load); | |
| 206 if ( percent_load > desired_load ) | |
| 207 break; // HIGH level is indeed higher | |
| 208 low = load_thread_level; // known to be lower | |
| 209 high *= 2; // else double it and try again | |
| 210 } | |
| 211 | |
| 212 // Now chop down to the level required | |
| 213 while (true) { | |
| 214 load_thread_level = (high + low) / 2; | |
| 215 start_load(desired_load); // Start up a given load | |
| 216 idle_thread_count = 0; | |
| 217 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 218 cyg_thread_delay(1*100); // Pause for one second | |
| 219 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 220 load_idle = idle_thread_count; | |
| 221 start_load(0); // Shut down background load | |
| 222 percent_load = 100 - ((load_idle * 100) / no_load_idle); | |
| 223 diag_printf("Load[%d] = %d => %d%%\n", load_thread_level, | |
| 224 (int)idle_thread_count, percent_load); | |
| 225 if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break; | |
| 226 if (percent_load < desired_load) { | |
| 227 low = load_thread_level; | |
| 228 } else { | |
| 229 high = load_thread_level; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 // Now we are within a few percent of the target; scale the load | |
| 234 // factor to get a better fit, and test it, print the answer. | |
| 235 load_thread_level *= desired_load; | |
| 236 load_thread_level /= percent_load; | |
| 237 start_load(desired_load); // Start up a given load | |
| 238 idle_thread_count = 0; | |
| 239 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 240 cyg_thread_delay(1*100); // Pause for one second | |
| 241 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 242 load_idle = idle_thread_count; | |
| 243 start_load(0); // Shut down background load | |
| 244 percent_load = 100 - ((load_idle * 100) / no_load_idle); | |
| 245 diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level, | |
| 246 (int)idle_thread_count, percent_load); | |
| 247 no_load_idle_count_1_second = no_load_idle; | |
| 248 } | |
| 249 | |
| 250 // | |
| 251 // This function is called to set up a load level of 'load' percent (given | |
| 252 // as a whole number, e.g. start_load(20) would mean initiate a background | |
| 253 // load of 20%, leaving the cpu 80% idle). | |
| 254 // | |
| 255 static void | |
| 256 start_load(int load) | |
| 257 { | |
| 258 static int prev_load = 0; | |
| 259 int i; | |
| 260 if (load == 0) { | |
| 261 diag_printf("Set no background load\n"); | |
| 262 if (prev_load == 0) return; // Nothing out there to stop | |
| 263 for (i = 0; i < prev_load * NUM_LOAD_THREADS/100; i++) { | |
| 264 cyg_semaphore_wait(&load_thread_sem[i]); | |
| 265 } | |
| 266 prev_load = 0; | |
| 267 } else { | |
| 268 diag_printf("Set background load = %d%% starting %d threads\n", | |
| 269 load, load * NUM_LOAD_THREADS/100 ); | |
| 270 for (i = 0; i < load * NUM_LOAD_THREADS/100; i++) { | |
| 271 cyg_semaphore_post(&load_thread_sem[i]); | |
| 272 } | |
| 273 prev_load = load; | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 // | |
| 278 // These thread(s) do some amount of "background" computing. This is used | |
| 279 // to simulate a given load level. They need to be run at a higher priority | |
| 280 // than the network code itself. | |
| 281 // | |
| 282 // Like the "idle" thread, they run as long as their "switch" (aka semaphore) | |
| 283 // is enabled. | |
| 284 // | |
| 285 void | |
| 286 net_load(cyg_addrword_t who) | |
| 287 { | |
| 288 int i; | |
| 289 while (true) { | |
| 290 cyg_semaphore_wait(&load_thread_sem[who]); | |
| 291 for (i = 0; i < load_thread_level; i++) { | |
| 292 do_some_random_computation(i,who); | |
| 293 } | |
| 294 cyg_thread_delay(1); // Wait until the next 'tick' | |
| 295 cyg_semaphore_post(&load_thread_sem[who]); | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 // | |
| 300 // Some arbitrary computation, designed to use up the CPU and cause associated | |
| 301 // cache "thrash" behaviour - part of background load modelling. | |
| 302 // | |
| 303 static void | |
| 304 do_some_random_computation(int p,int id) | |
| 305 { | |
| 306 // Just something that might be "hard" | |
| 307 #if 0 | |
| 308 { | |
| 309 volatile double x; | |
| 310 x = ((p * 10) * 3.14159) / 180.0; // radians | |
| 311 } | |
| 312 #endif | |
| 313 #if 1 | |
| 314 { | |
| 315 static int footle[0x10001]; | |
| 316 static int counter = 0; | |
| 317 register int i; | |
| 318 | |
| 319 i = (p << 8) + id + counter++; | |
| 320 i &= 0xffff; | |
| 321 footle[ i+1 ] += footle[ i ] + 1; | |
| 322 } | |
| 323 #endif | |
| 324 } | |
| 325 | |
| 326 // | |
| 327 // This thread does nothing but count. It will be allowed to count | |
| 328 // as long as the semaphore is "free". | |
| 329 // | |
| 330 void | |
| 331 net_idle(cyg_addrword_t param) | |
| 332 { | |
| 333 while (true) { | |
| 334 cyg_semaphore_wait(&idle_thread_sem); | |
| 335 idle_thread_count++; | |
| 336 cyg_semaphore_post(&idle_thread_sem); | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 static void | |
| 341 echo_test(cyg_addrword_t p) | |
| 342 { | |
| 343 int s_source, s_sink, e_source, e_sink; | |
| 344 struct sockaddr_in e_source_addr, e_sink_addr, local; | |
| 345 int one = 1; | |
| 346 fd_set in_fds; | |
| 347 int i, num, len; | |
| 348 struct test_params params,nparams; | |
| 349 struct test_status status,nstatus; | |
| 350 | |
| 351 cyg_tick_count_t starttime, stoptime; | |
| 352 | |
| 353 s_source = socket(AF_INET, SOCK_STREAM, 0); | |
| 354 if (s_source < 0) { | |
| 355 pexit("stream socket"); | |
| 356 } | |
| 357 memset(&local, 0, sizeof(local)); | |
| 358 local.sin_family = AF_INET; | |
| 359 local.sin_len = sizeof(local); | |
| 360 local.sin_port = ntohs(SOURCE_PORT); | |
| 361 local.sin_addr.s_addr = INADDR_ANY; | |
| 362 if(bind(s_source, (struct sockaddr *) &local, sizeof(local)) < 0) { | |
| 363 pexit("bind /source/ error"); | |
| 364 } | |
| 365 if (setsockopt(s_source, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { | |
| 366 pexit("setsockopt /source/ SO_REUSEADDR"); | |
| 367 } | |
| 368 if (setsockopt(s_source, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) { | |
| 369 pexit("setsockopt /source/ SO_REUSEPORT"); | |
| 370 } | |
| 371 listen(s_source, SOMAXCONN); | |
| 372 | |
| 373 s_sink = socket(AF_INET, SOCK_STREAM, 0); | |
| 374 if (s_sink < 0) { | |
| 375 pexit("stream socket"); | |
| 376 } | |
| 377 memset(&local, 0, sizeof(local)); | |
| 378 local.sin_family = AF_INET; | |
| 379 local.sin_len = sizeof(local); | |
| 380 local.sin_port = ntohs(SINK_PORT); | |
| 381 local.sin_addr.s_addr = INADDR_ANY; | |
| 382 if(bind(s_sink, (struct sockaddr *) &local, sizeof(local)) < 0) { | |
| 383 pexit("bind /sink/ error"); | |
| 384 } | |
| 385 if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { | |
| 386 pexit("setsockopt /sink/ SO_REUSEADDR"); | |
| 387 } | |
| 388 if (setsockopt(s_sink, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) { | |
| 389 pexit("setsockopt /sink/ SO_REUSEPORT"); | |
| 390 } | |
| 391 listen(s_sink, SOMAXCONN); | |
| 392 | |
| 393 e_source = 0; e_sink = 0; | |
| 394 while (true) { | |
| 395 // Wait for a connection on either of the ports | |
| 396 FD_ZERO(&in_fds); | |
| 397 FD_SET(s_source, &in_fds); | |
| 398 FD_SET(s_sink, &in_fds); | |
| 399 num = select(max(s_sink,s_source)+1, &in_fds, 0, 0, 0); | |
| 400 if (FD_ISSET(s_source, &in_fds)) { | |
| 401 len = sizeof(e_source_addr); | |
| 402 if ((e_source = accept(s_source, (struct sockaddr *)&e_source_addr, &len)) < 0) { | |
| 403 pexit("accept /source/"); | |
| 404 } | |
| 405 diag_printf("SOURCE connection from %s:%d\n", | |
| 406 inet_ntoa(e_source_addr.sin_addr), ntohs(e_source_addr.sin_port)); | |
| 407 } | |
| 408 if (FD_ISSET(s_sink, &in_fds)) { | |
| 409 len = sizeof(e_sink_addr); | |
| 410 if ((e_sink = accept(s_sink, (struct sockaddr *)&e_sink_addr, &len)) < 0) { | |
| 411 pexit("accept /sink/"); | |
| 412 } | |
| 413 diag_printf("SINK connection from %s:%d\n", | |
| 414 inet_ntoa(e_sink_addr.sin_addr), ntohs(e_sink_addr.sin_port)); | |
| 415 } | |
| 416 // Continue with test once a connection is established in both directions | |
| 417 if ((e_source != 0) && (e_sink != 0)) { | |
| 418 break; | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 // Wait for "source" to tell us the testing paramters | |
| 423 if (do_read(e_source, &nparams, sizeof(nparams)) != sizeof(nparams)) { | |
| 424 pexit("Can't read initialization parameters"); | |
| 425 } | |
| 426 | |
| 427 params.nbufs = ntohl(nparams.nbufs); | |
| 428 params.bufsize = ntohl(nparams.bufsize); | |
| 429 params.load = ntohl(nparams.load); | |
| 430 | |
| 431 diag_printf("Using %d buffers of %d bytes each, %d%% background load\n", | |
| 432 params.nbufs, params.bufsize, params.load); | |
| 433 | |
| 434 // Tell the sink what the parameters are | |
| 435 if (do_write(e_sink, &nparams, sizeof(nparams)) != sizeof(nparams)) { | |
| 436 pexit("Can't write initialization parameters"); | |
| 437 } | |
| 438 | |
| 439 status.ok = 1; | |
| 440 nstatus.ok = htonl(status.ok); | |
| 441 | |
| 442 // Tell the "source" to start - we're all connected and ready to go! | |
| 443 if (do_write(e_source, &nstatus, sizeof(nstatus)) != sizeof(nstatus)) { | |
| 444 pexit("Can't send ACK to 'source' host"); | |
| 445 } | |
| 446 | |
| 447 idle_thread_count = 0; | |
| 448 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 449 starttime = cyg_current_time(); | |
| 450 start_load(params.load); | |
| 451 | |
| 452 // Echo the data from the source to the sink hosts | |
| 453 for (i = 0; i < params.nbufs; i++) { | |
| 454 if ((len = do_read(e_source, data_buf, params.bufsize)) != params.bufsize) { | |
| 455 diag_printf("Can't read buf #%d: ", i+1); | |
| 456 if (len < 0) { | |
| 457 perror("I/O error"); | |
| 458 } else { | |
| 459 diag_printf("short read - only %d bytes\n", len); | |
| 460 } | |
| 461 } | |
| 462 if ((len = do_write(e_sink, data_buf, params.bufsize)) != params.bufsize) { | |
| 463 diag_printf("Can't write buf #%d: ", i+1); | |
| 464 if (len < 0) { | |
| 465 perror("I/O error"); | |
| 466 } else { | |
| 467 diag_printf("short write - only %d bytes\n", len); | |
| 468 } | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 // Wait for the data to drain and the "sink" to tell us all is OK. | |
| 473 if (do_read(e_sink, &status, sizeof(status)) != sizeof(status)) { | |
| 474 pexit("Can't receive ACK from 'sink' host"); | |
| 475 } | |
| 476 | |
| 477 start_load(0); | |
| 478 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 479 stoptime = cyg_current_time(); | |
| 480 stoptime -= starttime; // time taken in cS | |
| 481 // expected idle loops in that time period for an idle system: | |
| 482 starttime = no_load_idle_count_1_second * stoptime / 100; | |
| 483 diag_printf( "%d ticks elapsed, %d kloops predicted for an idle system\n", | |
| 484 (int)stoptime, (int)(starttime/1000) ); | |
| 485 diag_printf( "actual kloops %d, CPU was %d%% idle during transfer\n", | |
| 486 (int)(idle_thread_count/1000), | |
| 487 (int)(idle_thread_count * 100 / starttime) ); | |
| 488 | |
| 489 // Now examine how close that loading actually was: | |
| 490 start_load(params.load); // Start up a given load | |
| 491 idle_thread_count = 0; | |
| 492 cyg_semaphore_post(&idle_thread_sem); // Start idle thread | |
| 493 cyg_thread_delay(1*100); // Pause for one second | |
| 494 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread | |
| 495 start_load(0); // Shut down background load | |
| 496 i = 100 - ((idle_thread_count * 100) / no_load_idle_count_1_second ); | |
| 497 diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level, | |
| 498 (int)idle_thread_count, i); | |
| 499 | |
| 500 //#ifdef CYGDBG_USE_ASSERTS | |
| 501 { | |
| 502 extern void show_net_times(void); | |
| 503 show_net_times(); | |
| 504 } | |
| 505 //#endif | |
| 506 } | |
| 507 | |
| 508 void | |
| 509 net_test(cyg_addrword_t param) | |
| 510 { | |
| 511 diag_printf("Start TCP test - ECHO mode\n"); | |
| 512 init_all_network_interfaces(); | |
| 513 calibrate_load(DESIRED_BACKGROUND_LOAD); | |
| 514 echo_test(param); | |
| 515 cyg_test_exit(); | |
| 516 } | |
| 517 | |
| 518 void | |
| 519 cyg_start(void) | |
| 520 { | |
| 521 int i; | |
| 522 // Create a main thread which actually runs the test | |
| 523 cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority | |
| 524 net_test, // entry | |
| 525 0, // entry parameter | |
| 526 "Network test", // Name | |
| 527 &stack[0], // Stack | |
| 528 STACK_SIZE, // Size | |
| 529 &thread_handle, // Handle | |
| 530 &thread_data // Thread data structure | |
| 531 ); | |
| 532 cyg_thread_resume(thread_handle); // Start it | |
| 533 // Create the idle thread environment | |
| 534 cyg_semaphore_init(&idle_thread_sem, 0); | |
| 535 cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority | |
| 536 net_idle, // entry | |
| 537 0, // entry parameter | |
| 538 "Network idle", // Name | |
| 539 &idle_thread_stack[0], // Stack | |
| 540 STACK_SIZE, // Size | |
| 541 &idle_thread_handle, // Handle | |
| 542 &idle_thread_data // Thread data structure | |
| 543 ); | |
| 544 cyg_thread_resume(idle_thread_handle); // Start it | |
| 545 // Create the load threads and their environment(s) | |
| 546 for (i = 0; i < NUM_LOAD_THREADS; i++) { | |
| 547 cyg_semaphore_init(&load_thread_sem[i], 0); | |
| 548 cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority | |
| 549 net_load, // entry | |
| 550 i, // entry parameter | |
| 551 "Background load", // Name | |
| 552 &load_thread_stack[i][0], // Stack | |
| 553 STACK_SIZE, // Size | |
| 554 &load_thread_handle[i], // Handle | |
| 555 &load_thread_data[i] // Thread data structure | |
| 556 ); | |
| 557 cyg_thread_resume(load_thread_handle[i]); // Start it | |
| 558 } | |
| 559 cyg_scheduler_start(); | |
| 560 } |
