in amazon-freertos/lib/FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_IP.c [1674:1853]
static int32_t prvTCPPrepareSend( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, UBaseType_t uxOptionsLength )
{
int32_t lDataLen;
uint8_t *pucEthernetBuffer, *pucSendData;
TCPPacket_t *pxTCPPacket;
size_t uxOffset;
uint32_t ulDataGot, ulDistance;
TCPWindow_t *pxTCPWindow;
NetworkBufferDescriptor_t *pxNewBuffer;
int32_t lStreamPos;
if( ( *ppxNetworkBuffer ) != NULL )
{
/* A network buffer descriptor was already supplied */
pucEthernetBuffer = ( *ppxNetworkBuffer )->pucEthernetBuffer;
}
else
{
/* For now let it point to the last packet header */
pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket;
}
pxTCPPacket = ( TCPPacket_t * ) ( pucEthernetBuffer );
pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;
lDataLen = 0;
lStreamPos = 0;
pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_ACK;
if( pxSocket->u.xTCP.txStream != NULL )
{
/* ulTCPWindowTxGet will return the amount of data which may be sent
along with the position in the txStream.
Why check for MSS > 1 ?
Because some TCP-stacks (like uIP) use it for flow-control. */
if( pxSocket->u.xTCP.usCurMSS > 1u )
{
lDataLen = ( int32_t ) ulTCPWindowTxGet( pxTCPWindow, pxSocket->u.xTCP.ulWindowSize, &lStreamPos );
}
if( lDataLen > 0 )
{
/* Check if the current network buffer is big enough, if not,
resize it. */
pxNewBuffer = prvTCPBufferResize( pxSocket, *ppxNetworkBuffer, lDataLen, uxOptionsLength );
if( pxNewBuffer != NULL )
{
*ppxNetworkBuffer = pxNewBuffer;
pucEthernetBuffer = pxNewBuffer->pucEthernetBuffer;
pxTCPPacket = ( TCPPacket_t * ) ( pucEthernetBuffer );
pucSendData = pucEthernetBuffer + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength;
/* Translate the position in txStream to an offset from the tail
marker. */
uxOffset = uxStreamBufferDistance( pxSocket->u.xTCP.txStream, pxSocket->u.xTCP.txStream->uxTail, ( size_t ) lStreamPos );
/* Here data is copied from the txStream in 'peek' mode. Only
when the packets are acked, the tail marker will be updated. */
ulDataGot = ( uint32_t ) uxStreamBufferGet( pxSocket->u.xTCP.txStream, uxOffset, pucSendData, ( size_t ) lDataLen, pdTRUE );
#if( ipconfigHAS_DEBUG_PRINTF != 0 )
{
if( ulDataGot != ( uint32_t ) lDataLen )
{
FreeRTOS_debug_printf( ( "uxStreamBufferGet: pos %lu offs %lu only %lu != %lu\n",
lStreamPos, uxOffset, ulDataGot, lDataLen ) );
}
}
#endif
/* If the owner of the socket requests a closure, add the FIN
flag to the last packet. */
if( ( pxSocket->u.xTCP.bits.bCloseRequested != pdFALSE_UNSIGNED ) && ( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) )
{
ulDistance = ( uint32_t ) uxStreamBufferDistance( pxSocket->u.xTCP.txStream, ( size_t ) lStreamPos, pxSocket->u.xTCP.txStream->uxHead );
if( ulDistance == ulDataGot )
{
#if (ipconfigHAS_DEBUG_PRINTF == 1)
{
/* the order of volatile accesses is undefined
so such workaround */
size_t uxHead = pxSocket->u.xTCP.txStream->uxHead;
size_t uxMid = pxSocket->u.xTCP.txStream->uxMid;
size_t uxTail = pxSocket->u.xTCP.txStream->uxTail;
FreeRTOS_debug_printf( ( "CheckClose %lu <= %lu (%lu <= %lu <= %lu)\n", ulDataGot, ulDistance,
uxTail, uxMid, uxHead ) );
}
#endif
/* Although the socket sends a FIN, it will stay in
ESTABLISHED until all current data has been received or
delivered. */
pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_FIN;
pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->ulOurSequenceNumber + ( uint32_t ) lDataLen;
pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED;
}
}
}
else
{
lDataLen = -1;
}
}
}
if( ( lDataLen >= 0 ) && ( pxSocket->u.xTCP.ucTCPState == eESTABLISHED ) )
{
/* See if the socket owner wants to shutdown this connection. */
if( ( pxSocket->u.xTCP.bits.bUserShutdown != pdFALSE_UNSIGNED ) &&
( xTCPWindowTxDone( pxTCPWindow ) != pdFALSE ) )
{
pxSocket->u.xTCP.bits.bUserShutdown = pdFALSE_UNSIGNED;
pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_FIN;
pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED;
pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED;
pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber;
vTCPStateChange( pxSocket, eFIN_WAIT_1 );
}
#if( ipconfigTCP_KEEP_ALIVE != 0 )
{
if( pxSocket->u.xTCP.ucKeepRepCount > 3u )
{
FreeRTOS_debug_printf( ( "keep-alive: giving up %lxip:%u\n",
pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine. */
pxSocket->u.xTCP.usRemotePort ) ); /* Port on remote machine. */
vTCPStateChange( pxSocket, eCLOSE_WAIT );
lDataLen = -1;
}
if( ( lDataLen == 0 ) && ( pxSocket->u.xTCP.bits.bWinChange == pdFALSE_UNSIGNED ) )
{
/* If there is no data to be sent, and no window-update message,
we might want to send a keep-alive message. */
TickType_t xAge = xTaskGetTickCount( ) - pxSocket->u.xTCP.xLastAliveTime;
TickType_t xMax;
xMax = ( ( TickType_t ) ipconfigTCP_KEEP_ALIVE_INTERVAL * configTICK_RATE_HZ );
if( pxSocket->u.xTCP.ucKeepRepCount )
{
xMax = ( 3u * configTICK_RATE_HZ );
}
if( xAge > xMax )
{
pxSocket->u.xTCP.xLastAliveTime = xTaskGetTickCount( );
if( xTCPWindowLoggingLevel )
FreeRTOS_debug_printf( ( "keep-alive: %lxip:%u count %u\n",
pxSocket->u.xTCP.ulRemoteIP,
pxSocket->u.xTCP.usRemotePort,
pxSocket->u.xTCP.ucKeepRepCount ) );
pxSocket->u.xTCP.bits.bSendKeepAlive = pdTRUE_UNSIGNED;
pxSocket->u.xTCP.usTimeout = ( ( uint16_t ) pdMS_TO_TICKS( 2500 ) );
pxSocket->u.xTCP.ucKeepRepCount++;
}
}
}
#endif /* ipconfigTCP_KEEP_ALIVE */
}
/* Anything to send, a change of the advertised window size, or maybe send a
keep-alive message? */
if( ( lDataLen > 0 ) ||
( pxSocket->u.xTCP.bits.bWinChange != pdFALSE_UNSIGNED ) ||
( pxSocket->u.xTCP.bits.bSendKeepAlive != pdFALSE_UNSIGNED ) )
{
pxTCPPacket->xTCPHeader.ucTCPFlags &= ( ( uint8_t ) ~ipTCP_FLAG_PSH );
pxTCPPacket->xTCPHeader.ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 );
pxTCPPacket->xTCPHeader.ucTCPFlags |= ( uint8_t ) ipTCP_FLAG_ACK;
if( lDataLen != 0l )
{
pxTCPPacket->xTCPHeader.ucTCPFlags |= ( uint8_t ) ipTCP_FLAG_PSH;
}
lDataLen += ( int32_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength );
}
return lDataLen;
}