//*****************************************************************************
//
// spi_flash.c - Driver for a SPI flash that supports the "Intel" SPI flash
//               command set, capable of utilizing Bi-SPI and Quad-SPI.
//
// Copyright (c) 2012-2020 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.2.0.295 of the Tiva Utility Library.
//
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_ssi.h"
#include "inc/hw_types.h"
#include "inc/hw_udma.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/ssi.h"
#include "driverlib/udma.h"
#include "utils/spi_flash.h"

//*****************************************************************************
//
//! \addtogroup spi_flash_api
//! @{
//
//*****************************************************************************

//*****************************************************************************
//
// The commands that can be sent to the SPI flash.  This is the "generic"
// command set that is supported by a wide number of SPI flashes.
//
//*****************************************************************************
#define CMD_WRSR                0x01        // Write status register
#define CMD_PP                  0x02        // Page program
#define CMD_READ                0x03        // Read data
#define CMD_WRDI                0x04        // Disable writes
#define CMD_RDSR                0x05        // Read status register
#define CMD_WREN                0x06        // Enable writes
#define CMD_FREAD               0x0b        // Fast read data
#define CMD_SE                  0x20        // Sector erase (4K)
#define CMD_DREAD               0x3b        // 1 in 2 out read data
#define CMD_BE32                0x52        // Block erase (32K)
#define CMD_QREAD               0x6b        // 1 in 4 out read data
#define CMD_RDID                0x9f        // Read JEDEC ID
#define CMD_CE                  0xc7        // Chip erase
#define CMD_BE64                0xd8        // Block erase (64K)

//*****************************************************************************
//
// The states for the SPI flash interrupt handler state machine.
//
//*****************************************************************************
#define STATE_IDLE              0
#define STATE_CMD               1
#define STATE_ADDR1             2
#define STATE_ADDR2             3
#define STATE_ADDR3             4
#define STATE_READ_DUMMY        5
#define STATE_READ_DATA_SETUP   6
#define STATE_READ_DATA         7
#define STATE_READ_DATA_DMA     8
#define STATE_READ_DATA_END     9
#define STATE_WRITE_DATA_SETUP  10
#define STATE_WRITE_DATA        11
#define STATE_WRITE_DATA_DMA    12
#define STATE_WRITE_DATA_END    13

//*****************************************************************************
//
//! Handles SSI module interrupts for the SPI flash driver.
//!
//! \param pState is a pointer to the SPI flash driver instance data.
//!
//! This function handles SSI module interrupts that are generated as a result
//! of SPI flash driver operations.  This must be called by the application in
//! response to the SSI module interrupt when using the SPIFlashxxxNonBlocking
//! APIs.
//!
//! \return Returns \b SPI_FLASH_IDLE if there is no transfer in progress,
//! \b SPI_FLASH_WORKING is the requested transfer is still in progress, or
//! \b SPI_FLASH_DONE if the requested transfer has completed.
//
//*****************************************************************************
uint32_t
SPIFlashIntHandler(tSPIFlashState *pState)
{
    uint32_t ui32Data, ui32Count;

    //
    // Set the write count to four.  This is the maximum number of bytes that
    // will be written into the SSI transmit FIFO in the interrupt handler.
    // Writing more might be possible but makes the latency of handling future
    // SSI interrupt critical to preventing receive FIFO overruns.
    //
    ui32Count = 4;

    //
    // Get the set of asserted and unmasked SSI module interrupts.  Only some
    // of these are directly handled; the others are implicitly handled via the
    // operation of the state machine.
    //
    ui32Data = HWREG(pState->ui32Base + SSI_O_MIS);

    //
    // See if the uDMA transmit complete interrupt has asserted.
    //
    if(ui32Data & SSI_MIS_DMATXMIS)
    {
        //
        // Determine the size of the uDMA transfer based on the number of bytes
        // left to write.
        //
        if(pState->ui32WriteCount > 1024)
        {
            //
            // There are more than 1024 bytes left to transfer, so the uDMA
            // transfer that just completed was for a full 1024 bytes.
            //
            pState->ui32WriteCount -= 1024;

            //
            // If a page program is being performed, then the data buffer
            // pointer needs to be incremented as well.
            //
            if(pState->ui16Cmd == CMD_PP)
            {
                //
                // Increment the data buffer pointer.
                //
                pState->pui8Buffer += 1024;

                //
                // See if there is more than one byte left to transfer.
                //
                if(pState->ui32WriteCount > 1)
                {
                    //
                    // Configure the uDMA to transmit the next portion of the
                    // data buffer.
                    //
                    uDMAChannelTransferSet(pState->ui32TxChannel,
                                           UDMA_MODE_BASIC,
                                           pState->pui8Buffer,
                                           (void *)(pState->ui32Base +
                                                    SSI_O_DR),
                                           (pState->ui32WriteCount > 1024) ?
                                           1024 : pState->ui32WriteCount - 1);

                    //
                    // Enable the uDMA transmit channel.
                    //
                    uDMAChannelEnable(pState->ui32TxChannel);
                }
            }
        }
        else
        {
            //
            // There are 1024 or less bytes left to transfer, so the uDMA
            // transfer that just copmleted was for one less than the remaining
            // transfer count.  If a page program is being performed, then the
            // data buffer pointer needs to be incremented.
            //
            if(pState->ui16Cmd == CMD_PP)
            {
                pState->pui8Buffer += (pState->ui32WriteCount - 1);
            }

            //
            // Set the remaining transfer count to 1.  The final byte will be
            // transferred with PIO since the end of frame flag needs to be set
            // first.
            //
            pState->ui32WriteCount = 1;
        }

        //
        // Clear the uDMA transmit complete interrupt.
        //
        HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC;
    }

    //
    // See if the uDMA receive complete interrupt has asserted.
    //
    if(ui32Data & SSI_MIS_DMARXMIS)
    {
        //
        // Determine the size of the uDMA transfer based on the number of bytes
        // left to read.
        //
        if(pState->ui32ReadCount >= 1024)
        {
            //
            // There are 1024 or more bytes left to transfer, so the uDMA
            // transfer that just completed was for a full 1024 bytes.
            //
            pState->ui32ReadCount -= 1024;
            if(pState->ui32WriteCount != 0)
            {
                pState->ui32WriteCount -= 1024;
            }

            //
            // The data buffer pointer needs to be incremented as well.
            //
            pState->pui8Buffer += 1024;

            //
            // See if there is additional data to transfer.
            //
            if(pState->ui32ReadCount != 0)
            {
                //
                // Configure the transmit uDMA if there is more than one byte
                // left to write.
                //
                if(pState->ui32WriteCount > 1)
                {
                    //
                    // Configure the uDMA to transmit the next portion of the
                    // data buffer.
                    //
                    uDMAChannelTransferSet(pState->ui32TxChannel,
                                           UDMA_MODE_BASIC, pState->pui8Buffer,
                                           (void *)(pState->ui32Base +
                                                    SSI_O_DR),
                                           (pState->ui32WriteCount > 1024) ?
                                           1024 : pState->ui32WriteCount - 1);

                    //
                    // Enable the uDMA transmit channel.
                    //
                    uDMAChannelEnable(pState->ui32TxChannel);
                }

                //
                // Configure the uDMA to receive the next portion of the data
                // buffer.
                //
                uDMAChannelTransferSet(pState->ui32RxChannel, UDMA_MODE_BASIC,
                                       (void *)(pState->ui32Base + SSI_O_DR),
                                       pState->pui8Buffer,
                                       (pState->ui32ReadCount >= 1024) ?
                                       1024 : pState->ui32ReadCount);

                //
                // Enable the uDMA receive channel.
                //
                uDMAChannelEnable(pState->ui32RxChannel);

                //
                // If this is the final receive uDMA buffer and there is a
                // transmit uDMA buffer associated, enable the DMA transmit
                // interrupt.
                //
                if((pState->ui32ReadCount <= 1024) &&
                   (pState->ui32WriteCount > 1))
                {
                    HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC;
                    HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM;
                }
            }
        }
        else
        {
            //
            // There are less than 1024 bytes left to transfer, so the uDMA
            // transfer that copmleted was for the remaining transfer count.
            //
            pState->ui32ReadCount = 0;
        }

        //
        // Clear the uDMA receive complete interrupt.
        //
        HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMARXIC;
    }

    //
    // Drain the receive FIFO is not using uDMA.
    //
    if(!pState->bUseDMA)
    {
        //
        // Loop while there is more data in the receive FIFO and more data to
        // be read.
        //
        while((pState->ui32ReadCount != 0) &&
              (MAP_SSIDataGetNonBlocking(pState->ui32Base, &ui32Data) != 0))
        {
            //
            // Save this byte into the data buffer.
            //
            *(pState->pui8Buffer)++ = ui32Data & 0xff;

            //
            // Decrement the read count.
            //
            pState->ui32ReadCount--;
        }
    }

    //
    // The SPI flash state machine.  Loop forever; the state machine will
    // explicitly return to the caller when there is no further work that can
    // be done without stalling.
    //
    while(1)
    {
        //
        // Determine the current state.
        //
        switch(pState->ui16State)
        {
            //
            // The state machine is idle.
            //
            case STATE_IDLE:
            {
                //
                // Return indicating that the state machine is idle.  This
                // should never happen since no further interrupts should occur
                // once the transfer has completed and the state machine goes
                // into the idle state.
                //
                return(SPI_FLASH_IDLE);
            }

            //
            // The state machine is in the command state.
            //
            case STATE_CMD:
            {
                //
                // Set the SSI module into write-only mode.
                //
                MAP_SSIAdvModeSet(pState->ui32Base, SSI_ADV_MODE_WRITE);

                //
                // Attempt to write the command byte into the FIFO.
                //
                if(ui32Count == 0)
                {
                    return(SPI_FLASH_WORKING);
                }
                if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                             pState->ui16Cmd) == 0)
                {
                    //
                    // The command byte could not be written, so return
                    // indicating that the transfer is still in progress.
                    //
                    return(SPI_FLASH_WORKING);
                }
                else
                {
                    //
                    // The command byte has been written, so move to the first
                    // address byte state.
                    //
                    pState->ui16State = STATE_ADDR1;

                    //
                    // Decrement the count of bytes that have been written.
                    //
                    ui32Count--;
                }

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the first address byte state.
            //
            case STATE_ADDR1:
            {
                //
                // Attempt to write the first address byte into the FIFO.
                //
                if(ui32Count == 0)
                {
                    return(SPI_FLASH_WORKING);
                }
                if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                             (pState->ui32Addr >> 16) &
                                             0xff) == 0)
                {
                    //
                    // The first address byte could not be written, so return
                    // indicating that the transfer is still in progress.
                    //
                    return(SPI_FLASH_WORKING);
                }
                else
                {
                    //
                    // The first address byte has been written, so move to the
                    // second address byte state.
                    //
                    pState->ui16State = STATE_ADDR2;

                    //
                    // Decrement the count of bytes that have been written.
                    //
                    ui32Count--;
                }

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the second address byte state.
            //
            case STATE_ADDR2:
            {
                //
                // Attempt to write the second address byte into the FIFO.
                //
                if(ui32Count == 0)
                {
                    return(SPI_FLASH_WORKING);
                }
                if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                             (pState->ui32Addr >> 8) & 0xff) ==
                   0)
                {
                    //
                    // The second address byte could not be written, so return
                    // indicating that the transfer is still in progress.
                    //
                    return(SPI_FLASH_WORKING);
                }
                else
                {
                    //
                    // The second address byte has been written, so move to the
                    // third address byte state.
                    //
                    pState->ui16State = STATE_ADDR3;

                    //
                    // Decrement the count of bytes that have been written.
                    //
                    ui32Count--;
                }

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the third address byte state.
            //
            case STATE_ADDR3:
            {
                //
                // Attempt to write the third address byte into the FIFO.
                //
                if(ui32Count == 0)
                {
                    return(SPI_FLASH_WORKING);
                }
                if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                             pState->ui32Addr & 0xff) == 0)
                {
                    //
                    // The third address byte could not be written, so return
                    // indicating that the transfer is still in progress.
                    //
                    return(SPI_FLASH_WORKING);
                }
                else
                {
                    //
                    // The third address byte has been written, so determine
                    // the next state based on the command byte.
                    //
                    if(pState->ui16Cmd == CMD_PP)
                    {
                        //
                        // A page program is being performed, so move to the
                        // write data setup state.
                        //
                        pState->ui16State = STATE_WRITE_DATA_SETUP;
                    }
                    else if(pState->ui16Cmd == CMD_READ)
                    {
                        //
                        // A read is being performed, so move to the read data
                        // setup state.
                        //
                        pState->ui16State = STATE_READ_DATA_SETUP;
                    }
                    else
                    {
                        //
                        // The other forms of read (fast read, dual read, and
                        // quad read) all require a dummy byte.  Move to the
                        // dummy byte state.
                        //
                        pState->ui16State = STATE_READ_DUMMY;
                    }

                    //
                    // Decrement the count of bytes that have been written.
                    //
                    ui32Count--;
                }

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the dummy byte state.
            //
            case STATE_READ_DUMMY:
            {
                //
                // Attempt to write the dummy byte into the FIFO.
                //
                if(ui32Count == 0)
                {
                    return(SPI_FLASH_WORKING);
                }
                if(MAP_SSIDataPutNonBlocking(pState->ui32Base, 0) == 0)
                {
                    //
                    // THe dummy byte could not be written, so return
                    // indicating that the transfer is still in progress.
                    //
                    return(SPI_FLASH_WORKING);
                }
                else
                {
                    //
                    // The dummy byte has been written, so move to the read
                    // data setup state.
                    //
                    pState->ui16State = STATE_READ_DATA_SETUP;

                    //
                    // Decrement the count of bytes that have been written.
                    //
                    ui32Count--;
                }

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the read data setup state.
            //
            case STATE_READ_DATA_SETUP:
            {
                //
                // Set the SSI module into the appropriate mode based on the
                // command byte.
                //
                if(pState->ui16Cmd == CMD_DREAD)
                {
                    //
                    // Bi-SPI read mode is used for the dual read command.
                    //
                    MAP_SSIAdvModeSet(pState->ui32Base, SSI_ADV_MODE_BI_READ);
                }
                else if(pState->ui16Cmd == CMD_QREAD)
                {
                    //
                    // Quad-SPI read mode is used for the quad read command.
                    //
                    MAP_SSIAdvModeSet(pState->ui32Base,
                                      SSI_ADV_MODE_QUAD_READ);
                }
                else
                {
                    //
                    // Advanced read/write mode is used for the read and fast
                    // read commands.
                    //
                    MAP_SSIAdvModeSet(pState->ui32Base,
                                      SSI_ADV_MODE_READ_WRITE);
                }

                //
                // See if a single byte is being transferred.
                //
                if(pState->ui32ReadCount == 1)
                {
                    //
                    // Disable the use of uDMA.
                    //
                    pState->bUseDMA = false;

                    //
                    // Move to the read data end state to transfer the single
                    // byte.  This uses PIO even if uDMA has been requested.
                    //
                    pState->ui16State = STATE_READ_DATA_END;
                }

                //
                // See if uDMA has been requested for this transfer.
                //
                else if(!pState->bUseDMA || (pState->ui32ReadCount < 4))
                {
                    //
                    // Disable the use of uDMA.
                    //
                    pState->bUseDMA = false;

                    //
                    // Move to the read data state.
                    //
                    pState->ui16State = STATE_READ_DATA;
                }

                //
                // This transfer should use uDMA.
                //
                else
                {
                    //
                    // If the transfer is larger than 1024 bytes, enable the
                    // uDMA receive complete interrupt which will be used to
                    // move to the next block of the transfer.  Otherwise,
                    // enable the uDMA transmit complete interrupt which will
                    // be used to complete the transaction.
                    //
                    if(pState->ui32ReadCount > 1024)
                    {
                        HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMARXIM;
                    }
                    else
                    {
                        HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM;
                    }

                    //
                    // Disable the uDMA channels.
                    //
                    HWREG(UDMA_ENACLR) = ((1 << pState->ui32TxChannel) |
                                          (1 << pState->ui32RxChannel));

                    //
                    // Configure the attributes for the transmit uDMA channel.
                    //
                    HWREG(UDMA_USEBURSTSET) = ((1 << pState->ui32TxChannel) |
                                               (1 << pState->ui32RxChannel));
                    HWREG(UDMA_ALTCLR) = ((1 << pState->ui32TxChannel) |
                                          (1 << pState->ui32RxChannel));
                    HWREG(UDMA_PRIOCLR) = 1 << pState->ui32TxChannel;
                    HWREG(UDMA_PRIOSET) = 1 << pState->ui32RxChannel;
                    HWREG(UDMA_REQMASKCLR) = ((1 << pState->ui32TxChannel) |
                                              (1 << pState->ui32RxChannel));

                    //
                    // Configure the control parameters of the uDMA channels.
                    //
                    uDMAChannelControlSet(pState->ui32TxChannel,
                                          UDMA_SRC_INC_NONE |
                                          UDMA_DST_INC_NONE |
                                          UDMA_SIZE_8 | UDMA_ARB_2);
                    uDMAChannelControlSet(pState->ui32RxChannel,
                                          UDMA_SRC_INC_NONE |
                                          UDMA_DST_INC_8 |
                                          UDMA_SIZE_8 | UDMA_ARB_4);

                    //
                    // Configure the uDMA receive channel to transfer the first
                    // portion of the data buffer.
                    //
                    uDMAChannelTransferSet(pState->ui32RxChannel,
                                           UDMA_MODE_BASIC,
                                           (void *)(pState->ui32Base +
                                                    SSI_O_DR),
                                           pState->pui8Buffer,
                                           (pState->ui32ReadCount >= 1024) ?
                                           1024 : pState->ui32ReadCount);

                    //
                    // Enable the uDMA receive channel.
                    //
                    uDMAChannelEnable(pState->ui32RxChannel);

                    //
                    // Configure the uDMA channel to transfer the dummy bytes
                    // for the first portion of the data buffer.  The last
                    // dummy byte will not be included since it must be treated
                    // special.
                    //
                    uDMAChannelTransferSet(pState->ui32TxChannel,
                                           UDMA_MODE_BASIC,
                                           pState->pui8Buffer,
                                           (void *)(pState->ui32Base +
                                                    SSI_O_DR),
                                           (pState->ui32WriteCount > 1024) ?
                                           1024 : pState->ui32WriteCount - 1);

                    //
                    // Enable the uDMA transmit channel.
                    //
                    uDMAChannelEnable(pState->ui32TxChannel);

                    //
                    // Clear any previously pending uDMA completion interrupt.
                    //
                    HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMARXIC;

                    //
                    // Enable uDMA transmit and receive in the SSI module.
                    //
                    MAP_SSIDMAEnable(pState->ui32Base,
                                     SSI_DMA_TX | SSI_DMA_RX);

                    //
                    // Move to the uDMA data read state.
                    //
                    pState->ui16State = STATE_READ_DATA_DMA;
                }

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the read data state.
            //
            case STATE_READ_DATA:
            {
                //
                // Loop while there is more than one byte left to write.
                //
                while(pState->ui32WriteCount != 1)
                {
                    //
                    // Dummy bytes are written into the FIFO in order to
                    // trigger the read operation.  Attempt to write another
                    // dummy byte into the FIFO.
                    //
                    if(ui32Count == 0)
                    {
                        return(SPI_FLASH_WORKING);
                    }
                    if(MAP_SSIDataPutNonBlocking(pState->ui32Base, 0) == 0)
                    {
                        //
                        // The dummy byte could not be written, so return
                        // indicating that the transfer is still in progress.
                        //
                        return(SPI_FLASH_WORKING);
                    }

                    //
                    // Decrement the count of dummy bytes to write.
                    //
                    pState->ui32WriteCount--;

                    //
                    // Decrement the count of bytes that have been written.
                    //
                    ui32Count--;
                }

                //
                // Move to the read data end state.
                //
                pState->ui16State = STATE_READ_DATA_END;

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the uDMA read data state.
            //
            case STATE_READ_DATA_DMA:
            {
                //
                // See if the write count is greater than one.
                //
                if(pState->ui32WriteCount > 1)
                {
                    //
                    // Return indicating that the transfer is still in
                    // progress.
                    //
                    return(SPI_FLASH_WORKING);
                }

                //
                // Disable uDMA transmit in the SSI module.
                //
                MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_TX);

                //
                // Enable the uDMA receive done and FIFO transmit interrupt.
                //
                HWREG(pState->ui32Base + SSI_O_IM) =
                    SSI_IM_DMARXIM | SSI_IM_TXIM;

                //
                // Move to the read data end state.
                //
                pState->ui16State = STATE_READ_DATA_END;

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the data read end state.
            //
            case STATE_READ_DATA_END:
            {
                //
                // See if the final dummy byte still needs to be written.
                //
                if(pState->ui32WriteCount != 0)
                {
                    //
                    // Attempt to write the final dummy byte into the FIFO and
                    // mark it as the end of the frame.
                    //
                    if(ui32Count == 0)
                    {
                        return(SPI_FLASH_WORKING);
                    }
                    if(MAP_SSIAdvDataPutFrameEndNonBlocking(pState->ui32Base,
                                                            0) == 0)
                    {
                        //
                        // The dummy byte could not be written, so return
                        // indicating that the transfer is still in progress.
                        //
                        return(SPI_FLASH_WORKING);
                    }

                    //
                    // The write portion of the transfer has completed.
                    //
                    pState->ui32WriteCount = 0;

                    //
                    // Disable the transmit interrupt now that the write
                    // write portion of the transfer has completed.
                    //
                    HWREG(pState->ui32Base + SSI_O_IM) &= ~(SSI_IM_TXIM);
                }

                //
                // Return indicating that the transfer is still in progress if
                // there are still data bytes to be read.
                //
                if(pState->ui32ReadCount != 0)
                {
                    return(SPI_FLASH_WORKING);
                }

                //
                // Disable uDMA receive in the SSI module.
                //
                MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_RX);

                //
                // The transfer is complete, so disable all interrupts.
                //
                HWREG(pState->ui32Base + SSI_O_IM) = 0;

                //
                // Move to the idle state.
                //
                pState->ui16State = STATE_IDLE;

                //
                // Return indicating that the transfer has completed.
                //
                return(SPI_FLASH_DONE);
            }

            //
            // The state machine is in the write data setup state.
            //
            case STATE_WRITE_DATA_SETUP:
            {
                //
                // See if a single data byte is being transferred.
                //
                if(pState->ui32WriteCount == 1)
                {
                    //
                    // Disable the use of uDMA.
                    //
                    pState->bUseDMA = false;

                    //
                    // Move to the write data end state to transfer the single
                    // byte.  This uses PIO even if uDMA has been requested.
                    //
                    pState->ui16State = STATE_WRITE_DATA_END;
                }

                //
                // See if uDMA has been requested for this transfer.
                //
                else if(!pState->bUseDMA || (pState->ui32WriteCount < 4))
                {
                    //
                    // Disable the use of uDMA.
                    //
                    pState->bUseDMA = false;

                    //
                    // uDMA is not being used, so move to the write data state.
                    //
                    pState->ui16State = STATE_WRITE_DATA;
                }

                //
                // This transfer should use uDMA.
                //
                else
                {
                    //
                    // Enable the uDMA transmit complete interrupt.
                    //
                    HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_DMATXIM;

                    //
                    // Disable the transmit uDMA channel.
                    //
                    HWREG(UDMA_ENACLR) = 1 << pState->ui32TxChannel;

                    //
                    // Configure the attributes for the transmit uDMA channel.
                    //
                    HWREG(UDMA_USEBURSTSET) = 1 << pState->ui32TxChannel;
                    HWREG(UDMA_ALTCLR) = 1 << pState->ui32TxChannel;
                    HWREG(UDMA_PRIOCLR) = 1 << pState->ui32TxChannel;
                    HWREG(UDMA_REQMASKCLR) = 1 << pState->ui32TxChannel;

                    //
                    // Configure the control parameters of the uDMA channel.
                    //
                    uDMAChannelControlSet(pState->ui32TxChannel,
                                          UDMA_SRC_INC_8 |
                                          UDMA_DST_INC_NONE |
                                          UDMA_SIZE_8 | UDMA_ARB_4);

                    //
                    // Configure the uDMA channel to transfer the next portion
                    // of the data buffer.  The last byte in the buffer will
                    // not be included since it must be treated special.
                    //
                    uDMAChannelTransferSet(pState->ui32TxChannel,
                                           UDMA_MODE_BASIC,
                                           pState->pui8Buffer,
                                           (void *)(pState->ui32Base +
                                                    SSI_O_DR),
                                           (pState->ui32WriteCount > 1024) ?
                                           1024 : pState->ui32WriteCount - 1);

                    //
                    // Enable the uDMA transmit channel.
                    //
                    uDMAChannelEnable(pState->ui32TxChannel);
                    HWREG(pState->ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC;

                    //
                    // Enable uDMA in the SSI module.
                    //
                    MAP_SSIDMAEnable(pState->ui32Base, SSI_DMA_TX);

                    //
                    // Move to the uDMA data write state.
                    //
                    pState->ui16State = STATE_WRITE_DATA_DMA;
                }

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the write data state.
            //
            case STATE_WRITE_DATA:
            {
                //
                // Loop while there is more than one byte left to write.
                //
                while(pState->ui32WriteCount != 1)
                {
                    //
                    // Attempt to write the next data byte into the FIFO.
                    //
                    if(ui32Count == 0)
                    {
                        return(SPI_FLASH_WORKING);
                    }
                    if(MAP_SSIDataPutNonBlocking(pState->ui32Base,
                                                 *(pState->pui8Buffer)) == 0)
                    {
                        //
                        // The next data byte could not be written, so return
                        // indicating that the transfer is still in progress.
                        //
                        return(SPI_FLASH_WORKING);
                    }

                    //
                    // Increment the buffer pointer and decrement the byte
                    // count.
                    //
                    pState->pui8Buffer++;
                    pState->ui32WriteCount--;

                    //
                    // Decrement the count of bytes that have been written.
                    //
                    ui32Count--;
                }

                //
                // Move to the write data end state.
                //
                pState->ui16State = STATE_WRITE_DATA_END;

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the uDMA write data state.
            //
            case STATE_WRITE_DATA_DMA:
            {
                //
                // See if the write count is greater than one.
                //
                if(pState->ui32WriteCount > 1)
                {
                    //
                    // Return indicating that the transfer is still in
                    // progress.
                    //
                    return(SPI_FLASH_WORKING);
                }

                //
                // Disable uDMA in the SSI module.
                //
                MAP_SSIDMADisable(pState->ui32Base, SSI_DMA_TX);

                //
                // Disable the uDMA transmit complete interrupt and enable the
                // FIFO interrupt.
                //
                HWREG(pState->ui32Base + SSI_O_IM) = SSI_IM_TXIM;

                //
                // Move to the write data end state.
                //
                pState->ui16State = STATE_WRITE_DATA_END;

                //
                // Done with this state.
                //
                break;
            }

            //
            // The state machine is in the write data end state.
            //
            case STATE_WRITE_DATA_END:
            {
                //
                // Attempt to write the final data byte into the FIFO.
                //
                if(MAP_SSIAdvDataPutFrameEndNonBlocking(pState->ui32Base,
                                                      *(pState->pui8Buffer)) ==
                   0)
                {
                    //
                    // The final data byte could not be written, so return
                    // indicating that the transfer is still in progress.
                    //
                    return(SPI_FLASH_WORKING);
                }

                //
                // The transfer is complete, so disable all interrupts.
                //
                HWREG(pState->ui32Base + SSI_O_IM) = 0;

                //
                // Move to the idle state.
                //
                pState->ui16State = STATE_IDLE;

                //
                // Return indicating that the transfer has completed.
                //
                return(SPI_FLASH_DONE);
            }
        }
    }
}

//*****************************************************************************
//
//! Initializes the SPI flash driver.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Clock is the rate of the clock supplied to the SSI module.
//! \param ui32BitRate is the SPI clock rate.
//!
//! This function configures the SSI module for use by the SPI flash driver.
//! The SSI module will be placed into the correct mode of operation to allow
//! communication with the SPI flash.  This function must be called prior to
//! calling the remaining SPI flash driver APIs.  It can be called at a later
//! point to reconfigure the SSI module, such as to increase the SPI clock rate
//! once it has been determined that it is safe to use a higher speed clock.
//!
//! It is the responsibility of the caller to enable the SSI module and
//! configure the pins that it will utilize.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashInit(uint32_t ui32Base, uint32_t ui32Clock, uint32_t ui32BitRate)
{
    //
    // Configure the SPI module.
    //
    MAP_SSIConfigSetExpClk(ui32Base, ui32Clock, SSI_FRF_MOTO_MODE_0,
                           SSI_MODE_MASTER, ui32BitRate, 8);

    //
    // Enable the advanced mode of operation, defaulting to read/write mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);

    //
    // Enable the frame hold feature.
    //
    MAP_SSIAdvFrameHoldEnable(ui32Base);

    //
    // Enable the SPI module.
    //
    MAP_SSIEnable(ui32Base);
}

//*****************************************************************************
//
//! Writes the SPI flash status register.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui8Status is the value to write to the status register.
//!
//! This function writes the SPI flash status register.  This uses the 0x01 SPI
//! flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashWriteStatus(uint32_t ui32Base, uint8_t ui8Status)
{
    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the write status register command.
    //
    MAP_SSIDataPut(ui32Base, CMD_WRSR);

    //
    // Send the new status register value, marking this byte as the end of the
    // frame.
    //
    MAP_SSIAdvDataPutFrameEnd(ui32Base, ui8Status);
}

//*****************************************************************************
//
//! Programs the SPI flash.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to be programmed.
//! \param pui8Data is a pointer to the data to be programmed.
//! \param ui32Count is the number of bytes to be programmed.
//!
//! This function programs data into the SPI flash, using PIO mode.  This
//! function will not return until the entire program command has been written
//! into the SSI transmit FIFO.  This uses the 0x02 SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashPageProgram(uint32_t ui32Base, uint32_t ui32Addr,
                    const uint8_t *pui8Data, uint32_t ui32Count)
{
    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the page program command.
    //
    MAP_SSIDataPut(ui32Base, CMD_PP);

    //
    // Send the address of the first byte to program.
    //
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
    MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);

    //
    // Loop while there is more than one data byte left to be sent.
    //
    while(ui32Count-- != 1)
    {
        //
        // Send the next data byte.
        //
        MAP_SSIDataPut(ui32Base, *pui8Data++);
    }

    //
    // Send the last data byte, marking it as the end of the frame.
    //
    MAP_SSIAdvDataPutFrameEnd(ui32Base, *pui8Data);
}

//*****************************************************************************
//
//! Programs the SPI flash in the background.
//!
//! \param pState is a pointer to the SPI flash state structure.
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to be programmed.
//! \param pui8Data is a pointer to the data to be programmed.
//! \param ui32Count is the number of bytes to be programmed.
//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
//! module.
//!
//! This function programs data into the SPI flash, using either interrupts or
//! uDMA to transfer the data.  This function will return immediately and send
//! the data in the background.  In order for this to complete successfully,
//! several conditions must be satisfied:
//!
//! - Prior to calling this function:
//!   - The SSI module must be enabled in SysCtl.
//!   - The SSI pins must be configured for use by the SSI module.
//!   - The SSI module interrupt must be enabled in NVIC.
//!   - The uDMA module must be enabled in SysCtl and the control table set (if
//!     using uDMA).
//!   - The uDMA channels must be assigned to the SSI module.
//!
//! - After calling this function:
//!   - The interrupt handler for the SSI module must call
//!     SPIFlashIntHandler(), passing the same pState structure pointer that
//!     was supplied to this function.
//!   - No other SPI flash operation can be called until this operation has
//!     completed.
//!
//! Completion of the programming operation is indicated when
//! SPIFlashIntHandler() returns \b SPI_FLASH_DONE.
//!
//! Like SPIFlashPageProgram(), this uses the 0x02 SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashPageProgramNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                               uint32_t ui32Addr, const uint8_t *pui8Data,
                               uint32_t ui32Count, bool bUseDMA,
                               uint32_t ui32TxChannel)
{
    //
    // Save the parameters of this program operation to the state structure.
    //
    pState->ui32Base = ui32Base;
    pState->ui16Cmd = CMD_PP;
    pState->ui16State = STATE_CMD;
    pState->ui32Addr = ui32Addr;
    pState->pui8Buffer = (uint8_t *)pui8Data;
    pState->ui32ReadCount = 0;
    pState->ui32WriteCount = ui32Count;
    pState->bUseDMA = bUseDMA;
    pState->ui32TxChannel = ui32TxChannel & 0x1f;

    //
    // Enable the SSI transmit interrupt.  This will start the transfer.  If
    // uDMA is being used, the uDMA-related interrupt will be enabled at the
    // appropriate time by the interrupt handler.
    //
    HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC;
    HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM;
}

//*****************************************************************************
//
//! Reads data from the SPI flash.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to read.
//! \param pui8Data is a pointer to the data buffer to into which to read the
//! data.
//! \param ui32Count is the number of bytes to read.
//!
//! This function reads data from the SPI flash, using PIO mode.  This function
//! will not return until the read has completed.  This uses the 0x03 SPI flash
//! command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data,
             uint32_t ui32Count)
{
    uint32_t ui32Trash;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
    {
    }

    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the read command.
    //
    MAP_SSIDataPut(ui32Base, CMD_READ);

    //
    // Send the address of the first byte to read.
    //
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
    MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);

    //
    // Set the SSI module into read/write mode.  In this mode, dummy writes are
    // required in order to make the transfer occur; the SPI flash will ignore
    // the data.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);

    //
    // See if there is a single byte to be read.
    //
    if(ui32Count == 1)
    {
        //
        // Perform a single dummy write, marking it as the end of the frame.
        //
        MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
    }
    else
    {
        //
        // Perform a dummy write to prime the loop.
        //
        MAP_SSIDataPut(ui32Base, 0);

        //
        // Loop while there is more than one byte left to be read.
        //
        while(--ui32Count != 1)
        {
            //
            // Perform a dummy write to keep the transmit FIFO from going
            // empty.
            //
            MAP_SSIDataPut(ui32Base, 0);

            //
            // Read the next data byte from the receive FIFO and place it into
            // the data buffer.
            //
            MAP_SSIDataGet(ui32Base, &ui32Addr);
            *pui8Data++ = ui32Addr & 0xff;
        }

        //
        // Perform the final dummy write, marking it as the end of the frame.
        //
        MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);

        //
        // Read the next data byte from the receive FIFO and place it into the
        // data buffer.
        //
        MAP_SSIDataGet(ui32Base, &ui32Addr);
        *pui8Data++ = ui32Addr & 0xff;
    }

    //
    // Read the final data byte from the receive FIFO and place it into the
    // data buffer.
    //
    MAP_SSIDataGet(ui32Base, &ui32Addr);
    *pui8Data++ = ui32Addr & 0xff;
}

//*****************************************************************************
//
//! Reads data from the SPI flash in the background.
//!
//! \param pState is a pointer to the SPI flash state structure.
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to read.
//! \param pui8Data is a pointer to the data buffer to into which to read the
//! data.
//! \param ui32Count is the number of bytes to read.
//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
//! module.
//! \param ui32RxChannel is the uDMA channel to be used for reading from the
//! SSI module.
//!
//! This function reads data from the SPI flash, using either interrupts or
//! uDMA to transfer the data.  This function will return immediately and read
//! the data in the background.  In order for this to complete successfully,
//! several conditions must be satisfied:
//!
//! - Prior to calling this function:
//!   - The SSI module must be enabled in SysCtl.
//!   - The SSI pins must be configured for use by the SSI module.
//!   - The SSI module interrupt must be enabled in NVIC.
//!   - The uDMA module must be enabled in SysCtl and the control table set (if
//!     using uDMA).
//!   - The uDMA channels must be assigned to the SSI module.
//!
//! - After calling this function:
//!   - The interrupt handler for the SSI module must call
//!     SPIFlashIntHandler(), passing the same pState structure pointer that
//!     was supplied to this function.
//!   - No other SPI flash operation can be called until this operation has
//!     completed.
//!
//! Completion of the read operation is indicated when SPIFlashIntHandler()
//! returns \b SPI_FLASH_DONE.
//!
//! Like SPIFlashRead(), this uses the 0x03 SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                        uint32_t ui32Addr, uint8_t *pui8Data,
                        uint32_t ui32Count, bool bUseDMA,
                        uint32_t ui32TxChannel, uint32_t ui32RxChannel)
{
    uint32_t ui32Trash;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
    {
    }

    //
    // Save the parameters of this read operation to the state structure.
    //
    pState->ui32Base = ui32Base;
    pState->ui16Cmd = CMD_READ;
    pState->ui16State = STATE_CMD;
    pState->ui32Addr = ui32Addr;
    pState->pui8Buffer = pui8Data;
    pState->ui32ReadCount = ui32Count;
    pState->ui32WriteCount = ui32Count;
    pState->bUseDMA = bUseDMA;
    pState->ui32TxChannel = ui32TxChannel & 0x1f;
    pState->ui32RxChannel = ui32RxChannel & 0x1f;

    //
    // Enable the SSI transmit and receive interrupts.  This will start the
    // transfer.  If uDMA is being used, the uDMA-related interrupts will be
    // enabled at the appropriate time by the interrupt handler.
    //
    HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC;
    HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM;
}

//*****************************************************************************
//
//! Disables SPI flash write operations.
//!
//! \param ui32Base is the SSI module base address.
//!
//! This function sets the SPI flash to disallow program and erase operations.
//! This uses the 0x04 SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashWriteDisable(uint32_t ui32Base)
{
    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the write disable command, marking this byte as the end of the
    // frame.
    //
    MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_WRDI);
}

//*****************************************************************************
//
//! Reads the SPI flash status register.
//!
//! \param ui32Base is the SSI module base address.
//!
//! This function reads the SPI flash status register.  This uses the 0x05 SPI
//! flash command.
//!
//! \return Returns the value of the SPI flash status register.
//
//*****************************************************************************
uint8_t
SPIFlashReadStatus(uint32_t ui32Base)
{
    uint32_t ui32Data;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Data) != 0)
    {
    }

    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the write status register command.
    //
    MAP_SSIDataPut(ui32Base, CMD_RDSR);

    //
    // Set the SSI module into read/write mode.  In this mode, dummy writes are
    // required in order to make the transfer occur; the SPI flash will ignore
    // the data.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);

    //
    // Perform a single dummy write, marking it as the end of the frame.
    //
    MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);

    //
    // Read the value of the status register.
    //
    MAP_SSIDataGet(ui32Base, &ui32Data);

    //
    // Return the status register value.
    //
    return(ui32Data & 0xff);
}

//*****************************************************************************
//
//! Enables SPI flash write operations.
//!
//! \param ui32Base is the SSI module base address.
//!
//! This function sets the SPI flash to allow program and erase operations.
//! This must be done prior to each SPI flash program or erase operation; the
//! SPI flash will automatically disable program and erase operations once a
//! program or erase operation has completed.  This uses the 0x06 SPI flash
//! command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashWriteEnable(uint32_t ui32Base)
{
    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the write enable command, marking this byte as the end of the
    // frame.
    //
    MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_WREN);
}

//*****************************************************************************
//
//! Reads data from the SPI flash using the fast read command.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to read.
//! \param pui8Data is a pointer to the data buffer to into which to read the
//! data.
//! \param ui32Count is the number of bytes to read.
//!
//! This function reads data from the SPI flash with the fast read command,
//! using PIO mode.  The fast read command allows the SPI flash to be read at
//! a higher SPI clock rate because of the addition of a dummy cycle during the
//! command setup.  This function will not return until the read has completed.
//! This uses the 0x0b SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashFastRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data,
                 uint32_t ui32Count)
{
    uint32_t ui32Trash;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
    {
    }

    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the fast read command.
    //
    MAP_SSIDataPut(ui32Base, CMD_FREAD);

    //
    // Send the address of the first byte to read.
    //
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
    MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);

    //
    // Send a dummy byte.
    //
    MAP_SSIDataPut(ui32Base, 0);

    //
    // Set the SSI module into read/write mode.  In this mode, dummy writes are
    // required in order to make the transfer occur; the SPI flash will ignore
    // the data.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);

    //
    // See if there is a single byte to be read.
    //
    if(ui32Count == 1)
    {
        //
        // Perform a single dummy write, marking it as the end of the frame.
        //
        MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
    }
    else
    {
        //
        // Perform a dummy write to prime the loop.
        //
        MAP_SSIDataPut(ui32Base, 0);

        //
        // Loop while there is more than one byte left to be read.
        //
        while(--ui32Count != 1)
        {
            //
            // Perform a dummy write to keep the transmit FIFO from going
            // empty.
            //
            MAP_SSIDataPut(ui32Base, 0);

            //
            // Read the next data byte from the receive FIFO and place it into
            // the data buffer.
            //
            MAP_SSIDataGet(ui32Base, &ui32Addr);
            *pui8Data++ = ui32Addr & 0xff;
        }

        //
        // Perform the final dummy write, marking it as the end of the frame.
        //
        MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);

        //
        // Read the next data byte from the receive FIFO and place it into the
        // data buffer.
        //
        MAP_SSIDataGet(ui32Base, &ui32Addr);
        *pui8Data++ = ui32Addr & 0xff;
    }

    //
    // Read the final data byte from the receive FIFO and place it into the
    // data buffer.
    //
    MAP_SSIDataGet(ui32Base, &ui32Addr);
    *pui8Data++ = ui32Addr & 0xff;
}

//*****************************************************************************
//
//! Reads data from the SPI flash using the fast read command in the
//! background.
//!
//! \param pState is a pointer to the SPI flash state structure.
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to read.
//! \param pui8Data is a pointer to the data buffer to into which to read the
//! data.
//! \param ui32Count is the number of bytes to read.
//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
//! module.
//! \param ui32RxChannel is the uDMA channel to be used for reading from the
//! SSI module.
//!
//! This function reads data from the SPI flash with the fast read command,
//! using either interrupts or uDMA to transfer the data.  The fast read
//! command allows the SPI flash to be read at a higher SPI clock rate because
//! of the addition of a dummy cycle during the command setup.  This function
//! will return immediately and read the data in the background.  In order for
//! this to complete successfully, several conditions must be satisfied:
//!
//! - Prior to calling this function:
//!   - The SSI module must be enabled in SysCtl.
//!   - The SSI pins must be configured for use by the SSI module.
//!   - The SSI module interrupt must be enabled in NVIC.
//!   - The uDMA module must be enabled in SysCtl and the control table set (if
//!     using uDMA).
//!   - The uDMA channels must be assigned to the SSI module.
//!
//! - After calling this function:
//!   - The interrupt handler for the SSI module must call
//!     SPIFlashIntHandler(), passing the same pState structure pointer that
//!     was supplied to this function.
//!   - No other SPI flash operation can be called until this operation has
//!     completed.
//!
//! Completion of the read operation is indicated when SPIFlashIntHandler()
//! returns \b SPI_FLASH_DONE.
//!
//! Like SPIFlashFastRead(), this uses the 0x0b SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashFastReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                            uint32_t ui32Addr, uint8_t *pui8Data,
                            uint32_t ui32Count, bool bUseDMA,
                            uint32_t ui32TxChannel, uint32_t ui32RxChannel)
{
    uint32_t ui32Trash;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
    {
    }

    //
    // Save the parameters of this read operation to the state structure.
    //
    pState->ui32Base = ui32Base;
    pState->ui16Cmd = CMD_FREAD;
    pState->ui16State = STATE_CMD;
    pState->ui32Addr = ui32Addr;
    pState->pui8Buffer = pui8Data;
    pState->ui32ReadCount = ui32Count;
    pState->ui32WriteCount = ui32Count;
    pState->bUseDMA = bUseDMA;
    pState->ui32TxChannel = ui32TxChannel & 0x1f;
    pState->ui32RxChannel = ui32RxChannel & 0x1f;

    //
    // Enable the SSI transmit and receive interrupts.  This will start the
    // transfer.  If uDMA is being used, the uDMA-related interrupts will be
    // enabled at the appropriate time by the interrupt handler.
    //
    HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC;
    HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM;
}

//*****************************************************************************
//
//! Erases a 4 KB sector of the SPI flash.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to erase.
//!
//! This function erases a sector of the SPI flash.  Each sector is 4 KB with a
//! 4 KB alignment; the SPI flash will ignore the lower ten bits of the address
//! provided.  The sector erase command is issued by this function;
//! SPIFlashReadStatus() must be used to query the SPI flash to determine when
//! the sector erase operation has completed.  This uses the 0x20 SPI flash
//! command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashSectorErase(uint32_t ui32Base, uint32_t ui32Addr)
{
    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the sector erase command.
    //
    MAP_SSIDataPut(ui32Base, CMD_SE);

    //
    // Send the address of the sector to be erased, marking the last byte of
    // the address as the end of the frame.
    //
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
    MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff);
}

//*****************************************************************************
//
//! Reads data from the SPI flash using Bi-SPI.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to read.
//! \param pui8Data is a pointer to the data buffer to into which to read the
//! data.
//! \param ui32Count is the number of bytes to read.
//!
//! This function reads data from the SPI flash with Bi-SPI, using PIO mode.
//! This function will not return until the read has completed.  This uses the
//! 0x3b SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashDualRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data,
                 uint32_t ui32Count)
{
    uint32_t ui32Trash;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
    {
    }

    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the dual read command.
    //
    MAP_SSIDataPut(ui32Base, CMD_DREAD);

    //
    // Send the address of the first byte to read.
    //
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
    MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);

    //
    // Send a dummy byte.
    //
    MAP_SSIDataPut(ui32Base, 0);

    //
    // Set the SSI module into Bi-SPI read mode.  In this mode, dummy writes
    // are required in order to make the transfer occur; the SSI module will
    // ignore the data (the SPI flash will never see the dummy data since
    // Bi-SPI read mode is a uni-directional input mode).
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_BI_READ);

    //
    // See if there is a single byte to be read.
    //
    if(ui32Count == 1)
    {
        //
        // Perform a single dummy write, marking it as the end of the frame.
        //
        MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
    }
    else
    {
        //
        // Perform a dummy write to prime the loop.
        //
        MAP_SSIDataPut(ui32Base, 0);

        //
        // Loop while there is more than one byte left to be read.
        //
        while(--ui32Count != 1)
        {
            //
            // Perform a dummy write to keep the transmit FIFO from going
            // empty.
            //
            MAP_SSIDataPut(ui32Base, 0);

            //
            // Read the next data byte from the receive FIFO and place it into
            // the data buffer.
            //
            MAP_SSIDataGet(ui32Base, &ui32Addr);
            *pui8Data++ = ui32Addr & 0xff;
        }

        //
        // Perform the final dummy write, marking it as the end of the frame.
        //
        MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);

        //
        // Read the next data byte from the receive FIFO and place it into the
        // data buffer.
        //
        MAP_SSIDataGet(ui32Base, &ui32Addr);
        *pui8Data++ = ui32Addr & 0xff;
    }

    //
    // Read the final data byte from the receive FIFO and place it into the
    // data buffer.
    //
    MAP_SSIDataGet(ui32Base, &ui32Addr);
    *pui8Data++ = ui32Addr & 0xff;
}

//*****************************************************************************
//
//! Reads data from the SPI flash using Bi-SPI in the background.
//!
//! \param pState is a pointer to the SPI flash state structure.
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to read.
//! \param pui8Data is a pointer to the data buffer to into which to read the
//! data.
//! \param ui32Count is the number of bytes to read.
//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
//! module.
//! \param ui32RxChannel is the uDMA channel to be used for reading from the
//! SSI module.
//!
//! This function reads data from the SPI flash with Bi-SPI, using either
//! interrupts or uDMA to transfer the data.  This function will return
//! immediately and read the data in the background.  In order for this to
//! complete successfully, several conditions must be satisfied:
//!
//! - Prior to calling this function:
//!   - The SSI module must be enabled in SysCtl.
//!   - The SSI pins must be configured for use by the SSI module.
//!   - The SSI module interrupt must be enabled in NVIC.
//!   - The uDMA module must be enabled in SysCtl and the control table set (if
//!     using uDMA).
//!   - The uDMA channels must be assigned to the SSI module.
//!
//! - After calling this function:
//!   - The interrupt handler for the SSI module must call
//!     SPIFlashIntHandler(), passing the same pState structure pointer that
//!     was supplied to this function.
//!   - No other SPI flash operation can be called until this operation has
//!     completed.
//!
//! Completion of the read operation is indicated when SPIFlashIntHandler()
//! returns \b SPI_FLASH_DONE.
//!
//! Like SPIFLashDualRead(), this uses the 0x3b SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashDualReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                            uint32_t ui32Addr, uint8_t *pui8Data,
                            uint32_t ui32Count, bool bUseDMA,
                            uint32_t ui32TxChannel, uint32_t ui32RxChannel)
{
    uint32_t ui32Trash;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
    {
    }

    //
    // Save the parameters of this read operation to the state structure.
    //
    pState->ui32Base = ui32Base;
    pState->ui16Cmd = CMD_DREAD;
    pState->ui16State = STATE_CMD;
    pState->ui32Addr = ui32Addr;
    pState->pui8Buffer = pui8Data;
    pState->ui32ReadCount = ui32Count;
    pState->ui32WriteCount = ui32Count;
    pState->bUseDMA = bUseDMA;
    pState->ui32TxChannel = ui32TxChannel & 0x1f;
    pState->ui32RxChannel = ui32RxChannel & 0x1f;

    //
    // Enable the SSI transmit and receive interrupts.  This will start the
    // transfer.  If uDMA is being used, the uDMA-related interrupts will be
    // enabled at the appropriate time by the interrupt handler.
    //
    HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC;
    HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM;
}

//*****************************************************************************
//
//! Erases a 32 KB block of the SPI flash.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to erase.
//!
//! This function erases a 32 KB block of the SPI flash.  Each 32 KB block has
//! a 32 KB alignment; the SPI flash will ignore the lower 15 bits of the
//! address provided.  The 32 KB block erase command is issued by this
//! function; SPIFlashReadStatus() must be used to query the SPI flash to
//! determine when the 32 KB block erase operation has completed.  This uses
//! the 0x52 SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashBlockErase32(uint32_t ui32Base, uint32_t ui32Addr)
{
    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the 32 KB block erase command command.
    //
    MAP_SSIDataPut(ui32Base, CMD_BE32);

    //
    // Send the address of the 32 KB block to be erased, marking the last byte
    // of the address as the end of the frame.
    //
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
    MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff);
}

//*****************************************************************************
//
//! Reads data from the SPI flash using Quad-SPI.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to read.
//! \param pui8Data is a pointer to the data buffer to into which to read the
//! data.
//! \param ui32Count is the number of bytes to read.
//!
//! This function reads data from the SPI flash with Quad-SPI, using PIO mode.
//! This function will not return until the read has completed.  This uses the
//! 0x6b SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashQuadRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data,
                 uint32_t ui32Count)
{
    uint32_t ui32Trash;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
    {
    }

    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the quad read command.
    //
    MAP_SSIDataPut(ui32Base, CMD_QREAD);

    //
    // Send the address of the first byte to read.
    //
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
    MAP_SSIDataPut(ui32Base, ui32Addr & 0xff);

    //
    // Send a dummy byte.
    //
    MAP_SSIDataPut(ui32Base, 0);

    //
    // Set the SSI module into Quad-SPI read mode.  In this mode, dummy writes
    // are required in order to make the transfer occur; the SSI module will
    // ignore the data (the SPI flash will never see the dummy data since
    // Quad-SPI read mode is a uni-directional input mode).
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_QUAD_READ);

    //
    // See if there is a single byte to be read.
    //
    if(ui32Count == 1)
    {
        //
        // Perform a single dummy write, marking it as the end of the frame.
        //
        MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);
    }
    else
    {
        //
        // Perform a dummy write to prime the loop.
        //
        MAP_SSIDataPut(ui32Base, 0);

        //
        // Loop while there is more than one byte left to be read.
        //
        while(--ui32Count != 1)
        {
            //
            // Perform a dummy write to keep the transmit FIFO from going
            // empty.
            //
            MAP_SSIDataPut(ui32Base, 0);

            //
            // Read the next data byte from the receive FIFO and place it into
            // the data buffer.
            //
            MAP_SSIDataGet(ui32Base, &ui32Addr);
            *pui8Data++ = ui32Addr & 0xff;
        }

        //
        // Perform the final dummy write, marking it as the end of the frame.
        //
        MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);

        //
        // Read the next data byte from the receive FIFO and place it into the
        // data buffer.
        //
        MAP_SSIDataGet(ui32Base, &ui32Addr);
        *pui8Data++ = ui32Addr & 0xff;
    }

    //
    // Read the final data byte from the receive FIFO and place it into the
    // data buffer.
    //
    MAP_SSIDataGet(ui32Base, &ui32Addr);
    *pui8Data++ = ui32Addr & 0xff;
}

//*****************************************************************************
//
//! Reads data from the SPI flash using Quad-SPI in the background.
//!
//! \param pState is a pointer to the SPI flash state structure.
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to read.
//! \param pui8Data is a pointer to the data buffer to into which to read the
//! data.
//! \param ui32Count is the number of bytes to read.
//! \param bUseDMA is \b true if uDMA should be used and \b false otherwise.
//! \param ui32TxChannel is the uDMA channel to be used for writing to the SSI
//! module.
//! \param ui32RxChannel is the uDMA channel to be used for reading from the
//! SSI module.
//!
//! This function reads data from the SPI flash with Quad-SPI, using either
//! interrupts or uDMA to transfer the data.  This function will return
//! immediately and read the data in the background.  In order for this to
//! complete successfully, several conditions must be satisfied:
//!
//! - Prior to calling this function:
//!   - The SSI module must be enabled in SysCtl.
//!   - The SSI pins must be configured for use by the SSI module.
//!   - The SSI module interrupt must be enabled in NVIC.
//!   - The uDMA module must be enabled in SysCtl and the control table set (if
//!     using uDMA).
//!   - The uDMA channels must be assigned to the SSI module.
//!
//! - After calling this function:
//!   - The interrupt handler for the SSI module must call
//!     SPIFlashIntHandler(), passing the same pState structure pointer that
//!     was supplied to this function.
//!   - No other SPI flash operation can be called until this operation has
//!     completed.
//!
//! Completion of the read operation is indicated when SPIFlashIntHandler()
//! returns \b SPI_FLASH_DONE.
//!
//! Like SPIFlashQuadRead(), this uses the 0x6b SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashQuadReadNonBlocking(tSPIFlashState *pState, uint32_t ui32Base,
                            uint32_t ui32Addr, uint8_t *pui8Data,
                            uint32_t ui32Count, bool bUseDMA,
                            uint32_t ui32TxChannel, uint32_t ui32RxChannel)
{
    uint32_t ui32Trash;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0)
    {
    }

    //
    // Save the parameters of this read operation to the state structure.
    //
    pState->ui32Base = ui32Base;
    pState->ui16Cmd = CMD_QREAD;
    pState->ui16State = STATE_CMD;
    pState->ui32Addr = ui32Addr;
    pState->pui8Buffer = pui8Data;
    pState->ui32ReadCount = ui32Count;
    pState->ui32WriteCount = ui32Count;
    pState->bUseDMA = bUseDMA;
    pState->ui32TxChannel = ui32TxChannel & 0x1f;
    pState->ui32RxChannel = ui32RxChannel & 0x1f;

    //
    // Enable the SSI transmit and receive interrupts.  This will start the
    // transfer.  If uDMA is being used, the uDMA-related interrupts will be
    // enabled at the appropriate time by the interrupt handler.
    //
    HWREG(ui32Base + SSI_O_ICR) = SSI_ICR_DMATXIC | SSI_ICR_DMARXIC;
    HWREG(ui32Base + SSI_O_IM) = SSI_IM_TXIM | SSI_IM_RXIM | SSI_IM_RTIM;
}

//*****************************************************************************
//
//! Reads the manufacturer and device IDs from the SPI flash.
//!
//! \param ui32Base is the SSI module base address.
//! \param pui8ManufacturerID is a pointer to the location into which to store
//! the manufacturer ID.
//! \param pui16DeviceID is a pointer to the location into which to store the
//! device ID.
//!
//! This function reads the manufacturer and device IDs from the SPI flash.
//! These values can be used to identify the SPI flash that is attached, as
//! well as determining if a SPI flash is attached (if the \b SSIRx pin is
//! pulled up or down, either using the pad's weak pull up/down or using an
//! external resistor, which will cause the returned IDs to be either all zeros
//! or all ones if the SPI flash is not attached).  This uses the 0x9f SPI
//! flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashReadID(uint32_t ui32Base, uint8_t *pui8ManufacturerID,
               uint16_t *pui16DeviceID)
{
    uint32_t ui32Data1, ui32Data2;

    //
    // Drain any residual data from the receive FIFO.
    //
    while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Data1) != 0)
    {
    }

    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the read ID command.
    //
    MAP_SSIDataPut(ui32Base, CMD_RDID);

    //
    // Set the SSI module into read/write mode.  In this mode, dummy writes are
    // required in order to make the transfer occur; the SPI flash will ignore
    // the data.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE);

    //
    // Send three dummy bytes, marking the last as the end of the frame.
    //
    MAP_SSIDataPut(ui32Base, 0);
    MAP_SSIDataPut(ui32Base, 0);
    MAP_SSIAdvDataPutFrameEnd(ui32Base, 0);

    //
    // Read the first returned data byte, which contains the manufacturer ID.
    //
    MAP_SSIDataGet(ui32Base, &ui32Data1);
    *pui8ManufacturerID = ui32Data1 & 0xff;

    //
    // Read the remaining two data bytes, which contain the device ID.
    //
    MAP_SSIDataGet(ui32Base, &ui32Data1);
    MAP_SSIDataGet(ui32Base, &ui32Data2);
    *pui16DeviceID = ((ui32Data1 & 0xff) << 8) | (ui32Data2 & 0xff);
}

//*****************************************************************************
//
//! Erases the entire SPI flash.
//!
//! \param ui32Base is the SSI module base address.
//!
//! This command erase the entire SPI flash.  The chip erase command is issued
//! by this function; SPIFlashReadStatus() must be used to query the SPI flash
//! to determine when the chip erase operation has completed.  This uses the
//! 0xc7 SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashChipErase(uint32_t ui32Base)
{
    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the chip erase command, marking this byte as the end of the frame.
    //
    MAP_SSIAdvDataPutFrameEnd(ui32Base, CMD_CE);
}

//*****************************************************************************
//
//! Erases a 64 KB block of the SPI flash.
//!
//! \param ui32Base is the SSI module base address.
//! \param ui32Addr is the SPI flash address to erase.
//!
//! This function erases a 64 KB block of the SPI flash.  Each 64 KB block has
//! a 64 KB alignment; the SPI flash will ignore the lower 16 bits of the
//! address provided.  The 64 KB block erase command is issued by this
//! function; SPIFlashReadStatus() must be used to query the SPI flash to
//! determine when the 64 KB block erase operation has completed.  This uses
//! the 0xd8 SPI flash command.
//!
//! \return None.
//
//*****************************************************************************
void
SPIFlashBlockErase64(uint32_t ui32Base, uint32_t ui32Addr)
{
    //
    // Set the SSI module into write-only mode.
    //
    MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE);

    //
    // Send the 64 KB block erase command command.
    //
    MAP_SSIDataPut(ui32Base, CMD_BE64);

    //
    // Send the address of the 64 KB block to be erased, marking the last byte
    // of the address as the end of the frame.
    //
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff);
    MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff);
    MAP_SSIAdvDataPutFrameEnd(ui32Base, ui32Addr & 0xff);
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
