/*!
 * @brief Finish up a transfer.
 * Cleans up after a transfer is complete. Interrupts are disabled, and the SPI module
 * is disabled. This is not a public API as it is called from other driver functions.
 */
static void SPI_DRV_MasterCompleteTransfer(uint32_t instance)
{
    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_master_state_t * spiState = (spi_master_state_t *)g_spiStatePtr[instance];

    SPI_Type *base = g_spiBase[instance];

    /* The transfer is complete.*/
    spiState->isTransferInProgress = false;

    /* Disable interrupts */
    SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, false);
    SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (g_spiFifoSize[instance] != 0)
    {
        /* Now disable the SPI FIFO interrupts */
        SPI_HAL_SetFifoIntCmd(base, kSpiTxFifoNearEmptyInt, false);
        SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, false);
    }
#endif

    if (spiState->isTransferBlocking)
    {
        /* Signal the synchronous completion object */
        OSA_SemaPost(&spiState->irqSync);
    }
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_MasterTransferBlocking
 * Description   : Performs a blocking SPI master mode transfer.
 * This function simultaneously sends and receives data on the SPI bus, as SPI is naturally
 * a full-duplex bus. The function will not return until the transfer is complete.
 *
 *END**************************************************************************/
spi_status_t SPI_DRV_MasterTransferBlocking(uint32_t instance,
                                            const spi_master_user_config_t * device,
                                            const uint8_t * sendBuffer,
                                            uint8_t * receiveBuffer,
                                            size_t transferByteCount,
                                            uint32_t timeout)
{
    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_master_state_t * spiState = (spi_master_state_t *)g_spiStatePtr[instance];
    spi_status_t errorStatus = kStatus_SPI_Success;
    SPI_Type *base = g_spiBase[instance];

    /* fill in members of the run-time state struct*/
    spiState->isTransferBlocking = true; /* Indicates this is a blocking transfer */
    spiState->sendBuffer = (const uint8_t *)sendBuffer;
    spiState->receiveBuffer = (uint8_t *)receiveBuffer;
    spiState->remainingSendByteCount = transferByteCount;
    spiState->remainingReceiveByteCount = transferByteCount;

    /* start the transfer process*/
    errorStatus = SPI_DRV_MasterStartTransfer(instance, device);
    if (errorStatus != kStatus_SPI_Success)
    {
        return errorStatus;
    }

    /* As this is a synchronous transfer, wait until the transfer is complete.*/
    osa_status_t syncStatus;

    do
    {
        syncStatus = OSA_SemaWait(&spiState->irqSync, timeout);
    }while(syncStatus == kStatus_OSA_Idle);

    /* If a timeout occurs, stop the transfer by setting the isTransferInProgress to false and
     * disabling interrupts, then return the timeout error status.
     */
    if (syncStatus != kStatus_OSA_Success)
    {
        /* The transfer is complete.*/
        spiState->isTransferInProgress = false;

        /* Disable interrupts */
        SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, false);
        SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
        if (g_spiFifoSize[instance] != 0)
        {
            /* Now disable the SPI FIFO interrupts */
            SPI_HAL_SetFifoIntCmd(base, kSpiTxFifoNearEmptyInt, false);
            SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, false);
        }
#endif
        errorStatus = kStatus_SPI_Timeout;
    }

    return errorStatus;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaSlaveCompleteTransfer
 * Description   : Finish up a transfer.
 * Cleans up after a transfer is complete. Interrupts are disabled, and the SPI module
 * is disabled. This is not a public API as it is called from other driver functions.
 *
 *END**************************************************************************/
static void SPI_DRV_DmaSlaveCompleteTransfer(uint32_t instance)
{
    spi_dma_slave_state_t * spiState = (spi_dma_slave_state_t *)g_spiStatePtr[instance];

    SPI_Type *base = g_spiBase[instance];

    /* Disable DMA requests and interrupts. */
    SPI_HAL_SetRxDmaCmd(base, false);
    SPI_HAL_SetTxDmaCmd(base, false);
    SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);

    /* Stop DMA channels */
    DMA_DRV_StopChannel(&spiState->dmaTransmit);
    DMA_DRV_StopChannel(&spiState->dmaReceive);

    /* Disable interrupts */
    SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, false);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (g_spiFifoSize[instance] != 0)
    {
        /* Now disable the SPI FIFO interrupts */
        SPI_HAL_SetFifoIntCmd(base, kSpiTxFifoNearEmptyInt, false);
        SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, false);
    }

    /* Receive extra byte if remaining receive byte is 0 */
    if ((spiState->hasExtraByte) && (!spiState->remainingReceiveByteCount) &&
        (spiState->receiveBuffer))
    {
        spiState->receiveBuffer[spiState->remainingReceiveByteCount] =
                                                                 SPI_HAL_ReadDataLow(base);
    }
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    if (spiState->isSync)
    {
        /* Signal the synchronous completion object */
        OSA_EventSet(&spiState->event, kSpiDmaTransferDone);
    }

    /* The transfer is complete, update the state structure */
    spiState->isTransferInProgress = false;
    spiState->status = kStatus_SPI_Success;
    spiState->errorCount = 0;
    spiState->sendBuffer = NULL;
    spiState->receiveBuffer = NULL;
    spiState->remainingSendByteCount = 0;
    spiState->remainingReceiveByteCount = 0;
}
/*!
 * @brief Finish up a transfer.
 * Cleans up after a transfer is complete. Interrupts are disabled, and the SPI module
 * is disabled. This is not a public API as it is called from other driver functions.
 */
static void SPI_DRV_DmaMasterCompleteTransfer(uint32_t instance)
{
    /* instantiate local variable of type spi_dma_master_state_t and point to global state */
    spi_dma_master_state_t * spiDmaState = (spi_dma_master_state_t *)g_spiStatePtr[instance];

    SPI_Type *base = g_spiBase[instance];

    /* Disable DMA requests and interrupts. */
    SPI_HAL_SetRxDmaCmd(base, false);
    SPI_HAL_SetTxDmaCmd(base, false);
    SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);

    /* The transfer is complete.*/
    spiDmaState->isTransferInProgress = false;

    if (spiDmaState->isTransferBlocking)
    {
        /* Signal the synchronous completion object */
        OSA_SemaPost(&spiDmaState->irqSync);
    }
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaSlaveCallback
 * Description   : This function is called when the DMA generates an interrupt.
 * The DMA generates an interrupt when the channel is "done", meaning that the
 * expected number of bytes have been transferred.  When the interrupt occurs,
 * the DMA will jump to this callback as it was registered in the DMA register
 * callback service function.  The user will defined their own callback function
 * to take whatever action they deem necessary for handling the end of a transfer.
 * For example, the user may simply want their callback function to set a global
 * flag to indicate that the transfer is complete.  The user defined callback
 * is passed in through the "param" parameter.
 * The parameter chanStatus is currently not used.
 *
 *END**************************************************************************/
void SPI_DRV_DmaSlaveCallback(void *param, dma_channel_status_t chanStatus)
{
    uint32_t instance = (uint32_t)(param);

    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_dma_slave_state_t * spiDmaState = (spi_dma_slave_state_t *)g_spiStatePtr[instance];

    SPI_Type *base = g_spiBase[instance];

    /* If hasExtraByte flag was set, need to enable the TX empty interrupt to get the last byte */
    if ((spiDmaState->hasExtraByte) && (spiDmaState->remainingReceiveByteCount))
    {
        SPI_HAL_SetTxDmaCmd(base, false);
        SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true);
    }
    else
    {
        /* Transfer completed, finish the transfer */
        SPI_DRV_DmaSlaveCompleteTransfer(instance);
    }

}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaSlaveDeinit
 * Description   : De-initializes the device.
 * Clears the control register and turns off the clock to the module.
 *
 *END**************************************************************************/
spi_status_t SPI_DRV_DmaSlaveDeinit(uint32_t instance)
{
    spi_dma_slave_state_t * spiState = (spi_dma_slave_state_t *)g_spiStatePtr[instance];
    SPI_Type *base = g_spiBase[instance];

    assert(instance < SPI_INSTANCE_COUNT);

    /* Disable SPI interrupt */
    INT_SYS_DisableIRQ(g_spiIrqId[instance]);

    /* Reset the SPI module to its default settings including disabling SPI and its interrupts */
    SPI_HAL_Init(base);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (g_spiFifoSize[instance] != 0)
    {
        SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, false);
    }

    /* disable transmit interrupt */
    SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    /* Free DMA channels */
    DMA_DRV_FreeChannel(&spiState->dmaReceive);
    DMA_DRV_FreeChannel(&spiState->dmaTransmit);

    /* Disable clock for SPI */
    CLOCK_SYS_DisableSpiClock(instance);

    /* Destroy the event */
    OSA_EventDestroy(&spiState->event);

    /* Clear state pointer */
    g_spiStatePtr[instance] = NULL;

    return kStatus_SPI_Success;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaSlaveStartTransfer
 * Description   : Starts the transfer with information passed.
 *
 *END**************************************************************************/
static spi_status_t SPI_DRV_DmaSlaveStartTransfer(uint32_t instance)
{
    spi_dma_slave_state_t * spiState = (spi_dma_slave_state_t *)g_spiStatePtr[instance];

    /* For temporarily storing DMA register channel */
    void * param;
    SPI_Type *base = g_spiBase[instance];
    uint32_t transferSizeInBytes;  /* DMA transfer size in bytes */

    /* Initialize s_byteToSend */
    s_byteToSend = spiState->dummyPattern;

    /* If the transfer count is zero, then return immediately. */
    if (spiState->remainingSendByteCount == 0)
    {
        /* Signal the synchronous completion object if the transfer wasn't async.
         * Otherwise, when we return the the sync function we'll get stuck in the sync wait loop.
         */
        if (spiState->isSync)
        {
            /* Signal the synchronous completion object */
            OSA_EventSet(&spiState->event, kSpiDmaTransferDone);
        }
        return kStatus_SPI_Success;
    }

    /* In order to flush any remaining data in the shift register, disable then enable the SPI */
    SPI_HAL_Disable(base);
    SPI_HAL_Enable(base);

    /* First, set the DMA transfer size in bytes */
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (SPI_HAL_Get8or16BitMode(base) == kSpi16BitMode)
    {
        transferSizeInBytes = 2;
        /* If bits/frame > 8, meaning 2 bytes, then the transfer byte count must not be an odd
         * count. If so, drop the last odd byte. This odd byte will be transferred in when dma
         * completed
         */
        if (spiState->remainingSendByteCount % 2 != 0)
        {
            spiState->remainingSendByteCount ++;
            spiState->remainingReceiveByteCount --;
            spiState->hasExtraByte = true;
        }
        else
        {
            spiState->hasExtraByte = false;
        }
    }
    else
    {
        transferSizeInBytes = 1;
    }
#else
    transferSizeInBytes = 1;
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    param = (void *)(instance);     /* For DMA callback, set "param" as the SPI instance number */

    /* Save information about the transfer for use by the ISR. */
    spiState->isTransferInProgress = true;

    /************************************************************************************
     * Set up the RX DMA channel Transfer Control Descriptor (TCD)
     * Note, if there is no receive buffer (if user passes in NULL), then bypass RX DMA
     * set up.
     ************************************************************************************/
    /* If no receive buffer then disable incrementing the destination and set the destination
     * to a temporary location
     */
    if ((spiState->remainingReceiveByteCount > 0) || (spiState->hasExtraByte))
    {
        uint32_t receiveSize = spiState->remainingReceiveByteCount;
        if ((!spiState->receiveBuffer) || (!spiState->remainingReceiveByteCount))
        {
            if (!spiState->remainingReceiveByteCount)
            {
                /* If receive count is 0, always receive 1 frame (2 bytes) */
                receiveSize = 2;
            }
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiState->dmaReceive,
                                   kDmaPeripheralToPeripheral,
                                   transferSizeInBytes,
                                   SPI_HAL_GetDataRegAddr(base), /* src is data register */
                                   (uint32_t)(&s_rxBuffIfNull), /* dest is temporary location */
                                   (uint32_t)(receiveSize));
        }
        else
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiState->dmaReceive,
                                   kDmaPeripheralToMemory,
                                   transferSizeInBytes,
                                   SPI_HAL_GetDataRegAddr(base), /* src is data register */
                                   (uint32_t)(spiState->receiveBuffer), /* dest is rx buffer */
                                   (uint32_t)(receiveSize));
        }
        /* Destination size is only one byte */
        DMA_DRV_SetDestTransferSize(&spiState->dmaReceive, 1U);

        /* Enable the DMA peripheral request */
        DMA_DRV_StartChannel(&spiState->dmaReceive);

        /* Register callback for DMA interrupt */
        DMA_DRV_RegisterCallback(&spiState->dmaReceive, SPI_DRV_DmaSlaveCallback, param);
    }

    /************************************************************************************
     * Set up the TX DMA channel Transfer Control Descriptor (TCD)
     * Note, if there is no source buffer (if user passes in NULL), then send zeros
     ************************************************************************************/
    /* Per the reference manual, before enabling the SPI transmit DMA request, we first need
     * to read the status register and then write to the SPI data register.  Afterwards, we need
     * to decrement the sendByteCount and perform other driver maintenance functions.
     */
    /* Read the SPI Status register */
    SPI_HAL_IsTxBuffEmptyPending(base);

    /* Start the transfer by writing the first byte/word to the SPI data register.
     * If a send buffer was provided, the byte/word comes from there. Otherwise we just send zeros.
     */
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (transferSizeInBytes == 2) /* 16-bit transfers for SPI16 module */
    {
        if (spiState->sendBuffer)
        {
            s_byteToSend = *(spiState->sendBuffer);
            SPI_HAL_WriteDataLow(base, s_byteToSend);
            ++spiState->sendBuffer;

            s_byteToSend = *(spiState->sendBuffer);
            SPI_HAL_WriteDataHigh(base, s_byteToSend);
            ++spiState->sendBuffer;
        }
        else  /* Else, if no send buffer, write zeros */
        {
            SPI_HAL_WriteDataLow(base, s_byteToSend);
            SPI_HAL_WriteDataHigh(base, s_byteToSend);
        }
        spiState->remainingSendByteCount -= 2;  /* Decrement the send byte count by 2 */
    }
    else /* 8-bit transfers for SPI16 module */
    {
        if (spiState->sendBuffer)
        {
            s_byteToSend = *(spiState->sendBuffer);
            ++spiState->sendBuffer;
        }
        SPI_HAL_WriteDataLow(base, s_byteToSend);  /* If no send buffer, s_byteToSend=0 */
        --spiState->remainingSendByteCount; /* Decrement the send byte count for use in DMA setup */
    }
#else
    /* For SPI modules that do not support 16-bit transfers */
    if (spiState->sendBuffer)
    {
        s_byteToSend = *(spiState->sendBuffer);
        ++spiState->sendBuffer;
    }
    SPI_HAL_WriteData(base, s_byteToSend); /* If no send buffer, s_byteToSend=0 */
    --spiState->remainingSendByteCount; /* Decrement the send byte count for use in DMA setup */
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    /* If there are no more bytes to send then return without setting up the TX DMA channel.
     * Else, set up the TX DMA channel and enable the TX DMA request.
     */
    if (!spiState->remainingSendByteCount) /* No more bytes to send */
    {
        if ((spiState->remainingReceiveByteCount) || (spiState->hasExtraByte))
        {
            /* Enable the RX DMA channel request now */
            SPI_HAL_SetRxDmaCmd(base, true);
            return kStatus_SPI_Success;
        }
        else    /* If RX DMA chan not setup then enable the interrupt to get the received byte */
        {
            SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true);
            return kStatus_SPI_Success;
        }
    }
    else  /* Since there are more bytes to send, set up the TX DMA channel */
    {
        /* If there is a send buffer, data comes from there, else send 0 */
        if (spiState->sendBuffer)
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiState->dmaTransmit, kDmaMemoryToPeripheral,
                                   transferSizeInBytes,
                                   (uint32_t)(spiState->sendBuffer),
                                   SPI_HAL_GetDataRegAddr(base),
                                   (uint32_t)(spiState->remainingSendByteCount));
        }
        else /* Configure TX DMA channel to send zeros */
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiState->dmaTransmit, kDmaPeripheralToPeripheral,
                                   transferSizeInBytes,
                                   (uint32_t)(&s_byteToSend),
                                   SPI_HAL_GetDataRegAddr(base),
                                   (uint32_t)(spiState->remainingSendByteCount));
        }
        /* Source size is only one byte */
        DMA_DRV_SetSourceTransferSize(&spiState->dmaTransmit, 1U);

        /* Enable the SPI TX DMA Request */
        SPI_HAL_SetTxDmaCmd(base, true);

        /* Enable the SPI RX DMA request also. */
        SPI_HAL_SetRxDmaCmd(base, true);

        /* Enable the DMA peripheral request */
        DMA_DRV_StartChannel(&spiState->dmaTransmit);
    }

    return kStatus_SPI_Success;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaMasterCallback
 * Description   : This function is called when the DMA generates an interrupt.
 * The DMA generates an interrupt when the channel is "done", meaning that the
 * expected number of bytes have been transferred.  When the interrupt occurs,
 * the DMA will jump to this callback as it was registered in the DMA register
 * callback service function.  The user will defined their own callback function
 * to take whatever action they deem necessary for handling the end of a transfer.
 * For example, the user may simply want their callback function to set a global
 * flag to indicate that the transfer is complete.  The user defined callback
 * is passed in through the "param" parameter.
 * The parameter chanStatus is currently not used.
 *
 *END**************************************************************************/
void SPI_DRV_DmaMasterCallback(void *param, dma_channel_status_t chanStatus)
{
    uint32_t instance = (uint32_t)(param);

    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_dma_master_state_t * spiDmaState = (spi_dma_master_state_t *)g_spiStatePtr[instance];

    SPI_Type *base = g_spiBase[instance];

    /* If the extraByte flag was set, need to enable the TX empty interrupt to get the last byte */
    if (spiDmaState->extraByte)
    {
        SPI_HAL_SetTxDmaCmd(base, false);

        /* If the TX buffer is already empty then it may not generate an interrupt so soon
         * after the TX DMA is disabled, therefore read the RX data and put into RX buffer.
         */
        if (SPI_HAL_GetIntStatusFlag(base, kSpiTxBufferEmptyFlag))
        {
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
            /* If the SPI module contains a FIFO (and if it is enabled), check the FIFO empty flag */
            if ((g_spiFifoSize[instance] != 0) && (SPI_HAL_GetFifoCmd(base)))
            {
                /* Wait till the rx buffer has data */
                while (SPI_HAL_GetFifoStatusFlag(base, kSpiRxFifoEmpty) == 1) {}

            }
            else /* Check the read pending flag */
            {
                /* Wait till the rx buffer has data */
                while (SPI_HAL_IsReadBuffFullPending(base) == 0) {}
            }

            /* If there is a receive buffer, copy the final byte from the SPI data register
             *  to the receive buffer
             */
            if (spiDmaState->receiveBuffer)
            {
                spiDmaState->receiveBuffer[spiDmaState->transferByteCnt-2] =
                                                              SPI_HAL_ReadDataLow(base);
            }
            /* Else, read out the data register and throw away the bytes read */
            else
            {
                /* Read and throw away the lower data buffer to clear it out */
                s_rxBuffIfNull = SPI_HAL_ReadDataLow(base);
            }
            /* Read and throw away the upper data buffer to clear it out */
            s_rxBuffIfNull = SPI_HAL_ReadDataHigh(base);
            SPI_DRV_DmaMasterCompleteTransfer(instance);
#endif
        }
        else
        {
            /* Else, if the TX buffer is not empty, enable the interrupt and handle the
             * receive in the ISR
             */
            SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true);
        }
    }
    else /* If no extra byte is needing to be receive, complete the transfer */
    {
        SPI_DRV_DmaMasterCompleteTransfer(instance);
    }
}
/*!
 * @brief Initiate (start) a transfer using DMA. This is not a public API as it is called from
 *  other driver functions
 */
spi_status_t SPI_DRV_DmaMasterStartTransfer(uint32_t instance,
                                            const spi_dma_master_user_config_t * device)
{
    /* instantiate local variable of type spi_dma_master_state_t and point to global state */
    spi_dma_master_state_t * spiDmaState = (spi_dma_master_state_t *)g_spiStatePtr[instance];

    /* For temporarily storing DMA register channel */
    void * param;
    uint32_t calculatedBaudRate;
    SPI_Type *base = g_spiBase[instance];
    uint32_t transferSizeInBytes;  /* DMA transfer size in bytes */

    /* Initialize s_byteToSend */
    s_byteToSend = 0;

    /* If the transfer count is zero, then return immediately.*/
    if (spiDmaState->remainingSendByteCount == 0)
    {
        /* Signal the synchronous completion object if the transfer wasn't async.
         * Otherwise, when we return the the sync function we'll get stuck in the sync wait loop.
         */
        if (spiDmaState->isTransferBlocking)
        {
            OSA_SemaPost(&spiDmaState->irqSync);
        }

        return kStatus_SPI_Success;
    }

    /* Configure bus for this device. If NULL is passed, we assume the caller has
     * preconfigured the bus using SPI_DRV_DmaMasterConfigureBus().
     * Do nothing for calculatedBaudRate. If the user wants to know the calculatedBaudRate
     * then they can call this function separately.
     */
    if (device)
    {
        SPI_DRV_DmaMasterConfigureBus(instance, device, &calculatedBaudRate);
    }

    /* In order to flush any remaining data in the shift register, disable then enable the SPI */
    SPI_HAL_Disable(base);
    SPI_HAL_Enable(base);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    /* Check the transfer byte count. If bits/frame > 8, meaning 2 bytes, and if
     * the transfer byte count is an odd count we'll have to round down the RX transfer byte count
     * to the next lowest even number by one and assert a flag to indicate in the interrupt handler
     * that we take care of sending and receiving this last byte.  We'll round up TX byte count.
     */
    if (SPI_HAL_Get8or16BitMode(base) == kSpi16BitMode) /* Applies to 16-bit transfers */
    {
        /* Odd byte count for 16-bit transfers, set the extraByte flag */
        if (spiDmaState->remainingSendByteCount & 1UL) /* If odd byte count */
        {
            transferSizeInBytes = 2; /* Set transfer size to two bytes for the DMA operation */
            spiDmaState->extraByte = true; /* Set the extraByte flag */

            /* Round up TX byte count so when DMA completes, all data would've been sent */
            spiDmaState->remainingSendByteCount += 1U;

            /* Round down RX byte count which means at the end of the RX DMA transfer, we'll need
             * to set up an interrupt to get the last byte.
             */
            spiDmaState->remainingReceiveByteCount &= ~1U;

            /* Store the transfer byte count to the run-time state struct
             * for later use in the interrupt handler.
             */
            spiDmaState->transferByteCnt = spiDmaState->remainingSendByteCount;
        }
        /* Even byte count for 16-bit transfers, clear the extraByte flag */
        else
        {
            transferSizeInBytes = 2; /* Set transfer size to two bytes for the DMA operation */
            spiDmaState->extraByte = false; /* Clear the extraByte flag */
        }
    }
    else /* For 8-bit transfers */
    {
        transferSizeInBytes = 1;
        spiDmaState->extraByte = false;
    }
#else
    transferSizeInBytes = 1;
#endif

    param = (void *)(instance); /* For DMA callback, set "param" as the SPI instance number */

    /* Check that we're not busy.*/
    if (spiDmaState->isTransferInProgress)
    {
        return kStatus_SPI_Busy;
    }

    /* Save information about the transfer for use by the ISR.*/
    spiDmaState->isTransferInProgress = true;

    /************************************************************************************
     * Set up the RX DMA channel Transfer Control Descriptor (TCD)
     * Note, if there is no receive byte count, then bypass RX DMA set up.
     ***********************************************************************************/
    if (spiDmaState->remainingReceiveByteCount)
    {
        /* If no receive buffer then disable incrementing the destination and set the destination
         * to a temporary location
         */
        if (!spiDmaState->receiveBuffer)
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiDmaState->dmaReceive,
                                   kDmaPeripheralToPeripheral,
                                   transferSizeInBytes,
                                   SPI_HAL_GetDataRegAddr(base), /* src is data register */
                                   (uint32_t)(&s_rxBuffIfNull), /* dest is temporary location */
                                   (uint32_t)(spiDmaState->remainingReceiveByteCount));
        }
        else
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiDmaState->dmaReceive,
                                   kDmaPeripheralToMemory,
                                   transferSizeInBytes,
                                   SPI_HAL_GetDataRegAddr(base), /* src is data register */
                                   (uint32_t)(spiDmaState->receiveBuffer),/* dest is rx buffer */
                                   (uint32_t)(spiDmaState->remainingReceiveByteCount));
        }

        /* Dest size is always 1 byte on each transfer */
        DMA_DRV_SetDestTransferSize(&spiDmaState->dmaReceive, 1U);

        /* Enable the DMA peripheral request */
        DMA_DRV_StartChannel(&spiDmaState->dmaReceive);

        /* Register callback for DMA interrupt */
        DMA_DRV_RegisterCallback(&spiDmaState->dmaReceive, SPI_DRV_DmaMasterCallback, param);
    }

    /************************************************************************************
     * Set up the TX DMA channel Transfer Control Descriptor (TCD)
     * Note, if there is no source buffer (if user passes in NULL), then send zeros
     ***********************************************************************************/
    /* Per the reference manual, before enabling the SPI transmit DMA request, we first need
     * to read the status register and then write to the SPI data register.  Afterwards, we need
     * to decrement the sendByteCount and perform other driver maintenance functions.
     */

    /* Read the SPI Status register */
    SPI_HAL_IsTxBuffEmptyPending(base);

    /* Start the transfer by writing the first byte/word to the SPI data register.
     * If a send buffer was provided, the byte/word comes from there. Otherwise we just send zeros.
     * This will cause an immeidate transfer which in some cases may cause the RX DMA channel to
     * complete before having the chance to completely set up the TX DMA channel. As such, we'll
     * enable the RX DMA channel last.
     */
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (transferSizeInBytes == 2) /* 16-bit transfers for SPI16 module */
    {
        if (spiDmaState->sendBuffer)
        {
            s_byteToSend = *(spiDmaState->sendBuffer);
            SPI_HAL_WriteDataLow(base, s_byteToSend);
            ++spiDmaState->sendBuffer;

            s_byteToSend = *(spiDmaState->sendBuffer);
            SPI_HAL_WriteDataHigh(base, s_byteToSend);
            ++spiDmaState->sendBuffer;
        }
        else  /* Else, if no send buffer, write zeros */
        {
            SPI_HAL_WriteDataLow(base, s_byteToSend);
            SPI_HAL_WriteDataHigh(base, s_byteToSend);
        }
        spiDmaState->remainingSendByteCount -= 2;  /* Decrement the send byte count by 2 */
    }
    else /* 8-bit transfers for SPI16 module */
    {
        if (spiDmaState->sendBuffer)
        {
            s_byteToSend = *(spiDmaState->sendBuffer);
            ++spiDmaState->sendBuffer;
        }
        SPI_HAL_WriteDataLow(base, s_byteToSend); /* If no send buffer, s_byteToSend=0 */
        --spiDmaState->remainingSendByteCount; /* Decrement the send byte count */
    }
#else
    /* For SPI modules that do not support 16-bit transfers */
    if (spiDmaState->sendBuffer)
    {
        s_byteToSend = *(spiDmaState->sendBuffer);
        ++spiDmaState->sendBuffer;
    }
    SPI_HAL_WriteData(base, s_byteToSend); /* If no send buffer, s_byteToSend=0 */
    --spiDmaState->remainingSendByteCount; /* Decrement the send byte count */
#endif

    /* If there are no more bytes to send then return without setting up the TX DMA channel
     * and let the receive DMA channel complete the transfer if the RX DMA channel was setup.
     * If the RX DMA channel was not set up (due to odd byte count of 1 in 16-bit mode), enable
     * the interrupt to get the received byte.
     */
    if (!spiDmaState->remainingSendByteCount) /* No more bytes to send */
    {
        if (spiDmaState->remainingReceiveByteCount)
        {
            /* Enable the RX DMA channel request now  */
            SPI_HAL_SetRxDmaCmd(base, true);
            return kStatus_SPI_Success;
        }
        else /* If RX DMA chan not setup then enable the interrupt to get the received byte */
        {
            SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true);
            return kStatus_SPI_Success;
        }
    }
    /* Else, since there are more bytes to send, go ahead and set up the TX DMA channel */
    else
    {
        /* If there is a send buffer, data comes from there, else send 0 */
        if (spiDmaState->sendBuffer)
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiDmaState->dmaTransmit, kDmaMemoryToPeripheral,
                                   transferSizeInBytes,
                                   (uint32_t)(spiDmaState->sendBuffer),
                                   SPI_HAL_GetDataRegAddr(base),
                                   (uint32_t)(spiDmaState->remainingSendByteCount));
        }
        else /* Configure TX DMA channel to send zeros */
        {
            /* Set up this channel's control which includes enabling the DMA interrupt */
            DMA_DRV_ConfigTransfer(&spiDmaState->dmaTransmit, kDmaPeripheralToPeripheral,
                                   transferSizeInBytes,
                                   (uint32_t)(&s_byteToSend),
                                   SPI_HAL_GetDataRegAddr(base),
                                   (uint32_t)(spiDmaState->remainingSendByteCount));
        }

        /* Source size is only one byte on each transfer */
        DMA_DRV_SetSourceTransferSize(&spiDmaState->dmaTransmit, 1U);

        /* Enable the SPI TX DMA Request */
        SPI_HAL_SetTxDmaCmd(base, true);

        /* Enable the SPI RX DMA Request after the TX DMA request is enabled.  This is done to
         * make sure that the RX DMA channel does not end prematurely before we've completely set
         * up the TX DMA channel since part of the TX DMA set up involves placing 1 or 2 bytes of
         * data into the send data register which causes an immediate transfer.
         */
        SPI_HAL_SetRxDmaCmd(base, true);

        /* Enable the DMA peripheral request */
        DMA_DRV_StartChannel(&spiDmaState->dmaTransmit);
    }

    return kStatus_SPI_Success;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_MasterIRQHandler
 * Description   : Interrupt handler for SPI master mode.
 * This handler uses the buffers stored in the spi_master_state_t structs to transfer data.
 *
 *END**************************************************************************/
void SPI_DRV_MasterIRQHandler(uint32_t instance)
{
    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_master_state_t * spiState = (spi_master_state_t *)g_spiStatePtr[instance];

    SPI_Type *base = g_spiBase[instance];

    /* Exit the ISR if no transfer is happening for this instance.*/
    if (!spiState->isTransferInProgress)
    {
        return;
    }

    /* RECEIVE IRQ handler: Check read buffer only if there are remaining bytes to read. */
    if (spiState->remainingReceiveByteCount)
    {
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
        uint8_t byteReceivedLow, byteReceivedHigh;

        spi_data_bitcount_mode_t bitCntRx = SPI_HAL_Get8or16BitMode(base);

        /* If the SPI module contains a FIFO (and if it's enabled), drain the FIFO until it is empty
         * or until the remainingSendByteCount reaches 0.
         */
        if ((g_spiFifoSize[instance] != 0) && (SPI_HAL_GetFifoCmd(base)))
        {
            /* Clear the RX near full interrupt */
            SPI_HAL_ClearFifoIntUsingBitWrite(base, kSpiRxNearFullClearInt);

            /* Architectural note: When developing the RX FIFO drain code, it was found that to
             * achieve more efficient run-time performance, it was better to first check the
             * bits/frame setting and then proceed with the FIFO fill management process, rather
             * than to clutter the drain process with continual checks of the bits/frame setting.
             */

            /* Optimized for bit count = 16 with FIFO support */
            if (bitCntRx == kSpi16BitMode)
            {
                /* Do this while the RX FIFO is not empty */
                while (SPI_HAL_GetFifoStatusFlag(base, kSpiRxFifoEmpty) == 0)
                {
                    /* Read the bytes from the RX FIFO */
                    byteReceivedLow = SPI_HAL_ReadDataLow(base);
                    byteReceivedHigh = SPI_HAL_ReadDataHigh(base);

                    /* Store read bytes into rx buffer only if a buffer pointer was provided */
                    if (spiState->receiveBuffer)
                    {
                        *spiState->receiveBuffer = byteReceivedLow;
                        ++spiState->receiveBuffer;

                        /* If the extraByte flag is set and these are the last 2 bytes, then skip.
                         * This will avoid over-writing the rx buffer with another un-needed byte.
                         */
                        if (!((spiState->extraByte) && (spiState->remainingReceiveByteCount == 2)))
                        {
                            *spiState->receiveBuffer = byteReceivedHigh;
                            ++spiState->receiveBuffer;
                        }
                    }

                    spiState->remainingReceiveByteCount -= 2; /* decrement the rx byte count by 2 */

                    /* If there is no more data to receive, break */
                    if (spiState->remainingReceiveByteCount == 0)
                    {
                        break;
                    }
                }
            }

            /* Optimized for bit count = 8 with FIFO support */
            else
            {
                while (SPI_HAL_GetFifoStatusFlag(base, kSpiRxFifoEmpty) == 0)
                {
                    /* Read the bytes from the RX FIFO */
                    byteReceivedLow = SPI_HAL_ReadDataLow(base);

                    /* Store read bytes into rx buffer only if a buffer pointer was provided */
                    if (spiState->receiveBuffer)
                    {
                        *spiState->receiveBuffer = byteReceivedLow;
                        ++spiState->receiveBuffer;
                    }

                    --spiState->remainingReceiveByteCount; /* decrement the rx byte count by 1 */

                    /* If there is no more data to receive, break */
                    if (spiState->remainingReceiveByteCount == 0)
                    {
                        break;
                    }
                }
            }

            /* If the remaining RX byte count is less than the RX FIFO watermark, enable
             * the TX FIFO empty interrupt. Once the TX FIFO is empty, we are ensured
             * that the transmission is complete and can then drain the RX FIFO.
             */
            if (spiState->remainingReceiveByteCount < g_spiFifoSize[instance])
            {
                SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true); /* TX FIFO empty interrupt */
            }
        }

        /* For SPI modules that do not have a FIFO (or if disabled), but have 16-bit transfer
         * capability */
        else
        {
            if (SPI_HAL_IsReadBuffFullPending(base))
            {

                /* For 16-bit transfers w/o FIFO support */
                if (bitCntRx == kSpi16BitMode)
                {
                    /* Read the bytes from the RX FIFO */
                    byteReceivedLow = SPI_HAL_ReadDataLow(base);
                    byteReceivedHigh = SPI_HAL_ReadDataHigh(base);

                    /* Store read bytes into rx buffer only if a buffer pointer was provided */
                    if (spiState->receiveBuffer)
                    {
                        *spiState->receiveBuffer = byteReceivedLow;
                        ++spiState->receiveBuffer;

                        /* If the extraByte flag is set and these are the last 2 bytes, then skip.
                         * This will avoid over-writing the rx buffer with another un-needed byte.
                         */
                        if (!((spiState->extraByte) && (spiState->remainingReceiveByteCount == 2)))
                        {
                            *spiState->receiveBuffer = byteReceivedHigh;
                            ++spiState->receiveBuffer;
                        }
                    }

                    spiState->remainingReceiveByteCount -= 2; /* decrement the rx byte count by 2 */

                }

                /* For 8-bit transfers w/o FIFO support */
                else
                {
                    /* Read the bytes from the RX FIFO */
                    byteReceivedLow = SPI_HAL_ReadDataLow(base);

                    /* Store read bytes into rx buffer only if a buffer pointer was provided */
                    if (spiState->receiveBuffer)
                    {
                        *spiState->receiveBuffer = byteReceivedLow;
                        ++spiState->receiveBuffer;
                    }

                    --spiState->remainingReceiveByteCount; /* decrement the rx byte count by 1 */
                }
            }
        }

#else /* For SPI modules that do not support 16-bit transfers and don't have FIFO */

        uint8_t byteReceived;
        /* For SPI modules without 16-bit transfer capability or FIFO support */
        if (SPI_HAL_IsReadBuffFullPending(base))
        {
            /* Read the bytes from the RX FIFO */
            byteReceived = SPI_HAL_ReadData(base);

            /* Store read bytes into rx buffer only if a buffer pointer was provided */
            if (spiState->receiveBuffer)
            {
                *spiState->receiveBuffer = byteReceived;
                ++spiState->receiveBuffer;
            }

            --spiState->remainingReceiveByteCount; /* decrement the rx byte count by 1 */
        }
#endif  /* FSL_FEATURE_SPI_16BIT_TRANSFERS */
    }

    /* TRANSMIT IRQ handler
     * Check write buffer. We always have to send a byte in order to keep the transfer
     * moving. So if the caller didn't provide a send buffer, we just send a zero byte.
     */
    uint8_t byteToSend = 0;
    if (spiState->remainingSendByteCount)
    {
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
        uint8_t byteToSendLow = 0;
        uint8_t byteToSendHigh = 0;

        spi_data_bitcount_mode_t bitCntTx = SPI_HAL_Get8or16BitMode(base);

        /* If SPI module has a FIFO and if it is enabled */
        if ((g_spiFifoSize[instance] != 0) && (SPI_HAL_GetFifoCmd(base)))
        {
            if (SPI_HAL_GetFifoStatusFlag(base, kSpiTxNearEmpty))
            {
                /* Fill of the TX FIFO with ongoing data */
                SPI_DRV_MasterFillupTxFifo(instance);
             }
        }
        else
        {
            if (SPI_HAL_IsTxBuffEmptyPending(base))
            {
                if (bitCntTx == kSpi16BitMode)
                {
                    if (spiState->sendBuffer)
                    {
                        byteToSendLow = *(spiState->sendBuffer);
                        ++spiState->sendBuffer;

                        byteToSendHigh = *(spiState->sendBuffer);
                        ++spiState->sendBuffer;
                    }
                    SPI_HAL_WriteDataLow(base, byteToSendLow);
                    SPI_HAL_WriteDataHigh(base, byteToSendHigh);

                    spiState->remainingSendByteCount -= 2;  /* decrement by 2 */
                    spiState->transferredByteCount += 2;  /* increment by 2 */
                }
                else /* SPI configured for 8-bit transfers */
                {
                    if (spiState->sendBuffer)
                    {
                        byteToSend = *(spiState->sendBuffer);
                        ++spiState->sendBuffer;
                    }
                    SPI_HAL_WriteDataLow(base, byteToSend);

                    --spiState->remainingSendByteCount;
                    ++spiState->transferredByteCount;
                }
            }
        }
#else /* For SPI modules that do not support 16-bit transfers */
        if (SPI_HAL_IsTxBuffEmptyPending(base))
        {
            if (spiState->sendBuffer)
            {
                byteToSend = *(spiState->sendBuffer);
                ++spiState->sendBuffer;
            }
            SPI_HAL_WriteData(base, byteToSend);

            --spiState->remainingSendByteCount;
            ++spiState->transferredByteCount;
        }
#endif  /* FSL_FEATURE_SPI_16BIT_TRANSFERS */
    }

    /* Check if we're done with this transfer.  The transfer is complete when all of the
     * expected data is received.
     */
    if ((spiState->remainingSendByteCount == 0) && (spiState->remainingReceiveByteCount == 0))
    {
        /* Complete the transfer. This disables the interrupts, so we don't wind up in
         * the ISR again.
         */
        SPI_DRV_MasterCompleteTransfer(instance);
    }
}
/*!
 * @brief Initiate (start) a transfer. This is not a public API as it is called from other
 *  driver functions
 */
static spi_status_t SPI_DRV_MasterStartTransfer(uint32_t instance,
                                                const spi_master_user_config_t * device)
{
    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_master_state_t * spiState = (spi_master_state_t *)g_spiStatePtr[instance];

    uint32_t calculatedBaudRate;
    SPI_Type *base = g_spiBase[instance];

    /* Check that we're not busy.*/
    if (spiState->isTransferInProgress)
    {
        return kStatus_SPI_Busy;
    }

    /* Configure bus for this device. If NULL is passed, we assume the caller has
     * preconfigured the bus using SPI_DRV_MasterConfigureBus().
     * Do nothing for calculatedBaudRate. If the user wants to know the calculatedBaudRate
     * then they can call this function separately.
     */
    if (device)
    {
        SPI_DRV_MasterConfigureBus(instance, device, &calculatedBaudRate);
    }

    /* In order to flush any remaining data in the shift register, disable then enable the SPI */
    SPI_HAL_Disable(base);
    SPI_HAL_Enable(base);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    spi_data_bitcount_mode_t bitCount;

    bitCount = SPI_HAL_Get8or16BitMode(base);

    /* Check the transfer byte count. If bits/frame > 8, meaning 2 bytes if bits/frame > 8, and if
     * the transfer byte count is an odd count we'll have to increase the transfer byte count
     * by one and assert a flag to indicate to the receive function that it will need to handle
     * an extra byte so that it does not inadverently over-write an another byte to the receive
     * buffer. For sending the extra byte, we don't care if we read past the send buffer since we
     * are only reading from it and the absolute last byte (that completes the final 16-bit word)
     * is a don't care byte anyway.
     */
    if ((bitCount == kSpi16BitMode) && (spiState->remainingSendByteCount & 1UL))
    {
        spiState->remainingSendByteCount += 1;
        spiState->remainingReceiveByteCount += 1;
        spiState->extraByte = true;
    }
    else
    {
        spiState->extraByte = false;
    }

#endif

    /* If the byte count is zero, then return immediately.*/
    if (spiState->remainingSendByteCount == 0)
    {
        SPI_DRV_MasterCompleteTransfer(instance);
        return kStatus_SPI_Success;
    }

    /* Save information about the transfer for use by the ISR.*/
    spiState->isTransferInProgress = true;
    spiState->transferredByteCount = 0;

    /* Make sure TX data register (or FIFO) is empty. If not, return appropriate
     * error status. This also causes a read of the status
     * register which is required before writing to the data register below.
     */
    if (SPI_HAL_IsTxBuffEmptyPending(base) != 1)
    {
        return kStatus_SPI_TxBufferNotEmpty;
    }


#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    /* If the module/instance contains a FIFO (and if enabled), proceed with FIFO usage for either
     *  8- or 16-bit transfers, else bypass to non-FIFO usage.
     */
    if ((g_spiFifoSize[instance] != 0) && (SPI_HAL_GetFifoCmd(base)))
    {
        /* First fill the FIFO with data */
        SPI_DRV_MasterFillupTxFifo(instance);

        /* If the remaining RX byte count is less than the RX FIFO watermark, enable
         * the TX FIFO empty interrupt. Once the TX FIFO is empty, we are ensured
         * that the transmission is complete and can then drain the RX FIFO.
         * Else, enable the RX FIFO interrupt based on the watermark. In the IRQ
         * handler, another check will be made to see if the remaining RX byte count
         * is less than the RX FIFO watermark.
         */
        if (spiState->remainingReceiveByteCount < g_spiFifoSize[instance])
        {
            SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true); /* TX FIFO empty interrupt */
        }
        else
        {
            SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, true);
        }
    }
    /* Modules that support 16-bit transfers but without FIFO support */
    else
    {
        uint8_t byteToSend = 0;

        /* SPI configured for 16-bit transfers, no FIFO */
        if (bitCount == kSpi16BitMode)
        {
            uint8_t byteToSendLow = 0;
            uint8_t byteToSendHigh = 0;

            if (spiState->sendBuffer)
            {
                byteToSendLow = *(spiState->sendBuffer);
                ++spiState->sendBuffer;

                /* If the extraByte flag is set and these are the last 2 bytes, then skip this */
                if (!((spiState->extraByte) && (spiState->remainingSendByteCount == 2)))
                {
                    byteToSendHigh = *(spiState->sendBuffer);
                    ++spiState->sendBuffer;
                }
            }
            SPI_HAL_WriteDataLow(base, byteToSendLow);
            SPI_HAL_WriteDataHigh(base, byteToSendHigh);

            spiState->remainingSendByteCount -= 2;  /* decrement by 2 */
            spiState->transferredByteCount += 2;  /* increment by 2 */
        }
        /* SPI configured for 8-bit transfers, no FIFO */
        else
        {
            if (spiState->sendBuffer)
            {
                byteToSend = *(spiState->sendBuffer);
                ++spiState->sendBuffer;
            }
            SPI_HAL_WriteDataLow(base, byteToSend);

            --spiState->remainingSendByteCount;
            ++spiState->transferredByteCount;
        }

        /* Only enable the receive interrupt.  This should be ok since SPI is a synchronous
         * protocol, so every RX interrupt we get, we should be ready to send. However, since
         * the SPI has a shift register and data buffer (for transmit and receive), things may not
         * be cycle-to-cycle synchronous. To compensate for this, enabling the RX interrupt only
         * ensures that we do indeed receive data before sending out the next data word. In the
         * ISR we make sure to first check for the RX data buffer full before checking the TX
         * data register empty flag.
         */
        SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, true);
    }

#else /* For SPI modules that do not support 16-bit transfers */

    /* Start the transfer by writing the first byte. If a send buffer was provided, the byte
     * comes from there. Otherwise we just send a zero byte. Note that before writing to the
     * data register, the status register must first be read, which was already performed above.
     */
    uint8_t byteToSend = 0;
    if (spiState->sendBuffer)
    {
        byteToSend = *(spiState->sendBuffer);
        ++spiState->sendBuffer;
    }
    SPI_HAL_WriteData(base, byteToSend);

    --spiState->remainingSendByteCount;
    ++spiState->transferredByteCount;

    /* Only enable the receive interrupt.  This should be ok since SPI is a synchronous
     * protocol, so every RX interrupt we get, we should be ready to send. However, since
     * the SPI has a shift register and data buffer (for transmit and receive), things may not
     * be cycle-to-cycle synchronous. To compensate for this, enabling the RX interrupt only
     * ensures that we do indeed receive data before sending out the next data word. In the
     * ISR we make sure to first check for the RX data buffer full before checking the TX
     * data register empty flag.
     */
    SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, true);
#endif

    return kStatus_SPI_Success;
}