| /* |
| * 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 */ |
| /*-----------------------------------------------------------*/ |
| |
| |