/*
 * FreeRTOS+UDP V1.0.4
 * 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!
 */

/* Standard includes. */
#include <stdint.h>

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

/* FreeRTOS+UDP includes. */
#include "FreeRTOS_UDP_IP.h"
#include "FreeRTOS_IP_Private.h"
#include "FreeRTOS_Sockets.h"
#include "NetworkBufferManagement.h"

/* Sanity check the UDP payload length setting is compatible with the
fragmentation setting. */
#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1
	#if ( ( ipMAX_UDP_PAYLOAD_LENGTH % 8 ) != 0 )
		#error ( ipconfigNETWORK_MTU - 28 ) must be divisible by 8 when fragmentation is used
	#endif /* ipMAX_UDP_PAYLOAD_LENGTH */
#endif /* ipconfigFRAGMENT_OUTGOING_PACKETS */

/* The ItemValue of the sockets xBoundSocketListItem member holds the socket's
port number. */
#define socketSET_SOCKET_ADDRESS( pxSocket, usPort ) listSET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ), ( usPort ) )
#define socketGET_SOCKET_ADDRESS( pxSocket ) listGET_LIST_ITEM_VALUE( ( &( ( pxSocket )->xBoundSocketListItem ) ) )

/* xWaitingPacketSemaphore is not created until the socket is bound, so can be
tested to see if bind() has been called. */
#define socketSOCKET_IS_BOUND( pxSocket ) ( ( BaseType_t ) pxSocket->xWaitingPacketSemaphore )

/* If FreeRTOS_sendto() is called on a socket that is not bound to a port
number then, depending on the FreeRTOSIPConfig.h settings, it might be that a
port number is automatically generated for the socket.  Automatically generated
port numbers will be between socketAUTO_PORT_ALLOCATION_START_NUMBER and
0xffff. */
#define socketAUTO_PORT_ALLOCATION_START_NUMBER ( ( uint16_t ) 0xc000 )

/* When the automatically generated port numbers overflow, the next value used
is not set back to socketAUTO_PORT_ALLOCATION_START_NUMBER because it is likely
that the first few automatically generated ports will still be in use.  Instead
it is reset back to the value defined by this constant. */
#define socketAUTO_PORT_ALLOCATION_RESET_NUMBER ( ( uint16_t ) 0xc100 )

/* The number of octets that make up an IP address. */
#define socketMAX_IP_ADDRESS_OCTETS		4
/*-----------------------------------------------------------*/

/*
 * Allocate the next port number from the private allocation range.
 */
static uint16_t prvGetPrivatePortNumber( void );

/*
 * Return the list itme from within pxList that has an item value of
 * xWantedItemValue.  If there is no such list item return NULL.
 */
xListItem * pxListFindListItemWithValue( xList *pxList, TickType_t xWantedItemValue );

/*-----------------------------------------------------------*/

typedef struct XSOCKET
{
	xSemaphoreHandle xWaitingPacketSemaphore;
	xList xWaitingPacketsList;
	xListItem xBoundSocketListItem; /* Used to reference the socket from a bound sockets list. */
	TickType_t xReceiveBlockTime;
	TickType_t xSendBlockTime;
	uint8_t ucSocketOptions;
	#if ipconfigSUPPORT_SELECT_FUNCTION == 1
		xQueueHandle xSelectQueue;
	#endif
} xFreeRTOS_Socket_t;


/* The list that contains mappings between sockets and port numbers.  Accesses
to this list must be protected by critical sections of one kind or another. */
static xList xBoundSocketsList;

/*-----------------------------------------------------------*/

xSocket_t FreeRTOS_socket( BaseType_t xDomain, BaseType_t xType, BaseType_t xProtocol )
{
xFreeRTOS_Socket_t *pxSocket;

	/* Only UDP on Ethernet is currently supported. */
	configASSERT( xDomain == FREERTOS_AF_INET );
	configASSERT( xType == FREERTOS_SOCK_DGRAM );
	configASSERT( xProtocol == FREERTOS_IPPROTO_UDP );
	configASSERT( listLIST_IS_INITIALISED( &xBoundSocketsList ) );

	/* Allocate the structure that will hold the socket information. */
	pxSocket = ( xFreeRTOS_Socket_t * ) pvPortMalloc( sizeof( xFreeRTOS_Socket_t ) );

	if( pxSocket == NULL )
	{
		pxSocket = ( xFreeRTOS_Socket_t * ) FREERTOS_INVALID_SOCKET;
		iptraceFAILED_TO_CREATE_SOCKET();
	}
	else
	{
		/* Initialise the socket's members.  The semaphore will be created if
		the socket is bound to an address, for now the pointer to the semaphore
		is just set to NULL to show it has not been created. */
		pxSocket->xWaitingPacketSemaphore = NULL;
		vListInitialise( &( pxSocket->xWaitingPacketsList ) );
		vListInitialiseItem( &( pxSocket->xBoundSocketListItem ) );
		listSET_LIST_ITEM_OWNER( &( pxSocket->xBoundSocketListItem ), ( void * ) pxSocket );
		pxSocket->xSendBlockTime = ( TickType_t ) 0;
		pxSocket->xReceiveBlockTime = portMAX_DELAY;
		pxSocket->ucSocketOptions = FREERTOS_SO_UDPCKSUM_OUT;
		#if ipconfigSUPPORT_SELECT_FUNCTION == 1
			pxSocket->xSelectQueue = NULL;
		#endif
	}

	/* Remove compiler warnings in the case the configASSERT() is not defined. */
	( void ) xDomain;
	( void ) xType;
	( void ) xProtocol;

	return ( xSocket_t ) pxSocket;
}
/*-----------------------------------------------------------*/

#if ipconfigSUPPORT_SELECT_FUNCTION == 1

	xSocketSet_t FreeRTOS_CreateSocketSet( UBaseType_t uxEventQueueLength )
	{
	xQueueHandle xSelectQueue;

		/* Create the queue into which the address of sockets that are
		available to read are posted. */
		xSelectQueue = xQueueCreate( uxEventQueueLength, sizeof( xSocket_t ) );

		return ( xSocketSet_t ) xSelectQueue;
	}

#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/

#if ipconfigSUPPORT_SELECT_FUNCTION == 1

	BaseType_t FreeRTOS_FD_SET( xSocket_t xSocket, xSocketSet_t xSocketSet )
	{
	xFreeRTOS_Socket_t *pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;
	BaseType_t xReturn = pdFALSE;
	UBaseType_t uxMessagesWaiting;

		configASSERT( xSocket );

		/* Is the socket already a member of a select group? */
		if( pxSocket->xSelectQueue == NULL )
		{
			taskENTER_CRITICAL();
			{
				/* Are there packets queued on the socket already? */
				uxMessagesWaiting = uxQueueMessagesWaiting( pxSocket->xWaitingPacketSemaphore );

				/* Are there enough notification spaces in the select queue for the
				number of packets already queued on the socket? */
				if( uxQueueSpacesAvailable( ( xQueueHandle ) xSocketSet ) >= uxMessagesWaiting )
				{
					/* Store a pointer to the select group in the socket for
					future reference. */
					pxSocket->xSelectQueue = ( xQueueHandle ) xSocketSet;

					while( uxMessagesWaiting > 0 )
					{
						/* Add notifications of the number of packets that are
						already queued on the socket to the select queue. */
						xQueueSendFromISR( pxSocket->xSelectQueue, &pxSocket, NULL );
						uxMessagesWaiting--;
					}

					xReturn = pdPASS;
				}
			}
			taskEXIT_CRITICAL();
		}

		return xReturn;
	}

#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/

#if ipconfigSUPPORT_SELECT_FUNCTION == 1

	BaseType_t FreeRTOS_FD_CLR( xSocket_t xSocket, xSocketSet_t xSocketSet )
	{
	xFreeRTOS_Socket_t *pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;
	BaseType_t xReturn;

		/* Is the socket a member of the select group? */
		if( pxSocket->xSelectQueue == ( xQueueHandle ) xSocketSet )
		{
			/* The socket is no longer a member of the select group. */
			pxSocket->xSelectQueue = NULL;
			xReturn = pdPASS;
		}
		else
		{
			xReturn = pdFAIL;
		}

		return xReturn;
	}

#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/

#if ipconfigSUPPORT_SELECT_FUNCTION == 1

	xSocket_t FreeRTOS_select( xSocketSet_t xSocketSet, TickType_t xBlockTimeTicks )
	{
	xFreeRTOS_Socket_t *pxSocket;

		/* Wait for a socket to be ready to read. */
		if( xQueueReceive( ( xQueueHandle ) xSocketSet, &pxSocket, xBlockTimeTicks ) != pdPASS )
		{
			pxSocket = NULL;
		}

		return ( xSocket_t ) pxSocket;
	}

#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/*-----------------------------------------------------------*/

int32_t FreeRTOS_recvfrom( xSocket_t xSocket, void *pvBuffer, size_t xBufferLength, uint32_t ulFlags, struct freertos_sockaddr *pxSourceAddress, socklen_t *pxSourceAddressLength )
{
xNetworkBufferDescriptor_t *pxNetworkBuffer;
int32_t lReturn;
xFreeRTOS_Socket_t *pxSocket;

	pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;

	/* The function prototype is designed to maintain the expected Berkeley
	sockets standard, but this implementation does not use all the parameters. */
	( void ) pxSourceAddressLength;

	if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE )
	{
		/* The semaphore is given when received data is queued on the socket. */
		if( xSemaphoreTake( pxSocket->xWaitingPacketSemaphore, pxSocket->xReceiveBlockTime ) == pdPASS )
		{
			taskENTER_CRITICAL();
			{
				configASSERT( ( listCURRENT_LIST_LENGTH( &( pxSocket->xWaitingPacketsList ) ) > 0U ) );

				/* The owner of the list item is the network buffer. */
				pxNetworkBuffer = ( xNetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->xWaitingPacketsList ) );

				/* Remove the network buffer from the list of buffers waiting to
				be processed by the socket. */
				uxListRemove( &( pxNetworkBuffer->xBufferListItem ) );
			}
			taskEXIT_CRITICAL();

			if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 )
			{
				/* The zero copy flag is not set.  Truncate the length if it
				won't fit in the provided buffer. */
				if( pxNetworkBuffer->xDataLength > xBufferLength )
				{
					iptraceRECVFROM_DISCARDING_BYTES( ( xBufferLength - pxNetworkBuffer->xDataLength ) );
					pxNetworkBuffer->xDataLength = xBufferLength;
				}

				/* Copy the received data into the provided buffer, then
				release the network buffer. */
				memcpy( pvBuffer, ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET ] ), pxNetworkBuffer->xDataLength );
				vNetworkBufferRelease( pxNetworkBuffer );
			}
			else
			{
				/* The zero copy flag was set.  pvBuffer is not a buffer into
				which the received data can be copied, but a pointer that must
				be set to point to the buffer in which the received data has
				already been placed. */
				*( ( void** ) pvBuffer ) = ( void * ) ( &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET ] ) );
			}

			/* The returned value is the data length, which may have been
			capped to the receive buffer size. */
			lReturn = ( int32_t ) pxNetworkBuffer->xDataLength;

			if( pxSourceAddress != NULL )
			{
				pxSourceAddress->sin_port = pxNetworkBuffer->usPort;
				pxSourceAddress->sin_addr = pxNetworkBuffer->ulIPAddress;
			}
		}
		else
		{
			lReturn = FREERTOS_EWOULDBLOCK;
			iptraceRECVFROM_TIMEOUT();
		}
	}
	else
	{
		lReturn = FREERTOS_EINVAL;
	}

	return lReturn;
}
/*-----------------------------------------------------------*/

#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1

	int32_t FreeRTOS_sendto( xSocket_t xSocket, const void *pvBuffer, size_t xTotalDataLength, uint32_t ulFlags, const struct freertos_sockaddr *pxDestinationAddress, socklen_t xDestinationAddressLength )
	{
	xNetworkBufferDescriptor_t *pxNetworkBuffer;
	xIPFragmentParameters_t *pxFragmentParameters;
	size_t xBytesToSend, xBytesRemaining;
	xIPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL };
	extern xQueueHandle xNetworkEventQueue;
	uint8_t *pucBuffer;
	xTimeOutType xTimeOut;
	TickType_t xTicksToWait;
	uint16_t usFragmentOffset;
	xFreeRTOS_Socket_t *pxSocket;

		pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;

		/* The function prototype is designed to maintain the expected Berkeley
		sockets standard, but this implementation does not use all the
		parameters. */
		( void ) xDestinationAddressLength;
		configASSERT( xNetworkEventQueue );
		configASSERT( pvBuffer );

		xBytesRemaining = xTotalDataLength;

		if( socketSOCKET_IS_BOUND( pxSocket ) == pdFALSE )
		{
			/* If the socket is not already bound to an address, bind it now.
			Passing NULL as the address parameter tells FreeRTOS_bind() to select
			the address to bind to. */
			FreeRTOS_bind( xSocket, NULL, 0 );
		}

		if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE )
		{
			/* pucBuffer will be reset if this send turns out to be a zero copy
			send because in that case pvBuffer is actually a pointer to an
			xUserData_t structure, not the UDP payload. */
			pucBuffer = ( uint8_t * ) pvBuffer;
			vTaskSetTimeOutState( &xTimeOut );
			xTicksToWait = pxSocket->xSendBlockTime;

			/* The data being transmitted will be sent in
			ipMAX_UDP_PAYLOAD_LENGTH chunks if xDataLength is greater than the
			network buffer payload size.  Loop until all the data is sent. */
			while( xBytesRemaining > 0 )
			{
				if( xBytesRemaining > ipMAX_UDP_PAYLOAD_LENGTH )
				{
					/* Cap the amount being sent in this packet to the maximum
					UDP payload size.  This will be a multiple of 8 already,
					removing the need to check in the code. */
					xBytesToSend = ipMAX_UDP_PAYLOAD_LENGTH;
				}
				else
				{
					/* Send all remaining bytes - which may well be the total
					number of bytes if the packet was not chopped up. */
					xBytesToSend = xBytesRemaining;
				}

				/* If the zero copy flag is set, then the data is already in a
				network buffer.  Otherwise, get a new network buffer. */
				if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 )
				{
					if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE )
					{
						xTicksToWait = 0;
					}

					pxNetworkBuffer = pxNetworkBufferGet( xBytesToSend + sizeof( xUDPPacket_t ), xTicksToWait );
				}
				else
				{
					if( xTotalDataLength > ipMAX_UDP_PAYLOAD_LENGTH )
					{
						/* The packet needs fragmenting, but zero copy buffers
						cannot be fragmented. */
						pxNetworkBuffer = NULL;
					}
					else
					{
						/* When zero copy is used, pvBuffer is a pointer to the
						payload of a buffer that has already been obtained from the
						stack.  Obtain the network buffer pointer from the buffer. */
						pucBuffer = ( uint8_t * ) pvBuffer;
						pucBuffer -= ( ipBUFFER_PADDING + sizeof( xUDPPacket_t ) );
						pxNetworkBuffer = * ( ( xNetworkBufferDescriptor_t ** ) pucBuffer );
					}
				}

				if( pxNetworkBuffer != NULL )
				{
					/* Use the part of the network buffer that will be completed
					by the IP layer as temporary storage to pass extra
					information required by the IP layer. */
					pxFragmentParameters = ( xIPFragmentParameters_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipFRAGMENTATION_PARAMETERS_OFFSET ] );
					pxFragmentParameters->ucSocketOptions = pxSocket->ucSocketOptions;

					if( xBytesRemaining > ipMAX_UDP_PAYLOAD_LENGTH )
					{
						/* The packet is being chopped up, and more data will
						follow. */
						pxFragmentParameters->ucSocketOptions = ( pxSocket->ucSocketOptions | FREERTOS_NOT_LAST_IN_FRAGMENTED_PACKET );
					}

					if( xTotalDataLength > ipMAX_UDP_PAYLOAD_LENGTH )
					{
						/* Let the IP layer know this packet has been chopped up,
						and supply the IP layer with any addition information it
						needs to make sense of it. */
						pxFragmentParameters->ucSocketOptions |= FREERTOS_FRAGMENTED_PACKET;
						usFragmentOffset = ( uint16_t ) ( xTotalDataLength - xBytesRemaining );
						pxFragmentParameters->usFragmentedPacketOffset = usFragmentOffset;
						pxFragmentParameters->usFragmentLength = ( uint16_t ) xBytesToSend;
					}
					else
					{
						usFragmentOffset = 0;
					}

					/* Write the payload into the packet.  The IP layer is
					queried to find where in the IP payload the data should be
					written.  This is because the necessary offset is different
					for the first packet, because the first packet leaves space
					for a UDP header.  Note that this changes usFragmentOffset
					from the offset in the entire UDP packet, to the offset
					in the IP packet. */
					if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 )
					{
						/* Only copy the data if it is not already in the
						expected location. */
						usFragmentOffset = ipGET_UDP_PAYLOAD_OFFSET_FOR_FRAGMENT( usFragmentOffset );
						memcpy( ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ usFragmentOffset ] ), ( void * ) pucBuffer, xBytesToSend );
					}
					pxNetworkBuffer->xDataLength = xTotalDataLength;
					pxNetworkBuffer->usPort = pxDestinationAddress->sin_port;
					pxNetworkBuffer->usBoundPort = ( uint16_t ) socketGET_SOCKET_ADDRESS( pxSocket );
					pxNetworkBuffer->ulIPAddress = pxDestinationAddress->sin_addr;

					/* Tell the networking task that the packet needs sending. */
					xStackTxEvent.pvData = pxNetworkBuffer;

					if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE )
					{
						xTicksToWait = 0;
					}

					if( xQueueSendToBack( xNetworkEventQueue, &xStackTxEvent, xTicksToWait ) != pdPASS )
					{
						/* If the buffer was allocated in this function, release it. */
						if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 )
						{
							vNetworkBufferRelease( pxNetworkBuffer );
						}
						iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT );
						break;
					}

					/* Adjust counters ready to either exit the loop, or send
					another chunk of data. */
					xBytesRemaining -= xBytesToSend;
					pucBuffer += xBytesToSend;
				}
				else
				{
					/* If errno was available, errno would be set to
					FREERTOS_ENOPKTS.  As it is, the function must return the
					number of transmitted bytes, so the calling function knows how
					much data was actually sent. */
					break;
				}
			}
		}

		return ( xTotalDataLength - xBytesRemaining );
	} /* Tested */

#else /* ipconfigCAN_FRAGMENT_OUTGOING_PACKETS */

	int32_t FreeRTOS_sendto( xSocket_t xSocket, const void *pvBuffer, size_t xTotalDataLength, uint32_t ulFlags, const struct freertos_sockaddr *pxDestinationAddress, socklen_t xDestinationAddressLength )
	{
	xNetworkBufferDescriptor_t *pxNetworkBuffer;
	xIPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL };
	extern xQueueHandle xNetworkEventQueue;
	xTimeOutType xTimeOut;
	TickType_t xTicksToWait;
	int32_t lReturn = 0;
	xFreeRTOS_Socket_t *pxSocket;
	uint8_t *pucBuffer;

		pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;

		/* The function prototype is designed to maintain the expected Berkeley
		sockets standard, but this implementation does not use all the
		parameters. */
		( void ) xDestinationAddressLength;
		configASSERT( xNetworkEventQueue );
		configASSERT( pvBuffer );

		if( xTotalDataLength <= ipMAX_UDP_PAYLOAD_LENGTH )
		{
			if( socketSOCKET_IS_BOUND( pxSocket ) == pdFALSE )
			{
				/* If the socket is not already bound to an address, bind it now.
				Passing NULL as the address parameter tells FreeRTOS_bind() to
				select the address to bind to. */
				FreeRTOS_bind( pxSocket, NULL, 0 );
			}

			if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE )
			{
				xTicksToWait = pxSocket->xSendBlockTime;

				if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 )
				{
					/* Zero copy is not set, so obtain a network buffer into
					which the payload will be copied. */
					vTaskSetTimeOutState( &xTimeOut );
					pxNetworkBuffer = pxNetworkBufferGet( xTotalDataLength + sizeof( xUDPPacket_t ), xTicksToWait );

					if( pxNetworkBuffer != NULL )
					{
						memcpy( ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET ] ), ( void * ) pvBuffer, xTotalDataLength );

						if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdTRUE )
						{
							/* The entire block time has been used up. */
							xTicksToWait = 0;
						}
					}
				}
				else
				{
					/* When zero copy is used, pvBuffer is a pointer to the
					payload of a buffer that has already been obtained from the
					stack.  Obtain the network buffer pointer from the buffer. */
					pucBuffer = ( uint8_t * ) pvBuffer;
					pucBuffer -= ( ipBUFFER_PADDING + sizeof( xUDPPacket_t ) );
					pxNetworkBuffer = * ( ( xNetworkBufferDescriptor_t ** ) pucBuffer );
				}

				if( pxNetworkBuffer != NULL )
				{
					pxNetworkBuffer->xDataLength = xTotalDataLength;
					pxNetworkBuffer->usPort = pxDestinationAddress->sin_port;
					pxNetworkBuffer->usBoundPort = ( uint16_t ) socketGET_SOCKET_ADDRESS( pxSocket );
					pxNetworkBuffer->ulIPAddress = pxDestinationAddress->sin_addr;

					/* The socket options are passed to the IP layer in the
					space that will eventually get used by the Ethernet header. */
					pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = pxSocket->ucSocketOptions;

					/* Tell the networking task that the packet needs sending. */
					xStackTxEvent.pvData = pxNetworkBuffer;

					if( xQueueSendToBack( xNetworkEventQueue, &xStackTxEvent, xTicksToWait ) != pdPASS )
					{
						/* If the buffer was allocated in this function, release it. */
						if( ( ulFlags & FREERTOS_ZERO_COPY ) == 0 )
						{
							vNetworkBufferRelease( pxNetworkBuffer );
						}
						iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT );
					}
					else
					{
						lReturn = ( int32_t ) xTotalDataLength;
					}
				}
				else
				{
					/* If errno was available, errno would be set to
					FREERTOS_ENOPKTS.  As it is, the function must return the
					number of transmitted bytes, so the calling function knows how
					much data was actually sent. */
					iptraceNO_BUFFER_FOR_SENDTO();
				}
			}
			else
			{
				iptraceSENDTO_SOCKET_NOT_BOUND();
			}
		}
		else
		{
			/* The data is longer than the available buffer space.  Setting
			ipconfigCAN_FRAGMENT_OUTGOING_PACKETS to 1 may allow this packet
			to be sent. */
			iptraceSENDTO_DATA_TOO_LONG();
		}

		return lReturn;
	} /* Tested */

#endif /* ipconfigCAN_FRAGMENT_OUTGOING_PACKETS */
/*-----------------------------------------------------------*/

BaseType_t FreeRTOS_bind( xSocket_t xSocket, struct freertos_sockaddr * pxAddress, socklen_t xAddressLength )
{
BaseType_t xReturn = 0; /* In Berkeley sockets, 0 means pass for bind(). */
xFreeRTOS_Socket_t *pxSocket;
#if ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1
	struct freertos_sockaddr xAddress;
#endif /* ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND */

	pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;

	/* The function prototype is designed to maintain the expected Berkeley
	sockets standard, but this implementation does not use all the parameters. */
	( void ) xAddressLength;

	configASSERT( xSocket );
	configASSERT( xSocket != FREERTOS_INVALID_SOCKET );

	#if ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1
	{
		/* pxAddress will be NULL if sendto() was called on a socket without the
		socket being bound to an address.  In this case, automatically allocate
		an address to the socket.  There is a very tiny chance that the allocated
		port will already be in use - if that is the case, then the check below
		[pxListFindListItemWithValue()] will result in an error being returned. */
		if( pxAddress == NULL )
		{
			pxAddress = &xAddress;
			pxAddress->sin_port = prvGetPrivatePortNumber();
		}
	}
	#endif /* ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND == 1 */

	/* Sockets must be bound before calling FreeRTOS_sendto() if
	ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND is not set to 1. */
	configASSERT( pxAddress );

	if( pxAddress != NULL )
	{
		if( pxAddress->sin_port == 0 )
		{
			pxAddress->sin_port = prvGetPrivatePortNumber();
		}

		vTaskSuspendAll();
		{
			/* Check to ensure the port is not already in use. */
			if( pxListFindListItemWithValue( &xBoundSocketsList, ( TickType_t ) pxAddress->sin_port ) != NULL )
			{
				xReturn = FREERTOS_EADDRINUSE;
			}
		}
		xTaskResumeAll();

		/* Check that xReturn has not been set before continuing. */
		if( xReturn == 0 )
		{
			if( pxSocket->xWaitingPacketSemaphore == NULL )
			{
				/* Create the semaphore used to count the number of packets that
				are queued on this socket. */
				pxSocket->xWaitingPacketSemaphore = xSemaphoreCreateCounting( ipconfigNUM_NETWORK_BUFFERS, 0 );

				if( pxSocket->xWaitingPacketSemaphore != NULL )
				{
					/* Allocate the port number to the socket. */
					socketSET_SOCKET_ADDRESS( pxSocket, pxAddress->sin_port );
					taskENTER_CRITICAL();
					{
						/* Add the socket to the list of bound ports. */
						vListInsertEnd( &xBoundSocketsList, &( pxSocket->xBoundSocketListItem ) );
					}
					taskEXIT_CRITICAL();
				}
				else
				{
					/* Out of memory. */
					xReturn = FREERTOS_ENOBUFS;
				}
			}
			else
			{
				/* The socket is already bound. */
				xReturn = FREERTOS_EINVAL;
			}
		}
	}
	else
	{
		xReturn = FREERTOS_EADDRNOTAVAIL;
	}

	if( xReturn != 0 )
	{
		iptraceBIND_FAILED( xSocket, ( FreeRTOS_ntohs( pxAddress->sin_port ) ) );
	}

	return xReturn;
} /* Tested */
/*-----------------------------------------------------------*/

BaseType_t FreeRTOS_closesocket( xSocket_t xSocket )
{
xNetworkBufferDescriptor_t *pxNetworkBuffer;
xFreeRTOS_Socket_t *pxSocket;

	pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;

	configASSERT( pxSocket );
	configASSERT( pxSocket != FREERTOS_INVALID_SOCKET );

	/* Socket must be unbound first, to ensure no more packets are queued on
	it. */
	if( socketSOCKET_IS_BOUND( pxSocket ) != pdFALSE )
	{
		taskENTER_CRITICAL();
		{
			uxListRemove( &( pxSocket->xBoundSocketListItem ) );
		}
		taskEXIT_CRITICAL();
	}

	/* Now the socket is not bound the list of waiting packets can be
	drained. */
	if( pxSocket->xWaitingPacketSemaphore != NULL )
	{
		while( listCURRENT_LIST_LENGTH( &( pxSocket->xWaitingPacketsList ) ) > 0U )
		{
			pxNetworkBuffer = ( xNetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->xWaitingPacketsList ) );
			uxListRemove( &( pxNetworkBuffer->xBufferListItem ) );
			vNetworkBufferRelease( pxNetworkBuffer );
		}
		vSemaphoreDelete( pxSocket->xWaitingPacketSemaphore );
	}

	vPortFree( pxSocket );

	return 0;
} /* Tested */
/*-----------------------------------------------------------*/

void FreeRTOS_SocketsInit( void )
{
	vListInitialise( &xBoundSocketsList );
}
/*-----------------------------------------------------------*/

BaseType_t FreeRTOS_setsockopt( xSocket_t xSocket, int32_t lLevel, int32_t lOptionName, const void *pvOptionValue, size_t xOptionLength )
{
/* The standard Berkeley function returns 0 for success. */
BaseType_t xReturn = 0;
BaseType_t lOptionValue;
xFreeRTOS_Socket_t *pxSocket;

	pxSocket = ( xFreeRTOS_Socket_t * ) xSocket;

	/* The function prototype is designed to maintain the expected Berkeley
	sockets standard, but this implementation does not use all the parameters. */
	( void ) lLevel;
	( void ) xOptionLength;

	configASSERT( xSocket );

	switch( lOptionName )
	{
		case FREERTOS_SO_RCVTIMEO	:
			/* Receive time out. */
			pxSocket->xReceiveBlockTime = *( ( TickType_t * ) pvOptionValue );
			break;

		case FREERTOS_SO_SNDTIMEO	:
			/* The send time out is capped for the reason stated in the comments
			where ipconfigMAX_SEND_BLOCK_TIME_TICKS is defined in
			FreeRTOSIPConfig.h (assuming an official configuration file is being
			used. */
			pxSocket->xSendBlockTime = *( ( TickType_t * ) pvOptionValue );
			if( pxSocket->xSendBlockTime > ipconfigMAX_SEND_BLOCK_TIME_TICKS )
			{
				pxSocket->xSendBlockTime = ipconfigMAX_SEND_BLOCK_TIME_TICKS;
			}
			break;

		case FREERTOS_SO_UDPCKSUM_OUT :
			/* Turn calculating of the UDP checksum on/off for this socket. */
			lOptionValue = ( BaseType_t ) pvOptionValue;

			if( lOptionValue == 0 )
			{
				pxSocket->ucSocketOptions &= ~FREERTOS_SO_UDPCKSUM_OUT;
			}
			else
			{
				pxSocket->ucSocketOptions |= FREERTOS_SO_UDPCKSUM_OUT;
			}
			break;

		default :
			/* No other options are handled. */
			xReturn = FREERTOS_ENOPROTOOPT;
			break;
	}

	return xReturn;
} /* Tested */
/*-----------------------------------------------------------*/

BaseType_t xProcessReceivedUDPPacket( xNetworkBufferDescriptor_t *pxNetworkBuffer, uint16_t usPort )
{
xListItem *pxListItem;
BaseType_t xReturn = pdPASS;
xFreeRTOS_Socket_t *pxSocket;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;

	vTaskSuspendAll();
	{
		/* See if there is a list item associated with the port number on the
		list of bound sockets. */
		pxListItem = pxListFindListItemWithValue( &xBoundSocketsList, ( TickType_t ) usPort );
	}
	xTaskResumeAll();

	if( pxListItem != NULL )
	{
		/* The owner of the list item is the socket itself. */
		pxSocket = ( xFreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxListItem );

		vTaskSuspendAll();
		{
			#if( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
			{
				/* Is the socket a member of a select() group? */
				if( pxSocket->xSelectQueue != NULL )
				{
					/* Can the select group be notified that the socket is
					ready to be read? */
					if( xQueueSendFromISR( pxSocket->xSelectQueue, &pxSocket, &xHigherPriorityTaskWoken ) != pdPASS )
					{
						/* Could not notify the select group. */
						xReturn = pdFAIL;
						iptraceFAILED_TO_NOTIFY_SELECT_GROUP( pxSocket );
					}
				}
			}
			#endif

			if( xReturn == pdPASS )
			{
				taskENTER_CRITICAL();
				{
					/* Add the network packet to the list of packets to be
					processed by the socket. */
					vListInsertEnd( &( pxSocket->xWaitingPacketsList ), &( pxNetworkBuffer->xBufferListItem ) );
				}
				taskEXIT_CRITICAL();

				/* The socket's counting semaphore records how many packets are
				waiting	to be processed by the socket. */
				xSemaphoreGiveFromISR( pxSocket->xWaitingPacketSemaphore, &xHigherPriorityTaskWoken );
			}
		}
		if( xTaskResumeAll() == pdFALSE )
		{
			if( xHigherPriorityTaskWoken != pdFALSE )
			{
				taskYIELD();
			}
		}
	}
	else
	{
		xReturn = pdFAIL;
	}

	return xReturn;
}
/*-----------------------------------------------------------*/

static uint16_t prvGetPrivatePortNumber( void )
{
static uint16_t usNextPortToUse = socketAUTO_PORT_ALLOCATION_START_NUMBER - 1;
uint16_t usReturn;

	/* Assign the next port in the range. */
	taskENTER_CRITICAL();
	{
		usNextPortToUse++;

		/* Has it overflowed? */
		if( usNextPortToUse == 0U )
		{
			/* Don't go right back to the start of the dynamic/private port
			range numbers as any persistent sockets are likely to have been
			create first so the early port numbers may still be in use. */
			usNextPortToUse = socketAUTO_PORT_ALLOCATION_RESET_NUMBER;
		}

		usReturn = FreeRTOS_htons( usNextPortToUse );
	}
	taskEXIT_CRITICAL();

	return usReturn;
} /* Tested */
/*-----------------------------------------------------------*/

xListItem * pxListFindListItemWithValue( xList *pxList, TickType_t xWantedItemValue )
{
xListItem *pxIterator, *pxReturn;

	pxReturn = NULL;
	for( pxIterator = ( xListItem * ) pxList->xListEnd.pxNext; pxIterator != ( xListItem* ) &( pxList->xListEnd ); pxIterator = ( xListItem * ) pxIterator->pxNext )
	{
		if( pxIterator->xItemValue == xWantedItemValue )
		{
			pxReturn = pxIterator;
			break;
		}
	}

	return pxReturn;
} /* Tested */
/*-----------------------------------------------------------*/

#if ipconfigINCLUDE_FULL_INET_ADDR == 1

	uint32_t FreeRTOS_inet_addr( const char *pcIPAddress )
	{
	const uint8_t ucDecimalBase = 10;
	uint8_t ucOctet[ socketMAX_IP_ADDRESS_OCTETS ];
	const char *pcPointerOnEntering;
	uint32_t ulReturn = 0UL, ulOctetNumber, ulValue;
	BaseType_t xResult = pdPASS;

		for( ulOctetNumber = 0; ulOctetNumber < socketMAX_IP_ADDRESS_OCTETS; ulOctetNumber++ )
		{
			ulValue = 0;
			pcPointerOnEntering = pcIPAddress;

			while( ( *pcIPAddress >= ( uint8_t ) '0' ) && ( *pcIPAddress <= ( uint8_t ) '9' ) )
			{
				/* Move previous read characters into the next decimal
				position. */
				ulValue *= ucDecimalBase;

				/* Add the binary value of the ascii character. */
				ulValue += ( *pcIPAddress - ( uint8_t ) '0' );

				/* Move to next character in the string. */
				pcIPAddress++;
			}

			/* Check characters were read. */
			if( pcIPAddress == pcPointerOnEntering )
			{
				xResult = pdFAIL;
			}

			/* Check the value fits in an 8-bit number. */
			if( ulValue > 0xffUL )
			{
				xResult = pdFAIL;
			}
			else
			{
				ucOctet[ ulOctetNumber ] = ( uint8_t ) ulValue;

				/* Check the next character is as expected. */
				if( ulOctetNumber < ( socketMAX_IP_ADDRESS_OCTETS - 1 ) )
				{
					if( *pcIPAddress != ( uint8_t ) '.' )
					{
						xResult = pdFAIL;
					}
					else
					{
						/* Move past the dot. */
						pcIPAddress++;
					}
				}
			}

			if( xResult == pdFAIL )
			{
				/* No point going on. */
				break;
			}
		}

		if( *pcIPAddress != ( uint8_t ) 0x00 )
		{
			/* Expected the end of the string. */
			xResult = pdFAIL;
		}

		if( ulOctetNumber != socketMAX_IP_ADDRESS_OCTETS )
		{
			/* Didn't read enough octets. */
			xResult = pdFAIL;
		}

		if( xResult == pdPASS )
		{
			ulReturn = FreeRTOS_inet_addr_quick( ucOctet[ 0 ], ucOctet[ 1 ], ucOctet[ 2 ], ucOctet[ 3 ] );
		}

		return ulReturn;
	}

#endif /* ipconfigINCLUDE_FULL_INET_ADDR */
/*-----------------------------------------------------------*/

