/*
 * FreeRTOS+TCP V3.1.0
 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * 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-PLUS-TCP
 */

#ifndef FREERTOS_TCP_WIN_H
#define FREERTOS_TCP_WIN_H

/* *INDENT-OFF* */
#ifdef __cplusplus
    extern "C" {
#endif
/* *INDENT-ON* */

/** @brief A very simple timer that registers the time that a packet was sent.  It is used to trigger re-sending. */
typedef struct xTCPTimerStruct
{
    TickType_t uxBorn; /**< The time at which a packet was sent ( using xTaskGetTickCount() ). */
} TCPTimer_t;

/** @brief This struct collects the properties of a TCP segment.  A segment is a chunk of data which
 *         is sent in a single TCP packet, at most 1460 bytes. */
typedef struct xTCP_SEGMENT
{
    uint32_t ulSequenceNumber; /**< The sequence number of the first byte in this packet */
    int32_t lMaxLength;        /**< Maximum space, number of bytes which can be stored in this segment */
    int32_t lDataLength;       /**< Actual number of bytes */
    int32_t lStreamPos;        /**< reference to the [t|r]xStream of the socket */
    TCPTimer_t xTransmitTimer; /**< saves a timestamp at the moment this segment gets transmitted (TX only) */
    union
    {
        struct
        {
            uint32_t
                ucTransmitCount : 8, /**< Number of times the segment has been transmitted, used to calculate the RTT */
                ucDupAckCount : 8,   /**< Counts the number of times that a higher segment was ACK'd. After 3 times a Fast Retransmission takes place */
                bOutstanding : 1,    /**< It the peer's turn, we're just waiting for an ACK */
                bAcked : 1,          /**< This segment has been acknowledged */
                bIsForRx : 1;        /**< pdTRUE if segment is used for reception */
        } bits;
        uint32_t ulFlags;
    } u;                                /**< A collection of boolean flags. */
    #if ( ipconfigUSE_TCP_WIN != 0 )
        struct xLIST_ITEM xQueueItem;   /**< TX only: segments can be linked in one of three queues: xPriorityQueue, xTxQueue, and xWaitQueue */
        struct xLIST_ITEM xSegmentItem; /**< With this item the segment can be connected to a list, depending on who is owning it */
    #endif
} TCPSegment_t;

/** @brief This struct describes the windows sizes, both for incoming and outgoing. */
typedef struct xTCP_WINSIZE
{
    uint32_t ulRxWindowLength; /**< The TCP window size of the incoming stream. */
    uint32_t ulTxWindowLength; /**< The TCP window size of the outgoing stream. */
} TCPWinSize_t;

/** @brief If TCP time-stamps are being used, they will occupy 12 bytes in
 * each packet, and thus the message space will become smaller.
 * Keep this as a multiple of 4 */
#if ( ipconfigUSE_TCP_WIN == 1 )
    #define ipSIZE_TCP_OPTIONS    16U
#else
    #define ipSIZE_TCP_OPTIONS    12U
#endif

/** @brief Every TCP connection owns a TCP window for the administration of all packets
 *  It owns two sets of segment descriptors, incoming and outgoing
 */
typedef struct xTCP_WINDOW
{
    union
    {
        struct
        {
            uint32_t
                bHasInit : 1,      /**< The window structure has been initialised */
                bSendFullSize : 1, /**< May only send packets with a size equal to MSS (for optimisation) */
                bTimeStamps : 1;   /**< Socket is supposed to use TCP time-stamps. This depends on the */
        } bits;                    /**< party which opens the connection */
        uint32_t ulFlags;
    } u;                           /**< A collection of boolean flags. */
    TCPWinSize_t xSize;            /**< The TCP window sizes of the incoming and outgoing streams. */
    struct
    {
        uint32_t ulFirstSequenceNumber;                                    /**< Logging & debug: the first segment received/sent in this connection
                                                                            * for Tx: initial send sequence number (ISS)
                                                                            * for Rx: initial receive sequence number (IRS) */
        uint32_t ulCurrentSequenceNumber;                                  /**< Tx/Rx: the oldest sequence number not yet confirmed, also SND.UNA / RCV.NXT
                                                                            * In other words: the sequence number of the left side of the sliding window */
        uint32_t ulFINSequenceNumber;                                      /**< The sequence number which carried the FIN flag */
        uint32_t ulHighestSequenceNumber;                                  /**< Sequence number of the right-most byte + 1 */
    } rx,                                                                  /**< Sequence number of the incoming data stream. */
      tx;                                                                  /**< Sequence number of the outgoing data stream. */
    uint32_t ulOurSequenceNumber;                                          /**< The SEQ number we're sending out */
    uint32_t ulUserDataLength;                                             /**< Number of bytes in Rx buffer which may be passed to the user, after having received a 'missing packet' */
    uint32_t ulNextTxSequenceNumber;                                       /**< The sequence number given to the next byte to be added for transmission */
    int32_t lSRTT;                                                         /**< Smoothed Round Trip Time, it may increment quickly and it decrements slower */
    uint8_t ucOptionLength;                                                /**< Number of valid bytes in ulOptionsData[] */
    #if ( ipconfigUSE_TCP_WIN == 1 )
        List_t xPriorityQueue;                                             /**< Priority queue: segments which must be sent immediately */
        List_t xTxQueue;                                                   /**< Transmit queue: segments queued for transmission */
        List_t xWaitQueue;                                                 /**< Waiting queue:  outstanding segments */
        TCPSegment_t * pxHeadSegment;                                      /**< points to a segment which has not been transmitted and it's size is still growing (user data being added) */
        uint32_t ulOptionsData[ ipSIZE_TCP_OPTIONS / sizeof( uint32_t ) ]; /**< Contains the options we send out */
        List_t xTxSegments;                                                /**< A linked list of all transmission segments, sorted on sequence number */
        List_t xRxSegments;                                                /**< A linked list of reception segments, order depends on sequence of arrival */
    #else
        /* For tiny TCP, there is only 1 outstanding TX segment */
        TCPSegment_t xTxSegment; /**< Priority queue */
    #endif
    uint16_t usOurPortNumber;    /**< Mostly for debugging/logging: our TCP port number */
    uint16_t usPeerPortNumber;   /**< debugging/logging: the peer's TCP port number */
    uint16_t usMSS;              /**< Current accepted MSS */
    uint16_t usMSSInit;          /**< MSS as configured by the socket owner */
} TCPWindow_t;


/*=============================================================================
 *
 * Creation and destruction
 *
 *=============================================================================*/

/* Create and initialize a window */
void vTCPWindowCreate( TCPWindow_t * pxWindow,
                       uint32_t ulRxWindowLength,
                       uint32_t ulTxWindowLength,
                       uint32_t ulAckNumber,
                       uint32_t ulSequenceNumber,
                       uint32_t ulMSS );

/* Destroy a window (always returns NULL)
 * It will free some resources: a collection of segments */
void vTCPWindowDestroy( TCPWindow_t const * pxWindow );

/* Initialize a window */
void vTCPWindowInit( TCPWindow_t * pxWindow,
                     uint32_t ulAckNumber,
                     uint32_t ulSequenceNumber,
                     uint32_t ulMSS );

/* Clean up allocated segments. Should only be called when FreeRTOS+TCP will no longer be used. */
void vTCPSegmentCleanup( void );

/*=============================================================================
 *
 * Rx functions
 *
 *=============================================================================*/

/* if true may be passed directly to user (segment expected and window is empty)
 * But pxWindow->ackno should always be used to set "BUF->ackno" */
int32_t lTCPWindowRxCheck( TCPWindow_t * pxWindow,
                           uint32_t ulSequenceNumber,
                           uint32_t ulLength,
                           uint32_t ulSpace,
                           uint32_t * pulSkipCount );

/* This function will be called as soon as a FIN is received. It will return true
 * if there are no 'open' reception segments */
BaseType_t xTCPWindowRxEmpty( const TCPWindow_t * pxWindow );

/*=============================================================================
 *
 * Tx functions
 *
 *=============================================================================*/

/* Adds data to the Tx-window */
int32_t lTCPWindowTxAdd( TCPWindow_t * pxWindow,
                         uint32_t ulLength,
                         int32_t lPosition,
                         int32_t lMax );

/* Check data to be sent and calculate the time period we may sleep */
BaseType_t xTCPWindowTxHasData( TCPWindow_t const * pxWindow,
                                uint32_t ulWindowSize,
                                TickType_t * pulDelay );

/* See if anything is left to be sent
 * Function will be called when a FIN has been received. Only when the TX window is clean,
 * it will return pdTRUE */
BaseType_t xTCPWindowTxDone( const TCPWindow_t * pxWindow );

/* Fetches data to be sent.
 * 'plPosition' will point to a location with the circular data buffer: txStream */
uint32_t ulTCPWindowTxGet( TCPWindow_t * pxWindow,
                           uint32_t ulWindowSize,
                           int32_t * plPosition );

/* Receive a normal ACK */
uint32_t ulTCPWindowTxAck( TCPWindow_t * pxWindow,
                           uint32_t ulSequenceNumber );

/* Receive a SACK option */
uint32_t ulTCPWindowTxSack( TCPWindow_t * pxWindow,
                            uint32_t ulFirst,
                            uint32_t ulLast );

/**
 * @brief Check if a > b, where a and b are rolling counters.
 *
 * @param[in] a: The value on the left-hand side.
 * @param[in] b: The value on the right-hand side.
 *
 * @return pdTRUE if a > b, otherwise pdFALSE.
 *
 * @note GreaterThan is calculated as "( a - ( b + 1U ) ) < 0x80000000".
 */
BaseType_t xSequenceGreaterThan( uint32_t a,
                                 uint32_t b );

/**
 * @brief Check if a < b, where a and b are rolling counters.
 *
 * @param[in] a: The value on the left-hand side.
 * @param[in] b: The value on the right-hand side.
 *
 * @return pdTRUE if a < b, otherwise pdFALSE.
 *
 * @note LessThan is implemented as "( b - ( a + 1 ) ) < 0x80000000".
 */
BaseType_t xSequenceLessThan( uint32_t a,
                              uint32_t b );

/* *INDENT-OFF* */
#ifdef __cplusplus
    } /* extern "C" */
#endif
/* *INDENT-ON* */

#endif /* FREERTOS_TCP_WIN_H */
