From 520885d4d0f94d730a73e12c88a0bad8f52acbff Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 16 Oct 2013 11:59:26 -0600 Subject: CC3000 driver changes from David Sidrane --- nuttx/drivers/wireless/cc3000/spi.c | 827 +++++++++--------------------------- 1 file changed, 194 insertions(+), 633 deletions(-) (limited to 'nuttx/drivers/wireless/cc3000/spi.c') diff --git a/nuttx/drivers/wireless/cc3000/spi.c b/nuttx/drivers/wireless/cc3000/spi.c index 78582cc62..08734a10a 100644 --- a/nuttx/drivers/wireless/cc3000/spi.c +++ b/nuttx/drivers/wireless/cc3000/spi.c @@ -1,64 +1,47 @@ /************************************************************************** -* -* spi. - SPI functions to connect an Arduidno to the TI CC3000 -* -* This code uses the Arduino hardware SPI library (or a bit-banged -* SPI for the Teensy 3.0) to send & receive data between the library -* API calls and the CC3000 hardware. Every -* -* Version 1.0.1b -* -* Copyright (C) 2013 Chris Magagna - cmagagna@yahoo.com -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* Don't sue me if my code blows up your board and burns down your house -* -****************************************************************************/ + * spi. - SPI functions to connect an Arduidno to the TI CC3000 + * + * This code uses the Arduino hardware SPI library (or a bit-banged + * SPI for the Teensy 3.0) to send & receive data between the library + * API calls and the CC3000 hardware. Every + * + * Version 1.0.1b + * + * Copyright (C) 2013 Chris Magagna - cmagagna@yahoo.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Don't sue me if my code blows up your board and burns down your house + * + ****************************************************************************/ + +/***************************************************************************** + * Included Files + *****************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include #include #include #include #include -#include -#include -#include -#include -#include -//#include - -// This flag lets the interrupt handler know if it should respond to -// the WL_SPI_IRQ pin going low or not -int16_t SPIInterruptsEnabled=0; - - -#define READ 3 -#define WRITE 1 +#include +#include "spi.h" -#define HI(value) (((value) & 0xFF00) >> 8) -#define LO(value) ((value) & 0x00FF) +#include +#include -#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) - -/* !!!HACK!!!*/ -#define KL_PORTA_ISFR 0x400490a0 -#define PIN16 16 -#define getreg32(a) (*(volatile uint32_t *)(a)) -#define putreg32(v,a) (*(volatile uint32_t *)(a) = (v)) +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ #undef SPI_DEBUG /* Define to enable debug */ #undef SPI_VERBOSE /* Define to enable verbose debug */ @@ -76,609 +59,187 @@ int16_t SPIInterruptsEnabled=0; # define spivdbg(x...) #endif -#ifdef CONFIG_KL_SPI0 -void kl_spi0select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, - bool selected) -{ - spivdbg("devid: %d CS: %s\n", - (int)devid, selected ? "assert" : "de-assert"); -} -#endif - -#ifdef CONFIG_KL_SPI1 -void kl_spi1select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, - bool selected) -{ - spivdbg("devid: %d CS: %s\n", - (int)devid, selected ? "assert" : "de-assert"); -} -#endif - -#ifdef CONFIG_KL_SPI0 -uint8_t kl_spi0status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) -{ - return 0; -} -#endif - -#ifdef CONFIG_KL_SPI1 -uint8_t kl_spi1status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) -{ - return 0; -} -#endif - -typedef struct -{ - gcSpiHandleRx SPIRxHandler; - - uint16_t usTxPacketLength; - uint16_t usRxPacketLength; - unsigned long ulSpiState; - uint8_t *pTxPacket; - uint8_t *pRxPacket; - -} tSpiInformation; - -tSpiInformation sSpiInformation; - -// -// Static buffer for 5 bytes of SPI HEADER -// -uint8_t tSpiReadHeader[] = {READ, 0, 0, 0, 0}; - -// 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 occured and either recevie function -// or send function will stuck forever. -#define CC3000_BUFFER_MAGIC_NUMBER (0xDE) - -char spi_buffer[CC3000_RX_BUFFER_SIZE]; -uint8_t wlan_tx_buffer[CC3000_TX_BUFFER_SIZE]; - -struct spi_dev_s *spi = NULL; - -unsigned int SPIPump(uint8_t data) -{ - uint8_t rx; - - printf("SPIPump tx = 0x%X ", data); - - if (!spi) - { - spi = up_spiinitialize(1); - SPI_SETBITS(spi, 8); - SPI_SETMODE(spi, SPIDEV_MODE1); - } - - SPI_EXCHANGE(spi, &data, &rx, 1); - - printf(" rx = 0x%X\n", rx); - - return rx; -} - -//***************************************************************************** -// -//! This function enter point for write flow -//! -//! \param SpiPauseSpi -//! -//! \return none -//! -//! \brief The function triggers a user provided callback for -// -//***************************************************************************** - -void SpiPauseSpi(void) -{ - SPIInterruptsEnabled = 0; -} - -//***************************************************************************** -// -//! This function enter point for write flow -//! -//! \param SpiResumeSpi -//! -//! \return none -//! -//! \brief The function triggers a user provided callback for -// -//***************************************************************************** +static struct +{ + int cc3000fd; + gcSpiHandleRx pfRxHandler; + pthread_t unsoliced_thread; + bool run; + uint8_t rx_buffer[CC3000_RX_BUFFER_SIZE]; + +} spiconf; + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: SpiResumeSpi + * + * Description: + * Will re enable the SPI_IRQ'a ability to create interrupts. It is used to + * resume processing after the code passed to SpiOpen is Called + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + *****************************************************************************/ void SpiResumeSpi(void) { - SPIInterruptsEnabled = 1; -} - -//***************************************************************************** -// -//! This function enter point for write flow -//! -//! \param SpiTriggerRxProcessing -//! -//! \return none -//! -//! \brief The function triggers a user provided callback for -// -//***************************************************************************** -void SpiTriggerRxProcessing(void) -{ - // - // Trigger Rx processing - // - SpiPauseSpi(); - DeassertWlanCS(); - - // 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 overriten - buffer overrun - // occurred - and we will stuck here forever! + DEBUGASSERT(spiconf.cc3000fd); - if (sSpiInformation.pRxPacket[CC3000_RX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER) + if (ioctl(spiconf.cc3000fd,CC3000IOC_COMPLETE,0)) { - while (1) - ; + printf("ioctl:CC3000IOC_COMPLETE failed: %s\n", strerror(errno)); } - - sSpiInformation.ulSpiState = eSPI_STATE_IDLE; - sSpiInformation.SPIRxHandler(sSpiInformation.pRxPacket + SPI_HEADER_SIZE); -} - -//***************************************************************************** -// -//! This function enter point for write flow -//! -//! \param buffer -//! -//! \return none -//! -//! \brief ... -// -//***************************************************************************** - -void SpiReadDataSynchronous(uint8_t *data, uint16_t size) -{ - long i = 0; - uint8_t *data_to_send = tSpiReadHeader; - - for (i = 0; i < size; i ++) - { - data[i] = SPIPump(data_to_send[0]); - } -} - -//***************************************************************************** -// -//! This function enter point for write flow -//! -//! \param buffer -//! -//! \return none -//! -//! \brief ... -// -//***************************************************************************** - -void SpiWriteDataSynchronous(uint8_t *data, uint16_t size) -{ - while (size) - { - SPIPump(*data); - size --; - data++; - } -} - -//***************************************************************************** -// -//! This function enter point for write flow -//! -//! \param buffer -//! -//! \return none -//! -//! \brief ... -// -//***************************************************************************** -long SpiFirstWrite(uint8_t *ucBuf, uint16_t usLength) -{ - // - // workaround for first transaction - // - - AssertWlanCS(); - - usleep(70); - - // SPI writes first 4 bytes of data - - SpiWriteDataSynchronous(ucBuf, 4); - - usleep(70); - - SpiWriteDataSynchronous(ucBuf + 4, usLength - 4); - - sSpiInformation.ulSpiState = eSPI_STATE_IDLE; - - DeassertWlanCS(); - - //printf("Executed SpiFirstWrite!\n"); - - return(0); } -//***************************************************************************** -// -//! This function enter point for write flow -//! -//! \param buffer -//! -//! \return none -//! -//! \brief ... -// -//***************************************************************************** +/***************************************************************************** + * Name: SpiWrite + * + * Description: + * This function enter point for write flow + * + * Input Parameters: + * pUserBuffer + * usLength + * + * Returned Value: + * + *****************************************************************************/ long SpiWrite(uint8_t *pUserBuffer, uint16_t usLength) { - uint8_t ucPad = 0; - - // - // Figure out the total length of the packet in order to figure out if there is padding or not - // - - if(!(usLength & 0x0001)) + DEBUGASSERT(spiconf.cc3000fd); + return write(spiconf.cc3000fd,pUserBuffer,usLength) == usLength ? 0 : -errno; +} + +/***************************************************************************** + * Name: SpiRead + * + * Description: + * This function enter point for read flow. This function will block the + * caller untinlthere is data Available + * + * Input Parameters: + * pUserBuffer + * usLength + * + * Returned Value: + * + *****************************************************************************/ + +long SpiRead(uint8_t *pUserBuffer, uint16_t usLength) +{ + DEBUGASSERT(spiconf.cc3000fd); + return read(spiconf.cc3000fd,pUserBuffer,usLength); +} + +/***************************************************************************** + * Name: unsoliced_thread_func + * + * Description: + * This is the thread for unsolicited events. This function will block the + * caller untinlthere is data Available + * + * Input Parameters: + * parameter + * + * Returned Value: + * + *****************************************************************************/ + +static void *unsoliced_thread_func(void *parameter) +{ + char queuename[QUEUE_NAMELEN]; + int status = 0; + int nbytes = 0; + int minor = 0; + + ioctl(spiconf.cc3000fd, CC3000IOC_GETQUEID, (unsigned long)&minor); + snprintf(queuename, QUEUE_NAMELEN, QUEUE_FORMAT, minor); + mqd_t queue = mq_open(queuename,O_RDONLY); + + while(spiconf.run) { - 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 overrun detection. If the magic number is overwritten - buffer overrun - // occurred - and we will be stuck here forever! - - if (wlan_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER) - { - while (1) - ; - } - - if (sSpiInformation.ulSpiState == eSPI_STATE_POWERUP) - { - while (sSpiInformation.ulSpiState != eSPI_STATE_INITIALIZED) + memset(spiconf.rx_buffer,0,sizeof(spiconf.rx_buffer)); + nbytes = mq_receive(queue, spiconf.rx_buffer, CC3000_RX_BUFFER_SIZE, 0); + if (nbytes > 0) { + spiconf.pfRxHandler(spiconf.rx_buffer); } } - 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 two 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) - { - ; - } - - 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 - // - - AssertWlanCS(); - - // - // 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; - - DeassertWlanCS(); - } - } - - - // - // 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); -} - -//***************************************************************************** -// -//! This function processes received SPI Header and in accordance with it - continues reading -//! the packet -//! -//! \param None -//! -//! \return None -//! -//! \brief ... -// -//***************************************************************************** - -long SpiReadDataCont(void) -{ - long data_to_recv; - uint8_t *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); -} - -//***************************************************************************** -// -//! This function enter point for write flow -//! -//! \param SSIContReadOperation -//! -//! \return none -//! -//! \brief The function triggers a user provided callback for -// -//***************************************************************************** - -void SSIContReadOperation(void) -{ - // - // The header was read - continue with the payload read - // - if (!SpiReadDataCont()) - { - // - // All the data was read - finalize handling by switching to teh task - // and calling from task Event Handler - // - - SpiTriggerRxProcessing(); - } -} - -//***************************************************************************** -// -//! This function enter point for read flow: first we read minimal 5 SPI header bytes and 5 Event -//! Data bytes -//! -//! \param buffer -//! -//! \return none -//! -//! \brief ... -// -//***************************************************************************** -void SpiReadHeader(void) -{ - SpiReadDataSynchronous(sSpiInformation.pRxPacket, 10); -} - -//***************************************************************************** -// -//! The IntSpiGPIOHandler interrupt handler -//! -//! \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 registrated this interrupt request -//! it set the corresponding /CS in active state. -// -//***************************************************************************** -//#pragma vector=PORT2_VECTOR -//__interrupt void IntSpiGPIOHandler(void) -int CC3000InterruptHandler(int irq, void *context) -{ - uint32_t regval = 0; - - regval = getreg32(KL_PORTA_ISFR); - if (regval & (1 << PIN16)) - { - //printf("\nAn interrupt was issued!\n"); - - if (!SPIInterruptsEnabled) - { - goto out; - } - - //printf("\nSPIInterrupt was enabled!\n"); - - 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 - start reception */ - - AssertWlanCS(); - - // - // Wait for TX/RX Complete 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; - - DeassertWlanCS(); - } - else - { - } - -out: - regval = (1 << PIN16); - putreg32(regval, KL_PORTA_ISFR); - } - - return 0; -} - -//***************************************************************************** -// -//! SpiClose -//! -//! \param none -//! -//! \return none -//! -//! \brief Cofigure the SSI -// -//***************************************************************************** + mq_close(queue); + pthread_exit((pthread_addr_t)status); + return (pthread_addr_t)status; +} + +/***************************************************************************** + * Name: SpiOpen + * + * Description: + * Configure the SPI + * + * Input Parameters: + * pfRxHandler the Rx handler for SPI + * + * Returned Value: + * None + * + *****************************************************************************/ void SpiOpen(gcSpiHandleRx pfRxHandler) { - sSpiInformation.ulSpiState = eSPI_STATE_POWERUP; - - memset(spi_buffer, 0, sizeof(spi_buffer)); - memset(wlan_tx_buffer, 0, sizeof(spi_buffer)); - - sSpiInformation.SPIRxHandler = pfRxHandler; - sSpiInformation.usTxPacketLength = 0; - sSpiInformation.pTxPacket = NULL; - sSpiInformation.pRxPacket = (uint8_t *)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; + pthread_attr_t attr; + int status; - // - // Enable interrupt on the GPIO pin of WLAN IRQ - // - tSLInformation.WlanInterruptEnable(); -} - -//***************************************************************************** -// -//! SpiClose -//! -//! \param none -//! -//! \return none -//! -//! \brief Cofigure the SSI -// -//***************************************************************************** + DEBUGASSERT(spiconf.cc3000fd == 0); + int fd = open("/dev/wireless0",O_RDWR|O_BINARY); + if (fd > 0) + { + spiconf.pfRxHandler = pfRxHandler; + spiconf.cc3000fd = fd; + spiconf.run = true; + + status = pthread_attr_init(&attr); + DEBUGASSERT(status == 0) + + status = pthread_create(&spiconf.unsoliced_thread, &attr, + unsoliced_thread_func, NULL); + DEBUGASSERT(status == 0) + } +} + +/***************************************************************************** + * Name: SpiClose + * + * Description: + * Configure the SPI + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + *****************************************************************************/ void SpiClose(void) { - if (sSpiInformation.pRxPacket) - { - sSpiInformation.pRxPacket = 0; - } + if (spiconf.cc3000fd) + { + int status; + spiconf.run = false; - // - // Disable Interrupt in GPIOA module... - // + pthread_cancel(spiconf.unsoliced_thread); + pthread_join(spiconf.unsoliced_thread, (pthread_addr_t*)&status); - tSLInformation.WlanInterruptDisable(); + close(spiconf.cc3000fd); + spiconf.cc3000fd = 0; + } } -- cgit v1.2.3