summaryrefslogtreecommitdiff
path: root/components/drivers/spi/spi_wifi_rw009.c
diff options
context:
space:
mode:
Diffstat (limited to 'components/drivers/spi/spi_wifi_rw009.c')
-rw-r--r--components/drivers/spi/spi_wifi_rw009.c590
1 files changed, 590 insertions, 0 deletions
diff --git a/components/drivers/spi/spi_wifi_rw009.c b/components/drivers/spi/spi_wifi_rw009.c
new file mode 100644
index 0000000000..f45d493717
--- /dev/null
+++ b/components/drivers/spi/spi_wifi_rw009.c
@@ -0,0 +1,590 @@
+/*
+ * File : spi_wifi_rw009.c
+ * This file is part of RT-Thread RTOS
+ * Copyright by Shanghai Real-Thread Electronic Technology Co.,Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2014-07-31 aozima the first version
+ */
+
+#include <rtthread.h>
+#include <drivers/spi.h>
+
+#include <netif/ethernetif.h>
+#include <netif/etharp.h>
+#include <lwip/icmp.h>
+#include "lwipopts.h"
+
+#include "spi_wifi_rw009.h"
+
+#define SSID_NAME "AP_SSID"
+#define SSID_PASSWORD "AP_passwd"
+
+//#define WIFI_DEBUG_ON
+// #define ETH_RX_DUMP
+// #define ETH_TX_DUMP
+
+#ifdef WIFI_DEBUG_ON
+#define WIFI_DEBUG rt_kprintf("[WIFI] ");rt_kprintf
+#else
+#define WIFI_DEBUG(...)
+#endif /* #ifdef WIFI_DEBUG_ON */
+
+#define MAX_BUFFER_SIZE (sizeof(struct response) + MAX_DATA_LEN)
+#define MAX_ADDR_LEN 6
+struct spi_wifi_eth
+{
+ /* inherit from ethernet device */
+ struct eth_device parent;
+
+ struct rt_spi_device *rt_spi_device;
+
+ /* interface address info. */
+ rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */
+ rt_uint8_t active;
+
+ struct rt_mempool spi_tx_mp;
+ struct rt_mempool spi_rx_mp;
+
+ struct rt_mailbox spi_tx_mb;
+ struct rt_mailbox eth_rx_mb;
+
+ int spi_tx_mb_pool[SPI_TX_POOL_SIZE];
+ int eth_rx_mb_pool[SPI_TX_POOL_SIZE];
+
+ int spi_wifi_cmd_mb_pool[3];
+ struct rt_mailbox spi_wifi_cmd_mb;
+
+ ALIGN(4)
+ rt_uint8_t spi_tx_mempool[(sizeof(struct spi_data_packet) + 4) * SPI_TX_POOL_SIZE];
+ ALIGN(4)
+ rt_uint8_t spi_rx_mempool[(sizeof(struct spi_data_packet) + 4) * SPI_TX_POOL_SIZE];
+
+ ALIGN(4)
+ uint8_t spi_hw_rx_buffer[MAX_BUFFER_SIZE];
+};
+static struct spi_wifi_eth spi_wifi_device;
+static struct rt_event spi_wifi_data_event;
+
+static void resp_handler(struct spi_wifi_eth *wifi_device, struct spi_wifi_resp *resp)
+{
+ struct spi_wifi_resp *resp_return;
+
+ switch (resp->cmd)
+ {
+ case SPI_WIFI_CMD_INIT:
+ WIFI_DEBUG("resp_handler SPI_WIFI_CMD_INIT\n");
+ resp_return = (struct spi_wifi_resp *)rt_malloc(sizeof(struct spi_wifi_resp)); //TODO:
+ memcpy(resp_return, resp, 10);
+ rt_mb_send(&wifi_device->spi_wifi_cmd_mb, (rt_uint32_t)resp_return);
+ break;
+
+ case SPI_WIFI_CMD_SCAN:
+ WIFI_DEBUG("resp_handler SPI_WIFI_CMD_SCAN\n");
+ break;
+
+ case SPI_WIFI_CMD_JOIN:
+ WIFI_DEBUG("resp_handler SPI_WIFI_CMD_JOIN\n");
+ wifi_device->active = 1;
+ eth_device_linkchange(&wifi_device->parent, RT_TRUE);
+ break;
+
+ default:
+ WIFI_DEBUG("resp_handler %d\n", resp->cmd);
+ break;
+ }
+
+}
+
+static rt_err_t spi_wifi_transfer(struct spi_wifi_eth *dev)
+{
+ struct pbuf *p = RT_NULL;
+ struct cmd_request cmd;
+ struct response resp;
+
+ rt_err_t result;
+ const struct spi_data_packet *data_packet = RT_NULL;
+
+ struct spi_wifi_eth *wifi_device = (struct spi_wifi_eth *)dev;
+ struct rt_spi_device *rt_spi_device = wifi_device->rt_spi_device;
+
+ spi_wifi_int_cmd(0);
+ while (spi_wifi_is_busy());
+ WIFI_DEBUG("sequence start!\n");
+
+ memset(&cmd, 0, sizeof(struct cmd_request));
+ cmd.magic1 = CMD_MAGIC1;
+ cmd.magic2 = CMD_MAGIC2;
+
+ cmd.flag |= CMD_FLAG_MRDY;
+
+ result = rt_mb_recv(&wifi_device->spi_tx_mb,
+ (rt_uint32_t *)&data_packet,
+ 0);
+ if ((result == RT_EOK) && (data_packet != RT_NULL) && (data_packet->data_len > 0))
+ {
+ cmd.M2S_len = data_packet->data_len + member_offset(struct spi_data_packet, buffer);
+ //WIFI_DEBUG("cmd.M2S_len = %d\n", cmd.M2S_len);
+ }
+
+ rt_spi_send(rt_spi_device, &cmd, sizeof(cmd));
+ while (spi_wifi_is_busy());
+
+ {
+ struct rt_spi_message message;
+ uint32_t max_data_len = 0;
+
+ /* setup message */
+ message.send_buf = RT_NULL;
+ message.recv_buf = &resp;
+ message.length = sizeof(resp);
+ message.cs_take = 1;
+ message.cs_release = 0;
+
+ rt_spi_take_bus(rt_spi_device);
+
+ /* transfer message */
+ rt_spi_device->bus->ops->xfer(rt_spi_device, &message);
+
+ if ((resp.magic1 != RESP_MAGIC1) || (resp.magic2 != RESP_MAGIC2))
+ {
+ WIFI_DEBUG("bad resp magic, abort!\n");
+ goto _bad_resp_magic;
+ }
+
+ if (resp.flag & RESP_FLAG_SRDY)
+ {
+ WIFI_DEBUG("RESP_FLAG_SRDY\n");
+ max_data_len = cmd.M2S_len;
+ }
+
+ if (resp.S2M_len)
+ {
+ WIFI_DEBUG("resp.S2M_len: %d\n", resp.S2M_len);
+ if (resp.S2M_len > sizeof(struct spi_data_packet))
+ {
+ WIFI_DEBUG("resp.S2M_len > sizeof(struct spi_data_packet), drop!\n");
+ resp.S2M_len = 0;//drop
+ }
+
+ if (resp.S2M_len > max_data_len)
+ max_data_len = resp.S2M_len;
+ }
+
+ if (max_data_len == 0)
+ {
+ WIFI_DEBUG("no rx or tx data!\n");
+ }
+
+ //WIFI_DEBUG("max_data_len = %d\n", max_data_len);
+
+_bad_resp_magic:
+ /* setup message */
+ message.send_buf = data_packet;//&tx_buffer;
+ message.recv_buf = wifi_device->spi_hw_rx_buffer;//&rx_buffer;
+ message.length = max_data_len;
+ message.cs_take = 0;
+ message.cs_release = 1;
+
+ /* transfer message */
+ rt_spi_device->bus->ops->xfer(rt_spi_device, &message);
+
+ rt_spi_release_bus(rt_spi_device);
+
+ if (cmd.M2S_len && (resp.flag & RESP_FLAG_SRDY))
+ {
+ rt_mp_free((void *)data_packet);
+ }
+
+ if ((resp.S2M_len) && (resp.S2M_len <= MAX_DATA_LEN))
+ {
+ data_packet = (struct spi_data_packet *)wifi_device->spi_hw_rx_buffer;
+ if (data_packet->data_type == data_type_eth_data)
+ {
+
+ if (wifi_device->active)
+ {
+ p = pbuf_alloc(PBUF_LINK, data_packet->data_len, PBUF_RAM);
+ pbuf_take(p, (rt_uint8_t *)data_packet->buffer, data_packet->data_len);
+
+ rt_mb_send(&wifi_device->eth_rx_mb, (rt_uint32_t)p);
+ eth_device_ready((struct eth_device *)dev);
+ }
+ else
+ {
+ WIFI_DEBUG("!active, RX drop.\n");
+ }
+ }
+ else if (data_packet->data_type == data_type_resp)
+ {
+ WIFI_DEBUG("data_type_resp\n");
+ resp_handler(dev, (struct spi_wifi_resp *)data_packet->buffer);
+ }
+ else
+ {
+ WIFI_DEBUG("data_type: %d, %dbyte\n",
+ data_packet->data_type,
+ data_packet->data_len);
+ }
+ }
+ }
+ spi_wifi_int_cmd(1);
+
+ WIFI_DEBUG("sequence finish!\n\n");
+
+ if ((cmd.M2S_len == 0) && (resp.S2M_len == 0))
+ {
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+
+#if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP)
+static void packet_dump(const char *msg, const struct pbuf *p)
+{
+ rt_uint32_t i;
+ rt_uint8_t *ptr = p->payload;
+
+ rt_kprintf("%s %d byte\n", msg, p->tot_len);
+
+ for (i = 0; i < p->tot_len; i++)
+ {
+ if ((i % 8) == 0)
+ {
+ rt_kprintf(" ");
+ }
+ if ((i % 16) == 0)
+ {
+ rt_kprintf("\r\n");
+ }
+ rt_kprintf("%02x ", *ptr);
+ ptr++;
+ }
+ rt_kprintf("\n\n");
+}
+#endif /* dump */
+
+/* initialize the interface */
+static rt_err_t spi_wifi_eth_init(rt_device_t dev)
+{
+ return RT_EOK;
+}
+
+static rt_err_t spi_wifi_eth_open(rt_device_t dev, rt_uint16_t oflag)
+{
+ return RT_EOK;
+}
+
+static rt_err_t spi_wifi_eth_close(rt_device_t dev)
+{
+ return RT_EOK;
+}
+
+static rt_size_t spi_wifi_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ rt_set_errno(-RT_ENOSYS);
+ return 0;
+}
+
+static rt_size_t spi_wifi_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ rt_set_errno(-RT_ENOSYS);
+ return 0;
+}
+
+
+static rt_err_t spi_wifi_eth_control(rt_device_t dev, rt_uint8_t cmd, void *args)
+{
+ struct spi_wifi_eth *wifi_device = (struct spi_wifi_eth *)dev;
+ struct spi_data_packet *data_packet;
+ struct spi_wifi_cmd *wifi_cmd;
+ struct spi_wifi_resp *resp;
+
+ switch (cmd)
+ {
+ case NIOCTL_GADDR:
+ memcpy(args, wifi_device->dev_addr, 6);
+ break;
+
+ case SPI_WIFI_CMD_INIT:
+ /* get mac address */
+ if (args)
+ {
+ rt_err_t result;
+
+ data_packet = (struct spi_data_packet *)rt_mp_alloc(&wifi_device->spi_tx_mp, RT_WAITING_FOREVER);
+ // TODO: check result.
+
+ wifi_cmd = (struct spi_wifi_cmd *)data_packet->buffer;
+ wifi_cmd->cmd = SPI_WIFI_CMD_INIT;
+
+ data_packet->data_type = data_type_cmd;
+ data_packet->data_len = member_offset(struct spi_wifi_cmd, buffer) + 0;
+
+ rt_mb_send(&wifi_device->spi_tx_mb, (rt_uint32_t)data_packet);
+ rt_event_send(&spi_wifi_data_event, 1);
+
+ result = rt_mb_recv(&wifi_device->spi_wifi_cmd_mb,
+ (rt_uint32_t *)&resp,
+ RT_WAITING_FOREVER);
+
+ if ((result == RT_EOK) && (resp != RT_NULL))
+ {
+ WIFI_DEBUG("resp cmd: %d\n", resp->cmd);
+
+ rt_memcpy(args, resp->buffer, 6);
+ }
+ }
+ else return -RT_ERROR;
+ break;
+
+
+ case SPI_WIFI_CMD_SCAN:
+
+ case SPI_WIFI_CMD_JOIN:
+ if (args)
+ {
+ struct cmd_join *cmd_join;
+
+ data_packet = (struct spi_data_packet *)rt_mp_alloc(&wifi_device->spi_tx_mp, RT_WAITING_FOREVER);
+
+ wifi_cmd = (struct spi_wifi_cmd *)data_packet->buffer;
+ wifi_cmd->cmd = SPI_WIFI_CMD_JOIN;
+ cmd_join = (struct cmd_join *)wifi_cmd->buffer;
+
+#define WPA_SECURITY 0x00200000
+#define WPA2_SECURITY 0x00400000
+
+#define TKIP_ENABLED 0x0002
+#define AES_ENABLED 0x0004
+
+
+ strncpy(cmd_join->ssid, SSID_NAME, SSID_NAME_LENGTH_MAX);
+ strncpy(cmd_join->passwd, SSID_PASSWORD, PASSWORD_LENGTH_MAX);
+ cmd_join->security = WPA2_SECURITY | TKIP_ENABLED | AES_ENABLED;
+ // cmd_join->security = WPA_SECURITY | TKIP_ENABLED;
+ data_packet->data_type = data_type_cmd;
+ data_packet->data_len = sizeof(struct cmd_join) + member_offset(struct spi_wifi_cmd, buffer);
+
+ rt_mb_send(&wifi_device->spi_tx_mb, (rt_uint32_t)data_packet);
+ rt_event_send(&spi_wifi_data_event, 1);
+ }
+ else return -RT_ERROR;
+ break;
+
+ default :
+ break;
+ }
+
+ return RT_EOK;
+}
+
+/* transmit packet. */
+rt_err_t spi_wifi_eth_tx(rt_device_t dev, struct pbuf *p)
+{
+ rt_err_t result = RT_EOK;
+ struct spi_data_packet *data_packet;
+ struct spi_wifi_eth *wifi_device = (struct spi_wifi_eth *)dev;
+
+ if (!wifi_device->active)
+ {
+ WIFI_DEBUG("!active, TX drop!\n");
+ return RT_EOK;
+ }
+
+ /* get free tx buffer */
+ data_packet = (struct spi_data_packet *)rt_mp_alloc(&wifi_device->spi_tx_mp, RT_WAITING_FOREVER);
+ if (data_packet != RT_NULL)
+ {
+ data_packet->data_type = data_type_eth_data;
+ data_packet->data_len = p->tot_len;
+
+ pbuf_copy_partial(p, data_packet->buffer, data_packet->data_len, 0);
+
+ rt_mb_send(&wifi_device->spi_tx_mb, (rt_uint32_t)data_packet);
+ eth_device_ready((struct eth_device *)dev);
+ }
+ else
+ return -RT_ERROR;
+
+#ifdef ETH_TX_DUMP
+ packet_dump("TX dump", p);
+#endif /* ETH_TX_DUMP */
+
+ /* Return SUCCESS */
+ return result;
+}
+
+/* reception packet. */
+struct pbuf *spi_wifi_eth_rx(rt_device_t dev)
+{
+ struct pbuf *p = RT_NULL;
+ struct spi_wifi_eth *wifi_device = (struct spi_wifi_eth *)dev;
+
+ if (rt_mb_recv(&wifi_device->eth_rx_mb, (rt_uint32_t *)&p, 0) != RT_EOK)
+ {
+ return RT_NULL;
+ }
+
+
+
+ return p;
+}
+
+static void spi_wifi_data_thread_entry(void *parameter)
+{
+ rt_uint32_t e;
+ rt_err_t result;
+
+ while (1)
+ {
+ /* receive first event */
+ if (rt_event_recv(&spi_wifi_data_event,
+ 1,
+ RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_FOREVER,
+ &e) != RT_EOK)
+ {
+ continue;
+ }
+
+ result = spi_wifi_transfer(&spi_wifi_device);
+
+ if (result == RT_EOK)
+ {
+ rt_event_send(&spi_wifi_data_event, 1);
+ }
+ }
+}
+
+rt_err_t rt_hw_wifi_init(const char *spi_device_name)
+{
+ memset(&spi_wifi_device, 0, sizeof(struct spi_wifi_eth));
+
+ spi_wifi_device.rt_spi_device = (struct rt_spi_device *)rt_device_find(spi_device_name);
+
+ if (spi_wifi_device.rt_spi_device == RT_NULL)
+ {
+ WIFI_DEBUG("spi device %s not found!\r\n", spi_device_name);
+ return -RT_ENOSYS;
+ }
+
+ /* config spi */
+ {
+ struct rt_spi_configuration cfg;
+ cfg.data_width = 8;
+ cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0 and Mode 3 */
+ cfg.max_hz = 1000000; /* 50M */
+ rt_spi_configure(spi_wifi_device.rt_spi_device, &cfg);
+ }
+
+ spi_wifi_device.parent.parent.init = spi_wifi_eth_init;
+ spi_wifi_device.parent.parent.open = spi_wifi_eth_open;
+ spi_wifi_device.parent.parent.close = spi_wifi_eth_close;
+ spi_wifi_device.parent.parent.read = spi_wifi_eth_read;
+ spi_wifi_device.parent.parent.write = spi_wifi_eth_write;
+ spi_wifi_device.parent.parent.control = spi_wifi_eth_control;
+ spi_wifi_device.parent.parent.user_data = RT_NULL;
+
+ spi_wifi_device.parent.eth_rx = spi_wifi_eth_rx;
+ spi_wifi_device.parent.eth_tx = spi_wifi_eth_tx;
+
+ rt_mp_init(&spi_wifi_device.spi_tx_mp,
+ "spi_tx",
+ &spi_wifi_device.spi_tx_mempool[0],
+ sizeof(spi_wifi_device.spi_tx_mempool),
+ sizeof(struct spi_data_packet));
+
+ rt_mp_init(&spi_wifi_device.spi_rx_mp,
+ "spi_rx",
+ &spi_wifi_device.spi_rx_mempool[0],
+ sizeof(spi_wifi_device.spi_rx_mempool),
+ sizeof(struct spi_data_packet));
+
+ rt_mb_init(&spi_wifi_device.spi_tx_mb,
+ "spi_tx",
+ &spi_wifi_device.spi_tx_mb_pool[0],
+ SPI_TX_POOL_SIZE,
+ RT_IPC_FLAG_PRIO);
+
+ rt_mb_init(&spi_wifi_device.eth_rx_mb,
+ "eth_rx",
+ &spi_wifi_device.eth_rx_mb_pool[0],
+ SPI_TX_POOL_SIZE,
+ RT_IPC_FLAG_PRIO);
+
+ rt_mb_init(&spi_wifi_device.spi_wifi_cmd_mb,
+ "wifi_cmd",
+ &spi_wifi_device.spi_wifi_cmd_mb_pool[0],
+ sizeof(spi_wifi_device.spi_wifi_cmd_mb_pool) / 4,
+ RT_IPC_FLAG_PRIO);
+ rt_event_init(&spi_wifi_data_event, "wifi", RT_IPC_FLAG_FIFO);
+
+ spi_wifi_hw_init();
+
+ {
+ rt_thread_t tid;
+
+
+ tid = rt_thread_create("wifi",
+ spi_wifi_data_thread_entry,
+ RT_NULL,
+ 2048,
+ RT_THREAD_PRIORITY_MAX - 2,
+ 20);
+
+ if (tid != RT_NULL)
+ rt_thread_startup(tid);
+ }
+
+ /* init: get mac address */
+ {
+ WIFI_DEBUG("wifi_control SPI_WIFI_CMD_INIT\n");
+ spi_wifi_eth_control((rt_device_t)&spi_wifi_device,
+ SPI_WIFI_CMD_INIT,
+ (void *)&spi_wifi_device.dev_addr[0]);
+
+ }
+ /* register eth device */
+ eth_device_init(&(spi_wifi_device.parent), "w0");
+ eth_device_linkchange(&spi_wifi_device.parent, RT_FALSE);
+
+ {
+ WIFI_DEBUG("wifi_control SPI_WIFI_CMD_JOIN\n");
+ spi_wifi_eth_control((rt_device_t)&spi_wifi_device,
+ SPI_WIFI_CMD_JOIN,
+ (void *)&spi_wifi_device.dev_addr[0]);
+
+ WIFI_DEBUG("wifi_control exit\n");
+ }
+
+ return RT_EOK;
+}
+
+void spi_wifi_isr(int vector)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ WIFI_DEBUG("spi_wifi_isr\n");
+ rt_event_send(&spi_wifi_data_event, 1);
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}