* drivers/wireless/gs2200m.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* 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/param.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <assert.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/mutex.h>
#include <nuttx/signal.h>
#include <nuttx/wireless/wireless.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
#define GS2200MWORK LPWORK
#define SPI_MAXFREQ CONFIG_WL_GS2200M_SPI_FREQUENCY
#define NRESPMSG (16 + 2)
#define BULK_CMD_HDR_SIZE_WITH_GUARD 36
#define MAX_PKT_LEN 1500
#define MAX_PAYLOAD (MAX_PKT_LEN - BULK_CMD_HDR_SIZE_WITH_GUARD)
#define MAX_NOTIF_Q 18
#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
#define WR_MAX_RETRY 100
#define PORT_START 50000
#define PORT_END 59999
#define SEC_MODE_WEP 2
#define SEC_MODE_WPA2PSK 8
#define BULK_THRESHOLD (1024 * 8)
* 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_DISASSOCIATE = 10,
TYPE_UNMATCH = 11,
};
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;
uint16_t len;
FAR uint8_t *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;
uint32_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;
mutex_t dev_lock;
bool int_enabled;
dq_queue_t pkt_q[16];
uint16_t pkt_q_cnt[16];
uint32_t valid_cid_bits;
uint32_t aip_cid_bits;
uint32_t total_bulk;
uint8_t tx_buff[MAX_PKT_LEN];
struct net_driver_s net_dev;
uint8_t op_mode;
FAR const struct gs2200m_lower_s *lower;
bool disassociate_flag;
struct gs2200m_assoc_msg reconnect_msg;
};
* Private Function Prototypes
****************************************************************************/
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);
static int gs2200m_irq(int irq, FAR void *context, FAR void *arg);
static void gs2200m_irq_worker(FAR void *arg);
static void _remove_all_pkt(FAR struct gs2200m_dev_s *dev, uint8_t c);
* Private Data
****************************************************************************/
static const struct file_operations g_gs2200m_fops =
{
NULL,
NULL,
gs2200m_read,
gs2200m_write,
NULL,
gs2200m_ioctl,
NULL,
NULL,
gs2200m_poll
};
static struct evt_code_s _evt_table[] =
{
{"OK", TYPE_OK},
{"Disassociation Event", TYPE_DISASSOCIATE},
{"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:
r = TYPE_UNMATCH;
PANIC();
break;
}
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 if (c == DISASSOCIATION_CID)
{
ret = 16;
}
else
{
ret = 0xff;
PANIC();
}
return ret;
}
* Name: _to_ascii_char
****************************************************************************/
static void _to_ascii_char(uint16_t num, char *str)
{
DEBUGASSERT(num <= 2032);
snprintf(str, 6, "%04d", num);
}
* Name: _to_uint16
****************************************************************************/
static uint16_t _to_uint16(char *str)
{
uint16_t ret = 0;
int n;
n = sscanf(str, "%04hu", &ret);
ASSERT(1 == n);
return ret;
}
* Name: _enable_cid
****************************************************************************/
static bool _enable_cid(uint32_t *cid_bits, char cid, bool on)
{
uint32_t mask = 1 << _cid_to_uint8(cid);
bool ret = true;
if (on)
{
if (*cid_bits & mask)
{
ret = false;
}
else
{
*cid_bits |= mask;
}
}
else
{
if (*cid_bits & mask)
{
*cid_bits &= ~mask;
}
else
{
ret = false;
}
}
return ret;
}
* Name: _cid_is_set
****************************************************************************/
static bool _cid_is_set(uint32_t *cid_bits, char cid)
{
uint32_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);
bool ret = _enable_cid(&dev->notif_q.inuse, cid, true);
if (false == ret)
{
return;
}
dev->notif_q.cids[dev->notif_q.wpos % MAX_NOTIF_Q] = cid;
dev->notif_q.wpos++;
dev->notif_q.count++;
if (dev->pfd)
{
poll_notify(&dev->pfd, 1, POLLIN);
}
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--;
_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(FAR struct gs2200m_dev_s *dev,
FAR 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);
if (pkt_dat->type == TYPE_BULK_DATA_TCP ||
pkt_dat->type == TYPE_BULK_DATA_UDP)
{
ASSERT(dev->total_bulk >= pkt_dat->len);
dev->total_bulk -= pkt_dat->len;
}
}
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 (cid=%c type=%d msg[0]=%s|)\n",
cid, pkt_dat->type, pkt_dat->msg[0]);
pkt_dat = (FAR struct pkt_dat_s *)pkt_dat->dq.flink;
if (_cid_is_set(&dev->valid_cid_bits, cid))
{
wlerr("+++ error: cid=%c is still active !!!\n", cid);
}
_enable_cid(&dev->valid_cid_bits, cid, false);
}
_remove_all_pkt(dev, c);
}
}
* Name: _control_pkt_q
****************************************************************************/
static bool _control_pkt_q(FAR struct gs2200m_dev_s *dev)
{
bool over = BULK_THRESHOLD < dev->total_bulk ? true : false;
if (dev->int_enabled && over)
{
wlinfo("--- disable irq\n");
dev->int_enabled = false;
dev->lower->disable();
}
if (!dev->int_enabled && !over)
{
wlinfo("--- enable irq again\n");
dev->lower->enable();
dev->int_enabled = true;
}
return over;
}
* 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;
ASSERT(0 < dev->pkt_q_cnt[c]);
dev->pkt_q_cnt[c]--;
pkt_dat = (FAR struct pkt_dat_s *)dq_remfirst(&dev->pkt_q[c]);
ASSERT(pkt_dat);
_release_pkt_dat(dev, 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;
uint32_t mask;
mask = 1 << c;
ASSERT(0 == (dev->valid_cid_bits & mask));
ASSERT(dev->pkt_q_cnt[c] == dq_count(&dev->pkt_q[c]));
pkt_dat = (FAR struct pkt_dat_s *)dq_peek(&dev->pkt_q[c]);
while (pkt_dat)
{
_remove_and_free_pkt(dev, c);
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;
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)
{
ret = false;
goto errout;
}
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;
* Remove the packet from the queue and free it.
*/
pkt_dat->remain -= len;
if (0 == pkt_dat->remain || TYPE_BULK_DATA_UDP == pkt_dat->type)
{
_remove_and_free_pkt(dev, c);
}
errout:
if (!msg->is_tcp)
{
memcpy(&msg->addr, &pkt_dat->addr, sizeof(pkt_dat->addr));
* NOTE: gs2200m only supports IPv4
*/
msg->addr.sin_family = AF_INET;
ret = false;
}
return ret;
}
* 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;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
ASSERT(1 == len);
ret = nxmutex_lock(&dev->dev_lock);
if (ret < 0)
{
* 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));
memcpy(buffer, &cid, sizeof(cid));
nxmutex_unlock(&dev->dev_lock);
return 1;
}
* Name: gs2200m_write
****************************************************************************/
static ssize_t gs2200m_write(FAR struct file *filep, FAR const char *buffer,
size_t len)
{
return 0;
}
* Name: gs2200m_spi_init
****************************************************************************/
static int gs2200m_spi_init(FAR struct gs2200m_dev_s *dev)
{
SPI_LOCK(dev->spi, true);
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;
*(p + 1) = class;
*(p + 2) = 0x0;
*(p + 3) = 0x0;
*(p + 4) = 0x0;
*(p + 5) = (uint8_t)len;
*(p + 6) = (uint8_t)(len >> 8);
*(p + 7) = _checksum(p + 1, 6);
}
* 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.2 SPI Command Response (SPI-DMA)
****************************************************************************/
static void _read_data(FAR struct gs2200m_dev_s *dev,
FAR uint8_t *buff,
FAR uint16_t len)
{
memset(buff, 0, len);
SPI_SELECT(dev->spi, SPIDEV_WIRELESS(0), true);
SPI_RECVBLOCK(dev->spi, 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(hdr, MAX_PKT_LEN, RD_REQ);
retry:
_write_data(dev, hdr, sizeof(hdr));
while (!dev->lower->dready(NULL))
{
}
* workaround to avoid an invalid frame response
*/
up_udelay(50);
_read_data(dev, res, sizeof(res));
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]);
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,
FAR const void *data,
uint16_t txlen)
{
FAR uint8_t *tx = (FAR uint8_t *)data;
uint8_t hdr[8];
uint8_t res[8];
int n = 0;
_prepare_header(hdr, txlen, WR_REQ);
retry:
_write_data(dev, hdr, sizeof(hdr) / 2);
up_udelay(4);
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
{
* 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;
}
_write_data(dev, hdr + (sizeof(hdr) / 2), sizeof(hdr) / 2);
while (!dev->lower->dready(NULL))
{
}
_read_data(dev, res, sizeof(res));
if (WR_RESP_NOK == res[1] || 0x0 == res[1])
{
wlwarn("*** warning: 0x%x received.. retrying. (n=%d)\n",
res[1], n);
nxsig_usleep(10 * 1000);
if (WR_MAX_RETRY < n)
{
return SPI_TIMEOUT;
}
n++;
goto retry;
}
ASSERT(WR_RESP_OK == res[1]);
_prepare_header(hdr, txlen, DT_FROM_MCU);
_write_data(dev, hdr, sizeof(hdr));
_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)
{
uint8_t hdr[8];
int i;
for (i = 0; i < HAL_TIMEOUT; i++)
{
if (dev->lower->dready(NULL))
{
break;
}
up_udelay(1);
}
if (HAL_TIMEOUT == i)
{
wlerr("***** error: timeout!\n");
return SPI_TIMEOUT;
}
* to get how many bytes we should read
*/
*len = _read_data_len(dev);
wlinfo("+++++ (len=%d)\n", *len);
ASSERT(0 < *len);
_read_data(dev, hdr, sizeof(hdr));
_read_data(dev, data, *len);
return SPI_OK;
}
* 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)
{
FAR char *msg;
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;
msg = 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);
n = sscanf(msg, "CONNECT %c", &(pkt_dat->cid));
DEBUGASSERT(1 == n);
wlinfo("+++++ msg=%s|\n", msg);
}
if (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");
pkt_ctx->state = PKT_BULK_DATA_TCP;
}
else if ('y' == c)
{
wlinfo("** <ESC>y\n");
pkt_ctx->state = PKT_BULK_DATA_UDP;
}
else if ('F' == c)
{
wlwarn("** <ESC>F\n");
pkt_ctx->state = PKT_START;
pkt_ctx->type = TYPE_FAIL;
}
else
{
wlerr("** <ESC>%c not supported\n", c);
PANIC();
}
}
* 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)
{
* NOTE: <CID><Data Length xxxx 4 ascii char>
*/
pkt_ctx->cid = (char)*(pkt_ctx->ptr);
pkt_ctx->ptr++;
pkt_dat->cid = pkt_ctx->cid;
pkt_ctx->dlen = _to_uint16((FAR char *)pkt_ctx->ptr);
pkt_ctx->ptr += 3;
wlinfo("dlen=%d\n", pkt_ctx->dlen);
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)
{
pkt_ctx->cid = (char)*(pkt_ctx->ptr);
pkt_ctx->ptr++;
pkt_dat->cid = pkt_ctx->cid;
memset(addr, 0, sizeof(addr));
memset(port, 0, sizeof(port));
n = sscanf((FAR const 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));
for (; '\t' != (char)*(pkt_ctx->ptr); pkt_ctx->ptr++);
pkt_ctx->ptr++;
pkt_ctx->dlen = _to_uint16((FAR char *)pkt_ctx->ptr);
pkt_ctx->ptr += 3;
wlinfo("dlen=%d\n", pkt_ctx->dlen);
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;
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:
PANIC();
break;
}
}
return pkt_ctx.type;
}
* Name: _dup_pkt_dat_and_notify
****************************************************************************/
static void _dup_pkt_dat_and_notify(FAR struct gs2200m_dev_s *dev,
FAR struct pkt_dat_s *pkt_dat0)
{
FAR struct pkt_dat_s *pkt_dat;
uint8_t c;
ASSERT(pkt_dat0->data && (0 == pkt_dat0->n));
pkt_dat = kmm_malloc(sizeof(struct pkt_dat_s));
ASSERT(pkt_dat);
memcpy(pkt_dat, pkt_dat0, sizeof(struct pkt_dat_s));
pkt_dat->data = kmm_malloc(pkt_dat0->len);
ASSERT(pkt_dat->data);
memcpy(pkt_dat->data, pkt_dat0->data, pkt_dat0->len);
c = _cid_to_uint8(pkt_dat->cid);
dq_addlast((FAR dq_entry_t *)pkt_dat, &dev->pkt_q[c]);
dev->pkt_q_cnt[c]++;
* Usually, total_bulk is updated in gs2200m_recv_pkt()
* However, the pkt_dat was duplicated from pkt_dat0
* So it needs to be updated, otherwise it will cause ASSERT
*/
dev->total_bulk += pkt_dat->len;
_notif_q_push(dev, pkt_dat->cid);
}
* 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;
FAR uint8_t *p;
p = 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);
t = _parse_pkt(p, len, pkt_dat);
if (t == TYPE_DISCONNECT)
{
_check_pkt_q_cnt(dev, pkt_dat->cid);
}
if (t == TYPE_DISASSOCIATE)
{
dev->disassociate_flag = true;
}
if (pkt_dat)
{
pkt_dat->type = t;
if (t == TYPE_BULK_DATA_TCP ||
t == TYPE_BULK_DATA_UDP)
{
dev->total_bulk += pkt_dat->len;
}
}
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;
bool bulk = false;
int n = 1;
dev->lower->disable();
wlinfo("+++ cmd=%s", cmd);
retry:
s = gs2200m_hal_write(dev, cmd, strlen(cmd));
r = _spi_err_to_pkt_type(s);
if (TYPE_OK != r)
{
goto errout;
}
retry_recv:
r = gs2200m_recv_pkt(dev, pkt_dat);
if ((TYPE_BULK_DATA_TCP == r || TYPE_BULK_DATA_UDP == r) && pkt_dat)
{
wlwarn("*** Found bulk data\n");
* duplicate the packet and notify
*/
_dup_pkt_dat_and_notify(dev, pkt_dat);
_release_pkt_dat(dev, pkt_dat);
memset(pkt_dat, 0, sizeof(*pkt_dat));
bulk = true;
goto retry_recv;
}
if ((TYPE_OK != r) && (0 <= --n))
{
if (pkt_dat)
{
_release_pkt_dat(dev, pkt_dat);
memset(pkt_dat, 0, sizeof(*pkt_dat));
}
wlwarn("*** retry cmd=%s (n=%d)\n", cmd, n);
goto retry;
}
errout:
if (bulk)
{
wlwarn("*** Normal response r=%d\n", r);
}
dev->lower->enable();
return r;
}
* Name: gs2200m_send_cmd2
****************************************************************************/
static enum pkt_type_e gs2200m_send_cmd2(FAR struct gs2200m_dev_s *dev,
FAR char *cmd)
{
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
_release_pkt_dat(dev, &pkt_dat);
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_cmd2(dev, cmd);
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;
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], "%2" PRIx32 ":%2" PRIx32 ":%2" PRIx32
":%2" PRIx32 ":%2" PRIx32 ":%2" PRIx32,
&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(dev, &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_cmd2(dev, "AT+WD\r\n");
}
* 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_cmd2(dev, cmd);
}
* 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_cmd2(dev, cmd);
}
* 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_cmd2(dev, cmd);
}
* 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;
memset(&pkt_dat, 0, sizeof(pkt_dat));
if (0 == dev->op_mode)
{
snprintf(cmd, sizeof(cmd), "AT+WA=%s\r\n", ssid);
}
else
{
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);
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(dev, &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_cmd2(dev, cmd);
}
* 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_cmd2(dev, cmd);
}
* 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_cmd2(dev, cmd);
}
#ifdef CONFIG_WL_GS2200M_ENABLE_WEP
* Name: gs2200m_set_wepkey
* NOTE: See 5.3.3.2
****************************************************************************/
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_cmd2(dev, cmd);
}
#else
* Name: gs2200m_set_wpa2pf
* NOTE: See 5.3.3.4
****************************************************************************/
static enum pkt_type_e gs2200m_set_wpa2pf(FAR struct gs2200m_dev_s *dev,
FAR char *key)
{
char cmd[64];
snprintf(cmd, sizeof(cmd), "AT+WWPA=%s\r\n", key);
return gs2200m_send_cmd2(dev, cmd);
}
#endif
* 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;
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, "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(dev, &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 struct gs2200m_connect_msg *msg,
FAR char *cid)
{
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
char cmd[40];
char *p;
int n;
*cid = 'z';
if (SOCK_STREAM == msg->type)
{
snprintf(cmd, sizeof(cmd), "AT+NCTCP=%s,%s\r\n",
msg->addr, msg->port);
}
else if (SOCK_DGRAM == msg->type)
{
if (0 == msg->lport)
{
snprintf(cmd, sizeof(cmd), "AT+NCUDP=%s,%s\r\n",
msg->addr, msg->port);
}
else
{
snprintf(cmd, sizeof(cmd), "AT+NCUDP=%s,%s,%d\r\n",
msg->addr, msg->port, msg->lport);
}
}
else
{
PANIC();
}
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
if (r != TYPE_OK || pkt_dat.n == 0)
{
wlerr("+++ 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(dev, &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)
{
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
char cmd[40];
char *p;
int n;
if (is_tcp)
{
snprintf(cmd, sizeof(cmd), "AT+NSTCP=%s\r\n", port);
}
else
{
snprintf(cmd, sizeof(cmd), "AT+NSUDP=%s\r\n", port);
}
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
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(dev, &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 spi_status_e s;
enum pkt_type_e r;
int bulk_hdr_size;
char digits[6];
char cmd[32];
memset(cmd, 0, sizeof(cmd));
if (MAX_PAYLOAD <= msg->len)
{
msg->len = MAX_PAYLOAD;
}
_to_ascii_char(msg->len, digits);
wlinfo("** cid=%c len=%d digits=%s\n", msg->cid, msg->len, digits);
if (msg->is_tcp)
{
* <ESC>Z<CID><Data Length xxxx 4 ascii char><data>
*/
snprintf(cmd, sizeof(cmd), "%cZ%c%s", ASCII_ESC, msg->cid, digits);
}
else
{
char inetaddr[INET_ADDRSTRLEN];
wlinfo("** addr=%s port=%d\n",
inet_ntoa_r(msg->addr.sin_addr, inetaddr, sizeof(inetaddr)),
NTOHS(msg->addr.sin_port));
* <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_r(msg->addr.sin_addr, inetaddr, sizeof(inetaddr)),
NTOHS(msg->addr.sin_port),
digits);
}
bulk_hdr_size = strlen(cmd);
memcpy(dev->tx_buff, cmd, bulk_hdr_size);
memcpy(dev->tx_buff + bulk_hdr_size, msg->buf, msg->len);
s = gs2200m_hal_write(dev, (FAR char *)dev->tx_buff,
msg->len + bulk_hdr_size);
if (s == SPI_TIMEOUT)
{
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)
{
char cmd[15];
snprintf(cmd, sizeof(cmd), "AT+NCLOSE=%c\r\n", cid);
return gs2200m_send_cmd2(dev, cmd);
}
* 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_cmd2(dev, cmd);
}
* 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[9];
snprintf(cmd, sizeof(cmd), "ATE%d\r\n", on);
return gs2200m_send_cmd2(dev, cmd);
}
* 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_cmd2(dev, cmd);
}
* Name: gs2200m_powersave_wrx
* NOTE: See 9.1.1 Active Radio Receive
****************************************************************************/
static enum pkt_type_e gs2200m_powersave_wrx(FAR struct gs2200m_dev_s *dev,
uint8_t on)
{
char cmd[30];
snprintf(cmd, sizeof(cmd), "AT+WRXPS=%d\r\n", on);
return gs2200m_send_cmd2(dev, cmd);
}
* Name: gs2200m_syncloss
* NOTE: See 5.1.4 Sync loss interval
****************************************************************************/
static enum pkt_type_e gs2200m_syncloss(FAR struct gs2200m_dev_s *dev,
int val)
{
char cmd[30];
snprintf(cmd, sizeof(cmd), "AT+WSYNCINTRL=%d\r\n", val);
return gs2200m_send_cmd2(dev, cmd);
}
* 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_cmd2(dev, cmd);
}
#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_cmd2(dev, cmd);
}
#endif
* Name: gs2200m_closeallsock
* NOTE: See 7.1.5 Closing All Connections
****************************************************************************/
static void gs2200m_closeallsock(FAR struct gs2200m_dev_s *dev)
{
gs2200m_send_cmd2(dev, "AT+NCLOSEALL\r\n");
}
* 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_cmd2(dev, cmd);
}
#endif
* Name: gs2200m_get_cstatus
****************************************************************************/
static enum pkt_type_e gs2200m_get_cstatus(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_name_msg *msg)
{
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
char cmd[16];
int i;
snprintf(cmd, sizeof(cmd), "AT+CID=?\r\n");
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
if (r != TYPE_OK || pkt_dat.n <= 2)
{
wlerr("+++ error: r=%d pkt_dat.msg[0]=%s\n",
r, pkt_dat.msg[0]);
goto errout;
}
for (i = 1; i < pkt_dat.n - 2; i++)
{
int n;
char c;
int a[4];
int p[2];
char type[8];
char mode[8];
memset(type, 0, sizeof(type));
memset(mode, 0, sizeof(mode));
n = sscanf(pkt_dat.msg[i], "%c %7s %6s %d %d %d.%d.%d.%d",
&c, type, mode, &p[0], &p[1],
&a[0], &a[1], &a[2], &a[3]);
ASSERT(9 == n);
wlinfo("[%d]: %c %s %s %d %d %d.%d.%d.%d\n",
i, c, type, mode, p[0], p[1],
a[0], a[1], a[2], a[3]);
if (c == msg->cid)
{
msg->addr.sin_family = AF_INET;
if (msg->local)
{
msg->addr.sin_port = HTONS(p[0]);
}
else
{
char addr[20];
msg->addr.sin_port = HTONS(p[1]);
snprintf(addr, sizeof(addr),
"%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
inet_aton(addr, &msg->addr.sin_addr);
}
goto errout;
}
}
r = TYPE_UNMATCH;
errout:
_release_pkt_dat(dev, &pkt_dat);
return r;
}
* 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;
bool auto_assign = false;
char port_str[6];
uint16_t port;
char cid = 'z';
int ret = OK;
wlinfo("+++ start: (cid=%c, port=%s)\n", msg->cid, msg->port);
port = (uint16_t)strtol(msg->port, NULL, 10);
if (0 == port)
{
auto_assign = true;
port = PORT_START;
}
retry:
snprintf(port_str, sizeof(port_str), "%d", port);
type = gs2200m_start_server(dev, port_str, msg->is_tcp, &cid);
if (type != TYPE_OK)
{
if (auto_assign && (port < PORT_END))
{
port++;
goto retry;
}
ret = -EINVAL;
goto errout;
}
_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);
type = gs2200m_create_clnt(dev, msg, &cid);
msg->type = type;
switch (type)
{
case TYPE_OK:
msg->cid = cid;
_enable_cid(&dev->valid_cid_bits, cid, true);
_check_pkt_q_empty(dev, cid);
break;
case TYPE_ERROR:
ret = -ECONNREFUSED;
break;
case TYPE_TIMEOUT:
ret = -ETIMEDOUT;
break;
default:
wlerr("+++ error: type=%d\n", type);
PANIC();
ret = -EINVAL;
break;
}
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;
int ret = OK;
wlinfo("+++ start: (cid=%c)\n", msg->cid);
#ifdef USE_LED
gs2200m_set_gpio(dev, LED_GPIO, 1);
#endif
if (!msg->is_tcp && 'z' == msg->cid)
{
memset(&bmsg, 0, sizeof(bmsg));
ret = gs2200m_ioctl_bind(dev, &bmsg);
ASSERT(0 == ret);
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;
ret = -ENOTCONN;
goto errout;
}
type = gs2200m_send_bulk(dev, msg);
msg->type = type;
errout:
if (type != TYPE_OK && type != TYPE_DISCONNECT)
{
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])
{
wlwarn("**** no packet for cid=%c\n", msg->cid);
ret = -EAGAIN;
goto errout;
}
while (1)
{
if (msg->reqlen == msg->len || 0 == dev->pkt_q_cnt[c] || !cont)
{
break;
}
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);
}
_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;
}
_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(dev, _cid_to_uint8(msg->cid));
_control_pkt_q(dev);
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;
struct gs2200m_name_msg nmsg;
enum pkt_type_e r;
uint8_t c;
char s_cid;
char c_cid;
int ret = OK;
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]);
if (NULL == pkt_dat)
{
wlerr("*** error: cid=%c not found\n", msg->cid);
ret = -EINVAL;
goto errout;
}
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_and_free_pkt(dev, _cid_to_uint8(msg->cid));
msg->type = TYPE_OK;
msg->cid = c_cid;
_enable_cid(&dev->aip_cid_bits, c_cid, false);
if (dev->pkt_q_cnt[_cid_to_uint8(c_cid)])
{
_notif_q_push(dev, c_cid);
}
nmsg.local = 0;
nmsg.cid = msg->cid;
r = gs2200m_get_cstatus(dev, &nmsg);
if (TYPE_OK != r)
{
wlerr("*** error: cid=%c not found\n", msg->cid);
ret = -EINVAL;
goto errout;
}
msg->addr = nmsg.addr;
errout:
wlinfo("+++ end: type=%d (msg->cid=%c)\n", msg->type, msg->cid);
return ret;
}
* 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;
memcpy(&dev->reconnect_msg, msg, sizeof(struct gs2200m_assoc_msg));
t = gs2200m_disassociate(dev);
ASSERT(TYPE_OK == t);
t = gs2200m_set_opmode(dev, 0);
ASSERT(TYPE_OK == t);
#ifdef CONFIG_WL_GS2200M_DISABLE_DHCPC
t = gs2200m_enable_dhcpc(dev, 0);
ASSERT(TYPE_OK == t);
t = gs2200m_set_addresses(dev,
"10.0.0.2",
"255.255.255.0",
"10.0.0.1"
);
ASSERT(TYPE_OK == t);
#else
t = gs2200m_enable_dhcpc(dev, 1);
ASSERT(TYPE_OK == t);
#endif
t = gs2200m_get_mac(dev);
ASSERT(TYPE_OK == t);
if (TYPE_OK != gs2200m_calc_key(dev, msg->ssid, msg->key))
{
wlerr("*** error: invalid wpa2 key (key:%s)\n", msg->key);
return -1;
}
if (TYPE_OK != gs2200m_join_network(dev, msg->ssid, 0))
{
wlerr("*** error: failed to join (ssid:%s)\n", msg->ssid);
return -1;
}
dev->disassociate_flag = false;
t = gs2200m_syncloss(dev, CONFIG_WL_GS2200M_SYNC_INTERVAL);
ASSERT(TYPE_OK == t);
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;
t = gs2200m_set_opmode(dev, 2);
ASSERT(TYPE_OK == t);
t = gs2200m_get_mac(dev);
ASSERT(TYPE_OK == t);
t = gs2200m_disassociate(dev);
ASSERT(TYPE_OK == t);
t = gs2200m_set_addresses(dev,
"192.168.11.1",
"255.255.255.0",
"192.168.11.1"
);
ASSERT(TYPE_OK == t);
t = gs2200m_set_auth(dev, 2);
ASSERT(TYPE_OK == t);
#ifdef CONFIG_WL_GS2200M_ENABLE_WEP
t = gs2200m_set_security(dev, SEC_MODE_WEP);
ASSERT(TYPE_OK == t);
if (TYPE_OK != gs2200m_set_wepkey(dev, msg->key))
{
wlerr("*** error: invalid wepkey: %s\n", msg->key);
return -1;
}
#else
t = gs2200m_set_security(dev, SEC_MODE_WPA2PSK);
ASSERT(TYPE_OK == t);
if (TYPE_OK != gs2200m_set_wpa2pf(dev, msg->key))
{
wlerr("*** error: invalid passphrase: %s\n", msg->key);
return -1;
}
#endif
t = gs2200m_enable_dhcps(dev, 1);
ASSERT(TYPE_OK == t);
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_gs2200m_ioctl_iwreq
****************************************************************************/
static int gs2200m_ioctl_iwreq(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_ifreq_msg *msg)
{
struct iwreq *res = (struct iwreq *)&msg->ifr;
struct pkt_dat_s pkt_dat;
enum pkt_type_e r;
char cmd[64];
char cmd2[64];
int n = 0;
snprintf(cmd, sizeof(cmd), "AT+NSTAT=?\r\n");
memset(&pkt_dat, 0, sizeof(pkt_dat));
r = gs2200m_send_cmd(dev, cmd, &pkt_dat);
if (r != TYPE_OK || pkt_dat.n <= 7)
{
wlerr("+++ error: r=%d pkt_dat.msg[0]=%s\n",
r, pkt_dat.msg[0]);
goto errout;
}
if (msg->cmd == SIOCGIWNWID)
{
if (strstr(pkt_dat.msg[2], "BSSID=") == NULL)
{
wlerr("+++ error: pkt_dat.msg[2]=%s\n", pkt_dat.msg[2]);
goto errout;
}
n = sscanf(pkt_dat.msg[2], "BSSID=%c:%c:%c:%c:%c:%c %s",
&res->u.ap_addr.sa_data[0], &res->u.ap_addr.sa_data[1],
&res->u.ap_addr.sa_data[2], &res->u.ap_addr.sa_data[3],
&res->u.ap_addr.sa_data[4], &res->u.ap_addr.sa_data[5],
cmd);
ASSERT(7 == n);
wlinfo("BSSID:%02X:%02X:%02X:%02X:%02X:%02X\n",
res->u.ap_addr.sa_data[0], res->u.ap_addr.sa_data[1],
res->u.ap_addr.sa_data[2], res->u.ap_addr.sa_data[3],
res->u.ap_addr.sa_data[4], res->u.ap_addr.sa_data[5]);
}
else if (msg->cmd == SIOCGIWFREQ)
{
if (strstr(pkt_dat.msg[2], "CHANNEL=") == NULL)
{
wlerr("+++ error: pkt_dat.msg[2]=%s\n", pkt_dat.msg[2]);
goto errout;
}
n = sscanf(pkt_dat.msg[2], "%s CHANNEL=%" SCNd32 " %s",
cmd, &res->u.freq.m, cmd2);
ASSERT(3 == n);
wlinfo("CHANNEL:%" PRId32 "\n", res->u.freq.m);
}
else if (msg->cmd == SIOCGIWSENS)
{
if (strstr(pkt_dat.msg[3], "RSSI=") == NULL)
{
wlerr("+++ error: pkt_dat.msg[3]=%s\n", pkt_dat.msg[3]);
goto errout;
}
n = sscanf(pkt_dat.msg[3], "RSSI=%" SCNd8, &res->u.qual.level);
ASSERT(1 == n);
wlinfo("RSSI:%d\n", res->u.qual.level);
}
errout:
_release_pkt_dat(dev, &pkt_dat);
if (n == 0)
{
return -EINVAL;
}
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=%" PRIx32 "\n", msg->cmd);
inaddr = (FAR struct sockaddr_in *)&msg->ifr.ifr_addr;
switch (msg->cmd)
{
case SIOCGIFFLAGS:
getreq = true;
msg->ifr.ifr_flags = dev->net_dev.d_flags;
break;
case SIOCGIFHWADDR:
getreq = true;
memcpy(&msg->ifr.ifr_hwaddr.sa_data,
dev->net_dev.d_mac.ether.ether_addr_octet, 6);
break;
case SIOCGIFADDR:
getreq = true;
memcpy(&inaddr->sin_addr,
&dev->net_dev.d_ipaddr,
sizeof(dev->net_dev.d_ipaddr)
);
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;
case SIOCGIWNWID:
case SIOCGIWFREQ:
case SIOCGIWSENS:
ret = gs2200m_ioctl_iwreq(dev, msg);
break;
default:
ret = -EINVAL;
break;
}
if (false == getreq && OK == ret)
{
char inetaddr[INET_ADDRSTRLEN];
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]));
strlcpy(addr[0], inet_ntoa_r(in[0], inetaddr, sizeof(inetaddr)),
sizeof(addr[0]));
strlcpy(addr[1], inet_ntoa_r(in[1], inetaddr, sizeof(inetaddr)),
sizeof(addr[1]));
strlcpy(addr[2], inet_ntoa_r(in[2], inetaddr, sizeof(inetaddr)),
sizeof(addr[2]));
gs2200m_set_addresses(dev, addr[0], addr[1], addr[2]);
}
wlinfo("+++ end:\n");
return ret;
}
* Name: gs2200m_ioctl_name
****************************************************************************/
static int gs2200m_ioctl_name(FAR struct gs2200m_dev_s *dev,
FAR struct gs2200m_name_msg *msg)
{
enum pkt_type_e r;
r = gs2200m_get_cstatus(dev, msg);
if (r != TYPE_OK)
{
return -EINVAL;
}
if (msg->local)
{
memcpy(&msg->addr.sin_addr,
&dev->net_dev.d_ipaddr,
sizeof(msg->addr.sin_addr)
);
}
return 0;
}
* 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;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
ret = nxmutex_lock(&dev->dev_lock);
if (ret < 0)
{
return ret;
}
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;
}
case GS2200M_IOC_NAME:
{
struct gs2200m_name_msg *msg =
(struct gs2200m_name_msg *)arg;
ret = gs2200m_ioctl_name(dev, msg);
break;
}
default:
DEBUGPANIC();
break;
}
dev->lower->enable();
nxmutex_unlock(&dev->dev_lock);
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(fds);
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
ret = nxmutex_lock(&dev->dev_lock);
if (ret < 0)
{
return ret;
}
if (setup)
{
if ((fds->events & POLLIN) == 0)
{
ret = -EDEADLK;
goto errout;
}
if (dev->pfd)
{
ret = -EBUSY;
goto errout;
}
dev->pfd = fds;
uint8_t n = _notif_q_count(dev);
if (0 < n)
{
poll_notify(&fds, 1, POLLIN);
}
}
else
{
dev->pfd = NULL;
}
errout:
nxmutex_unlock(&dev->dev_lock);
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;
bool over;
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 = nxmutex_lock(&dev->dev_lock);
* is very unlikely, however.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (ret < 0);
repeat:
n = dev->lower->dready(&ec);
wlinfo("== start (dready=%d, ec=%d)\n", n, ec);
pkt_dat = kmm_malloc(sizeof(struct pkt_dat_s));
ASSERT(NULL != pkt_dat);
memset(pkt_dat, 0, sizeof(struct pkt_dat_s));
pkt_dat->cid = 'z';
t = gs2200m_recv_pkt(dev, pkt_dat);
if (true == dev->disassociate_flag)
{
wlwarn("=== receive DISASSOCIATE\n");
dev->valid_cid_bits = 0;
do
{
while (gs2200m_recv_pkt(dev, NULL) != TYPE_TIMEOUT)
{
nxsig_usleep(100 * 1000);
}
}
while (gs2200m_ioctl_assoc_sta(dev, &dev->reconnect_msg) != OK);
wlwarn("=== recover DISASSOCIATE\n");
dev->disassociate_flag = false;
gs2200m_closeallsock(dev);
_notif_q_push(dev, DISASSOCIATION_CID);
goto errout;
}
if (TYPE_ERROR == t || 'z' == pkt_dat->cid)
{
wlerr("=== ignore (type=%d msg[0]=%s|)\n",
pkt_dat->type, pkt_dat->msg[0]);
ignored = true;
goto errout;
}
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);
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);
if (TYPE_DISCONNECT == t)
{
wlinfo("=== received DISCONNECT for cid=%c\n", pkt_dat->cid);
_enable_cid(&dev->valid_cid_bits, pkt_dat->cid, false);
}
*
* 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);
}
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_empty(dev, c_cid);
_enable_cid(&dev->valid_cid_bits, c_cid, true);
_enable_cid(&dev->aip_cid_bits, c_cid, true);
}
over = _control_pkt_q(dev);
errout:
if (ignored)
{
_release_pkt_dat(dev, pkt_dat);
kmm_free(pkt_dat);
ignored = false;
}
n = dev->lower->dready(&ec);
wlinfo("== end: cid=%c (dready=%d, ec=%d) type=%d\n",
pkt_dat->cid, n, ec, t);
if (1 == n && !over)
{
goto repeat;
}
dev->lower->enable();
nxmutex_unlock(&dev->dev_lock);
}
* Name: gs2200m_interrupt
****************************************************************************/
static int gs2200m_irq(int irq, FAR void *context, FAR void *arg)
{
FAR struct gs2200m_dev_s *dev;
DEBUGASSERT(arg != NULL);
dev = (FAR struct gs2200m_dev_s *)arg;
wlinfo(">>>>\n");
if (!work_available(&dev->irq_work))
{
wlwarn("*** warning: there is still pending work ****\n");
return 0;
}
dev->lower->disable();
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;
wlinfo("*** wait for boot msg\n");
while (dev->lower->dready(NULL))
{
gs2200m_recv_pkt(dev, NULL);
break;
}
* If we need to change the damin, set here.
*/
t = gs2200m_enable_echo(dev, 0);
ASSERT(TYPE_OK == t);
#if CONFIG_WL_GS2200M_LOGLEVEL > 0
t = gs2200m_set_loglevel(dev, CONFIG_WL_GS2200M_LOGLEVEL);
ASSERT(TYPE_OK == t);
#endif
#ifdef CONFIG_WL_GS2200M_CHECK_VERSION
t = gs2200m_get_version(dev);
ASSERT(TYPE_OK == t);
#endif
t = gs2200m_activate_wrx(dev, 1);
ASSERT(TYPE_OK == t);
t = gs2200m_powersave_wrx(dev, 0);
ASSERT(TYPE_OK == t);
t = gs2200m_enable_bulk(dev, 1);
ASSERT(TYPE_OK == t);
dev->net_dev.d_flags |= IFF_UP;
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 (i = 0; i < 16; i++)
{
dq_init(&dev->pkt_q[i]);
}
ret = gs2200m_spi_init(dev);
lower->reset(true);
up_mdelay(1);
lower->reset(false);
up_mdelay(180);
lower->attach(gs2200m_irq, dev);
dev->int_enabled = true;
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 = 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;
dev->pfd = NULL;
nxmutex_init(&dev->dev_lock);
if (!dev->path)
{
wlerr("Failed to allocate driver path.\n");
goto errout;
}
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_IEEE80211);
if (ret < 0)
{
unregister_driver(devpath);
goto errout;
}
dev->net_dev.d_pktsize = MAX_PKT_LEN;
dev->net_dev.d_llhdrlen = 0;
return dev;
errout:
nxmutex_destroy(&dev->dev_lock);
lib_free(dev->path);
kmm_free(dev);
return NULL;
}