blob: 8040880092d4a3cfbccb8a132a65ffbf0d5223d1 [file] [log] [blame]
/*
* FreeRTOS+TCP V2.2.0
* 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://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/*
* FreeRTOS_TCP_WIN.c
* Module which handles the TCP windowing schemes for FreeRTOS+TCP. Many
* functions have two versions - one for FreeRTOS+TCP (full) and one for
* FreeRTOS+TCP (lite).
*
* In this module all ports and IP addresses and sequence numbers are
* being stored in host byte-order.
*/
/* Standard includes. */
#include <stdint.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_UDP_IP.h"
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_IP_Private.h"
#include "NetworkBufferManagement.h"
#include "FreeRTOS_TCP_WIN.h"
/* Constants used for Smoothed Round Trip Time (SRTT). */
#define winSRTT_INCREMENT_NEW 2
#define winSRTT_INCREMENT_CURRENT 6
#define winSRTT_DECREMENT_NEW 1
#define winSRTT_DECREMENT_CURRENT 7
#define winSRTT_CAP_mS 50
#if( ipconfigUSE_TCP_WIN == 1 )
#define xTCPWindowRxNew( pxWindow, ulSequenceNumber, lCount ) xTCPWindowNew( pxWindow, ulSequenceNumber, lCount, pdTRUE )
#define xTCPWindowTxNew( pxWindow, ulSequenceNumber, lCount ) xTCPWindowNew( pxWindow, ulSequenceNumber, lCount, pdFALSE )
/* The code to send a single Selective ACK (SACK):
* NOP (0x01), NOP (0x01), SACK (0x05), LEN (0x0a),
* followed by a lower and a higher sequence number,
* where LEN is 2 + 2*4 = 10 bytes. */
#if( ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN )
#define OPTION_CODE_SINGLE_SACK ( 0x0101050aUL )
#else
#define OPTION_CODE_SINGLE_SACK ( 0x0a050101UL )
#endif
/* Normal retransmission:
* A packet will be retransmitted after a Retransmit Time-Out (RTO).
* Fast retransmission:
* When 3 packets with a higher sequence number have been acknowledged
* by the peer, it is very unlikely a current packet will ever arrive.
* It will be retransmitted far before the RTO.
*/
#define DUPLICATE_ACKS_BEFORE_FAST_RETRANSMIT ( 3u )
/* If there have been several retransmissions (4), decrease the
* size of the transmission window to at most 2 times MSS.
*/
#define MAX_TRANSMIT_COUNT_USING_LARGE_WINDOW ( 4u )
#endif /* configUSE_TCP_WIN */
/*-----------------------------------------------------------*/
extern void vListInsertGeneric( List_t * const pxList, ListItem_t * const pxNewListItem, MiniListItem_t * const pxWhere );
/*
* All TCP sockets share a pool of segment descriptors (TCPSegment_t)
* Available descriptors are stored in the 'xSegmentList'
* When a socket owns a descriptor, it will either be stored in
* 'xTxSegments' or 'xRxSegments'
* As soon as a package has been confirmed, the descriptor will be returned
* to the segment pool
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static BaseType_t prvCreateSectors( void );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* Find a segment with a given sequence number in the list of received
* segments: 'pxWindow->xRxSegments'.
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowRxFind( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* Allocate a new segment
* The socket will borrow all segments from a common pool: 'xSegmentList',
* which is a list of 'TCPSegment_t'
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowNew( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber, int32_t lCount, BaseType_t xIsForRx );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/* When the peer has a close request (FIN flag), the driver will check if
* there are missing packets in the Rx-queue
* It will accept the closure of the connection if both conditions are true:
* - the Rx-queue is empty
* - we've ACK'd the highest Rx sequence number seen
*/
#if( ipconfigUSE_TCP_WIN == 1 )
BaseType_t xTCPWindowRxEmpty( TCPWindow_t *pxWindow );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* Detaches and returns the head of a queue
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowGetHead( List_t *pxList );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* Returns the head of a queue but it won't be detached
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowPeekHead( List_t *pxList );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* Free entry pxSegment because it's not used anymore
* The ownership will be passed back to the segment pool
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static void vTCPWindowFree( TCPSegment_t *pxSegment );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* A segment has been received with sequence number 'ulSequenceNumber', where
* 'ulCurrentSequenceNumber == ulSequenceNumber', which means that exactly this
* segment was expected. xTCPWindowRxConfirm() will check if there is already
* another segment with a sequence number between (ulSequenceNumber) and
* (ulSequenceNumber+xLength). Normally none will be found, because the next Rx
* segment should have a sequence number equal to '(ulSequenceNumber+xLength)'.
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowRxConfirm( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber, uint32_t ulLength );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* FreeRTOS+TCP stores data in circular buffers. Calculate the next position to
* store.
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static int32_t lTCPIncrementTxPosition( int32_t lPosition, int32_t lMax, int32_t lCount );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* This function will look if there is new transmission data. It will return
* true if there is data to be sent.
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t *pxWindow, uint32_t ulWindowSize );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* An acknowledge was received. See if some outstanding data may be removed
* from the transmission queue(s).
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static uint32_t prvTCPWindowTxCheckAck( TCPWindow_t *pxWindow, uint32_t ulFirst, uint32_t ulLast );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*
* A higher Tx block has been acknowledged. Now iterate through the xWaitQueue
* to find a possible condition for a FAST retransmission.
*/
#if( ipconfigUSE_TCP_WIN == 1 )
static uint32_t prvTCPWindowFastRetransmit( TCPWindow_t *pxWindow, uint32_t ulFirst );
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
/* TCP segment pool. */
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPSegments = NULL;
#endif /* ipconfigUSE_TCP_WIN == 1 */
/* List of free TCP segments. */
#if( ipconfigUSE_TCP_WIN == 1 )
static List_t xSegmentList;
#endif
/* Logging verbosity level. */
BaseType_t xTCPWindowLoggingLevel = 0;
#if( ipconfigUSE_TCP_WIN == 1 )
/* Some 32-bit arithmetic: comparing sequence numbers */
static portINLINE BaseType_t xSequenceLessThanOrEqual( uint32_t a, uint32_t b );
static portINLINE BaseType_t xSequenceLessThanOrEqual( uint32_t a, uint32_t b )
{
/* Test if a <= b
Return true if the unsigned subtraction of (b-a) doesn't generate an
arithmetic overflow. */
return ( ( b - a ) & 0x80000000UL ) == 0UL;
}
#endif /* ipconfigUSE_TCP_WIN */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static portINLINE BaseType_t xSequenceLessThan( uint32_t a, uint32_t b );
static portINLINE BaseType_t xSequenceLessThan( uint32_t a, uint32_t b )
{
/* Test if a < b */
return ( ( b - a - 1UL ) & 0x80000000UL ) == 0UL;
}
#endif /* ipconfigUSE_TCP_WIN */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static portINLINE BaseType_t xSequenceGreaterThan( uint32_t a, uint32_t b );
static portINLINE BaseType_t xSequenceGreaterThan( uint32_t a, uint32_t b )
{
/* Test if a > b */
return ( ( a - b - 1UL ) & 0x80000000UL ) == 0UL;
}
#endif /* ipconfigUSE_TCP_WIN */
/*-----------------------------------------------------------*/
static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a, uint32_t b );
static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a, uint32_t b )
{
/* Test if a >= b */
return ( ( a - b ) & 0x80000000UL ) == 0UL;
}
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static portINLINE void vListInsertFifo( List_t * const pxList, ListItem_t * const pxNewListItem );
static portINLINE void vListInsertFifo( List_t * const pxList, ListItem_t * const pxNewListItem )
{
vListInsertGeneric( pxList, pxNewListItem, &pxList->xListEnd );
}
#endif
/*-----------------------------------------------------------*/
static portINLINE void vTCPTimerSet( TCPTimer_t *pxTimer );
static portINLINE void vTCPTimerSet( TCPTimer_t *pxTimer )
{
pxTimer->ulBorn = xTaskGetTickCount ( );
}
/*-----------------------------------------------------------*/
static portINLINE uint32_t ulTimerGetAge( TCPTimer_t *pxTimer );
static portINLINE uint32_t ulTimerGetAge( TCPTimer_t *pxTimer )
{
return ( ( xTaskGetTickCount() - pxTimer->ulBorn ) * portTICK_PERIOD_MS );
}
/*-----------------------------------------------------------*/
/* _HT_ GCC (using the settings that I'm using) checks for every public function if it is
preceded by a prototype. Later this prototype will be located in list.h? */
extern void vListInsertGeneric( List_t * const pxList, ListItem_t * const pxNewListItem, MiniListItem_t * const pxWhere );
void vListInsertGeneric( List_t * const pxList, ListItem_t * const pxNewListItem, MiniListItem_t * const pxWhere )
{
/* Insert a new list item into pxList, it does not sort the list,
but it puts the item just before xListEnd, so it will be the last item
returned by listGET_HEAD_ENTRY() */
pxNewListItem->pxNext = (struct xLIST_ITEM * configLIST_VOLATILE)pxWhere;
pxNewListItem->pxPrevious = pxWhere->pxPrevious;
pxWhere->pxPrevious->pxNext = pxNewListItem;
pxWhere->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
listLIST_ITEM_CONTAINER( pxNewListItem ) = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static BaseType_t prvCreateSectors( void )
{
BaseType_t xIndex, xReturn;
/* Allocate space for 'xTCPSegments' and store them in 'xSegmentList'. */
vListInitialise( &xSegmentList );
xTCPSegments = ( TCPSegment_t * ) pvPortMallocLarge( ipconfigTCP_WIN_SEG_COUNT * sizeof( xTCPSegments[ 0 ] ) );
if( xTCPSegments == NULL )
{
FreeRTOS_debug_printf( ( "prvCreateSectors: malloc %lu failed\n",
ipconfigTCP_WIN_SEG_COUNT * sizeof( xTCPSegments[ 0 ] ) ) );
xReturn = pdFAIL;
}
else
{
/* Clear the allocated space. */
memset( xTCPSegments, '\0', ipconfigTCP_WIN_SEG_COUNT * sizeof( xTCPSegments[ 0 ] ) );
for( xIndex = 0; xIndex < ipconfigTCP_WIN_SEG_COUNT; xIndex++ )
{
/* Could call vListInitialiseItem here but all data has been
nulled already. Set the owner to a segment descriptor. */
listSET_LIST_ITEM_OWNER( &( xTCPSegments[ xIndex ].xListItem ), ( void* ) &( xTCPSegments[ xIndex ] ) );
listSET_LIST_ITEM_OWNER( &( xTCPSegments[ xIndex ].xQueueItem ), ( void* ) &( xTCPSegments[ xIndex ] ) );
/* And add it to the pool of available segments */
vListInsertFifo( &xSegmentList, &( xTCPSegments[xIndex].xListItem ) );
}
xReturn = pdPASS;
}
return xReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowRxFind( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber )
{
const ListItem_t *pxIterator;
const MiniListItem_t* pxEnd;
TCPSegment_t *pxSegment, *pxReturn = NULL;
/* Find a segment with a given sequence number in the list of received
segments. */
pxEnd = ( const MiniListItem_t* )listGET_END_MARKER( &pxWindow->xRxSegments );
for( pxIterator = ( const ListItem_t * ) listGET_NEXT( pxEnd );
pxIterator != ( const ListItem_t * ) pxEnd;
pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) )
{
pxSegment = ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
if( pxSegment->ulSequenceNumber == ulSequenceNumber )
{
pxReturn = pxSegment;
break;
}
}
return pxReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowNew( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber, int32_t lCount, BaseType_t xIsForRx )
{
TCPSegment_t *pxSegment;
ListItem_t * pxItem;
/* Allocate a new segment. The socket will borrow all segments from a
common pool: 'xSegmentList', which is a list of 'TCPSegment_t' */
if( listLIST_IS_EMPTY( &xSegmentList ) != pdFALSE )
{
/* If the TCP-stack runs out of segments, you might consider
increasing 'ipconfigTCP_WIN_SEG_COUNT'. */
FreeRTOS_debug_printf( ( "xTCPWindow%cxNew: Error: all segments occupied\n", xIsForRx ? 'R' : 'T' ) );
pxSegment = NULL;
}
else
{
/* Pop the item at the head of the list. Semaphore protection is
not required as only the IP task will call these functions. */
pxItem = ( ListItem_t * ) listGET_HEAD_ENTRY( &xSegmentList );
pxSegment = ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxItem );
configASSERT( pxItem != NULL );
configASSERT( pxSegment != NULL );
/* Remove the item from xSegmentList. */
uxListRemove( pxItem );
/* Add it to either the connections' Rx or Tx queue. */
vListInsertFifo( xIsForRx ? &pxWindow->xRxSegments : &pxWindow->xTxSegments, pxItem );
/* And set the segment's timer to zero */
vTCPTimerSet( &pxSegment->xTransmitTimer );
pxSegment->u.ulFlags = 0;
pxSegment->u.bits.bIsForRx = ( xIsForRx != 0 );
pxSegment->lMaxLength = lCount;
pxSegment->lDataLength = lCount;
pxSegment->ulSequenceNumber = ulSequenceNumber;
#if( ipconfigHAS_DEBUG_PRINTF != 0 )
{
static UBaseType_t xLowestLength = ipconfigTCP_WIN_SEG_COUNT;
UBaseType_t xLength = listCURRENT_LIST_LENGTH( &xSegmentList );
if( xLowestLength > xLength )
{
xLowestLength = xLength;
}
}
#endif /* ipconfigHAS_DEBUG_PRINTF */
}
return pxSegment;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
BaseType_t xTCPWindowRxEmpty( TCPWindow_t *pxWindow )
{
BaseType_t xReturn;
/* When the peer has a close request (FIN flag), the driver will check
if there are missing packets in the Rx-queue. It will accept the
closure of the connection if both conditions are true:
- the Rx-queue is empty
- the highest Rx sequence number has been ACK'ed */
if( listLIST_IS_EMPTY( ( &pxWindow->xRxSegments ) ) == pdFALSE )
{
/* Rx data has been stored while earlier packets were missing. */
xReturn = pdFALSE;
}
else if( xSequenceGreaterThanOrEqual( pxWindow->rx.ulCurrentSequenceNumber, pxWindow->rx.ulHighestSequenceNumber ) != pdFALSE )
{
/* No Rx packets are being stored and the highest sequence number
that has been received has been ACKed. */
xReturn = pdTRUE;
}
else
{
FreeRTOS_debug_printf( ( "xTCPWindowRxEmpty: cur %lu highest %lu (empty)\n",
( pxWindow->rx.ulCurrentSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ),
( pxWindow->rx.ulHighestSequenceNumber - pxWindow->rx.ulFirstSequenceNumber ) ) );
xReturn = pdFALSE;
}
return xReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowGetHead( List_t *pxList )
{
TCPSegment_t *pxSegment;
ListItem_t * pxItem;
/* Detaches and returns the head of a queue. */
if( listLIST_IS_EMPTY( pxList ) != pdFALSE )
{
pxSegment = NULL;
}
else
{
pxItem = ( ListItem_t * ) listGET_HEAD_ENTRY( pxList );
pxSegment = ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxItem );
uxListRemove( pxItem );
}
return pxSegment;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowPeekHead( List_t *pxList )
{
ListItem_t *pxItem;
TCPSegment_t *pxReturn;
/* Returns the head of a queue but it won't be detached. */
if( listLIST_IS_EMPTY( pxList ) != pdFALSE )
{
pxReturn = NULL;
}
else
{
pxItem = ( ListItem_t * ) listGET_HEAD_ENTRY( pxList );
pxReturn = ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxItem );
}
return pxReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static void vTCPWindowFree( TCPSegment_t *pxSegment )
{
/* Free entry pxSegment because it's not used any more. The ownership
will be passed back to the segment pool.
Unlink it from one of the queues, if any. */
if( listLIST_ITEM_CONTAINER( &( pxSegment->xQueueItem ) ) != NULL )
{
uxListRemove( &( pxSegment->xQueueItem ) );
}
pxSegment->ulSequenceNumber = 0u;
pxSegment->lDataLength = 0l;
pxSegment->u.ulFlags = 0u;
/* Take it out of xRxSegments/xTxSegments */
if( listLIST_ITEM_CONTAINER( &( pxSegment->xListItem ) ) != NULL )
{
uxListRemove( &( pxSegment->xListItem ) );
}
/* Return it to xSegmentList */
vListInsertFifo( &xSegmentList, &( pxSegment->xListItem ) );
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
void vTCPWindowDestroy( TCPWindow_t *pxWindow )
{
List_t * pxSegments;
BaseType_t xRound;
TCPSegment_t *pxSegment;
/* Destroy a window. A TCP window doesn't serve any more. Return all
owned segments to the pool. In order to save code, it will make 2 rounds,
one to remove the segments from xRxSegments, and a second round to clear
xTxSegments*/
for( xRound = 0; xRound < 2; xRound++ )
{
if( xRound != 0 )
{
pxSegments = &( pxWindow->xRxSegments );
}
else
{
pxSegments = &( pxWindow->xTxSegments );
}
if( listLIST_IS_INITIALISED( pxSegments ) != pdFALSE )
{
while( listCURRENT_LIST_LENGTH( pxSegments ) > 0U )
{
pxSegment = ( TCPSegment_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxSegments );
vTCPWindowFree( pxSegment );
}
}
}
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
void vTCPWindowCreate( TCPWindow_t *pxWindow, uint32_t ulRxWindowLength,
uint32_t ulTxWindowLength, uint32_t ulAckNumber, uint32_t ulSequenceNumber, uint32_t ulMSS )
{
/* Create and initialize a window. */
#if( ipconfigUSE_TCP_WIN == 1 )
{
if( xTCPSegments == NULL )
{
prvCreateSectors();
}
vListInitialise( &pxWindow->xTxSegments );
vListInitialise( &pxWindow->xRxSegments );
vListInitialise( &pxWindow->xPriorityQueue ); /* Priority queue: segments which must be sent immediately */
vListInitialise( &pxWindow->xTxQueue ); /* Transmit queue: segments queued for transmission */
vListInitialise( &pxWindow->xWaitQueue ); /* Waiting queue: outstanding segments */
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
if( xTCPWindowLoggingLevel != 0 )
{
FreeRTOS_debug_printf( ( "vTCPWindowCreate: for WinLen = Rx/Tx: %lu/%lu\n",
ulRxWindowLength, ulTxWindowLength ) );
}
pxWindow->xSize.ulRxWindowLength = ulRxWindowLength;
pxWindow->xSize.ulTxWindowLength = ulTxWindowLength;
vTCPWindowInit( pxWindow, ulAckNumber, ulSequenceNumber, ulMSS );
}
/*-----------------------------------------------------------*/
void vTCPWindowInit( TCPWindow_t *pxWindow, uint32_t ulAckNumber, uint32_t ulSequenceNumber, uint32_t ulMSS )
{
const int32_t l500ms = 500;
pxWindow->u.ulFlags = 0ul;
pxWindow->u.bits.bHasInit = pdTRUE_UNSIGNED;
if( ulMSS != 0ul )
{
if( pxWindow->usMSSInit != 0u )
{
pxWindow->usMSSInit = ( uint16_t ) ulMSS;
}
if( ( ulMSS < ( uint32_t ) pxWindow->usMSS ) || ( pxWindow->usMSS == 0u ) )
{
pxWindow->xSize.ulRxWindowLength = ( pxWindow->xSize.ulRxWindowLength / ulMSS ) * ulMSS;
pxWindow->usMSS = ( uint16_t ) ulMSS;
}
}
#if( ipconfigUSE_TCP_WIN == 0 )
{
pxWindow->xTxSegment.lMaxLength = ( int32_t ) pxWindow->usMSS;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*Start with a timeout of 2 * 500 ms (1 sec). */
pxWindow->lSRTT = l500ms;
/* Just for logging, to print relative sequence numbers. */
pxWindow->rx.ulFirstSequenceNumber = ulAckNumber;
/* The segment asked for in the next transmission. */
pxWindow->rx.ulCurrentSequenceNumber = ulAckNumber;
/* The right-hand side of the receive window. */
pxWindow->rx.ulHighestSequenceNumber = ulAckNumber;
pxWindow->tx.ulFirstSequenceNumber = ulSequenceNumber;
/* The segment asked for in next transmission. */
pxWindow->tx.ulCurrentSequenceNumber = ulSequenceNumber;
/* The sequence number given to the next outgoing byte to be added is
maintained by lTCPWindowTxAdd(). */
pxWindow->ulNextTxSequenceNumber = ulSequenceNumber;
/* The right-hand side of the transmit window. */
pxWindow->tx.ulHighestSequenceNumber = ulSequenceNumber;
pxWindow->ulOurSequenceNumber = ulSequenceNumber;
}
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
void vTCPSegmentCleanup( void )
{
/* Free and clear the TCP segments pointer. This function should only be called
* once FreeRTOS+TCP will no longer be used. No thread-safety is provided for this
* function. */
if( xTCPSegments != NULL )
{
vPortFreeLarge( xTCPSegments );
xTCPSegments = NULL;
}
}
#endif /* ipconfgiUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
/*=============================================================================
*
* ###### # #
* # # # #
* # # # #
* # # ####
* ###### ##
* # ## ####
* # # # #
* # # # #
* ### ## # #
* Rx functions
*
*=============================================================================*/
#if( ipconfigUSE_TCP_WIN == 1 )
static TCPSegment_t *xTCPWindowRxConfirm( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber, uint32_t ulLength )
{
TCPSegment_t *pxBest = NULL;
const ListItem_t *pxIterator;
uint32_t ulNextSequenceNumber = ulSequenceNumber + ulLength;
const MiniListItem_t* pxEnd = ( const MiniListItem_t* ) listGET_END_MARKER( &pxWindow->xRxSegments );
TCPSegment_t *pxSegment;
/* A segment has been received with sequence number 'ulSequenceNumber',
where 'ulCurrentSequenceNumber == ulSequenceNumber', which means that
exactly this segment was expected. xTCPWindowRxConfirm() will check if
there is already another segment with a sequence number between (ulSequenceNumber)
and (ulSequenceNumber+ulLength). Normally none will be found, because
the next RX segment should have a sequence number equal to
'(ulSequenceNumber+ulLength)'. */
/* Iterate through all RX segments that are stored: */
for( pxIterator = ( const ListItem_t * ) listGET_NEXT( pxEnd );
pxIterator != ( const ListItem_t * ) pxEnd;
pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) )
{
pxSegment = ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
/* And see if there is a segment for which:
'ulSequenceNumber' <= 'pxSegment->ulSequenceNumber' < 'ulNextSequenceNumber'
If there are more matching segments, the one with the lowest sequence number
shall be taken */
if( ( xSequenceGreaterThanOrEqual( pxSegment->ulSequenceNumber, ulSequenceNumber ) != 0 ) &&
( xSequenceLessThan( pxSegment->ulSequenceNumber, ulNextSequenceNumber ) != 0 ) )
{
if( ( pxBest == NULL ) || ( xSequenceLessThan( pxSegment->ulSequenceNumber, pxBest->ulSequenceNumber ) != 0 ) )
{
pxBest = pxSegment;
}
}
}
if( ( pxBest != NULL ) &&
( ( pxBest->ulSequenceNumber != ulSequenceNumber ) || ( pxBest->lDataLength != ( int32_t ) ulLength ) ) )
{
FreeRTOS_flush_logging();
FreeRTOS_debug_printf( ( "xTCPWindowRxConfirm[%u]: search %lu (+%ld=%lu) found %lu (+%ld=%lu)\n",
pxWindow->usPeerPortNumber,
ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
ulLength,
ulSequenceNumber + ulLength - pxWindow->rx.ulFirstSequenceNumber,
pxBest->ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
pxBest->lDataLength,
pxBest->ulSequenceNumber + ( ( uint32_t ) pxBest->lDataLength ) - pxWindow->rx.ulFirstSequenceNumber ) );
}
return pxBest;
}
#endif /* ipconfgiUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
int32_t lTCPWindowRxCheck( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber, uint32_t ulLength, uint32_t ulSpace )
{
uint32_t ulCurrentSequenceNumber, ulLast, ulSavedSequenceNumber;
int32_t lReturn, lDistance;
TCPSegment_t *pxFound;
/* If lTCPWindowRxCheck( ) returns == 0, the packet will be passed
directly to user (segment is expected). If it returns a positive
number, an earlier packet is missing, but this packet may be stored.
If negative, the packet has already been stored, or it is out-of-order,
or there is not enough space.
As a side-effect, pxWindow->ulUserDataLength will get set to non-zero,
if more Rx data may be passed to the user after this packet. */
ulCurrentSequenceNumber = pxWindow->rx.ulCurrentSequenceNumber;
/* For Selective Ack (SACK), used when out-of-sequence data come in. */
pxWindow->ucOptionLength = 0u;
/* Non-zero if TCP-windows contains data which must be popped. */
pxWindow->ulUserDataLength = 0ul;
if( ulCurrentSequenceNumber == ulSequenceNumber )
{
/* This is the packet with the lowest sequence number we're waiting
for. It can be passed directly to the rx stream. */
if( ulLength > ulSpace )
{
FreeRTOS_debug_printf( ( "lTCPWindowRxCheck: Refuse %lu bytes, due to lack of space (%lu)\n", ulLength, ulSpace ) );
lReturn = -1;
}
else
{
ulCurrentSequenceNumber += ulLength;
if( listCURRENT_LIST_LENGTH( &( pxWindow->xRxSegments ) ) != 0 )
{
ulSavedSequenceNumber = ulCurrentSequenceNumber;
/* Clean up all sequence received between ulSequenceNumber and ulSequenceNumber + ulLength since they are duplicated.
If the server is forced to retransmit packets several time in a row it might send a batch of concatenated packet for speed.
So we cannot rely on the packets between ulSequenceNumber and ulSequenceNumber + ulLength to be sequential and it is better to just
clean them out. */
do
{
pxFound = xTCPWindowRxConfirm( pxWindow, ulSequenceNumber, ulLength );
if ( pxFound != NULL )
{
/* Remove it because it will be passed to user directly. */
vTCPWindowFree( pxFound );
}
} while ( pxFound );
/* Check for following segments that are already in the
queue and increment ulCurrentSequenceNumber. */
while( ( pxFound = xTCPWindowRxFind( pxWindow, ulCurrentSequenceNumber ) ) != NULL )
{
ulCurrentSequenceNumber += ( uint32_t ) pxFound->lDataLength;
/* As all packet below this one have been passed to the
user it can be discarded. */
vTCPWindowFree( pxFound );
}
if( ulSavedSequenceNumber != ulCurrentSequenceNumber )
{
/* After the current data-package, there is more data
to be popped. */
pxWindow->ulUserDataLength = ulCurrentSequenceNumber - ulSavedSequenceNumber;
if( xTCPWindowLoggingLevel >= 1 )
{
FreeRTOS_debug_printf( ( "lTCPWindowRxCheck[%d,%d]: retran %lu (Found %lu bytes at %lu cnt %ld)\n",
pxWindow->usPeerPortNumber, pxWindow->usOurPortNumber,
ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
pxWindow->ulUserDataLength,
ulSavedSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
listCURRENT_LIST_LENGTH( &pxWindow->xRxSegments ) ) );
}
}
}
pxWindow->rx.ulCurrentSequenceNumber = ulCurrentSequenceNumber;
/* Packet was expected, may be passed directly to the socket
buffer or application. Store the packet at offset 0. */
lReturn = 0;
}
}
else if( ulCurrentSequenceNumber == ( ulSequenceNumber + 1UL ) )
{
/* Looks like a TCP keep-alive message. Do not accept/store Rx data
ulUserDataLength = 0. Not packet out-of-sync. Just reply to it. */
lReturn = -1;
}
else
{
/* The packet is not the one expected. See if it falls within the Rx
window so it can be stored. */
/* An "out-of-sequence" segment was received, must have missed one.
Prepare a SACK (Selective ACK). */
ulLast = ulSequenceNumber + ulLength;
lDistance = ( int32_t ) ( ulLast - ulCurrentSequenceNumber );
if( lDistance <= 0 )
{
/* An earlier has been received, must be a retransmission of a
packet that has been accepted already. No need to send out a
Selective ACK (SACK). */
lReturn = -1;
}
else if( lDistance > ( int32_t ) ulSpace )
{
/* The new segment is ahead of rx.ulCurrentSequenceNumber. The
sequence number of this packet is too far ahead, ignore it. */
FreeRTOS_debug_printf( ( "lTCPWindowRxCheck: Refuse %lu+%lu bytes, due to lack of space (%lu)\n", lDistance, ulLength, ulSpace ) );
lReturn = -1;
}
else
{
/* See if there is more data in a contiguous block to make the
SACK describe a longer range of data. */
/* TODO: SACK's may also be delayed for a short period
* This is useful because subsequent packets will be SACK'd with
* single one message
*/
while( ( pxFound = xTCPWindowRxFind( pxWindow, ulLast ) ) != NULL )
{
ulLast += ( uint32_t ) pxFound->lDataLength;
}
if( xTCPWindowLoggingLevel >= 1 )
{
FreeRTOS_debug_printf( ( "lTCPWindowRxCheck[%d,%d]: seqnr %lu exp %lu (dist %ld) SACK to %lu\n",
pxWindow->usPeerPortNumber, pxWindow->usOurPortNumber,
ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
ulCurrentSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
( BaseType_t ) ( ulSequenceNumber - ulCurrentSequenceNumber ), /* want this signed */
ulLast - pxWindow->rx.ulFirstSequenceNumber ) );
}
/* Now prepare the SACK message.
Code OPTION_CODE_SINGLE_SACK already in network byte order. */
pxWindow->ulOptionsData[0] = OPTION_CODE_SINGLE_SACK;
/* First sequence number that we received. */
pxWindow->ulOptionsData[1] = FreeRTOS_htonl( ulSequenceNumber );
/* Last + 1 */
pxWindow->ulOptionsData[2] = FreeRTOS_htonl( ulLast );
/* Which make 12 (3*4) option bytes. */
pxWindow->ucOptionLength = 3 * sizeof( pxWindow->ulOptionsData[ 0 ] );
pxFound = xTCPWindowRxFind( pxWindow, ulSequenceNumber );
if( pxFound != NULL )
{
/* This out-of-sequence packet has been received for a
second time. It is already stored but do send a SACK
again. */
lReturn = -1;
}
else
{
pxFound = xTCPWindowRxNew( pxWindow, ulSequenceNumber, ( int32_t ) ulLength );
if( pxFound == NULL )
{
/* Can not send a SACK, because the segment cannot be
stored. */
pxWindow->ucOptionLength = 0u;
/* Needs to be stored but there is no segment
available. */
lReturn = -1;
}
else
{
if( xTCPWindowLoggingLevel != 0 )
{
FreeRTOS_debug_printf( ( "lTCPWindowRxCheck[%u,%u]: seqnr %lu (cnt %lu)\n",
pxWindow->usPeerPortNumber, pxWindow->usOurPortNumber, ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
listCURRENT_LIST_LENGTH( &pxWindow->xRxSegments ) ) );
FreeRTOS_flush_logging( );
}
/* Return a positive value. The packet may be accepted
and stored but an earlier packet is still missing. */
lReturn = ( int32_t ) ( ulSequenceNumber - ulCurrentSequenceNumber );
}
}
}
}
return lReturn;
}
#endif /* ipconfgiUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
/*=============================================================================
*
* ######### # #
* # # # # #
* # # #
* # ####
* # ##
* # ####
* # # #
* # # #
* ##### # #
*
* Tx functions
*
*=============================================================================*/
#if( ipconfigUSE_TCP_WIN == 1 )
static int32_t lTCPIncrementTxPosition( int32_t lPosition, int32_t lMax, int32_t lCount )
{
/* +TCP stores data in circular buffers. Calculate the next position to
store. */
lPosition += lCount;
if( lPosition >= lMax )
{
lPosition -= lMax;
}
return lPosition;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
int32_t lTCPWindowTxAdd( TCPWindow_t *pxWindow, uint32_t ulLength, int32_t lPosition, int32_t lMax )
{
int32_t lBytesLeft = ( int32_t ) ulLength, lToWrite;
int32_t lDone = 0;
TCPSegment_t *pxSegment = pxWindow->pxHeadSegment;
/* Puts a message in the Tx-window (after buffer size has been
verified). */
if( pxSegment != NULL )
{
if( pxSegment->lDataLength < pxSegment->lMaxLength )
{
if( ( pxSegment->u.bits.bOutstanding == pdFALSE_UNSIGNED ) && ( pxSegment->lDataLength != 0 ) )
{
/* Adding data to a segment that was already in the TX queue. It
will be filled-up to a maximum of MSS (maximum segment size). */
lToWrite = FreeRTOS_min_int32( lBytesLeft, pxSegment->lMaxLength - pxSegment->lDataLength );
pxSegment->lDataLength += lToWrite;
if( pxSegment->lDataLength >= pxSegment->lMaxLength )
{
/* This segment is full, don't add more bytes. */
pxWindow->pxHeadSegment = NULL;
}
lBytesLeft -= lToWrite;
/* ulNextTxSequenceNumber is the sequence number of the next byte to
be stored for transmission. */
pxWindow->ulNextTxSequenceNumber += ( uint32_t ) lToWrite;
/* Increased the return value. */
lDone += lToWrite;
/* Some detailed logging, for those who're interested. */
if( ( xTCPWindowLoggingLevel >= 2 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != 0 ) )
{
FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: Add %4lu bytes for seqNr %lu len %4lu (nxt %lu) pos %lu\n",
ulLength,
pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
pxSegment->lDataLength,
pxWindow->ulNextTxSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
pxSegment->lStreamPos ) );
FreeRTOS_flush_logging( );
}
/* Calculate the next position in the circular data buffer, knowing
its maximum length 'lMax'. */
lPosition = lTCPIncrementTxPosition( lPosition, lMax, lToWrite );
}
}
}
while( lBytesLeft > 0 )
{
/* The current transmission segment is full, create new segments as
needed. */
pxSegment = xTCPWindowTxNew( pxWindow, pxWindow->ulNextTxSequenceNumber, pxWindow->usMSS );
if( pxSegment != NULL )
{
/* Store as many as needed, but no more than the maximum
(MSS). */
lToWrite = FreeRTOS_min_int32( lBytesLeft, pxSegment->lMaxLength );
pxSegment->lDataLength = lToWrite;
pxSegment->lStreamPos = lPosition;
lBytesLeft -= lToWrite;
lPosition = lTCPIncrementTxPosition( lPosition, lMax, lToWrite );
pxWindow->ulNextTxSequenceNumber += ( uint32_t ) lToWrite;
lDone += lToWrite;
/* Link this segment in the Tx-Queue. */
vListInsertFifo( &( pxWindow->xTxQueue ), &( pxSegment->xQueueItem ) );
/* Let 'pxHeadSegment' point to this segment if there is still
space. */
if( pxSegment->lDataLength < pxSegment->lMaxLength )
{
pxWindow->pxHeadSegment = pxSegment;
}
else
{
pxWindow->pxHeadSegment = NULL;
}
if( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != 0 )
{
if( ( xTCPWindowLoggingLevel >= 3 ) ||
( ( xTCPWindowLoggingLevel >= 2 ) && ( pxWindow->pxHeadSegment != NULL ) ) )
{
FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: New %4ld bytes for seqNr %lu len %4lu (nxt %lu) pos %lu\n",
ulLength,
pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
pxSegment->lDataLength,
pxWindow->ulNextTxSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
pxSegment->lStreamPos ) );
FreeRTOS_flush_logging( );
}
}
}
else
{
/* A sever situation: running out of segments for transmission.
No more data can be sent at the moment. */
if( lDone != 0 )
{
FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: Sorry all buffers full (cancel %ld bytes)\n", lBytesLeft ) );
}
break;
}
}
return lDone;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
BaseType_t xTCPWindowTxDone( TCPWindow_t *pxWindow )
{
return listLIST_IS_EMPTY( ( &pxWindow->xTxSegments) );
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t *pxWindow, uint32_t ulWindowSize )
{
uint32_t ulTxOutstanding;
BaseType_t xHasSpace;
TCPSegment_t *pxSegment;
/* This function will look if there is new transmission data. It will
return true if there is data to be sent. */
pxSegment = xTCPWindowPeekHead( &( pxWindow->xTxQueue ) );
if( pxSegment == NULL )
{
xHasSpace = pdFALSE;
}
else
{
/* How much data is outstanding, i.e. how much data has been sent
but not yet acknowledged ? */
if( pxWindow->tx.ulHighestSequenceNumber >= pxWindow->tx.ulCurrentSequenceNumber )
{
ulTxOutstanding = pxWindow->tx.ulHighestSequenceNumber - pxWindow->tx.ulCurrentSequenceNumber;
}
else
{
ulTxOutstanding = 0UL;
}
/* Subtract this from the peer's space. */
ulWindowSize -= FreeRTOS_min_uint32( ulWindowSize, ulTxOutstanding );
/* See if the next segment may be sent. */
if( ulWindowSize >= ( uint32_t ) pxSegment->lDataLength )
{
xHasSpace = pdTRUE;
}
else
{
xHasSpace = pdFALSE;
}
/* If 'xHasSpace', it looks like the peer has at least space for 1
more new segment of size MSS. xSize.ulTxWindowLength is the self-imposed
limitation of the transmission window (in case of many resends it
may be decreased). */
if( ( ulTxOutstanding != 0UL ) && ( pxWindow->xSize.ulTxWindowLength < ulTxOutstanding + ( ( uint32_t ) pxSegment->lDataLength ) ) )
{
xHasSpace = pdFALSE;
}
}
return xHasSpace;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
BaseType_t xTCPWindowTxHasData( TCPWindow_t *pxWindow, uint32_t ulWindowSize, TickType_t *pulDelay )
{
TCPSegment_t *pxSegment;
BaseType_t xReturn;
TickType_t ulAge, ulMaxAge;
*pulDelay = 0u;
if( listLIST_IS_EMPTY( &pxWindow->xPriorityQueue ) == pdFALSE )
{
/* No need to look at retransmissions or new transmission as long as
there are priority segments. *pulDelay equals zero, meaning it must
be sent out immediately. */
xReturn = pdTRUE;
}
else
{
pxSegment = xTCPWindowPeekHead( &( pxWindow->xWaitQueue ) );
if( pxSegment != NULL )
{
/* There is an outstanding segment, see if it is time to resend
it. */
ulAge = ulTimerGetAge( &pxSegment->xTransmitTimer );
/* After a packet has been sent for the first time, it will wait
'1 * lSRTT' ms for an ACK. A second time it will wait '2 * lSRTT' ms,
each time doubling the time-out */
ulMaxAge = ( 1u << pxSegment->u.bits.ucTransmitCount ) * ( ( uint32_t ) pxWindow->lSRTT );
if( ulMaxAge > ulAge )
{
/* A segment must be sent after this amount of msecs */
*pulDelay = ulMaxAge - ulAge;
}
xReturn = pdTRUE;
}
else
{
/* No priority segment, no outstanding data, see if there is new
transmission data. */
pxSegment = xTCPWindowPeekHead( &pxWindow->xTxQueue );
/* See if it fits in the peer's reception window. */
if( pxSegment == NULL )
{
xReturn = pdFALSE;
}
else if( prvTCPWindowTxHasSpace( pxWindow, ulWindowSize ) == pdFALSE )
{
/* Too many outstanding messages. */
xReturn = pdFALSE;
}
else if( ( pxWindow->u.bits.bSendFullSize != pdFALSE_UNSIGNED ) && ( pxSegment->lDataLength < pxSegment->lMaxLength ) )
{
/* 'bSendFullSize' is a special optimisation. If true, the
driver will only sent completely filled packets (of MSS
bytes). */
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
}
}
return xReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
uint32_t ulTCPWindowTxGet( TCPWindow_t *pxWindow, uint32_t ulWindowSize, int32_t *plPosition )
{
TCPSegment_t *pxSegment;
uint32_t ulMaxTime;
uint32_t ulReturn = ~0UL;
/* Fetches data to be sent-out now.
Priority messages: segments with a resend need no check current sliding
window size. */
pxSegment = xTCPWindowGetHead( &( pxWindow->xPriorityQueue ) );
pxWindow->ulOurSequenceNumber = pxWindow->tx.ulHighestSequenceNumber;
if( pxSegment == NULL )
{
/* Waiting messages: outstanding messages with a running timer
neither check peer's reception window size because these packets
have been sent earlier. */
pxSegment = xTCPWindowPeekHead( &( pxWindow->xWaitQueue ) );
if( pxSegment != NULL )
{
/* Do check the timing. */
ulMaxTime = ( 1u << pxSegment->u.bits.ucTransmitCount ) * ( ( uint32_t ) pxWindow->lSRTT );
if( ulTimerGetAge( &pxSegment->xTransmitTimer ) > ulMaxTime )
{
/* A normal (non-fast) retransmission. Move it from the
head of the waiting queue. */
pxSegment = xTCPWindowGetHead( &( pxWindow->xWaitQueue ) );
pxSegment->u.bits.ucDupAckCount = pdFALSE_UNSIGNED;
/* Some detailed logging. */
if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != 0 ) )
{
FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u,%u]: WaitQueue %ld bytes for sequence number %lu (%lX)\n",
pxWindow->usPeerPortNumber,
pxWindow->usOurPortNumber,
pxSegment->lDataLength,
pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
pxSegment->ulSequenceNumber ) );
FreeRTOS_flush_logging( );
}
}
else
{
pxSegment = NULL;
}
}
if( pxSegment == NULL )
{
/* New messages: sent-out for the first time. Check current
sliding window size of peer. */
pxSegment = xTCPWindowPeekHead( &( pxWindow->xTxQueue ) );
if( pxSegment == NULL )
{
/* No segments queued. */
ulReturn = 0UL;
}
else if( ( pxWindow->u.bits.bSendFullSize != pdFALSE_UNSIGNED ) && ( pxSegment->lDataLength < pxSegment->lMaxLength ) )
{
/* A segment has been queued but the driver waits until it
has a full size of MSS. */
ulReturn = 0;
}
else if( prvTCPWindowTxHasSpace( pxWindow, ulWindowSize ) == pdFALSE )
{
/* Peer has no more space at this moment. */
ulReturn = 0;
}
else
{
/* Move it out of the Tx queue. */
pxSegment = xTCPWindowGetHead( &( pxWindow->xTxQueue ) );
/* Don't let pxHeadSegment point to this segment any more,
so no more data will be added. */
if( pxWindow->pxHeadSegment == pxSegment )
{
pxWindow->pxHeadSegment = NULL;
}
/* pxWindow->tx.highest registers the highest sequence
number in our transmission window. */
pxWindow->tx.ulHighestSequenceNumber = pxSegment->ulSequenceNumber + ( ( uint32_t ) pxSegment->lDataLength );
/* ...and more detailed logging */
if( ( xTCPWindowLoggingLevel >= 2 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) )
{
FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u,%u]: XmitQueue %ld bytes for sequence number %lu (ws %lu)\n",
pxWindow->usPeerPortNumber,
pxWindow->usOurPortNumber,
pxSegment->lDataLength,
pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
ulWindowSize ) );
FreeRTOS_flush_logging( );
}
}
}
}
else
{
/* There is a priority segment. It doesn't need any checking for
space or timeouts. */
if( xTCPWindowLoggingLevel != 0 )
{
FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u,%u]: PrioQueue %ld bytes for sequence number %lu (ws %lu)\n",
pxWindow->usPeerPortNumber,
pxWindow->usOurPortNumber,
pxSegment->lDataLength,
pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
ulWindowSize ) );
FreeRTOS_flush_logging( );
}
}
/* See if it has already been determined to return 0. */
if( ulReturn != 0UL )
{
configASSERT( listLIST_ITEM_CONTAINER( &(pxSegment->xQueueItem ) ) == NULL );
/* Now that the segment will be transmitted, add it to the tail of
the waiting queue. */
vListInsertFifo( &pxWindow->xWaitQueue, &pxSegment->xQueueItem );
/* And mark it as outstanding. */
pxSegment->u.bits.bOutstanding = pdTRUE_UNSIGNED;
/* Administer the transmit count, needed for fast
retransmissions. */
( pxSegment->u.bits.ucTransmitCount )++;
/* If there have been several retransmissions (4), decrease the
size of the transmission window to at most 2 times MSS. */
if( pxSegment->u.bits.ucTransmitCount == MAX_TRANSMIT_COUNT_USING_LARGE_WINDOW )
{
if( pxWindow->xSize.ulTxWindowLength > ( 2U * pxWindow->usMSS ) )
{
FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u - %d]: Change Tx window: %lu -> %u\n",
pxWindow->usPeerPortNumber, pxWindow->usOurPortNumber,
pxWindow->xSize.ulTxWindowLength, 2 * pxWindow->usMSS ) );
pxWindow->xSize.ulTxWindowLength = ( 2UL * pxWindow->usMSS );
}
}
/* Clear the transmit timer. */
vTCPTimerSet( &( pxSegment->xTransmitTimer ) );
pxWindow->ulOurSequenceNumber = pxSegment->ulSequenceNumber;
/* Inform the caller where to find the data within the queue. */
*plPosition = pxSegment->lStreamPos;
/* And return the length of the data segment */
ulReturn = ( uint32_t ) pxSegment->lDataLength;
}
return ulReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static uint32_t prvTCPWindowTxCheckAck( TCPWindow_t *pxWindow, uint32_t ulFirst, uint32_t ulLast )
{
uint32_t ulBytesConfirmed = 0u;
uint32_t ulSequenceNumber = ulFirst, ulDataLength;
const ListItem_t *pxIterator;
const MiniListItem_t *pxEnd = ( const MiniListItem_t* )listGET_END_MARKER( &pxWindow->xTxSegments );
BaseType_t xDoUnlink;
TCPSegment_t *pxSegment;
/* An acknowledgement or a selective ACK (SACK) was received. See if some outstanding data
may be removed from the transmission queue(s).
All TX segments for which
( ( ulSequenceNumber >= ulFirst ) && ( ulSequenceNumber < ulLast ) in a
contiguous block. Note that the segments are stored in xTxSegments in a
strict sequential order. */
/* SRTT[i] = (1-a) * SRTT[i-1] + a * RTT
0 < a < 1; usually a = 1/8
RTO = 2 * SRTT
where:
RTT is Round Trip Time
SRTT is Smoothed RTT
RTO is Retransmit timeout
A Smoothed RTT will increase quickly, but it is conservative when
becoming smaller. */
for(
pxIterator = ( const ListItem_t * ) listGET_NEXT( pxEnd );
( pxIterator != ( const ListItem_t * ) pxEnd ) && ( xSequenceLessThan( ulSequenceNumber, ulLast ) != 0 );
)
{
xDoUnlink = pdFALSE;
pxSegment = ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
/* Move to the next item because the current item might get
removed. */
pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator );
/* Continue if this segment does not fall within the ACK'd range. */
if( xSequenceGreaterThan( ulSequenceNumber, pxSegment->ulSequenceNumber ) != pdFALSE )
{
continue;
}
/* Is it ready? */
if( ulSequenceNumber != pxSegment->ulSequenceNumber )
{
break;
}
ulDataLength = ( uint32_t ) pxSegment->lDataLength;
if( pxSegment->u.bits.bAcked == pdFALSE_UNSIGNED )
{
if( xSequenceGreaterThan( pxSegment->ulSequenceNumber + ( uint32_t )ulDataLength, ulLast ) != pdFALSE )
{
/* What happens? Only part of this segment was accepted,
probably due to WND limits
AAAAAAA BBBBBBB << acked
aaaaaaa aaaa << sent */
#if( ipconfigHAS_DEBUG_PRINTF != 0 )
{
uint32_t ulFirstSeq = pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber;
FreeRTOS_debug_printf( ( "prvTCPWindowTxCheckAck[%u.%u]: %lu - %lu Partial sequence number %lu - %lu\n",
pxWindow->usPeerPortNumber,
pxWindow->usOurPortNumber,
ulFirstSeq - pxWindow->tx.ulFirstSequenceNumber,
ulLast - pxWindow->tx.ulFirstSequenceNumber,
ulFirstSeq, ulFirstSeq + ulDataLength ) );
}
#endif /* ipconfigHAS_DEBUG_PRINTF */
break;
}
/* This segment is fully ACK'd, set the flag. */
pxSegment->u.bits.bAcked = pdTRUE_UNSIGNED;
/* Calculate the RTT only if the segment was sent-out for the
first time and if this is the last ACK'd segment in a range. */
if( ( pxSegment->u.bits.ucTransmitCount == 1 ) && ( ( pxSegment->ulSequenceNumber + ulDataLength ) == ulLast ) )
{
int32_t mS = ( int32_t ) ulTimerGetAge( &( pxSegment->xTransmitTimer ) );
if( pxWindow->lSRTT >= mS )
{
/* RTT becomes smaller: adapt slowly. */
pxWindow->lSRTT = ( ( winSRTT_DECREMENT_NEW * mS ) + ( winSRTT_DECREMENT_CURRENT * pxWindow->lSRTT ) ) / ( winSRTT_DECREMENT_NEW + winSRTT_DECREMENT_CURRENT );
}
else
{
/* RTT becomes larger: adapt quicker */
pxWindow->lSRTT = ( ( winSRTT_INCREMENT_NEW * mS ) + ( winSRTT_INCREMENT_CURRENT * pxWindow->lSRTT ) ) / ( winSRTT_INCREMENT_NEW + winSRTT_INCREMENT_CURRENT );
}
/* Cap to the minimum of 50ms. */
if( pxWindow->lSRTT < winSRTT_CAP_mS )
{
pxWindow->lSRTT = winSRTT_CAP_mS;
}
}
/* Unlink it from the 3 queues, but do not destroy it (yet). */
xDoUnlink = pdTRUE;
}
/* pxSegment->u.bits.bAcked is now true. Is it located at the left
side of the transmission queue? If so, it may be freed. */
if( ulSequenceNumber == pxWindow->tx.ulCurrentSequenceNumber )
{
if( ( xTCPWindowLoggingLevel >= 2 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) )
{
FreeRTOS_debug_printf( ( "prvTCPWindowTxCheckAck: %lu - %lu Ready sequence number %lu\n",
ulFirst - pxWindow->tx.ulFirstSequenceNumber,
ulLast - pxWindow->tx.ulFirstSequenceNumber,
pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ) );
}
/* Increase the left-hand value of the transmission window. */
pxWindow->tx.ulCurrentSequenceNumber += ulDataLength;
/* This function will return the number of bytes that the tail
of txStream may be advanced. */
ulBytesConfirmed += ulDataLength;
/* All segments below tx.ulCurrentSequenceNumber may be freed. */
vTCPWindowFree( pxSegment );
/* No need to unlink it any more. */
xDoUnlink = pdFALSE;
}
if( ( xDoUnlink != pdFALSE ) && ( listLIST_ITEM_CONTAINER( &( pxSegment->xQueueItem ) ) != NULL ) )
{
/* Remove item from its queues. */
uxListRemove( &pxSegment->xQueueItem );
}
ulSequenceNumber += ulDataLength;
}
return ulBytesConfirmed;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
static uint32_t prvTCPWindowFastRetransmit( TCPWindow_t *pxWindow, uint32_t ulFirst )
{
const ListItem_t *pxIterator;
const MiniListItem_t* pxEnd;
TCPSegment_t *pxSegment;
uint32_t ulCount = 0UL;
/* A higher Tx block has been acknowledged. Now iterate through the
xWaitQueue to find a possible condition for a FAST retransmission. */
pxEnd = ( const MiniListItem_t* ) listGET_END_MARKER( &( pxWindow->xWaitQueue ) );
for( pxIterator = ( const ListItem_t * ) listGET_NEXT( pxEnd );
pxIterator != ( const ListItem_t * ) pxEnd; )
{
/* Get the owner, which is a TCP segment. */
pxSegment = ( TCPSegment_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
/* Hop to the next item before the current gets unlinked. */
pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator );
/* Fast retransmission:
When 3 packets with a higher sequence number have been acknowledged
by the peer, it is very unlikely a current packet will ever arrive.
It will be retransmitted far before the RTO. */
if( ( pxSegment->u.bits.bAcked == pdFALSE_UNSIGNED ) &&
( xSequenceLessThan( pxSegment->ulSequenceNumber, ulFirst ) != pdFALSE ) &&
( ++( pxSegment->u.bits.ucDupAckCount ) == DUPLICATE_ACKS_BEFORE_FAST_RETRANSMIT ) )
{
pxSegment->u.bits.ucTransmitCount = pdFALSE_UNSIGNED;
/* Not clearing 'ucDupAckCount' yet as more SACK's might come in
which might lead to a second fast rexmit. */
if( ( xTCPWindowLoggingLevel >= 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) )
{
FreeRTOS_debug_printf( ( "prvTCPWindowFastRetransmit: Requeue sequence number %lu < %lu\n",
pxSegment->ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
ulFirst - pxWindow->tx.ulFirstSequenceNumber ) );
FreeRTOS_flush_logging( );
}
/* Remove it from xWaitQueue. */
uxListRemove( &pxSegment->xQueueItem );
/* Add this segment to the priority queue so it gets
retransmitted immediately. */
vListInsertFifo( &( pxWindow->xPriorityQueue ), &( pxSegment->xQueueItem ) );
ulCount++;
}
}
return ulCount;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
uint32_t ulTCPWindowTxAck( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber )
{
uint32_t ulFirstSequence, ulReturn;
/* Receive a normal ACK. */
ulFirstSequence = pxWindow->tx.ulCurrentSequenceNumber;
if( xSequenceLessThanOrEqual( ulSequenceNumber, ulFirstSequence ) != pdFALSE )
{
ulReturn = 0UL;
}
else
{
ulReturn = prvTCPWindowTxCheckAck( pxWindow, ulFirstSequence, ulSequenceNumber );
}
return ulReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 1 )
uint32_t ulTCPWindowTxSack( TCPWindow_t *pxWindow, uint32_t ulFirst, uint32_t ulLast )
{
uint32_t ulAckCount = 0UL;
uint32_t ulCurrentSequenceNumber = pxWindow->tx.ulCurrentSequenceNumber;
/* Receive a SACK option. */
ulAckCount = prvTCPWindowTxCheckAck( pxWindow, ulFirst, ulLast );
prvTCPWindowFastRetransmit( pxWindow, ulFirst );
if( ( xTCPWindowLoggingLevel >= 1 ) && ( xSequenceGreaterThan( ulFirst, ulCurrentSequenceNumber ) != pdFALSE ) )
{
FreeRTOS_debug_printf( ( "ulTCPWindowTxSack[%u,%u]: from %lu to %lu (ack = %lu)\n",
pxWindow->usPeerPortNumber,
pxWindow->usOurPortNumber,
ulFirst - pxWindow->tx.ulFirstSequenceNumber,
ulLast - pxWindow->tx.ulFirstSequenceNumber,
pxWindow->tx.ulCurrentSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ) );
FreeRTOS_flush_logging( );
}
return ulAckCount;
}
#endif /* ipconfigUSE_TCP_WIN == 1 */
/*-----------------------------------------------------------*/
/*
##### # ##### #### ######
# # # # # # # # # # #
# # # # # #
# ### ##### # # # # # #
# # # # # # # # #####
# # # # # # #### # # #
# # # # # # # # # #
# # # # #### # # # #
#### ##### # # # #### #### ####
#
###
*/
#if( ipconfigUSE_TCP_WIN == 0 )
int32_t lTCPWindowRxCheck( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber, uint32_t ulLength, uint32_t ulSpace )
{
int32_t iReturn;
/* Data was received at 'ulSequenceNumber'. See if it was expected
and if there is enough space to store the new data. */
if( ( pxWindow->rx.ulCurrentSequenceNumber != ulSequenceNumber ) || ( ulSpace < ulLength ) )
{
iReturn = -1;
}
else
{
pxWindow->rx.ulCurrentSequenceNumber += ( uint32_t ) ulLength;
iReturn = 0;
}
return iReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 0 )
int32_t lTCPWindowTxAdd( TCPWindow_t *pxWindow, uint32_t ulLength, int32_t lPosition, int32_t lMax )
{
TCPSegment_t *pxSegment = &( pxWindow->xTxSegment );
int32_t lResult;
/* Data is being scheduled for transmission. */
/* lMax would indicate the size of the txStream. */
( void ) lMax;
/* This is tiny TCP: there is only 1 segment for outgoing data.
As long as 'lDataLength' is unequal to zero, the segment is still occupied. */
if( pxSegment->lDataLength > 0 )
{
lResult = 0L;
}
else
{
if( ulLength > ( uint32_t ) pxSegment->lMaxLength )
{
if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) )
{
FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: can only store %ld / %ld bytes\n", ulLength, pxSegment->lMaxLength ) );
}
ulLength = ( uint32_t ) pxSegment->lMaxLength;
}
if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) )
{
FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: SeqNr %ld (%ld) Len %ld\n",
pxWindow->ulNextTxSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
pxWindow->tx.ulCurrentSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
ulLength ) );
}
/* The sequence number of the first byte in this packet. */
pxSegment->ulSequenceNumber = pxWindow->ulNextTxSequenceNumber;
pxSegment->lDataLength = ( int32_t ) ulLength;
pxSegment->lStreamPos = lPosition;
pxSegment->u.ulFlags = 0UL;
vTCPTimerSet( &( pxSegment->xTransmitTimer ) );
/* Increase the sequence number of the next data to be stored for
transmission. */
pxWindow->ulNextTxSequenceNumber += ulLength;
lResult = ( int32_t )ulLength;
}
return lResult;
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 0 )
uint32_t ulTCPWindowTxGet( TCPWindow_t *pxWindow, uint32_t ulWindowSize, int32_t *plPosition )
{
TCPSegment_t *pxSegment = &( pxWindow->xTxSegment );
uint32_t ulLength = ( uint32_t ) pxSegment->lDataLength;
uint32_t ulMaxTime;
if( ulLength != 0UL )
{
/* _HT_ Still under investigation */
( void ) ulWindowSize;
if( pxSegment->u.bits.bOutstanding != pdFALSE_UNSIGNED )
{
/* As 'ucTransmitCount' has a minimum of 1, take 2 * RTT */
ulMaxTime = ( ( uint32_t ) 1u << pxSegment->u.bits.ucTransmitCount ) * ( ( uint32_t ) pxWindow->lSRTT );
if( ulTimerGetAge( &( pxSegment->xTransmitTimer ) ) < ulMaxTime )
{
ulLength = 0ul;
}
}
if( ulLength != 0ul )
{
pxSegment->u.bits.bOutstanding = pdTRUE_UNSIGNED;
pxSegment->u.bits.ucTransmitCount++;
vTCPTimerSet (&pxSegment->xTransmitTimer);
pxWindow->ulOurSequenceNumber = pxSegment->ulSequenceNumber;
*plPosition = pxSegment->lStreamPos;
}
}
return ulLength;
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 0 )
BaseType_t xTCPWindowTxDone( TCPWindow_t *pxWindow )
{
BaseType_t xReturn;
/* Has the outstanding data been sent because user wants to shutdown? */
if( pxWindow->xTxSegment.lDataLength == 0 )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 0 )
static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t *pxWindow, uint32_t ulWindowSize );
static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t *pxWindow, uint32_t ulWindowSize )
{
BaseType_t xReturn;
if( ulWindowSize >= pxWindow->usMSSInit )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 0 )
BaseType_t xTCPWindowTxHasData( TCPWindow_t *pxWindow, uint32_t ulWindowSize, TickType_t *pulDelay )
{
TCPSegment_t *pxSegment = &( pxWindow->xTxSegment );
BaseType_t xReturn;
TickType_t ulAge, ulMaxAge;
/* Check data to be sent. */
*pulDelay = ( TickType_t ) 0;
if( pxSegment->lDataLength == 0 )
{
/* Got nothing to send right now. */
xReturn = pdFALSE;
}
else
{
if( pxSegment->u.bits.bOutstanding != pdFALSE_UNSIGNED )
{
ulAge = ulTimerGetAge ( &pxSegment->xTransmitTimer );
ulMaxAge = ( ( TickType_t ) 1u << pxSegment->u.bits.ucTransmitCount ) * ( ( uint32_t ) pxWindow->lSRTT );
if( ulMaxAge > ulAge )
{
*pulDelay = ulMaxAge - ulAge;
}
xReturn = pdTRUE;
}
else if( prvTCPWindowTxHasSpace( pxWindow, ulWindowSize ) == pdFALSE )
{
/* Too many outstanding messages. */
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
}
return xReturn;
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 0 )
uint32_t ulTCPWindowTxAck( TCPWindow_t *pxWindow, uint32_t ulSequenceNumber )
{
TCPSegment_t *pxSegment = &( pxWindow->xTxSegment );
uint32_t ulDataLength = ( uint32_t ) pxSegment->lDataLength;
/* Receive a normal ACK */
if( ulDataLength != 0ul )
{
if( ulSequenceNumber < ( pxWindow->tx.ulCurrentSequenceNumber + ulDataLength ) )
{
if( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE )
{
FreeRTOS_debug_printf( ( "win_tx_ack: acked %ld expc %ld len %ld\n",
ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
pxWindow->tx.ulCurrentSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
ulDataLength ) );
}
/* Nothing to send right now. */
ulDataLength = 0ul;
}
else
{
pxWindow->tx.ulCurrentSequenceNumber += ulDataLength;
if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) )
{
FreeRTOS_debug_printf( ( "win_tx_ack: acked seqnr %ld len %ld\n",
ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber,
ulDataLength ) );
}
pxSegment->lDataLength = 0;
}
}
return ulDataLength;
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 0 )
BaseType_t xTCPWindowRxEmpty( TCPWindow_t *pxWindow )
{
/* Return true if 'ulCurrentSequenceNumber >= ulHighestSequenceNumber'
'ulCurrentSequenceNumber' is the highest sequence number stored,
'ulHighestSequenceNumber' is the highest sequence number seen. */
return xSequenceGreaterThanOrEqual( pxWindow->rx.ulCurrentSequenceNumber, pxWindow->rx.ulHighestSequenceNumber );
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/
#if( ipconfigUSE_TCP_WIN == 0 )
/* Destroy a window (always returns NULL) */
void vTCPWindowDestroy( TCPWindow_t *pxWindow )
{
/* As in tiny TCP there are no shared segments descriptors, there is
nothing to release. */
( void ) pxWindow;
}
#endif /* ipconfigUSE_TCP_WIN == 0 */
/*-----------------------------------------------------------*/