/***************************************************************************/ /* Copyright (c) 2024 Microsoft Corporation */ /* Copyright (c) 2026 Eclipse ThreadX contributors */ /* */ /* This program and the accompanying materials are made available under */ /* the terms of the MIT License which is available at */ /* https://opensource.org/licenses/MIT. */ /* */ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ /* This NetX test concentrates on the small window TCP operation. This is the same as the small window test... but instead the receiver keeps the receive queue empty so the immediate ACK must come back from the socket data receive function. */ #include "tx_api.h" #include "nx_api.h" extern void test_control_return(UINT status); #if !defined(NX_DISABLE_IPV4) #define DEMO_STACK_SIZE 2048 /* Define the ThreadX and NetX object control blocks... */ static TX_THREAD ntest_0; static TX_THREAD ntest_1; static NX_PACKET_POOL pool_0; static NX_IP ip_0; static NX_IP ip_1; static NX_TCP_SOCKET client_socket; static NX_TCP_SOCKET server_socket; /* Define the counters used in the test application... */ static ULONG error_counter; /* Define thread prototypes. */ static void ntest_0_entry(ULONG thread_input); static void ntest_1_entry(ULONG thread_input); static void ntest_1_connect_received(NX_TCP_SOCKET *server_socket, UINT port); static void ntest_1_disconnect_received(NX_TCP_SOCKET *server_socket); extern void _nx_ram_network_driver_256(struct NX_IP_DRIVER_STRUCT *driver_req); /* Define what the initial system looks like. */ #ifdef CTEST VOID test_application_define(void *first_unused_memory) #else void netx_tcp_small_window_preempt_test_application_define(void *first_unused_memory) #endif { CHAR *pointer; UINT status; /* Setup the working pointer. */ pointer = (CHAR *) first_unused_memory; error_counter = 0; /* Create the main thread. */ tx_thread_create(&ntest_0, "thread 0", ntest_0_entry, 0, pointer, DEMO_STACK_SIZE, 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); pointer = pointer + DEMO_STACK_SIZE; /* Create the main thread. */ tx_thread_create(&ntest_1, "thread 1", ntest_1_entry, 0, pointer, DEMO_STACK_SIZE, 3, 3, TX_NO_TIME_SLICE, TX_AUTO_START); pointer = pointer + DEMO_STACK_SIZE; /* Initialize the NetX system. */ nx_system_initialize(); /* Create a packet pool. */ status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 256, pointer, 8192); pointer = pointer + 8192; if (status) error_counter++; /* Create an IP instance. */ status = nx_ip_create(&ip_0, "NetX IP Instance 0", IP_ADDRESS(1, 2, 3, 4), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver_256, pointer, 2048, 1); pointer = pointer + 2048; /* Create another IP instance. */ status += nx_ip_create(&ip_1, "NetX IP Instance 1", IP_ADDRESS(1, 2, 3, 5), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver_256, pointer, 2048, 1); pointer = pointer + 2048; if (status) error_counter++; /* Enable ARP and supply ARP cache memory for IP Instance 0. */ status = nx_arp_enable(&ip_0, (void *) pointer, 1024); pointer = pointer + 1024; if (status) error_counter++; /* Enable ARP and supply ARP cache memory for IP Instance 1. */ status = nx_arp_enable(&ip_1, (void *) pointer, 1024); pointer = pointer + 1024; if (status) error_counter++; /* Enable TCP processing for both IP instances. */ status = nx_tcp_enable(&ip_0); status += nx_tcp_enable(&ip_1); /* Check TCP enable status. */ if (status) error_counter++; } /* Define the test threads. */ static void ntest_0_entry(ULONG thread_input) { UINT status; NX_PACKET *my_packet; ULONG start_time; ULONG end_time; /* Print out test information banner. */ printf("NetX Test: TCP Small Window Size Preemption Test....................."); /* Check for earlier error. */ if (error_counter) { printf("ERROR!\n"); test_control_return(1); } /* Create a socket. */ status = nx_tcp_socket_create(&ip_0, &client_socket, "Client Socket", NX_IP_NORMAL, NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, 128, NX_NULL, NX_NULL); /* Check for error. */ if (status) error_counter++; /* Bind the socket. */ status = nx_tcp_client_socket_bind(&client_socket, 12, NX_WAIT_FOREVER); /* Check for error. */ if (status) error_counter++; /* Attempt to connect the socket. */ status = nx_tcp_client_socket_connect(&client_socket, IP_ADDRESS(1, 2, 3, 5), 12, 132); /* Check for error. */ if (status) error_counter++; /* Send 5 packets... the 5th packet should block because of the window size. */ /* Allocate a 1st packet. */ status = nx_packet_allocate(&pool_0, &my_packet, NX_TCP_PACKET, NX_WAIT_FOREVER); /* Check status. */ if (status != NX_SUCCESS) error_counter++; /* Write ABCs into the packet payload! */ memcpy(my_packet -> nx_packet_prepend_ptr, "ABCDEFGHIJKLMNOPQRSTUVWXYZ ", 28); /* Adjust the write pointer. */ my_packet -> nx_packet_length = 28; my_packet -> nx_packet_append_ptr = my_packet -> nx_packet_prepend_ptr + 28; /* Send the packet out! */ status = nx_tcp_socket_send(&client_socket, my_packet, 5 * NX_IP_PERIODIC_RATE); /* Determine if the status is valid. */ if (status) { error_counter++; nx_packet_release(my_packet); } /* Allocate a 2nd packet. */ status = nx_packet_allocate(&pool_0, &my_packet, NX_TCP_PACKET, NX_WAIT_FOREVER); /* Check status. */ if (status != NX_SUCCESS) error_counter++; /* Write ABCs into the packet payload! */ memcpy(my_packet -> nx_packet_prepend_ptr, "ABCDEFGHIJKLMNOPQRSTUVWXYZ ", 28); /* Adjust the write pointer. */ my_packet -> nx_packet_length = 28; my_packet -> nx_packet_append_ptr = my_packet -> nx_packet_prepend_ptr + 28; /* Send the packet out! */ status = nx_tcp_socket_send(&client_socket, my_packet, 5 * NX_IP_PERIODIC_RATE); /* Determine if the status is valid. */ if (status) { error_counter++; nx_packet_release(my_packet); } /* Allocate a 3rd packet. */ status = nx_packet_allocate(&pool_0, &my_packet, NX_TCP_PACKET, NX_WAIT_FOREVER); /* Check status. */ if (status != NX_SUCCESS) error_counter++; /* Write ABCs into the packet payload! */ memcpy(my_packet -> nx_packet_prepend_ptr, "ABCDEFGHIJKLMNOPQRSTUVWXYZ ", 28); /* Adjust the write pointer. */ my_packet -> nx_packet_length = 28; my_packet -> nx_packet_append_ptr = my_packet -> nx_packet_prepend_ptr + 28; /* Send the packet out! */ status = nx_tcp_socket_send(&client_socket, my_packet, 5 * NX_IP_PERIODIC_RATE); /* Determine if the status is valid. */ if (status) { error_counter++; nx_packet_release(my_packet); } /* Allocate a 4th packet. */ status = nx_packet_allocate(&pool_0, &my_packet, NX_TCP_PACKET, NX_WAIT_FOREVER); /* Check status. */ if (status != NX_SUCCESS) error_counter++; /* Write ABCs into the packet payload! */ memcpy(my_packet -> nx_packet_prepend_ptr, "ABCDEFGHIJKLMNOPQRSTUVWXYZ ", 28); /* Adjust the write pointer. */ my_packet -> nx_packet_length = 28; my_packet -> nx_packet_append_ptr = my_packet -> nx_packet_prepend_ptr + 28; /* Send the packet out! */ status = nx_tcp_socket_send(&client_socket, my_packet, 5 * NX_IP_PERIODIC_RATE); /* Determine if the status is valid. */ if (status) { error_counter++; nx_packet_release(my_packet); } /* Allocate a 5th packet. */ status = nx_packet_allocate(&pool_0, &my_packet, NX_TCP_PACKET, NX_WAIT_FOREVER); /* Check status. */ if (status != NX_SUCCESS) error_counter++; /* Write ABCs into the packet payload! */ memcpy(my_packet -> nx_packet_prepend_ptr, "ABCDEFGHIJKLMNOPQRSTUVWXYZ ", 28); /* Adjust the write pointer. */ my_packet -> nx_packet_length = 28; my_packet -> nx_packet_append_ptr = my_packet -> nx_packet_prepend_ptr + 28; /* Remember the starting time. */ start_time = tx_time_get(); /* Send the packet out! */ status = nx_tcp_socket_send(&client_socket, my_packet, 5 * NX_IP_PERIODIC_RATE); /* Get the end time. */ end_time = tx_time_get(); /* Determine if the status is valid. */ if ((status) || (end_time > (start_time+NX_IP_PERIODIC_RATE/10))) { printf("ERROR!\n"); test_control_return(1); } /* Disconnect this socket. */ status = nx_tcp_socket_disconnect(&client_socket, 5 * NX_IP_PERIODIC_RATE); /* Determine if the status is valid. */ if (status) error_counter++; /* Unbind the socket. */ status = nx_tcp_client_socket_unbind(&client_socket); /* Check for error. */ if (status) error_counter++; /* Delete the socket. */ status = nx_tcp_socket_delete(&client_socket); /* Check for error. */ if (status) error_counter++; /* Determine if the test was successful. */ if (error_counter) { printf("ERROR!\n"); test_control_return(1); } else { printf("SUCCESS!\n"); test_control_return(0); } } static void ntest_1_entry(ULONG thread_input) { UINT status; NX_PACKET *packet_ptr; ULONG actual_status; /* Ensure the IP instance has been initialized. */ status = nx_ip_status_check(&ip_1, NX_IP_INITIALIZE_DONE, &actual_status, NX_IP_PERIODIC_RATE); /* Check status... */ if (status != NX_SUCCESS) { printf("ERROR!\n"); test_control_return(1); } /* Create a socket. */ status = nx_tcp_socket_create(&ip_1, &server_socket, "Server Socket", NX_IP_NORMAL, NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, 132, NX_NULL, ntest_1_disconnect_received); /* Check for error. */ if (status) error_counter++; /* Setup this thread to listen. */ status = nx_tcp_server_socket_listen(&ip_1, 12, &server_socket, 5, ntest_1_connect_received); /* Check for error. */ if (status) error_counter++; /* Accept a client socket connection. */ status = nx_tcp_server_socket_accept(&server_socket, 5 * NX_IP_PERIODIC_RATE); /* Check for error. */ if (status) error_counter++; /* Receive five TCP messages from the socket - 1st packet. */ status = nx_tcp_socket_receive(&server_socket, &packet_ptr, 5 * NX_IP_PERIODIC_RATE); /* Check for error. */ if ((status) || (packet_ptr -> nx_packet_length != 28)) error_counter++; else /* Release the packet. */ nx_packet_release(packet_ptr); /* Receive five TCP messages from the socket - 2nd packet. */ status = nx_tcp_socket_receive(&server_socket, &packet_ptr, 5 * NX_IP_PERIODIC_RATE); /* Check for error. */ if ((status) || (packet_ptr -> nx_packet_length != 28)) error_counter++; else /* Release the packet. */ nx_packet_release(packet_ptr); /* Receive five TCP messages from the socket - 3rd packet. */ status = nx_tcp_socket_receive(&server_socket, &packet_ptr, 5 * NX_IP_PERIODIC_RATE); /* Check for error. */ if ((status) || (packet_ptr -> nx_packet_length != 28)) error_counter++; else /* Release the packet. */ nx_packet_release(packet_ptr); /* Receive five TCP messages from the socket - 4th packet. */ status = nx_tcp_socket_receive(&server_socket, &packet_ptr, 5 * NX_IP_PERIODIC_RATE); /* Check for error. */ if ((status) || (packet_ptr -> nx_packet_length != 28)) error_counter++; else /* Release the packet. */ nx_packet_release(packet_ptr); /* Receive five TCP messages from the socket - 5th packet. */ status = nx_tcp_socket_receive(&server_socket, &packet_ptr, 5 * NX_IP_PERIODIC_RATE); /* Check for error. */ if ((status) || (packet_ptr -> nx_packet_length != 28)) error_counter++; else /* Release the packet. */ nx_packet_release(packet_ptr); /* Disconnect the server socket. */ status = nx_tcp_socket_disconnect(&server_socket, 5 * NX_IP_PERIODIC_RATE); /* Check for error. */ if (status) error_counter++; /* Unaccept the server socket. */ status = nx_tcp_server_socket_unaccept(&server_socket); /* Check for error. */ if (status) error_counter++; /* Setup server socket for listening again. */ status = nx_tcp_server_socket_relisten(&ip_1, 12, &server_socket); /* Check for error. */ if (status) error_counter++; } static void ntest_1_connect_received(NX_TCP_SOCKET *socket_ptr, UINT port) { /* Check for the proper socket and port. */ if ((socket_ptr != &server_socket) || (port != 12)) error_counter++; } static void ntest_1_disconnect_received(NX_TCP_SOCKET *socket) { /* Check for proper disconnected socket. */ if (socket != &server_socket) error_counter++; } #else #ifdef CTEST VOID test_application_define(void *first_unused_memory) #else void netx_tcp_small_window_preempt_test_application_define(void *first_unused_memory) #endif { /* Print out test information banner. */ printf("NetX Test: TCP Small Window Size Preemption Test.....................N/A\n"); test_control_return(3); } #endif