/**************************************************************************** * drivers/wireless/gs2200m.c * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * 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 (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 /* for 16 sockets and disasso event + dummy */ #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 #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; /* 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; 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 ****************************************************************************/ /* Character driver methods */ 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); static void _remove_all_pkt(FAR struct gs2200m_dev_s *dev, uint8_t c); /**************************************************************************** * Private Data ****************************************************************************/ /* This the vtable that supports the character driver interface. */ static const struct file_operations g_gs2200m_fops = { NULL, /* open */ NULL, /* close */ gs2200m_read, /* read */ gs2200m_write, /* write */ NULL, /* seek */ gs2200m_ioctl, /* ioctl */ NULL, /* truncate */ NULL, /* mmap */ gs2200m_poll /* 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; 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 if (c == DISASSOCIATION_CID) { ret = 16; } else { ret = 0xff; 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, 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; /* 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(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); /* 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 */ 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--; /* 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(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) { /* Update total bulk data size */ 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); } /* NOTE: force to disable the cid to remove */ _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; /* TODO: should enable again if disabled for long time */ 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; /* 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(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])); /* 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 || TYPE_BULK_DATA_UDP == pkt_dat->type) { _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)); /* Set the address family * NOTE: gs2200m only supports IPv4 */ msg->addr.sin_family = AF_INET; /* In udp case, treat the packet separately */ 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; DEBUGASSERT(filep); inode = filep->f_inode; DEBUGASSERT(inode && inode->i_private); dev = (FAR struct gs2200m_dev_s *)inode->i_private; ASSERT(1 == len); ret = nxmutex_lock(&dev->dev_lock); 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)); 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; /* 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.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 */ _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, 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 */ _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 or 0x0, retry */ 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 */ _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) { 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"); return SPI_TIMEOUT; } /* 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); 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 = (FAR 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
*/ 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("** Z\n"); /* NOTE: See 7.5.3.2 Bulk Data Handling */ pkt_ctx->state = PKT_BULK_DATA_TCP; } else if ('y' == c) { wlinfo("** y\n"); /* NOTE: See 7.5.3.2 Bulk Data Handling */ pkt_ctx->state = PKT_BULK_DATA_UDP; } else if ('F' == c) { wlwarn("** F\n"); /* NOTE: See Table 6 Data Handling Responses at Completion */ pkt_ctx->state = PKT_START; pkt_ctx->type = TYPE_FAIL; } else { wlerr("** %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: */ /* 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((FAR 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) { /*
*/ /* 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((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)); /* Skip until data length */ for (; '\t' != (char)*(pkt_ctx->ptr); pkt_ctx->ptr++); pkt_ctx->ptr++; /* Read data length */ pkt_ctx->dlen = _to_uint16((FAR 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: _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; /* Only bulk data */ ASSERT(pkt_dat0->data && (0 == pkt_dat0->n)); /* Allocate a new pkt_dat */ pkt_dat = (FAR struct pkt_dat_s *)kmm_malloc(sizeof(struct pkt_dat_s)); ASSERT(pkt_dat); /* Copy pkt_dat0 to pkt_dat */ memcpy(pkt_dat, pkt_dat0, sizeof(struct pkt_dat_s)); /* Allocate bulk data and copy */ pkt_dat->data = (FAR uint8_t *)kmm_malloc(pkt_dat0->len); ASSERT(pkt_dat->data); memcpy(pkt_dat->data, pkt_dat0->data, pkt_dat0->len); /* Convert cid to c */ 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]++; /* NOTE: total_bulk must be updated * 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 = (FAR 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 (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) { /* Update total bulk data size */ 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; /* Disable gs2200m irq to poll dready */ 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"); /* Bulk data found in the response, * duplicate the packet and notify */ _dup_pkt_dat_and_notify(dev, pkt_dat); /* release & initialize pkt_dat before retry */ _release_pkt_dat(dev, pkt_dat); memset(pkt_dat, 0, sizeof(*pkt_dat)); bulk = true; goto retry_recv; } /* NOTE: retry in case of errors */ if ((TYPE_OK != r) && (0 <= --n)) { if (pkt_dat) { /* release & initialize pkt_dat before retry */ _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); } /* Enable gs2200m irq again */ 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; /* Initialize pkt_dat and send */ memset(&pkt_dat, 0, sizeof(pkt_dat)); r = gs2200m_send_cmd(dev, cmd, &pkt_dat); /* Release the 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; /* 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], "%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; /* 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(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 /* CONFIG_WL_GS2200M_ENABLE_WEP */ /**************************************************************************** * 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, "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'; /* Invalidate cid */ 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 { 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) { 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; /* 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); 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; } /* 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 * Z */ 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)); /* NOTE: See 7.5.3.2 Bulk Data Handling for UDP * Y:: */ 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); /* Send the bulk data */ s = gs2200m_hal_write(dev, (FAR 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) { 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[8]; 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"); /* 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 <= 2) { wlerr("+++ error: r=%d pkt_dat.msg[0]=%s\n", r, pkt_dat.msg[0]); goto errout; } /* Find cid in the connection status */ 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) { /* Set family, port and address (remote only) */ 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; } } /* Not found */ 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); /* Start TCP/UDP server and retrieve cid */ 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 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, &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: 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; 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) { /* NOTE: need to assign port automatically */ 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]) { /* 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)); /* Do packet flow control */ _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 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); } /* Obtain remote address info */ 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; /* Remember assoc request msg for reconnection */ memcpy(&dev->reconnect_msg, msg, sizeof(struct gs2200m_assoc_msg)); /* 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; } dev->disassociate_flag = false; /* Sync lost time interval */ 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; /* 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); #ifdef CONFIG_WL_GS2200M_ENABLE_WEP /* Set security mode (WEP) */ t = gs2200m_set_security(dev, SEC_MODE_WEP); 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; } #else /* Set security mode (WPA2-PSK) */ t = gs2200m_set_security(dev, SEC_MODE_WPA2PSK); ASSERT(TYPE_OK == t); /* Set WPA-PSK and WPA2-PSK Passphrase */ if (TYPE_OK != gs2200m_set_wpa2pf(dev, msg->key)) { wlerr("*** error: invalid passphrase: %s\n", msg->key); return -1; } #endif /* 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_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"); /* 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 <= 7) { wlerr("+++ error: r=%d pkt_dat.msg[0]=%s\n", r, pkt_dat.msg[0]); goto errout; } /* Find cid in the connection status */ 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; /* Obtain connection status */ r = gs2200m_get_cstatus(dev, msg); if (r != TYPE_OK) { return -EINVAL; } if (msg->local) { /* Copy local address from net_dev */ 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; DEBUGASSERT(filep); inode = filep->f_inode; DEBUGASSERT(inode && inode->i_private); dev = (FAR struct gs2200m_dev_s *)inode->i_private; /* Lock the device */ ret = nxmutex_lock(&dev->dev_lock); 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; } case GS2200M_IOC_NAME: { struct gs2200m_name_msg *msg = (struct gs2200m_name_msg *)arg; ret = gs2200m_ioctl_name(dev, msg); break; } default: DEBUGPANIC(); break; } /* Enable gs2200m irq again */ dev->lower->enable(); /* Unlock the device */ 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(filep && fds); inode = filep->f_inode; DEBUGASSERT(inode && inode->i_private); dev = (FAR struct gs2200m_dev_s *)inode->i_private; ret = nxmutex_lock(&dev->dev_lock); 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) { poll_notify(&dev->pfd, 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); /* The only failure would be if the worker thread were canceled. That * 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); /* 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 (true == dev->disassociate_flag) { /* Disassociate recovery */ wlwarn("=== receive DISASSOCIATE\n"); dev->valid_cid_bits = 0; do { /* Discard incoming packets until timeout happens */ 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) { /* An error event? */ wlerr("=== 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); } /* Do packet flow control */ 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; } /* NOTE: Enable gs2200m irq which was disabled in gs2200m_irq() */ 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; } /* NOTE: Disable gs2200m irq during processing */ 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; /* 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); /* Power save disable */ t = gs2200m_powersave_wrx(dev, 0); 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); dev->int_enabled = true; /* 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; dev->pfd = NULL; nxmutex_init(&dev->dev_lock); 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; } return dev; errout: nxmutex_destroy(&dev->dev_lock); kmm_free(dev); return NULL; }