/* | |
* FreeRTOS Kernel V10.0.1 | |
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of | |
* this software and associated documentation files (the "Software"), to deal in | |
* the Software without restriction, including without limitation the rights to | |
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
* the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* http://www.FreeRTOS.org | |
* http://aws.amazon.com/freertos | |
* | |
* 1 tab == 4 spaces! | |
*/ | |
/****************************************************************************** | |
* | |
* See the following web page for essential TwoEchoClient.c usage and | |
* configuration details: | |
* http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_UDP/Embedded_Ethernet_Examples/Common_Echo_Clients.shtml | |
* | |
******************************************************************************/ | |
/* Standard includes. */ | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
/* FreeRTOS+UDP includes. */ | |
#include "FreeRTOS_UDP_IP.h" | |
#include "FreeRTOS_Sockets.h" | |
/* Small delay used between attempts to obtain a zero copy buffer. */ | |
#define echoTINY_DELAY ( ( TickType_t ) 2 ) | |
/* The echo tasks create a socket, send out a number of echo requests | |
(listening for each echo reply), then close the socket again before | |
starting over. This delay is used between each iteration to ensure the | |
network does not get too congested. */ | |
#define echoLOOP_DELAY ( ( TickType_t ) 250 / portTICK_RATE_MS ) | |
#if ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 | |
/* When the trace recorder code is included user events are generated to | |
mark the sending and receiving of the echoed data (only in the zero copy | |
task. */ | |
#define echoMARK_SEND_IN_TRACE_BUFFER( x ) vTraceUserEvent( x ) | |
traceLabel xZeroCopySendEvent, xZeroCopyReceiveEvent; | |
#else | |
/* When the trace recorder code is not included just #define away the call | |
to post the user event. */ | |
#define echoMARK_SEND_IN_TRACE_BUFFER( x ) | |
#define xZeroCopySendEvent 0 | |
#define xZeroCopyReceiveEvent 0 | |
#endif | |
/* The echo server is assumed to be on port 7, which is the standard echo | |
protocol port. */ | |
#define echoECHO_PORT ( 7 ) | |
/* | |
* Uses a socket to send data to, then receive data from, the standard echo | |
* port number 7. prvEchoClientTask() uses the standard interface. | |
* prvZeroCopyEchoClientTask() uses the zero copy interface. | |
*/ | |
static void prvEchoClientTask( void *pvParameters ); | |
static void prvZeroCopyEchoClientTask( void *pvParameters ); | |
/* The receive timeout is set shorter when the windows simulator is used | |
because simulated time is slower than real time. */ | |
#ifdef _WINDOWS_ | |
const TickType_t xReceiveTimeOut = 50 / portTICK_RATE_MS; | |
#else | |
const TickType_t xReceiveTimeOut = 500 / portTICK_RATE_MS; | |
#endif | |
/*-----------------------------------------------------------*/ | |
void vStartEchoClientTasks( uint16_t usTaskStackSize, UBaseType_t uxTaskPriority ) | |
{ | |
/* Create the echo client task that does not use the zero copy interface. */ | |
xTaskCreate( prvEchoClientTask, /* The function that implements the task. */ | |
"Echo0", /* Just a text name for the task to aid debugging. */ | |
usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */ | |
NULL, /* The task parameter, not used in this case. */ | |
uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */ | |
NULL ); /* The task handle is not used. */ | |
/* Create the echo client task that does use the zero copy interface. */ | |
xTaskCreate( prvZeroCopyEchoClientTask, /* The function that implements the task. */ | |
"Echo1", /* Just a text name for the task to aid debugging. */ | |
usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */ | |
NULL, /* The task parameter, not used in this case. */ | |
uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */ | |
NULL ); /* The task handle is not used. */ | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvEchoClientTask( void *pvParameters ) | |
{ | |
xSocket_t xSocket; | |
struct freertos_sockaddr xEchoServerAddress; | |
char cTxString[ 25 ], cRxString[ 25 ]; /* Make sure the stack is large enough to hold these. Turn on stack overflow checking during debug to be sure. */ | |
int32_t lLoopCount = 0UL; | |
const int32_t lMaxLoopCount = 50; | |
volatile uint32_t ulRxCount = 0UL, ulTxCount = 0UL; | |
uint32_t xAddressLength = sizeof( xEchoServerAddress ); | |
/* Remove compiler warning about unused parameters. */ | |
( void ) pvParameters; | |
/* Echo requests are sent to the echo server. The address of the echo | |
server is configured by the constants configECHO_SERVER_ADDR0 to | |
configECHO_SERVER_ADDR3 in FreeRTOSConfig.h. */ | |
xEchoServerAddress.sin_port = FreeRTOS_htons( echoECHO_PORT ); | |
xEchoServerAddress.sin_addr = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0, | |
configECHO_SERVER_ADDR1, | |
configECHO_SERVER_ADDR2, | |
configECHO_SERVER_ADDR3 ); | |
for( ;; ) | |
{ | |
/* Create a socket. */ | |
xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); | |
configASSERT( xSocket != FREERTOS_INVALID_SOCKET ); | |
/* Set a time out so a missing reply does not cause the task to block | |
indefinitely. */ | |
FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) ); | |
/* Send a number of echo requests. */ | |
for( lLoopCount = 0; lLoopCount < lMaxLoopCount; lLoopCount++ ) | |
{ | |
/* Create the string that is sent to the echo server. */ | |
sprintf( cTxString, "Message number %u\r\n", ( unsigned int ) ulTxCount ); | |
/* Send the string to the socket. ulFlags is set to 0, so the zero | |
copy interface is not used. That means the data from cTxString is | |
copied into a network buffer inside FreeRTOS_sendto(), and cTxString | |
can be reused as soon as FreeRTOS_sendto() has returned. 1 is added | |
to ensure the NULL string terminator is sent as part of the message. */ | |
FreeRTOS_sendto( xSocket, /* The socket being sent to. */ | |
( void * ) cTxString, /* The data being sent. */ | |
strlen( cTxString ) + 1,/* The length of the data being sent. */ | |
0, /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */ | |
&xEchoServerAddress, /* The destination address. */ | |
sizeof( xEchoServerAddress ) ); | |
/* Keep a count of how many echo requests have been transmitted so | |
it can be compared to the number of echo replies received. It would | |
be expected to loose at least one to an ARP message the first time | |
the connection is created. */ | |
ulTxCount++; | |
/* Receive data echoed back to the socket. ulFlags is zero, so the | |
zero copy option is not being used and the received data will be | |
copied into the buffer pointed to by cRxString. xAddressLength is | |
not actually used (at the time of writing this comment, anyway) by | |
FreeRTOS_recvfrom(), but is set appropriately in case future | |
versions do use it. */ | |
memset( ( void * ) cRxString, 0x00, sizeof( cRxString ) ); | |
FreeRTOS_recvfrom( xSocket, /* The socket being received from. */ | |
cRxString, /* The buffer into which the received data will be written. */ | |
sizeof( cRxString ), /* The size of the buffer provided to receive the data. */ | |
0, /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */ | |
&xEchoServerAddress, /* The address from where the data was sent (the source address). */ | |
&xAddressLength ); | |
/* Compare the transmitted string to the received string. */ | |
if( strcmp( cRxString, cTxString ) == 0 ) | |
{ | |
/* The echo reply was received without error. */ | |
ulRxCount++; | |
} | |
}; | |
/* Pause for a short while to ensure the network is not too | |
congested. */ | |
vTaskDelay( echoLOOP_DELAY ); | |
/* Close this socket before looping back to create another. */ | |
FreeRTOS_closesocket( xSocket ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvZeroCopyEchoClientTask( void *pvParameters ) | |
{ | |
xSocket_t xSocket; | |
struct freertos_sockaddr xEchoServerAddress; | |
static char cTxString[ 40 ]; | |
int32_t lLoopCount = 0UL; | |
volatile uint32_t ulRxCount = 0UL, ulTxCount = 0UL; | |
uint32_t xAddressLength = sizeof( xEchoServerAddress ); | |
int32_t lReturned; | |
uint8_t *pucUDPPayloadBuffer; | |
const int32_t lMaxLoopCount = 50; | |
const char * const pcStringToSend = "Zero copy message number"; | |
/* The buffer is large enough to hold the string, a number, and the string terminator. */ | |
const size_t xBufferLength = strlen( pcStringToSend ) + 15; | |
#if ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 | |
{ | |
/* When the trace recorder code is included user events are generated to | |
mark the sending and receiving of the echoed data (only in the zero copy | |
task). */ | |
xZeroCopySendEvent = xTraceOpenLabel( "ZeroCopyTx" ); | |
xZeroCopyReceiveEvent = xTraceOpenLabel( "ZeroCopyRx" ); | |
} | |
#endif /* ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS */ | |
/* Remove compiler warning about unused parameters. */ | |
( void ) pvParameters; | |
/* Delay for a little while to ensure the task is out of synch with the | |
other echo task implemented above. */ | |
vTaskDelay( echoLOOP_DELAY >> 1 ); | |
/* Echo requests are sent to the echo server. The address of the echo | |
server is configured by the constants configECHO_SERVER_ADDR0 to | |
configECHO_SERVER_ADDR3 in FreeRTOSConfig.h. */ | |
xEchoServerAddress.sin_port = FreeRTOS_htons( echoECHO_PORT ); | |
xEchoServerAddress.sin_addr = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0, | |
configECHO_SERVER_ADDR1, | |
configECHO_SERVER_ADDR2, | |
configECHO_SERVER_ADDR3 ); | |
for( ;; ) | |
{ | |
/* Create a socket. */ | |
xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); | |
configASSERT( xSocket != FREERTOS_INVALID_SOCKET ); | |
/* Set a time out so a missing reply does not cause the task to block | |
indefinitely. */ | |
FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) ); | |
/* Send a number of echo requests. */ | |
for( lLoopCount = 0; lLoopCount < lMaxLoopCount; lLoopCount++ ) | |
{ | |
/* This task is going to send using the zero copy interface. The | |
data being sent is therefore written directly into a buffer that is | |
passed by reference into the FreeRTOS_sendto() function. First | |
obtain a buffer of adequate size from the IP stack. Although a max | |
delay is used, the actual delay will be capped to | |
ipconfigMAX_SEND_BLOCK_TIME_TICKS, hence the test to ensure a buffer | |
was actually obtained. */ | |
pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xBufferLength, portMAX_DELAY ); | |
if( pucUDPPayloadBuffer != NULL ) | |
{ | |
/* A buffer was successfully obtained. Create the string that is | |
sent to the echo server. Note the string is written directly | |
into the buffer obtained from the IP stack. */ | |
sprintf( ( char * ) pucUDPPayloadBuffer, "%s %u\r\n", "Zero copy message number", ( unsigned int ) ulTxCount ); | |
/* Also copy the string into a local buffer so it can be compared | |
with the string that is later received back from the echo server. */ | |
strcpy( cTxString, ( char * ) pucUDPPayloadBuffer ); | |
/* Pass the buffer into the send function. ulFlags has the | |
FREERTOS_ZERO_COPY bit set so the IP stack will take control of | |
the buffer, rather than copy data out of the buffer. */ | |
echoMARK_SEND_IN_TRACE_BUFFER( xZeroCopySendEvent ); | |
lReturned = FreeRTOS_sendto( xSocket, /* The socket being sent to. */ | |
( void * ) pucUDPPayloadBuffer, /* The buffer being passed into the IP stack. */ | |
strlen( cTxString ) + 1, /* The length of the data being sent. Plus 1 to ensure the null terminator is part of the data. */ | |
FREERTOS_ZERO_COPY, /* ulFlags with the zero copy bit is set. */ | |
&xEchoServerAddress, /* Where the data is being sent. */ | |
sizeof( xEchoServerAddress ) ); | |
if( lReturned == 0 ) | |
{ | |
/* The send operation failed, so this task is still | |
responsible for the buffer obtained from the IP stack. To | |
ensure the buffer is not lost it must either be used again, | |
or, as in this case, returned to the IP stack using | |
FreeRTOS_ReleaseUDPPayloadBuffer(). pucUDPPayloadBuffer can | |
be safely re-used to receive from the socket below once the | |
buffer has been returned to the stack. */ | |
FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer ); | |
} | |
else | |
{ | |
/* The send was successful so the IP stack is now managing | |
the buffer pointed to by pucUDPPayloadBuffer, and the IP | |
stack will return the buffer once it has been sent. | |
pucUDPPayloadBuffer can be safely re-used to receive from | |
the socket below. */ | |
} | |
/* Keep a count of how many echo requests have been transmitted | |
so it can be compared to the number of echo replies received. | |
It would be expected to loose at least one to an ARP message the | |
first time the connection is created. */ | |
ulTxCount++; | |
/* Receive data on the socket. ulFlags has the zero copy bit set | |
(FREERTOS_ZERO_COPY) indicating to the stack that a reference to | |
the received data should be passed out to this task using the | |
second parameter to the FreeRTOS_recvfrom() call. When this is | |
done the IP stack is no longer responsible for releasing the | |
buffer, and the task *must* return the buffer to the stack when | |
it is no longer needed. By default the receive block time is | |
portMAX_DELAY. */ | |
echoMARK_SEND_IN_TRACE_BUFFER( xZeroCopyReceiveEvent ); | |
lReturned = FreeRTOS_recvfrom( xSocket, /* The socket to receive from. */ | |
( void * ) &pucUDPPayloadBuffer, /* pucUDPPayloadBuffer will be set to point to the buffer that already contains the received data. */ | |
0, /* Ignored because the zero copy interface is being used. */ | |
FREERTOS_ZERO_COPY, /* ulFlags with the FREERTOS_ZERO_COPY bit set. */ | |
&xEchoServerAddress, /* The address from which the data was sent. */ | |
&xAddressLength ); | |
if( lReturned > 0 ) | |
{ | |
/* Compare the string sent to the echo server with the string | |
received back from the echo server. */ | |
if( strcmp( ( char * ) pucUDPPayloadBuffer, cTxString ) == 0 ) | |
{ | |
/* The strings matched. */ | |
ulRxCount++; | |
} | |
/* The buffer that contains the data passed out of the stack | |
*must* be returned to the stack. */ | |
FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); | |
} | |
} | |
} | |
/* Pause for a short while to ensure the network is not too | |
congested. */ | |
vTaskDelay( echoLOOP_DELAY ); | |
/* Close this socket before looping back to create another. */ | |
FreeRTOS_closesocket( xSocket ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |