mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-28 23:49:44 +08:00
2516 lines
100 KiB
C
2516 lines
100 KiB
C
/*
|
|
* FreeRTOS+TCP V2.3.2
|
|
* Copyright (C) 2020 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
|
|
*/
|
|
|
|
|
|
/**
|
|
* @file FreeRTOS_TCP_WIN.c
|
|
* @brief 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"
|
|
|
|
/* FreeRTOS+TCP includes. */
|
|
#include "FreeRTOS_UDP_IP.h"
|
|
#include "FreeRTOS_IP.h"
|
|
#include "FreeRTOS_Sockets.h"
|
|
#include "FreeRTOS_IP_Private.h"
|
|
|
|
|
|
#ifndef FREERTOS_DEFAULT_IP_CONFIG_H
|
|
#error "FreeRTOSIPConfigDefaults.h not included."
|
|
#endif
|
|
|
|
#if ( ipconfigUSE_TCP == 1 )
|
|
|
|
/* Constants used for Smoothed Round Trip Time (SRTT). */
|
|
#define winSRTT_INCREMENT_NEW 2 /**< New increment for the smoothed RTT. */
|
|
#define winSRTT_INCREMENT_CURRENT 6 /**< Current increment for the smoothed RTT. */
|
|
#define winSRTT_DECREMENT_NEW 1 /**< New decrement for the smoothed RTT. */
|
|
#define winSRTT_DECREMENT_CURRENT 7 /**< Current decrement for the smoothed RTT. */
|
|
#define winSRTT_CAP_mS 50 /**< Cap in milliseconds. */
|
|
|
|
/**
|
|
* @brief Utility function to cast pointer of a type to pointer of type TCPSegment_t.
|
|
*
|
|
* @return The casted pointer.
|
|
*/
|
|
static portINLINE ipDECL_CAST_PTR_FUNC_FOR_TYPE( TCPSegment_t )
|
|
{
|
|
return ( TCPSegment_t * ) pvArgument;
|
|
}
|
|
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/** @brief Create a new Rx window. */
|
|
#define xTCPWindowRxNew( pxWindow, ulSequenceNumber, lCount ) xTCPWindowNew( pxWindow, ulSequenceNumber, lCount, pdTRUE )
|
|
|
|
/** @brief Create a new Tx window. */
|
|
#define xTCPWindowTxNew( pxWindow, ulSequenceNumber, lCount ) xTCPWindowNew( pxWindow, ulSequenceNumber, lCount, pdFALSE )
|
|
|
|
/** @brief 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
|
|
|
|
/** @brief 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 )
|
|
|
|
/** @brief 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 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static 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( const 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 */
|
|
|
|
/*
|
|
* Detaches and returns the head of a queue
|
|
*/
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
static TCPSegment_t * xTCPWindowGetHead( const 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( const 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( const 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 const * 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
|
|
|
|
/** @brief 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 );
|
|
|
|
/**
|
|
* @brief Check if a <= b.
|
|
*
|
|
* @param[in] a: The value on the left-hand side.
|
|
* @param[in] b: The value on the right-hand side.
|
|
*
|
|
* @return pdTRUE when "( b - a ) < 0x80000000". Else, pdFALSE.
|
|
*/
|
|
static portINLINE BaseType_t xSequenceLessThanOrEqual( uint32_t a,
|
|
uint32_t b )
|
|
{
|
|
BaseType_t xResult;
|
|
|
|
/* Test if a <= b
|
|
* Return true if the unsigned subtraction of (b-a) doesn't generate an
|
|
* arithmetic overflow. */
|
|
if( ( ( b - a ) & 0x80000000UL ) == 0UL )
|
|
{
|
|
xResult = pdTRUE;
|
|
}
|
|
else
|
|
{
|
|
xResult = pdFALSE;
|
|
}
|
|
|
|
return xResult;
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
static portINLINE BaseType_t xSequenceLessThan( uint32_t a,
|
|
uint32_t b );
|
|
|
|
/**
|
|
* @brief Check if a < b.
|
|
*
|
|
* @param[in] a: The value on the left-hand side.
|
|
* @param[in] b: The value on the right-hand side.
|
|
*
|
|
* @return pdTRUE when "( b - ( a + 1 ) ) < 0x80000000", else pdFALSE.
|
|
*/
|
|
static portINLINE BaseType_t xSequenceLessThan( uint32_t a,
|
|
uint32_t b )
|
|
{
|
|
BaseType_t xResult;
|
|
|
|
/* Test if a < b */
|
|
if( ( ( b - ( a + 1UL ) ) & 0x80000000UL ) == 0UL )
|
|
{
|
|
xResult = pdTRUE;
|
|
}
|
|
else
|
|
{
|
|
xResult = pdFALSE;
|
|
}
|
|
|
|
return xResult;
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
static portINLINE BaseType_t xSequenceGreaterThan( uint32_t a,
|
|
uint32_t b );
|
|
|
|
/**
|
|
* @brief Check if a > b.
|
|
*
|
|
* @param[in] a: The value on the left-hand side.
|
|
* @param[in] b: The value on the right-hand side.
|
|
*
|
|
* @return pdTRUE when "( a - b ) < 0x80000000", else pdFALSE.
|
|
*/
|
|
static portINLINE BaseType_t xSequenceGreaterThan( uint32_t a,
|
|
uint32_t b )
|
|
{
|
|
BaseType_t xResult;
|
|
|
|
/* Test if a > b */
|
|
if( ( ( a - ( b + 1UL ) ) & 0x80000000UL ) == 0UL )
|
|
{
|
|
xResult = pdTRUE;
|
|
}
|
|
else
|
|
{
|
|
xResult = pdFALSE;
|
|
}
|
|
|
|
return xResult;
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN */
|
|
|
|
/*-----------------------------------------------------------*/
|
|
static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a,
|
|
uint32_t b );
|
|
|
|
/**
|
|
* @brief Test if a>=b. This function is required since the sequence numbers can roll over.
|
|
*
|
|
* @param[in] a: The first sequence number.
|
|
* @param[in] b: The second sequence number.
|
|
*
|
|
* @return pdTRUE if a>=b, else pdFALSE.
|
|
*/
|
|
|
|
static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a,
|
|
uint32_t b )
|
|
{
|
|
BaseType_t xResult;
|
|
|
|
/* Test if a >= b */
|
|
if( ( ( a - b ) & 0x80000000UL ) == 0UL )
|
|
{
|
|
xResult = pdTRUE;
|
|
}
|
|
else
|
|
{
|
|
xResult = pdFALSE;
|
|
}
|
|
|
|
return xResult;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
static portINLINE void vListInsertFifo( List_t * const pxList,
|
|
ListItem_t * const pxNewListItem );
|
|
|
|
/**
|
|
* @brief Insert the given item in the list in FIFO manner.
|
|
*
|
|
* @param[in] pxList: The list in which the item is to inserted.
|
|
* @param[in] pxNewListItem: The item to be inserted.
|
|
*/
|
|
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 );
|
|
|
|
/**
|
|
* @brief Set the timer's "born" time.
|
|
*
|
|
* @param[in] pxTimer: The TCP timer.
|
|
*/
|
|
static portINLINE void vTCPTimerSet( TCPTimer_t * pxTimer )
|
|
{
|
|
pxTimer->ulBorn = xTaskGetTickCount();
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static portINLINE uint32_t ulTimerGetAge( const TCPTimer_t * pxTimer );
|
|
|
|
/**
|
|
* @brief Get the timer age in milliseconds.
|
|
*
|
|
* @param[in] pxTimer: The timer whose age is to be fetched.
|
|
*
|
|
* @return The time in milliseconds since the timer was born.
|
|
*/
|
|
static portINLINE uint32_t ulTimerGetAge( const TCPTimer_t * pxTimer )
|
|
{
|
|
return( ( xTaskGetTickCount() - ( ( TickType_t ) pxTimer->ulBorn ) ) * portTICK_PERIOD_MS );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Insert a new list item into a list.
|
|
*
|
|
* @param[in] pxList: The list in which the item is to be inserted.
|
|
* @param[in] pxNewListItem: The item to be inserted.
|
|
* @param[in] pxWhere: Where should the item be inserted.
|
|
*/
|
|
static 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 ) = ( struct xLIST * configLIST_VOLATILE ) pxList;
|
|
|
|
( pxList->uxNumberOfItems )++;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief Creates a pool of 'ipconfigTCP_WIN_SEG_COUNT' sector buffers. Should be called once only.
|
|
*
|
|
* @return When the allocation was successful: pdPASS, otherwise pdFAIL.
|
|
*/
|
|
static BaseType_t prvCreateSectors( void )
|
|
{
|
|
BaseType_t xIndex, xReturn;
|
|
|
|
/* Allocate space for 'xTCPSegments' and store them in 'xSegmentList'. */
|
|
|
|
vListInitialise( &xSegmentList );
|
|
xTCPSegments = ipCAST_PTR_TO_TYPE_PTR( TCPSegment_t, pvPortMallocLarge( ( size_t ) ipconfigTCP_WIN_SEG_COUNT * sizeof( xTCPSegments[ 0 ] ) ) );
|
|
|
|
if( xTCPSegments == NULL )
|
|
{
|
|
FreeRTOS_debug_printf( ( "prvCreateSectors: malloc %u failed\n",
|
|
( unsigned ) ipconfigTCP_WIN_SEG_COUNT * sizeof( xTCPSegments[ 0 ] ) ) );
|
|
|
|
xReturn = pdFAIL;
|
|
}
|
|
else
|
|
{
|
|
/* Clear the allocated space. */
|
|
( void ) memset( xTCPSegments, 0, ( size_t ) 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 ].xSegmentItem ), ( 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 ].xSegmentItem ) );
|
|
}
|
|
|
|
xReturn = pdPASS;
|
|
}
|
|
|
|
return xReturn;
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief Find a segment with a given sequence number in the list of received segments.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulSequenceNumber: the sequence number to look-up
|
|
*
|
|
* @return The address of the segment descriptor found, or NULL when not found.
|
|
*/
|
|
static TCPSegment_t * xTCPWindowRxFind( const TCPWindow_t * pxWindow,
|
|
uint32_t ulSequenceNumber )
|
|
{
|
|
const ListItem_t * pxIterator;
|
|
const ListItem_t * pxEnd;
|
|
TCPSegment_t * pxSegment, * pxReturn = NULL;
|
|
|
|
/* Find a segment with a given sequence number in the list of received
|
|
* segments. */
|
|
pxEnd = listGET_END_MARKER( &pxWindow->xRxSegments );
|
|
|
|
for( pxIterator = listGET_NEXT( pxEnd );
|
|
pxIterator != pxEnd;
|
|
pxIterator = listGET_NEXT( pxIterator ) )
|
|
{
|
|
pxSegment = ipCAST_PTR_TO_TYPE_PTR( 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 )
|
|
|
|
/**
|
|
* @brief Allocate a new segment object, either for transmission or reception.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulSequenceNumber: The sequence number.
|
|
* @param[in] lCount: The number of bytes stored in this segment.
|
|
* @param[in] xIsForRx: True when this is a reception segment.
|
|
*
|
|
* @return Allocate and initialise a segment descriptor, or NULL when none was available.
|
|
*/
|
|
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 != 0 ) ? '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 = ipCAST_PTR_TO_TYPE_PTR( TCPSegment_t, listGET_LIST_ITEM_OWNER( pxItem ) );
|
|
|
|
configASSERT( pxItem != NULL );
|
|
configASSERT( pxSegment != NULL );
|
|
|
|
/* Remove the item from xSegmentList. */
|
|
( void ) uxListRemove( pxItem );
|
|
|
|
/* Add it to either the connections' Rx or Tx queue. */
|
|
if( xIsForRx != 0 )
|
|
{
|
|
vListInsertFifo( &pxWindow->xRxSegments, pxItem );
|
|
}
|
|
else
|
|
{
|
|
vListInsertFifo( &pxWindow->xTxSegments, pxItem );
|
|
}
|
|
|
|
/* And set the segment's timer to zero */
|
|
vTCPTimerSet( &pxSegment->xTransmitTimer );
|
|
|
|
pxSegment->u.ulFlags = 0;
|
|
pxSegment->u.bits.bIsForRx = ( xIsForRx != 0 ) ? 1U : 0U;
|
|
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 )
|
|
|
|
/**
|
|
* @brief See if the peer has more packets for this node, before allowing to shut down the connection.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
*
|
|
* @return pdTRUE if the connection can be closed. Else, pdFALSE.
|
|
*/
|
|
BaseType_t xTCPWindowRxEmpty( const 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 )
|
|
|
|
/**
|
|
* @brief Remove the head item of a list (generic function).
|
|
*
|
|
* @param[in] pxList: The list of segment descriptors.
|
|
*
|
|
* @return The address of the segment descriptor, or NULL when not found.
|
|
*/
|
|
static TCPSegment_t * xTCPWindowGetHead( const 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 = ipCAST_PTR_TO_TYPE_PTR( TCPSegment_t, listGET_LIST_ITEM_OWNER( pxItem ) );
|
|
|
|
( void ) uxListRemove( pxItem );
|
|
}
|
|
|
|
return pxSegment;
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief Return the head item of a list (generic function).
|
|
*
|
|
* @param[in] pxList: The list of segment descriptors.
|
|
*
|
|
* @return The address of the segment descriptor, or NULL when the list is empty.
|
|
*/
|
|
static TCPSegment_t * xTCPWindowPeekHead( const List_t * pxList )
|
|
{
|
|
const 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 = ipCAST_PTR_TO_TYPE_PTR( TCPSegment_t, listGET_LIST_ITEM_OWNER( pxItem ) );
|
|
}
|
|
|
|
return pxReturn;
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief Release a segment object, return it to the list of available segment holders.
|
|
*
|
|
* @param[in] pxSegment: The segment descriptor that must be freed.
|
|
*/
|
|
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 )
|
|
{
|
|
( void ) uxListRemove( &( pxSegment->xQueueItem ) );
|
|
}
|
|
|
|
pxSegment->ulSequenceNumber = 0UL;
|
|
pxSegment->lDataLength = 0L;
|
|
pxSegment->u.ulFlags = 0UL;
|
|
|
|
/* Take it out of xRxSegments/xTxSegments */
|
|
if( listLIST_ITEM_CONTAINER( &( pxSegment->xSegmentItem ) ) != NULL )
|
|
{
|
|
( void ) uxListRemove( &( pxSegment->xSegmentItem ) );
|
|
}
|
|
|
|
/* Return it to xSegmentList */
|
|
vListInsertFifo( &xSegmentList, &( pxSegment->xSegmentItem ) );
|
|
}
|
|
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief Return all segment descriptor to the poll of descriptors, before deleting a socket.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
*/
|
|
void vTCPWindowDestroy( TCPWindow_t const * pxWindow )
|
|
{
|
|
const 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 ) )
|
|
{
|
|
while( listCURRENT_LIST_LENGTH( pxSegments ) > 0U )
|
|
{
|
|
pxSegment = ipCAST_PTR_TO_TYPE_PTR( TCPSegment_t, listGET_OWNER_OF_HEAD_ENTRY( pxSegments ) );
|
|
vTCPWindowFree( pxSegment );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Create a window for TCP.
|
|
*
|
|
* @param[in] pxWindow: The window to be created.
|
|
* @param[in] ulRxWindowLength: The length of the receive window.
|
|
* @param[in] ulTxWindowLength: The length of the transmit window.
|
|
* @param[in] ulAckNumber: The first ACK number.
|
|
* @param[in] ulSequenceNumber: The first sequence number.
|
|
* @param[in] ulMSS: The MSS of the connection.
|
|
*/
|
|
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 )
|
|
{
|
|
( void ) 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 );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Initialise a TCP window.
|
|
*
|
|
* @param[in] pxWindow: The window to be initialised.
|
|
* @param[in] ulAckNumber: The number of the first ACK.
|
|
* @param[in] ulSequenceNumber: The first sequence number.
|
|
* @param[in] ulMSS: The MSS of the connection.
|
|
*/
|
|
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 )
|
|
|
|
/**
|
|
* @brief Free the space occupied by the pool of segment descriptors, normally never used
|
|
*/
|
|
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 )
|
|
|
|
/**
|
|
* @brief A expected segment has been received, see if there is overlap with earlier segments.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulSequenceNumber: The sequence number of the segment that was received.
|
|
* @param[in] ulLength: The number of bytes that were received.
|
|
*
|
|
* @return The first segment descriptor involved, or NULL when no matching descriptor was found.
|
|
*/
|
|
static TCPSegment_t * xTCPWindowRxConfirm( const TCPWindow_t * pxWindow,
|
|
uint32_t ulSequenceNumber,
|
|
uint32_t ulLength )
|
|
{
|
|
TCPSegment_t * pxBest = NULL;
|
|
const ListItem_t * pxIterator;
|
|
uint32_t ulNextSequenceNumber = ulSequenceNumber + ulLength;
|
|
const ListItem_t * pxEnd = 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 = listGET_NEXT( pxEnd );
|
|
pxIterator != pxEnd;
|
|
pxIterator = listGET_NEXT( pxIterator ) )
|
|
{
|
|
pxSegment = ipCAST_PTR_TO_TYPE_PTR( 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_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 )
|
|
|
|
/**
|
|
* @brief Check what to do with a new incoming packet: store or ignore.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulSequenceNumber: The sequence number of the packet received.
|
|
* @param[in] ulLength: The number of bytes received.
|
|
* @param[in] ulSpace: The available space in the RX stream buffer.
|
|
*
|
|
* @return 0 or positive value indicating the offset at which the packet is to
|
|
* be stored, -1 if the packet is to be ignored.
|
|
*/
|
|
int32_t lTCPWindowRxCheck( TCPWindow_t * pxWindow,
|
|
uint32_t ulSequenceNumber,
|
|
uint32_t ulLength,
|
|
uint32_t ulSpace )
|
|
{
|
|
uint32_t ulCurrentSequenceNumber, ulLast, ulSavedSequenceNumber, ulIntermediateResult = 0;
|
|
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 ) ) != 0U )
|
|
{
|
|
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 != NULL );
|
|
|
|
/* Check for following segments that are already in the
|
|
* queue and increment ulCurrentSequenceNumber. */
|
|
for( ; ; )
|
|
{
|
|
pxFound = xTCPWindowRxFind( pxWindow, ulCurrentSequenceNumber );
|
|
|
|
if( pxFound == NULL )
|
|
{
|
|
break;
|
|
}
|
|
|
|
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;
|
|
|
|
ulIntermediateResult = ulLast - ulCurrentSequenceNumber;
|
|
/* The cast from unsigned long to signed long is on purpose. */
|
|
lDistance = ( int32_t ) ulIntermediateResult;
|
|
|
|
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
|
|
*/
|
|
for( ; ; )
|
|
{
|
|
pxFound = xTCPWindowRxFind( pxWindow, ulLast );
|
|
|
|
if( pxFound == NULL )
|
|
{
|
|
break;
|
|
}
|
|
|
|
ulLast += ( uint32_t ) pxFound->lDataLength;
|
|
}
|
|
|
|
if( xTCPWindowLoggingLevel >= 1 )
|
|
{
|
|
FreeRTOS_debug_printf( ( "lTCPWindowRxCheck[%d,%d]: seqnr %u exp %u (dist %d) SACK to %u\n",
|
|
( int ) pxWindow->usPeerPortNumber,
|
|
( int ) pxWindow->usOurPortNumber,
|
|
( unsigned ) ulSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
|
|
( unsigned ) ulCurrentSequenceNumber - pxWindow->rx.ulFirstSequenceNumber,
|
|
( unsigned ) ( ulSequenceNumber - ulCurrentSequenceNumber ), /* want this signed */
|
|
( unsigned ) ( 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 = ( uint8_t ) ( 3U * 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. */
|
|
ulIntermediateResult = ulSequenceNumber - ulCurrentSequenceNumber;
|
|
lReturn = ( int32_t ) ulIntermediateResult;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
|
|
#endif /* ipconfgiUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
/*=============================================================================
|
|
*
|
|
* ######### # #
|
|
* # # # # #
|
|
* # # #
|
|
* # ####
|
|
* # ##
|
|
* # ####
|
|
* # # #
|
|
* # # #
|
|
* ##### # #
|
|
*
|
|
* Tx functions
|
|
*
|
|
*=============================================================================*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief Increment the position in a circular buffer of size 'lMax'.
|
|
*
|
|
* @param[in] lPosition: The current index in the buffer.
|
|
* @param[in] lMax: The total number of items in this buffer.
|
|
* @param[in] lCount: The number of bytes that must be advanced.
|
|
*
|
|
* @return The new incremented position, or "( lPosition + lCount ) % lMax".
|
|
*/
|
|
static int32_t lTCPIncrementTxPosition( int32_t lPosition,
|
|
int32_t lMax,
|
|
int32_t lCount )
|
|
{
|
|
int32_t lReturn;
|
|
|
|
|
|
/* +TCP stores data in circular buffers. Calculate the next position to
|
|
* store. */
|
|
lReturn = lPosition + lCount;
|
|
|
|
if( lReturn >= lMax )
|
|
{
|
|
lReturn -= lMax;
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief Will add data to be transmitted to the front of the segment fifo.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulLength: The number of bytes that will be sent.
|
|
* @param[in] lPosition: The index in the TX stream buffer.
|
|
* @param[in] lMax: The size of the ( circular ) TX stream buffer.
|
|
*
|
|
* @return The number of bytes added to the sliding window for transmission.
|
|
*
|
|
*/
|
|
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;
|
|
int32_t lBufferIndex = lPosition;
|
|
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 ) )
|
|
{
|
|
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'. */
|
|
lBufferIndex = lTCPIncrementTxPosition( lBufferIndex, lMax, lToWrite );
|
|
}
|
|
}
|
|
}
|
|
|
|
while( lBytesLeft > 0 )
|
|
{
|
|
/* The current transmission segment is full, create new segments as
|
|
* needed. */
|
|
pxSegment = xTCPWindowTxNew( pxWindow, pxWindow->ulNextTxSequenceNumber, ( int32_t ) 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 = lBufferIndex;
|
|
lBytesLeft -= lToWrite;
|
|
lBufferIndex = lTCPIncrementTxPosition( lBufferIndex, 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 ) )
|
|
{
|
|
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 )
|
|
|
|
/**
|
|
* @brief Returns true if there are no more outstanding TX segments.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
*
|
|
* @return pdTRUE if there are no more outstanding Tx segments, else pdFALSE.
|
|
*/
|
|
BaseType_t xTCPWindowTxDone( const TCPWindow_t * pxWindow )
|
|
{
|
|
return listLIST_IS_EMPTY( ( &pxWindow->xTxSegments ) );
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief Find out if the peer is able to receive more data.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulWindowSize: The number of bytes in this segment.
|
|
*
|
|
* @return True if the peer has space in it window to receive more data.
|
|
*/
|
|
static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t const * pxWindow,
|
|
uint32_t ulWindowSize )
|
|
{
|
|
uint32_t ulTxOutstanding;
|
|
BaseType_t xHasSpace;
|
|
const TCPSegment_t * pxSegment;
|
|
uint32_t ulNettSize;
|
|
|
|
/* 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. */
|
|
ulNettSize = ulWindowSize - FreeRTOS_min_uint32( ulWindowSize, ulTxOutstanding );
|
|
|
|
/* See if the next segment may be sent. */
|
|
if( ulNettSize >= ( 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 )
|
|
|
|
/**
|
|
* @brief Returns true if there is TX data that can be sent right now.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulWindowSize: The current size of the sliding RX window of the peer.
|
|
* @param[out] pulDelay: The delay before the packet may be sent.
|
|
*
|
|
* @return pdTRUE if there is Tx data that can be sent, else pdFALSE.
|
|
*/
|
|
BaseType_t xTCPWindowTxHasData( TCPWindow_t const * pxWindow,
|
|
uint32_t ulWindowSize,
|
|
TickType_t * pulDelay )
|
|
{
|
|
TCPSegment_t const * 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 = ( 1UL << 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 )
|
|
|
|
/**
|
|
* @brief Get data that can be transmitted right now.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulWindowSize: The current size of the sliding RX window of the peer.
|
|
* @param[out] plPosition: The index within the TX stream buffer of the first byte to be sent.
|
|
*
|
|
* @return The amount of data in bytes that can be transmitted right now.
|
|
*/
|
|
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 = ( 1UL << 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 = ( uint8_t ) pdFALSE_UNSIGNED;
|
|
|
|
/* Some detailed logging. */
|
|
if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) ) )
|
|
{
|
|
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 ) ) )
|
|
{
|
|
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 )
|
|
{
|
|
/* pxSegment is not NULL when ulReturn != 0UL. */
|
|
configASSERT( pxSegment != NULL );
|
|
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 * ( ( uint32_t ) pxWindow->usMSS ) ) )
|
|
{
|
|
FreeRTOS_debug_printf( ( "ulTCPWindowTxGet[%u - %d]: Change Tx window: %lu -> %u\n",
|
|
pxWindow->usPeerPortNumber,
|
|
pxWindow->usOurPortNumber,
|
|
pxWindow->xSize.ulTxWindowLength,
|
|
2U * 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 )
|
|
|
|
/**
|
|
* @brief 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.
|
|
*
|
|
* @param[in] pxWindow: The TCP-window object of the current connection.
|
|
* @param[in] ulFirst: The sequence number of the first byte that was acknowledged.
|
|
* @param[in] ulLast: The sequence number of the last byte ( minus one ) that was acknowledged.
|
|
*
|
|
* @return number of bytes that the tail of txStream may be advanced.
|
|
*/
|
|
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 ListItem_t * pxEnd = 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. */
|
|
|
|
pxIterator = listGET_NEXT( pxEnd );
|
|
|
|
while( ( pxIterator != pxEnd ) && ( xSequenceLessThan( ulSequenceNumber, ulLast ) != 0 ) )
|
|
{
|
|
xDoUnlink = pdFALSE;
|
|
pxSegment = ipCAST_PTR_TO_TYPE_PTR( 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 )
|
|
{
|
|
/* coverity[break_stmt] : Break statement terminating the loop */
|
|
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;
|
|
|
|
/* 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 == 1U ) && ( ( 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 ) ) )
|
|
{
|
|
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. */
|
|
( void ) uxListRemove( &pxSegment->xQueueItem );
|
|
}
|
|
|
|
ulSequenceNumber += ulDataLength;
|
|
}
|
|
|
|
return ulBytesConfirmed;
|
|
}
|
|
|
|
#endif /* ipconfigUSE_TCP_WIN == 1 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#if ( ipconfigUSE_TCP_WIN == 1 )
|
|
|
|
/**
|
|
* @brief See if there are segments that need a fast retransmission.
|
|
*
|
|
* @param[in] pxWindow: The descriptor of the TCP sliding windows.
|
|
* @param[in] ulFirst: The sequence number of the first segment that must be checked.
|
|
*
|
|
* @return The number of segments that need a fast retransmission.
|
|
*/
|
|
static uint32_t prvTCPWindowFastRetransmit( TCPWindow_t * pxWindow,
|
|
uint32_t ulFirst )
|
|
{
|
|
const ListItem_t * pxIterator;
|
|
const ListItem_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 = listGET_END_MARKER( &( pxWindow->xWaitQueue ) );
|
|
|
|
pxIterator = listGET_NEXT( pxEnd );
|
|
|
|
while( pxIterator != pxEnd )
|
|
{
|
|
/* Get the owner, which is a TCP segment. */
|
|
pxSegment = ipCAST_PTR_TO_TYPE_PTR( TCPSegment_t, listGET_LIST_ITEM_OWNER( pxIterator ) );
|
|
|
|
/* Hop to the next item before the current gets unlinked. */
|
|
pxIterator = 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 )
|
|
{
|
|
if( xSequenceLessThan( pxSegment->ulSequenceNumber, ulFirst ) != pdFALSE )
|
|
{
|
|
pxSegment->u.bits.ucDupAckCount++;
|
|
|
|
if( pxSegment->u.bits.ucDupAckCount == DUPLICATE_ACKS_BEFORE_FAST_RETRANSMIT )
|
|
{
|
|
pxSegment->u.bits.ucTransmitCount = ( uint8_t ) pdFALSE;
|
|
|
|
/* 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 ) ) )
|
|
{
|
|
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. */
|
|
( void ) 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 )
|
|
|
|
/**
|
|
* @brief Receive a normal ACK.
|
|
*
|
|
* @param[in] pxWindow: Window in which a data is receive.
|
|
* @param[in] ulSequenceNumber: The sequence number of the ACK.
|
|
*
|
|
* @return The location where the packet should be added.
|
|
*/
|
|
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 )
|
|
|
|
/**
|
|
* @brief Receive a SACK option.
|
|
*
|
|
* @param[in] pxWindow: Window in which the data is received.
|
|
* @param[in] ulFirst: Index of starting position of options.
|
|
* @param[in] ulLast: Index of end position of the options.
|
|
*
|
|
* @return returns the number of bytes which have been acked starting from
|
|
* the head position.
|
|
*/
|
|
uint32_t ulTCPWindowTxSack( TCPWindow_t * pxWindow,
|
|
uint32_t ulFirst,
|
|
uint32_t ulLast )
|
|
{
|
|
uint32_t ulAckCount;
|
|
uint32_t ulCurrentSequenceNumber = pxWindow->tx.ulCurrentSequenceNumber;
|
|
|
|
/* Receive a SACK option. */
|
|
ulAckCount = prvTCPWindowTxCheckAck( pxWindow, ulFirst, ulLast );
|
|
( void ) 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 )
|
|
|
|
/**
|
|
* @brief Data was received at 'ulSequenceNumber'. See if it was expected
|
|
* and if there is enough space to store the new data.
|
|
*
|
|
* @param[in] pxWindow: The window to be checked.
|
|
* @param[in] ulSequenceNumber: Sequence number of the data received.
|
|
* @param[in] ulLength: Length of the data received.
|
|
* @param[in] ulSpace: Space in the buffer.
|
|
*
|
|
* @return A 0 is returned if there is enough space and the sequence number is correct,
|
|
* if not then a -1 is returned.
|
|
*
|
|
* @note if true may be passed directly to user (segment expected and window is empty).
|
|
* But pxWindow->ackno should always be used to set "BUF->ackno".
|
|
*/
|
|
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 )
|
|
|
|
/**
|
|
* @brief Add data to the Tx Window.
|
|
*
|
|
* @param[in] pxWindow: The window to which the data is to be added.
|
|
* @param[in] ulLength: The length of the data to be added.
|
|
* @param[in] lPosition: Position in the stream.
|
|
* @param[in] lMax: Size of the Tx stream.
|
|
*
|
|
* @return The data actually added.
|
|
*/
|
|
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 )
|
|
|
|
/**
|
|
* @brief Fetches data to be sent.
|
|
*
|
|
* @param[in] pxWindow: The window for the connection.
|
|
* @param[in] ulWindowSize: The size of the window.
|
|
* @param[out] plPosition: plPosition will point to a location with the circular data buffer: txStream.
|
|
*
|
|
* @return return the amount of data which may be sent along with the position in the txStream.
|
|
*/
|
|
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 )
|
|
|
|
/**
|
|
* @brief Has the transmission completed.
|
|
*
|
|
* @param[in] pxWindow: The window whose transmission window is to be checked.
|
|
*
|
|
* @return If there is no outstanding data then pdTRUE is returned,
|
|
* else pdFALSE.
|
|
*/
|
|
BaseType_t xTCPWindowTxDone( const 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 const * pxWindow,
|
|
uint32_t ulWindowSize );
|
|
|
|
/**
|
|
* @brief Check if the window has space for one message.
|
|
*
|
|
* @param[in] pxWindow: The window to be checked.
|
|
* @param[in] ulWindowSize: Size of the window.
|
|
*
|
|
* @return pdTRUE if the window has space, pdFALSE otherwise.
|
|
*/
|
|
static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t const * 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 )
|
|
|
|
/**
|
|
* @brief Check data to be sent and calculate the time period the process may sleep.
|
|
*
|
|
* @param[in] pxWindow: The window to be checked.
|
|
* @param[in] ulWindowSize: Size of the window.
|
|
* @param[out] pulDelay: The time period (in ticks) that the process may sleep.
|
|
*
|
|
* @return pdTRUE if the process should sleep or pdFALSE.
|
|
*/
|
|
BaseType_t xTCPWindowTxHasData( TCPWindow_t const * pxWindow,
|
|
uint32_t ulWindowSize,
|
|
TickType_t * pulDelay )
|
|
{
|
|
TCPSegment_t const * 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 )
|
|
|
|
/**
|
|
* @brief Receive a normal ACK.
|
|
*
|
|
* @param[in] pxWindow: The window for this particular connection.
|
|
* @param[in] ulSequenceNumber: The sequence number of the packet.
|
|
*
|
|
* @return Number of bytes to send.
|
|
*/
|
|
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 )
|
|
|
|
/**
|
|
* @brief This function will be called as soon as a FIN is received to check
|
|
* whether all transmit queues are empty or not.
|
|
*
|
|
* @param[in] pxWindow: The window to be checked.
|
|
*
|
|
* @return It will return true if there are no 'open' reception segments.
|
|
*/
|
|
BaseType_t xTCPWindowRxEmpty( const 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 )
|
|
|
|
/**
|
|
* @brief Destroy a window.
|
|
*
|
|
* @param[in] pxWindow: Pointer to the window to be destroyed.
|
|
*
|
|
* @return Always returns a NULL.
|
|
*/
|
|
void vTCPWindowDestroy( const 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 */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
#endif /* ipconfigUSE_TCP == 1 */
|