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