/*
 * FreeRTOS STM32 Reference Integration
 * Copyright (C) 2021 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://www.FreeRTOS.org
 * http://aws.amazon.com/freertos
 */

#include "logging_levels.h"
#define LOG_LEVEL    LOG_WARN

#include "logging.h"

#include "hw_defs.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "event_groups.h"
#include "stdbool.h"
#include "stm32u5xx_hal.h"
#include "message_buffer.h"
#include "atomic.h"

#include "mx_ipc.h"
#include "mx_prv.h"

#define EVT_SPI_DONE        0x8
#define EVT_SPI_ERROR       0x10

#define EVT_SPI_FLOW        0x2

#define SPI_EVT_DMA_IDX     1
#define SPI_EVT_FLOW_IDX    2

static MxDataplaneCtx_t * volatile pxSpiCtx = NULL;

uint32_t prvGetNextRequestID( void )
{
    uint32_t ulRequestId = 0;

    if( pxSpiCtx != NULL )
    {
        ulRequestId = Atomic_Increment_u32( &( pxSpiCtx->ulLastRequestId ) );

        /* Avoid ulRequestId == 0 */
        if( ulRequestId == 0 )
        {
            ulRequestId = Atomic_Increment_u32( &( pxSpiCtx->ulLastRequestId ) );
        }
    }

    return ulRequestId;
}


/* Callback functions */
static void spi_transfer_done_callback( SPI_HandleTypeDef * hspi )
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    BaseType_t rslt = pdFALSE;

    if( pxSpiCtx != NULL )
    {
        rslt = xTaskNotifyIndexedFromISR( pxSpiCtx->xDataPlaneTaskHandle,
                                          SPI_EVT_DMA_IDX,
                                          EVT_SPI_DONE,
                                          eSetBits,
                                          &xHigherPriorityTaskWoken );
        configASSERT( rslt == pdTRUE );

        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    }
}

static void spi_transfer_error_callback( SPI_HandleTypeDef * hspi )
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    BaseType_t rslt = pdFALSE;

    if( pxSpiCtx != NULL )
    {
        rslt = xTaskNotifyIndexedFromISR( pxSpiCtx->xDataPlaneTaskHandle,
                                          SPI_EVT_DMA_IDX,
                                          EVT_SPI_ERROR,
                                          eSetBits,
                                          &xHigherPriorityTaskWoken );
        configASSERT( rslt == pdTRUE );

        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    }
}

/* Notify / IRQ pin transition means data is ready */
static void spi_notify_callback( void * pvContext )
{
    MxDataplaneCtx_t * pxCtx = ( MxDataplaneCtx_t * ) pvContext;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    if( pxSpiCtx != NULL )
    {
        vTaskNotifyGiveIndexedFromISR( pxCtx->xDataPlaneTaskHandle,
                                       DATA_WAITING_IDX,
                                       &xHigherPriorityTaskWoken );

        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    }
}

static void spi_flow_callback( void * pvContext )
{
    MxDataplaneCtx_t * pxCtx = ( MxDataplaneCtx_t * ) pvContext;

    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    if( pxSpiCtx != NULL )
    {
        vTaskNotifyGiveIndexedFromISR( pxCtx->xDataPlaneTaskHandle,
                                       SPI_EVT_FLOW_IDX,
                                       &xHigherPriorityTaskWoken );

        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    }
}

/* Handle GPIO operations */
static inline void vGpioClear( const IotMappedPin_t * gpio )
{
    HAL_GPIO_WritePin( gpio->xPort, gpio->xPinMask, GPIO_PIN_RESET );
}

static inline void vGpioSet( const IotMappedPin_t * gpio )
{
    HAL_GPIO_WritePin( gpio->xPort, gpio->xPinMask, GPIO_PIN_SET );
}

static inline BaseType_t xGpioGet( const IotMappedPin_t * gpio )
{
    return ( BaseType_t ) HAL_GPIO_ReadPin( gpio->xPort, gpio->xPinMask );
}

static inline void vDoHardReset( MxDataplaneCtx_t * pxCtx )
{
    vGpioClear( pxCtx->gpio_reset );
    vTaskDelay( 100 );

    vGpioSet( pxCtx->gpio_reset );
    vTaskDelay( 2000 );
}

/* SPI protocol definitions */
#define MX_SPI_WRITE    ( 0x0A )
#define MX_SPI_READ     ( 0x0B )

static inline BaseType_t xWaitForSPIEvent( TickType_t xTimeout )
{
    BaseType_t xWaitResult = pdFALSE;
    BaseType_t xReturnValue = pdFALSE;
    uint32_t ulNotifiedValue = 0;

    LogDebug( "Starting wait for SPI DMA event, Timeout=%d", xTimeout );

    xWaitResult = xTaskNotifyWaitIndexed( SPI_EVT_DMA_IDX, 0, 0xFFFFFFFF, &ulNotifiedValue, xTimeout );

    if( xWaitResult == pdTRUE )
    {
        if( ulNotifiedValue & EVT_SPI_DONE )
        {
            LogDebug( "SPI done event received." );
            xReturnValue = pdTRUE;
        }

        if( ulNotifiedValue & EVT_SPI_ERROR )
        {
            LogError( "SPI error event received." );
            xReturnValue = pdFALSE;
        }

        if( ( ulNotifiedValue & ( EVT_SPI_ERROR | EVT_SPI_DONE ) ) == 0 )
        {
            LogError( "Timeout while waiting for SPI event." );
            xReturnValue = pdFALSE;
        }
    }

    return xReturnValue;
}

/*
 * @brief Exchange SPIHeader_t headers with the wifi module.
 * */
static inline BaseType_t xDoSpiHeaderTransfer( MxDataplaneCtx_t * pxCtx,
                                               uint16_t * psTxLen,
                                               uint16_t * psRxLen )
{
    HAL_StatusTypeDef xHalStatus = HAL_ERROR;

    SPIHeader_t xRxHeader = { 0 };
    SPIHeader_t xTxHeader = { 0 };

    xTxHeader.type = MX_SPI_WRITE;
    xTxHeader.len = *psTxLen;
    xTxHeader.lenx = ~( xTxHeader.len );

    ( void ) xTaskNotifyStateClearIndexed( NULL, SPI_EVT_DMA_IDX );

    xHalStatus = HAL_SPI_TransmitReceive_DMA( pxCtx->pxSpiHandle,
                                              ( uint8_t * ) &xTxHeader,
                                              ( uint8_t * ) &xRxHeader,
                                              sizeof( SPIHeader_t ) );

    /* Exchange headers with the module */
    if( xHalStatus != HAL_OK )
    {
        LogError( "Error returned by HAL_SPI_TransmitReceive_DMA call during transfer. xHalStatus=%d", xHalStatus );
    }
    else
    {
        xHalStatus = ( xWaitForSPIEvent( MX_SPI_EVENT_TIMEOUT ) == pdTRUE ) ? HAL_OK : HAL_ERROR;
    }

    if( ( xHalStatus == HAL_OK ) &&
        ( xRxHeader.len < MX_MAX_MESSAGE_LEN ) &&
        ( xRxHeader.type == MX_SPI_READ ) &&
        ( ( ( xRxHeader.len ) ^ ( xRxHeader.lenx ) ) == 0xFFFF ) )
    {
        *psRxLen = xRxHeader.len;
    }
    else
    {
        if( ( xRxHeader.type == MX_SPI_READ ) &&
            ( xRxHeader.len != 0 ) )
        {
            LogError( "RX header validation failed. len: %d, lenx: %d, xord: %d, type: %d, xHalStatus: %d",
                      xRxHeader.len, xRxHeader.lenx, ( xRxHeader.len ) ^ ( xRxHeader.lenx ), xRxHeader.type, xHalStatus );
        }

        *psRxLen = 0;
        *psTxLen = 0;
    }

    return( xHalStatus == HAL_OK );
}

static inline BaseType_t xReceiveMessage( MxDataplaneCtx_t * pxCtx,
                                          uint8_t * pucRxBuffer,
                                          uint32_t ulRxDataLen )
{
    HAL_StatusTypeDef xHalStatus;

    configASSERT( pxCtx != NULL );
    configASSERT( pucRxBuffer != NULL );
    configASSERT( ulRxDataLen > 0 );

    ( void ) xTaskNotifyStateClearIndexed( NULL, SPI_EVT_DMA_IDX );

    xHalStatus = HAL_SPI_Receive_DMA( pxCtx->pxSpiHandle,
                                      pucRxBuffer,
                                      ulRxDataLen );

    xHalStatus |= ( xWaitForSPIEvent( MX_SPI_EVENT_TIMEOUT ) == pdTRUE ) ? HAL_OK : HAL_ERROR;

    return xHalStatus == HAL_OK;
}

static inline BaseType_t xTransmitMessage( MxDataplaneCtx_t * pxCtx,
                                           uint8_t * pucTxBuffer,
                                           uint32_t usTxDataLen )
{
    HAL_StatusTypeDef xHalStatus;

    configASSERT( pxCtx != NULL );
    configASSERT( pucTxBuffer != NULL );
    configASSERT( usTxDataLen > 0 );


    ( void ) xTaskNotifyStateClearIndexed( NULL, SPI_EVT_DMA_IDX );

    xHalStatus = HAL_SPI_Transmit_DMA( pxCtx->pxSpiHandle,
                                       pucTxBuffer,
                                       usTxDataLen );

    xHalStatus |= ( xWaitForSPIEvent( MX_SPI_EVENT_TIMEOUT ) == pdTRUE ) ? HAL_OK : HAL_ERROR;

    return xHalStatus == HAL_OK;
}

static inline BaseType_t xTransmitReceiveMessage( MxDataplaneCtx_t * pxCtx,
                                                  uint8_t * pucTxBuffer,
                                                  uint32_t usTxDataLen,
                                                  uint8_t * pucRxBuffer,
                                                  uint32_t usRxDataLen )
{
    HAL_StatusTypeDef xHalStatus;

    configASSERT( pxCtx != NULL );
    configASSERT( pucTxBuffer != NULL );
    configASSERT( usTxDataLen > 0 );
    configASSERT( pucRxBuffer != NULL );
    configASSERT( usRxDataLen > 0 );

    /* Split into two dma transactions */
    if( usTxDataLen > usRxDataLen )
    {
        ( void ) xTaskNotifyStateClearIndexed( NULL, SPI_EVT_DMA_IDX );

        xHalStatus = HAL_SPI_TransmitReceive_DMA( pxCtx->pxSpiHandle,
                                                  pucTxBuffer,
                                                  pucRxBuffer,
                                                  usRxDataLen );

        xHalStatus |= ( xWaitForSPIEvent( MX_SPI_EVENT_TIMEOUT ) == pdTRUE ) ? HAL_OK : HAL_ERROR;

        xHalStatus |= xTransmitMessage( pxCtx,
                                        &pucTxBuffer[ usRxDataLen ],
                                        usTxDataLen - usRxDataLen );
    }
    else if( usTxDataLen < usRxDataLen )
    {
        ( void ) xTaskNotifyStateClearIndexed( NULL, SPI_EVT_DMA_IDX );

        xHalStatus = HAL_SPI_TransmitReceive_DMA( pxCtx->pxSpiHandle,
                                                  pucTxBuffer,
                                                  pucRxBuffer,
                                                  usTxDataLen );

        xHalStatus |= ( xWaitForSPIEvent( MX_SPI_EVENT_TIMEOUT ) == pdTRUE ) ? HAL_OK : HAL_ERROR;

        xHalStatus |= xReceiveMessage( pxCtx,
                                       &pucRxBuffer[ usTxDataLen ],
                                       usRxDataLen - usTxDataLen );
    }
    else /* usTxDataLen == usRxDataLen */
    {
        xHalStatus = HAL_SPI_TransmitReceive_DMA( pxCtx->pxSpiHandle,
                                                  pucTxBuffer,
                                                  pucRxBuffer,
                                                  usTxDataLen );

        xHalStatus |= ( xWaitForSPIEvent( MX_SPI_EVENT_TIMEOUT ) == pdTRUE ) ? HAL_OK : HAL_ERROR;
    }

    return xHalStatus == HAL_OK;
}


static void vProcessRxPacket( MessageBufferHandle_t * xControlPlaneResponseBuff,
                              NetInterface_t * pxNetif,
                              PacketBuffer_t ** ppxRxPacket )
{
    BaseType_t xResult = pdFALSE;

    /* Read header */
    IPCHeader_t * pxRxPktHeader = ( IPCHeader_t * ) ( *ppxRxPacket )->payload;

    /* Fast path: bypass in packets */
    if( ( pxRxPktHeader->usIPCApiId == IPC_WIFI_EVT_BYPASS_IN ) &&
        ( ( *ppxRxPacket )->len > sizeof( BypassInOut_t ) ) )
    {
        /* adjust header */
        pbuf_remove_header( ( *ppxRxPacket ), sizeof( BypassInOut_t ) );

        /* Enqueue */
        if( prvxLinkInput( pxNetif, *ppxRxPacket ) != pdTRUE )
        {
            /* Free packet on failure */
            PBUF_FREE( *ppxRxPacket );
        }

        /* Clear pointer */
        ( *ppxRxPacket ) = NULL;
    }
    /* Drop bypass out ACK packets */
    else if( pxRxPktHeader->usIPCApiId == IPC_WIFI_BYPASS_OUT )
    {
        IPCResponsBypassOut_t * pxBypassOutResponse = ( *ppxRxPacket )->payload + ( ptrdiff_t ) sizeof( IPCHeader_t );

        if( pxBypassOutResponse->status == IPC_SUCCESS )
        {
            LogDebug( "Dropping bypass response for request id: %d.", pxRxPktHeader->ulIPCRequestId );
        }
        else
        {
            LogError( "IPCError %d reported in bypass response for request ID: %d",
                      pxBypassOutResponse->status,
                      pxRxPktHeader->ulIPCRequestId );
        }

        PBUF_FREE( *ppxRxPacket );
        ( *ppxRxPacket ) = NULL;
        pxRxPktHeader = NULL;
    }
    /* forward to control plane handler */
    else
    {
        xResult = xMessageBufferSend( xControlPlaneResponseBuff,
                                      ppxRxPacket,
                                      sizeof( PacketBuffer_t * ),
                                      0 );

        if( xResult == pdFALSE )
        {
            LogError( "Error while adding received message to xControlPlaneResponseBuff: %d.", xResult );

            LogError( "Dropping response message: Request ID: %d, AppID: %d",
                      pxRxPktHeader->ulIPCRequestId,
                      pxRxPktHeader->usIPCApiId );
            PBUF_FREE( *ppxRxPacket );
            pxRxPktHeader = NULL;
        }

        /* Clear pointer */
        ( *ppxRxPacket ) = NULL;
    }
}


void vInitCallbacks( MxDataplaneCtx_t * pxCtx )
{
    HAL_StatusTypeDef xHalResult = HAL_ERROR;

    /* Register SPI and GPIO callback functions */
    GPIO_EXTI_Register_Callback( pxCtx->gpio_notify->xPinMask,
                                 spi_notify_callback,
                                 pxCtx );

    GPIO_EXTI_Register_Callback( pxCtx->gpio_flow->xPinMask,
                                 spi_flow_callback,
                                 pxCtx );



    xHalResult = HAL_SPI_RegisterCallback( pxCtx->pxSpiHandle,
                                           HAL_SPI_TX_COMPLETE_CB_ID,
                                           spi_transfer_done_callback );

    configASSERT( xHalResult == HAL_OK );

    xHalResult = HAL_SPI_RegisterCallback( pxCtx->pxSpiHandle,
                                           HAL_SPI_RX_COMPLETE_CB_ID,
                                           spi_transfer_done_callback );

    configASSERT( xHalResult == HAL_OK );

    xHalResult = HAL_SPI_RegisterCallback( pxCtx->pxSpiHandle,
                                           HAL_SPI_TX_RX_COMPLETE_CB_ID,
                                           spi_transfer_done_callback );

    configASSERT( xHalResult == HAL_OK );

    xHalResult = HAL_SPI_RegisterCallback( pxCtx->pxSpiHandle,
                                           HAL_SPI_ERROR_CB_ID,
                                           spi_transfer_error_callback );

    configASSERT( xHalResult == HAL_OK );
}

/* Wait for the flow pin go high signifying that the module is ready for more data. */
static inline BaseType_t xWaitForFlow( MxDataplaneCtx_t * pxCtx )
{
    uint32_t ulFlowValue = 0;

    /* Wait for flow pin to go high to signal that the module is ready */
    ulFlowValue = ulTaskNotifyTakeIndexed( SPI_EVT_FLOW_IDX, pdTRUE, MX_SPI_FLOW_TIMEOUT );

    if( ulFlowValue == 0 )
    {
        LogDebug( "Timed out while waiting for EVT_SPI_FLOW. ulFlowValue: %d, xTimeout: %d",
                  ulFlowValue, MX_SPI_FLOW_TIMEOUT );
    }

    return( ( BaseType_t ) ( ulFlowValue != 0 ) );
}

void vDataplaneThread( void * pvParameters )
{
    /* Get context struct (contains instance parameters) */
    MxDataplaneCtx_t * pxCtx = ( MxDataplaneCtx_t * ) pvParameters;

    BaseType_t exitFlag = pdFALSE;

    /* Export context for callbacks */
    pxSpiCtx = pxCtx;

    vInitCallbacks( pxCtx );

    /* set CS/NSS high */
    vGpioSet( pxCtx->gpio_nss );

    /* Do hardware reset */
    vDoHardReset( pxCtx );

    while( exitFlag == pdFALSE )
    {
        PacketBuffer_t * pxTxBuff = NULL;
        PacketBuffer_t * pxRxBuff = NULL;

        if( pxCtx->ulTxPacketsWaiting == 0 )
        {
            LogDebug( "Starting wait for DATA_WAITING_IDX event" );
            ulTaskNotifyTakeIndexed( DATA_WAITING_IDX,
                                     pdFALSE,
                                     500 );
        }

        /* Skip this transaction if IRQ pin is low and there are no pending tx packets */
        if( ( xGpioGet( pxCtx->gpio_notify ) == pdFALSE ) &&
            ( pxCtx->ulTxPacketsWaiting == 0 ) )
        {
            continue;
        }

        /* Clear flow state */
        xTaskNotifyStateClearIndexed( NULL, SPI_EVT_FLOW_IDX );

        /* Set CS low to initiate transaction */
        vGpioClear( pxCtx->gpio_nss );

        BaseType_t xResult = pdTRUE;

        /* Wait for the module to be ready */
        if( xWaitForFlow( pxCtx ) == pdTRUE )
        {
            uint16_t usTxLen = 0;
            uint16_t usRxLen = 0;

            QueueHandle_t xSourceQueue = NULL;

            /* Prepare a control plane messages for TX */
            if( xQueuePeek( pxCtx->xControlPlaneSendQueue, &pxTxBuff, 0 ) == pdTRUE )
            {
                configASSERT( pxTxBuff != NULL );
                configASSERT( pxTxBuff->ref > 0 );
                usTxLen = pxTxBuff->tot_len;
                xSourceQueue = pxCtx->xControlPlaneSendQueue;
                LogDebug( "Preparing controlplane message for transmission" );
            }
            else if( xQueuePeek( pxCtx->xDataPlaneSendQueue, &pxTxBuff, 0 ) == pdTRUE )
            {
                configASSERT( pxTxBuff != NULL );
                configASSERT( pxTxBuff->ref > 0 );
                usTxLen = pxTxBuff->tot_len;
                xSourceQueue = pxCtx->xDataPlaneSendQueue;
                LogDebug( "Preparing dataplane message for transmission" );
            }
            else
            {
                /* Empty, no TX packets */
            }

            if( ( pxTxBuff == NULL ) &&
                ( pxCtx->ulTxPacketsWaiting != 0 ) )
            {
                LogWarn( "Mismatch between ulTxPacketsWaiting and queue contents. Resetting ulTxPacketsWaiting" );
                pxSpiCtx->ulTxPacketsWaiting = 0;
            }

            if( xResult == pdTRUE )
            {
                /* Transfer the header */
                xResult = xDoSpiHeaderTransfer( pxCtx, &usTxLen, &usRxLen );
            }

            if( xResult == pdTRUE )
            {
                /* Allocate RX buffer */
                if( usRxLen > 0 )
                {
                    pxRxBuff = PBUF_ALLOC_RX( usRxLen );
                }

                /* Wait for flow pin to go high */
                xResult = xWaitForFlow( pxCtx );
            }

            /* Read from the queue */
            if( ( xResult == pdTRUE ) &&
                ( xSourceQueue != NULL ) )
            {
                xResult = xQueueReceive( xSourceQueue, &pxTxBuff, 0 );
                configASSERT( pxTxBuff != NULL );
                configASSERT( xResult == pdTRUE );
            }
            else if( pxTxBuff != NULL )
            {
                pxTxBuff = NULL;
            }

            /* Transmit / receive packet data */
            if( xResult == pdTRUE )
            {
                /* Transmit case */
                if( ( usTxLen > 0 ) &&
                    ( usRxLen == 0 ) )
                {
                    configASSERT( pxTxBuff );
                    xResult = xTransmitMessage( pxCtx, pxTxBuff->payload, usTxLen );
                }
                else if( ( usRxLen > 0 ) &&
                         ( usTxLen == 0 ) )
                {
                    configASSERT( pxRxBuff );
                    xResult = xReceiveMessage( pxCtx, pxRxBuff->payload, usRxLen );
                }
                else if( ( usRxLen > 0 ) &&
                         ( usTxLen > 0 ) )
                {
                    configASSERT( pxRxBuff );
                    configASSERT( pxTxBuff );

                    xResult = xTransmitReceiveMessage( pxCtx,
                                                       pxTxBuff->payload,
                                                       usTxLen,
                                                       pxRxBuff->payload,
                                                       usRxLen );
                }
            }
        }
        else
        {
            LogDebug( "Timed out while waiting for flow event." );
            xResult = pdFALSE;
        }

        /* Set CS / NSS high (idle) */
        vGpioSet( pxCtx->gpio_nss );

        if( pxTxBuff != NULL )
        {
            /* Decrement TX packets waiting counter */
            ( void ) Atomic_Decrement_u32( &( pxSpiCtx->ulTxPacketsWaiting ) );

            /* Free the TX buffer */
            LogDebug( "Decreasing reference count of pxTxBuff %p from %d to %d", pxTxBuff, pxTxBuff->ref, ( pxTxBuff->ref - 1 ) );
            PBUF_FREE( pxTxBuff );
            pxTxBuff = NULL;
        }

        if( ( xResult == pdTRUE ) &&
            ( pxRxBuff != NULL ) )
        {
            vProcessRxPacket( pxCtx->xControlPlaneResponseBuff, pxCtx->pxNetif, &pxRxBuff );
        }
        else if( pxRxBuff != NULL )
        {
            LogDebug( "Decreasing reference count of pxRxBuff %p from %d to %d", pxRxBuff, pxRxBuff->ref, ( pxRxBuff->ref - 1 ) );
            PBUF_FREE( pxRxBuff );
            pxRxBuff = NULL;
        }

        configASSERT( pxTxBuff == NULL );
        configASSERT( pxRxBuff == NULL );
    }
}