Added mutex for multiple SPI devices on the same SPI bus
Fork of cc3000_hostdriver_mbedsocket by
cc3000_spi.cpp
- Committer:
- Kojto
- Date:
- 2013-09-19
- Revision:
- 0:615c697c33b0
- Child:
- 20:30b6ed7bf8fd
File content as of revision 0:615c697c33b0:
/***************************************************************************** * * C++ interface/implementation created by Martin Kojtal (0xc0170). Thanks to * Jim Carver and Frank Vannieuwkerke for their inital cc3000 mbed port and * provided help. * * This version of "host driver" uses CC3000 Host Driver Implementation. Thus * read the following copyright: * * 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. * *****************************************************************************/ #include "cc3000.h" #include "cc3000_spi.h" namespace mbed_cc3000 { cc3000_spi::cc3000_spi(PinName cc3000_irq, PinName cc3000_en, PinName cc3000_cs, SPI cc3000_spi, IRQn_Type irq_port, cc3000_event &event, cc3000_simple_link &simple_link) : _wlan_irq(cc3000_irq), _wlan_en(cc3000_en), _wlan_cs(cc3000_cs), _wlan_spi(cc3000_spi), _irq_port(irq_port), _event(event), _simple_link(simple_link) { /* TODO = clear pending interrupts for PORTS. This is dependent on the used chip */ _wlan_spi.format(8,1); _wlan_spi.frequency(12000000); _function_pointer = _wlan_irq.fall(this, &cc3000_spi::WLAN_IRQHandler); _wlan_en = 0; _wlan_cs = 1; } cc3000_spi::~cc3000_spi() { } void cc3000_spi::wlan_irq_enable() { NVIC_EnableIRQ(_irq_port); } void cc3000_spi::wlan_irq_disable() { NVIC_DisableIRQ(_irq_port); } void cc3000_spi::wlan_irq_set(uint8_t value) { if (value) { _wlan_en = 1; } else { _wlan_en = 0; } } uint32_t cc3000_spi::wlan_irq_read() { return _wlan_irq.read(); } void cc3000_spi::close() { if (_simple_link.get_received_buffer() != 0) { _simple_link.set_received_buffer(0); } wlan_irq_disable(); } // void cc3000_spi::SpiReceiveHandler() { // _simple_link.usEventOrDataReceived = 1; // //_simple_link.pucReceivedData = (unsigned char *)pvBuffer; // hci_unsolicited_event_handler(); // } /* TODO pRxPacket, pTxPacket do we need to hold this pointer ? SPIRxHandler - remove? */ void cc3000_spi::open() { _spi_info.spi_state = eSPI_STATE_POWERUP; //_spi_info.SPIRxHandler = pfRxHandler; _spi_info.tx_packet_length = 0; _spi_info.rx_packet_length = 0; //_rx_buffer[CC3000_RX_BUFFER_SIZE - 1] = CC3000_BUFFER_MAGIC_NUMBER; //_tx_buffer[CC3000_TX_BUFFER_SIZE - 1] = CC3000_BUFFER_MAGIC_NUMBER; wlan_irq_enable(); } uint32_t cc3000_spi::first_write(uint8_t *buffer, uint16_t length) { _wlan_cs = 0; wait_us(50); /* first 4 bytes of the data */ write_synchronous(buffer, 4); wait_us(50); write_synchronous(buffer + 4, length - 4); _spi_info.spi_state = eSPI_STATE_IDLE; _wlan_cs = 1; return 0; } uint32_t cc3000_spi::write(uint8_t *buffer, uint16_t length) { uint8_t pad = 0; // check the total length of the packet in order to figure out if padding is necessary if(!(length & 0x0001)) { pad++; } buffer[0] = WRITE; buffer[1] = HI(length + pad); buffer[2] = LO(length + pad); buffer[3] = 0; buffer[4] = 0; length += (SPI_HEADER_SIZE + pad); // The magic number resides at the end of the TX/RX buffer (1 byte after the allocated size) // If the magic number is overwitten - buffer overrun occurred - we will be stuck here forever! uint8_t * transmit_buffer = _simple_link.get_transmit_buffer(); if (transmit_buffer[CC3000_TX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER) { while (1); } if (_spi_info.spi_state == eSPI_STATE_POWERUP) { while (_spi_info.spi_state != eSPI_STATE_INITIALIZED); } if (_spi_info.spi_state == eSPI_STATE_INITIALIZED) { // TX/RX transaction over SPI after powerup: IRQ is low - send read buffer size command first_write(buffer, length); } else { // Prevent occurence of a race condition when 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 wlan_irq_disable(); while (_spi_info.spi_state != eSPI_STATE_IDLE); _spi_info.spi_state = eSPI_STATE_WRITE_IRQ; //_spi_info.pTxPacket = buffer; _spi_info.tx_packet_length = length; // Assert the CS line and wait until the IRQ line is active, then initialize the write operation _wlan_cs = 0; wlan_irq_enable(); } // Wait until the transaction ends while (_spi_info.spi_state != eSPI_STATE_IDLE); return 0; } void cc3000_spi::write_synchronous(uint8_t *data, uint16_t size) { while(size) { _wlan_spi.write(*data++); size--; } } void cc3000_spi::read_synchronous(uint8_t *data, uint16_t size) { for (uint32_t i = 0; i < size; i++) { data[i] = _wlan_spi.write(0x03);; } } uint32_t cc3000_spi::read_data_cont() { long data_to_recv; unsigned char *evnt_buff, type; //determine the packet type evnt_buff = _simple_link.get_received_buffer(); data_to_recv = 0; STREAM_TO_UINT8((uint8_t *)(evnt_buff + SPI_HEADER_SIZE), HCI_PACKET_TYPE_OFFSET, type); switch(type) { case HCI_TYPE_DATA: { // Read the remaining data.. STREAM_TO_UINT16((uint8_t *)(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) { read_synchronous(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) { read_synchronous(evnt_buff + 10, data_to_recv); } _spi_info.spi_state = eSPI_STATE_READ_EOT; break; } } return (0); } void cc3000_spi::write_wlan_en(uint8_t value) { if (value) { _wlan_en = 1; } else { _wlan_en = 0; } } void cc3000_spi::WLAN_IRQHandler() { if (_spi_info.spi_state == eSPI_STATE_POWERUP) { // Inform HCI Layer that IRQ occured after powerup _spi_info.spi_state = eSPI_STATE_INITIALIZED; } else if (_spi_info.spi_state == eSPI_STATE_IDLE) { _spi_info.spi_state = eSPI_STATE_READ_IRQ; /* IRQ line goes low - acknowledge it */ _wlan_cs = 0; read_synchronous(_simple_link.get_received_buffer(), 10); _spi_info.spi_state = eSPI_STATE_READ_EOT; // The header was read - continue with the payload read if (!read_data_cont()) { // All the data was read - finalize handling by switching to the task // Trigger Rx processing wlan_irq_disable(); _wlan_cs = 1; // The magic number resides at the end of the TX/RX buffer (1 byte after the allocated size) // If the magic number is overwitten - buffer overrun occurred - we will be stuck here forever! uint8_t *received_buffer = _simple_link.get_received_buffer(); if (received_buffer[CC3000_RX_BUFFER_SIZE - 1] != CC3000_BUFFER_MAGIC_NUMBER) { while (1); } _spi_info.spi_state = eSPI_STATE_IDLE; _event.received_handler(received_buffer + SPI_HEADER_SIZE); } } else if (_spi_info.spi_state == eSPI_STATE_WRITE_IRQ) { write_synchronous(_simple_link.get_transmit_buffer(), _spi_info.tx_packet_length); _spi_info.spi_state = eSPI_STATE_IDLE; _wlan_cs = 1; } } }