in FreeRTOS_TCP_IP.c [815:1106]
static void prvTCPReturnPacket( FreeRTOS_Socket_t * pxSocket,
NetworkBufferDescriptor_t * pxDescriptor,
uint32_t ulLen,
BaseType_t xReleaseAfterSend )
{
TCPPacket_t * pxTCPPacket;
IPHeader_t * pxIPHeader;
BaseType_t xDoRelease = xReleaseAfterSend;
EthernetHeader_t * pxEthernetHeader;
uint32_t ulFrontSpace, ulSpace, ulSourceAddress, ulWinSize;
const TCPWindow_t * pxTCPWindow;
NetworkBufferDescriptor_t * pxNetworkBuffer = pxDescriptor;
NetworkBufferDescriptor_t xTempBuffer;
/* memcpy() helper variables for MISRA Rule 21.15 compliance*/
const void * pvCopySource;
void * pvCopyDest;
/* For sending, a pseudo network buffer will be used, as explained above. */
if( pxNetworkBuffer == NULL )
{
pxNetworkBuffer = &xTempBuffer;
#if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
{
pxNetworkBuffer->pxNextBuffer = NULL;
}
#endif
pxNetworkBuffer->pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket;
pxNetworkBuffer->xDataLength = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket );
xDoRelease = pdFALSE;
}
#if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
{
if( xDoRelease == pdFALSE )
{
pxNetworkBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, ( size_t ) pxNetworkBuffer->xDataLength );
if( pxNetworkBuffer != NULL )
{
xDoRelease = pdTRUE;
}
else
{
FreeRTOS_debug_printf( ( "prvTCPReturnPacket: duplicate failed\n" ) );
}
}
}
#endif /* ipconfigZERO_COPY_TX_DRIVER */
#ifndef __COVERITY__
if( pxNetworkBuffer != NULL )
#endif
{
/* Map the ethernet buffer onto a TCPPacket_t struct for easy access to the fields. */
pxTCPPacket = ipCAST_PTR_TO_TYPE_PTR( 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.usMSS ) && ( ulFrontSpace >= pxSocket->u.xTCP.usMSS ) )
{
ulSpace = pxSocket->u.xTCP.usMSS;
}
/* 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 > 0xfffcU )
{
ulWinSize = 0xfffcU;
}
pxTCPPacket->xTCPHeader.usWindow = FreeRTOS_htons( ( uint16_t ) ulWinSize );
/* 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 and
* 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 - 1U;
pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxTCPPacket->xTCPHeader.ulSequenceNumber );
}
else
#endif /* if ( ipconfigTCP_KEEP_ALIVE == 1 ) */
{
pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber );
if( ( pxTCPPacket->xTCPHeader.ucTCPFlags & ( uint8_t ) tcpTCP_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 ) ~tcpTCP_FLAG_FIN );
FreeRTOS_debug_printf( ( "Suppress FIN for %u + %u < %u\n",
( unsigned ) ( pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ),
( unsigned ) ulDataLen,
( unsigned ) ( 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 == 0U ) )
{
/* 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 == 0U ). */
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++;
/* The stack doesn't support fragments, so the fragment offset field must always be zero.
* The header was never memset to zero, so set both the fragment offset and fragmentation flags in one go.
*/
#if ( ipconfigFORCE_IP_DONT_FRAGMENT != 0 )
pxIPHeader->usFragmentOffset = ipFRAGMENT_FLAGS_DONT_FRAGMENT;
#else
pxIPHeader->usFragmentOffset = 0U;
#endif
/* Important: tell NIC driver how many bytes must be sent. */
pxNetworkBuffer->xDataLength = ( size_t ) ulLen;
pxNetworkBuffer->xDataLength += ipSIZE_OF_ETH_HEADER;
#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( 0U, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
/* calculate the TCP checksum for an outgoing packet. */
( void ) usGenerateProtocolChecksum( ( uint8_t * ) pxTCPPacket, pxNetworkBuffer->xDataLength, pdTRUE );
}
#endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) */
#if ( ipconfigUSE_LINKED_RX_MESSAGES != 0 )
{
pxNetworkBuffer->pxNextBuffer = NULL;
}
#endif
{
MACAddress_t xMACAddress;
uint32_t ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress;
eARPLookupResult_t eResult;
eResult = eARPGetCacheEntry( &ulDestinationIPAddress, &xMACAddress );
if( eResult == eARPCacheHit )
{
pvCopySource = &xMACAddress;
}
else
{
pvCopySource = &pxEthernetHeader->xSourceAddress;
}
/* Fill in the destination MAC addresses. */
( void ) memcpy( ( void * ) ( &( pxEthernetHeader->xDestinationAddress ) ),
pvCopySource,
sizeof( pxEthernetHeader->xDestinationAddress ) );
}
/*
* Use helper variables for memcpy() to remain
* compliant with MISRA Rule 21.15. These should be
* optimized away.
*/
/* The source MAC addresses is fixed to 'ipLOCAL_MAC_ADDRESS'. */
pvCopySource = ipLOCAL_MAC_ADDRESS;
pvCopyDest = &pxEthernetHeader->xSourceAddress;
( void ) memcpy( pvCopyDest, pvCopySource, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES );
#if ( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 )
{
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 /* if( ipconfigETHERNET_MINIMUM_PACKET_BYTES > 0 ) */
/* Send! */
iptraceNETWORK_INTERFACE_OUTPUT( pxNetworkBuffer->xDataLength, pxNetworkBuffer->pucEthernetBuffer );
( void ) xNetworkInterfaceOutput( pxNetworkBuffer, xDoRelease );
if( xDoRelease == 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;
( void ) memcpy( ( void * ) ( pxEthernetHeader->xSourceAddress.ucBytes ), ( const void * ) ( 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 ) */
}