1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers: add netdev driver for ATWINC15x0 WiFi module

This commit is contained in:
Gunar Schorcht 2020-06-26 10:30:43 +02:00
parent 649b315214
commit 660a852935
12 changed files with 1178 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -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 <gunar@schorcht.net>
*
* @}
*/
#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;
}

View File

@ -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 <gunar@schorcht.net>
*
* @}
*/
#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;
}

View File

@ -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 <gunar@schorcht.net>
*
* @}
*/
#include <string.h>
#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);
}
}

174
drivers/atwinc15x0/doc.txt Normal file
View File

@ -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 <gunar@schorcht.net>
### 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
```
*/

View File

@ -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 <gunar@schorcht.net>
*/
#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 */
/** @} */

View File

@ -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 <gunar@schorcht.net>
*/
#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 */
/** @} */

View File

@ -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 <gunar@schorcht.net>
*/
#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 */

View File

@ -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 <gunar@schorcht.net>
*/
#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 */
/** @} */

View File

@ -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%