nuttx/drivers/wireless/cc3000/cc3000.c
Gregory Nutt 936df1bcb5 Adds new OS internal functions nxsig_sleep() and nxsig_usleep. These differ from the standard sleep() and usleep() in that (1) they don't cause cancellation points, and (2) don't set the errno variable (if applicable). All calls to sleep() and usleep() changed to calls to nxsig_sleep() and nxsig_usleep().
Squashed commit of the following:

    Change all calls to usleep() in the OS proper to calls to nxsig_usleep()

    sched/signal:  Add a new OS internal function nxsig_usleep() that is functionally equivalent to usleep() but does not cause a cancellaption point and does not modify the errno variable.

    sched/signal:  Add a new OS internal function nxsig_sleep() that is functionally equivalent to sleep() but does not cause a cancellaption point.
2017-10-06 10:15:01 -06:00

1854 lines
52 KiB
C

/****************************************************************************
* drivers/wireless/cc3000.c
*
* Copyright (C) 2013-2016 Gregory Nutt. All rights reserved.
* Authors: Gregory Nutt <gnutt@nuttx.org>
* David_s5 <david_s5@nscdg.com>
*
* References:
* CC30000 from Texas Instruments http://processors.wiki.ti.com/index.php/CC3000
*
* See also:
* http://processors.wiki.ti.com/index.php/CC3000_Host_Driver_Porting_Guide
* http://processors.wiki.ti.com/index.php/CC3000_Host_Programming_Guide
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name NuttX 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <poll.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <arpa/inet.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <nuttx/signal.h>
#include <nuttx/semaphore.h>
#include <nuttx/fs/fs.h>
#include <nuttx/spi/spi.h>
#include <nuttx/wireless/ioctl.h>
#include <nuttx/wireless/cc3000.h>
#include <nuttx/wireless/cc3000/include/cc3000_upif.h>
#include <nuttx/wireless/cc3000/cc3000_common.h>
#include <nuttx/wireless/cc3000/hci.h>
#include "cc3000_socket.h"
#include "cc3000.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CCASSERT
#define CCASSERT(predicate) _x_CCASSERT_LINE(predicate, __LINE__)
#define _x_CCASSERT_LINE(predicate, line) typedef char constraint_violated_on_line_##line[2*((predicate)!=0)-1];
#endif
CCASSERT(sizeof(cc3000_buffer_desc) <= CONFIG_MQ_MAXMSGSIZE);
#ifndef CONFIG_CC3000_WORKER_THREAD_PRIORITY
# define CONFIG_CC3000_WORKER_THREAD_PRIORITY (SCHED_PRIORITY_MAX)
#endif
#ifndef CONFIG_CC3000_WORKER_STACKSIZE
# define CONFIG_CC3000_WORKER_STACKSIZE 240
#endif
#ifndef CONFIG_CC3000_SELECT_THREAD_PRIORITY
# define CONFIG_CC3000_SELECT_THREAD_PRIORITY (SCHED_PRIORITY_DEFAULT-1)
#endif
#ifndef CONFIG_CC3000_SELECT_STACKSIZE
# define CONFIG_CC3000_SELECT_STACKSIZE 368
#endif
#ifndef ARRAY_SIZE
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
#define NUMBER_OF_MSGS 1
#define FREE_SLOT -1
#define CLOSE_SLOT -2
#if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_CC3000_PROBES)
# define CC3000_GUARD (0xc35aa53c)
# define INIT_GUARD(p) p->guard = CC3000_GUARD
# define CHECK_GUARD(p) DEBUGASSERT(p->guard == CC3000_GUARD)
# define PROBE(pin,state) priv->config->probe(priv->config,pin, state)
#else
# define INIT_GUARD(p)
# define CHECK_GUARD(p)
# define PROBE(pin,state)
#endif
#define waiterr(x,...) // _err
#define waitinfo(x,...) // _info
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Low-level SPI helpers */
static void cc3000_lock_and_select(FAR struct spi_dev_s *spi);
static void cc3000_deselect_and_unlock(FAR struct spi_dev_s *spi);
/* Interrupts and data sampling */
static void cc3000_notify(FAR struct cc3000_dev_s *priv);
static void *cc3000_worker(FAR void *arg);
static int cc3000_interrupt(int irq, FAR void *context, FAR void *arg);
/* Character driver methods */
static int cc3000_open(FAR struct file *filep);
static int cc3000_close(FAR struct file *filep);
static ssize_t cc3000_read(FAR struct file *filep, FAR char *buffer,
size_t len);
static ssize_t cc3000_write(FAR struct file *filep,
FAR const char *buffer, size_t len);
static int cc3000_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
#ifndef CONFIG_DISABLE_POLL
static int cc3000_poll(FAR struct file *filep, struct pollfd *fds,
bool setup);
#endif
static int cc3000_wait_data(FAR struct cc3000_dev_s *priv, int sockfd);
static int cc3000_accept_socket(FAR struct cc3000_dev_s *priv, int sd,
FAR struct sockaddr *addr,
socklen_t *addrlen);
static int cc3000_add_socket(FAR struct cc3000_dev_s *priv, int sd);
static int cc3000_remove_socket(FAR struct cc3000_dev_s *priv, int sd);
static int cc3000_remote_closed_socket(FAR struct cc3000_dev_s *priv, int sd);
/****************************************************************************
* Private Data
****************************************************************************/
/* This the vtable that supports the character driver interface */
static const struct file_operations cc3000_fops =
{
cc3000_open, /* open */
cc3000_close, /* close */
cc3000_read, /* read */
cc3000_write, /* write */
0, /* seek */
cc3000_ioctl, /* ioctl */
#ifndef CONFIG_DISABLE_POLL
cc3000_poll /* poll */
#endif
};
/* If only a single CC3000 device is supported, then the driver state
* structure may as well be pre-allocated.
*/
#ifndef CONFIG_CC3000_MULTIPLE
static struct cc3000_dev_s g_cc3000;
/* Otherwise, we will need to maintain allocated driver instances in a list */
#else
static struct cc3000_dev_s *g_cc3000list;
#endif
uint8_t spi_readCommand[] = READ_COMMAND;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: cc3000_devtake() and cc3000_devgive()
*
* Description:
* Used to get exclusive access to a CC3000 driver.
*
****************************************************************************/
static inline void cc3000_devtake(FAR struct cc3000_dev_s *priv)
{
int ret;
do
{
/* Take the semaphore (perhaps waiting) */
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
}
static inline void cc3000_devgive(FAR struct cc3000_dev_s *priv)
{
(void)nxsem_post(&priv->devsem);
}
/****************************************************************************
* Name: cc3000_configspi
*
* Description:
* Configure the SPI for use with the CC3000. This function should be
* called to configure the SPI
* bus.
*
* Parameters:
* spi - Reference to the SPI driver structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static inline void cc3000_configspi(FAR struct spi_dev_s *spi)
{
ninfo("Mode: %d Bits: 8 Frequency: %d\n",
CONFIG_CC3000_SPI_MODE, CONFIG_CC3000_SPI_FREQUENCY);
SPI_SETMODE(spi, CONFIG_CC3000_SPI_MODE);
SPI_SETBITS(spi, 8);
(void)SPI_HWFEATURES(spi, 0);
(void)SPI_SETFREQUENCY(spi, CONFIG_CC3000_SPI_FREQUENCY);
}
/****************************************************************************
* Name: cc3000_lock
*
* Description:
* Lock the SPI bus and re-configure as necessary. This function must be
* to assure: (1) exclusive access to the SPI bus, and (2) to assure that
* the shared bus is properly configured for the cc3000 module.
*
* Parameters:
* spi - Reference to the SPI driver structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void cc3000_lock_and_select(FAR struct spi_dev_s *spi)
{
/* Lock the SPI bus so that we have exclusive access */
(void)SPI_LOCK(spi, true);
/* We have the lock. Now make sure that the SPI bus is configured for the
* CC3000 (it might have gotten configured for a different device while
* unlocked)
*/
cc3000_configspi(spi);
SPI_SELECT(spi, SPIDEV_WIRELESS(0), true);
}
/****************************************************************************
* Name: cc3000_unlock
*
* Description:
* Un-lock the SPI bus after each transfer, possibly losing the current
* configuration if we are sharing the SPI bus with other devices.
*
* Parameters:
* spi - Reference to the SPI driver structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void cc3000_deselect_and_unlock(FAR struct spi_dev_s *spi)
{
/* De select */
SPI_SELECT(spi, SPIDEV_WIRELESS(0), false);
/* Relinquish the SPI bus. */
(void)SPI_LOCK(spi, false);
}
/****************************************************************************
* Name: cc3000_wait
*
* Description:
* Helper function to wait on the semaphore signaled by the
*
* Parameters:
* priv - Reference to the CC3000 driver structure
* priv -
*
* Returned Value:
* 0 - Semaphore signaled and devsem retaken
* < 0 - Some Error occurred
* Assumptions:
* Own the devsem on entry
*
****************************************************************************/
static int cc3000_wait(FAR struct cc3000_dev_s *priv, sem_t *psem)
{
int ret;
/* Give up */
sched_lock();
cc3000_devgive(priv);
/* Wait on first psem to become signaled */
ret = nxsem_wait(psem);
if (ret < 0)
{
return ret;
}
/* Then retake the mutual exclusion semaphore */
cc3000_devtake(priv);
sched_unlock();
return OK;
}
/****************************************************************************
* Name: cc3000_wait_irq
*
* Description:
* Helper function to wait on the irqsem signaled by the interrupt
*
* Parameters:
* priv - Reference to the CC3000 driver structure
*
* Returned Value:
* 0 - Semaphore signaled and devsem retaken
* < 0 - Some Error occurred
* Assumptions:
* Own the devsem on entry
*
****************************************************************************/
static inline int cc3000_wait_irq(FAR struct cc3000_dev_s *priv)
{
return cc3000_wait(priv, &priv->irqsem);
}
/****************************************************************************
* Name: cc3000_wait_ready
*
* Description:
* Helper function to wait on the readysem signaled by the interrupt
*
* Parameters:
* priv - Reference to the CC3000 driver structure
*
* Returned Value:
* 0 - Semaphore signaled and devsem retaken
* < 0 - Some Error occurred
* Assumptions:
* Own the devsem on entry
*
****************************************************************************/
static inline int cc3000_wait_ready(FAR struct cc3000_dev_s *priv)
{
return cc3000_wait(priv, &priv->readysem);
}
/****************************************************************************
* Name: cc3000_pollnotify
****************************************************************************/
#ifndef CONFIG_DISABLE_POLL
static void cc3000_pollnotify(FAR struct cc3000_dev_s *priv, uint32_t type)
{
int i;
for (i = 0; i < CONFIG_CC3000_NPOLLWAITERS; i++)
{
struct pollfd *fds = priv->fds[i];
if (fds)
{
fds->revents |= type;
ninfo("Report events: %02x\n", fds->revents);
nxsem_post(fds->sem);
}
}
}
#endif
/****************************************************************************
* Name: cc3000_notify
****************************************************************************/
static void cc3000_notify(FAR struct cc3000_dev_s *priv)
{
/* If there are threads waiting for read data, then signal one of them
* that the read data is available.
*/
if (priv->nwaiters > 0)
{
/* After posting this semaphore, we need to exit because the CC3000
* is no longer available.
*/
nxsem_post(&priv->waitsem);
}
/* If there are threads waiting on poll() for CC3000 data to become available,
* then wake them up now. NOTE: we wake up all waiting threads because we
* do not know that they are going to do. If they all try to read the data,
* then some make end up blocking after all.
*/
#ifndef CONFIG_DISABLE_POLL
cc3000_pollnotify(priv, POLLIN);
#endif
}
/****************************************************************************
* Name: cc3000_worker
****************************************************************************/
static void *select_thread_func(FAR void *arg)
{
FAR struct cc3000_dev_s *priv = (FAR struct cc3000_dev_s *)arg;
struct timeval timeout;
TICC3000fd_set readsds;
TICC3000fd_set exceptsds;
int ret = 0;
int maxFD = 0;
int s = 0;
memset(&timeout, 0, sizeof(struct timeval));
timeout.tv_sec = 0;
timeout.tv_usec = (500000); /* 500 msecs */
while (1)
{
nxsem_wait(&priv->selectsem);
CHECK_GUARD(priv);
/* Increase the count back by one to be decreased by the original caller */
nxsem_post(&priv->selectsem);
maxFD = -1;
CC3000_FD_ZERO(&readsds);
CC3000_FD_ZERO(&exceptsds);
/* Ping correct socket descriptor param for select */
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
if (priv->sockets[s].sd != FREE_SLOT)
{
if (priv->sockets[s].sd == CLOSE_SLOT)
{
priv->sockets[s].sd = FREE_SLOT;
waitinfo("Close\n");
int count;
do
{
nxsem_getvalue(&priv->sockets[s].semwait, &count);
if (count < 0)
{
/* Release the waiting threads */
waitinfo("Closed Signaled %d\n", count);
nxsem_post(&priv->sockets[s].semwait);
}
}
while (count < 0);
continue;
}
CC3000_FD_SET(priv->sockets[s].sd, &readsds);
CC3000_FD_SET(priv->sockets[s].sd, &exceptsds);
if (maxFD <= priv->sockets[s].sd)
{
maxFD = priv->sockets[s].sd + 1;
}
}
}
if (maxFD < 0)
{
/* Handled only socket close. */
continue;
}
/* Polling instead of blocking here to process "accept" below */
ret = cc3000_select(maxFD, (fd_set *) &readsds, NULL,
(fd_set *) &exceptsds, &timeout);
if (priv->selecttid == -1)
{
/* driver close will terminate the thread and by that all sync
* objects owned by it will be released
*/
return OK;
}
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
if ((priv->sockets[s].sd != FREE_SLOT ||
priv->sockets[s].sd != CLOSE_SLOT) && /* Check that the socket is valid */
priv->sockets[s].sd != priv->accepting_socket.acc.sd) /* Verify this is not an accept socket */
{
if (ret > 0 && CC3000_FD_ISSET(priv->sockets[s].sd, &readsds)) /* and has pending data */
{
waitinfo("Signaled %d\n", priv->sockets[s].sd);
nxsem_post(&priv->sockets[s].semwait); /* release the waiting thread */
}
else if (ret > 0 && CC3000_FD_ISSET(priv->sockets[s].sd, &exceptsds)) /* or has pending exception */
{
waitinfo("Signaled %d (exception)\n", priv->sockets[s].sd);
nxsem_post(&priv->sockets[s].semwait); /* release the waiting thread */
}
else if (priv->sockets[s].received_closed_event) /* or remote has closed connection and we have now read all of HW buffer. */
{
waitinfo("Signaled %d (closed & empty)\n", priv->sockets[s].sd);
priv->sockets[s].emptied_and_remotely_closed = true;
nxsem_post(&priv->sockets[s].semwait); /* release the waiting thread */
}
}
}
if (priv->accepting_socket.acc.sd != FREE_SLOT) /* If accept polling in needed */
{
if (priv->accepting_socket.acc.sd == CLOSE_SLOT)
{
ret = CC3000_SOC_ERROR;
}
else
{
ret = cc3000_do_accept(priv->accepting_socket.acc.sd, /* Send the select command on non blocking */
&priv->accepting_socket.addr, /* Set up in ioctl */
&priv->accepting_socket.addrlen);
}
if (ret != CC3000_SOC_IN_PROGRESS) /* Not waiting => error or accepted */
{
priv->accepting_socket.acc.sd = FREE_SLOT;
priv->accepting_socket.acc.status = ret;
nxsem_post(&priv->accepting_socket.acc.semwait); /* Release the waiting thread */
}
}
}
return OK;
}
/****************************************************************************
* Name: cc3000_worker
****************************************************************************/
static void *cc3000_worker(FAR void *arg)
{
FAR struct cc3000_dev_s *priv = (FAR struct cc3000_dev_s *)arg;
int ret;
ASSERT(priv != NULL && priv->config != NULL);
/* We have started, release our creator */
nxsem_post(&priv->readysem);
while (1)
{
PROBE(0, 1);
CHECK_GUARD(priv);
cc3000_devtake(priv);
/* Done ? */
if ((cc3000_wait_irq(priv) != -EINTR) && (priv->workertid != -1))
{
PROBE(0, 0);
ninfo("State%d\n", priv->state);
switch (priv->state)
{
case eSPI_STATE_POWERUP:
/* Signal the device has interrupted after power up */
priv->state = eSPI_STATE_INITIALIZED;
nxsem_post(&priv->readysem);
break;
case eSPI_STATE_WRITE_WAIT_IRQ:
/* Signal the device has interrupted after Chip Select During a write operation */
priv->state = eSPI_STATE_WRITE_PROCEED;
nxsem_post(&priv->readysem);
break;
case eSPI_STATE_WRITE_DONE: /* IRQ post a write => Solicited */
case eSPI_STATE_IDLE: /* IRQ when Idel => cc3000 has data for the hosts Unsolicited */
{
uint16_t data_to_recv;
priv->state = eSPI_STATE_READ_IRQ;
/* Issue the read command */
cc3000_lock_and_select(priv->spi); /* Assert CS */
priv->state = eSPI_STATE_READ_PROCEED;
SPI_EXCHANGE(priv->spi, spi_readCommand, priv->rx_buffer.pbuffer,
ARRAY_SIZE(spi_readCommand));
/* Extract Length bytes from Rx Buffer. Here we need to convert
* unaligned data in network order (big endian, MS byte first) to
* host order. We cannot use ntohs here because the data is not
* aligned.
*/
data_to_recv = (uint16_t)priv->rx_buffer.pbuffer[READ_OFFSET_TO_LENGTH] << 8 |
(uint16_t)priv->rx_buffer.pbuffer[READ_OFFSET_TO_LENGTH + 1];
if (data_to_recv)
{
/* We will read ARRAY_SIZE(spi_readCommand) + data_to_recv. is it odd? */
if ((data_to_recv + ARRAY_SIZE(spi_readCommand)) & 1)
{
/* Odd so make it even */
data_to_recv++;
}
/* Read the whole payload in at the beginning of the buffer
* Will it fit?
*/
if (data_to_recv >= priv->rx_buffer_max_len)
{
ninfo("data_to_recv %d", data_to_recv);
}
DEBUGASSERT(data_to_recv < priv->rx_buffer_max_len);
SPI_RECVBLOCK(priv->spi, priv->rx_buffer.pbuffer, data_to_recv);
}
cc3000_deselect_and_unlock(priv->spi); /* De assert CS */
/* Disable more messages as the wl code will resume via CC3000IOC_COMPLETE */
if (data_to_recv)
{
int count;
priv->state = eSPI_STATE_READ_READY;
priv->rx_buffer.len = data_to_recv;
ret = mq_send(priv->queue, (FAR const char *)&priv->rx_buffer,
sizeof(priv->rx_buffer), 1);
DEBUGASSERT(ret >= 0);
UNUSED(ret);
/* Notify any waiters that new CC3000 data is available */
cc3000_notify(priv);
/* Give up driver */
cc3000_devgive(priv);
ninfo("Wait On Completion\n");
nxsem_wait(priv->wrkwaitsem);
ninfo("Completed S:%d irq :%d\n",
priv->state, priv->config->irq_read(priv->config));
nxsem_getvalue(&priv->irqsem, &count);
if (priv->config->irq_read(priv->config) && count == 0)
{
nxsem_post(&priv->irqsem);
}
if (priv->state == eSPI_STATE_READ_READY)
{
priv->state = eSPI_STATE_IDLE;
}
continue;
}
}
break;
default:
ninfo("default: State%d\n", priv->state);
break;
}
}
cc3000_devgive(priv);
}
return OK;
}
/****************************************************************************
* Name: cc3000_interrupt
****************************************************************************/
static int cc3000_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct cc3000_dev_s *priv = (FAR struct cc3000_dev_s *)arg;
DEBUGASSERT(priv != NULL);
/* Run the worker thread */
PROBE(1, 0);
nxsem_post(&priv->irqsem);
PROBE(1, 1);
/* Clear any pending interrupts and return success */
priv->config->irq_clear(priv->config);
return OK;
}
/****************************************************************************
* Name: cc3000_open
****************************************************************************/
static int cc3000_open(FAR struct file *filep)
{
FAR struct inode *inode;
struct mq_attr attr;
pthread_attr_t tattr;
pthread_t threadid;
struct sched_param param;
char queuename[QUEUE_NAMELEN];
FAR struct cc3000_dev_s *priv;
uint8_t tmp;
int ret;
#ifdef CONFIG_CC3000_MT
int s;
#endif
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct cc3000_dev_s *)inode->i_private;
CHECK_GUARD(priv);
ninfo("crefs: %d\n", priv->crefs);
/* Get exclusive access to the driver data structure */
cc3000_devtake(priv);
/* Increment the reference count */
tmp = priv->crefs + 1;
if (tmp == 0)
{
/* More than 255 opens; uint8_t overflows to zero */
ret = -EMFILE;
goto out_with_sem;
}
/* When the reference increments to 1, this is the first open event
* on the driver.. and an opportunity to do any one-time initialization.
*/
if (tmp == 1)
{
/* Initialize semaphores */
nxsem_init(&priv->waitsem, 0, 0); /* Initialize event wait semaphore */
nxsem_init(&priv->irqsem, 0, 0); /* Initialize IRQ Ready semaphore */
nxsem_init(&priv->readysem, 0, 0); /* Initialize Device Ready semaphore */
/* These semaphores are all used for signaling and, hence, should
* not have priority inheritance enabled.
*/
nxsem_setprotocol(&priv->waitsem, SEM_PRIO_NONE);
nxsem_setprotocol(&priv->irqsem, SEM_PRIO_NONE);
nxsem_setprotocol(&priv->readysem, SEM_PRIO_NONE);
#ifdef CONFIG_CC3000_MT
priv->accepting_socket.acc.sd = FREE_SLOT;
nxsem_init(&priv->accepting_socket.acc.semwait, 0, 0);
nxsem_setprotocol(&priv->accepting_socket.acc.semwait, SEM_PRIO_NONE);
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
priv->sockets[s].sd = FREE_SLOT;
priv->sockets[s].received_closed_event = false;
priv->sockets[s].emptied_and_remotely_closed = false;
nxsem_init(&priv->sockets[s].semwait, 0, 0);
nxsem_setprotocol(&priv->sockets[s].semwait, SEM_PRIO_NONE);
}
#endif
/* Ensure the power is off so we get the falling edge of IRQ */
priv->config->power_enable(priv->config, false);
attr.mq_maxmsg = NUMBER_OF_MSGS;
attr.mq_msgsize = sizeof(cc3000_buffer_desc);
attr.mq_flags = 0;
/* Set the flags for the open of the queue.
* Make it a blocking open on the queue, meaning it will block if
* this process tries to send to the queue and the queue is full.
*
* O_CREAT - the queue will get created if it does not already exist.
* O_WRONLY - we are only planning to write to the queue.
*
* Open the queue, and create it if the receiving process hasn't
* already created it.
*/
snprintf(queuename, QUEUE_NAMELEN, QUEUE_FORMAT, priv->minor);
priv->queue = mq_open(queuename, O_WRONLY | O_CREAT, 0666, &attr);
if (priv->queue < 0)
{
ret = -errno;
goto errout_sem_destroy;
}
pthread_attr_init(&tattr);
tattr.stacksize = CONFIG_CC3000_WORKER_STACKSIZE;
param.sched_priority = CONFIG_CC3000_WORKER_THREAD_PRIORITY;
pthread_attr_setschedparam(&tattr, &param);
ret = pthread_create(&priv->workertid, &tattr, cc3000_worker,
(pthread_addr_t)priv);
if (ret != 0)
{
ret = -ret; /* pthread_create does not modify errno. */
goto errout_mq_close;
}
pthread_attr_init(&tattr);
tattr.stacksize = CONFIG_CC3000_SELECT_STACKSIZE;
param.sched_priority = CONFIG_CC3000_SELECT_THREAD_PRIORITY;
pthread_attr_setschedparam(&tattr, &param);
nxsem_init(&priv->selectsem, 0, 0);
nxsem_setprotocol(&priv->selectsem, SEM_PRIO_NONE);
ret = pthread_create(&priv->selecttid, &tattr, select_thread_func,
(pthread_addr_t)priv);
if (ret != 0)
{
ret = -ret; /* pthread_create does not modify errno. */
goto errout_worker_cancel;
}
/* Do late allocation with hopes of realloc not fragmenting */
priv->rx_buffer.pbuffer = kmm_malloc(priv->rx_buffer_max_len);
DEBUGASSERT(priv->rx_buffer.pbuffer);
if (!priv->rx_buffer.pbuffer)
{
ret = -errno;
goto errout_select_cancel;
}
priv->state = eSPI_STATE_POWERUP;
priv->config->irq_clear(priv->config);
/* Bring the device Online A) on http://processors.wiki.ti.com/index.php/File:CC3000_Master_SPI_Write_Sequence_After_Power_Up.png */
priv->config->irq_enable(priv->config, true);
/* Wait on child thread */
cc3000_wait_ready(priv);
priv->config->power_enable(priv->config, true);
}
/* Save the new open count on success */
priv->crefs = tmp;
ret = OK;
goto out_with_sem;
errout_select_cancel:
threadid = priv->selecttid;
priv->selecttid = -1;
pthread_cancel(threadid);
pthread_join(threadid, NULL);
nxsem_destroy(&priv->selectsem);
errout_worker_cancel:
threadid = priv->workertid;
priv->workertid = -1;
pthread_cancel(threadid);
pthread_join(threadid, NULL);
errout_mq_close:
mq_close(priv->queue);
priv->queue = 0;
errout_sem_destroy:
#ifdef CONFIG_CC3000_MT
nxsem_destroy(&priv->accepting_socket.acc.semwait);
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
priv->sockets[s].sd = FREE_SLOT;
nxsem_destroy(&priv->sockets[s].semwait);
}
#endif
nxsem_destroy(&priv->waitsem);
nxsem_destroy(&priv->irqsem);
nxsem_destroy(&priv->readysem);
out_with_sem:
cc3000_devgive(priv);
return ret;
}
/****************************************************************************
* Name: cc3000_close
****************************************************************************/
static int cc3000_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct cc3000_dev_s *priv;
#ifdef CONFIG_CC3000_MT
int s;
#endif
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct cc3000_dev_s *)inode->i_private;
CHECK_GUARD(priv);
ninfo("crefs: %d\n", priv->crefs);
/* Get exclusive access to the driver data structure */
cc3000_devtake(priv);
/* Decrement the reference count unless it would decrement a negative
* value. When the count decrements to zero, there are no further
* open references to the driver.
*/
int tmp = priv->crefs;
if (priv->crefs >= 1)
{
priv->crefs--;
}
if (tmp == 1)
{
pthread_t id = priv->selecttid;
priv->selecttid = -1;
pthread_cancel(id);
pthread_join(id, NULL);
nxsem_destroy(&priv->selectsem);
priv->config->irq_enable(priv->config, false);
priv->config->irq_clear(priv->config);
priv->config->power_enable(priv->config, false);
id = priv->workertid;
priv->workertid = -1;
pthread_cancel(id);
pthread_join(id, NULL);
mq_close(priv->queue);
priv->queue = 0;
kmm_free(priv->rx_buffer.pbuffer);
priv->rx_buffer.pbuffer = 0;
#ifdef CONFIG_CC3000_MT
nxsem_destroy(&priv->accepting_socket.acc.semwait);
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
priv->sockets[s].sd = FREE_SLOT;
nxsem_destroy(&priv->sockets[s].semwait);
}
#endif
nxsem_destroy(&priv->waitsem);
nxsem_destroy(&priv->irqsem);
nxsem_destroy(&priv->readysem);
}
cc3000_devgive(priv);
return OK;
}
/****************************************************************************
* Name: cc3000_read
****************************************************************************/
static ssize_t cc3000_read(FAR struct file *filep, FAR char *buffer, size_t len)
{
FAR struct inode *inode;
FAR struct cc3000_dev_s *priv;
int ret;
ssize_t nread;
ninfo("buffer:%p len:%d\n", buffer, len);
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct cc3000_dev_s *)inode->i_private;
CHECK_GUARD(priv);
/* Get exclusive access to the driver data structure */
cc3000_devtake(priv);
/* Verify that the caller has provided a buffer large enough to receive
* the maximum data.
*/
if (len < priv->rx_buffer_max_len)
{
nerr("ERROR: Unsupported read size: %d\n", len);
nread = -ENOSYS;
goto errout_with_sem;
}
for (nread = priv->rx_buffer.len; nread == 0; nread = priv->rx_buffer.len)
{
if (nread > 0)
{
/* Yes.. break out to return what we have. */
break;
}
/* data is not available now. We would have to wait to get
* receive sample data. If the user has specified the O_NONBLOCK
* option, then just return an error.
*/
ninfo("CC3000 data is not available\n");
if (filep->f_oflags & O_NONBLOCK)
{
nread = -EAGAIN;
break;
}
/* Otherwise, wait for something to be written to the
* buffer. Increment the number of waiters so that the notify
* will know that it needs to post the semaphore to wake us up.
*/
sched_lock();
priv->nwaiters++;
cc3000_devgive(priv);
/* We may now be pre-empted! But that should be okay because we
* have already incremented nwaiters. Pre-emptions is disabled
* but will be re-enabled while we are waiting.
*/
ninfo("Waiting..\n");
ret = nxsem_wait(&priv->waitsem);
priv->nwaiters--;
sched_unlock();
/* Did we successfully get the waitsem? */
if (ret >= 0)
{
/* Yes... then retake the mutual exclusion semaphore */
cc3000_devtake(priv);
}
/* Was the semaphore wait successful? Did we successful re-take the
* mutual exclusion semaphore?
*/
if (ret < 0)
{
/* No.. One of the two nxsem_wait's failed. */
/* Were we awakened by a signal? Did we read anything before
* we received the signal?
*/
if (ret != -EINTR || nread >= 0)
{
/* Yes.. return the error. */
nread = ret;
}
/* Break out to return what we have. Note, we can't exactly
* "break" out because whichever error occurred, we do not hold
* the exclusion semaphore.
*/
goto errout_without_sem;
}
}
if (nread > 0)
{
memcpy(buffer, priv->rx_buffer.pbuffer, priv->rx_buffer.len);
priv->rx_buffer.len = 0;
}
errout_with_sem:
cc3000_devgive(priv);
errout_without_sem:
ninfo("Returning: %d\n", nread);
#ifndef CONFIG_DISABLE_POLL
if (nread > 0)
{
cc3000_pollnotify(priv, POLLOUT);
}
#endif
return nread;
}
/****************************************************************************
* Name:cc3000_write
*
* Bit of non standard buffer management ahead
* The buffer is memory allocated in the user space with space for the spi
* header
*
****************************************************************************/
static ssize_t cc3000_write(FAR struct file *filep, FAR const char *usrbuffer, size_t len)
{
FAR struct inode *inode;
FAR struct cc3000_dev_s *priv;
FAR char *buffer = (FAR char *) usrbuffer;
ssize_t nwritten = 0;
int ret;
/* Set the padding if count(buffer) is even ( as it will be come odd with header) */
size_t tx_len = (len & 1) ? len : len +1;
ninfo("buffer:%p len:%d tx_len:%d\n", buffer, len, tx_len);
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct cc3000_dev_s *)inode->i_private;
CHECK_GUARD(priv);
/* Get exclusive access to the driver data structure */
cc3000_devtake(priv);
/* Figure out the total length of the packet in order to figure out if there is padding or not */
buffer[0] = WRITE;
buffer[1] = HI(tx_len);
buffer[2] = LO(tx_len);
buffer[3] = 0;
buffer[4] = 0;
tx_len += SPI_HEADER_SIZE;
/* The first write transaction to occur after release of the shutdown has
* slightly different timing than the others. The normal Master SPI
* write sequence is nCS low, followed by IRQ low (CC3000 host),
* indicating that the CC3000 core device is ready to accept data.
* However, after power up the sequence is slightly different, as shown
* in the following Figure:
*
* http://processors.wiki.ti.com/index.php/File:CC3000_Master_SPI_Write_Sequence_After_Power_Up.png
*
* The following is a sequence of operations:
* - The master detects the IRQ line low: in this case the detection of
* IRQ low does not indicate the intention of the CC3000 device to
* communicate with the master but rather CC3000 readiness after power
* up.
* - The master asserts nCS.
* - The master introduces a delay of at least 50 μs before starting
* actual transmission of data.
* - The master transmits the first 4 bytes of the SPI header.
* - The master introduces a delay of at least an additional 50 μs.
* - The master transmits the rest of the packet.
*/
if (priv->state == eSPI_STATE_POWERUP)
{
ret = cc3000_wait_ready(priv);
if (ret < 0)
{
nwritten = ret;
goto errout_without_sem;
}
}
if (priv->state == eSPI_STATE_INITIALIZED)
{
cc3000_lock_and_select(priv->spi); /* Assert CS */
nxsig_usleep(55);
SPI_SNDBLOCK(priv->spi, buffer, 4);
nxsig_usleep(55);
SPI_SNDBLOCK(priv->spi, buffer+4, tx_len-4);
}
else
{
ninfo("Assert CS\n");
priv->state = eSPI_STATE_WRITE_WAIT_IRQ;
cc3000_lock_and_select(priv->spi); /* Assert CS */
ninfo("Wait on IRQ Active\n");
ret = cc3000_wait_ready(priv);
ninfo("IRQ Signaled\n");
if (ret < 0)
{
/* This should only happen if the wait was canceled by an signal */
cc3000_deselect_and_unlock(priv->spi);
ninfo("nxsem_wait: %d\n", ret);
DEBUGASSERT(ret == -EINTR);
nwritten = ret;
goto errout_without_sem;
}
SPI_SNDBLOCK(priv->spi, buffer, tx_len);
}
priv->state = eSPI_STATE_WRITE_DONE;
ninfo("Deassert CS S:eSPI_STATE_WRITE_DONE\n");
cc3000_deselect_and_unlock(priv->spi);
nwritten = tx_len;
cc3000_devgive(priv);
errout_without_sem:
ninfo("Returning: %d\n", ret);
return nwritten;
}
/****************************************************************************
* Name:cc3000_ioctl
****************************************************************************/
static int cc3000_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode;
FAR struct cc3000_dev_s *priv;
int ret;
ninfo("cmd: %d arg: %ld\n", cmd, arg);
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct cc3000_dev_s *)inode->i_private;
CHECK_GUARD(priv);
/* Get exclusive access to the driver data structure */
cc3000_devtake(priv);
/* Process the IOCTL by command */
ret = OK;
switch (cmd)
{
case CC3000IOC_GETQUESEMID:
{
FAR int *pminor = (FAR int *)(arg);
DEBUGASSERT(pminor != NULL);
*pminor = priv->minor;
break;
}
case CC3000IOC_ADDSOCKET:
{
FAR int *pfd = (FAR int *)(arg);
DEBUGASSERT(pfd != NULL);
*pfd = cc3000_add_socket(priv, *pfd);
break;
}
case CC3000IOC_REMOVESOCKET:
{
FAR int *pfd = (FAR int *)(arg);
DEBUGASSERT(pfd != NULL);
*pfd = cc3000_remove_socket(priv, *pfd);
break;
}
case CC3000IOC_REMOTECLOSEDSOCKET:
{
FAR int *pfd = (FAR int *)(arg);
DEBUGASSERT(pfd != NULL);
*pfd = cc3000_remote_closed_socket(priv, *pfd);
break;
}
case CC3000IOC_SELECTDATA:
{
FAR int *pfd = (FAR int *)(arg);
DEBUGASSERT(pfd != NULL);
*pfd = cc3000_wait_data(priv, *pfd);
break;
}
case CC3000IOC_SELECTACCEPT:
{
FAR cc3000_acceptcfg *pcfg = (FAR cc3000_acceptcfg *)(arg);
DEBUGASSERT(pcfg != NULL);
pcfg->sockfd = cc3000_accept_socket(priv, pcfg->sockfd, pcfg->addr, pcfg->addrlen);
break;
}
case CC3000IOC_SETRX_SIZE:
{
irqstate_t flags;
FAR int *psize = (FAR int *)(arg);
int rv;
DEBUGASSERT(psize != NULL);
rv = priv->rx_buffer_max_len;
flags = enter_critical_section();
priv->rx_buffer_max_len = *psize;
priv->rx_buffer.pbuffer = kmm_realloc(priv->rx_buffer.pbuffer, *psize);
leave_critical_section(flags);
DEBUGASSERT(priv->rx_buffer.pbuffer);
*psize = rv;
break;
}
default:
ret = -ENOTTY;
break;
}
cc3000_devgive(priv);
return ret;
}
/****************************************************************************
* Name: cc3000_poll
****************************************************************************/
#ifndef CONFIG_DISABLE_POLL
static int cc3000_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup)
{
FAR struct inode *inode;
FAR struct cc3000_dev_s *priv;
int ret = OK;
int i;
ninfo("setup: %d\n", (int)setup);
DEBUGASSERT(filep && fds);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct cc3000_dev_s *)inode->i_private;
CHECK_GUARD(priv);
/* Are we setting up the poll? Or tearing it down? */
cc3000_devtake(priv);
if (setup)
{
/* Ignore waits that do not include POLLIN */
if ((fds->events & POLLIN) == 0)
{
ret = -EDEADLK;
goto errout;
}
/* This is a request to set up the poll. Find an available
* slot for the poll structure reference
*/
for (i = 0; i < CONFIG_CC3000_NPOLLWAITERS; i++)
{
/* Find an available slot */
if (!priv->fds[i])
{
/* Bind the poll structure and this slot */
priv->fds[i] = fds;
fds->priv = &priv->fds[i];
break;
}
}
if (i >= CONFIG_CC3000_NPOLLWAITERS)
{
fds->priv = NULL;
ret = -EBUSY;
goto errout;
}
/* Should we immediately notify on any of the requested events? */
if (priv->rx_buffer.len)
{
cc3000_notify(priv);
}
}
else if (fds->priv)
{
/* This is a request to tear down the poll. */
struct pollfd **slot = (struct pollfd **)fds->priv;
DEBUGASSERT(slot != NULL);
/* Remove all memory of the poll setup */
*slot = NULL;
fds->priv = NULL;
}
errout:
cc3000_devgive(priv);
return ret;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: cc3000_register
*
* Description:
* Configure the CC3000 to use the provided SPI device instance. This
* will register the driver as /dev/inputN where N is the minor device
* number
*
* Input Parameters:
* dev - An SPI driver instance
* config - Persistent board configuration data
* minor - The input device minor number
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
* returned to indicate the nature of the failure.
*
****************************************************************************/
int cc3000_register(FAR struct spi_dev_s *spi,
FAR struct cc3000_config_s *config, int minor)
{
FAR struct cc3000_dev_s *priv;
char drvname[DEV_NAMELEN];
char semname[SEM_NAMELEN];
#ifdef CONFIG_CC3000_MULTIPLE
irqstate_t flags;
#endif
int ret;
ninfo("spi: %p minor: %d\n", spi, minor);
/* Debug-only sanity checks */
DEBUGASSERT(spi != NULL && config != NULL && minor >= 0 && minor < 100);
/* Create and initialize a CC3000 device driver instance */
#ifndef CONFIG_CC3000_MULTIPLE
priv = &g_cc3000;
#else
priv = (FAR struct cc3000_dev_s *)kmm_malloc(sizeof(struct cc3000_dev_s));
if (!priv)
{
nerr("ERROR: kmm_malloc(%d) failed\n", sizeof(struct cc3000_dev_s));
return -ENOMEM;
}
#endif
/* Initialize the CC3000 device driver instance */
memset(priv, 0, sizeof(struct cc3000_dev_s));
INIT_GUARD(priv);
priv->minor = minor; /* Save the minor number */
priv->spi = spi; /* Save the SPI device handle */
priv->config = config; /* Save the board configuration */
priv->rx_buffer_max_len = config->max_rx_size;
nxsem_init(&priv->devsem, 0, 1); /* Initialize device structure semaphore */
(void)snprintf(semname, SEM_NAMELEN, SEM_FORMAT, minor);
priv->wrkwaitsem = sem_open(semname, O_CREAT, 0, 0); /* Initialize Worker Wait semaphore */
#ifdef CONFIG_CC3000_MT
pthread_mutex_init(&g_cc3000_mut, NULL);
#endif
/* Make sure that interrupts are disabled */
config->irq_clear(config);
config->irq_enable(config, false);
/* Attach the interrupt handler */
ret = config->irq_attach(config, cc3000_interrupt, priv);
if (ret < 0)
{
nerr("ERROR: Failed to attach interrupt\n");
goto errout_with_priv;
}
/* Register the device as an input device */
(void)snprintf(drvname, DEV_NAMELEN, DEV_FORMAT, minor);
ninfo("Registering %s\n", drvname);
ret = register_driver(drvname, &cc3000_fops, 0666, priv);
if (ret < 0)
{
nerr("ERROR: register_driver() failed: %d\n", ret);
goto errout_with_priv;
}
/* If multiple CC3000 devices are supported, then we will need to add
* this new instance to a list of device instances so that it can be
* found by the interrupt handler based on the recieved IRQ number.
*/
#ifdef CONFIG_CC3000_MULTIPLE
priv->flink = g_cc3000list;
g_cc3000list = priv;
leave_critical_section(flags);
#endif
/* And return success (?) */
return OK;
errout_with_priv:
nxsem_destroy(&priv->devsem);
sem_close(priv->wrkwaitsem);
sem_unlink(semname);
#ifdef CONFIG_CC3000_MT
pthread_mutex_destroy(&g_cc3000_mut);
#endif
#ifdef CONFIG_CC3000_MULTIPLE
kmm_free(priv);
#endif
return ret;
}
/****************************************************************************
* Name: cc3000_wait_data
*
* Description:
* Adds this socket for monitoring for the data available
*
* Input Parameters:
* priv - The device cc3000_dev_s instance
* sockfd cc3000 socket handle
*
* Returned Value:
* Zero is returned on success. Otherwise, a -1 value is
* returned to indicate socket not found or shut down occured.
*
****************************************************************************/
static int cc3000_wait_data(FAR struct cc3000_dev_s *priv, int sockfd)
{
int s;
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
if (priv->sockets[s].sd == sockfd)
{
sched_lock();
cc3000_devgive(priv);
nxsem_post(&priv->selectsem); /* Wake select thread if need be */
nxsem_wait(&priv->sockets[s].semwait); /* Wait caller on select to finish */
nxsem_wait(&priv->selectsem); /* Sleep select thread */
cc3000_devtake(priv);
sched_unlock();
if (priv->sockets[s].sd != sockfd)
{
return -1;
}
if (priv->sockets[s].emptied_and_remotely_closed)
{
return -ECONNABORTED;
}
return OK;
}
}
return (s >= CONFIG_WL_MAX_SOCKETS || priv->selecttid == -1) ? -1 : OK;
}
/****************************************************************************
* Name: cc3000_accept_socket
*
* Description:
* Adds this socket for monitoring for the accept operation
*
* Input Parameters:
* priv - The device cc3000_dev_s instance
* sockfd - cc3000 socket handle to monitor
*
* Returned Value:
* Zero is returned on success. Otherwise, a negative value is
* returned to indicate an error.
*
****************************************************************************/
static int cc3000_accept_socket(FAR struct cc3000_dev_s *priv, int sd, struct sockaddr *addr,
socklen_t *addrlen)
{
priv->accepting_socket.acc.status = CC3000_SOC_ERROR;
priv->accepting_socket.acc.sd = sd;
sched_lock();
cc3000_devgive(priv);
nxsem_post(&priv->selectsem); /* Wake select thread if need be */
nxsem_wait(&priv->accepting_socket.acc.semwait); /* Wait caller on select to finish */
nxsem_wait(&priv->selectsem); /* Sleep the Thread */
cc3000_devtake(priv);
sched_unlock();
if (priv->accepting_socket.acc.status != CC3000_SOC_ERROR)
{
*addr = priv->accepting_socket.addr;
*addrlen = priv->accepting_socket.addrlen;
cc3000_add_socket(priv, priv->accepting_socket.acc.status);
}
return priv->accepting_socket.acc.status;
}
/****************************************************************************
* Name: cc3000_add_socket
*
* Description:
* Adds a socket to the list for monitoring for long operation
*
* Input Parameters:
* sd cc3000 socket handle
* minor - The input device minor number
*
* Returned Value:
* Zero is returned on success. Otherwise, a -1 value is
* returned to indicate socket not found.
*
****************************************************************************/
static int cc3000_add_socket(FAR struct cc3000_dev_s *priv, int sd)
{
irqstate_t flags;
int s;
if (sd < 0)
{
return sd;
}
flags = enter_critical_section();
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
if (priv->sockets[s].sd == FREE_SLOT)
{
priv->sockets[s].received_closed_event = false;
priv->sockets[s].emptied_and_remotely_closed = false;
priv->sockets[s].sd = sd;
break;
}
}
leave_critical_section(flags);
return s >= CONFIG_WL_MAX_SOCKETS ? -1 : OK;
}
/****************************************************************************
* Name: cc3000_remove_socket
*
* Description:
* Removes a socket from the list of monitoring for long operation
*
* Input Parameters:
* sd cc3000 socket handle
* minor - The input device minor number
*
* Returned Value:
* Zero is returned on success. Otherwise, a -1 value is
* returned to indicate socket not found.
*
****************************************************************************/
static int cc3000_remove_socket(FAR struct cc3000_dev_s *priv, int sd)
{
irqstate_t flags;
int s;
sem_t *ps = 0;
if (sd < 0)
{
return sd;
}
flags = enter_critical_section();
if (priv->accepting_socket.acc.sd == sd)
{
priv->accepting_socket.acc.sd = CLOSE_SLOT;
ps = &priv->accepting_socket.acc.semwait;
}
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
if (priv->sockets[s].sd == sd)
{
priv->sockets[s].received_closed_event = false;
priv->sockets[s].emptied_and_remotely_closed = false;
priv->sockets[s].sd = CLOSE_SLOT;
ps = &priv->sockets[s].semwait;
break;
}
}
leave_critical_section(flags);
if (ps)
{
sched_lock();
cc3000_devgive(priv);
nxsem_post(&priv->selectsem); /* Wake select thread if need be */
nxsem_wait(ps);
nxsem_wait(&priv->selectsem); /* Sleep the Thread */
cc3000_devtake(priv);
sched_unlock();
}
return s >= CONFIG_WL_MAX_SOCKETS ? -1 : OK;
}
/****************************************************************************
* Name: cc3000_remote_closed_socket
*
* Description:
* Mark socket as closed by remote host
*
* Input Parameters:
* sd cc3000 socket handle
*
* Returned Value:
* Zero is returned on success. Otherwise, a -1 value is
* returned to indicate socket not found.
*
****************************************************************************/
static int cc3000_remote_closed_socket(FAR struct cc3000_dev_s *priv, int sd)
{
irqstate_t flags;
int s;
if (sd < 0)
{
return sd;
}
flags = enter_critical_section();
for (s = 0; s < CONFIG_WL_MAX_SOCKETS; s++)
{
if (priv->sockets[s].sd == sd)
{
priv->sockets[s].received_closed_event = true;
}
}
leave_critical_section(flags);
return s >= CONFIG_WL_MAX_SOCKETS ? -1 : OK;
}