1 //==========================================================================
3 // tests/nc6_test_slave.c
5 // Network characterizations test (slave portion) IPv4+IPv6 aware
7 //==========================================================================
8 //####BSDCOPYRIGHTBEGIN####
10 // -------------------------------------------
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.
15 // -------------------------------------------
17 //####BSDCOPYRIGHTEND####
18 //==========================================================================
19 //#####DESCRIPTIONBEGIN####
22 // Contributors: gthomas
28 //####DESCRIPTIONEND####
30 //==========================================================================
32 // Network characterization test code - slave portion
34 #include "nc_test_framework.h"
39 #ifndef CYGPKG_LIBC_STDIO
40 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
42 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
43 #define MAX_LOAD_THREAD_LEVEL 20
44 #define MIN_LOAD_THREAD_LEVEL 0
45 #define NUM_LOAD_THREADS 10
46 #define IDLE_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY+3
47 #define LOAD_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-1
48 #define MAIN_THREAD_PRIORITY CYGPKG_NET_THREAD_PRIORITY-2
49 #define DESIRED_BACKGROUND_LOAD 20
50 static char main_thread_stack[CYGHWR_NET_DRIVERS][STACK_SIZE];
51 static cyg_thread main_thread_data[CYGHWR_NET_DRIVERS];
52 static cyg_handle_t main_thread_handle[CYGHWR_NET_DRIVERS];
53 static char idle_thread_stack[STACK_SIZE];
54 static cyg_thread idle_thread_data;
55 static cyg_handle_t idle_thread_handle;
56 static cyg_sem_t idle_thread_sem;
57 volatile static long long idle_thread_count;
58 static cyg_tick_count_t idle_thread_start_time;
59 static cyg_tick_count_t idle_thread_stop_time;
60 static char load_thread_stack[NUM_LOAD_THREADS][STACK_SIZE];
61 static cyg_thread load_thread_data[NUM_LOAD_THREADS];
62 static cyg_handle_t load_thread_handle[NUM_LOAD_THREADS];
63 static cyg_sem_t load_thread_sem[NUM_LOAD_THREADS];
64 static long load_thread_level;
65 static void calibrate_load(int load);
66 static void start_load(int load);
67 static void do_some_random_computation(int p);
68 #define abs(n) ((n) < 0 ? -(n) : (n))
72 #define test_param_t cyg_addrword_t
73 #ifdef CYGDBG_NET_TIMING_STATS
74 extern void show_net_times(void);
76 extern void cyg_kmem_print_stats(void);
78 #define test_param_t int
82 #ifdef CYGPKG_NET_INET6
90 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
99 test_printf("... Done\n");
111 test_delay(int ticks)
113 cyg_thread_delay(ticks);
119 test_delay(int ticks)
121 usleep(ticks * 10000);
129 #ifdef CYGDBG_NET_TIMING_STATS
133 cyg_kmem_print_stats();
139 sa_len(struct sockaddr *sa)
141 switch (sa->sa_family) {
143 return sizeof(struct sockaddr_in);
146 return sizeof(struct sockaddr_in6);
149 test_printf("Unknown socket type: %d\n", sa->sa_family);
150 return sizeof(struct sockaddr_storage);
159 do_udp_test(int s1, struct nc_request *req, struct sockaddr *master)
161 int i, s, td_len, seq, seq_errors, lost;
162 struct sockaddr_storage test_chan_slave, test_chan_master;
164 struct timeval timeout;
165 struct nc_test_results results;
166 struct nc_test_data *tdp;
168 int need_recv, need_send;
170 need_recv = true; need_send = true;
171 switch (ntohl(req->type)) {
172 case NC_REQUEST_UDP_SEND:
176 case NC_REQUEST_UDP_RECV:
180 case NC_REQUEST_UDP_ECHO:
184 s = socket(master->sa_family, SOCK_DGRAM, 0);
186 pexit("datagram socket");
189 memset((char *)&test_chan_slave, 0, sizeof(test_chan_slave));
190 memcpy(&test_chan_master, master, sizeof(*master));
192 ((struct sockaddr *)&test_chan_slave)->sa_len = master->sa_len;
194 ((struct sockaddr *)&test_chan_slave)->sa_family = master->sa_family;
195 switch (master->sa_family) {
197 ((struct sockaddr_in *)&test_chan_slave)->sin_addr.s_addr = htonl(INADDR_ANY);
198 ((struct sockaddr_in *)&test_chan_slave)->sin_port = htons(ntohl(req->slave_port));
199 ((struct sockaddr_in *)&test_chan_master)->sin_port = htons(ntohl(req->master_port));
203 ((struct sockaddr_in6 *)&test_chan_slave)->sin6_addr = in6addr_any;
204 ((struct sockaddr_in6 *)&test_chan_slave)->sin6_port = htons(ntohl(req->slave_port));
205 ((struct sockaddr_in6 *)&test_chan_master)->sin6_port = htons(ntohl(req->master_port));
209 pexit("strange UDP sockaddr");
212 if (bind(s, (struct sockaddr *) &test_chan_slave,
213 sa_len((struct sockaddr *)&test_chan_slave)) < 0) {
214 perror("bind UDP slave");
218 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0;
219 for (i = 0; i < ntohl(req->nbufs); i++) {
223 timeout.tv_sec = NC_TEST_TIMEOUT;
225 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
226 test_printf("recvfrom timeout, expecting seq #%d\n", seq);
227 if (++lost > MAX_ERRORS) {
228 test_printf("... giving up\n");
233 tdp = (struct nc_test_data *)in_buf;
234 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
235 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
236 perror("recvfrom UDP data");
240 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
241 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
242 if (ntohl(tdp->seq) != seq) {
243 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
244 ntohl(tdp->seq), seq);
245 seq = ntohl(tdp->seq);
249 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
250 ntohl(tdp->key1), ntohl(tdp->key2),
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 if (sendto(s, tdp, td_len, 0,
263 (struct sockaddr *)&test_chan_master, sa_len(master)) < 0) {
264 perror("sendto UDP data");
265 if (errno == ENOBUFS) {
266 // Saturated the system
267 test_delay(10); // Time for 200 500 byte 10-baseT packets
279 results.key1 = htonl(NC_TEST_RESULT_KEY1);
280 results.key2 = htonl(NC_TEST_RESULT_KEY2);
281 results.seq = req->seq;
282 results.nsent = htonl(nsent);
283 results.nrecvd = htonl(nrecvd);
284 if (sendto(s, &results, sizeof(results), 0,
285 (struct sockaddr *)&test_chan_master, sa_len(master)) < 0) {
286 perror("sendto UDP results");
292 // Read data from a stream, accounting for the fact that packet 'boundaries'
293 // are not preserved. This can also timeout (which would probably wreck the
298 do_read(int fd, void *buf, int buflen)
300 char *p = (char *)buf;
304 res = read(fd, p, len);
315 return (buflen - len);
323 do_tcp_test(int s1, struct nc_request *req, struct sockaddr *master)
325 int i, s, len, td_len, seq, seq_errors, lost, test_chan, res;
326 struct sockaddr_storage test_chan_slave, test_chan_master;
327 struct nc_test_results results;
328 struct nc_test_data *tdp;
330 int need_recv, need_send;
333 static int slave_tcp_port[2] = {-1, -1};
336 need_recv = true; need_send = true;
337 switch (ntohl(req->type)) {
338 case NC_REQUEST_TCP_SEND:
342 case NC_REQUEST_TCP_RECV:
346 case NC_REQUEST_TCP_ECHO:
350 switch (master->sa_family) {
360 pexit("unknown TCP family");
362 if (slave_tcp_port[which_port] < 0) {
363 test_printf("TCP - listen on %s/%d\n", which_port ? "IPv6" : "IPv4",
364 ntohl(req->slave_port));
365 s = socket(master->sa_family, SOCK_STREAM, 0);
367 pexit("datagram socket");
370 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
371 perror("setsockopt SO_REUSEADDR");
375 if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
376 perror("setsockopt SO_REUSEPORT");
380 memset((char *)&test_chan_slave, 0, sizeof(test_chan_slave));
382 ((struct sockaddr *)&test_chan_slave)->sa_len = master->sa_len;
384 ((struct sockaddr *)&test_chan_slave)->sa_family = master->sa_family;
385 switch (master->sa_family) {
387 ((struct sockaddr_in *)&test_chan_slave)->sin_addr.s_addr = htonl(INADDR_ANY);
388 ((struct sockaddr_in *)&test_chan_slave)->sin_port = htons(ntohl(req->slave_port));
392 ((struct sockaddr_in6 *)&test_chan_slave)->sin6_addr = in6addr_any;
393 ((struct sockaddr_in6 *)&test_chan_slave)->sin6_port = htons(ntohl(req->slave_port));
397 pexit("strange TCP sockaddr");
400 if (bind(s, (struct sockaddr *)&test_chan_slave, sa_len(master)) < 0) {
404 listen(s, SOMAXCONN);
405 slave_tcp_port[which_port] = s;
408 s = slave_tcp_port[which_port];
409 len = sizeof(test_chan_master);
410 if ((test_chan = accept(s, (struct sockaddr *)&test_chan_master, &len)) < 0) {
413 len = sizeof(test_chan_master);
414 getpeername(test_chan, (struct sockaddr *)&test_chan_master, &len);
416 _inet_ntop((struct sockaddr *)&test_chan_master, addr_buf, sizeof(addr_buf));
417 test_printf("connection from %s(%d)\n", addr_buf,
418 _inet_port((struct sockaddr *)&test_chan_master));
421 nsent = 0; nrecvd = 0; seq = 0; seq_errors = 0; lost = 0;
422 for (i = 0; i < ntohl(req->nbufs); i++) {
424 tdp = (struct nc_test_data *)in_buf;
425 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
426 res = do_read(test_chan, tdp, td_len);
428 test_printf("recvfrom timeout, expecting seq #%d\n", seq);
429 if (++lost > MAX_ERRORS) {
430 test_printf("... giving up\n");
435 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
436 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
437 if (ntohl(tdp->seq) != seq) {
438 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
439 ntohl(tdp->seq), seq);
440 seq = ntohl(tdp->seq);
444 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
445 ntohl(tdp->key1), ntohl(tdp->key2),
451 tdp = (struct nc_test_data *)out_buf;
452 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
453 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
454 tdp->seq = htonl(seq);
455 td_len = ntohl(req->buflen) + sizeof(struct nc_test_data);
456 tdp->len = htonl(td_len);
457 if (write(test_chan, tdp, td_len) != td_len) {
459 if (errno == ENOBUFS) {
460 // Saturated the system
473 results.key1 = htonl(NC_TEST_RESULT_KEY1);
474 results.key2 = htonl(NC_TEST_RESULT_KEY2);
475 results.seq = req->seq;
476 results.nsent = htonl(nsent);
477 results.nrecvd = htonl(nrecvd);
478 if (write(test_chan, &results, sizeof(results)) != sizeof(results)) {
484 #define MAXSOCK 16 // Max # sockets to listen on
487 waitfor(int *socks, int len, int last_sock)
492 // Wait for some activity on one of the ports
494 for (s = 0; s < len; s++) {
495 FD_SET(socks[s], &src_fds);
497 num = select(last_sock+1, &src_fds, 0, 0, 0);
499 for (i = 0; i < len; i++) {
501 if (FD_ISSET(s, &src_fds)) {
502 return s; // Return first available socket
505 pexit("select, but no socket!");
513 // Protocol driver for testing slave.
515 // This function is the main routine running here, handling requests sent from
516 // the master and providing various responses.
519 nc_slave(test_param_t param)
521 int sock_indx, s, err, last_sock, socks[MAXSOCK], masterlen;
522 struct sockaddr_storage master;
523 struct nc_request req;
524 struct nc_reply reply;
526 struct addrinfo *ai, *addrs, hints;
529 test_printf("Start test for eth%d\n", param);
530 bzero(&hints, sizeof(hints));
531 hints.ai_family = PF_UNSPEC;
532 hints.ai_socktype = SOCK_DGRAM;
533 hints.ai_flags = AI_PASSIVE;
534 if ((err = getaddrinfo(NULL, _string(NC_SLAVE_PORT), &hints, &addrs)) != EAI_NONE) {
535 test_printf("<ERROR> can't getaddrinfo(): %s\n", gai_strerror(err));
536 pexit("getaddrinfo");
538 // Prepare a socket for each connection type
539 sock_indx = 0; last_sock = -1;
543 _inet_ntop(ai->ai_addr, addr_buf, sizeof(addr_buf));
544 test_printf("Family: %d, Socket: %d, Addr: %s\n", ai->ai_family, ai->ai_socktype, addr_buf);
546 test_printf("Family: %d, Socket: %d\n", ai->ai_family, ai->ai_socktype);
549 // This code is very sensitive on Linux
550 s = socket(ai->ai_family, ai->ai_socktype, 0);
552 test_printf("Can't bind socket\n");
553 // pexit("dgram socket");
555 if(bind(s, ai->ai_addr, sa_len(ai->ai_addr)) < 0) {
556 test_printf("Failed to bind family: %d\n", ai->ai_family);
557 // pexit("bind server error");
559 socks[sock_indx++] = s;
560 if (sock_indx >= MAXSOCK) {
561 pexit("Too many address types");
566 s = socket(ai->ai_family, ai->ai_socktype, 0);
568 pexit("dgram socket");
570 if(bind(s, ai->ai_addr, sa_len(ai->ai_addr)) < 0) {
571 test_printf("Failed to bind family: %d\n", ai->ai_family);
572 pexit("bind server error");
574 socks[sock_indx++] = s;
575 if (sock_indx >= MAXSOCK) {
576 pexit("Too many address types");
580 if (s > last_sock) last_sock = s;
582 while (!done && (s = waitfor(socks, sock_indx, last_sock))) {
583 // Data is available from socket 's'
584 masterlen = sizeof(master);
585 if (recvfrom(s, &req, sizeof(req), 0, (struct sockaddr *)&master, &masterlen) < 0) {
586 pexit("recvfrom service");
589 _inet_ntop((struct sockaddr *)&master, addr_buf, sizeof(addr_buf));
590 test_printf("Request %d %s(%d)\n", ntohl(req.type), addr_buf,
591 (struct sockaddr *)_inet_port(&master));
593 reply.response = htonl(NC_REPLY_ACK);
595 switch (ntohl(req.type)) {
596 case NC_REQUEST_DISCONNECT:
598 test_printf("Master has issued disconnect - I quit!\n");
600 case NC_REQUEST_UDP_SEND:
601 test_printf("UDP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
603 case NC_REQUEST_UDP_RECV:
604 test_printf("UDP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
606 case NC_REQUEST_UDP_ECHO:
607 test_printf("UDP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
609 case NC_REQUEST_TCP_SEND:
610 test_printf("TCP send - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
612 case NC_REQUEST_TCP_RECV:
613 test_printf("TCP recv - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
615 case NC_REQUEST_TCP_ECHO:
616 test_printf("TCP echo - %d buffers, %d bytes\n", ntohl(req.nbufs), ntohl(req.buflen));
619 case NC_REQUEST_SET_LOAD:
620 start_load(ntohl(req.nbufs));
622 case NC_REQUEST_START_IDLE:
623 test_printf("Start IDLE thread\n");
624 idle_thread_count = 0;
625 idle_thread_start_time = cyg_current_time();
626 cyg_semaphore_post(&idle_thread_sem);
628 case NC_REQUEST_STOP_IDLE:
629 cyg_semaphore_wait(&idle_thread_sem);
630 idle_thread_stop_time = cyg_current_time();
631 test_printf("Stop IDLE thread\n");
632 reply.misc.idle_results.elapsed_time = htonl(idle_thread_stop_time - idle_thread_start_time);
633 reply.misc.idle_results.count[0] = htonl(idle_thread_count >> 32);
634 reply.misc.idle_results.count[1] = htonl((long)idle_thread_count);
638 test_printf("Unrecognized request: %d\n", ntohl(req.type));
639 reply.response = htonl(NC_REPLY_NAK);
640 reply.reason = htonl(NC_REPLY_NAK_UNKNOWN_REQUEST);
643 if (sendto(s, &reply, sizeof(reply), 0, (struct sockaddr *)&master, masterlen) < 0) {
646 if (reply.response == ntohl(NC_REPLY_NAK)) {
649 switch (ntohl(req.type)) {
650 case NC_REQUEST_UDP_SEND:
651 case NC_REQUEST_UDP_RECV:
652 case NC_REQUEST_UDP_ECHO:
653 do_udp_test(s, &req, (struct sockaddr *)&master);
655 case NC_REQUEST_TCP_SEND:
656 case NC_REQUEST_TCP_RECV:
657 case NC_REQUEST_TCP_ECHO:
658 do_tcp_test(s, &req, (struct sockaddr *)&master);
660 case NC_REQUEST_START_IDLE:
661 case NC_REQUEST_STOP_IDLE:
662 case NC_REQUEST_SET_LOAD:
666 // cyg_kmem_print_stats();
671 net_test(test_param_t param)
675 test_printf("Start Network Characterization - SLAVE\n");
677 init_all_network_interfaces();
678 calibrate_load(DESIRED_BACKGROUND_LOAD);
680 // I can see what this is trying to do, but I get "bind: Address already in
681 // use" errors from the 2nd interface - and the parameter is not used
682 // anyway, so one thread does quite well enough (but only tests one i/f at
685 // Comment in the 'int i' above too.
686 for (i = 1; i < CYGHWR_NET_DRIVERS; i++) {
687 cyg_thread_resume(main_thread_handle[i]); // Start other threads
693 #ifdef CYGDBG_NET_TIMING_STATS
702 // This function is called to calibrate the "background load" which can be
703 // applied during testing. It will be called before any commands from the
707 calibrate_load(int desired_load)
709 long long no_load_idle, load_idle;
714 high = MAX_LOAD_THREAD_LEVEL;
715 low = MIN_LOAD_THREAD_LEVEL;
717 // Compute the "no load" idle value
718 idle_thread_count = 0;
719 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
720 cyg_thread_delay(1*100); // Pause for one second
721 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
722 no_load_idle = idle_thread_count;
723 diag_printf("No load = %d\n", (int)idle_thread_count);
725 // First ensure that the HIGH level is indeed higher
727 load_thread_level = high;
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("High Load[%d] = %d => %d%%\n", load_thread_level,
737 (int)idle_thread_count, percent_load);
738 if ( percent_load > desired_load )
739 break; // HIGH level is indeed higher
740 low = load_thread_level; // known to be lower
741 high *= 2; // else double it and try again
744 // Now chop down to the level required
746 load_thread_level = (high + low) / 2;
747 start_load(desired_load); // Start up a given load
748 idle_thread_count = 0;
749 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
750 cyg_thread_delay(1*100); // Pause for one second
751 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
752 load_idle = idle_thread_count;
753 start_load(0); // Shut down background load
754 percent_load = 100 - ((load_idle * 100) / no_load_idle);
755 diag_printf("Load[%d] = %d => %d%%\n", load_thread_level,
756 (int)idle_thread_count, percent_load);
757 if (((high-low) <= 1) || (abs(desired_load-percent_load) <= 2)) break;
758 if (percent_load < desired_load) {
759 low = load_thread_level;
761 high = load_thread_level;
765 // Now we are within a few percent of the target; scale the load
766 // factor to get a better fit, and test it, print the answer.
767 load_thread_level *= desired_load;
768 load_thread_level /= percent_load;
769 start_load(desired_load); // Start up a given load
770 idle_thread_count = 0;
771 cyg_semaphore_post(&idle_thread_sem); // Start idle thread
772 cyg_thread_delay(1*100); // Pause for one second
773 cyg_semaphore_wait(&idle_thread_sem); // Stop idle thread
774 load_idle = idle_thread_count;
775 start_load(0); // Shut down background load
776 percent_load = 100 - ((load_idle * 100) / no_load_idle);
777 diag_printf("Final load[%d] = %d => %d%%\n", load_thread_level,
778 (int)idle_thread_count, percent_load);
779 // no_load_idle_count_1_second = no_load_idle;
783 // This function is called to set up a load level of 'load' percent (given
784 // as a whole number, e.g. start_load(20) would mean initiate a background
785 // load of 20%, leaving the cpu 80% idle).
790 static int prev_load = 0;
792 test_printf("Set background load = %d%%\n", load);
794 if (prev_load == 0) return; // Nothing out there to stop
795 for (i = 0; i < prev_load/10; i++) {
796 cyg_semaphore_wait(&load_thread_sem[i]);
800 for (i = 0; i < load/10; i++) {
801 cyg_semaphore_post(&load_thread_sem[i]);
808 // These thread(s) do some amount of "background" computing. This is used
809 // to simulate a given load level. They need to be run at a higher priority
810 // than the network code itself.
812 // Like the "idle" thread, they run as long as their "switch" (aka semaphore)
816 net_load(cyg_addrword_t who)
820 cyg_semaphore_wait(&load_thread_sem[who]);
821 for (i = 0; i < load_thread_level; i++) {
822 do_some_random_computation(i);
824 cyg_thread_delay(1); // Wait until the next 'tick'
825 cyg_semaphore_post(&load_thread_sem[who]);
830 // Some arbitrary computation, designed to use up the CPU and cause associated
831 // cache "thrash" behaviour - part of background load modelling.
834 do_some_random_computation(int p)
836 // Just something that might be "hard"
838 x = ((p * 10) * 3.14159) / 180.0; // radians
842 // This thread does nothing but count. It will be allowed to count
843 // as long as the semaphore is "free".
846 net_idle(cyg_addrword_t param)
849 cyg_semaphore_wait(&idle_thread_sem);
851 cyg_semaphore_post(&idle_thread_sem);
859 // Create processing threads
860 for (i = 0; i < CYGHWR_NET_DRIVERS; i++) {
861 cyg_thread_create(MAIN_THREAD_PRIORITY, // Priority
863 i, // entry parameter
864 "Network test", // Name
865 &main_thread_stack[i][0], // Stack
867 &main_thread_handle[i], // Handle
868 &main_thread_data[i] // Thread data structure
871 cyg_thread_resume(main_thread_handle[0]); // Start first one
872 // Create the idle thread environment
873 cyg_semaphore_init(&idle_thread_sem, 0);
874 cyg_thread_create(IDLE_THREAD_PRIORITY, // Priority
876 0, // entry parameter
877 "Network idle", // Name
878 &idle_thread_stack[0], // Stack
880 &idle_thread_handle, // Handle
881 &idle_thread_data // Thread data structure
883 cyg_thread_resume(idle_thread_handle); // Start it
884 // Create the load threads and their environment(s)
885 for (i = 0; i < NUM_LOAD_THREADS; i++) {
886 cyg_semaphore_init(&load_thread_sem[i], 0);
887 cyg_thread_create(LOAD_THREAD_PRIORITY, // Priority
889 i, // entry parameter
890 "Background load", // Name
891 &load_thread_stack[i][0], // Stack
893 &load_thread_handle[i], // Handle
894 &load_thread_data[i] // Thread data structure
896 cyg_thread_resume(load_thread_handle[i]); // Start it
898 cyg_scheduler_start();
904 main(int argc, char *argv[])