nuttx/drivers/wireless/gs2200m.c
Masayuki Ishikawa 350ffb20ad drivers: wireless: Improve packet receiving performance in gs2200m.c
Signed-off-by: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
2020-04-30 09:09:17 +02:00

3060 lines
73 KiB
C

/****************************************************************************
* drivers/wireless/gs2200m.c
*
* Copyright 2019 Sony Home Entertainment & Sound Products Inc.
* Author: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
*
* 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.
*
****************************************************************************/
/****************************************************************************
* gs2200m driver.
*
* See "GS2200MS2W Adapter Command Reference Guide" for the explanation
* of AT commands. You can find the document at:
* https://telit.com/m2m-iot-products/wifi-bluetooth-modules/wi-fi-gs2200m/
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <debug.h>
#include <poll.h>
#include <nuttx/ascii.h>
#include <nuttx/arch.h>
#include <nuttx/spi/spi.h>
#include <nuttx/kmalloc.h>
#include <nuttx/wqueue.h>
#include <nuttx/semaphore.h>
#include <nuttx/signal.h>
#include <nuttx/wireless/gs2200m.h>
#include <nuttx/net/netdev.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if !defined(CONFIG_SCHED_WORKQUEUE)
# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
#endif
#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#define GS2200MWORK LPWORK
#define SPI_MAXFREQ CONFIG_WL_GS2200M_SPI_FREQUENCY
#define NRESPMSG 8
#define MAX_PKT_LEN 1500
#define MAX_NOTIF_Q 16
#define WR_REQ 0x01
#define RD_REQ 0x02
#define DT_FROM_MCU 0x03
#define WR_RESP_OK 0x11
#define RD_RESP_OK 0x12
#define WR_RESP_NOK 0x13
#define RD_RESP_NOK 0x14
#define LED_GPIO 30
#define HAL_TIMEOUT 5000000 /* in us */
#define WR_MAX_RETRY 100
/****************************************************************************
* Private Data Types
****************************************************************************/
enum pkt_state_e
{
PKT_START = 0,
PKT_EVENT,
PKT_ESC_START,
PKT_BULK_DATA_TCP,
PKT_BULK_DATA_UDP,
};
enum spi_status_e
{
SPI_OK = 0,
SPI_ERROR,
SPI_TIMEOUT
};
enum pkt_type_e
{
TYPE_OK = 0,
TYPE_ERROR = 1,
TYPE_DISCONNECT = 2,
TYPE_CONNECT = 3,
TYPE_BOOT_MSG = 4,
TYPE_BULK_DATA_TCP = 5,
TYPE_BULK_DATA_UDP = 6,
TYPE_FAIL = 7,
TYPE_TIMEOUT = 8,
TYPE_SPI_ERROR = 9,
TYPE_UNMATCH = 10,
};
struct evt_code_s
{
FAR const char *str;
enum pkt_type_e code;
};
struct pkt_dat_s
{
struct dq_entry_s dq;
enum pkt_type_e type;
struct sockaddr_in addr;
char cid;
uint8_t n;
FAR char *msg[NRESPMSG];
uint16_t remain; /* bulk data length to be read */
uint16_t len; /* bulk data length */
FAR uint8_t *data; /* bulk data */
};
struct pkt_ctx_s
{
enum pkt_type_e type;
enum pkt_state_e state;
FAR uint8_t *ptr;
FAR uint8_t *head;
char cid;
uint16_t dlen;
};
struct notif_q_s
{
uint8_t rpos;
uint8_t wpos;
uint8_t count;
uint16_t inuse;
char cids[MAX_NOTIF_Q];
};
struct gs2200m_dev_s
{
FAR char *path;
FAR struct pollfd *pfd;
struct notif_q_s notif_q;
FAR struct spi_dev_s *spi;
struct work_s irq_work;
sem_t dev_sem;
dq_queue_t pkt_q[16];
uint8_t pkt_q_cnt[16];
uint16_t valid_cid_bits;
uint16_t aip_cid_bits;
uint8_t tx_buff[MAX_PKT_LEN];
struct net_driver_s net_dev;
uint8_t op_mode;
FAR const struct gs2200m_lower_s *lower;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Character driver methods */
static int gs2200m_open(FAR struct file *filep);
static int gs2200m_close(FAR struct file *filep);
static ssize_t gs2200m_read(FAR struct file *filep, FAR char *buff,
size_t len);
static ssize_t gs2200m_write(FAR struct file *filep, FAR const char *buff,
size_t len);
static int gs2200m_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static int gs2200m_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
/* Interrupt handler and work queue handler */
static int gs2200m_irq(int irq, FAR void *context, FAR void *arg);
static void gs2200m_irq_worker(FAR void *arg);
/****************************************************************************
* Private Data
****************************************************************************/
/* This the vtable that supports the character driver interface. */
static const struct file_operations g_gs2200m_fops =
{
gs2200m_open, /* open */
gs2200m_close, /* close */
gs2200m_read, /* read */
gs2200m_write, /* write */
NULL, /* seek */
gs2200m_ioctl, /* ioctl */
gs2200m_poll, /* poll */
NULL /* unlink */
};
static struct evt_code_s _evt_table[] =
{
{"OK", TYPE_OK},
{"ERROR", TYPE_ERROR},
{"DISCONNECT", TYPE_DISCONNECT},
{"CONNECT", TYPE_CONNECT},
{"Serial2WiFi APP", TYPE_BOOT_MSG}
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: _spi_err_to_pkt_type
****************************************************************************/
static enum pkt_type_e _spi_err_to_pkt_type(enum spi_status_e s)
{
enum pkt_type_e r;
switch (s)
{
case SPI_OK:
r = TYPE_OK;
break;
case SPI_ERROR:
r = TYPE_SPI_ERROR;
break;
case SPI_TIMEOUT:
r = TYPE_TIMEOUT;
break;
default:
ASSERT(false);
}
return r;
}
/****************************************************************************
* Name: _cid_to_uint8
****************************************************************************/
static uint8_t _cid_to_uint8(char c)
{
uint8_t ret;
if ('0' <= c && c <= '9')
{
ret = c - '0';
}
else if ('a' <= c && c <= 'f')
{
ret = (c - 'a') + 10;
}
else
{
ASSERT(false);
}
return ret;
}
/****************************************************************************
* Name: _to_ascii_char
****************************************************************************/
static void _to_ascii_char(uint16_t num, char *str)
{
DEBUGASSERT(num <= 2032); /* See Table 20 */
snprintf(str, 5, "%04d", num);
}
/****************************************************************************
* Name: _to_uint16
****************************************************************************/
static uint16_t _to_uint16(char *str)
{
uint16_t ret = 0;
int n;
n = sscanf(str, "%04d", &ret);
ASSERT(1 == n);
return ret;
}
/****************************************************************************
* Name: _enable_cid
****************************************************************************/
static bool _enable_cid(uint16_t *cid_bits, char cid, bool on)
{
uint16_t mask = 1 << _cid_to_uint8(cid);
bool ret = true;
if (on)
{
if (*cid_bits & mask)
{
ret = false; /* already set */
}
else
{
*cid_bits |= mask;
}
}
else
{
if (*cid_bits & mask)
{
*cid_bits &= ~mask;
}
else
{
ret = false; /* not set yet */
}
}
return ret;
}
/****************************************************************************
* Name: _cid_is_set
****************************************************************************/
static bool _cid_is_set(uint16_t *cid_bits, char cid)
{
uint16_t mask = 1 << _cid_to_uint8(cid);
if (*cid_bits & mask)
{
return true;
}
else
{
return false;
}
}
/****************************************************************************
* Name: _notif_q_count()
****************************************************************************/
static uint8_t _notif_q_count(FAR struct gs2200m_dev_s *dev)
{
return dev->notif_q.count;
}
/****************************************************************************
* Name: _notif_q_push()
****************************************************************************/
static void _notif_q_push(FAR struct gs2200m_dev_s *dev, char cid)
{
ASSERT(MAX_NOTIF_Q > dev->notif_q.count);
/* Set cid in _notif_q.inuse */
bool ret = _enable_cid(&dev->notif_q.inuse, cid, true);
if (false == ret)
{
/* already registered */
return;
}
dev->notif_q.cids[dev->notif_q.wpos % MAX_NOTIF_Q] = cid;
dev->notif_q.wpos++;
dev->notif_q.count++;
if (dev->pfd)
{
/* If poll() waits and cid has been pushed to the queue, notify */
dev->pfd->revents |= POLLIN;
nxsem_post(dev->pfd->sem);
}
wlinfo("+++ pushed %c count=%d \n", cid, dev->notif_q.count);
}
/****************************************************************************
* Name: _notif_q_pop()
****************************************************************************/
static char _notif_q_pop(FAR struct gs2200m_dev_s *dev)
{
char cid;
ASSERT(0 < dev->notif_q.count);
cid = dev->notif_q.cids[dev->notif_q.rpos % MAX_NOTIF_Q];
dev->notif_q.rpos++;
dev->notif_q.count--;
/* Clear cid in _notif_q.inuse */
_enable_cid(&dev->notif_q.inuse, cid, false);
return cid;
}
/****************************************************************************
* Name: _push_data_to_pkt
****************************************************************************/
static void _push_data_to_pkt(struct pkt_dat_s *pkt, uint8_t data)
{
ASSERT(pkt->len < MAX_PKT_LEN);
pkt->data[pkt->len++] = data;
pkt->remain = pkt->len;
}
/****************************************************************************
* Name: _release_pkt_dat
****************************************************************************/
static void _release_pkt_dat(struct pkt_dat_s *pkt_dat)
{
int i;
for (i = 0; i < pkt_dat->n; i++)
{
kmm_free(pkt_dat->msg[i]);
}
if (pkt_dat->len)
{
kmm_free(pkt_dat->data);
}
pkt_dat->n = 0;
pkt_dat->len = 0;
}
/****************************************************************************
* Name: _check_pkt_q_cnt
****************************************************************************/
static void _check_pkt_q_cnt(FAR struct gs2200m_dev_s *dev, char cid)
{
uint8_t cnt;
cnt = dev->pkt_q_cnt[_cid_to_uint8(cid)];
if (0 != cnt)
{
wlinfo("--- _pkt_p_cnt[%c]=%d \n", cid, cnt);
}
}
/****************************************************************************
* Name: _check_pkt_q_empty
****************************************************************************/
static void _check_pkt_q_empty(FAR struct gs2200m_dev_s *dev, char cid)
{
uint8_t c = _cid_to_uint8(cid);
FAR struct pkt_dat_s *pkt_dat;
if (0 != dev->pkt_q_cnt[c])
{
pkt_dat = (FAR struct pkt_dat_s *)dq_peek(&dev->pkt_q[c]);
while (pkt_dat)
{
wlerr("=== error: found (type=%d msg[0]=%s|) \n",
pkt_dat->type, pkt_dat->msg[0]);
pkt_dat = (FAR struct pkt_dat_s *)pkt_dat->dq.flink;
}
ASSERT(false);
}
}
/****************************************************************************
* Name: _control_pkt_q
****************************************************************************/
static void _control_pkt_q(FAR struct gs2200m_dev_s *dev)
{
uint8_t c;
uint16_t mask;
bool over = false;
static bool enabled = true;
/* TODO: should enable again if disabled for long time
* Or, should use flow control commands for gs2200m
*/
/* For all cid(c) */
for (c = 0; c < 16; c++)
{
mask = 1 << c;
if (!(dev->valid_cid_bits & mask))
{
continue;
}
/* Check the pkt_q_cnt */
if (4 <= dev->pkt_q_cnt[c])
{
over = true;
break;
}
}
if (enabled && over)
{
wlinfo("--- pkt_q[%d] exceeds, disable irq \n", c);
enabled = false;
dev->lower->disable();
}
if (!enabled && !over)
{
wlinfo("--- enable irq again \n");
dev->lower->enable();
enabled = true;
}
}
/****************************************************************************
* Name: _remove_and_free_pkt
****************************************************************************/
static void _remove_and_free_pkt(FAR struct gs2200m_dev_s *dev, uint8_t c)
{
FAR struct pkt_dat_s *pkt_dat;
/* Decrement _pkt_q_cnt before remove */
ASSERT(0 < dev->pkt_q_cnt[c]);
dev->pkt_q_cnt[c]--;
/* Remove a packet from the queue */
pkt_dat = (FAR struct pkt_dat_s *)dq_remfirst(&dev->pkt_q[c]);
ASSERT(pkt_dat);
/* Release the packet */
_release_pkt_dat(pkt_dat);
kmm_free(pkt_dat);
}
/****************************************************************************
* Name: _remove_all_pkt
****************************************************************************/
static void _remove_all_pkt(FAR struct gs2200m_dev_s *dev, uint8_t c)
{
FAR struct pkt_dat_s *pkt_dat;
uint16_t mask;
mask = 1 << c;
ASSERT(0 == (dev->valid_cid_bits & mask));
ASSERT(dev->pkt_q_cnt[c] == dq_count(&dev->pkt_q[c]));
/* Remove all packets for this cid */
pkt_dat = (FAR struct pkt_dat_s *)dq_peek(&dev->pkt_q[c]);
while (pkt_dat)
{
_remove_and_free_pkt(dev, c);
/* Check the next */
pkt_dat = (FAR struct pkt_dat_s *)dq_peek(&dev->pkt_q[c]);
}
}
/****************************************************************************
* Name: _copy_data_from_pkt
****************************************************************************/
static bool _copy_data_from_pkt(FAR struct gs2200m_dev_s *dev,
struct gs2200m_recv_msg *msg)
{
FAR struct pkt_dat_s *pkt_dat;
uint8_t c = _cid_to_uint8(msg->cid);
uint16_t len;
uint16_t off;
bool ret = true;
/* Peek a packet from the queue and check the remaining size */
pkt_dat = (FAR struct pkt_dat_s *)dq_peek(&dev->pkt_q[c]);
ASSERT(pkt_dat);
wlinfo("+++ msg(req=%d:len=%d) pkt_data(t=%d:remain=%d) \n",
msg->reqlen, msg->len, pkt_dat->type, pkt_dat->remain);
if (msg->len && TYPE_DISCONNECT == pkt_dat->type)
{
/* Treat the packet separately */
ret = false;
goto errout;
}
/* Copy the pkt data to msg buffer up to MIN(request - len, remain) */
len = MIN(msg->reqlen - msg->len, pkt_dat->remain);
off = pkt_dat->len - pkt_dat->remain;
memcpy(msg->buf + msg->len, pkt_dat->data + off, len);
msg->len += len;
msg->type = pkt_dat->type;
/* Update the remaining size. If the remaining size is 0.
* Remove the packet from the queue and free it.
*/
pkt_dat->remain -= len;
if (0 == pkt_dat->remain)
{
_remove_and_free_pkt(dev, c);
}
errout:
if (!msg->is_tcp)
{
/* Copy the source address and port */
memcpy(&msg->addr, &pkt_dat->addr, sizeof(pkt_dat->addr));
/* In udp case, treat the packet separately */
ret = false;
}
return ret;
}
/****************************************************************************
* Name: gs2200m_lock
****************************************************************************/
static int gs2200m_lock(FAR struct gs2200m_dev_s *dev)
{
return nxsem_wait_uninterruptible(&dev->dev_sem);
}
/****************************************************************************
* Name: gs2200m_unlock
****************************************************************************/
static void gs2200m_unlock(FAR struct gs2200m_dev_s *dev)
{
nxsem_post(&dev->dev_sem);
}
/****************************************************************************
* Name: gs2200m_open
****************************************************************************/
static int gs2200m_open(FAR struct file *filep)
{
return OK;
}
/****************************************************************************
* Name: gs2200m_close
****************************************************************************/
static int gs2200m_close(FAR struct file *filep)
{
return OK;
}
/****************************************************************************
* Name: gs2200m_read
****************************************************************************/
static ssize_t gs2200m_read(FAR struct file *filep, FAR char *buffer,
size_t len)
{
FAR struct inode *inode;
FAR struct gs2200m_dev_s *dev;
int ret;
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct gs2200m_dev_s *)inode->i_private;
ASSERT(1 == len);
ret = nxsem_wait(&dev->dev_sem);
if (ret < 0)
{
/* Return if a signal is received or if the the task was canceled
* while we were waiting.
*/
return ret;
}
ASSERT(0 < _notif_q_count(dev));
char cid = _notif_q_pop(dev);
wlinfo("---- cid=%c (notif_q_cnt=%d) \n", cid, _notif_q_count(dev));
/* Copy the cid to the buffer */
memcpy(buffer, &cid, sizeof(cid));
gs2200m_unlock(dev);
return 1;
}
/****************************************************************************
* Name: gs2200m_write
****************************************************************************/
static ssize_t gs2200m_write(FAR struct file *filep, FAR const char *buffer,
size_t len)
{
return 0; /* REVISIT: Zero is not a legal return value from write() */
}
/****************************************************************************
* Name: gs2200m_spi_init
****************************************************************************/
static int gs2200m_spi_init(FAR struct gs2200m_dev_s *dev)
{
SPI_LOCK(dev->spi, true);
/* SPI settings (mode1/8bits/max freq) */
SPI_SETMODE(dev->spi, SPIDEV_MODE1);
SPI_SETBITS(dev->spi, 8);
SPI_SETFREQUENCY(dev->spi, SPI_MAXFREQ);
SPI_LOCK(dev->spi, false);
return 0;
}
/****************************************************************************
* Name: _checksum
* NOTE: See 3.2.2.3 Annexure - HI Frame Format (From Host)
****************************************************************************/
static uint8_t _checksum(uint8_t *p, uint8_t len)
{
uint8_t i;
uint32_t chksum = 0x0;
for (i = 0; i < len; i++, p++)
{
chksum += *p;
}
chksum ^= ~0x0;
return chksum;
}
/****************************************************************************
* Name: _prepare_header
* NOTE: See 3.2.2.3 Annexure - HI Frame Format (From Host)
****************************************************************************/
static void _prepare_header(uint8_t *p, uint16_t len, uint8_t class)
{
*(p + 0) = 0xa5; /* SOF: start of frame */
*(p + 1) = class;
*(p + 2) = 0x0; /* reserved */
*(p + 3) = 0x0; /* additional info */
*(p + 4) = 0x0; /* additional info */
*(p + 5) = (uint8_t)len;
*(p + 6) = (uint8_t)(len >> 8);
*(p + 7) = _checksum(p + 1, 6); /* exclude SOF */
}
/****************************************************************************
* Name: _write_data
****************************************************************************/
static void _write_data(FAR struct gs2200m_dev_s *dev,
FAR uint8_t *buf,
FAR uint16_t len)
{
SPI_SELECT(dev->spi, SPIDEV_WIRELESS(0), true);
SPI_SNDBLOCK(dev->spi, buf, len);
SPI_SELECT(dev->spi, SPIDEV_WIRELESS(0), false);
}
/****************************************************************************
* Name: _read_data
* NOTE: See 3.2.2.1 SPI Byte Stuffing for the idle character
****************************************************************************/
static void _read_data(FAR struct gs2200m_dev_s *dev,
FAR uint8_t *buff,
FAR uint16_t len)
{
uint8_t req = 0xf5; /* idle character */
memset(buff, 0, len);
SPI_SELECT(dev->spi, SPIDEV_WIRELESS(0), true);
SPI_EXCHANGE(dev->spi, &req, buff, len);
SPI_SELECT(dev->spi, SPIDEV_WIRELESS(0), false);
}
/****************************************************************************
* Name: _read_data_len
****************************************************************************/
static uint16_t _read_data_len(FAR struct gs2200m_dev_s *dev)
{
uint8_t hdr[8];
uint8_t res[8];
uint16_t len = 0;
int n = 0;
/* Prepare header */
_prepare_header(hdr, MAX_PKT_LEN, RD_REQ);
retry:
/* Send the header read request */
_write_data(dev, hdr, sizeof(hdr));
/* Wait for data ready */
while (!dev->lower->dready(NULL))
{
/* TODO: timeout */
}
/* NOTE: busy wait 50us
* workaround to avoid an invalid frame response
*/
up_udelay(50);
/* Read frame response */
_read_data(dev, res, sizeof(res));
/* In case of NOK, retry */
if (RD_RESP_NOK == res[1])
{
wlwarn("*** warning: RD_RESP_NOK received.. retrying. (n=%d) \n", n);
nxsig_usleep(100 * 1000);
n++;
goto retry;
}
ASSERT(RD_RESP_OK == res[1]);
/* Retrieve the length */
len = ((uint16_t)res[6] << 8) + (uint16_t)res[5];
return len;
}
/****************************************************************************
* Name: gs2200m_hal_write
* NOTE: See Figure 13,14 Transferring data from MCU to GS node
****************************************************************************/
enum spi_status_e gs2200m_hal_write(FAR struct gs2200m_dev_s *dev,
const void *data,
uint16_t txlen)
{
uint8_t *tx = (uint8_t *)data;
uint8_t hdr[8];
uint8_t res[8];
int n = 0;
/* Prepare header */
_prepare_header(hdr, txlen, WR_REQ);
retry:
/* 1. Send the first 4bytes of WRITE_REQUEST */
_write_data(dev, hdr, sizeof(hdr) / 2);
/* 2. Delay 3.2usec (NOTE: here we specify 4us) */
up_udelay(4);
/* Check if a pending interrupt exists */
if (dev->lower->dready(NULL))
{
wlwarn("*** warning: gs2200m is busy.. retrying. (n=%d) \n", n);
if (!work_available(&dev->irq_work))
{
wlwarn("*** warning: there is still pending work **** \n");
}
else
{
/* NOTE: Disable gs2200m irq before calling work_queue()
* This is the same sequence in the irq handler
*/
dev->lower->disable();
work_queue(GS2200MWORK, &dev->irq_work, gs2200m_irq_worker,
(FAR void *)dev, 0);
}
nxsig_usleep(100 * 1000);
n++;
goto retry;
}
/* 3. Send remaining 4bytes of the WRITE_REQUEST */
_write_data(dev, hdr + (sizeof(hdr) / 2), sizeof(hdr) / 2);
/* 4. Wait for dready status (GPIO37 goes high) */
while (!dev->lower->dready(NULL))
{
/* TODO: timeout */
}
/* 5 Read 8bytes of WRITE_RESPONSE */
_read_data(dev, res, sizeof(res));
/* In case of NOK, retry */
if (WR_RESP_NOK == res[1])
{
wlwarn("*** warning: WR_RESP_NOK received.. retrying. (n=%d) \n", n);
nxsig_usleep(10 * 1000);
if (WR_MAX_RETRY < n)
{
return SPI_TIMEOUT;
}
n++;
goto retry;
}
ASSERT(WR_RESP_OK == res[1]);
/* Prepare header */
_prepare_header(hdr, txlen, DT_FROM_MCU);
/* 6. Send 8bytes of data header */
_write_data(dev, hdr, sizeof(hdr));
/* 7. Send actual data */
_write_data(dev, tx, txlen);
return SPI_OK;
}
/****************************************************************************
* Name: gs2200m_hal_read
****************************************************************************/
enum spi_status_e gs2200m_hal_read(FAR struct gs2200m_dev_s *dev,
FAR uint8_t *data,
FAR uint16_t *len)
{
enum spi_status_e r = SPI_OK;
uint8_t hdr[8];
int i;
/* NOTE: need to wait for data ready even if we use irq */
for (i = 0; i < HAL_TIMEOUT; i++)
{
if (dev->lower->dready(NULL))
{
break;
}
/* Busy wait 1us */
up_udelay(1);
}
if (HAL_TIMEOUT == i)
{
wlerr("***** error: timeout! \n");
r = SPI_TIMEOUT;
goto errout;
}
/* Send READ_REQUEST then receive READ_RESPONSE
* to get how many bytes we should read
*/
*len = _read_data_len(dev);
wlinfo("+++++ (len=%d) \n", *len);
/* Check the length */
ASSERT(0 < *len);
/* Read data header */
_read_data(dev, hdr, sizeof(hdr));
/* Read the actual data */
_read_data(dev, data, *len);
errout:
return r;
}
/****************************************************************************
* Name: _check_evt
****************************************************************************/
enum pkt_type_e _check_evt(FAR const char *buff)
{
int i = 0;
for (i = 0; i < sizeof(_evt_table) / sizeof(struct evt_code_s); i++)
{
if (strstr(buff, _evt_table[i].str))
{
return _evt_table[i].code;
}
}
wlinfo("+++++ %s +++++ \n", buff);
return TYPE_UNMATCH;
}
/****************************************************************************
* Name: _parse_pkt_in_s0
****************************************************************************/
static void _parse_pkt_in_s0(FAR struct pkt_ctx_s *pkt_ctx,
FAR struct pkt_dat_s *pkt_dat)
{
switch (*(pkt_ctx->ptr))
{
case ASCII_CR:
case ASCII_LF:
break;
case ASCII_ESC:
pkt_ctx->state = PKT_ESC_START;
break;
default:
pkt_ctx->head = pkt_ctx->ptr;
pkt_ctx->state = PKT_EVENT;
break;
}
}
/****************************************************************************
* Name: _parse_pkt_in_s1
****************************************************************************/
static void _parse_pkt_in_s1(FAR struct pkt_ctx_s *pkt_ctx,
FAR struct pkt_dat_s *pkt_dat)
{
int msize;
int n;
if (ASCII_LF != *(pkt_ctx->ptr))
{
return;
}
ASSERT(pkt_ctx->ptr > pkt_ctx->head);
msize = pkt_ctx->ptr - pkt_ctx->head;
char *msg = (char *)kmm_calloc(msize + 1, 1);
ASSERT(msg);
memcpy(msg, pkt_ctx->head, msize);
pkt_ctx->type = _check_evt(msg);
if (pkt_ctx->type == TYPE_DISCONNECT)
{
ASSERT(pkt_dat);
n = sscanf(msg, "DISCONNECT %c", &(pkt_dat->cid));
ASSERT(1 == n);
wlinfo("+++++ msg=%s| cid=%c \n", msg, pkt_dat->cid);
}
else if (pkt_ctx->type == TYPE_CONNECT)
{
ASSERT(pkt_dat);
/* NOTE: CONNECT <server cid> <new cid> <ip> <address> */
n = sscanf(msg, "CONNECT %c", &(pkt_dat->cid));
DEBUGASSERT(1 == n);
wlinfo("+++++ msg=%s| \n", msg);
}
if (pkt_dat)
{
/* If specified, store the msg pointer to pkt_dat */
wlinfo("+++++ %d:(msize=%d, msg=%s|) \n", pkt_dat->n, msize, msg);
ASSERT(pkt_dat->n < NRESPMSG);
pkt_dat->msg[pkt_dat->n++] = msg;
}
else
{
wlinfo("+++++ (msize=%d, msg=%s|) \n", msize, msg);
kmm_free(msg);
}
pkt_ctx->head = pkt_ctx->ptr + 1;
if (TYPE_UNMATCH != pkt_ctx->type)
{
pkt_ctx->state = PKT_START;
}
}
/****************************************************************************
* Name: _parse_pkt_in_s2 (ESC detected)
****************************************************************************/
static void _parse_pkt_in_s2(FAR struct pkt_ctx_s *pkt_ctx,
FAR struct pkt_dat_s *pkt_dat)
{
ASSERT(pkt_ctx && pkt_ctx->ptr);
char c = (char)*(pkt_ctx->ptr);
if ('Z' == c)
{
wlinfo("** <ESC>Z \n");
/* NOTE: See 7.5.3.2 Bulk Data Handling */
pkt_ctx->state = PKT_BULK_DATA_TCP;
}
else if ('y' == c)
{
wlinfo("** <ESC>y \n");
/* NOTE: See 7.5.3.2 Bulk Data Handling */
pkt_ctx->state = PKT_BULK_DATA_UDP;
}
else if ('F' == c)
{
wlwarn("** <ESC>F \n");
/* NOTE: See Table 6 Data Handling Responses at Completion */
pkt_ctx->state = PKT_START;
pkt_ctx->type = TYPE_FAIL;
}
else
{
wlerr("** <ESC>%c not supported \n", c);
ASSERT(false);
}
}
/****************************************************************************
* Name: _parse_pkt_in_s3 (BULK data for TCP)
****************************************************************************/
static void _parse_pkt_in_s3(FAR struct pkt_ctx_s *pkt_ctx,
FAR struct pkt_dat_s *pkt_dat)
{
ASSERT(pkt_dat);
if ('z' == pkt_ctx->cid)
{
/* Proceed ptr to obtain data length
* NOTE: <CID><Data Length xxxx 4 ascii char>
*/
/* Read CID */
pkt_ctx->cid = (char)*(pkt_ctx->ptr);
pkt_ctx->ptr++;
pkt_dat->cid = pkt_ctx->cid;
/* Read data length */
pkt_ctx->dlen = _to_uint16((char *)pkt_ctx->ptr);
pkt_ctx->ptr += 3;
wlinfo("dlen=%d \n", pkt_ctx->dlen);
/* Allocate memory for the packet */
pkt_dat->data = kmm_calloc(pkt_ctx->dlen, 1);
ASSERT(pkt_dat->data);
}
else
{
_push_data_to_pkt(pkt_dat, *(pkt_ctx->ptr));
pkt_ctx->dlen--;
if (0 == pkt_ctx->dlen)
{
pkt_ctx->state = PKT_START;
pkt_ctx->type = TYPE_BULK_DATA_TCP;
}
}
}
/****************************************************************************
* Name: _parse_pkt_in_s4 (BULK data for UDP: see Table 217)
****************************************************************************/
static void _parse_pkt_in_s4(FAR struct pkt_ctx_s *pkt_ctx,
FAR struct pkt_dat_s *pkt_dat)
{
char addr[17];
char port[6];
int n;
ASSERT(pkt_dat);
if ('z' == pkt_ctx->cid)
{
/* <CID><address><sp><port><tab><Data Length xxxx 4 ascii char> */
/* Read CID */
pkt_ctx->cid = (char)*(pkt_ctx->ptr);
pkt_ctx->ptr++;
pkt_dat->cid = pkt_ctx->cid;
/* Read address and port */
memset(addr, 0, sizeof(addr));
memset(port, 0, sizeof(port));
n = sscanf((char *)pkt_ctx->ptr, "%s %s\t", addr, port);
ASSERT(2 == n);
wlinfo("from (%s:%s) \n", addr, port);
inet_aton(addr, &pkt_dat->addr.sin_addr);
pkt_dat->addr.sin_port = htons((uint16_t)atoi(port));
/* Skip until data length */
for (; '\t' != (char)*(pkt_ctx->ptr); pkt_ctx->ptr++);
pkt_ctx->ptr++;
/* Read data length */
pkt_ctx->dlen = _to_uint16((char *)pkt_ctx->ptr);
pkt_ctx->ptr += 3;
wlinfo("dlen=%d \n", pkt_ctx->dlen);
/* Allocate memory for the packet */
pkt_dat->data = kmm_calloc(pkt_ctx->dlen, 1);
ASSERT(pkt_dat->data);
}
else
{
_push_data_to_pkt(pkt_dat, *(pkt_ctx->ptr));
pkt_ctx->dlen--;
if (0 == pkt_ctx->dlen)
{
pkt_ctx->state = PKT_START;
pkt_ctx->type = TYPE_BULK_DATA_UDP;
}
}
}
/****************************************************************************
* Name: _parse_pkt
****************************************************************************/
static enum pkt_type_e _parse_pkt(FAR uint8_t *p, uint16_t len,
FAR struct pkt_dat_s *pkt_dat)
{
struct pkt_ctx_s pkt_ctx;
/* Initialize pkt_ctx */
pkt_ctx.type = TYPE_UNMATCH;
pkt_ctx.state = PKT_START;
pkt_ctx.head = NULL;
pkt_ctx.cid = 'z';
pkt_ctx.dlen = 0;
for (pkt_ctx.ptr = p; pkt_ctx.ptr < (p + len); pkt_ctx.ptr++)
{
switch (pkt_ctx.state)
{
case PKT_START:
_parse_pkt_in_s0(&pkt_ctx, pkt_dat);
break;
case PKT_EVENT:
_parse_pkt_in_s1(&pkt_ctx, pkt_dat);
break;
case PKT_ESC_START:
_parse_pkt_in_s2(&pkt_ctx, pkt_dat);
break;
case PKT_BULK_DATA_TCP:
_parse_pkt_in_s3(&pkt_ctx, pkt_dat);
break;
case PKT_BULK_DATA_UDP:
_parse_pkt_in_s4(&pkt_ctx, pkt_dat);
break;
default:
ASSERT(false);
break;
}
}
return pkt_ctx.type;
}
/****************************************************************************
* Name: gs2200m_recv_pkt
****************************************************************************/
static enum pkt_type_e gs2200m_recv_pkt(FAR struct gs2200m_dev_s *dev,
FAR struct pkt_dat_s *pkt_dat)
{
enum pkt_type_e t = TYPE_ERROR;
enum spi_status_e s;
uint16_t len;
uint8_t *p;
p = (uint8_t *)kmm_calloc(MAX_PKT_LEN, 1);
ASSERT(p);
s = gs2200m_hal_read(dev, p, &len);
t = _spi_err_to_pkt_type(s);
if (TYPE_OK != t)
{
goto errout;
}
wlinfo("+++ len=%d pkt_dat=%p \n", len, pkt_dat);
/* Parse the received packet */
t = _parse_pkt(p, len, pkt_dat);
if (t == TYPE_DISCONNECT)
{
_check_pkt_q_cnt(dev, pkt_dat->cid);
}
if (pkt_dat)
{
pkt_dat->type = t;
}
errout:
kmm_free(p);
return t;
}
/****************************************************************************
* Name: gs2200m_send_cmd
****************************************************************************/
static enum pkt_type_e gs2200m_send_cmd(FAR struct gs2200m_dev_s *dev,
FAR char *cmd,
FAR struct pkt_dat_s *pkt_dat)
{
enum spi_status_e s;
enum pkt_type_e r = TYPE_SPI_ERROR;
/* Disable gs2200m irq to poll dready */
dev->lower->disable();
wlinfo("+++ cmd=%s", cmd);
s = gs2200m_hal_write(dev, cmd, strlen(cmd));
r = _spi_err_to_pkt_type(s);
if (TYPE_OK != r)
{
goto errout;
}
r = gs2200m_recv_pkt(dev, pkt_dat);
errout:
/* Enable gs2200m irq again */
dev->lower->enable();
return r;
}
/****************************************************************************
* Name: gs2200m_set_opmode
* NOTE: See 5.1.2 Operation Mode
****************************************************************************/
static enum pkt_type_e gs2200m_set_opmode(FAR struct gs2200m_dev_s *dev,
uint8_t mode)
{
enum pkt_type_e t;
char cmd[20];
snprintf(cmd, sizeof(cmd), "AT+WM=%d\r\n", mode);
t = gs2200m_send_cmd(dev, cmd, NULL);
if (TYPE_OK == t)
{
dev->op_mode = mode;
}
return t;
}
/****************************************************************************
* Name: gs2200m_get_mac
* NOTE: See 4.5.2 Get MAC Address
****************************************************************************/
static enum pkt_type_e gs2200m_get_mac(FAR struct gs2200m_dev_s *dev)
{
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
uint32_t mac[6];
char cmd[16];
int n;
/* Initialize pkt_dat and send command */
memset(&pkt_dat, 0, sizeof(pkt_dat));
snprintf(cmd, sizeof(cmd), "AT+NMAC=?\r\n");
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
if (r != TYPE_OK)
{
goto errout;
}
n = sscanf(pkt_dat.msg[0], "%2x:%2x:%2x:%2x:%2x:%2x",
&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
DEBUGASSERT(n == 6);
for (n = 0; n < 6; n++)
{
dev->net_dev.d_mac.ether.ether_addr_octet[n] = (uint8_t)mac[n];
}
errout:
_release_pkt_dat(&pkt_dat);
return r;
}
/****************************************************************************
* Name: gs2200m_disassociate
* NOTE: See 5.3.6 Disassociation
****************************************************************************/
static enum pkt_type_e gs2200m_disassociate(FAR struct gs2200m_dev_s *dev)
{
return gs2200m_send_cmd(dev, (char *)"AT+WD\r\n", NULL);
}
/****************************************************************************
* Name: gs2200m_enable_dhcpc
* NOTE: See 6.3 DHCP Client
****************************************************************************/
static enum pkt_type_e gs2200m_enable_dhcpc(FAR struct gs2200m_dev_s *dev,
uint8_t on)
{
char cmd[16];
snprintf(cmd, sizeof(cmd), "AT+NDHCP=%d\r\n", on);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_calc_key
* NOTE: See 5.3.3.5 WPA-PSK and WPA2-PSK Key Calculation
****************************************************************************/
static enum pkt_type_e gs2200m_calc_key(FAR struct gs2200m_dev_s *dev,
FAR char *ssid, FAR char *psk)
{
char cmd[80];
snprintf(cmd, sizeof(cmd), "AT+WPAPSK=%s,%s\r\n", ssid, psk);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_set_security
* NOTE: See 5.3.3.1 Security Setting
****************************************************************************/
static enum pkt_type_e gs2200m_set_security(FAR struct gs2200m_dev_s *dev,
uint8_t mode)
{
char cmd[16];
snprintf(cmd, sizeof(cmd), "AT+WSEC=%d\r\n", mode);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_join_network
* NOTE: See 5.3.5 Association
****************************************************************************/
static enum pkt_type_e gs2200m_join_network(FAR struct gs2200m_dev_s *dev,
FAR char *ssid, uint8_t ch)
{
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
char cmd[64];
char addr[3][17];
int n;
/* Initialize pkt_dat and send command */
memset(&pkt_dat, 0, sizeof(pkt_dat));
if (0 == dev->op_mode)
{
snprintf(cmd, sizeof(cmd), "AT+WA=%s\r\n", ssid);
}
else
{
/* In AP mode, we can specify channel to use */
snprintf(cmd, sizeof(cmd), "AT+WA=%s,,%d\r\n", ssid, ch);
}
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
if (r != TYPE_OK)
{
goto errout;
}
ASSERT(3 == pkt_dat.n);
n = sscanf(pkt_dat.msg[1] + 1,
" %[^:]:%[^:]:%[^ ]",
addr[0], addr[1], addr[2]);
ASSERT(3 == n);
/* Set addresses to be shown with ifconfig */
inet_aton(addr[0], (struct in_addr *)&dev->net_dev.d_ipaddr);
inet_aton(addr[1], (struct in_addr *)&dev->net_dev.d_netmask);
inet_aton(addr[2], (struct in_addr *)&dev->net_dev.d_draddr);
errout:
_release_pkt_dat(&pkt_dat);
return r;
}
/****************************************************************************
* Name: gs2200m_set_addresses
* NOTE: See 6.4 IP Address
****************************************************************************/
static enum pkt_type_e gs2200m_set_addresses(FAR struct gs2200m_dev_s *dev,
FAR const char *address,
FAR const char *netmask,
FAR const char *gateway)
{
char cmd[100];
snprintf(cmd, sizeof(cmd), "AT+NSET=%s,%s,%s\r\n",
address, netmask, gateway);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_enable_dhcps
* NOTE: See 6.5 DHCP Server
****************************************************************************/
static enum pkt_type_e gs2200m_enable_dhcps(FAR struct gs2200m_dev_s *dev,
uint8_t on)
{
char cmd[20];
snprintf(cmd, sizeof(cmd), "AT+DHCPSRVR=%d\r\n", on);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_set_auth
* NOTE: See 5.3.2 Authentication Mode
****************************************************************************/
static enum pkt_type_e gs2200m_set_auth(FAR struct gs2200m_dev_s *dev,
int mode)
{
char cmd[16];
snprintf(cmd, sizeof(cmd), "AT+WAUTH=%d\r\n", mode);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_set_wepkey
* NOTE: See xxxx
****************************************************************************/
static enum pkt_type_e gs2200m_set_wepkey(FAR struct gs2200m_dev_s *dev,
FAR char *key)
{
char cmd[32];
snprintf(cmd, sizeof(cmd), "AT+WWEP1=%s\r\n", key);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_get_wstatus
* NOTE: See 11.3.5 WLAN Status
****************************************************************************/
enum pkt_type_e gs2200m_get_wstatus(FAR struct gs2200m_dev_s *dev)
{
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
int i;
/* Initialize pkt_dat and send command */
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, (char *)"AT+WSTATUS\r\n", &pkt_dat);
if (r != TYPE_OK)
{
goto errout;
}
for (i = 0; i < pkt_dat.n; i++)
{
wlinfo("%s\n", pkt_dat.msg[i]);
}
errout:
_release_pkt_dat(&pkt_dat);
return r;
}
/****************************************************************************
* Name: gs2200m_create_tcpc
* NOTE: See 7.5.1.1 Create TCP Clients and 7.5.1.2 Create UDP Client
****************************************************************************/
static enum pkt_type_e gs2200m_create_clnt(FAR struct gs2200m_dev_s *dev,
FAR char *address, FAR char *port,
int type, FAR char *cid)
{
enum pkt_type_e r;
struct pkt_dat_s pkt_dat;
char cmd[40];
char *p;
int n;
*cid = 'z'; /* Invalidate cid */
if (SOCK_STREAM == type)
{
snprintf(cmd, sizeof(cmd), "AT+NCTCP=%s,%s\r\n", address, port);
}
else if (SOCK_DGRAM == type)
{
snprintf(cmd, sizeof(cmd), "AT+NCUDP=%s,%s\r\n", address, port);
}
else
{
ASSERT(false);
}
/* Initialize pkt_dat and send */
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
if (r != TYPE_OK || pkt_dat.n == 0)
{
wlinfo("+++ error: r=%d pkt_dat.msg[0]=%s \n",
r, pkt_dat.msg[0]);
goto errout;
}
if (NULL != (p = strstr(pkt_dat.msg[0], "CONNECT")))
{
n = sscanf(p, "CONNECT %c", cid);
ASSERT(1 == n);
wlinfo("+++ OK: p=%s| (n=%d) \n", p, n);
}
errout:
_release_pkt_dat(&pkt_dat);
return r;
}
/****************************************************************************
* Name: gs2200m_start_server
* NOTE: See 7.5.1.3 Start TCP Server, 7.5.1.4 Start UDP Server
****************************************************************************/
static enum pkt_type_e gs2200m_start_server(FAR struct gs2200m_dev_s *dev,
FAR char *port, bool is_tcp,
FAR char *cid)
{
enum pkt_type_e r;
struct pkt_dat_s pkt_dat;
char cmd[40];
char *p;
int n;
/* Prepare cmd */
if (is_tcp)
{
snprintf(cmd, sizeof(cmd), "AT+NSTCP=%s\r\n", port);
}
else
{
snprintf(cmd, sizeof(cmd), "AT+NSUDP=%s\r\n", port);
}
/* Initialize pkt_dat and send */
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
/* REVISIT:
* TYPE_BULK for other sockets might be received here,
* if the sockets have heavy bulk traffic.
* In this case, the packet should be queued and
* wait for a response to the NSTCP command.
*/
if (r != TYPE_OK || pkt_dat.n == 0)
{
goto errout;
}
if (NULL != (p = strstr(pkt_dat.msg[0], "CONNECT")))
{
n = sscanf(p, "CONNECT %c", cid);
ASSERT(1 == n);
wlinfo("+++ OK: p=%s| (n=%d) \n", p, n);
}
errout:
_release_pkt_dat(&pkt_dat);
return r;
}
/****************************************************************************
* Name: gs2200m_send_bulk
* NOTE: See 7.5.3.2 Bulk Data Handling
****************************************************************************/
static enum pkt_type_e gs2200m_send_bulk(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_send_msg *msg)
{
enum pkt_type_e r;
enum spi_status_e s;
int bulk_hdr_size;
char digits[5];
char cmd[32];
memset(cmd, 0, sizeof(cmd));
/* Convert the data length to 4 ascii char */
_to_ascii_char(msg->len, digits);
wlinfo("** cid=%c len=%d digits=%s \n", msg->cid, msg->len, digits);
if (msg->is_tcp)
{
/* NOTE: See 7.5.3.2 Bulk Data Handling for TCP
* <ESC>Z<CID><Data Length xxxx 4 ascii char><data>
*/
snprintf(cmd, sizeof(cmd), "%cZ%c%s", ASCII_ESC, msg->cid, digits);
}
else
{
wlinfo("** addr=%s port=%d \n", inet_ntoa(msg->addr.sin_addr),
ntohs(msg->addr.sin_port));
/* NOTE: See 7.5.3.2 Bulk Data Handling for UDP
* <ESC>Y<CID><IP address>:<port>:<Data Length xxxx 4 ascii char><data>
*/
snprintf(cmd, sizeof(cmd), "%cY%c%s:%d:%s",
ASCII_ESC, msg->cid,
inet_ntoa(msg->addr.sin_addr), ntohs(msg->addr.sin_port),
digits);
}
bulk_hdr_size = strlen(cmd);
memset(dev->tx_buff, 0, sizeof(dev->tx_buff));
memcpy(dev->tx_buff, cmd, bulk_hdr_size);
memcpy(dev->tx_buff + bulk_hdr_size, msg->buf, msg->len);
/* Send the bulk data */
s = gs2200m_hal_write(dev, (char *)dev->tx_buff, msg->len + bulk_hdr_size);
if (s == SPI_TIMEOUT)
{
/* In case of SPI_TIMEOUT, return OK with 0 bytes sent */
s = SPI_OK;
msg->len = 0;
}
r = _spi_err_to_pkt_type(s);
return r;
}
/****************************************************************************
* Name: gs2200m_close_conn
* NOTE: See 7.1.4 Closing a Connection
****************************************************************************/
static enum pkt_type_e gs2200m_close_conn(FAR struct gs2200m_dev_s *dev,
char cid)
{
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
char cmd[15];
/* Prepare cmd */
snprintf(cmd, sizeof(cmd), "AT+NCLOSE=%c\r\n", cid);
/* Initialize pkt_dat and send */
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
_release_pkt_dat(&pkt_dat);
return r;
}
/****************************************************************************
* Name: gs2200m_enable_bulk
* NOTE: See 7.1.1 Data Transfer in Bulk Mode
****************************************************************************/
static enum pkt_type_e gs2200m_enable_bulk(FAR struct gs2200m_dev_s *dev,
uint8_t on)
{
char cmd[20];
snprintf(cmd, sizeof(cmd), "AT+BDATA=%d\r\n", on);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_enable_echo
* NOTE: See 11.3.2 Echo
****************************************************************************/
static enum pkt_type_e gs2200m_enable_echo(FAR struct gs2200m_dev_s *dev,
uint8_t on)
{
char cmd[8];
snprintf(cmd, sizeof(cmd), "ATE%d\r\n", on);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_activate_wrx
* NOTE: See 9.1.1 Active Radio Receive
****************************************************************************/
static enum pkt_type_e gs2200m_activate_wrx(FAR struct gs2200m_dev_s *dev,
uint8_t on)
{
char cmd[30];
snprintf(cmd, sizeof(cmd), "AT+WRXACTIVE=%d\r\n", on);
return gs2200m_send_cmd(dev, cmd, NULL);
}
/****************************************************************************
* Name: gs2200m_set_gpio
* NOTE: See 10.3 GPIO Commands
****************************************************************************/
#ifdef USE_LED
static enum pkt_type_e gs2200m_set_gpio(FAR struct gs2200m_dev_s *dev,
int n, int val)
{
char cmd[24];
snprintf(cmd, sizeof(cmd), "AT+DGPIO=%d,%d\r\n", n, val);
return gs2200m_send_cmd(dev, cmd, NULL);
}
#endif
/****************************************************************************
* Name: gs2200m_set_loglevel
* NOTE: See 11.3.1 Log Level
****************************************************************************/
#if CONFIG_WL_GS2200M_LOGLEVEL > 0
static enum pkt_type_e gs2200m_set_loglevel(FAR struct gs2200m_dev_s *dev,
int level)
{
char cmd[16];
snprintf(cmd, sizeof(cmd), "AT+LOGLVL=%d\r\n", level);
return gs2200m_send_cmd(dev, cmd, NULL);
}
#endif
/****************************************************************************
* Name: gs2200m_get_version
****************************************************************************/
#ifdef CONFIG_WL_GS2200M_CHECK_VERSION
static enum pkt_type_e gs2200m_get_version(FAR struct gs2200m_dev_s *dev)
{
char cmd[16];
snprintf(cmd, sizeof(cmd), "AT+VER=??\r\n");
return gs2200m_send_cmd(dev, cmd, NULL);
}
#endif
/****************************************************************************
* Name: gs2200m_ioctl_bind
****************************************************************************/
static int gs2200m_ioctl_bind(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_bind_msg *msg)
{
enum pkt_type_e type = TYPE_OK;
char cid = 'z';
int ret = OK;
wlinfo("+++ start: (cid=%c, port=%s) \n", msg->cid, msg->port);
/* Start TCP/UDP server and retrieve cid */
type = gs2200m_start_server(dev, msg->port, msg->is_tcp, &cid);
if (type != TYPE_OK)
{
ret = -EINVAL;
goto errout;
}
/* Enable the cid for server socket and if the pkt_q is empty */
_enable_cid(&dev->valid_cid_bits, cid, true);
_check_pkt_q_empty(dev, cid);
errout:
msg->type = type;
msg->cid = cid;
wlinfo("+++ end: type=%d (cid=%c) \n", type, cid);
return ret;
}
/****************************************************************************
* Name: gs2200m_ioctl_connect
****************************************************************************/
static int gs2200m_ioctl_connect(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_connect_msg *msg)
{
enum pkt_type_e type;
char cid = 'z';
int ret = OK;
wlinfo("++ start: addr=%s port=%s \n", msg->addr, msg->port);
/* Create TCP or UDP connection */
type = gs2200m_create_clnt(dev, msg->addr, msg->port, msg->type, &cid);
msg->type = type;
switch (type)
{
case TYPE_OK:
msg->cid = cid;
/* Enable the cid and checi if the pkt_q is empty */
_enable_cid(&dev->valid_cid_bits, cid, true);
_check_pkt_q_empty(dev, cid);
break;
case TYPE_ERROR:
/* We assume the connection has been refused */
ret = -ECONNREFUSED;
break;
case TYPE_TIMEOUT:
ret = -ETIMEDOUT;
break;
default:
/* REVISIT:
* TYPE_BULK for other sockets might be received here,
* if the sockets have heavy bulk traffic.
* In this case, the packet should be queued and
* wait for a response to the NCTCP command.
*/
wlerr("+++ error: type=%d \n", type);
ASSERT(false);
ret = -EINVAL;
}
wlinfo("++ end: cid=%c (type=%d,ret=%d) \n", cid, type, ret);
return ret;
}
/****************************************************************************
* Name: gs2200m_ioctl_send
****************************************************************************/
static int gs2200m_ioctl_send(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_send_msg *msg)
{
FAR struct gs2200m_bind_msg bmsg;
enum pkt_type_e type;
bool assigned = false;
uint16_t s_port;
int ret = OK;
wlinfo("+++ start: (cid=%c) \n", msg->cid);
#ifdef USE_LED
gs2200m_set_gpio(dev, LED_GPIO, 1);
#endif
/* If the msg is udp having unassgined cid */
if (!msg->is_tcp && 'z' == msg->cid)
{
memset(&bmsg, 0, sizeof(bmsg));
for (s_port = 50000; s_port < 60000; s_port++)
{
snprintf(bmsg.port, sizeof(bmsg.port), "%d", s_port);
if (0 == gs2200m_ioctl_bind(dev, &bmsg))
{
assigned = true;
break;
}
}
ASSERT(assigned);
wlinfo("+++ cid is assigned for udp (cid=%c) \n", bmsg.cid);
msg->cid = bmsg.cid;
}
if (!_cid_is_set(&dev->valid_cid_bits, msg->cid))
{
wlinfo("+++ already closed \n");
type = TYPE_DISCONNECT;
goto errout;
}
type = gs2200m_send_bulk(dev, msg);
msg->type = type;
errout:
if (type != TYPE_OK)
{
ret = -EINVAL;
}
#ifdef USE_LED
gs2200m_set_gpio(dev, LED_GPIO, 0);
#endif
wlinfo("+++ end: cid=%c len=%d type=%d \n",
msg->cid, msg->len, type);
return ret;
}
/****************************************************************************
* Name: gs2200m_ioctl_recv
****************************************************************************/
static int gs2200m_ioctl_recv(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_recv_msg *msg)
{
bool cont = true;
int ret = OK;
uint8_t c = _cid_to_uint8(msg->cid);
wlinfo("+++ start: cid=%c \n", msg->cid);
#ifdef USE_LED
gs2200m_set_gpio(dev, LED_GPIO, 1);
#endif
if (0 == dev->pkt_q_cnt[c])
{
/* REVISIT */
wlwarn("**** no packet for cid=%c \n", msg->cid);
ret = -EAGAIN;
goto errout;
}
while (1)
{
/* Finished copying or no packet */
if (msg->reqlen == msg->len || 0 == dev->pkt_q_cnt[c] || !cont)
{
break;
}
/* Copy data from the front-most packet */
cont = _copy_data_from_pkt(dev, msg);
}
wlinfo("+++ pkt_q_cnt[%c]=%d \n", msg->cid, dev->pkt_q_cnt[c]);
if (dev->pkt_q_cnt[c])
{
_notif_q_push(dev, msg->cid);
}
/* Do packet flow control */
_control_pkt_q(dev);
errout:
#ifdef USE_LED
gs2200m_set_gpio(dev, LED_GPIO, 0);
#endif
wlinfo("+++ end: cid=%c len=%d type=%d ret=%d \n",
msg->cid, msg->len, msg->type, ret);
return ret;
}
/****************************************************************************
* Name: gs2200m_ioctl_close
****************************************************************************/
static int gs2200m_ioctl_close(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_close_msg *msg)
{
enum pkt_type_e type = TYPE_OK;
int ret = OK;
wlinfo("++ start: (cid=%c) \n", msg->cid);
if (!_cid_is_set(&dev->valid_cid_bits, msg->cid))
{
wlinfo("+++ already closed \n");
goto errout;
}
/* Disable the cid */
_enable_cid(&dev->valid_cid_bits, msg->cid, false);
type = gs2200m_close_conn(dev, msg->cid);
if (type != TYPE_OK)
{
ret = -EINVAL;
}
errout:
/* Remove all pkt associated with this cid */
_remove_all_pkt(dev, _cid_to_uint8(msg->cid));
wlinfo("++ end: cid=%c type=%d \n", msg->cid, type);
return ret;
}
/****************************************************************************
* Name: gs2200m_ioctl_accept
****************************************************************************/
static int gs2200m_ioctl_accept(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_accept_msg *msg)
{
FAR struct pkt_dat_s *pkt_dat;
uint8_t c;
char s_cid;
char c_cid;
int n;
wlinfo("+++ start: cid=%c \n", msg->cid);
c = _cid_to_uint8(msg->cid);
pkt_dat = (FAR struct pkt_dat_s *)dq_peek(&dev->pkt_q[c]);
ASSERT(pkt_dat);
n = sscanf(pkt_dat->msg[0], "CONNECT %c %c", &s_cid, &c_cid);
ASSERT(2 == n);
wlinfo("+++ s_cid=%c c_cid=%c \n", s_cid, c_cid);
/* Remove the accept packet (actually CONNECT) from the queue */
_remove_and_free_pkt(dev, _cid_to_uint8(msg->cid));
/* Copy a client cid which was obtained in CONNECT event */
msg->type = TYPE_OK;
msg->cid = c_cid; /* NOTE: override new client cid */
/* Disable accept in progress */
_enable_cid(&dev->aip_cid_bits, c_cid, false);
/* If a packet still exists, notify it */
if (dev->pkt_q_cnt[_cid_to_uint8(c_cid)])
{
_notif_q_push(dev, c_cid);
}
wlinfo("+++ end: type=%d (msg->cid=%c) \n", msg->type, msg->cid);
return OK;
}
/****************************************************************************
* Name: gs2200m_ioctl_assoc_sta
****************************************************************************/
static int gs2200m_ioctl_assoc_sta(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_assoc_msg *msg)
{
enum pkt_type_e t;
/* Disassociate */
t = gs2200m_disassociate(dev);
ASSERT(TYPE_OK == t);
/* Set to STA mode */
t = gs2200m_set_opmode(dev, 0);
ASSERT(TYPE_OK == t);
#ifdef CONFIG_WL_GS2200M_DISABLE_DHCPC
/* Disable DHCP Client */
t = gs2200m_enable_dhcpc(dev, 0);
ASSERT(TYPE_OK == t);
/* Set static address */
t = gs2200m_set_addresses(dev,
"10.0.0.2",
"255.255.255.0",
"10.0.0.1"
);
ASSERT(TYPE_OK == t);
#else
/* Enable DHCP Client */
t = gs2200m_enable_dhcpc(dev, 1);
ASSERT(TYPE_OK == t);
#endif
/* Get mac address info */
t = gs2200m_get_mac(dev);
ASSERT(TYPE_OK == t);
/* Set WPA2 Passphrase */
if (TYPE_OK != gs2200m_calc_key(dev, msg->ssid, msg->key))
{
wlerr("*** error: invalid wpa2 key (key:%s) \n", msg->key);
return -1;
}
/* Associate with AP */
if (TYPE_OK != gs2200m_join_network(dev, msg->ssid, 0))
{
wlerr("*** error: failed to join (ssid:%s) \n", msg->ssid);
return -1;
}
return OK;
}
/****************************************************************************
* Name: gs2200m_ioctl_assoc_ap
****************************************************************************/
static int gs2200m_ioctl_assoc_ap(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_assoc_msg *msg)
{
enum pkt_type_e t;
/* Set to AP mode */
t = gs2200m_set_opmode(dev, 2);
ASSERT(TYPE_OK == t);
/* Get mac address info */
t = gs2200m_get_mac(dev);
ASSERT(TYPE_OK == t);
/* Disassociate */
t = gs2200m_disassociate(dev);
ASSERT(TYPE_OK == t);
/* Set address info */
t = gs2200m_set_addresses(dev,
"192.168.11.1",
"255.255.255.0",
"192.168.11.1"
);
ASSERT(TYPE_OK == t);
/* Set auth mode */
t = gs2200m_set_auth(dev, 2);
ASSERT(TYPE_OK == t);
/* Set security mode */
t = gs2200m_set_security(dev, 2);
ASSERT(TYPE_OK == t);
/* Set WEP key */
if (TYPE_OK != gs2200m_set_wepkey(dev, msg->key))
{
wlerr("*** error: invalid wepkey: %s \n", msg->key);
return -1;
}
/* Start DHCP server */
t = gs2200m_enable_dhcps(dev, 1);
ASSERT(TYPE_OK == t);
/* Enable the AP */
if (TYPE_OK != gs2200m_join_network(dev, msg->ssid, msg->ch))
{
wlerr("*** error: failed to join (ssid:%s, ch:%d) \n",
msg->ssid, msg->ch);
return -1;
}
return OK;
}
/****************************************************************************
* Name: gs2200m_ifreq_ifreq
****************************************************************************/
static int gs2200m_ioctl_ifreq(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_ifreq_msg *msg)
{
FAR struct sockaddr_in *inaddr;
struct in_addr in[3];
char addr[3][17];
bool getreq = false;
int ret = OK;
wlinfo("+++ start: cmd=%x \n", msg->cmd);
inaddr = (FAR struct sockaddr_in *)&msg->ifr.ifr_addr;
switch (msg->cmd)
{
case SIOCGIFHWADDR:
getreq = true;
memcpy(&msg->ifr.ifr_hwaddr.sa_data,
dev->net_dev.d_mac.ether.ether_addr_octet, 6);
break;
case SIOCSIFADDR:
memcpy(&dev->net_dev.d_ipaddr,
&inaddr->sin_addr, sizeof(inaddr->sin_addr)
);
break;
case SIOCSIFDSTADDR:
memcpy(&dev->net_dev.d_draddr,
&inaddr->sin_addr, sizeof(inaddr->sin_addr)
);
break;
case SIOCSIFNETMASK:
memcpy(&dev->net_dev.d_netmask,
&inaddr->sin_addr, sizeof(inaddr->sin_addr)
);
break;
default:
ret = -EINVAL;
break;
}
if (false == getreq && OK == ret)
{
memcpy(&in[0], &dev->net_dev.d_ipaddr, sizeof(in[0]));
memcpy(&in[1], &dev->net_dev.d_netmask, sizeof(in[1]));
memcpy(&in[2], &dev->net_dev.d_draddr, sizeof(in[2]));
strncpy(addr[0], inet_ntoa(in[0]), sizeof(addr[0]));
strncpy(addr[1], inet_ntoa(in[1]), sizeof(addr[1]));
strncpy(addr[2], inet_ntoa(in[2]), sizeof(addr[2]));
gs2200m_set_addresses(dev, addr[0], addr[1], addr[2]);
}
wlinfo("+++ end: \n");
return ret;
}
/****************************************************************************
* Name: gs2200m_ioctl
****************************************************************************/
static int gs2200m_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode;
FAR struct gs2200m_dev_s *dev;
int ret = -EINVAL;
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct gs2200m_dev_s *)inode->i_private;
/* Lock the device */
ret = gs2200m_lock(dev);
if (ret < 0)
{
/* Return only if the task was canceled */
return ret;
}
/* Disable gs2200m irq to poll dready */
DEBUGASSERT(dev);
dev->lower->disable();
switch (cmd)
{
case GS2200M_IOC_CONNECT:
{
struct gs2200m_connect_msg *msg =
(struct gs2200m_connect_msg *)arg;
ret = gs2200m_ioctl_connect(dev, msg);
}
break;
case GS2200M_IOC_SEND:
{
struct gs2200m_send_msg *msg =
(struct gs2200m_send_msg *)arg;
ret = gs2200m_ioctl_send(dev, msg);
}
break;
case GS2200M_IOC_RECV:
{
struct gs2200m_recv_msg *msg =
(struct gs2200m_recv_msg *)arg;
ret = gs2200m_ioctl_recv(dev, msg);
break;
}
case GS2200M_IOC_CLOSE:
{
struct gs2200m_close_msg *msg =
(struct gs2200m_close_msg *)arg;
ret = gs2200m_ioctl_close(dev, msg);
break;
}
case GS2200M_IOC_BIND:
{
struct gs2200m_bind_msg *msg =
(struct gs2200m_bind_msg *)arg;
ret = gs2200m_ioctl_bind(dev, msg);
break;
}
case GS2200M_IOC_ACCEPT:
{
struct gs2200m_accept_msg *msg =
(struct gs2200m_accept_msg *)arg;
ret = gs2200m_ioctl_accept(dev, msg);
break;
}
case GS2200M_IOC_ASSOC:
{
struct gs2200m_assoc_msg *msg =
(struct gs2200m_assoc_msg *)arg;
if (0 == msg->mode)
{
ret = gs2200m_ioctl_assoc_sta(dev, msg);
}
else
{
ret = gs2200m_ioctl_assoc_ap(dev, msg);
}
break;
}
case GS2200M_IOC_IFREQ:
{
struct gs2200m_ifreq_msg *msg =
(struct gs2200m_ifreq_msg *)arg;
ret = gs2200m_ioctl_ifreq(dev, msg);
break;
}
default:
DEBUGPANIC();
break;
}
/* Enable gs2200m irq again */
dev->lower->enable();
/* Unlock the device */
gs2200m_unlock(dev);
return ret;
}
/****************************************************************************
* Name: gs2200m_poll
****************************************************************************/
static int gs2200m_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup)
{
FAR struct inode *inode;
FAR struct gs2200m_dev_s *dev;
int ret = OK;
wlinfo("== setup:%d\n", (int)setup);
DEBUGASSERT(filep && fds);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct gs2200m_dev_s *)inode->i_private;
ret = gs2200m_lock(dev);
if (ret < 0)
{
/* Return if the task was canceled */
return ret;
}
/* Are we setting up the poll? Or tearing it down? */
if (setup)
{
/* Ignore waits that do not include POLLIN */
if ((fds->events & POLLIN) == 0)
{
ret = -EDEADLK;
goto errout;
}
/* NOTE: only one thread can poll the device at any time */
if (dev->pfd)
{
ret = -EBUSY;
goto errout;
}
dev->pfd = fds;
uint8_t n = _notif_q_count(dev);
if (0 < n)
{
dev->pfd->revents |= POLLIN;
nxsem_post(dev->pfd->sem);
wlinfo("==== _notif_q_count=%d \n", n);
}
}
else
{
dev->pfd = NULL;
}
errout:
gs2200m_unlock(dev);
return ret;
}
/****************************************************************************
* Name: gs2200m_irq_worker
****************************************************************************/
static void gs2200m_irq_worker(FAR void *arg)
{
FAR struct gs2200m_dev_s *dev;
enum pkt_type_e t = TYPE_ERROR;
struct pkt_dat_s *pkt_dat;
bool ignored = false;
uint8_t c;
char s_cid;
char c_cid;
int n;
int ec;
int ret;
DEBUGASSERT(arg != NULL);
dev = (FAR struct gs2200m_dev_s *)arg;
do
{
ret = gs2200m_lock(dev);
/* The only failure would be if the worker thread were canceled. That
* is very unlikely, however.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (ret < 0);
n = dev->lower->dready(&ec);
wlinfo("== start (dready=%d, ec=%d) \n", n, ec);
/* Allocate a new pkt_dat and initialize it */
pkt_dat = (FAR struct pkt_dat_s *)kmm_malloc(sizeof(struct pkt_dat_s));
ASSERT(NULL != pkt_dat);
memset(pkt_dat, 0, sizeof(struct pkt_dat_s));
pkt_dat->cid = 'z';
/* Receive a packet */
t = gs2200m_recv_pkt(dev, pkt_dat);
if (TYPE_ERROR == t || 'z' == pkt_dat->cid)
{
/* An error event? */
wlinfo("=== ignore (type=%d msg[0]=%s|) \n",
pkt_dat->type, pkt_dat->msg[0]);
ignored = true;
goto errout;
}
/* Check if the cid has been invalid */
if (!_cid_is_set(&dev->valid_cid_bits, pkt_dat->cid))
{
wlinfo("=== already closed (type=%d msg[0]=%s|) \n",
pkt_dat->type, pkt_dat->msg[0]);
ignored = true;
goto errout;
}
c = _cid_to_uint8(pkt_dat->cid);
/* Add the pkt_dat to the pkt_q */
dq_addlast((FAR dq_entry_t *)pkt_dat, &dev->pkt_q[c]);
dev->pkt_q_cnt[c]++;
wlinfo("=== added to qkt_q[%d] t=%d \n", c, t);
/* When a DISCONNECT packet received, disable the cid */
if (TYPE_DISCONNECT == t)
{
wlinfo("=== received DISCONNECT for cid=%c \n", pkt_dat->cid);
_enable_cid(&dev->valid_cid_bits, pkt_dat->cid, false);
}
/* If accept() is not in progress for the cid, add the packet to notif_q
*
* NOTE: we need this condition to process the packet in correct order,
* when accept() sequcence is in progress.
*/
if (!_cid_is_set(&dev->aip_cid_bits, pkt_dat->cid))
{
_notif_q_push(dev, pkt_dat->cid);
}
/* Check if the packet is CONNECT event from client */
if (TYPE_CONNECT == t)
{
n = sscanf(pkt_dat->msg[0], "CONNECT %c %c", &s_cid, &c_cid);
ASSERT(2 == n);
wlinfo("==== CONNECT requested (%c:%c) pfd=%p \n",
s_cid, c_cid, dev->pfd);
/* Check pkt_q for the new client is empty */
_check_pkt_q_empty(dev, c_cid);
/* Enable the cid */
_enable_cid(&dev->valid_cid_bits, c_cid, true);
/* Enable accept in progress */
_enable_cid(&dev->aip_cid_bits, c_cid, true);
}
errout:
/* NOTE: Enable gs2200m irq which was disabled in gs2200m_irq() */
dev->lower->enable();
n = dev->lower->dready(&ec);
wlinfo("== end: cid=%c (dready=%d, ec=%d) type=%d \n",
pkt_dat->cid, n, ec, t);
if (ignored)
{
_release_pkt_dat(pkt_dat);
kmm_free(pkt_dat);
}
gs2200m_unlock(dev);
}
/****************************************************************************
* Name: gs2200m_interrupt
****************************************************************************/
static int gs2200m_irq(int irq, FAR void *context, FAR void *arg)
{
FAR struct gs2200m_dev_s *dev;
int ec = 0;
DEBUGASSERT(arg != NULL);
dev = (FAR struct gs2200m_dev_s *)arg;
dev->lower->dready(&ec);
ASSERT(0 < ec);
wlinfo(">>>> \n");
/* NOTE: Disable gs2200m irq during processing */
dev->lower->disable();
if (!work_available(&dev->irq_work))
{
wlwarn("*** warning: there is still pending work **** \n");
return 0;
}
return work_queue(GS2200MWORK, &dev->irq_work, gs2200m_irq_worker,
(FAR void *)dev, 0);
}
/****************************************************************************
* Name: gs2200m_start
****************************************************************************/
static int gs2200m_start(FAR struct gs2200m_dev_s *dev)
{
enum pkt_type_e t;
/* NOTE: irq is still disabled here */
/* Check boot msg */
wlinfo("*** wait for boot msg \n");
while (dev->lower->dready(NULL))
{
gs2200m_recv_pkt(dev, NULL);
break;
}
/* TODO: Need to check Regulatory Domain stored in the internal flash.
* If we need to change the damin, set here.
*/
/* Disable echo */
t = gs2200m_enable_echo(dev, 0);
ASSERT(TYPE_OK == t);
#if CONFIG_WL_GS2200M_LOGLEVEL > 0
/* Set log level */
t = gs2200m_set_loglevel(dev, CONFIG_WL_GS2200M_LOGLEVEL);
ASSERT(TYPE_OK == t);
#endif
#ifdef CONFIG_WL_GS2200M_CHECK_VERSION
/* Version */
t = gs2200m_get_version(dev);
ASSERT(TYPE_OK == t);
#endif
/* Activate RX */
t = gs2200m_activate_wrx(dev, 1);
ASSERT(TYPE_OK == t);
/* Set Bulk Data mode */
t = gs2200m_enable_bulk(dev, 1);
ASSERT(TYPE_OK == t);
/* Interface is up */
dev->net_dev.d_flags |= IFF_UP;
/* NOTE: Enable interrupt here */
dev->lower->enable();
return 0;
}
/****************************************************************************
* Name: gs2200m_initialize
****************************************************************************/
static int gs2200m_initialize(FAR struct gs2200m_dev_s *dev,
FAR const struct gs2200m_lower_s *lower)
{
int ret;
int i;
/* For each cid (0-f) */
for (i = 0; i < 16; i++)
{
/* Initialize packet queue */
dq_init(&dev->pkt_q[i]);
}
/* Initialize SPI driver. */
ret = gs2200m_spi_init(dev);
/* Reset and Unreset GS2200M */
lower->reset(true);
up_mdelay(1);
lower->reset(false);
up_mdelay(180);
/* Attach interrupt handler */
lower->attach(gs2200m_irq, dev);
/* Start gs2200m by sending commands */
gs2200m_start(dev);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: gs2200m_register
****************************************************************************/
FAR void *gs2200m_register(FAR const char *devpath,
FAR struct spi_dev_s *spi,
FAR const struct gs2200m_lower_s *lower)
{
FAR struct gs2200m_dev_s *dev;
int ret;
int size;
size = sizeof(struct gs2200m_dev_s);
dev = (FAR struct gs2200m_dev_s *)kmm_malloc(size);
if (!dev)
{
wlerr("Failed to allocate instance.\n");
return NULL;
}
memset(dev, 0, size);
dev->spi = spi;
dev->path = strdup(devpath);
dev->lower = lower;
nxsem_init(&dev->dev_sem, 0, 1);
dev->pfd = NULL;
ret = gs2200m_initialize(dev, lower);
if (ret < 0)
{
wlerr("Failed to initialize driver: %d\n", ret);
goto errout;
}
ret = register_driver(devpath, &g_gs2200m_fops, 0666, dev);
if (ret < 0)
{
wlerr("Failed to register driver: %d\n", ret);
goto errout;
}
ret = netdev_register(&dev->net_dev, NET_LL_ETHERNET);
return (FAR void *)dev;
errout:
kmm_free(dev);
return NULL;
}