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