From 660a8529351125b1a9ca191165d382f9c4a840c5 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 26 Jun 2020 10:30:43 +0200 Subject: [PATCH] drivers: add netdev driver for ATWINC15x0 WiFi module --- drivers/Makefile.dep | 10 + drivers/Makefile.include | 4 + drivers/atwinc15x0/Makefile | 1 + drivers/atwinc15x0/atwinc15x0_bsp.c | 98 ++++ drivers/atwinc15x0/atwinc15x0_bus.c | 88 +++ drivers/atwinc15x0/atwinc15x0_netdev.c | 524 ++++++++++++++++++ drivers/atwinc15x0/doc.txt | 174 ++++++ .../atwinc15x0/include/atwinc15x0_internal.h | 47 ++ .../atwinc15x0/include/atwinc15x0_params.h | 101 ++++ .../include/bsp/include/nm_bsp_internal.h | 50 ++ drivers/include/atwinc15x0.h | 80 +++ makefiles/pseudomodules.inc.mk | 1 + 12 files changed, 1178 insertions(+) create mode 100644 drivers/atwinc15x0/Makefile create mode 100644 drivers/atwinc15x0/atwinc15x0_bsp.c create mode 100644 drivers/atwinc15x0/atwinc15x0_bus.c create mode 100644 drivers/atwinc15x0/atwinc15x0_netdev.c create mode 100644 drivers/atwinc15x0/doc.txt create mode 100644 drivers/atwinc15x0/include/atwinc15x0_internal.h create mode 100644 drivers/atwinc15x0/include/atwinc15x0_params.h create mode 100644 drivers/atwinc15x0/include/bsp/include/nm_bsp_internal.h create mode 100644 drivers/include/atwinc15x0.h diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index bf8b4bc5ad..52fddbd05c 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -118,6 +118,16 @@ ifneq (,$(filter ata8520e,$(USEMODULE))) FEATURES_REQUIRED += periph_spi endif +ifneq (,$(filter atwinc15x0,$(USEMODULE))) + USEMODULE += luid + USEMODULE += netdev_eth + USEMODULE += xtimer + USEPKG += driver_atwinc15x0 + FEATURES_REQUIRED += periph_gpio + FEATURES_REQUIRED += periph_gpio_irq + FEATURES_REQUIRED += periph_spi +endif + ifneq (,$(filter bh1750fvi,$(USEMODULE))) USEMODULE += xtimer FEATURES_REQUIRED += periph_i2c diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 017bd65d67..7984bb3261 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -48,6 +48,10 @@ ifneq (,$(filter ata8520e,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ata8520e/include endif +ifneq (,$(filter atwinc15x0,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/atwinc15x0/include +endif + ifneq (,$(filter bh1750fvi,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/bh1750fvi/include endif diff --git a/drivers/atwinc15x0/Makefile b/drivers/atwinc15x0/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/atwinc15x0/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/atwinc15x0/atwinc15x0_bsp.c b/drivers/atwinc15x0/atwinc15x0_bsp.c new file mode 100644 index 0000000000..d3d13f1f1c --- /dev/null +++ b/drivers/atwinc15x0/atwinc15x0_bsp.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_atwinc15x0 + * @{ + * + * @file + * @brief RIOT BSP API implementation + * + * @author Gunar Schorcht + * + * @} + */ + +#include "atwinc15x0_internal.h" +#include "mutex.h" +#include "periph/spi.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +void atwinc15x0_isr(void *arg) +{ + (void)arg; + if (atwinc15x0->bsp_isr != NULL && atwinc15x0->bsp_irq_enabled) { + atwinc15x0->bsp_isr(); + } + atwinc15x0_irq(); +} + +sint8 nm_bsp_init(void) +{ + assert(atwinc15x0); + assert(atwinc15x0->params.reset_pin != GPIO_UNDEF); + assert(atwinc15x0->params.irq_pin != GPIO_UNDEF); + + gpio_init(atwinc15x0->params.reset_pin, GPIO_OUT); + gpio_set(atwinc15x0->params.reset_pin); + + gpio_init_int(atwinc15x0->params.irq_pin, GPIO_IN_PU, GPIO_FALLING, + atwinc15x0_isr, NULL); + + if (atwinc15x0->params.chip_en_pin != GPIO_UNDEF) { + gpio_init(atwinc15x0->params.chip_en_pin, GPIO_OUT); + gpio_set(atwinc15x0->params.chip_en_pin); + } + + if (atwinc15x0->params.wake_pin != GPIO_UNDEF) { + gpio_init(atwinc15x0->params.wake_pin, GPIO_OUT); + gpio_set(atwinc15x0->params.wake_pin); + } + + return 0; +} + +sint8 nm_bsp_deinit(void) +{ + return 0; +} + +void nm_bsp_reset(void) +{ + assert(atwinc15x0); + gpio_clear(atwinc15x0->params.reset_pin); + nm_bsp_sleep(100); + gpio_set(atwinc15x0->params.reset_pin); + nm_bsp_sleep(100); +} + +void nm_bsp_sleep(uint32 u32TimeMsec) +{ + xtimer_usleep(u32TimeMsec * US_PER_MS); +} + +void nm_bsp_register_isr(tpfNmBspIsr pfIsr) +{ + assert(atwinc15x0); + + DEBUG("%s %p\n", __func__, pfIsr); + + atwinc15x0->bsp_isr = pfIsr; +} + +void nm_bsp_interrupt_ctrl(uint8 u8Enable) +{ + assert(atwinc15x0); + + DEBUG("%s %u\n", __func__, u8Enable); + + atwinc15x0->bsp_irq_enabled = u8Enable; +} diff --git a/drivers/atwinc15x0/atwinc15x0_bus.c b/drivers/atwinc15x0/atwinc15x0_bus.c new file mode 100644 index 0000000000..947ffc05c3 --- /dev/null +++ b/drivers/atwinc15x0/atwinc15x0_bus.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_atwinc15x0 + * @{ + * + * @file + * @brief RIOT bus wrapper API implementation + * + * @author Gunar Schorcht + * + * @} + */ + +#include "atwinc15x0_internal.h" +#include "bus_wrapper/include/nm_bus_wrapper.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define NM_BUS_MAX_TRX_SZ 256 + +tstrNmBusCapabilities egstrNmBusCapabilities = +{ + NM_BUS_MAX_TRX_SZ +}; + +sint8 nm_bus_init(void *arg) +{ + (void)arg; + + assert(atwinc15x0); + assert(atwinc15x0->params.ssn_pin != GPIO_UNDEF); + + gpio_init(atwinc15x0->params.ssn_pin, GPIO_OUT); + gpio_set(atwinc15x0->params.ssn_pin); + + nm_bsp_reset(); + nm_bsp_sleep(1); + + return 0; +} + +sint8 nm_bus_ioctl(uint8 cmd, void* params) +{ + assert(atwinc15x0); + + sint8 res = 0; + tstrNmSpiRw *spi_params = (tstrNmSpiRw *)params; + + switch (cmd) + { + case NM_BUS_IOCTL_RW: + spi_acquire(atwinc15x0->params.spi, atwinc15x0->params.ssn_pin, + SPI_MODE_0, atwinc15x0->params.spi_clk); + spi_transfer_bytes(atwinc15x0->params.spi, + atwinc15x0->params.ssn_pin, 0, + spi_params->pu8InBuf, + spi_params->pu8OutBuf, + spi_params->u16Sz); + spi_release(atwinc15x0->params.spi); + break; + + default: + res = M2M_ERR_BUS_FAIL; + DEBUG("invalid ioctl cmd\n"); + break; + } + + return res; +} + +sint8 nm_bus_deinit(void) +{ + return 0; +} + +sint8 nm_bus_reinit(void *arg) +{ + (void)arg; + return 0; +} diff --git a/drivers/atwinc15x0/atwinc15x0_netdev.c b/drivers/atwinc15x0/atwinc15x0_netdev.c new file mode 100644 index 0000000000..a541575d59 --- /dev/null +++ b/drivers/atwinc15x0/atwinc15x0_netdev.c @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_atwinc15x0 + * @{ + * + * @file + * @brief Netdev driver for the ATWINC15x0 WiFi module + * + * @author Gunar Schorcht + * + * @} + */ + +#include + +#define ETH_MODE (1) + +#include "atwinc15x0_internal.h" +#include "atwinc15x0_params.h" + +#include "bus_wrapper/include/nm_bus_wrapper.h" +#include "driver/source/m2m_hif.h" +#include "driver/include/m2m_wifi.h" + +#include "assert.h" +#include "log.h" +#include "net/netdev/eth.h" +#include "od.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (0) +#define ENABLE_DEBUG_DUMP (0) +#include "debug.h" + +#define ATWINC15X0_MAC_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define ATWINC15X0_MAC_STR_ARG(m) m[0], m[1], m[2], m[3], m[4], m[5] + +#define ATWINC15X0_WAIT_TIME (1 * US_PER_MS) +#define ATWINC15X0_WAIT_TIMEOUT (20) +#define ATWINC15X0_WAIT_RECONNECT (5 * US_PER_SEC) + +/* Forward function declarations */ +static void _atwinc15x0_wifi_cb(uint8_t event, void *msg); +static void _atwinc15x0_eth_cb(uint8_t type, void *msg, void *ctrl); +static int _atwinc15x0_connect(void); + +/** + * The following buffer is required by the ATWINC15x0 vendor driver to store + * packets received from the ATWINC15x0 WiFI module in it. Its size has to be + * at least one Ethernet frame of maximum length. + * + * The event-driven handling of incoming packets is strictly sequential in + * the context of the `netif` thread. This means that an incoming packet + * is first received by the `netif` thread and copied to its packet buffer + * before the next event of an incoming packet is handled by the ATWINC15x0 + * vendor driver. It can therefore be assumed that only one received packet + * can be in the buffer at a time. No further separate intermediate buffer + * is required. + * + * Furthermore, this buffer can be used for preparing a packet to be sent, + * since it can be assumed that receiving and sending packets are implicitly + * mutually exclusive due to their strictly sequential processing. + */ +static uint8_t atwinc15x0_eth_buf[ETHERNET_MAX_LEN]; + +/* ATWINC15x0 vendor driver initialization structure (can't be const) */ +static tstrWifiInitParam atwinc15x0_wifi_params = { + .pfAppWifiCb = _atwinc15x0_wifi_cb, + .strEthInitParam = { + .pfAppWifiCb = _atwinc15x0_wifi_cb, + .pfAppEthCb = _atwinc15x0_eth_cb, + .au8ethRcvBuf = atwinc15x0_eth_buf, + .u16ethRcvBufSize = ARRAY_SIZE(atwinc15x0_eth_buf), + .u8EthernetEnable = M2M_WIFI_MODE_ETHERNET, + }, +}; + +/** + * Reference to the single ATWINC15x0 device instance + * + * Since the vendor ATWINC15x0 host driver uses many global variables, only + * a single ATWINC15x0 device can be used. Therefore, the RIOT driver only + * supports a single instance of an ATWINC15x0 device. The reference is + * needed in callback functions where a reference to the device is not + * available. + */ +atwinc15x0_t *atwinc15x0 = NULL; + +static void _atwinc15x0_eth_cb(uint8_t type, void *msg, void *ctrl_buf) +{ + assert(atwinc15x0); + assert(msg != NULL); + assert(ctrl_buf != NULL); + + tstrM2mIpCtrlBuf *ctrl = (tstrM2mIpCtrlBuf *)ctrl_buf; + + DEBUG("%s type=%u msg=%p len=%d remaining=%d\n", __func__, + type, msg, ctrl->u16DataSize, ctrl->u16RemainigDataSize); +#if MODULE_OD && ENABLE_DEBUG_DUMP + od_hex_dump(msg, ctrl->u16DataSize, 16); +#endif + + /* the buffer shouldn't be used here */ + assert(atwinc15x0->rx_buf == NULL); + + uint32_t state = irq_disable(); + + atwinc15x0->rx_buf = msg; + atwinc15x0->rx_len = ctrl->u16DataSize; + + irq_restore(state); + + /** + * This function is executed in the thread context. Therefore + * netdev.event_callback can be called directly, which avoids an + * additional intermediate buffer. + */ + atwinc15x0->netdev.event_callback(&atwinc15x0->netdev, + NETDEV_EVENT_RX_COMPLETE); +} + +typedef union { + tstrM2mWifiStateChanged state_changed; + tstrM2MConnInfo conn_info; + tstrM2mScanDone scan_done; + tstrM2mWifiscanResult scan_result; + int8_t rssi; +} atwinc15x0_event_t; + +static bool _rssi_info_ready = false; + +static void _atwinc15x0_wifi_cb(uint8_t type, void *msg) +{ + /** + * This function is executed in thread context. There is no need to call + * netdev_trigger_event_isr and to handle the events in _atwinc15x0_isr + */ + + DEBUG("%s %u %p\n", __func__, type, msg); + + atwinc15x0_event_t* event = (atwinc15x0_event_t *)msg; + + switch (type) { + case M2M_WIFI_RESP_SCAN_DONE: + DEBUG("%s scan done, %d APs found\n", __func__, + event->scan_done.u8NumofCh); + /* read the first scan result record */ + m2m_wifi_req_scan_result(0); + break; + + case M2M_WIFI_RESP_SCAN_RESULT: + LOG_DEBUG("[atwinc15x0] %s: rssi %d, auth %d, ch %d, bssid " + ATWINC15X0_MAC_STR "\n", + event->scan_result.au8SSID, + event->scan_result.s8rssi, + event->scan_result.u8AuthType, + event->scan_result.u8ch, + ATWINC15X0_MAC_STR_ARG(event->scan_result.au8BSSID)); + + if (memcmp(&event->scan_result.au8BSSID, + &atwinc15x0->ap, ETHERNET_ADDR_LEN) == 0) { + /* use the results for current AP to set the current channel */ + atwinc15x0->channel = event->scan_result.u8ch; + } + if (event->scan_result.u8index < m2m_wifi_get_num_ap_found()) { + /* read the next scan result record */ + m2m_wifi_req_scan_result(event->scan_result.u8index + 1); + } + break; + + case M2M_WIFI_RESP_CON_STATE_CHANGED: + switch (event->state_changed.u8CurrState) { + case M2M_WIFI_DISCONNECTED: + LOG_INFO("[atwinc15x0] WiFi disconnected\n"); + atwinc15x0->connected = false; + atwinc15x0->netdev.event_callback(&atwinc15x0->netdev, + NETDEV_EVENT_LINK_DOWN); + /* wait and try to reconnect */ + xtimer_usleep(ATWINC15X0_WAIT_RECONNECT); + _atwinc15x0_connect(); + break; + case M2M_WIFI_CONNECTED: + LOG_INFO("[atwinc15x0] WiFi connected\n"); + atwinc15x0->connected = true; + atwinc15x0->netdev.event_callback(&atwinc15x0->netdev, + NETDEV_EVENT_LINK_UP); + /* get information about the current AP */ + m2m_wifi_get_connection_info(); + /* start a scan for additional info, e.g. used channel */ + m2m_wifi_request_scan(M2M_WIFI_CH_ALL); + break; + default: + break; + } + break; + + case M2M_WIFI_RESP_CONN_INFO: + DEBUG("%s conn info %s, rssi %d, sec %u, bssid " + ATWINC15X0_MAC_STR "\n", __func__, + event->conn_info.acSSID, + event->conn_info.s8RSSI, + event->conn_info.u8SecType, + ATWINC15X0_MAC_STR_ARG(event->conn_info.au8MACAddress)); + + /* set the RSSI and BSSID of the current AP */ + atwinc15x0->rssi = event->conn_info.s8RSSI; + memcpy(atwinc15x0->ap, + event->conn_info.au8MACAddress, ETHERNET_ADDR_LEN); + break; + + case M2M_WIFI_RESP_CURRENT_RSSI: + DEBUG("%s current rssi %d\n", __func__, event->rssi); + /* set the RSSI */ + atwinc15x0->rssi = event->rssi; + _rssi_info_ready = true; + break; + + default: + break; + } +} + +static int _atwinc15x0_send(netdev_t *netdev, const iolist_t *iolist) +{ + atwinc15x0_t *dev = (atwinc15x0_t *)netdev; + + assert(dev); + assert(dev == atwinc15x0); + assert(iolist); + + if (!dev->connected) { + DEBUG("%s WiFi is still not connected to AP, cannot send", __func__); + return -ENODEV; + } + + /* atwinc15x0_eth_buf should not be used for incoming packets here */ + assert(dev->rx_buf == NULL); + + uint32_t state = irq_disable(); + uint16_t tx_len = 0; + + /* load packet data into the buffer */ + for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { + if (tx_len + iol->iol_len > ETHERNET_MAX_LEN) { + irq_restore(state); + return -EOVERFLOW; + } + if (iol->iol_len) { + memcpy (atwinc15x0_eth_buf + tx_len, iol->iol_base, iol->iol_len); + tx_len += iol->iol_len; + } + } + +#if ENABLE_DEBUG + DEBUG("%s send %d byte", __func__, tx_len); +#if MODULE_OD && ENABLE_DEBUG_DUMP + od_hex_dump(dev->tx_buf, dev->tx_len, OD_WIDTH_DEFAULT); +#endif /* MODULE_OD && ENABLE_DEBUG_HEXDUMP */ +#endif + irq_restore(state); + + /* send the the packet */ + if (m2m_wifi_send_ethernet_pkt(atwinc15x0_eth_buf, tx_len) == M2M_SUCCESS) { + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); + return tx_len; + } + else { + DEBUG("%s sending WiFi packet failed", __func__); + return -EIO; + } +} + +static int _atwinc15x0_recv(netdev_t *netdev, void *buf, size_t len, void *info) +{ + atwinc15x0_t *dev = (atwinc15x0_t *)netdev; + + (void)info; + assert(dev); + assert(dev == atwinc15x0); + + uint32_t state = irq_disable(); + uint16_t rx_size = dev->rx_len; + + if (!rx_size) { + /* there is nothing in receive buffer */ + irq_restore(state); + return 0; + } + + if (!buf) { + /* get the size of the frame */ + if (len > 0) { + /* if len > 0, drop the frame */ + dev->rx_len = 0; + dev->rx_buf = NULL; + } + irq_restore(state); + return rx_size; + } + + if (len < rx_size) { + /* buffer is smaller than the number of received bytes */ + DEBUG("%s not enough space in receive buffer", __func__); + /* newest API requires to drop the frame in that case */ + dev->rx_len = 0; + dev->rx_buf = NULL; + irq_restore(state); + return -ENOBUFS; + } + + /* remove length bytes, copy received packet to buffer */ + memcpy(buf, dev->rx_buf, dev->rx_len); + dev->rx_len = 0; + dev->rx_buf = NULL; + +#if ENABLE_DEBUG + ethernet_hdr_t *hdr = (ethernet_hdr_t *)buf; + DEBUG("%s received %u byte from addr " ATWINC15X0_MAC_STR "\n", + __func__, rx_size, ATWINC15X0_MAC_STR_ARG(hdr->src)); +#if MODULE_OD && ENABLE_DEBUG_DUMP + od_hex_dump(buf, rx_size, OD_WIDTH_DEFAULT); +#endif /* MODULE_OD && ENABLE_DEBUG_HEXDUMP */ +#endif /* ENABLE_DEBUG */ + + irq_restore(state); + + return rx_size; +} + +static int _atwinc15x0_get(netdev_t *netdev, netopt_t opt, void *val, + size_t max_len) +{ + atwinc15x0_t *dev = (atwinc15x0_t *)netdev; + + (void)max_len; + assert(val); + assert(dev); + assert(dev == atwinc15x0); + + DEBUG("%s dev=%p opt=%u val=%p max_len=%u\n", __func__, + netdev, opt, val, max_len); + + switch (opt) { + case NETOPT_IS_WIRED: + return -ENOTSUP; + + case NETOPT_ADDRESS: + assert(max_len >= ETHERNET_ADDR_LEN); + uint8_t valid; + m2m_wifi_get_otp_mac_address((uint8_t *)val, &valid); + return (valid) ? ETHERNET_ADDR_LEN : 0; + + case NETOPT_LINK: + assert(max_len == sizeof(netopt_enable_t)); + *((netopt_enable_t *)val) = (dev->connected) ? NETOPT_ENABLE + : NETOPT_DISABLE; + return sizeof(netopt_enable_t); + + case NETOPT_CHANNEL: + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)val) = dev->channel; + return sizeof(uint16_t); + + case NETOPT_RSSI: + assert(max_len == sizeof(int8_t)); + _rssi_info_ready = false; + /* trigger the request current RSSI (asynchronous function) */ + if (m2m_wifi_req_curr_rssi() != M2M_SUCCESS) { + return 0; + } + /* wait for the response with a given timeout */ + unsigned int _rssi_info_time_out = ATWINC15X0_WAIT_TIMEOUT; + while (!_rssi_info_ready && _rssi_info_time_out--) { + xtimer_usleep(ATWINC15X0_WAIT_TIME); + } + /* return the RSSI */ + *((int8_t *)val) = dev->rssi; + return sizeof(int8_t); + + default: + return netdev_eth_get(netdev, opt, val, max_len); + } +} + +static int _atwinc15x0_set(netdev_t *netdev, netopt_t opt, const void *val, + size_t max_len) +{ + assert(val); + + DEBUG("%s dev=%p opt=%u val=%p max_len=%u\n", __func__, + netdev, opt, val, max_len); + + switch (opt) { + case NETOPT_ADDRESS: + assert(max_len == ETHERNET_ADDR_LEN); + m2m_wifi_set_mac_address((uint8_t *)val); + return ETHERNET_ADDR_LEN; + default: + return netdev_eth_set(netdev, opt, val, max_len); + } +} + +static int _atwinc15x0_init(netdev_t *netdev) +{ + atwinc15x0_t *dev = (atwinc15x0_t *)netdev; + + (void)netdev; + assert(dev); + assert(dev == atwinc15x0); + + DEBUG("%s dev=%p\n", __func__, dev); + + atwinc15x0->bsp_isr = NULL; + atwinc15x0->bsp_irq_enabled = true; + atwinc15x0->connected = false; + atwinc15x0->rx_len = 0; + atwinc15x0->rx_buf = NULL; + + nm_bsp_init(); + + int8_t res; + + /* initialize the WINC Driver*/ + if ((res = m2m_wifi_init(&atwinc15x0_wifi_params)) != M2M_SUCCESS) { + DEBUG("m2m_wifi_init failed with code %d\n", res); + if (res == M2M_ERR_FW_VER_MISMATCH) { + LOG_WARNING("[atwinc15x0] Firmware version mismatch, " + "this may lead to problems.\n"); + } + else { + LOG_ERROR("[atwinc15x0] Driver initialization error %d\n", res); + return res; + } + } + + /* disable the built-in DHCP client */ + if ((res = m2m_wifi_enable_dhcp(false)) != M2M_SUCCESS) { + LOG_ERROR("[atwinc15x0] m2m_wifi_enable_dhcp failed with %d\n", res); + return res; + } + + /* try to connect and return */ + return _atwinc15x0_connect(); +} + +static int _atwinc15x0_connect(void) +{ + tuniM2MWifiAuth auth_info; + tenuM2mSecType auth_type = M2M_WIFI_SEC_OPEN; + +#if !defined(MODULE_WIFI_ENTERPRISE) && defined(WIFI_PASS) + + strncpy((char *)auth_info.au8PSK, WIFI_PASS, M2M_MAX_PSK_LEN); + auth_type = M2M_WIFI_SEC_WPA_PSK; + +#elif defined(MODULE_WIFI_ENTERPRISE) + +#if defined(WIFI_USER) && defined(WIFI_PASS) + strncpy((char *)&auth_info.strCred1x.au8UserName, WIFI_USER, M2M_1X_USR_NAME_MAX); + strncpy((char *)&auth_info.strCred1x.au8Passwd, WIFI_PASS, M2M_1X_PWD_MAX); + auth_type = M2M_WIFI_SEC_802_1X; +#else /* defined(WIFI_EAP_USER) && defined(WIFI_EAP_PASS) */ +#error WIFI_EAP_USER and WIFI_EAP_PASS have to define the user name \ + and the password for EAP phase 2 authentication in wifi_enterprise +#endif /* defined(WIFI_EAP_USER) && defined(WIFI_EAP_PASS) */ + +#endif /* defined(MODULE_ESP_WIFI_ENTERPRISE) */ + + /* connect */ + int8_t res; + if ((res = m2m_wifi_connect(WIFI_SSID, sizeof(WIFI_SSID), + auth_type, &auth_info, + M2M_WIFI_CH_ALL)) != M2M_SUCCESS) { + LOG_ERROR("[atwinc15x0] WiFi connect failed with %d\n", res); + return res; + } + + return 0; +} + +static void _atwinc15x0_isr(netdev_t *netdev) +{ + atwinc15x0_t *dev = (atwinc15x0_t *)netdev; + + assert(dev); + assert(dev == atwinc15x0); + + DEBUG("%s dev=%p\n", __func__, dev); + + /* handle pending ATWINC15x0 module events */ + while (m2m_wifi_handle_events(NULL) != M2M_SUCCESS) { } +} + +const netdev_driver_t atwinc15x0_netdev_driver = { + .send = _atwinc15x0_send, + .recv = _atwinc15x0_recv, + .init = _atwinc15x0_init, + .isr = _atwinc15x0_isr, + .get = _atwinc15x0_get, + .set = _atwinc15x0_set, +}; + +void atwinc15x0_setup(atwinc15x0_t *dev, const atwinc15x0_params_t *params) +{ + assert(dev); + + atwinc15x0 = dev; + atwinc15x0->netdev.driver = &atwinc15x0_netdev_driver; + atwinc15x0->params = *params; +} + +void atwinc15x0_irq(void) +{ + if (atwinc15x0) { + netdev_trigger_event_isr(&atwinc15x0->netdev); + } +} diff --git a/drivers/atwinc15x0/doc.txt b/drivers/atwinc15x0/doc.txt new file mode 100644 index 0000000000..a51ea86894 --- /dev/null +++ b/drivers/atwinc15x0/doc.txt @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_atwinc15x0 ATWINC15x0 WiFi module driver + * @ingroup drivers_netdev + * @brief Network device driver for the Microchip ATWINC15x0 WiFi Module + * + * @author Gunar Schorcht + +### Introduction + +This module implements a `netdev` device driver for the Microchip ATWINC15x0 +WiFi module. The ATWINC15x0 WiFi module is widely used as WiFi interface on +various boards and extension boards. Examples are: + +- [Adafruit Feather M0 WiFi](https://learn.adafruit.com/adafruit-feather-m0-wifi-atwinc1500/) + (also supported by [RIOT](https://doc.riot-os.org/group__boards__feather-m0.html)) +- [ATWIN1500-XPRO](https://www.microchip.com/DevelopmentTools/ProductDetails/ATWINC1500-XPRO) + for the extension of Atmel Xplained PRO boards (also supported by RIOT) +- [Adafruit ATWINC1500 WiFi breakout](https://learn.adafruit.com/adafruit-atwinc1500-wifi-module-breakout) +- [Watterott ATWINC1500-Breakout](https://github.com/watterott/ATWINC1500-Breakout) + +Since the ATWINC15x0 WiFi module is also the only module supported by the +standard [Arduino WiFi101 library](https://github.com/arduino-libraries/WiFi101), +there are a number of Arduino shields with the ATWINC15x0 WiFi module, e.g. +[Adafruit WINC1500 WiFi Shield for Arduino](https://learn.adafruit.com/adafruit-winc1500-wifi-shield-for-arduino). + +Using this 'netdev' driver together with an ATWINC15x0 WiFi module enables +the connection via WiFi in RIOT. + +### Microchip ATWINC15x0 WiFi module + +The ATWINC15x0 WiFi module is a low-power IEEE 802.11 b/g/n module with a +bandwidth of 20 MHz in the 2,4 GHz ISM band. It supports the IEEE 802.11i/WPA2 +personal and enterprise modes. In enterprise mode it supports + +- EAP-TLS +- EAP-PEAPv0/1 with TLS +- EAP-TTLSv0 with MSCHAPv2 +- EAP-PEAPv0/1 with MSCHAPv2 + +The WiFi module ATWINC15x0 is connected via SPI and supports SPI clock speeds +of up to 48 MHz. Although the module also has an I2C and a UART interface, +these interfaces are only used for debugging. + +The ATWINC15x0 WiFi module implements a complete TCP/IP procotol stack which is +not used in RIOT. + +### Driver Details + +The ATWINC15x0 WiFi `netdev` driver doesn't directly use the ATWINC15x0 WiFi +module. Instead, it uses the ATWINC15x0 vendor driver +[WINC1500 Wifi](http://asf.atmel.com/docs/latest/sam4s/html/group__winc1500__group.html). +from Microchip's Advanced Software Framework. For that purpose, the +[Arduino WiFi101 library](https://github.com/arduino-libraries/WiFi101) which +also includes this vendor library is used as a package. + +Although the ATWINC15x0 vendor driver is complex and quite large, there is +no other way to do it because the ATWINC15x0 WiFi module's programming +interface is not documented. At the very least, the ATWINC15x0 WiFi `netdev` +driver uses only those parts of the vendor driver that are absolutely +necessary. + +The ATWINC15x0 vendor driver consists of several parts: + +- the M2M WiFi library, which implements high-level WiFi connection management + Functions +- the HIF (Host Interface) library, which implements the interface with the + ATWINC15x0 module +- the BSP (Bus Support Package) as an abstraction layer for platform-specific + Functions +- the bus wrapper interface for access to the SPI + +In addition to these parts, the ATWINC15x0 vendor driver contains a +socket library and a library for accessing the SPI flash of the ATWINC15x0 +module, but these are not used in RIOT. + +The ATWINC15x0 WiFi `netdev` driver replaces the BSP and the Bus Wrapper +Interface by RIOT specific parts and interacts with ATWINC15x0 vendor driver +as shown in the following figure. + +``` ++-----------------+ +---------------+ +----------+ +------------+ +| RIOT ATWINC15x0 | | ATWINC15x0 | | RIOT Bus | | ATWINC15x0 | +| netdev driver | | vendor driver | | Wrapper | | module | ++-----------------+ +---------------+ +----------+ +------------+ + | | | | + | m2m_wifi_func | command | SPI(command) | + |------------------------->|--------------->|--------------->| + | return | | | + |<-------------------------| | | + | | | | + | | | | + | | | | + |<------------------------------- IRQ -----------------------| + | | | | + | m2m_wifi_handle_events | request | SPI(request) | + |------------------------->|--------------->|--------------->| + | | response | SPI(response) | + | atwinc15x0_wifi_cb |<---------------|<---------------| + |<-------------------------| | | + | | | | + | | | | + | | | | + | | | | + |<------------------------------- IRQ -----------------------| + | | | | + | m2m_wifi_handle_events | request | SPI(request) | + |------------------------->|--------------->|--------------->| + | | response | SPI(response) | + | atwinc15x0_eth_cb |<---------------|<---------------| + |<-------------------------| | | +``` + +As shown, the ATWINC15x0 WiFi module operates asynchronously. This means +that when an API function of the ATWINC15x0 driver is called, it simply +sends a command to the ATWINC15x0 WiFi module and returns immediately. +The command is then executed asynchronously by the ATWINC15x0 WiFi module. +As soon as the execution of the command is completed or one or more events +are pending, the module interrupts the host. Since the ISR is executed in +the interrupt context, the ATWINC15x0 'netdev' driver simply indicates +that an interrupt has occurred. The interrupt is handled later in the +thread context by calling 'm2m_wifi_handle_events'. This in turn fetches +available information from the ATWINC15x0 WiFi module and then calls +various callback functions according to the state, all of which are +executed in the thread context. + +## Configuration + +The ATWINC15x0 'netdev' driver requires the configuration ATWINC15x0 WiFi +module as WiFi settings: + +### Hardware Configuration + +The default configuration is defined in atwinc15x0_params.h and can be +overridden either by the board definition or by environment variables in +make command line. + +Symbol | Default | Description +:------------------------------|:-----------------|:--------------------- +`ATWINC15X0_PARAM_SPI` | `SPI_DEV(0)` | Used SPI device +`ATWINC15X0_PARAM_SPI_CLK` | `SPI_CLK_10MHZ` | Used SPI clock speed +`ATWINC15X0_PARAM_SSN_PIN` | `GPIO_PIN(1, 4)` | SPI slave select pin +`ATWINC15X0_PARAM_RESET_PIN` | `GPIO_PIN(4, 3)` | `RESET` pin +`ATWINC15X0_PARAM_IRQ_PIN` | `GPIO_PIN(7, 4)` | `IRQ` pin +`ATWINC15X0_PARAM_CHIP_EN_PIN` | `GPIO_UNDEF` | `CHIP_EN` pin +`ATWINC15X0_PARAM_WAKE_PIN` | `GPIO_UNDEF` | WAKE pin + +### WiFi Configuration + +At the moment only WPA2 Personal Mode is supported. The required settings are: + +Parameter | Default | Description +:---------|:----------|:------------ +WIFI_SSID | "RIOT_AP" | SSID of the AP to be used. +WIFI_PASS | - | Passphrase used for the AP as clear text (max. 64 chars). + +The following example shows the make command with the setting of different GPIOs and the WiFi parameters. +``` +USEMODULE='atwinc15x0' \ +CFLAGS='-DWIFI_SSID=\"ssid\" -DWIFI_PASS=\"pass\" \ + -DATWINC15X0_PARAM_SSN_PIN=GPIO_PIN\(1,6\) \ + -DATWINC15X0_PARAM_RESET_PIN=GPIO_PIN\(1,4\) \ + -DATWINC15X0_PARAM_IRQ_PIN=GPIO_PIN\(0,8\)' \ +make BOARD=... -C examples/gnrc_networking flash term +``` + +*/ diff --git a/drivers/atwinc15x0/include/atwinc15x0_internal.h b/drivers/atwinc15x0/include/atwinc15x0_internal.h new file mode 100644 index 0000000000..d3a7c5ce27 --- /dev/null +++ b/drivers/atwinc15x0/include/atwinc15x0_internal.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_atwinc15x0 + * @{ + * + * @file + * @brief Internal definitions for the ATWINC15x0 WiFi netdev driver + * + * @author Gunar Schorcht + */ + +#ifndef ATWINC15X0_INTERNAL_H +#define ATWINC15X0_INTERNAL_H + +#include "atwinc15x0.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Reference to the single ATWINC15x0 device instance + * + * Since the vendor ATWINC15x0 host driver uses many global variables, only + * a single ATWINC15x0 device can be used. Therefore, the RIOT driver only + * supports a single instance of an ATWINC15x0 device. + */ +extern atwinc15x0_t *atwinc15x0; + +/** + * @brief ATWINC15x0 device driver ISR + */ +void atwinc15x0_irq(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ATWINC15X0_INTERNAL_H */ +/** @} */ diff --git a/drivers/atwinc15x0/include/atwinc15x0_params.h b/drivers/atwinc15x0/include/atwinc15x0_params.h new file mode 100644 index 0000000000..5fa220a45f --- /dev/null +++ b/drivers/atwinc15x0/include/atwinc15x0_params.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_atwinc15x0 + * @{ + * + * @file + * @brief Default configuration for the ATWINC15x0 WiFi netdev driver + * + * @author Gunar Schorcht + */ + +#ifndef ATWINC15X0_PARAMS_H +#define ATWINC15X0_PARAMS_H + +#include "board.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SSID of the AP to be used. + */ +#ifndef WIFI_SSID +#define WIFI_SSID "RIOT_AP" +#endif + +/** + * @brief Passphrase used for the AP as clear text (max. 64 chars). + */ +#ifdef DOXYGEN +#define WIFI_PASS "ThisistheRIOTporttoESP" +#endif + +/** + * @name Set default configuration parameters + * Pins are adapted to Arduino Mega2560 boards. + * @{ + */ +#ifndef ATWINC15X0_PARAM_SPI +#define ATWINC15X0_PARAM_SPI SPI_DEV(0) +#endif + +#ifndef ATWINC15X0_PARAM_SPI_CLK +#define ATWINC15X0_PARAM_SPI_CLK SPI_CLK_10MHZ +#endif + +#ifndef ATWINC15X0_PARAM_SSN_PIN +#define ATWINC15X0_PARAM_SSN_PIN GPIO_PIN(1, 4) /* D10 (PB4) */ +#endif + +#ifndef ATWINC15X0_PARAM_RESET_PIN +#define ATWINC15X0_PARAM_RESET_PIN GPIO_PIN(4, 3) /* D5 (PE3) */ +#endif + +#ifndef ATWINC15X0_PARAM_IRQ_PIN +#define ATWINC15X0_PARAM_IRQ_PIN GPIO_PIN(7, 4) /* D7 (PH4) */ +#endif + +#ifndef ATWINC15X0_PARAM_CHIP_EN_PIN +#define ATWINC15X0_PARAM_CHIP_EN_PIN GPIO_UNDEF +#endif + +#ifndef ATWINC15X0_PARAM_WAKE_PIN +#define ATWINC15X0_PARAM_WAKE_PIN GPIO_UNDEF +#endif + +#ifndef ATWINC15X0_PARAMS +#define ATWINC15X0_PARAMS { \ + .spi = ATWINC15X0_PARAM_SPI, \ + .spi_clk = ATWINC15X0_PARAM_SPI_CLK, \ + .ssn_pin = ATWINC15X0_PARAM_SSN_PIN, \ + .reset_pin = ATWINC15X0_PARAM_RESET_PIN, \ + .irq_pin = ATWINC15X0_PARAM_IRQ_PIN, \ + .chip_en_pin = ATWINC15X0_PARAM_CHIP_EN_PIN, \ + .wake_pin = ATWINC15X0_PARAM_WAKE_PIN, \ + } +#endif +/** @} */ + +/** + * @brief Allocate some memory to store the actual configuration + */ +static const atwinc15x0_params_t atwinc15x0_params[] = +{ + ATWINC15X0_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ATWINC15X0_PARAMS_H */ +/** @} */ diff --git a/drivers/atwinc15x0/include/bsp/include/nm_bsp_internal.h b/drivers/atwinc15x0/include/bsp/include/nm_bsp_internal.h new file mode 100644 index 0000000000..3ade5df3d2 --- /dev/null +++ b/drivers/atwinc15x0/include/bsp/include/nm_bsp_internal.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#if !DOXYGEN + +/** + * @ingroup drivers_atwinc15x0 + * @{ + * + * @file + * @brief Internal compile config for the ATWINC15x0 WiFi netdev driver + * + * @author Gunar Schorcht + */ + +#ifndef BSP_INCLUDE_NM_BSP_INTERNAL_H +#define BSP_INCLUDE_NM_BSP_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef MODULE_ATMEGA_COMMON +#define ARDUINO +#endif + +#define CONF_WINC_USE_SPI (1) +#define CONF_WINC_PRINTF printf + +#ifndef CONF_WINC_DEBUG +#define CONF_WINC_DEBUG (0) +#endif + +#ifndef M2M_LOG_LEVEL +#define M2M_LOG_LEVEL M2M_LOG_ERROR +#endif + +#define NM_EDGE_INTERRUPT (1) + +#ifdef __cplusplus +} +#endif + +#endif /* BSP_INCLUDE_NM_BSP_INTERNAL_H */ +#endif /* DOXYGEN */ diff --git a/drivers/include/atwinc15x0.h b/drivers/include/atwinc15x0.h new file mode 100644 index 0000000000..bb36a74d82 --- /dev/null +++ b/drivers/include/atwinc15x0.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_netdev + * @brief Netdev Driver for the Microchip ATWINC15x0 WiFi Module + * @{ + * + * @file + * @brief Public interface for ATWINC15x0 netdev driver + * + * @author Gunar Schorcht + */ + +#ifndef ATWINC15X0_H +#define ATWINC15X0_H + +#include "bsp/include/nm_bsp.h" +#include "net/ethernet.h" +#include "net/netdev.h" +#include "periph/gpio.h" +#include "periph/spi.h" +#include "ringbuffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ATWINC15x0 hardware and global parameters + */ +typedef struct { + spi_t spi; /**< SPI device */ + spi_clk_t spi_clk; /**< SPI clock speed used */ + gpio_t ssn_pin; /**< SPI SS pin (slave select LOW active) */ + gpio_t reset_pin; /**< RESET_N pin (LOW active) */ + gpio_t irq_pin; /**< IRQN pin (LOW active) */ + gpio_t chip_en_pin; /**< CHIP_EN pin */ + gpio_t wake_pin; /**< WAKE pin */ +} atwinc15x0_params_t; + +/** + * @brief ATWINC15x0 device descriptor type + */ +typedef struct atwinc15x0 { + netdev_t netdev; /**< Pulls in the netdev fields */ + atwinc15x0_params_t params; /**< Device initialization parameters */ + + bool connected; /**< Indicates whether connected to an AP */ + char ap[ETHERNET_ADDR_LEN]; /**< BSSID of current AP */ + uint8_t channel; /**< Channel used for current AP */ + int8_t rssi; /**< RSSI last measured by the WiFi module */ + + uint8_t* rx_buf; /**< Incoming packet in receive buffer */ + uint16_t rx_len; /**< Length of an incoming packet, if there + is no packet in the buffer, it is 0 */ + + tpfNmBspIsr bsp_isr; /**< Board support package ISR */ + bool bsp_irq_enabled; /**< Board support package interrupt enabled */ +} atwinc15x0_t; + +/** + * @brief Setup the ATWINC15x0 WiFi module + * + * @param[in] dev Device descriptor + * @param[in] params Parameters for device initialization + */ +void atwinc15x0_setup(atwinc15x0_t *dev, const atwinc15x0_params_t *params); + +#ifdef __cplusplus +} +#endif + +#endif /* ATWINC15X0_H */ +/** @} */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 96ce971b13..be8440904c 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -107,6 +107,7 @@ PSEUDOMODULES += stdio_cdc_acm PSEUDOMODULES += stdio_uart_rx PSEUDOMODULES += suit_transport_% PSEUDOMODULES += wakaama_objects_% +PSEUDOMODULES += wifi_enterprise PSEUDOMODULES += xtimer_on_ztimer PSEUDOMODULES += zptr PSEUDOMODULES += ztimer%