/*
* FreeRTOS + TCP V2 .0 .7
* 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 segement 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. */
pxNewListItem - > pvContainer = ( void * ) pxList ; /* If this line fails to build then ensure configENABLE_BACKWARD_COMPATIBILITY is set to 1 in FreeRTOSConfig.h. */
( 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 ;
}
/*-----------------------------------------------------------*/
/*=============================================================================
*
* # # # # # # # #
* # # # #
* # # # #
* # # # # # #
* # # # # # # # #
* # # # # # # #
* # # # #
* # # # #
* # # # # # # #
* 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 */
/*-----------------------------------------------------------*/