static void prvTCPReturnPacket()

in device_firmware/libraries/freertos_plus/standard/freertos_plus_tcp/source/FreeRTOS_TCP_IP.c [690:957]


static void prvTCPReturnPacket( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, uint32_t ulLen, BaseType_t xReleaseAfterSend )
{
TCPPacket_t * pxTCPPacket;
IPHeader_t *pxIPHeader;
EthernetHeader_t *pxEthernetHeader;
uint32_t ulFrontSpace, ulSpace, ulSourceAddress, ulWinSize;
TCPWindow_t *pxTCPWindow;
NetworkBufferDescriptor_t xTempBuffer;
/* For sending, a pseudo network buffer will be used, as explained above. */

	if( pxNetworkBuffer == NULL )
	{
		pxNetworkBuffer = &xTempBuffer;

		#if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
		{
			xTempBuffer.pxNextBuffer = NULL;
		}
		#endif
		xTempBuffer.pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket;
		xTempBuffer.xDataLength = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket );
		xReleaseAfterSend = pdFALSE;
	}

	#if( ipconfigZERO_COPY_TX_DRIVER != 0 )
	{
		if( xReleaseAfterSend == pdFALSE )
		{
			pxNetworkBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, ( BaseType_t ) pxNetworkBuffer->xDataLength );
			if( pxNetworkBuffer == NULL )
			{
				FreeRTOS_debug_printf( ( "prvTCPReturnPacket: duplicate failed\n" ) );
			}
			xReleaseAfterSend = pdTRUE;
		}
	}
	#endif /* ipconfigZERO_COPY_TX_DRIVER */

	if( pxNetworkBuffer != NULL )
	{
		pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer );
		pxIPHeader = &pxTCPPacket->xIPHeader;
		pxEthernetHeader = &pxTCPPacket->xEthernetHeader;

		/* Fill the packet, using hton translations. */
		if( pxSocket != NULL )
		{
			/* Calculate the space in the RX buffer in order to advertise the
			size of this socket's reception window. */
			pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow );

			if( pxSocket->u.xTCP.rxStream != NULL )
			{
				/* An RX stream was created already, see how much space is
				available. */
				ulFrontSpace = ( uint32_t ) uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream );
			}
			else
			{
				/* No RX stream has been created, the full stream size is
				available. */
				ulFrontSpace = ( uint32_t ) pxSocket->u.xTCP.uxRxStreamSize;
			}

			/* Take the minimum of the RX buffer space and the RX window size. */
			ulSpace = FreeRTOS_min_uint32( pxTCPWindow->xSize.ulRxWindowLength, ulFrontSpace );

			if( ( pxSocket->u.xTCP.bits.bLowWater != pdFALSE_UNSIGNED ) || ( pxSocket->u.xTCP.bits.bRxStopped != pdFALSE_UNSIGNED ) )
			{
				/* The low-water mark was reached, meaning there was little
				space left.  The socket will wait until the application has read
				or flushed the incoming data, and 'zero-window' will be
				advertised. */
				ulSpace = 0u;
			}

			/* If possible, advertise an RX window size of at least 1 MSS, otherwise
			the peer might start 'zero window probing', i.e. sending small packets
			(1, 2, 4, 8... bytes). */
			if( ( ulSpace < pxSocket->u.xTCP.usCurMSS ) && ( ulFrontSpace >= pxSocket->u.xTCP.usCurMSS ) )
			{
				ulSpace = pxSocket->u.xTCP.usCurMSS;
			}

			/* Avoid overflow of the 16-bit win field. */
			#if( ipconfigUSE_TCP_WIN != 0 )
			{
				ulWinSize = ( ulSpace >> pxSocket->u.xTCP.ucMyWinScaleFactor );
			}
			#else
			{
				ulWinSize = ulSpace;
			}
			#endif
			if( ulWinSize > 0xfffcUL )
			{
				ulWinSize = 0xfffcUL;
			}

			pxTCPPacket->xTCPHeader.usWindow = FreeRTOS_htons( ( uint16_t ) ulWinSize );

			#if( ipconfigHAS_DEBUG_PRINTF != 0 )
			{
				if( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE )
				{
					if( ( xTCPWindowLoggingLevel != 0 ) && ( pxSocket->u.xTCP.bits.bWinChange != pdFALSE_UNSIGNED ) )
					{
					size_t uxFrontSpace;

						if(pxSocket->u.xTCP.rxStream != NULL)
						{
							uxFrontSpace =  uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ) ;
						}
						else
						{
							uxFrontSpace = 0u;
						}

						FreeRTOS_debug_printf( ( "%s: %lxip:%u: [%lu < %lu] winSize %ld\n",
						pxSocket->u.xTCP.bits.bLowWater ? "STOP" : "GO ",
							pxSocket->u.xTCP.ulRemoteIP,
							pxSocket->u.xTCP.usRemotePort,
							pxSocket->u.xTCP.bits.bLowWater ? pxSocket->u.xTCP.uxLittleSpace : uxFrontSpace, pxSocket->u.xTCP.uxEnoughSpace,
							(int32_t) ( pxTCPWindow->rx.ulHighestSequenceNumber - pxTCPWindow->rx.ulCurrentSequenceNumber ) ) );
					}
				}
			}
			#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */

			/* The new window size has been advertised, switch off the flag. */
			pxSocket->u.xTCP.bits.bWinChange = pdFALSE_UNSIGNED;

			/* Later on, when deciding to delay an ACK, a precise estimate is needed
			of the free RX space.  At this moment, 'ulHighestRxAllowed' would be the
			highest sequence number minus 1 that the socket will accept. */
			pxSocket->u.xTCP.ulHighestRxAllowed = pxTCPWindow->rx.ulCurrentSequenceNumber + ulSpace;

			#if( ipconfigTCP_KEEP_ALIVE == 1 )
				if( pxSocket->u.xTCP.bits.bSendKeepAlive != pdFALSE_UNSIGNED )
				{
					/* Sending a keep-alive packet, send the current sequence number
					minus 1, which will	be recognised as a keep-alive packet an
					responded to by acknowledging the last byte. */
					pxSocket->u.xTCP.bits.bSendKeepAlive = pdFALSE_UNSIGNED;
					pxSocket->u.xTCP.bits.bWaitKeepAlive = pdTRUE_UNSIGNED;

					pxTCPPacket->xTCPHeader.ulSequenceNumber = pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - 1UL;
					pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxTCPPacket->xTCPHeader.ulSequenceNumber );
				}
				else
			#endif
			{
				pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber );

				if( ( pxTCPPacket->xTCPHeader.ucTCPFlags & ( uint8_t ) ipTCP_FLAG_FIN ) != 0u )
				{
					/* Suppress FIN in case this packet carries earlier data to be
					retransmitted. */
					uint32_t ulDataLen = ( uint32_t ) ( ulLen - ( ipSIZE_OF_TCP_HEADER + ipSIZE_OF_IPv4_HEADER ) );
					if( ( pxTCPWindow->ulOurSequenceNumber + ulDataLen ) != pxTCPWindow->tx.ulFINSequenceNumber )
					{
						pxTCPPacket->xTCPHeader.ucTCPFlags &= ( ( uint8_t ) ~ipTCP_FLAG_FIN );
						FreeRTOS_debug_printf( ( "Suppress FIN for %lu + %lu < %lu\n",
							pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber,
							ulDataLen,
							pxTCPWindow->tx.ulFINSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ) );
					}
				}
			}

			/* Tell which sequence number is expected next time */
			pxTCPPacket->xTCPHeader.ulAckNr = FreeRTOS_htonl( pxTCPWindow->rx.ulCurrentSequenceNumber );
		}
		else
		{
			/* Sending data without a socket, probably replying with a RST flag
			Just swap the two sequence numbers. */
			vFlip_32( pxTCPPacket->xTCPHeader.ulSequenceNumber, pxTCPPacket->xTCPHeader.ulAckNr );
		}

		pxIPHeader->ucTimeToLive		   = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE;
		pxIPHeader->usLength			   = FreeRTOS_htons( ulLen );
		if( ( pxSocket == NULL ) || ( *ipLOCAL_IP_ADDRESS_POINTER == 0ul ) )
		{
			/* When pxSocket is NULL, this function is called by prvTCPSendReset()
			and the IP-addresses must be swapped.
			Also swap the IP-addresses in case the IP-tack doesn't have an
			IP-address yet, i.e. when ( *ipLOCAL_IP_ADDRESS_POINTER == 0ul ). */
			ulSourceAddress = pxIPHeader->ulDestinationIPAddress;
		}
		else
		{
			ulSourceAddress = *ipLOCAL_IP_ADDRESS_POINTER;
		}
		pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress;
		pxIPHeader->ulSourceIPAddress = ulSourceAddress;
		vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort );

		/* Just an increasing number. */
		pxIPHeader->usIdentification = FreeRTOS_htons( usPacketIdentifier );
		usPacketIdentifier++;
		pxIPHeader->usFragmentOffset = 0u;

		#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
		{
			/* calculate the IP header checksum, in case the driver won't do that. */
			pxIPHeader->usHeaderChecksum = 0x00u;
			pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0UL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
			pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );

			/* calculate the TCP checksum for an outgoing packet. */
			usGenerateProtocolChecksum( (uint8_t*)pxTCPPacket, pxNetworkBuffer->xDataLength, pdTRUE );

			/* A calculated checksum of 0 must be inverted as 0 means the checksum
			is disabled. */
			if( pxTCPPacket->xTCPHeader.usChecksum == 0x00u )
			{
				pxTCPPacket->xTCPHeader.usChecksum = 0xffffU;
			}
		}
		#endif

	#if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
		pxNetworkBuffer->pxNextBuffer = NULL;
	#endif

		/* Important: tell NIC driver how many bytes must be sent. */
		pxNetworkBuffer->xDataLength = ulLen + ipSIZE_OF_ETH_HEADER;

		/* Fill in the destination MAC addresses. */
		memcpy( ( void * ) &( pxEthernetHeader->xDestinationAddress ), ( void * ) &( pxEthernetHeader->xSourceAddress ),
			sizeof( pxEthernetHeader->xDestinationAddress ) );

		/* The source MAC addresses is fixed to 'ipLOCAL_MAC_ADDRESS'. */
		memcpy( ( void * ) &( pxEthernetHeader->xSourceAddress) , ( void * ) ipLOCAL_MAC_ADDRESS, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES );

		#if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES )
		{
			if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES )
			{
			BaseType_t xIndex;

				for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ )
				{
					pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0u;
				}
				pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES;
			}
		}
		#endif

		/* Send! */
		xNetworkInterfaceOutput( pxNetworkBuffer, xReleaseAfterSend );

		if( xReleaseAfterSend == pdFALSE )
		{
			/* Swap-back some fields, as pxBuffer probably points to a socket field
			containing the packet header. */
			vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort);
			pxTCPPacket->xIPHeader.ulSourceIPAddress = pxTCPPacket->xIPHeader.ulDestinationIPAddress;
			memcpy( pxEthernetHeader->xSourceAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES );
		}
		else
		{
			/* Nothing to do: the buffer has been passed to DMA and will be released after use */
		}
	} /* if( pxNetworkBuffer != NULL ) */
}