CC3000HostDriver for device TI CC3000 some changes were made due to mbed compiler and the use of void*

Dependents:   CC3000Test

spi.cpp

Committer:
dflet
Date:
2013-08-02
Revision:
0:9cb694f00b7b

File content as of revision 0:9cb694f00b7b:


/*****************************************************************************
*
*  spi.c - CC3000 Host Driver Implementation.
*  Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*    Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
*    Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the   
*    distribution.
*
*    Neither the name of Texas Instruments Incorporated nor the names of
*    its contributors may be used to endorse or promote products derived
*    from this software without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
*  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
*  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/

//*****************************************************************************
//
//! \addtogroup link_buff_api
//! @{
//
//*****************************************************************************
#include "hci.h"
#include "spi.h"
#include "evnt_handler.h"
#include "mbed.h"
#include "CC3000Core.h"
//#include "board.h"
//#include <msp430.h>

SPI spi(p5, p6, p7); // mosi, miso, sclk
DigitalOut cs(p8); // chip select


#define READ                    3
#define WRITE                   1

#define HI(value)               (((value) & 0xFF00) >> 8)
#define LO(value)               ((value) & 0x00FF)

#define ASSERT_CS()          (cs = 0)//(RF_CS_OUT &= ~RF_CS)

#define DEASSERT_CS()        (cs = 1)//(RF_CS_OUT |= RF_CS)

#define HEADERS_SIZE_EVNT       (SPI_HEADER_SIZE + 5)

#define SPI_HEADER_SIZE            (5)

#define     eSPI_STATE_POWERUP                  (0)
#define     eSPI_STATE_INITIALIZED           (1)
#define     eSPI_STATE_IDLE                     (2)
#define     eSPI_STATE_WRITE_IRQ                (3)
#define     eSPI_STATE_WRITE_FIRST_PORTION   (4)
#define     eSPI_STATE_WRITE_EOT             (5)
#define     eSPI_STATE_READ_IRQ                 (6)
#define     eSPI_STATE_READ_FIRST_PORTION     (7)
#define     eSPI_STATE_READ_EOT                 (8)


typedef struct
{
    gcSpiHandleRx  SPIRxHandler;
    unsigned short usTxPacketLength;
    unsigned short usRxPacketLength;
    unsigned long  ulSpiState;
    unsigned char *pTxPacket;
    unsigned char *pRxPacket;

}tSpiInformation;


tSpiInformation sSpiInformation;


// buffer for 5 bytes of SPI HEADER
unsigned char tSpiReadHeader[] = {READ, 0, 0, 0, 0};


void SpiWriteDataSynchronous(unsigned char *data, unsigned short size);
void SpiWriteAsync(const unsigned char *data, unsigned short size);
void SpiPauseSpi(void);
void SpiResumeSpi(void);
void SSIContReadOperation(void);

// The magic number that resides at the end of the TX/RX buffer (1 byte after
// the allocated size) for the purpose of detection of the overrun. The location
// of the memory where the magic number resides shall never be written. In case 
// it is written - the overrun occurred and either receive function or send
// function will stuck forever.
#define CC3000_BUFFER_MAGIC_NUMBER (0xDE)

///////////////////////////////////////////////////////////////////////////////////////////////////////////    
//__no_init is used to prevent the buffer initialization in order to prevent hardware WDT expiration    ///
// before entering to 'main()'.                                                                         ///
//for every IDE, different syntax exists :          1.   __CCS__ for CCS v5                             ///
//                                                  2.  __IAR_SYSTEMS_ICC__ for IAR Embedded Workbench  ///
// *CCS does not initialize variables - therefore, __no_init is not needed.                             ///
///////////////////////////////////////////////////////////////////////////////////////////////////////////

//#ifdef __CCS__
char spi_buffer[CC3000_RX_BUFFER_SIZE];

//#elif __IAR_SYSTEMS_ICC__
//__no_init char spi_buffer[CC3000_RX_BUFFER_SIZE];
//#endif

//#ifdef __CCS__
unsigned char wlan_tx_buffer[CC3000_TX_BUFFER_SIZE];

//#elif __IAR_SYSTEMS_ICC__
//__no_init unsigned char wlan_tx_buffer[CC3000_TX_BUFFER_SIZE];
//#endif

//*****************************************************************************
// 
//!  SpiCleanGPIOISR
//! 
//!  \param  none
//! 
//!  \return none
//! 
//!  \brief  This function get the reason for the GPIO interrupt and clear
//!          corresponding interrupt flag
// 
//*****************************************************************************
void
SpiCleanGPIOISR(void)
{
    WlanInterruptDisable();
    //SPI_IFG_PORT &= ~SPI_IRQ_PIN;
}
 
//*****************************************************************************
//
//!  SpiClose
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Close Spi interface
//
//*****************************************************************************
void
SpiClose(void)
{
    if (sSpiInformation.pRxPacket)
    {
        sSpiInformation.pRxPacket = 0;
    }
    
    //    Disable Interrupt
    tSLInformation.WlanInterruptDisable();
}


//*****************************************************************************
//
//!  SpiOpen
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Open Spi interface 
//
//*****************************************************************************
void 
SpiOpen(gcSpiHandleRx pfRxHandler)
{
    sSpiInformation.ulSpiState = eSPI_STATE_POWERUP;
    sSpiInformation.SPIRxHandler = pfRxHandler;
    sSpiInformation.usTxPacketLength = 0;
    sSpiInformation.pTxPacket = NULL;
    sSpiInformation.pRxPacket = (unsigned char *)spi_buffer;
    sSpiInformation.usRxPacketLength = 0;
    spi_buffer[CC3000_RX_BUFFER_SIZE - 1] = CC3000_BUFFER_MAGIC_NUMBER;
    wlan_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] = CC3000_BUFFER_MAGIC_NUMBER;
    
    // Enable interrupt on WLAN IRQ pin 
    tSLInformation.WlanInterruptEnable();
}

//*****************************************************************************
//
//!  init_spi
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  initializes an SPI interface
//
//*****************************************************************************

int init_spi(void)
{

    spi.frequency(16000000);
    spi.format(8, 1);
    cs = 1;

    //UCB0CTL1 |= UCSWRST; // Put state machine in reset
    //UCB0CTL0 = UCMSB + UCMST + UCMODE_0 + UCSYNC; // 3-pin, 8-bit SPI master

    //UCB0CTL1 = UCSWRST + UCSSEL_2; // Use SMCLK, keep RESET
    
    // Set SPI clock
    //UCB0CTL1 |= UCSWRST; // Put state machine in reset
    //UCB0BR0 = 2; // f_UCxCLK = 25MHz/2 = 12.5MHz
    //UCB0BR1 = 0;
    //UCB0CTL1 &= ~UCSWRST;
    
    return(ESUCCESS);
}

//*****************************************************************************
//
//! SpiFirstWrite
//!
//!  @param  ucBuf     buffer to write
//!  @param  usLength  buffer's length
//!
//!  @return none
//!
//!  @brief  enter point for first write flow
//
//*****************************************************************************
long
SpiFirstWrite(unsigned char *ucBuf, unsigned short usLength)
{
    // workaround for first transaction
    ASSERT_CS();
    
    // Assuming we are running on 24 MHz ~50 micro delay is 1200 cycles;
    //__delay_cycles(1200);
    wait_us(50);
    
    // SPI writes first 4 bytes of data
    SpiWriteDataSynchronous(ucBuf, 4);
    
    //__delay_cycles(1200);
    wait_us(50);
    SpiWriteDataSynchronous(ucBuf + 4, usLength - 4);
    
    // From this point on - operate in a regular way
    sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
    
    DEASSERT_CS();
    
    return(0);
}


//*****************************************************************************
//
//!  SpiWrite
//!
//!  @param  pUserBuffer  buffer to write
//!  @param  usLength     buffer's length
//!
//!  @return none
//!
//!  @brief  Spi write operation
//
//*****************************************************************************
long
SpiWrite(unsigned char *pUserBuffer, unsigned short usLength)
{
    unsigned char ucPad = 0;
    
    // Figure out the total length of the packet in order to figure out if there 
    // is padding or not
    if(!(usLength & 0x0001))
    {
        ucPad++;
    }
    
    pUserBuffer[0] = WRITE;
    pUserBuffer[1] = HI(usLength + ucPad);
    pUserBuffer[2] = LO(usLength + ucPad);
    pUserBuffer[3] = 0;
    pUserBuffer[4] = 0;
    
    usLength += (SPI_HEADER_SIZE + ucPad);
    
    // The magic number that resides at the end of the TX/RX buffer (1 byte after 
    // the allocated size) for the purpose of detection of the overrun. If the 
    // magic number is overwritten - buffer overrun occurred - and we will stuck 
    // here forever!
    if (wlan_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER)
    {
        while (1)
        {
        printf("Buffer over run\r\n");
         }//   ;
    }
    
    if (sSpiInformation.ulSpiState == eSPI_STATE_POWERUP)
    {
        while (sSpiInformation.ulSpiState != eSPI_STATE_INITIALIZED)
            ;
    }
    
    if (sSpiInformation.ulSpiState == eSPI_STATE_INITIALIZED)
    {
        // This is time for first TX/RX transactions over SPI: the IRQ is down - 
        // so need to send read buffer size command
        SpiFirstWrite(pUserBuffer, usLength);
        
    }
    else 
    {
        // We need to prevent here race that can occur in case 2 back to back 
        // packets are sent to the  device, so the state will move to IDLE and once 
        //again to not IDLE due to IRQ
        tSLInformation.WlanInterruptDisable();
        
        while (sSpiInformation.ulSpiState != eSPI_STATE_IDLE)
        {
            printf("Wait for eSPI_STATE_IDLE\r\n");
        }
        
        sSpiInformation.ulSpiState = eSPI_STATE_WRITE_IRQ;
        sSpiInformation.pTxPacket = pUserBuffer;
        sSpiInformation.usTxPacketLength = usLength;
        
        // Assert the CS line and wait till SSI IRQ line is active and then
        // initialize write operation
        ASSERT_CS();
        
        // Re-enable IRQ - if it was not disabled - this is not a problem...
        tSLInformation.WlanInterruptEnable();

        // check for a missing interrupt between the CS assertion and enabling back the interrupts
        if (tSLInformation.ReadWlanInterruptPin() == 0)
        {
            SpiWriteDataSynchronous(sSpiInformation.pTxPacket, sSpiInformation.usTxPacketLength);
            
            sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
            
            DEASSERT_CS();
        }
    }
    
    // Due to the fact that we are currently implementing a blocking situation
    // here we will wait till end of transaction
    while (eSPI_STATE_IDLE != sSpiInformation.ulSpiState)
        ;
    
    return(0);
}

 
//*****************************************************************************
//
//!  SpiWriteDataSynchronous
//!
//!  @param  data  buffer to write
//!  @param  size  buffer's size
//!
//!  @return none
//!
//!  @brief  Spi write operation
//
//*****************************************************************************
void
SpiWriteDataSynchronous(unsigned char *data, unsigned short size)
{
    while (size)
    {   
    
        //while (!(TXBufferIsEmpty()));
        //UCB0TXBUF = *data;
        spi.write(*data);
        //while (!(RXBufferIsEmpty()));
        //spi.write(0x00);
        //UCB0RXBUF;
        size --;
        //printf("data %x\r\n",*data);
        data++;
    }
}

//*****************************************************************************
//
//! SpiReadDataSynchronous
//!
//!  @param  data  buffer to read
//!  @param  size  buffer's size
//!
//!  @return none
//!
//!  @brief  Spi read operation
//
//*****************************************************************************
void
SpiReadDataSynchronous(unsigned char *data, unsigned short size)
{
    
    unsigned char *data_to_send = tSpiReadHeader;
    //printf("SPI Read..\r\n");
    for (int i = 0; i < size; i ++)
    {
        //while (!(TXBufferIsEmpty()));
        //Dummy write to trigger the clock
        //UCB0TXBUF = data_to_send[0];
        //spi.write(data_to_send[0]);
        //while (!(RXBufferIsEmpty()));
        data[i] = spi.write(data_to_send[0]);
        //data[i] = UCB0RXBUF;
        //printf("SPI Read..%x\r\n",data[i]);
    }
}


//*****************************************************************************
//
//!  SpiReadHeader
//!
//!  \param  buffer
//!
//!  \return none
//!
//!  \brief   This function enter point for read flow: first we read minimal 5 
//!              SPI header bytes and 5 Event Data bytes
//
//*****************************************************************************
void
SpiReadHeader(void)
{
    SpiReadDataSynchronous(sSpiInformation.pRxPacket, 10);
}


//*****************************************************************************
//
//!  SpiReadDataCont
//!
//!  @param  None
//!
//!  @return None
//!
//!  @brief  This function processes received SPI Header and in accordance with 
//!             it - continues reading the packet
//
//*****************************************************************************
long
SpiReadDataCont(void)
{
    long data_to_recv;
    unsigned char *evnt_buff, type;
    
    //determine what type of packet we have
    evnt_buff =  sSpiInformation.pRxPacket;
    data_to_recv = 0;
    STREAM_TO_UINT8((char *)(evnt_buff + SPI_HEADER_SIZE), HCI_PACKET_TYPE_OFFSET, type);
    
    switch(type)
    {
    case HCI_TYPE_DATA:
        {
            // We need to read the rest of data..
            STREAM_TO_UINT16((char *)(evnt_buff + SPI_HEADER_SIZE), HCI_DATA_LENGTH_OFFSET, data_to_recv);
            if (!((HEADERS_SIZE_EVNT + data_to_recv) & 1))
            {    
                data_to_recv++;
            }
            
            if (data_to_recv)
            {
                SpiReadDataSynchronous(evnt_buff + 10, data_to_recv);
            }
            break;
        }
    case HCI_TYPE_EVNT:
        {
            // Calculate the rest length of the data
            STREAM_TO_UINT8((char *)(evnt_buff + SPI_HEADER_SIZE), 
                                            HCI_EVENT_LENGTH_OFFSET, data_to_recv);
            data_to_recv -= 1;
            
            // Add padding byte if needed
            if ((HEADERS_SIZE_EVNT + data_to_recv) & 1)
            {
                
                data_to_recv++;
            }
            
            if (data_to_recv)
            {
                SpiReadDataSynchronous(evnt_buff + 10, data_to_recv);
            }
            
            sSpiInformation.ulSpiState = eSPI_STATE_READ_EOT;
            break;
        }
    }
    
    return (0);
}


//*****************************************************************************
//
//! SpiPauseSpi
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Spi pause operation
//
//*****************************************************************************

void 
SpiPauseSpi(void)
{
    WlanInterruptDisable();
    //SPI_IRQ_IE &= ~SPI_IRQ_PIN;
}


//*****************************************************************************
//
//! SpiResumeSpi
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Spi resume operation
//
//*****************************************************************************

void 
SpiResumeSpi(void)
{
    WlanInterruptEnable();
    //SPI_IRQ_IE |= SPI_IRQ_PIN;
}


//*****************************************************************************
//
//! SpiTriggerRxProcessing
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  Spi RX processing 
//
//*****************************************************************************
void 
SpiTriggerRxProcessing(void)
{
    
    // Trigger Rx processing
    SpiPauseSpi();
    DEASSERT_CS();
    
    // The magic number that resides at the end of the TX/RX buffer (1 byte after 
    // the allocated size) for the purpose of detection of the overrun. If the 
    // magic number is overwritten - buffer overrun occurred - and we will stuck 
    // here forever!
    if (sSpiInformation.pRxPacket[CC3000_RX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER)
    {
        while (1)
        printf("Buffer Over run....\r\n");
            ;
    }
    
    sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
    sSpiInformation.SPIRxHandler(sSpiInformation.pRxPacket + SPI_HEADER_SIZE);
}

//*****************************************************************************
// 
//!  IntSpiGPIOHandler
//! 
//!  @param  none
//! 
//!  @return none
//! 
//!  @brief  GPIO A interrupt handler. When the external SSI WLAN device is
//!          ready to interact with Host CPU it generates an interrupt signal.
//!          After that Host CPU has registered this interrupt request
//!          it set the corresponding /CS in active state.
// 
//*****************************************************************************
//#pragma vector=PORT2_VECTOR
//__interrupt void IntSpiGPIOHandler(void)
void IntSpiGPIOHandler(void)
{
    //switch(__even_in_range(P2IV, P2IV_P2IFG7))
    //printf("IRQ ISR\r\n");
    //{
    //case event:
        if (sSpiInformation.ulSpiState == eSPI_STATE_POWERUP)
        {
            //This means IRQ line was low call a callback of HCI Layer to inform 
            //on event 
             sSpiInformation.ulSpiState = eSPI_STATE_INITIALIZED;
        }
        else if (sSpiInformation.ulSpiState == eSPI_STATE_IDLE)
        {
            sSpiInformation.ulSpiState = eSPI_STATE_READ_IRQ;
            
            /* IRQ line goes down - we are start reception */
            ASSERT_CS();
            
            // Wait for TX/RX Compete which will come as DMA interrupt
            SpiReadHeader();
            
            sSpiInformation.ulSpiState = eSPI_STATE_READ_EOT;
            
            SSIContReadOperation();
        }
        else if (sSpiInformation.ulSpiState == eSPI_STATE_WRITE_IRQ)
        {
            SpiWriteDataSynchronous(sSpiInformation.pTxPacket, sSpiInformation.usTxPacketLength);
            
            sSpiInformation.ulSpiState = eSPI_STATE_IDLE;
            
            DEASSERT_CS();
        }
    //    break;
    //default:
    //    break;
    //}
    
}

//*****************************************************************************
//
//! SSIContReadOperation
//!
//!  @param  none
//!
//!  @return none
//!
//!  @brief  SPI read operation
//
//*****************************************************************************

void
SSIContReadOperation(void)
{
    // The header was read - continue with  the payload read
    if (!SpiReadDataCont())
    {
        // All the data was read - finalize handling by switching to the task
        //    and calling from task Event Handler
        SpiTriggerRxProcessing();
    }
}


//*****************************************************************************
//
//! TXBufferIsEmpty
//!
//!  @param  
//!
//!  @return returns 1 if buffer is empty, 0 otherwise 
//!
//!  @brief  Indication if TX SPI buffer is empty 
//
//*****************************************************************************

long TXBufferIsEmpty(void)
{
    //return (UCB0IFG&UCTXIFG);
    return (1);
}

//*****************************************************************************
//
//! RXBufferIsEmpty
//!
//!  @param  none  
//!
//!  @return returns 1 if buffer is empty, 0 otherwise
//!
//!  @brief  Indication if RX SPI buffer is empty
//
//*****************************************************************************

long RXBufferIsEmpty(void)
{
    //return (UCB0IFG&UCRXIFG);
    return (0);
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************