mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
19387: drivers/atwinc15x0: support dynamic scanning and connection to AP r=benpicco a=fabian18 19874: coap: add missing option numbers r=benpicco a=JKRhb 19875: coap: add missing Content-Format definitions r=benpicco a=JKRhb Co-authored-by: Fabian Hüßler <fabian.huessler@ml-pa.loc@MLPA-NB119.(none)> Co-authored-by: Fabian Hüßler <fabian.huessler@ml-pa.com> Co-authored-by: Jan Romann <jan.romann@hs-emden-leer.de>
This commit is contained in:
commit
e56353cf24
@ -1,6 +1,18 @@
|
||||
USEMODULE += netdev_eth
|
||||
USEMODULE += ztimer_msec
|
||||
USEMODULE += netdev_legacy_api
|
||||
|
||||
ifeq (,$(filter atwinc15x0_dynamic_%,$(USEMODULE)))
|
||||
# use static connect by default when no dynamic module is loaded
|
||||
USEMODULE += atwinc15x0_static_connect
|
||||
endif
|
||||
ifneq (,$(filter atwinc15x0_dynamic_scan,$(USEMODULE)))
|
||||
USEMODULE += wifi_scan_list
|
||||
endif
|
||||
ifneq (,$(filter atwinc15x0_static_connect,$(USEMODULE)))
|
||||
USEMODULE += ztimer
|
||||
endif
|
||||
|
||||
USEPKG += driver_atwinc15x0
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
FEATURES_REQUIRED += periph_gpio_irq
|
||||
|
@ -1,2 +1,9 @@
|
||||
USEMODULE_INCLUDES_atwinc15x0 := $(LAST_MAKEFILEDIR)/include
|
||||
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_atwinc15x0)
|
||||
|
||||
# Try to connect to a preknown AP (WIFI_SSID, WIFI_PASS)
|
||||
PSEUDOMODULES += atwinc15x0_static_connect
|
||||
# Accept connection requests (NETOPT_CONNECT)
|
||||
PSEUDOMODULES += atwinc15x0_dynamic_connect
|
||||
# Accept scan requests (NETOPT_SCAN)
|
||||
PSEUDOMODULES += atwinc15x0_dynamic_scan
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Gunar Schorcht
|
||||
* 2023 ML!PA Consulting GmbH
|
||||
*
|
||||
* 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
|
||||
@ -14,6 +15,7 @@
|
||||
* @brief Netdev driver for the ATWINC15x0 WiFi module
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @author Fabian Hüßler <fabian.huessler@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
@ -29,11 +31,19 @@
|
||||
#include "driver/source/m2m_hif.h"
|
||||
#include "driver/include/m2m_wifi.h"
|
||||
|
||||
#include "thread.h"
|
||||
#include "compiler_hints.h"
|
||||
#include "assert.h"
|
||||
#include "log.h"
|
||||
#include "net/netopt.h"
|
||||
#include "net/wifi.h"
|
||||
#include "net/netdev/eth.h"
|
||||
#include "net/netdev/wifi.h"
|
||||
#include "net/wifi_scan_list.h"
|
||||
#include "od.h"
|
||||
#include "ztimer.h"
|
||||
#include "string_utils.h"
|
||||
#include "net/netif.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#define ENABLE_DEBUG_DUMP 0
|
||||
@ -47,20 +57,20 @@
|
||||
#define ATWINC15X0_WAIT_RECONNECT_MS (5000)
|
||||
|
||||
/**
|
||||
* @brief Don't perform operations that would wake the device from sleep
|
||||
* @brief Maximum number of scan list entries to deliver
|
||||
*/
|
||||
#define _CHECK_SLEEP_STATE(dev) do { \
|
||||
if (dev->state != NETOPT_STATE_IDLE) { \
|
||||
return -EBUSY; \
|
||||
} \
|
||||
} while (0)
|
||||
#define ATWINC15X0_SCAN_LIST_NUMOF CONFIG_ATWINC15X0_SCAN_LIST_NUMOF
|
||||
|
||||
/* 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);
|
||||
static int _atwinc15x0_connect(const wifi_connect_request_t *req);
|
||||
static int _atwinc15x0_disconnect(const wifi_disconnect_request_t *req);
|
||||
static int _atwinc15x0_scan(const wifi_scan_request_t *req);
|
||||
static int _atwinc15x0_init(netdev_t *netdev);
|
||||
static int _set_state(atwinc15x0_t *dev, netopt_state_t state);
|
||||
static netopt_state_t _get_state(const atwinc15x0_t *dev);
|
||||
static void _atwinc15x0_isr(netdev_t *netdev);
|
||||
|
||||
/**
|
||||
* The following buffer is required by the ATWINC15x0 vendor driver to store
|
||||
@ -104,6 +114,175 @@ static tstrWifiInitParam atwinc15x0_wifi_params = {
|
||||
*/
|
||||
atwinc15x0_t *atwinc15x0 = NULL;
|
||||
|
||||
MAYBE_UNUSED
|
||||
static struct {
|
||||
wifi_scan_list_t head;
|
||||
wifi_scan_list_node_t array[ATWINC15X0_SCAN_LIST_NUMOF];
|
||||
} _atwinc15x0_scan_list;
|
||||
|
||||
static inline void _wifi_scan_list_empty(void)
|
||||
{
|
||||
#if IS_USED(MODULE_WIFI_SCAN_LIST)
|
||||
wifi_scan_list_empty(&_atwinc15x0_scan_list.head,
|
||||
_atwinc15x0_scan_list.array,
|
||||
ARRAY_SIZE(_atwinc15x0_scan_list.array));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _wifi_scan_list_insert(const wifi_scan_result_t *result)
|
||||
{
|
||||
(void)result;
|
||||
#if IS_USED(MODULE_WIFI_SCAN_LIST)
|
||||
wifi_scan_list_insert(&_atwinc15x0_scan_list.head,
|
||||
_atwinc15x0_scan_list.array,
|
||||
ARRAY_SIZE(_atwinc15x0_scan_list.array),
|
||||
result);
|
||||
#endif
|
||||
}
|
||||
|
||||
MAYBE_UNUSED
|
||||
static wifi_scan_request_t _atwinc15x0_scan_req;
|
||||
|
||||
static inline void _wifi_scan_result_callback(const wifi_scan_list_t *scan_list)
|
||||
{
|
||||
if (_atwinc15x0_scan_req.base.scan_cb) {
|
||||
void *netif = netif_get_by_id(thread_getpid());
|
||||
((wifi_on_scan_result_t)_atwinc15x0_scan_req.base.scan_cb)(netif,
|
||||
scan_list);
|
||||
}
|
||||
_atwinc15x0_scan_req.base.scan_cb = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Internal next timeout type
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* @brief No / clear timeout
|
||||
*/
|
||||
ATWINC15X0_WIFI_STA_TIMEOUT_NONE = 0,
|
||||
/**
|
||||
* @brief Timeout to reconnect to
|
||||
*/
|
||||
ATWINC15X0_WIFI_STA_TIMEOUT_RECONNECT,
|
||||
} atwinc15x0_wifi_sta_timeout_t;
|
||||
|
||||
MAYBE_UNUSED
|
||||
static union {
|
||||
wifi_disconnect_request_t disconn_req;
|
||||
wifi_connect_request_t conn_req;
|
||||
} _atwinc15x0_connect_req;
|
||||
|
||||
static inline void _wifi_connect_result_callback(const wifi_connect_result_t *result)
|
||||
{
|
||||
if (_atwinc15x0_connect_req.conn_req.base.conn_cb) {
|
||||
void *netif = netif_get_by_id(thread_getpid());
|
||||
((wifi_on_connect_result_t)_atwinc15x0_connect_req.conn_req.base.conn_cb)(netif,
|
||||
result);
|
||||
}
|
||||
_atwinc15x0_connect_req.conn_req.base.conn_cb = NULL;
|
||||
/* _atwinc15x0_connect_req.conn_req.base.disconn_cb is called when connection is lost */
|
||||
}
|
||||
|
||||
static inline void _wifi_disconnect_result_callback(const wifi_disconnect_result_t *result)
|
||||
{
|
||||
if (_atwinc15x0_connect_req.conn_req.base.disconn_cb) {
|
||||
void *netif = netif_get_by_id(thread_getpid());
|
||||
((wifi_on_disconnect_result_t)_atwinc15x0_connect_req.conn_req.base.disconn_cb)(netif,
|
||||
result);
|
||||
}
|
||||
_atwinc15x0_connect_req.conn_req.base.conn_cb = NULL;
|
||||
_atwinc15x0_connect_req.conn_req.base.disconn_cb = NULL;
|
||||
}
|
||||
|
||||
MAYBE_UNUSED
|
||||
static struct {
|
||||
atwinc15x0_wifi_sta_timeout_t timeout;
|
||||
ztimer_t timer;
|
||||
} _atwinc15x0_timer;
|
||||
|
||||
static void _atwinc15x0_reconnect_timer(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
_atwinc15x0_timer.timeout = ATWINC15X0_WIFI_STA_TIMEOUT_RECONNECT;
|
||||
_atwinc15x0_timer.timer.callback = NULL;
|
||||
atwinc15x0_irq();
|
||||
}
|
||||
|
||||
static void _atwinc15x0_set_timer(void *arg, ztimer_callback_t cb, uint32_t timeout)
|
||||
{
|
||||
ztimer_remove(ZTIMER_MSEC, &_atwinc15x0_timer.timer);
|
||||
_atwinc15x0_timer.timer.arg = arg;
|
||||
_atwinc15x0_timer.timer.callback = cb;
|
||||
ztimer_set(ZTIMER_MSEC, &_atwinc15x0_timer.timer, timeout);
|
||||
}
|
||||
|
||||
static inline void _atwinc15x0_set_reconnect_timer(void)
|
||||
{
|
||||
_atwinc15x0_set_timer(NULL, _atwinc15x0_reconnect_timer, ATWINC15X0_WAIT_RECONNECT_MS);
|
||||
}
|
||||
|
||||
static int _atwinc15x0_static_connect(void)
|
||||
{
|
||||
if (!IS_USED(MODULE_ATWINC15X0_STATIC_CONNECT)) {
|
||||
return 0;
|
||||
}
|
||||
tuniM2MWifiAuth auth_info;
|
||||
tenuM2mSecType auth_type = M2M_WIFI_SEC_OPEN;
|
||||
if (_atwinc15x0_is_busy(atwinc15x0)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
if (_atwinc15x0_is_connected(atwinc15x0)) {
|
||||
return -EALREADY;
|
||||
}
|
||||
if (_atwinc15x0_is_sleeping(atwinc15x0)) {
|
||||
return -ECANCELED;
|
||||
}
|
||||
#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_USER) && defined(WIFI_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_USER) && defined(WIFI_PASS) */
|
||||
|
||||
#endif /* defined(MODULE_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 -EIO;
|
||||
}
|
||||
_atwinc15x0_set_connecting(atwinc15x0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int _atwinc15x0_get_sec_mode(tenuM2mSecType mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case M2M_WIFI_SEC_OPEN:
|
||||
return WIFI_SECURITY_MODE_OPEN;
|
||||
case M2M_WIFI_SEC_WPA_PSK:
|
||||
return WIFI_SECURITY_MODE_WPA2_PERSONAL;
|
||||
case M2M_WIFI_SEC_WEP:
|
||||
return WIFI_SECURITY_MODE_WEP_PSK;
|
||||
case M2M_WIFI_SEC_802_1X:
|
||||
return WIFI_SECURITY_MODE_WPA2_ENTERPRISE;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void _atwinc15x0_eth_cb(uint8_t type, void *msg, void *ctrl_buf)
|
||||
{
|
||||
assert(atwinc15x0);
|
||||
@ -115,7 +294,7 @@ static void _atwinc15x0_eth_cb(uint8_t type, void *msg, void *ctrl_buf)
|
||||
DEBUG("%s type=%u msg=%p len=%d remaining=%d\n", __func__,
|
||||
type, msg, ctrl->u16DataSize, ctrl->u16RemainigDataSize);
|
||||
|
||||
if (IS_ACTIVE(ENABLE_DEBUG) && IS_USED(MODULE_OD)) {
|
||||
if (IS_ACTIVE(ENABLE_DEBUG_DUMP) && IS_USED(MODULE_OD)) {
|
||||
od_hex_dump(msg, ctrl->u16DataSize, 16);
|
||||
}
|
||||
|
||||
@ -144,6 +323,181 @@ typedef union {
|
||||
|
||||
static bool _rssi_info_ready = false;
|
||||
|
||||
static void _atwinc15x0_handle_resp_scan_done(const tstrM2mScanDone* scan_done)
|
||||
{
|
||||
DEBUG("%s scan done, %d APs found\n", __func__, scan_done->u8NumofCh);
|
||||
if (scan_done->u8NumofCh > 0) {
|
||||
/* read the first scan result record */
|
||||
m2m_wifi_req_scan_result(0);
|
||||
}
|
||||
else {
|
||||
/* no results */
|
||||
_atwinc15x0_set_idle(atwinc15x0);
|
||||
if (IS_USED(MODULE_ATWINC15X0_DYNAMIC_SCAN)) {
|
||||
_wifi_scan_result_callback(&_atwinc15x0_scan_list.head);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _atwinc15x0_handle_resp_scan_result(const tstrM2mWifiscanResult* scan_result)
|
||||
{
|
||||
LOG_DEBUG("[atwinc15x0] %s: rssi %d, auth %d, ch %d, bssid "
|
||||
ATWINC15X0_MAC_STR "\n",
|
||||
scan_result->au8SSID,
|
||||
scan_result->s8rssi,
|
||||
scan_result->u8AuthType,
|
||||
scan_result->u8ch,
|
||||
ATWINC15X0_MAC_STR_ARG(scan_result->au8BSSID));
|
||||
if (_atwinc15x0_is_connected(atwinc15x0)) {
|
||||
if (!memcmp(scan_result->au8BSSID, &atwinc15x0->ap, ETHERNET_ADDR_LEN)) {
|
||||
/* use the results for current AP to set the current channel */
|
||||
atwinc15x0->channel = scan_result->u8ch;
|
||||
}
|
||||
}
|
||||
if (IS_USED(MODULE_ATWINC15X0_DYNAMIC_SCAN)) {
|
||||
int sec;
|
||||
if ((sec = _atwinc15x0_get_sec_mode(scan_result->u8AuthType)) != -1) {
|
||||
wifi_scan_result_t result = WIFI_SCAN_RESULT_INITIALIZER(scan_result->u8ch,
|
||||
scan_result->s8rssi, sec);
|
||||
memcpy(result.bssid, scan_result->au8BSSID, sizeof(result.bssid));
|
||||
strncpy(result.ssid, (const char *)scan_result->au8SSID, sizeof(result.ssid) - 1);
|
||||
_wifi_scan_list_insert(&result);
|
||||
}
|
||||
}
|
||||
if (scan_result->u8index < m2m_wifi_get_num_ap_found() - 1) {
|
||||
/* read the next scan result record */
|
||||
m2m_wifi_req_scan_result(scan_result->u8index + 1);
|
||||
}
|
||||
else {
|
||||
_atwinc15x0_set_idle(atwinc15x0);
|
||||
if (IS_USED(MODULE_ATWINC15X0_DYNAMIC_SCAN)) {
|
||||
_wifi_scan_result_callback(&_atwinc15x0_scan_list.head);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _atwinc15x0_handle_resp_con_state_changed(const tstrM2mWifiStateChanged *state_changed)
|
||||
{
|
||||
/**
|
||||
* The logic here can be tested with the following test cases:
|
||||
* 1. connect when disconnected
|
||||
* 2. connect to another AP when connected
|
||||
* 3. disconnect when connected
|
||||
* 4. go to sleep when connected
|
||||
*/
|
||||
switch (state_changed->u8CurrState) {
|
||||
case M2M_WIFI_DISCONNECTED:
|
||||
LOG_INFO("[atwinc15x0] WiFi disconnected\n");
|
||||
/* We disconnect before we connect, so we will first get a disconnect event when we
|
||||
were connected. After that when connection to the new AP fails we are already in a
|
||||
disconnected state. */
|
||||
bool was_connected = _atwinc15x0_is_connected(atwinc15x0);
|
||||
bool is_connecting = _atwinc15x0_is_connecting(atwinc15x0);
|
||||
bool is_disconnecting = _atwinc15x0_is_disconnecting(atwinc15x0);
|
||||
bool is_sleeping;
|
||||
if (!(is_sleeping = _atwinc15x0_is_sleeping(atwinc15x0))) {
|
||||
/* We requested to disconnect before sleep.
|
||||
Don´t override the sleep state when the disconnect event is received. */
|
||||
_atwinc15x0_set_disconnected(atwinc15x0);
|
||||
}
|
||||
if (was_connected || is_disconnecting || is_sleeping) {
|
||||
/* notify when connection state changed or when we disconnected due to sleep */
|
||||
DEBUG("atwinc15x0: notify upper layer about disconnect\n");
|
||||
atwinc15x0->netdev.event_callback(&atwinc15x0->netdev, NETDEV_EVENT_LINK_DOWN);
|
||||
}
|
||||
if (IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT)) {
|
||||
if (!was_connected && !is_disconnecting && !is_sleeping) {
|
||||
/* connection failed */
|
||||
DEBUG("atwinc15x0: notify about connection failure\n");
|
||||
wifi_disconnect_result_t disconn
|
||||
= WIFI_DISCONNECT_RESULT_INITIALIZER(
|
||||
_atwinc15x0_connect_req.conn_req.base.channel,
|
||||
_atwinc15x0_connect_req.conn_req.ssid);
|
||||
_wifi_disconnect_result_callback(&disconn);
|
||||
}
|
||||
else if ((was_connected || is_disconnecting || is_sleeping) && !is_connecting) {
|
||||
/* disconnect from previous connection */
|
||||
DEBUG("atwinc15x0: notify about disconnect\n");
|
||||
wifi_disconnect_result_t disconn
|
||||
= WIFI_DISCONNECT_RESULT_INITIALIZER(
|
||||
atwinc15x0->channel,
|
||||
_atwinc15x0_sta_get_current_ssid(atwinc15x0));
|
||||
_wifi_disconnect_result_callback(&disconn);
|
||||
}
|
||||
}
|
||||
if (IS_USED(MODULE_ATWINC15X0_STATIC_CONNECT)) {
|
||||
/* do not reconnect on sleep */
|
||||
if (!_atwinc15x0_is_sleeping(atwinc15x0)) {
|
||||
/* schedule reconnect timer:
|
||||
Not trying to reconnect immediately allows
|
||||
other connect requests to get through. */
|
||||
_atwinc15x0_set_reconnect_timer();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case M2M_WIFI_CONNECTED:
|
||||
LOG_INFO("[atwinc15x0] WiFi connected\n");
|
||||
_atwinc15x0_set_connected(atwinc15x0);
|
||||
atwinc15x0->netdev.event_callback(&atwinc15x0->netdev, NETDEV_EVENT_LINK_UP);
|
||||
/* get information about the current AP */
|
||||
m2m_wifi_get_connection_info();
|
||||
if (IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT)) {
|
||||
_atwinc15x0_sta_set_current_ssid(atwinc15x0,
|
||||
_atwinc15x0_connect_req.conn_req.ssid);
|
||||
atwinc15x0->channel = _atwinc15x0_connect_req.conn_req.base.channel;
|
||||
wifi_connect_result_t conn
|
||||
= WIFI_CONNECT_RESULT_INITIALIZER(
|
||||
atwinc15x0->channel,
|
||||
_atwinc15x0_sta_get_current_ssid(atwinc15x0));
|
||||
if (_atwinc15x0_connect_req.conn_req.cred) {
|
||||
if (*_atwinc15x0_connect_req.conn_req.cred == WIFI_SECURITY_MODE_WEP_PSK) {
|
||||
conn.credentials.wep = *((const wifi_security_wep_psk_t *)
|
||||
_atwinc15x0_connect_req.conn_req.cred);
|
||||
}
|
||||
else if (*_atwinc15x0_connect_req.conn_req.cred == WIFI_SECURITY_MODE_WPA2_PERSONAL) {
|
||||
conn.credentials.wpa_psk = *((const wifi_security_wpa_psk_t *)
|
||||
_atwinc15x0_connect_req.conn_req.cred);
|
||||
}
|
||||
else if (*_atwinc15x0_connect_req.conn_req.cred == WIFI_SECURITY_MODE_WPA2_ENTERPRISE) {
|
||||
conn.credentials.wpa_enterprise = *((const wifi_security_wpa_enterprise_t *)
|
||||
_atwinc15x0_connect_req.conn_req.cred);
|
||||
}
|
||||
}
|
||||
_wifi_connect_result_callback(&conn);
|
||||
}
|
||||
if (IS_USED(MODULE_ATWINC15X0_STATIC_CONNECT)) {
|
||||
/* start a scan for additional info, e.g. used channel */
|
||||
m2m_wifi_request_scan(M2M_WIFI_CH_ALL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _atwinc15x0_handle_resp_conn_info(const tstrM2MConnInfo *conn_info)
|
||||
{
|
||||
DEBUG("%s conn info %s, rssi %d, sec %u, bssid "
|
||||
ATWINC15X0_MAC_STR "\n", __func__,
|
||||
conn_info->acSSID,
|
||||
conn_info->s8RSSI,
|
||||
conn_info->u8SecType,
|
||||
ATWINC15X0_MAC_STR_ARG(conn_info->au8MACAddress));
|
||||
|
||||
/* set the RSSI and BSSID of the current AP */
|
||||
atwinc15x0->rssi = conn_info->s8RSSI;
|
||||
memcpy(atwinc15x0->ap, conn_info->au8MACAddress, ETHERNET_ADDR_LEN);
|
||||
if (IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT)) {
|
||||
_atwinc15x0_sta_set_current_ssid(atwinc15x0, conn_info->acSSID);
|
||||
}
|
||||
}
|
||||
|
||||
static void _atwinc15x0_handle_resp_current_rssi(int8_t rssi)
|
||||
{
|
||||
DEBUG("%s current rssi %d\n", __func__, rssi);
|
||||
/* set the RSSI */
|
||||
atwinc15x0->rssi = rssi;
|
||||
_rssi_info_ready = true;
|
||||
}
|
||||
|
||||
static void _atwinc15x0_wifi_cb(uint8_t type, void *msg)
|
||||
{
|
||||
/**
|
||||
@ -157,85 +511,19 @@ static void _atwinc15x0_wifi_cb(uint8_t type, void *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);
|
||||
_atwinc15x0_handle_resp_scan_done(&event->scan_done);
|
||||
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);
|
||||
}
|
||||
_atwinc15x0_handle_resp_scan_result(&event->scan_result);
|
||||
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);
|
||||
/* do not reconnect on standby or sleep */
|
||||
if (atwinc15x0->state == NETOPT_STATE_STANDBY ||
|
||||
atwinc15x0->state == NETOPT_STATE_SLEEP) {
|
||||
break;
|
||||
}
|
||||
/* wait and try to reconnect */
|
||||
ztimer_sleep(ZTIMER_MSEC, ATWINC15X0_WAIT_RECONNECT_MS);
|
||||
_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;
|
||||
}
|
||||
_atwinc15x0_handle_resp_con_state_changed(&event->state_changed);
|
||||
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);
|
||||
_atwinc15x0_handle_resp_conn_info(&event->conn_info);
|
||||
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:
|
||||
_atwinc15x0_handle_resp_current_rssi(event->rssi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -248,20 +536,15 @@ static int _atwinc15x0_send(netdev_t *netdev, const iolist_t *iolist)
|
||||
assert(dev == atwinc15x0);
|
||||
assert(iolist);
|
||||
|
||||
if (!dev->connected) {
|
||||
DEBUG("%s WiFi is still not connected to AP, cannot send\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* send wakes from standby but not from sleep */
|
||||
if (dev->state == NETOPT_STATE_SLEEP) {
|
||||
if (_atwinc15x0_is_sleeping(dev)) {
|
||||
DEBUG("%s WiFi is in SLEEP state, cannot send\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (dev->state == NETOPT_STATE_STANDBY) {
|
||||
_set_state(dev, NETOPT_STATE_IDLE);
|
||||
if (!_atwinc15x0_is_connected(dev)) {
|
||||
DEBUG("%s WiFi is still not connected to AP, cannot send\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* atwinc15x0_eth_buf should not be used for incoming packets here */
|
||||
assert(dev->rx_buf == NULL);
|
||||
|
||||
@ -350,15 +633,7 @@ static int _atwinc15x0_recv(netdev_t *netdev, void *buf, size_t len, void *info)
|
||||
|
||||
static netopt_enable_t _get_link_state(atwinc15x0_t *dev)
|
||||
{
|
||||
if (dev->state != NETOPT_STATE_IDLE) {
|
||||
return NETOPT_DISABLE;
|
||||
}
|
||||
|
||||
if (dev->connected) {
|
||||
return NETOPT_ENABLE;
|
||||
}
|
||||
|
||||
return NETOPT_DISABLE;
|
||||
return _atwinc15x0_is_connected(dev) ? NETOPT_ENABLE : NETOPT_DISABLE;
|
||||
}
|
||||
|
||||
static int _atwinc15x0_get(netdev_t *netdev, netopt_t opt, void *val,
|
||||
@ -367,7 +642,6 @@ static int _atwinc15x0_get(netdev_t *netdev, netopt_t opt, void *val,
|
||||
atwinc15x0_t *dev = (atwinc15x0_t *)netdev;
|
||||
|
||||
(void)max_len;
|
||||
assert(val);
|
||||
assert(dev);
|
||||
assert(dev == atwinc15x0);
|
||||
|
||||
@ -396,13 +670,15 @@ static int _atwinc15x0_get(netdev_t *netdev, netopt_t opt, void *val,
|
||||
|
||||
case NETOPT_STATE:
|
||||
assert(max_len >= sizeof(netopt_state_t));
|
||||
*((netopt_state_t *)val) = dev->state;
|
||||
*((netopt_state_t *)val) = _get_state(dev);
|
||||
return sizeof(netopt_state_t);
|
||||
|
||||
case NETOPT_RSSI:
|
||||
assert(max_len == sizeof(int16_t));
|
||||
_rssi_info_ready = false;
|
||||
_CHECK_SLEEP_STATE(dev);
|
||||
if (!_atwinc15x0_is_connected(dev)) {
|
||||
return -ECANCELED;
|
||||
}
|
||||
/* trigger the request current RSSI (asynchronous function) */
|
||||
if (m2m_wifi_req_curr_rssi() != M2M_SUCCESS) {
|
||||
return 0;
|
||||
@ -423,28 +699,39 @@ static int _atwinc15x0_get(netdev_t *netdev, netopt_t opt, void *val,
|
||||
|
||||
static int _set_state(atwinc15x0_t *dev, netopt_state_t state)
|
||||
{
|
||||
if (_atwinc15x0_is_busy(dev)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
switch (state) {
|
||||
case NETOPT_STATE_SLEEP:
|
||||
case NETOPT_STATE_STANDBY:
|
||||
dev->state = state;
|
||||
_atwinc15x0_set_sleeping(dev);
|
||||
m2m_wifi_disconnect();
|
||||
m2m_wifi_set_sleep_mode(M2M_PS_MANUAL, CONFIG_ATWINC15X0_RECV_BCAST);
|
||||
m2m_wifi_request_sleep(UINT32_MAX);
|
||||
if (gpio_is_valid(atwinc15x0->params.wake_pin)) {
|
||||
gpio_clear(atwinc15x0->params.wake_pin);
|
||||
}
|
||||
return sizeof(netopt_state_t);
|
||||
return sizeof(netopt_state_t);
|
||||
case NETOPT_STATE_IDLE:
|
||||
if (gpio_is_valid(atwinc15x0->params.wake_pin)) {
|
||||
gpio_set(atwinc15x0->params.wake_pin);
|
||||
if (_atwinc15x0_is_sleeping(dev)) {
|
||||
if (gpio_is_valid(atwinc15x0->params.wake_pin)) {
|
||||
gpio_set(atwinc15x0->params.wake_pin);
|
||||
}
|
||||
m2m_wifi_set_sleep_mode(M2M_PS_DEEP_AUTOMATIC, CONFIG_ATWINC15X0_RECV_BCAST);
|
||||
_atwinc15x0_set_disconnected(dev);
|
||||
if (IS_USED(MODULE_ATWINC15X0_STATIC_CONNECT)) {
|
||||
_atwinc15x0_set_reconnect_timer();
|
||||
}
|
||||
}
|
||||
m2m_wifi_set_sleep_mode(M2M_PS_DEEP_AUTOMATIC, CONFIG_ATWINC15X0_RECV_BCAST);
|
||||
dev->state = state;
|
||||
_atwinc15x0_connect();
|
||||
return sizeof(netopt_state_t);
|
||||
case NETOPT_STATE_RESET:
|
||||
if (_atwinc15x0_is_sleeping(dev)) {
|
||||
_set_state(dev, NETOPT_STATE_IDLE);
|
||||
}
|
||||
else if (_atwinc15x0_is_connected(dev)) {
|
||||
m2m_wifi_disconnect();
|
||||
}
|
||||
_atwinc15x0_init(&dev->netdev);
|
||||
dev->state = NETOPT_STATE_IDLE;
|
||||
return sizeof(netopt_state_t);
|
||||
default:
|
||||
break;
|
||||
@ -453,15 +740,25 @@ static int _set_state(atwinc15x0_t *dev, netopt_state_t state)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static netopt_state_t _get_state(const atwinc15x0_t *dev)
|
||||
{
|
||||
if (dev->state == ATWINC15X0_STATE_SLEEP) {
|
||||
return NETOPT_STATE_SLEEP;
|
||||
}
|
||||
else {
|
||||
return NETOPT_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
static int _atwinc15x0_set(netdev_t *netdev, netopt_t opt, const void *val,
|
||||
size_t max_len)
|
||||
{
|
||||
atwinc15x0_t *dev = (atwinc15x0_t *)netdev;
|
||||
assert(val);
|
||||
|
||||
DEBUG("%s dev=%p opt=%u val=%p max_len=%u\n", __func__,
|
||||
(void *)netdev, opt, val, max_len);
|
||||
|
||||
int ret;
|
||||
switch (opt) {
|
||||
case NETOPT_ADDRESS:
|
||||
assert(max_len == ETHERNET_ADDR_LEN);
|
||||
@ -471,7 +768,15 @@ static int _atwinc15x0_set(netdev_t *netdev, netopt_t opt, const void *val,
|
||||
assert(max_len <= sizeof(netopt_state_t));
|
||||
return _set_state(dev, *((const netopt_state_t *)val));
|
||||
case NETOPT_L2_GROUP:
|
||||
if (m2m_wifi_enable_mac_mcast((void *)val, 1)) {
|
||||
/* sometimes m2m_wifi_enable_mac_mcast() fails with M2M_ERR_MEM_ALLOC */
|
||||
m2m_wifi_enable_mac_mcast((void *)val, 0);
|
||||
/* sometimes it fails with M2M_ERR_BUS_FAIL */
|
||||
int tries = 5;
|
||||
do {
|
||||
ret = m2m_wifi_enable_mac_mcast((void *)val, 1);
|
||||
DEBUG_PUTS("busy loop setting L2 multicast address on atwinc15x0");
|
||||
} while (--tries && ret == M2M_ERR_BUS_FAIL);
|
||||
if (ret) {
|
||||
return -EINVAL;
|
||||
} else {
|
||||
return max_len;
|
||||
@ -482,9 +787,53 @@ static int _atwinc15x0_set(netdev_t *netdev, netopt_t opt, const void *val,
|
||||
} else {
|
||||
return max_len;
|
||||
}
|
||||
case NETOPT_SCAN:
|
||||
if (!IS_USED(MODULE_ATWINC15X0_DYNAMIC_SCAN)) {
|
||||
break;
|
||||
}
|
||||
if (max_len < sizeof(wifi_scan_request_t)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((ret = _atwinc15x0_scan((const wifi_scan_request_t *)val)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
return sizeof(wifi_scan_request_t);
|
||||
case NETOPT_CONNECT:
|
||||
if (!IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT)) {
|
||||
break;
|
||||
}
|
||||
if (max_len < sizeof(wifi_connect_request_t)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((ret = _atwinc15x0_connect((const wifi_connect_request_t *)val)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
return sizeof(wifi_connect_request_t);
|
||||
case NETOPT_DISCONNECT:
|
||||
if (!IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT)) {
|
||||
break;
|
||||
}
|
||||
if (max_len < sizeof(wifi_disconnect_request_t)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((ret = _atwinc15x0_disconnect((const wifi_disconnect_request_t *)val)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
return sizeof(wifi_disconnect_request_t);
|
||||
default:
|
||||
return netdev_eth_set(netdev, opt, val, max_len);
|
||||
break;
|
||||
}
|
||||
return netdev_eth_set(netdev, opt, val, max_len);
|
||||
}
|
||||
|
||||
static void _print_firmware_version(const tstrM2mRev *info)
|
||||
{
|
||||
LOG_DEBUG("[atwinc15x0] CHIP ID: %lu\n",
|
||||
info->u32Chipid);
|
||||
LOG_DEBUG("[atwinc15x0] FIRMWARE: %u.%u.%u\n",
|
||||
info->u8FirmwareMajor, info->u8FirmwareMinor, info->u8FirmwarePatch);
|
||||
LOG_DEBUG("[atwinc15x0] DRIVER: %u.%u.%u\n",
|
||||
info->u8DriverMajor, info->u8DriverMinor, info->u8DriverPatch);
|
||||
}
|
||||
|
||||
static int _atwinc15x0_init(netdev_t *netdev)
|
||||
@ -499,15 +848,13 @@ static int _atwinc15x0_init(netdev_t *netdev)
|
||||
|
||||
atwinc15x0->bsp_isr = NULL;
|
||||
atwinc15x0->bsp_irq_enabled = true;
|
||||
atwinc15x0->connected = false;
|
||||
atwinc15x0->state = NETOPT_STATE_IDLE;
|
||||
atwinc15x0->state = ATWINC15X0_STATE_DISCONNECTED;
|
||||
atwinc15x0->rx_len = 0;
|
||||
atwinc15x0->rx_buf = NULL;
|
||||
|
||||
nm_bsp_init();
|
||||
|
||||
int8_t res;
|
||||
|
||||
int 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);
|
||||
@ -521,6 +868,29 @@ static int _atwinc15x0_init(netdev_t *netdev)
|
||||
}
|
||||
}
|
||||
|
||||
/* get firmware version */
|
||||
if (IS_ACTIVE(ENABLE_DEBUG)) {
|
||||
tstrM2mRev fw_ver;
|
||||
if ((res = m2m_wifi_get_firmware_version(&fw_ver)) != M2M_SUCCESS) {
|
||||
LOG_ERROR("[atwinc15x0] Could not read firmware version\n");
|
||||
}
|
||||
else {
|
||||
_print_firmware_version(&fw_ver);
|
||||
}
|
||||
}
|
||||
/* set Wi-Fi region */
|
||||
if (WIFI_REGION == WIFI_REGION_EUROPE) {
|
||||
res = m2m_wifi_set_scan_region(EUROPE);
|
||||
}
|
||||
else if (WIFI_REGION == WIFI_REGION_NORTH_AMERICA) {
|
||||
res = m2m_wifi_set_scan_region(NORTH_AMERICA);
|
||||
}
|
||||
else if (WIFI_REGION == WIFI_REGION_ASIA ) {
|
||||
res = m2m_wifi_set_scan_region(ASIA);
|
||||
}
|
||||
if (res != M2M_SUCCESS) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
/* 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);
|
||||
@ -530,42 +900,145 @@ static int _atwinc15x0_init(netdev_t *netdev)
|
||||
/* enable automatic power saving */
|
||||
m2m_wifi_set_sleep_mode(M2M_PS_DEEP_AUTOMATIC, CONFIG_ATWINC15X0_RECV_BCAST);
|
||||
|
||||
/* try to connect and return */
|
||||
return _atwinc15x0_connect();
|
||||
res = 0;
|
||||
if (IS_USED(MODULE_ATWINC15X0_STATIC_CONNECT)) {
|
||||
/* try to connect and return */
|
||||
res = _atwinc15x0_static_connect();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _atwinc15x0_connect(void)
|
||||
static int _atwinc15x0_scan(const wifi_scan_request_t *req)
|
||||
{
|
||||
assert(req);
|
||||
(void)req;
|
||||
if (!IS_USED(MODULE_ATWINC15X0_DYNAMIC_SCAN)) {
|
||||
return 0;
|
||||
}
|
||||
if (_atwinc15x0_is_busy(atwinc15x0)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
if (req->base.channel != NETOPT_SCAN_REQ_ALL_CH) {
|
||||
if (req->base.channel < WIFI_2_4_CH_MIN ||
|
||||
req->base.channel > WIFI_2_4_CH_MAX) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (_atwinc15x0_is_sleeping(atwinc15x0)) {
|
||||
_set_state(atwinc15x0, NETOPT_STATE_IDLE);
|
||||
}
|
||||
tstrM2MScanOption opt = {
|
||||
.u8NumOfSlot = ATWINC1510_SCAN_SLOTS_DEF,
|
||||
.u8ProbesPerSlot = ATWINC1510_SCAN_PROBES_NUMOF_DEF,
|
||||
.u8SlotTime = ATWINC1510_SCAN_SLOT_TIME_MS_DEF,
|
||||
.s8RssiThresh = ATWINC1510_SCAN_THRESHOLD_DBM_DEF,
|
||||
};
|
||||
if (req->timeout_ms_per_ch) {
|
||||
uint16_t ch_time_max = ATWINC1510_SCAN_SLOT_TIME_MS_MAX * opt.u8NumOfSlot;
|
||||
if (req->timeout_ms_per_ch > ch_time_max) {
|
||||
opt.u8SlotTime = ATWINC1510_SCAN_SLOT_TIME_MS_MAX;
|
||||
}
|
||||
else {
|
||||
opt.u8SlotTime = req->timeout_ms_per_ch / opt.u8NumOfSlot;
|
||||
}
|
||||
}
|
||||
int ret;
|
||||
if ((ret = m2m_wifi_set_scan_options(&opt)) != M2M_SUCCESS) {
|
||||
LOG_ERROR("[atwinc15x0] WiFi setting scan options failed with %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
if ((ret = m2m_wifi_request_scan(req->base.channel == NETOPT_SCAN_REQ_ALL_CH
|
||||
? M2M_WIFI_CH_ALL
|
||||
: req->base.channel)) != M2M_SUCCESS) {
|
||||
LOG_ERROR("[atwinc15x0] WiFi scan failed with %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
_atwinc15x0_set_scanning(atwinc15x0);
|
||||
_atwinc15x0_scan_req = *req;
|
||||
_wifi_scan_list_empty();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _atwinc15x0_disconnect(const wifi_disconnect_request_t *req)
|
||||
{
|
||||
assert(req);
|
||||
if (_atwinc15x0_is_busy(atwinc15x0)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
if (!_atwinc15x0_is_connected(atwinc15x0)) {
|
||||
/* also when sleeping */
|
||||
return -EALREADY;
|
||||
}
|
||||
int ret;
|
||||
if ((ret = m2m_wifi_disconnect()) != M2M_SUCCESS) {
|
||||
LOG_ERROR("[atwinc15x0] WiFi disconnect failed with %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
_atwinc15x0_set_disconnecting(atwinc15x0);
|
||||
_atwinc15x0_connect_req.disconn_req = *req;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _atwinc15x0_connect(const wifi_connect_request_t *req)
|
||||
{
|
||||
assert(req);
|
||||
if (!IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT)) {
|
||||
return 0;
|
||||
}
|
||||
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 */
|
||||
if (_atwinc15x0_is_busy(atwinc15x0)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
if (_atwinc15x0_is_connected(atwinc15x0)) {
|
||||
if (!strcmp(req->ssid, _atwinc15x0_sta_get_current_ssid(atwinc15x0))) {
|
||||
return -EALREADY;
|
||||
}
|
||||
}
|
||||
else if (_atwinc15x0_is_sleeping(atwinc15x0)) {
|
||||
_set_state(atwinc15x0, NETOPT_STATE_IDLE);
|
||||
}
|
||||
if (req->cred && *(req->cred) != WIFI_SECURITY_MODE_OPEN) {
|
||||
if (*(req->cred) == WIFI_SECURITY_MODE_WEP_PSK) {
|
||||
auth_type = M2M_WIFI_SEC_WEP;
|
||||
const wifi_security_wep_psk_t *cred = (const wifi_security_wep_psk_t *)req->cred;
|
||||
strscpy((char *)auth_info.au8PSK, cred->psk, sizeof(auth_info.au8PSK));
|
||||
}
|
||||
else if (*(req->cred) == WIFI_SECURITY_MODE_WPA2_PERSONAL) {
|
||||
auth_type = M2M_WIFI_SEC_WPA_PSK;
|
||||
const wifi_security_wpa_psk_t *cred = (const wifi_security_wpa_psk_t *)req->cred;
|
||||
strscpy((char *)auth_info.au8PSK, cred->psk, sizeof(auth_info.au8PSK));
|
||||
}
|
||||
else if (*(req->cred) == WIFI_SECURITY_MODE_WPA2_ENTERPRISE) {
|
||||
auth_type = M2M_WIFI_SEC_802_1X;
|
||||
const wifi_security_wpa_enterprise_t *cred
|
||||
= (const wifi_security_wpa_enterprise_t *)req->cred;
|
||||
strscpy((char *)auth_info.strCred1x.au8UserName, cred->user,
|
||||
sizeof(auth_info.strCred1x.au8UserName));
|
||||
strscpy((char *)auth_info.strCred1x.au8Passwd, cred->pwd,
|
||||
sizeof(auth_info.strCred1x.au8Passwd));
|
||||
}
|
||||
else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
int8_t res;
|
||||
if ((res = m2m_wifi_connect(WIFI_SSID, sizeof(WIFI_SSID),
|
||||
if (_atwinc15x0_is_connected(atwinc15x0)) {
|
||||
/* late disconnect to not interrupt connection on errors before */
|
||||
if ((res = m2m_wifi_disconnect()) != M2M_SUCCESS) {
|
||||
LOG_ERROR("[atwinc15x0] WiFi disconnect failed with %d\n", res);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
/* connect */
|
||||
if ((res = m2m_wifi_connect((char *)req->ssid, strlen(req->ssid),
|
||||
auth_type, &auth_info,
|
||||
M2M_WIFI_CH_ALL)) != M2M_SUCCESS) {
|
||||
LOG_ERROR("[atwinc15x0] WiFi connect failed with %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
_atwinc15x0_set_connecting(atwinc15x0);
|
||||
_atwinc15x0_connect_req.conn_req = *req;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -583,6 +1056,18 @@ static void _atwinc15x0_isr(netdev_t *netdev)
|
||||
DEBUG("%s handle events failed, reset device\n", __func__);
|
||||
_atwinc15x0_init(netdev);
|
||||
}
|
||||
int err;
|
||||
if (IS_USED(MODULE_ATWINC15X0_STATIC_CONNECT)) {
|
||||
if (_atwinc15x0_timer.timeout == ATWINC15X0_WIFI_STA_TIMEOUT_RECONNECT) {
|
||||
if (!_atwinc15x0_is_connected(atwinc15x0)) {
|
||||
/* try again if device is busy or the Atmel firmware throws an error */
|
||||
if ((err = _atwinc15x0_static_connect()) == -EBUSY || err == -EIO) {
|
||||
_atwinc15x0_set_reconnect_timer();
|
||||
}
|
||||
}
|
||||
_atwinc15x0_timer.timeout = ATWINC15X0_WIFI_STA_TIMEOUT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const netdev_driver_t atwinc15x0_netdev_driver = {
|
||||
|
@ -19,6 +19,10 @@
|
||||
#ifndef ATWINC15X0_INTERNAL_H
|
||||
#define ATWINC15X0_INTERNAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "driver/include/m2m_types.h"
|
||||
#include "atwinc15x0.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -34,6 +38,264 @@ extern "C" {
|
||||
*/
|
||||
extern atwinc15x0_t *atwinc15x0;
|
||||
|
||||
/**
|
||||
* @brief Minimum number of slots to scan a channel
|
||||
*/
|
||||
#define ATWINC1510_SCAN_SLOTS_MIN 2
|
||||
|
||||
/**
|
||||
* @brief Default number of slots to scan a channel
|
||||
*/
|
||||
#define ATWINC1510_SCAN_SLOTS_DEF M2M_SCAN_DEFAULT_NUM_SLOTS
|
||||
|
||||
/**
|
||||
* @brief Maximum number of slots to scan a channel
|
||||
*/
|
||||
#define ATWINC1510_SCAN_SLOTS_MAX 255
|
||||
|
||||
/**
|
||||
* @brief Time in ms to scan a slot in a channel
|
||||
*/
|
||||
#define ATWINC1510_SCAN_SLOT_TIME_MS_MIN 10
|
||||
|
||||
/**
|
||||
* @brief Default time in ms to scan a slot in a channel
|
||||
*/
|
||||
#define ATWINC1510_SCAN_SLOT_TIME_MS_DEF M2M_SCAN_DEFAULT_SLOT_TIME
|
||||
|
||||
/**
|
||||
* @brief Maximum time in ms to scan a slot in a channel
|
||||
*/
|
||||
#define ATWINC1510_SCAN_SLOT_TIME_MS_MAX 250
|
||||
|
||||
/**
|
||||
* @brief Default number of probes to send to scan a channel
|
||||
*/
|
||||
#define ATWINC1510_SCAN_PROBES_NUMOF_DEF M2M_SCAN_DEFAULT_NUM_PROBE
|
||||
|
||||
/**
|
||||
* @brief Default threshold in dbm for an AP to pass
|
||||
*/
|
||||
#define ATWINC1510_SCAN_THRESHOLD_DBM_DEF (-99)
|
||||
|
||||
/**
|
||||
* @brief Check if @p dev is scanning
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
*
|
||||
* @returns true if @p dev is scanning
|
||||
* @returns false if @p dev is not scanning
|
||||
*/
|
||||
static inline bool _atwinc15x0_is_scanning(const atwinc15x0_t *dev) {
|
||||
return dev->state == ATWINC15X0_STATE_CONNECTED_SCANNING ||
|
||||
dev->state == ATWINC15X0_STATE_DISCONNECTED_SCANNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set state to indicate that @p dev is scanning
|
||||
*
|
||||
* @param[in, out] dev ATWINC15x0 device
|
||||
*/
|
||||
static inline void _atwinc15x0_set_scanning(atwinc15x0_t *dev) {
|
||||
if (dev->state == ATWINC15X0_STATE_CONNECTED) {
|
||||
dev->state = ATWINC15X0_STATE_CONNECTED_SCANNING;
|
||||
}
|
||||
else if (dev->state == ATWINC15X0_STATE_DISCONNECTED) {
|
||||
dev->state = ATWINC15X0_STATE_DISCONNECTED_SCANNING;
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if @p dev is connecting to an AP
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
*
|
||||
* @returns true if @p dev is connecting
|
||||
* @returns false if @p dev is not connecting
|
||||
*/
|
||||
static inline bool _atwinc15x0_is_connecting(const atwinc15x0_t *dev) {
|
||||
return dev->state == ATWINC15X0_STATE_CONNECTED_CONNECTING ||
|
||||
dev->state == ATWINC15X0_STATE_DISCONNECTED_CONNECTING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set state to indicate that @p dev is connecting
|
||||
* to an AP
|
||||
*
|
||||
* @param[in, out] dev ATWINC15x0 device
|
||||
*/
|
||||
static inline void _atwinc15x0_set_connecting(atwinc15x0_t *dev) {
|
||||
if (dev->state == ATWINC15X0_STATE_CONNECTED) {
|
||||
dev->state = ATWINC15X0_STATE_CONNECTED_CONNECTING;
|
||||
}
|
||||
else if (dev->state == ATWINC15X0_STATE_DISCONNECTED) {
|
||||
dev->state = ATWINC15X0_STATE_DISCONNECTED_CONNECTING;
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if @p dev is disconnecting from an AP
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
*
|
||||
* @returns true if @p dev is disconnecting
|
||||
* @returns false if @p dev is not disconnecting
|
||||
*/
|
||||
static inline bool _atwinc15x0_is_disconnecting(const atwinc15x0_t *dev) {
|
||||
return dev->state == ATWINC15X0_STATE_DISCONNECTING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set state to indicate that @p dev is disconnecting
|
||||
* from an AP
|
||||
*
|
||||
* @param[in, out] dev ATWINC15x0 device
|
||||
*/
|
||||
static inline void _atwinc15x0_set_disconnecting(atwinc15x0_t *dev) {
|
||||
if (dev->state == ATWINC15X0_STATE_CONNECTED) {
|
||||
dev->state = ATWINC15X0_STATE_DISCONNECTING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if @p dev is connected to an AP
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
*
|
||||
* @returns true if @p dev is connected
|
||||
* @returns false if @p dev is not connected
|
||||
*/
|
||||
static inline bool _atwinc15x0_is_connected(const atwinc15x0_t *dev) {
|
||||
return dev->state == ATWINC15X0_STATE_CONNECTED ||
|
||||
dev->state == ATWINC15X0_STATE_CONNECTED_SCANNING ||
|
||||
dev->state == ATWINC15X0_STATE_CONNECTED_CONNECTING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set state to indicate that @p dev is connected
|
||||
* to an AP
|
||||
*
|
||||
* @param[in, out] dev ATWINC15x0 device
|
||||
*/
|
||||
static inline void _atwinc15x0_set_connected(atwinc15x0_t *dev) {
|
||||
assert(dev->state == ATWINC15X0_STATE_DISCONNECTED ||
|
||||
dev->state == ATWINC15X0_STATE_DISCONNECTED_CONNECTING ||
|
||||
dev->state == ATWINC15X0_STATE_CONNECTED_SCANNING);
|
||||
dev->state = ATWINC15X0_STATE_CONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set state to indicate that @p dev is disconnected
|
||||
*
|
||||
* @param[in, out] dev ATWINC15x0 device
|
||||
*/
|
||||
static inline void _atwinc15x0_set_disconnected(atwinc15x0_t *dev) {
|
||||
dev->state = ATWINC15X0_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if @p dev is currently performing an asynchronous operation
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
* @returns true if @p dev is busy
|
||||
* @returns false if @p dev is not busy
|
||||
*/
|
||||
static inline bool _atwinc15x0_is_busy(const atwinc15x0_t *dev) {
|
||||
return dev->state == ATWINC15X0_STATE_DISCONNECTING ||
|
||||
dev->state == ATWINC15X0_STATE_DISCONNECTED_SCANNING ||
|
||||
dev->state == ATWINC15X0_STATE_DISCONNECTED_CONNECTING ||
|
||||
dev->state == ATWINC15X0_STATE_CONNECTED_SCANNING ||
|
||||
dev->state == ATWINC15X0_STATE_CONNECTED_CONNECTING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check is @p dev is currently not performing an asynchronous operation
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
*
|
||||
* @return true if @p dev is idle
|
||||
* @return false if @p dev is not idle
|
||||
*/
|
||||
static inline bool _atwinc15x0_is_idle(const atwinc15x0_t *dev) {
|
||||
return dev->state == ATWINC15X0_STATE_CONNECTED ||
|
||||
dev->state == ATWINC15X0_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return from any busy state to corresponding idle state
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
*/
|
||||
static inline void _atwinc15x0_set_idle(atwinc15x0_t *dev) {
|
||||
if (dev->state == ATWINC15X0_STATE_CONNECTED_SCANNING ||
|
||||
dev->state == ATWINC15X0_STATE_CONNECTED_CONNECTING) {
|
||||
_atwinc15x0_set_connected(dev);
|
||||
}
|
||||
else if (dev->state == ATWINC15X0_STATE_DISCONNECTED_SCANNING ||
|
||||
dev->state == ATWINC15X0_STATE_DISCONNECTED_CONNECTING) {
|
||||
_atwinc15x0_set_disconnected(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set state to indicate that @p dev is sleeping
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
*/
|
||||
static inline void _atwinc15x0_set_sleeping(atwinc15x0_t *dev) {
|
||||
assert(!_atwinc15x0_is_busy(dev));
|
||||
dev->state = ATWINC15X0_STATE_SLEEP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if @p dev is currently sleeping
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
* @return true if @p dev is sleeping
|
||||
* @return false if @p dev is not sleeping
|
||||
*/
|
||||
static inline bool _atwinc15x0_is_sleeping(const atwinc15x0_t *dev) {
|
||||
return dev->state == ATWINC15X0_STATE_SLEEP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set member of currently connected AP SSID
|
||||
*
|
||||
* @pre Module atwinc15x0_dynamic_connect is used
|
||||
*
|
||||
* @param[in, out] dev ATWINC15x0 device
|
||||
* @param[in] ssid SSID
|
||||
*/
|
||||
static inline void _atwinc15x0_sta_set_current_ssid(atwinc15x0_t *dev, const char *ssid) {
|
||||
(void)dev; (void)ssid;
|
||||
#if IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT)
|
||||
strcpy(atwinc15x0->ssid, ssid);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get member of currently connected AP
|
||||
*
|
||||
* @pre Module atwinc15x0_dynamic_connect is used
|
||||
*
|
||||
* @param[in] dev ATWINC15x0 device
|
||||
*
|
||||
* @returns SSID member
|
||||
*/
|
||||
static inline const char *_atwinc15x0_sta_get_current_ssid(const atwinc15x0_t *dev) {
|
||||
(void)dev;
|
||||
#if IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT)
|
||||
return dev->ssid;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ATWINC15x0 device driver ISR
|
||||
*/
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "bsp/include/nm_bsp.h"
|
||||
#include "net/ethernet.h"
|
||||
#include "net/wifi.h"
|
||||
#include "net/netdev.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "periph/spi.h"
|
||||
@ -41,6 +42,14 @@ extern "C" {
|
||||
#define CONFIG_ATWINC15X0_RECV_BCAST (1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum number of supported entries in a scan result of
|
||||
* an ATWINC15x0 transceiver
|
||||
*/
|
||||
#ifndef CONFIG_ATWINC15X0_SCAN_LIST_NUMOF
|
||||
#define CONFIG_ATWINC15X0_SCAN_LIST_NUMOF (3)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ATWINC15x0 hardware and global parameters
|
||||
*/
|
||||
@ -54,16 +63,32 @@ typedef struct {
|
||||
gpio_t wake_pin; /**< WAKE pin */
|
||||
} atwinc15x0_params_t;
|
||||
|
||||
/**
|
||||
* @brief ATWINC15x0 internal states
|
||||
*/
|
||||
typedef enum {
|
||||
ATWINC15X0_STATE_SLEEP, /**< Sleep state */
|
||||
ATWINC15X0_STATE_DISCONNECTING, /**< Disconnect received when connected before */
|
||||
ATWINC15X0_STATE_DISCONNECTED, /**< Disconnect state */
|
||||
ATWINC15X0_STATE_DISCONNECTED_SCANNING, /**< Scanning state when disconnected */
|
||||
ATWINC15X0_STATE_DISCONNECTED_CONNECTING, /**< Connecting state where disconnected before */
|
||||
ATWINC15X0_STATE_CONNECTED, /**< Connected state */
|
||||
ATWINC15X0_STATE_CONNECTED_SCANNING, /**< Scanning state when connected */
|
||||
ATWINC15X0_STATE_CONNECTED_CONNECTING, /**< Connecting state where disconnect
|
||||
event is not yet received */
|
||||
} atwinc15x0_state_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 */
|
||||
netopt_state_t state; /**< Current interface state, only sleep or idle */
|
||||
atwinc15x0_state_t state; /**< Device state */
|
||||
char ap[ETHERNET_ADDR_LEN]; /**< BSSID of current AP */
|
||||
#if IS_USED(MODULE_ATWINC15X0_DYNAMIC_CONNECT) || defined(DOXYGEN)
|
||||
char ssid[WIFI_SSID_LEN_MAX + 1]; /**< SSID of current AP */
|
||||
#endif
|
||||
uint8_t channel; /**< Channel used for current AP */
|
||||
int8_t rssi; /**< RSSI last measured by the WiFi module */
|
||||
|
||||
|
209
drivers/include/net/netdev/wifi.h
Normal file
209
drivers/include/net/netdev/wifi.h
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Fabian Hüßler ML!PA Consulting GmbH
|
||||
*
|
||||
* 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_netdev_wifi Wi-Fi drivers
|
||||
* @ingroup drivers_netdev_api
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definitions for netdev common Wi-Fi code
|
||||
*
|
||||
* @author Fabian Hüßler <fabian.huessler@ml-pa.com>
|
||||
*/
|
||||
|
||||
#ifndef NET_NETDEV_WIFI_H
|
||||
#define NET_NETDEV_WIFI_H
|
||||
|
||||
#include "net/ethernet/hdr.h"
|
||||
#include "net/netopt.h"
|
||||
#include "net/wifi.h"
|
||||
#include "net/l2scan_list.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A Wi-Fi scan request
|
||||
*/
|
||||
typedef struct wifi_scan_request {
|
||||
netopt_scan_request_t base; /**< Basic scan request */
|
||||
uint16_t timeout_ms_per_ch; /**< timeout for each channel to scan */
|
||||
} wifi_scan_request_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref wifi_scan_request_t
|
||||
*
|
||||
* @param ch Channel to scan or @ref NETOPT_SCAN_REQ_ALL_CH
|
||||
* @param cb Callback on scan result @ref wifi_on_scan_result_t
|
||||
* @param ms Timeout for each channel to scan
|
||||
*/
|
||||
#define WIFI_SCAN_REQUEST_INITIALIZER(ch, cb, ms) \
|
||||
(wifi_scan_request_t) { \
|
||||
.base = NETOPT_SCAN_REQUEST_INITIALIZER(ch, cb), \
|
||||
.timeout_ms_per_ch = ms, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A Wi-Fi scan result
|
||||
*/
|
||||
typedef struct wifi_scan_result {
|
||||
netopt_scan_result_t base; /**< Basic scan result */
|
||||
wifi_security_mode_t sec_mode; /**< Security mode of AP */
|
||||
uint8_t bssid[ETHERNET_ADDR_LEN]; /**< BSSID of AP */
|
||||
char ssid[WIFI_SSID_LEN_MAX + 1]; /**< SSID of AP */
|
||||
} wifi_scan_result_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref wifi_scan_result_t
|
||||
*
|
||||
* @param ch Channel that was scanned
|
||||
* @param str Received signal strength
|
||||
* @param sec AP security mode
|
||||
*/
|
||||
#define WIFI_SCAN_RESULT_INITIALIZER(ch, str, sec) \
|
||||
(wifi_scan_result_t) { \
|
||||
.base = NETOPT_SCAN_RESULT_INITIALIZER(ch, str), \
|
||||
.sec_mode = sec, \
|
||||
.bssid = { 0 }, \
|
||||
.ssid = { 0 }, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A Wi-Fi scan list
|
||||
*/
|
||||
typedef l2scan_list_t wifi_scan_list_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref wifi_scan_list_t
|
||||
*
|
||||
* @param list Pointer to first list node
|
||||
*/
|
||||
#define WIFI_SCAN_LIST_INITIALIZER(list) \
|
||||
(wifi_scan_list_t) { \
|
||||
.head = (list_node_t *)list, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Node type in a wifi scan list @ref wifi_scan_list_t
|
||||
*/
|
||||
typedef struct wifi_scan_list_node {
|
||||
list_node_t node; /**< Basic list node */
|
||||
wifi_scan_result_t result; /**< Wi-Fi scan result */
|
||||
} wifi_scan_list_node_t;
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi scan result callback prototype
|
||||
*/
|
||||
typedef void (*wifi_on_scan_result_t) (void *netif, const wifi_scan_list_t *res);
|
||||
|
||||
/**
|
||||
* @brief A Wi-Fi connect request
|
||||
*/
|
||||
typedef struct wifi_connect_request {
|
||||
netopt_connect_request_t base; /**< Basic connect request */
|
||||
const wifi_security_mode_t *cred; /**< Pointer to credentials */
|
||||
char ssid[WIFI_SSID_LEN_MAX + 1]; /**< SSID of AP to connect to */
|
||||
} wifi_connect_request_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref wifi_connect_request_t
|
||||
*
|
||||
* @param ch Channel to connect to
|
||||
* @param ccb On connect callback
|
||||
* @param dcb On disconnect callback
|
||||
* @param cr Pointer to credentials
|
||||
*/
|
||||
#define WIFI_CONNECT_REQUEST_INITIALIZER(ch, ccb, dcb, cr) \
|
||||
(wifi_connect_request_t) { \
|
||||
.base = NETOPT_CONNECT_REQUEST_INITIALIZER(ch, ccb, dcb), \
|
||||
.cred = cr, \
|
||||
.ssid = { 0 }, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A Wi-Fi connect result
|
||||
*/
|
||||
typedef struct wifi_connect_result {
|
||||
netopt_connect_result_t base; /**< Basic connect result */
|
||||
const char *ssid; /**< SSID of currently connected AP */
|
||||
union {
|
||||
wifi_security_mode_t sec; /**< WiFi security mode */
|
||||
wifi_security_wpa_psk_t wpa_psk; /**< WPA2 PSK security mode*/
|
||||
wifi_security_wep_psk_t wep; /**< WEP security mode */
|
||||
wifi_security_wpa_enterprise_t wpa_enterprise; /**< WPA2 enterprise security mode */
|
||||
} credentials; /**< Credentials */
|
||||
} wifi_connect_result_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref wifi_connect_result_t
|
||||
*
|
||||
* @param ch Channel of the connected AP
|
||||
* @param p_ssid SSID of connected AP
|
||||
*/
|
||||
#define WIFI_CONNECT_RESULT_INITIALIZER(ch, p_ssid) \
|
||||
(wifi_connect_result_t) { \
|
||||
.base = NETOPT_CONNECT_RESULT_INITIALIZER(ch), \
|
||||
.ssid = p_ssid, \
|
||||
.credentials = { .sec = WIFI_SECURITY_MODE_OPEN }, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi connect result callback prototype
|
||||
*/
|
||||
typedef void (*wifi_on_connect_result_t) (void *netif, const wifi_connect_result_t *res);
|
||||
|
||||
/**
|
||||
* @brief A Wi-Fi disconnect request
|
||||
*/
|
||||
typedef struct wifi_disconnect_request {
|
||||
netopt_disconnect_request_t base; /**< Basic disconnect request */
|
||||
} wifi_disconnect_request_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref wifi_disconnect_request_t
|
||||
*
|
||||
* @param dcb On disconnect callback
|
||||
*/
|
||||
#define WIFI_DISCONNECT_REQUEST_INITIALIZER(dcb) \
|
||||
(wifi_disconnect_request_t) { \
|
||||
.base = NETOPT_DISCONNECT_REQUEST_INITIALIZER(dcb), \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A Wi-Fi disconnect result
|
||||
*/
|
||||
typedef struct wifi_disconnect_result {
|
||||
netopt_disconnect_result_t base; /**< Basic disconnect result */
|
||||
const char *ssid; /**< SSID of the AP no longer connected to */
|
||||
} wifi_disconnect_result_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref wifi_disconnect_result_t
|
||||
*
|
||||
* @param ch Channel of no longer connected AP
|
||||
* @param p_ssid SSID of no longer connected AP
|
||||
*/
|
||||
#define WIFI_DISCONNECT_RESULT_INITIALIZER(ch, p_ssid) \
|
||||
(wifi_disconnect_result_t) { \
|
||||
.base = NETOPT_DISCONNECT_RESULT_INITIALIZER(ch), \
|
||||
.ssid = p_ssid, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi disconnect result callback prototype
|
||||
*/
|
||||
typedef void (*wifi_on_disconnect_result_t) (void *netif, const wifi_disconnect_result_t *res);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_NETDEV_WIFI_H */
|
||||
/** @} */
|
@ -10,8 +10,6 @@ ifeq (,$(filter native,$(BOARD)))
|
||||
ifneq (ble, $(DOWNLINK))
|
||||
USEMODULE += esp_now
|
||||
endif
|
||||
else
|
||||
$(error Only esp32 and esp8266 are currently supported)
|
||||
endif
|
||||
endif
|
||||
ifeq (ble, $(DOWNLINK))
|
||||
|
@ -472,6 +472,7 @@ PSEUDOMODULES += shell_cmd_gnrc_sixlowpan_frag_stats
|
||||
PSEUDOMODULES += shell_cmd_gnrc_udp
|
||||
PSEUDOMODULES += shell_cmd_heap
|
||||
PSEUDOMODULES += shell_cmd_i2c_scan
|
||||
PSEUDOMODULES += shell_cmd_iw
|
||||
PSEUDOMODULES += shell_cmd_lwip_netif
|
||||
PSEUDOMODULES += shell_cmd_mci
|
||||
PSEUDOMODULES += shell_cmd_md5sum
|
||||
@ -580,6 +581,7 @@ PSEUDOMODULES += vfs_auto_mount
|
||||
PSEUDOMODULES += vfs_default
|
||||
|
||||
PSEUDOMODULES += wakaama_objects_%
|
||||
PSEUDOMODULES += wifi_scan_list
|
||||
PSEUDOMODULES += wifi_enterprise
|
||||
PSEUDOMODULES += xtimer_on_ztimer
|
||||
PSEUDOMODULES += xtimer_no_ztimer_default
|
||||
|
@ -101,6 +101,9 @@ endif
|
||||
ifneq (,$(filter l2filter,$(USEMODULE)))
|
||||
DIRS += net/link_layer/l2filter
|
||||
endif
|
||||
ifneq (,$(filter l2scan_list,$(USEMODULE)))
|
||||
DIRS += net/link_layer/l2scan_list
|
||||
endif
|
||||
ifneq (,$(filter l2util,$(USEMODULE)))
|
||||
DIRS += net/link_layer/l2util
|
||||
endif
|
||||
|
@ -694,4 +694,8 @@ ifneq (,$(filter auto_init%,$(USEMODULE)))
|
||||
USEMODULE += preprocessor_successor
|
||||
endif
|
||||
|
||||
ifneq (,$(filter wifi_scan_list,$(USEMODULE)))
|
||||
USEMODULE += l2scan_list
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/sys/test_utils/Makefile.dep
|
||||
|
@ -48,12 +48,46 @@ extern "C" {
|
||||
#define COAP_OPT_IF_NONE_MATCH (5)
|
||||
#define COAP_OPT_OBSERVE (6)
|
||||
#define COAP_OPT_LOCATION_PATH (8)
|
||||
/**
|
||||
* @brief OSCORE option
|
||||
*
|
||||
* Indicates that the CoAP message is an OSCORE message and that it contains a
|
||||
* compressed COSE object.
|
||||
*
|
||||
* @see [RFC 8613](https://datatracker.ietf.org/doc/html/rfc8613)
|
||||
*/
|
||||
#define COAP_OPT_OSCORE (9)
|
||||
#define COAP_OPT_URI_PATH (11)
|
||||
#define COAP_OPT_CONTENT_FORMAT (12)
|
||||
#define COAP_OPT_MAX_AGE (14)
|
||||
#define COAP_OPT_URI_QUERY (15)
|
||||
/**
|
||||
* @brief Hop-Limit option
|
||||
*
|
||||
* Used to prevent infinite loops when communicating over multiple proxies.
|
||||
*
|
||||
* @see [RFC 8768](https://datatracker.ietf.org/doc/html/rfc8768)
|
||||
*/
|
||||
#define COAP_OPT_HOP_LIMIT (16)
|
||||
#define COAP_OPT_ACCEPT (17)
|
||||
/**
|
||||
* @brief Q-Block1 option
|
||||
*
|
||||
* Used for block-wise transfer supporting robust transmission in requests.
|
||||
*
|
||||
* @see [RFC 9177](https://datatracker.ietf.org/doc/html/rfc9177)
|
||||
*/
|
||||
#define COAP_OPT_Q_BLOCK1 (19)
|
||||
#define COAP_OPT_LOCATION_QUERY (20)
|
||||
/**
|
||||
* @brief EDHOC option
|
||||
*
|
||||
* Used in a CoAP request to signal that the request payload conveys both an
|
||||
* EDHOC message_3 and OSCORE protected data, combined together.
|
||||
*
|
||||
* @see [draft-ietf-core-oscore-edhoc-02](https://datatracker.ietf.org/doc/draft-ietf-core-oscore-edhoc/02/)
|
||||
*/
|
||||
#define COAP_OPT_EDHOC (21)
|
||||
#define COAP_OPT_BLOCK2 (23)
|
||||
#define COAP_OPT_BLOCK1 (27)
|
||||
/**
|
||||
@ -67,6 +101,14 @@ extern "C" {
|
||||
* @see [RFC 8613](https://datatracker.ietf.org/doc/html/rfc8613)
|
||||
*/
|
||||
#define COAP_OPT_SIZE2 (28)
|
||||
/**
|
||||
* @brief Q-Block2 option
|
||||
*
|
||||
* Used for block-wise transfer supporting robust transmission in responses.
|
||||
*
|
||||
* @see [RFC 9177](https://datatracker.ietf.org/doc/html/rfc9177)
|
||||
*/
|
||||
#define COAP_OPT_Q_BLOCK2 (31)
|
||||
#define COAP_OPT_PROXY_URI (35)
|
||||
#define COAP_OPT_PROXY_SCHEME (39)
|
||||
/**
|
||||
@ -82,11 +124,29 @@ extern "C" {
|
||||
* @see [RFC 8613](https://datatracker.ietf.org/doc/html/rfc8613)
|
||||
*/
|
||||
#define COAP_OPT_SIZE1 (60)
|
||||
/**
|
||||
* @brief Echo option
|
||||
*
|
||||
* Enables a CoAP server to verify the freshness of a request or to force a
|
||||
* client to demonstrate reachability at its claimed network address.
|
||||
*
|
||||
* @see [RFC 9175](https://datatracker.ietf.org/doc/html/rfc9175)
|
||||
*/
|
||||
#define COAP_OPT_ECHO (252)
|
||||
/**
|
||||
* @brief suppress CoAP response
|
||||
* @see [RFC 7968](https://datatracker.ietf.org/doc/html/rfc7967)
|
||||
*/
|
||||
#define COAP_OPT_NO_RESPONSE (258)
|
||||
/**
|
||||
* @brief Request-Tag option
|
||||
*
|
||||
* Allows a CoAP server to match block-wise message fragments belonging to the
|
||||
* same request.
|
||||
*
|
||||
* @see [RFC 9175](https://datatracker.ietf.org/doc/html/rfc9175)
|
||||
*/
|
||||
#define COAP_OPT_REQUEST_TAG (292)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -177,6 +237,41 @@ extern "C" {
|
||||
* @{
|
||||
*/
|
||||
#define COAP_FORMAT_TEXT (0)
|
||||
/**
|
||||
* @brief Content-Type `application/cose; cose-type="cose-encrypt0"`
|
||||
* @see [RFC 9052](https://datatracker.ietf.org/doc/html/rfc9052)
|
||||
*/
|
||||
#define COAP_FORMAT_COSE_ENCRYPT0 (16)
|
||||
/**
|
||||
* @brief Content-Type `application/cose; cose-type="cose-mac0"`
|
||||
* @see [RFC 9052](https://datatracker.ietf.org/doc/html/rfc9052)
|
||||
*/
|
||||
#define COAP_FORMAT_COSE_MAC0 (17)
|
||||
/**
|
||||
* @brief Content-Type `application/cose; cose-type="cose-sign1"`
|
||||
* @see [RFC 9052](https://datatracker.ietf.org/doc/html/rfc9052)
|
||||
*/
|
||||
#define COAP_FORMAT_COSE_SIGN1 (18)
|
||||
/**
|
||||
* @brief Content-Type `application/ace+cbor`
|
||||
* @see [RFC 9200](https://datatracker.ietf.org/doc/html/rfc9200)
|
||||
*/
|
||||
#define COAP_FORMAT_ACE_CBOR (19)
|
||||
/**
|
||||
* @brief Content-Type `image/gif`
|
||||
* @see https://www.w3.org/Graphics/GIF/spec-gif89a.txt
|
||||
*/
|
||||
#define COAP_FORMAT_IMAGE_GIF (21)
|
||||
/**
|
||||
* @brief Content-Type `image/jpeg`
|
||||
* @see [ISO/IEC 10918-5](https://www.itu.int/rec/T-REC-T.871-201105-I/en)
|
||||
*/
|
||||
#define COAP_FORMAT_IMAGE_JPEG (22)
|
||||
/**
|
||||
* @brief Content-Type `image/png`
|
||||
* @see [RFC 2083](https://datatracker.ietf.org/doc/html/rfc2083)
|
||||
*/
|
||||
#define COAP_FORMAT_IMAGE_PNG (23)
|
||||
#define COAP_FORMAT_LINK (40)
|
||||
#define COAP_FORMAT_XML (41)
|
||||
#define COAP_FORMAT_OCTET (42)
|
||||
@ -185,14 +280,226 @@ extern "C" {
|
||||
#define COAP_FORMAT_JSON_PATCH_JSON (51)
|
||||
#define COAP_FORMAT_MERGE_PATCH_JSON (52)
|
||||
#define COAP_FORMAT_CBOR (60)
|
||||
/**
|
||||
* @brief Content-Type `application/cwt`
|
||||
* @see [RFC 8392](https://datatracker.ietf.org/doc/html/rfc8392)
|
||||
*/
|
||||
#define COAP_FORMAT_CWT (61)
|
||||
/**
|
||||
* @brief Content-Type `application/multipart-core`
|
||||
* @see [RFC 8710](https://datatracker.ietf.org/doc/html/rfc8710)
|
||||
*/
|
||||
#define COAP_FORMAT_MULTIPART_CORE (62)
|
||||
/**
|
||||
* @brief Content-Type `application/cbor-seq`
|
||||
* @see [RFC 8742](https://datatracker.ietf.org/doc/html/rfc8742)
|
||||
*/
|
||||
#define COAP_FORMAT_CBOR_SEQ (63)
|
||||
/**
|
||||
* @brief Content-Type `application/cose; cose-type="cose-encrypt"`
|
||||
* @see [RFC 9052](https://datatracker.ietf.org/doc/html/rfc9052)
|
||||
*/
|
||||
#define COAP_FORMAT_COSE_ENCRYPT (96)
|
||||
/**
|
||||
* @brief Content-Type `application/cose; cose-type="cose-mac"`
|
||||
* @see [RFC 9052](https://datatracker.ietf.org/doc/html/rfc9052)
|
||||
*/
|
||||
#define COAP_FORMAT_COSE_MAC (97)
|
||||
/**
|
||||
* @brief Content-Type `application/cose; cose-type="cose-sign"`
|
||||
* @see [RFC 9052](https://datatracker.ietf.org/doc/html/rfc9052)
|
||||
*/
|
||||
#define COAP_FORMAT_COSE_SIGN (98)
|
||||
/**
|
||||
* @brief Content-Type `application/cose-key`
|
||||
* @see [RFC 9052](https://datatracker.ietf.org/doc/html/rfc9052)
|
||||
*/
|
||||
#define COAP_FORMAT_COSE_KEY (101)
|
||||
/**
|
||||
* @brief Content-Type `application/cose-key-set`
|
||||
* @see [RFC 9052](https://datatracker.ietf.org/doc/html/rfc9052)
|
||||
*/
|
||||
#define COAP_FORMAT_COSE_KEY_SET (102)
|
||||
#define COAP_FORMAT_SENML_JSON (110)
|
||||
#define COAP_FORMAT_SENSML_JSON (111)
|
||||
#define COAP_FORMAT_SENML_CBOR (112)
|
||||
#define COAP_FORMAT_SENSML_CBOR (113)
|
||||
#define COAP_FORMAT_SENML_EXI (114)
|
||||
#define COAP_FORMAT_SENSML_EXI (115)
|
||||
/**
|
||||
* @brief Content-Type `application/yang-data+cbor; id=sid`
|
||||
* @see [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254)
|
||||
*/
|
||||
#define COAP_FORMAT_YANG_DATA_CBOR_SID (140)
|
||||
/**
|
||||
* @brief Content-Type `application/coap-group+json`
|
||||
* @see [RFC 7390](https://datatracker.ietf.org/doc/html/rfc7390)
|
||||
*/
|
||||
#define COAP_FORMAT_COAP_GROUP_JSON (256)
|
||||
/**
|
||||
* @brief Content-Type `application/concise-problem-details+cbor`
|
||||
* @see [RFC 9290](https://datatracker.ietf.org/doc/html/rfc9290)
|
||||
*/
|
||||
#define COAP_FORMAT_PROBLEM_DETAILS_CBOR (257)
|
||||
/**
|
||||
* @brief Content-Type `application/swid+cbor`
|
||||
* @see [RFC 9393](https://datatracker.ietf.org/doc/html/rfc9393)
|
||||
*/
|
||||
#define COAP_FORMAT_SWID_CBOR (258)
|
||||
/**
|
||||
* @brief Content-Type `application/pkixcmp`
|
||||
* @see [draft-ietf-ace-cmpv2-coap-transport](https://datatracker.ietf.org/doc/draft-ietf-ace-cmpv2-coap-transport/)
|
||||
* @see [RFC 4210](https://datatracker.ietf.org/doc/html/rfc4210)
|
||||
*/
|
||||
#define COAP_FORMAT_PKIXCMP (259)
|
||||
/**
|
||||
* @brief Content-Type `application/dots+cbor`
|
||||
* @see [RFC 9132](https://datatracker.ietf.org/doc/html/rfc9132)
|
||||
*/
|
||||
#define COAP_FORMAT_DOTS_CBOR (271)
|
||||
/**
|
||||
* @brief Content-Type `application/missing-blocks+cbor-seq`
|
||||
* @see [RFC 9177](https://datatracker.ietf.org/doc/html/rfc9177)
|
||||
*/
|
||||
#define COAP_FORMAT_MISSING_BLOCKS_CBOR_SEQ (272)
|
||||
/**
|
||||
* @brief Content-Type `application/pkcs7-mime; smime-type=server-generated-key`
|
||||
* @see [RFC 7030](https://datatracker.ietf.org/doc/html/rfc7030)
|
||||
* @see [RFC 8551](https://datatracker.ietf.org/doc/html/rfc8551)
|
||||
* @see [RFC 9148](https://datatracker.ietf.org/doc/html/rfc9148)
|
||||
*/
|
||||
#define COAP_FORMAT_PKCS7_MIME_SERVER_GEN (280)
|
||||
/**
|
||||
* @brief Content-Type `application/pkcs7-mime; smime-type=certs-only`
|
||||
* @see [RFC 8551](https://datatracker.ietf.org/doc/html/rfc8551)
|
||||
* @see [RFC 9148](https://datatracker.ietf.org/doc/html/rfc9148)
|
||||
*/
|
||||
#define COAP_FORMAT_PKCS7_MIME_CERTS_ONLY (281)
|
||||
/**
|
||||
* @brief Content-Type `application/pkcs8`
|
||||
* @see [RFC 5958](https://datatracker.ietf.org/doc/html/rfc5958)
|
||||
* @see [RFC 8551](https://datatracker.ietf.org/doc/html/rfc8551)
|
||||
* @see [RFC 9148](https://datatracker.ietf.org/doc/html/rfc9148)
|
||||
*/
|
||||
#define COAP_FORMAT_PKCS8 (284)
|
||||
/**
|
||||
* @brief Content-Type `application/csrattrs`
|
||||
* @see [RFC 7030](https://datatracker.ietf.org/doc/html/rfc7030)
|
||||
* @see [RFC 9148](https://datatracker.ietf.org/doc/html/rfc9148)
|
||||
*/
|
||||
#define COAP_FORMAT_CSRATTRS (285)
|
||||
/**
|
||||
* @brief Content-Type `application/pkcs10`
|
||||
* @see [RFC 5967](https://datatracker.ietf.org/doc/html/rfc5967)
|
||||
* @see [RFC 8551](https://datatracker.ietf.org/doc/html/rfc8551)
|
||||
* @see [RFC 9148](https://datatracker.ietf.org/doc/html/rfc9148)
|
||||
*/
|
||||
#define COAP_FORMAT_PKCS10 (286)
|
||||
/**
|
||||
* @brief Content-Type `application/pkix-cert`
|
||||
* @see [RFC 2585](https://datatracker.ietf.org/doc/html/rfc2585)
|
||||
* @see [RFC 9148](https://datatracker.ietf.org/doc/html/rfc9148)
|
||||
*/
|
||||
#define COAP_FORMAT_PKIX_CERT (287)
|
||||
/**
|
||||
* @brief Content-Type `application/aif+cbor`
|
||||
* @see [RFC 9237](https://datatracker.ietf.org/doc/html/rfc9237)
|
||||
*/
|
||||
#define COAP_FORMAT_AIF_CBOR (290)
|
||||
/**
|
||||
* @brief Content-Type `application/aif+json`
|
||||
* @see [RFC 9237](https://datatracker.ietf.org/doc/html/rfc9237)
|
||||
*/
|
||||
#define COAP_FORMAT_AIF_JSON (291)
|
||||
#define COAP_FORMAT_SENML_XML (310)
|
||||
#define COAP_FORMAT_SENSML_XML (311)
|
||||
/**
|
||||
* @brief Content-Type `application/senml-etch+json`
|
||||
* @see [RFC 8790](https://datatracker.ietf.org/doc/html/rfc8790)
|
||||
*/
|
||||
#define COAP_FORMAT_SNML_ETCH_JSON (320)
|
||||
/**
|
||||
* @brief Content-Type `application/senml-etch+cbor`
|
||||
* @see [RFC 8790](https://datatracker.ietf.org/doc/html/rfc8790)
|
||||
*/
|
||||
#define COAP_FORMAT_SNML_ETCH_CBOR (322)
|
||||
/**
|
||||
* @brief Content-Type `application/yang-data+cbor`
|
||||
* @see [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254)
|
||||
*/
|
||||
#define COAP_FORMAT_YAML_DATA_CBOR (340)
|
||||
/**
|
||||
* @brief Content-Type `application/yang-data+cbor; id=name`
|
||||
* @see [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254)
|
||||
*/
|
||||
#define COAP_FORMAT_YAML_DATA_CBOR_ID_NAME (341)
|
||||
/**
|
||||
* @brief Content-Type `application/td+json`
|
||||
* @see [Web of Things (WoT) Thing Description 1.1](https://www.w3.org/TR/wot-thing-description11/)
|
||||
*/
|
||||
#define COAP_FORMAT_TD_JSON (432)
|
||||
/**
|
||||
* @brief Content-Type `application/tm+json`
|
||||
* @see [Web of Things (WoT) Thing Description 1.1](https://www.w3.org/TR/wot-thing-description11/)
|
||||
*/
|
||||
#define COAP_FORMAT_TM_JSON (433)
|
||||
/**
|
||||
* @brief Content-Type `application/voucher-cose+cbor`
|
||||
* @see [draft-ietf-anima-constrained-voucher](https://datatracker.ietf.org/doc/draft-ietf-anima-constrained-voucher/)
|
||||
* @note Temporary registration until April 12, 2024.
|
||||
*/
|
||||
#define COAP_FORMAT_VOUCER_COSE_CBOR (836)
|
||||
/**
|
||||
* @brief Content-Type `application/vnd.ocf+cbor`
|
||||
*/
|
||||
#define COAP_FORMAT_VND_OCF_CBOR (10000)
|
||||
/**
|
||||
* @brief Content-Type `application/oscore`
|
||||
* @see [RFC 8613](https://datatracker.ietf.org/doc/html/rfc8613)
|
||||
*/
|
||||
#define COAP_FORMAT_OSCORE (10001)
|
||||
/**
|
||||
* @brief Content-Type `application/javascript`
|
||||
* @see [RFC 4329](https://datatracker.ietf.org/doc/html/rfc4329)
|
||||
*/
|
||||
#define COAP_FORMAT_JAVASCRIPT (10002)
|
||||
/**
|
||||
* @brief Content-Type `application/json` with Content Coding `deflate`
|
||||
* @see [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259)
|
||||
* @see [RFC 9110, Section 8.4.1.2](https://datatracker.ietf.org/doc/html/rfc9110)
|
||||
*/
|
||||
#define COAP_FORMAT_JSON_DEFLATE (11050)
|
||||
/**
|
||||
* @brief Content-Type `application/cbor` with Content Coding `deflate`
|
||||
* @see [RFC 8949](https://datatracker.ietf.org/doc/html/rfc8949)
|
||||
* @see [RFC 9110, Section 8.4.1.2](https://datatracker.ietf.org/doc/html/rfc9110)
|
||||
*/
|
||||
#define COAP_FORMAT_CBOR_DEFLATE (11060)
|
||||
/**
|
||||
* @brief Content-Type `application/vnd.oma.lwm2m+tlv`
|
||||
* @see [OMA-TS-LightweightM2M-V1_0](https://www.openmobilealliance.org/release/LightweightM2M/V1_0-20170208-A/OMA-TS-LightweightM2M-V1_0-20170208-A.pdf)
|
||||
*/
|
||||
#define COAP_FORMAT_VND_OMA_LWM2M_TLV (11542)
|
||||
/**
|
||||
* @brief Content-Type `application/vnd.oma.lwm2m+json`
|
||||
* @see [OMA-TS-LightweightM2M-V1_0](https://www.openmobilealliance.org/release/LightweightM2M/V1_0-20170208-A/OMA-TS-LightweightM2M-V1_0-20170208-A.pdf)
|
||||
*/
|
||||
#define COAP_FORMAT_VND_OMA_LWM2M_JSON (11543)
|
||||
/**
|
||||
* @brief Content-Type `application/vnd.oma.lwm2m+cbor`
|
||||
* @see [OMA-TS-LightweightM2M-V1_2](https://www.openmobilealliance.org/release/LightweightM2M/V1_2-20201110-A/HTML-Version/OMA-TS-LightweightM2M_Core-V1_2-20201110-A.html)
|
||||
*/
|
||||
#define COAP_FORMAT_VND_OMA_LWM2M_CBOR (11544)
|
||||
/**
|
||||
* @brief Content-Type `text/css`
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc2318
|
||||
*/
|
||||
#define COAP_FORMAT_TEXT_CSS (20000)
|
||||
/**
|
||||
* @brief Content-Type `image/svg+xml`
|
||||
* @see [RFC 2318](https://www.w3.org/TR/SVG/mimereg.html)
|
||||
*/
|
||||
#define COAP_FORMAT_IMAGE_SVG_XML (30000)
|
||||
#define COAP_FORMAT_DNS_MESSAGE (65053) /**< NON STANDARD! */
|
||||
/** @} */
|
||||
|
||||
|
87
sys/include/net/l2scan_list.h
Normal file
87
sys/include/net/l2scan_list.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2023 ML!PA Consulting Gmbh
|
||||
*
|
||||
* 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 net_l2scanlist Scan List - List of wireless access points
|
||||
* @ingroup net
|
||||
* @brief Internal list data structure of scanned access points
|
||||
* @ref NETOPT_SCAN
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief L2 Scan list API
|
||||
*
|
||||
* @author Fabian Hüßler <fabian.huessler@ml-pa.com>
|
||||
*/
|
||||
|
||||
#ifndef NET_L2SCAN_LIST_H
|
||||
#define NET_L2SCAN_LIST_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "net/netopt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of a Link Layer scan list
|
||||
*/
|
||||
typedef struct l2scan_list {
|
||||
list_node_t head; /**< List head, where head->next is the first element */
|
||||
/* items */
|
||||
} l2scan_list_t;
|
||||
|
||||
/**
|
||||
* @brief Empty the list to start a new scan
|
||||
*
|
||||
* @param[in, out] list Pointer to list
|
||||
* @param[in, out] nodes Pointer to nodes array
|
||||
* @param[in] nodes_numof Number of nodes in the array
|
||||
* @param[in] node_size Size of one node element in the array
|
||||
*/
|
||||
void l2scan_list_empty(l2scan_list_t *list,
|
||||
list_node_t *nodes, unsigned nodes_numof,
|
||||
size_t node_size);
|
||||
|
||||
/**
|
||||
* @brief Insert a new scan result into the list
|
||||
*
|
||||
* @param[in, out] list Pointer to list
|
||||
* @param[in, out] nodes Pointer to nodes array
|
||||
* @param[in] nodes_numof Number of nodes in the array
|
||||
* @param[in] node_size Size of one node element in the array
|
||||
* @param[in] result New result to insert
|
||||
*/
|
||||
void l2scan_list_insert(l2scan_list_t *list,
|
||||
list_node_t *nodes, unsigned nodes_numof,
|
||||
size_t node_size,
|
||||
const netopt_scan_result_t *result);
|
||||
|
||||
/**
|
||||
* @brief Copy the content of a L2 scan list to an array to get rid of the list overhead
|
||||
*
|
||||
* @param[in] list Pointer to list
|
||||
* @param[out] nodes_array Buffer of nodes to store the result
|
||||
* @param[in] nodes_numof Maximum number of nodes that can be copied
|
||||
* @param[in] node_size Size of one node element in the array
|
||||
*
|
||||
* @return Number of copied nodes
|
||||
*/
|
||||
unsigned l2scan_list_to_array(const l2scan_list_t *list,
|
||||
void *nodes_array, unsigned nodes_numof,
|
||||
size_t node_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_L2SCAN_LIST_H */
|
||||
/** @} */
|
@ -25,6 +25,9 @@
|
||||
#ifndef NET_NETOPT_H
|
||||
#define NET_NETOPT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -783,6 +786,35 @@ typedef enum {
|
||||
*/
|
||||
NETOPT_RSSI,
|
||||
|
||||
/**
|
||||
* @brief (@ref netopt_scan_request_t) Instruct the interface to do a network scan
|
||||
*
|
||||
* This netopt triggers an asynchronous network scan.
|
||||
* The result is a list of reachable access points @ref l2scan_list_t.
|
||||
* Notification happens by a callback @ref netopt_on_scan_result_t.
|
||||
*/
|
||||
NETOPT_SCAN,
|
||||
|
||||
/**
|
||||
* @brief (@ref netopt_connect_request_t) Instructs the interface to connect to a network
|
||||
*
|
||||
* This netopt triggers an asynchronous connection attempt to a network.
|
||||
* The result is a derivative of @ref netopt_connect_result_t or
|
||||
* @ref netopt_disconnect_result_t. Notification happens through a callback
|
||||
* @ref netopt_on_connect_result_t or @ref netopt_on_disconnect_result_t respectively.
|
||||
*/
|
||||
NETOPT_CONNECT,
|
||||
|
||||
/**
|
||||
* @brief (@ref netopt_disconnect_request_t) Instructs the interface to disconnect
|
||||
* from a network
|
||||
*
|
||||
* This netopt triggers a disconnect procedure from a network.
|
||||
* The result is a derivative of @ref netopt_disconnect_result_t.
|
||||
* Notification happens through a callback @ref netopt_on_disconnect_result_t.
|
||||
*/
|
||||
NETOPT_DISCONNECT,
|
||||
|
||||
/**
|
||||
* @brief (uint16_t) Set the battery monitor voltage (in mV).
|
||||
*
|
||||
@ -877,6 +909,157 @@ typedef enum {
|
||||
NETOPT_RF_TESTMODE_CTX_PRBS9, /**< PRBS9 continuous tx mode */
|
||||
} netopt_rf_testmode_t;
|
||||
|
||||
/**
|
||||
* @brief Netopt RF channel type
|
||||
*/
|
||||
typedef uint16_t netopt_channel_t;
|
||||
|
||||
/**
|
||||
* @brief Netopt RSSI type
|
||||
*/
|
||||
typedef int16_t netopt_rssi_t;
|
||||
|
||||
/**
|
||||
* @brief Request to scan all channels
|
||||
*/
|
||||
#define NETOPT_SCAN_REQ_ALL_CH ((netopt_channel_t)(-1))
|
||||
|
||||
/**
|
||||
* @brief Basic network scan result
|
||||
*/
|
||||
typedef struct netopt_scan_result {
|
||||
netopt_channel_t channel; /**< Scanned channel */
|
||||
netopt_rssi_t strength; /**< Received signal strength */
|
||||
} netopt_scan_result_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref netopt_scan_result_t
|
||||
*
|
||||
* @param ch Scanned channel
|
||||
* @param str Received signal strength
|
||||
*/
|
||||
#define NETOPT_SCAN_RESULT_INITIALIZER(ch, str) \
|
||||
(netopt_scan_result_t) { \
|
||||
.channel = ch, \
|
||||
.strength = str, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forward declaration of a list of network scan results
|
||||
*
|
||||
* This prevents a recursive include.
|
||||
*/
|
||||
struct l2scan_list;
|
||||
|
||||
/**
|
||||
* @brief Basic callback type on network scan @ref NETOPT_CONNECT
|
||||
*/
|
||||
typedef void (*netopt_on_scan_result_t) (void *netif, const struct l2scan_list *res);
|
||||
|
||||
/**
|
||||
* @brief Basic network scan request
|
||||
*/
|
||||
typedef struct netopt_scan_request {
|
||||
netopt_on_scan_result_t scan_cb; /**< Scan result callback */
|
||||
netopt_channel_t channel; /**< Channel to scan */
|
||||
} netopt_scan_request_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref netopt_scan_request_t
|
||||
*
|
||||
* @param ch Channel to be scanned
|
||||
* @param cb Scan result callback
|
||||
*/
|
||||
#define NETOPT_SCAN_REQUEST_INITIALIZER(ch, cb) \
|
||||
(netopt_scan_request_t) { \
|
||||
.channel = ch, \
|
||||
.scan_cb = (netopt_on_scan_result_t)cb, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Basic network connect result
|
||||
*/
|
||||
typedef struct netopt_connect_result {
|
||||
netopt_channel_t channel; /**< Connected channel */
|
||||
} netopt_connect_result_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref netopt_connect_result_t
|
||||
*
|
||||
* @param ch Connected channel
|
||||
*/
|
||||
#define NETOPT_CONNECT_RESULT_INITIALIZER(ch) \
|
||||
(netopt_connect_result_t) { \
|
||||
.channel = ch, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Basic disconnect result
|
||||
*/
|
||||
typedef struct netopt_disconnect_result {
|
||||
netopt_channel_t channel; /**< Channel of the disconnected AP */
|
||||
} netopt_disconnect_result_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref netopt_disconnect_result_t
|
||||
*
|
||||
* @param ch Channel of the disconnected AP
|
||||
*/
|
||||
#define NETOPT_DISCONNECT_RESULT_INITIALIZER(ch) \
|
||||
(netopt_disconnect_result_t) { \
|
||||
.channel = ch, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Basic callback type on network connection @ref NETOPT_CONNECT
|
||||
*/
|
||||
typedef void (*netopt_on_connect_result_t) (void *netif, const struct netopt_connect_result *res);
|
||||
|
||||
/**
|
||||
* @brief Basic callback type on network disconnection @ref NETOPT_CONNECT
|
||||
*/
|
||||
typedef void (*netopt_on_disconnect_result_t) (void *netif, const struct netopt_disconnect_result *res);
|
||||
|
||||
/**
|
||||
* @brief Basic network connect request
|
||||
*/
|
||||
typedef struct netopt_connect_request {
|
||||
netopt_on_disconnect_result_t disconn_cb; /**< On disconnect callback */
|
||||
netopt_on_connect_result_t conn_cb; /**< On connect callback */
|
||||
netopt_channel_t channel; /**< Channel of the network to connect to */
|
||||
} netopt_connect_request_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref netopt_connect_request_t
|
||||
*
|
||||
* @param ch Channel of the network to connect to
|
||||
* @param ccb On connect callback
|
||||
* @param dcb On disconnect callback
|
||||
*/
|
||||
#define NETOPT_CONNECT_REQUEST_INITIALIZER(ch, ccb, dcb) \
|
||||
(netopt_connect_request_t) { \
|
||||
.disconn_cb = (netopt_on_disconnect_result_t)dcb, \
|
||||
.conn_cb = (netopt_on_connect_result_t)ccb, \
|
||||
.channel = ch, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Basic network disconnect request
|
||||
*/
|
||||
typedef struct netopt_disconnect_request {
|
||||
netopt_on_disconnect_result_t disconn_cb; /**< On disconnect callback */
|
||||
} netopt_disconnect_request_t;
|
||||
|
||||
/**
|
||||
* @brief Static initializer for a @ref netopt_disconnect_request_t
|
||||
*
|
||||
* @param dcb On disconnect callback
|
||||
*/
|
||||
#define NETOPT_DISCONNECT_REQUEST_INITIALIZER(dcb) \
|
||||
(netopt_disconnect_request_t) { \
|
||||
.disconn_cb = (netopt_on_disconnect_result_t)dcb, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a string ptr corresponding to opt, for debugging
|
||||
*
|
||||
|
167
sys/include/net/wifi.h
Normal file
167
sys/include/net/wifi.h
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Fabian Hüßler ML!PA Consulting GmbH
|
||||
*
|
||||
* 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 net_wifi Wi-Fi
|
||||
* @ingroup net
|
||||
* @brief Provides Wi-Fi definitions
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Definitions for Wi-Fi
|
||||
*
|
||||
* @author Fabian Hüßler <fabian.huessler@ml-pa.com>
|
||||
*/
|
||||
|
||||
#ifndef NET_WIFI_H
|
||||
#define NET_WIFI_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi region Europe
|
||||
*/
|
||||
#define WIFI_REGION_EUROPE 0
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi region North America
|
||||
*/
|
||||
#define WIFI_REGION_NORTH_AMERICA 1
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi region Asia
|
||||
*/
|
||||
#define WIFI_REGION_ASIA 2
|
||||
|
||||
#if !defined(CONFIG_WIFI_REGION) || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Used to select regional Wi-Fi restrictions
|
||||
*/
|
||||
#define CONFIG_WIFI_REGION WIFI_REGION_EUROPE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Current Wi-Fi region
|
||||
*/
|
||||
#define WIFI_REGION CONFIG_WIFI_REGION
|
||||
|
||||
/**
|
||||
* @brief 2.4 GHz Channels
|
||||
*/
|
||||
typedef enum {
|
||||
WIFI_2_4_CH_1 = 1, /**< 2.4 GHz channel 1 */
|
||||
WIFI_2_4_CH_2 = 2, /**< 2.4 GHz channel 2 */
|
||||
WIFI_2_4_CH_3 = 3, /**< 2.4 GHz channel 3 */
|
||||
WIFI_2_4_CH_4 = 4, /**< 2.4 GHz channel 4 */
|
||||
WIFI_2_4_CH_5 = 5, /**< 2.4 GHz channel 5 */
|
||||
WIFI_2_4_CH_6 = 6, /**< 2.4 GHz channel 6 */
|
||||
WIFI_2_4_CH_7 = 7, /**< 2.4 GHz channel 7 */
|
||||
WIFI_2_4_CH_8 = 8, /**< 2.4 GHz channel 8 */
|
||||
WIFI_2_4_CH_9 = 9, /**< 2.4 GHz channel 9 */
|
||||
WIFI_2_4_CH_10 = 10, /**< 2.4 GHz channel 10 */
|
||||
WIFI_2_4_CH_11 = 11, /**< 2.4 GHz channel 11 */
|
||||
WIFI_2_4_CH_12 = 12, /**< 2.4 GHz channel 12 */
|
||||
WIFI_2_4_CH_13 = 13, /**< 2.4 GHz channel 13 */
|
||||
WIFI_2_4_CH_14 = 14, /**< 2.4 GHz channel 14 */
|
||||
} wifi_2_4_channel_t;
|
||||
|
||||
#if WIFI_REGION == WIFI_REGION_EUROPE
|
||||
/**
|
||||
* @brief Lowest Wi-Fi channel
|
||||
*/
|
||||
#define WIFI_2_4_CH_MIN 1
|
||||
|
||||
/**
|
||||
* @brief Highest Wi-Fi channel
|
||||
*/
|
||||
#define WIFI_2_4_CH_MAX 13
|
||||
#elif WIFI_REGION == WIFI_REGION_NORTH_AMERICA
|
||||
#define WIFI_2_4_CH_MIN 1
|
||||
#define WIFI_2_4_CH_MAX 11
|
||||
#elif WIFI_REGION == WIFI_REGION_ASIA
|
||||
#define WIFI_2_4_CH_MIN 1
|
||||
#define WIFI_2_4_CH_MAX 14
|
||||
#else
|
||||
#error "WIFI_REGION undefined"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of 2.4 GHz channels
|
||||
*/
|
||||
#define WIFI_2_4_CH_NUMOF (2 + (WIFI_2_4_CH_MAX - WIFI_2_4_CH_MIN))
|
||||
|
||||
/**
|
||||
* @brief Type to express a Wi-Fi security mode
|
||||
*/
|
||||
typedef enum {
|
||||
WIFI_SECURITY_MODE_OPEN, /**< AP is open */
|
||||
WIFI_SECURITY_MODE_WEP_PSK, /**< AP is protected with WEP */
|
||||
WIFI_SECURITY_MODE_WPA2_PERSONAL, /**< AP is protected with WPA2 Personal mode */
|
||||
WIFI_SECURITY_MODE_WPA2_ENTERPRISE, /**< AP is protected with WPA2 Enterprise mode */
|
||||
} wifi_security_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Maximum length of an SSID
|
||||
*/
|
||||
#define WIFI_SSID_LEN_MAX 32
|
||||
|
||||
/**
|
||||
* @brief Maximum length of a WPA key
|
||||
*/
|
||||
#define WIFI_WPA_PSK_LEN_MAX 63
|
||||
|
||||
/**
|
||||
* @brief Maximum length of a WEP key
|
||||
*/
|
||||
#define WIFI_WEP_PSK_LEN_MAX 16
|
||||
|
||||
/**
|
||||
* @brief Maximum username length for WPA2 Enterprise
|
||||
*/
|
||||
#define WIFI_EAP_USER_LEN_MAX 20
|
||||
|
||||
/**
|
||||
* @brief Maximum password length for WPA2 Enterprise
|
||||
*/
|
||||
#define WIFI_EAP_PWD_LEN_MAX 40
|
||||
|
||||
/**
|
||||
* @brief A WPA pre-shared-key
|
||||
*/
|
||||
typedef struct wifi_security_wpa_psk {
|
||||
wifi_security_mode_t sec; /**< @ref WIFI_SECURITY_MODE_WPA2_PERSONAL */
|
||||
char psk[WIFI_WPA_PSK_LEN_MAX + 1]; /**< Key data */
|
||||
} wifi_security_wpa_psk_t;
|
||||
|
||||
/**
|
||||
* @brief A WEP pre-shared-key
|
||||
*/
|
||||
typedef struct wifi_security_wep_psk {
|
||||
wifi_security_mode_t sec; /**< @ref WIFI_SECURITY_MODE_WEP_PSK */
|
||||
char psk[WIFI_WEP_PSK_LEN_MAX + 1]; /**< Key data */
|
||||
} wifi_security_wep_psk_t;
|
||||
|
||||
/**
|
||||
* @brief WPA2 Enterprise credentials
|
||||
*/
|
||||
typedef struct wifi_security_wpa_enterprise {
|
||||
wifi_security_mode_t sec; /**< @ref WIFI_SECURITY_MODE_WPA2_ENTERPRISE */
|
||||
char user[WIFI_EAP_USER_LEN_MAX + 1]; /**< Username */
|
||||
char pwd[WIFI_EAP_PWD_LEN_MAX + 1]; /**< Password */
|
||||
} wifi_security_wpa_enterprise_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_WIFI_H */
|
||||
/** @} */
|
86
sys/include/net/wifi_scan_list.h
Normal file
86
sys/include/net/wifi_scan_list.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2023 ML!PA Consulting GmbH
|
||||
*
|
||||
* 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 net_wifi_scan_list List of scanned WiFis access points
|
||||
* @ingroup net
|
||||
* @brief Wrapper around l2scan list for WiFi scan results
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Thin wrapper around l2scan list to support WiFi scan results
|
||||
*
|
||||
* @author Fabian Hüßler <fabian.huessler@ml-pa.com>
|
||||
*/
|
||||
|
||||
#ifndef NET_WIFI_SCAN_LIST_H
|
||||
#define NET_WIFI_SCAN_LIST_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "l2scan_list.h"
|
||||
#include "net/netdev/wifi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Empty the WiFi scan list to start a new scan
|
||||
*
|
||||
* @param[in, out] list Pointer to WiFi scan result list
|
||||
* @param[in, out] nodes Pointer to WiFi scan result nodes array
|
||||
* @param[in] nodes_numof Number of nodes in the array
|
||||
*/
|
||||
static inline void wifi_scan_list_empty(wifi_scan_list_t *list,
|
||||
wifi_scan_list_node_t *nodes,
|
||||
unsigned nodes_numof)
|
||||
{
|
||||
l2scan_list_empty(list, &nodes->node, nodes_numof,
|
||||
sizeof(wifi_scan_list_node_t));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert a new WiFi scan result into the list
|
||||
*
|
||||
* @param[in, out] list Pointer to WiFi scan result list
|
||||
* @param[in, out] nodes Pointer to WiFi scan result nodes array
|
||||
* @param[in] nodes_numof Number of nodes in the array
|
||||
* @param[in] result New WiFi scan result to insert
|
||||
*/
|
||||
static inline void wifi_scan_list_insert(wifi_scan_list_t *list,
|
||||
wifi_scan_list_node_t *nodes,
|
||||
unsigned nodes_numof,
|
||||
const wifi_scan_result_t *result)
|
||||
{
|
||||
l2scan_list_insert(list, &nodes->node, nodes_numof,
|
||||
sizeof(wifi_scan_list_node_t), &result->base);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy the content of a WiFi scan list to an array to get rid of the list overhead
|
||||
*
|
||||
* @param[in] list Pointer to list
|
||||
* @param[in] array Buffer of nodes to store the result
|
||||
* @param[in] numof Maximum number of nodes that can be copied
|
||||
*
|
||||
* @return Number of copied nodes
|
||||
*/
|
||||
static inline unsigned wifi_scan_list_to_array(const wifi_scan_list_t *list,
|
||||
wifi_scan_result_t *array,
|
||||
unsigned numof)
|
||||
{
|
||||
return l2scan_list_to_array(list, array, numof,
|
||||
sizeof(wifi_scan_list_node_t));
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_WIFI_SCAN_LIST_H */
|
||||
/** @} */
|
@ -129,6 +129,9 @@ static const char *_netopt_strmap[] = {
|
||||
[NETOPT_NUM_GATEWAYS] = "NETOPT_NUM_GATEWAYS",
|
||||
[NETOPT_LINK_CHECK] = "NETOPT_LINK_CHECK",
|
||||
[NETOPT_RSSI] = "NETOPT_RSSI",
|
||||
[NETOPT_SCAN] = "NETOPT_SCAN",
|
||||
[NETOPT_CONNECT] = "NETOPT_CONNECT",
|
||||
[NETOPT_DISCONNECT] = "NETOPT_DISCONNECT",
|
||||
[NETOPT_BATMON] = "NETOPT_BATMON",
|
||||
[NETOPT_L2_GROUP] = "NETOPT_L2_GROUP",
|
||||
[NETOPT_L2_GROUP_LEAVE] = "NETOPT_L2_GROUP_LEAVE",
|
||||
|
9
sys/net/link_layer/l2scan_list/Kconfig
Normal file
9
sys/net/link_layer/l2scan_list/Kconfig
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2023 ML!PA Consulting Gmbh
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
config MODULE_L2SCAN_LIST
|
||||
bool "List of scanned Link Layer access points"
|
1
sys/net/link_layer/l2scan_list/Makefile
Normal file
1
sys/net/link_layer/l2scan_list/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
94
sys/net/link_layer/l2scan_list/l2scan_list.c
Normal file
94
sys/net/link_layer/l2scan_list/l2scan_list.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2023 ML!PA Consulting Gmbh
|
||||
*
|
||||
* 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 net_l2scan_list
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief List to store the result of a network scan
|
||||
*
|
||||
* @author Fabian Hüßler <fabian.huessler@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "net/netopt.h"
|
||||
#include "net/l2scan_list.h"
|
||||
|
||||
#define SCAN_LIST_NODE_AT(array, pos, size) ((void *)(((uint8_t *)(array)) + ((pos) * (size))))
|
||||
|
||||
/**
|
||||
* @brief Basic type of a scan result in a list
|
||||
*/
|
||||
typedef struct scan_list_node {
|
||||
list_node_t node; /* Basic node */
|
||||
netopt_scan_result_t result; /* Basic scan result */
|
||||
} scan_list_node_t;
|
||||
|
||||
static scan_list_node_t *_scan_list_get_insert(l2scan_list_t *list,
|
||||
list_node_t *array, unsigned array_numof,
|
||||
size_t item_size)
|
||||
{
|
||||
scan_list_node_t *lowest = (scan_list_node_t *)array;
|
||||
for (unsigned i = 0; i < array_numof; i++) {
|
||||
/* look for free slot or lowest element */
|
||||
scan_list_node_t *result = SCAN_LIST_NODE_AT(array, i, item_size);
|
||||
if (!result->result.strength) {
|
||||
return result; /* free slot */
|
||||
}
|
||||
if (result->result.strength < lowest->result.strength) {
|
||||
lowest = result; /* override lowest element */
|
||||
}
|
||||
}
|
||||
list_remove(&list->head, &lowest->node);
|
||||
return lowest;
|
||||
}
|
||||
|
||||
void l2scan_list_empty(l2scan_list_t *list,
|
||||
list_node_t *nodes, unsigned nodes_numof,
|
||||
size_t node_size)
|
||||
{
|
||||
list->head.next = NULL;
|
||||
memset(nodes, 0, nodes_numof * node_size);
|
||||
}
|
||||
|
||||
void l2scan_list_insert(l2scan_list_t *list,
|
||||
list_node_t *nodes, unsigned nodes_numof,
|
||||
size_t node_size,
|
||||
const netopt_scan_result_t *result)
|
||||
{
|
||||
scan_list_node_t *insert = _scan_list_get_insert(list, nodes,
|
||||
nodes_numof, node_size);
|
||||
*insert = (scan_list_node_t) { .node = { .next = NULL }, };
|
||||
memcpy(&insert->result, result, node_size - sizeof(list_node_t));
|
||||
const scan_list_node_t *next;
|
||||
list_node_t *before = &list->head;
|
||||
while ((next = (scan_list_node_t *)before->next) &&
|
||||
next->result.strength > result->strength) {
|
||||
before = before->next;
|
||||
}
|
||||
list_add(before, &insert->node);
|
||||
}
|
||||
|
||||
unsigned l2scan_list_to_array(const l2scan_list_t *list,
|
||||
void *nodes_array, unsigned nodes_numof,
|
||||
size_t node_size)
|
||||
{
|
||||
list_node_t *node = list->head.next;
|
||||
uint8_t *buf = nodes_array;
|
||||
size_t size = node_size - sizeof(*node);
|
||||
unsigned i;
|
||||
for (i = 0; i < nodes_numof && node; i++, buf += size, node = node->next) {
|
||||
memcpy(buf, &node[1], size);
|
||||
}
|
||||
return i;
|
||||
}
|
@ -208,6 +208,9 @@ endif
|
||||
ifneq (,$(filter shell_cmd_i2c_scan,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_i2c
|
||||
endif
|
||||
ifneq (,$(filter shell_cmd_iw,$(USEMODULE)))
|
||||
USEMODULE += ztimer_sec
|
||||
endif
|
||||
ifneq (,$(filter shell_cmd_lwip_netif,$(USEMODULE)))
|
||||
USEMODULE += lwip_netif
|
||||
endif
|
||||
|
@ -176,6 +176,10 @@ config MODULE_SHELL_CMD_I2C_SCAN
|
||||
depends on MODULE_SHELL_CMDS
|
||||
depends on MODULE_PERIPH_I2C
|
||||
|
||||
config MODULE_SHELL_CMD_IW
|
||||
bool "Command to interact with WiFi interfaces"
|
||||
depends on MODULE_ZTIMER_SEC
|
||||
|
||||
config MODULE_SHELL_CMD_LWIP_NETIF
|
||||
bool "Command to manage lwIP network interfaces (ifconfig)"
|
||||
default y if MODULE_SHELL_CMDS_DEFAULT
|
||||
|
413
sys/shell/cmds/iw.c
Normal file
413
sys/shell/cmds/iw.c
Normal file
@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (C) 2023 ML!PA Consulting Gmbh
|
||||
*
|
||||
* 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 sys_shell_commands
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Shell commands for interacting with Wi-Fi interfaces
|
||||
*
|
||||
* @author Fabian Hüßler <fabian.huessler@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "mutex.h"
|
||||
#include "net/wifi_scan_list.h"
|
||||
#include "net/netdev/wifi.h"
|
||||
#include "net/netopt.h"
|
||||
#include "net/wifi.h"
|
||||
#include "sched.h"
|
||||
#include "shell.h"
|
||||
#include "net/netif.h"
|
||||
#include "net/netdev.h"
|
||||
#include "fmt.h"
|
||||
#include "string_utils.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#define SC_IW_AP_NUMOF 10
|
||||
#define SC_IW_AP_SCAN_TIMEOUT_SEC_MAX 30
|
||||
#define SC_IW_AP_CONNECT_TIMEOUT_SEC_MAX 60
|
||||
|
||||
static mutex_t _sync = MUTEX_INIT;
|
||||
|
||||
static struct {
|
||||
unsigned numof;
|
||||
wifi_scan_result_t ap[SC_IW_AP_NUMOF];
|
||||
} _aps;
|
||||
|
||||
static union {
|
||||
wifi_security_wep_psk_t wep;
|
||||
wifi_security_wpa_psk_t wpa;
|
||||
wifi_security_wpa_enterprise_t eap;
|
||||
} _cred;
|
||||
|
||||
static const wifi_scan_result_t *_get_ap(const char *ssid)
|
||||
{
|
||||
for (unsigned i = 0; i < _aps.numof; i++) {
|
||||
if (!strcmp(_aps.ap[i].ssid, ssid)) {
|
||||
return &_aps.ap[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *_ssec(wifi_security_mode_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case WIFI_SECURITY_MODE_OPEN:
|
||||
return "open";
|
||||
case WIFI_SECURITY_MODE_WEP_PSK:
|
||||
return "WEP";
|
||||
case WIFI_SECURITY_MODE_WPA2_PERSONAL:
|
||||
return "WPA";
|
||||
case WIFI_SECURITY_MODE_WPA2_ENTERPRISE:
|
||||
return "WPA2 Enterprise";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void _list_ap(void)
|
||||
{
|
||||
puts(" SSID | SEC | RSSI | CHANNEL");
|
||||
puts("---------------------------------+-----------------+-------+--------");
|
||||
for (unsigned i = 0; i < _aps.numof; i++) {
|
||||
printf(" %-31s | %-15s | %-5"PRId16" | %-6d \n",
|
||||
_aps.ap[i].ssid,
|
||||
_ssec(_aps.ap[i].sec_mode),
|
||||
_aps.ap[i].base.strength,
|
||||
_aps.ap[i].base.channel);
|
||||
}
|
||||
}
|
||||
|
||||
static void _wifi_scan_cb(void *netif, const wifi_scan_list_t *result)
|
||||
{
|
||||
(void)netif;
|
||||
_aps.numof = wifi_scan_list_to_array(result, _aps.ap, ARRAY_SIZE(_aps.ap));
|
||||
_list_ap();
|
||||
mutex_unlock(&_sync);
|
||||
}
|
||||
|
||||
static void _wifi_connect_cb(void *netif, const wifi_connect_result_t *result)
|
||||
{
|
||||
(void)netif;
|
||||
if (result) {
|
||||
printf("connected to %s\n", result->ssid);
|
||||
}
|
||||
mutex_unlock(&_sync);
|
||||
}
|
||||
|
||||
static void _wifi_disconnect_cb(void *netif, const wifi_disconnect_result_t *result)
|
||||
{
|
||||
(void)netif;
|
||||
if (result) {
|
||||
printf("could not connect to %s\n", result->ssid);
|
||||
}
|
||||
mutex_unlock(&_sync);
|
||||
}
|
||||
|
||||
static int _iw_probe(netif_t *iface)
|
||||
{
|
||||
long ret;
|
||||
uint16_t val16;
|
||||
if ((ret = netif_get_opt(iface, NETOPT_IS_WIRED, 0, &val16, sizeof(&val16))) < 0) {
|
||||
if (ret != -ENOTSUP) { /* -ENOTSUP means wireless */
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -ENOTSUP; /* wired */
|
||||
}
|
||||
if ((ret = netif_get_opt(iface, NETOPT_DEVICE_TYPE, 0, &val16, sizeof(val16))) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
if (val16 != NETDEV_TYPE_ETHERNET) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _iw_disconnect(netif_t *iface)
|
||||
{
|
||||
long ret;
|
||||
wifi_disconnect_request_t request = WIFI_DISCONNECT_REQUEST_INITIALIZER(NULL);
|
||||
if ((ret = netif_set_opt(iface, NETOPT_DISCONNECT, 0, &request, sizeof(request))) < 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _iw_cmd_disconnect(netif_t *iface, int argc, char **argv)
|
||||
{
|
||||
(void)argc; (void)argv;
|
||||
return _iw_disconnect(iface);
|
||||
}
|
||||
|
||||
static int _iw_connect(netif_t *iface, wifi_connect_request_t *request)
|
||||
{
|
||||
long ret;
|
||||
/* this should not block! */
|
||||
mutex_lock(&_sync);
|
||||
if ((ret = netif_set_opt(iface, NETOPT_CONNECT, 0, request, sizeof(*request))) < 0) {
|
||||
mutex_unlock(&_sync);
|
||||
return ret;
|
||||
}
|
||||
/* callback unlocks mutex */
|
||||
ztimer_mutex_lock_timeout(ZTIMER_SEC, &_sync, SC_IW_AP_CONNECT_TIMEOUT_SEC_MAX);
|
||||
mutex_unlock(&_sync);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _iw_cmd_connect(netif_t *iface, int argc, char **argv)
|
||||
{
|
||||
(void)iface; (void)argc; (void)argv;
|
||||
if (argc < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
wifi_connect_request_t request = WIFI_CONNECT_REQUEST_INITIALIZER(0, _wifi_connect_cb,
|
||||
_wifi_disconnect_cb, NULL);
|
||||
if (strlen(argv[0]) > sizeof(request.ssid) - 1) {
|
||||
printf("SSID too long\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
strcpy(request.ssid, argv[0]);
|
||||
argc--;
|
||||
argv++;
|
||||
const char *user = NULL;
|
||||
const char *pwd = NULL;
|
||||
const char *psk = NULL;
|
||||
bool wep = false;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (argv[i][0] != '-') {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!strcmp("-u", argv[i])) {
|
||||
if (++i >= argc) {
|
||||
printf("-u requires an argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
user = argv[i];
|
||||
}
|
||||
else if (!strcmp("-p", argv[i])) {
|
||||
if (++i >= argc) {
|
||||
printf("-p requires an argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
pwd = argv[i];
|
||||
}
|
||||
else if (!strcmp("-k", argv[i])) {
|
||||
if (++i >= argc) {
|
||||
printf("-k requires an argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!strcmp("WEP", argv[i])) {
|
||||
if (++i >= argc) {
|
||||
printf("-k requires an argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
wep = true;
|
||||
}
|
||||
psk = argv[i];
|
||||
}
|
||||
else {
|
||||
printf("unknown option %s\n", argv[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
const wifi_scan_result_t *ap = _get_ap(request.ssid);
|
||||
if (ap) {
|
||||
if (ap->sec_mode == WIFI_SECURITY_MODE_WEP_PSK) {
|
||||
if (!psk) {
|
||||
printf("%s requires -k WEP\n", request.ssid);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
else if (ap->sec_mode == WIFI_SECURITY_MODE_WPA2_PERSONAL) {
|
||||
if (!psk) {
|
||||
printf("%s requires -k\n", request.ssid);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
else if (ap->sec_mode == WIFI_SECURITY_MODE_WPA2_ENTERPRISE) {
|
||||
if (!user || !pwd) {
|
||||
printf("%s requires -u and -p\n", request.ssid);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
request.base.channel = ap->base.channel;
|
||||
}
|
||||
if (psk) {
|
||||
if (strlen(psk) > sizeof(_cred.wpa.psk) - 1) {
|
||||
printf("Key too long\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* also copies to WEP key */
|
||||
strcpy(_cred.wpa.psk, psk);
|
||||
if (wep) {
|
||||
_cred.wep.sec = WIFI_SECURITY_MODE_WEP_PSK;
|
||||
request.cred = &_cred.wep.sec;
|
||||
}
|
||||
else {
|
||||
_cred.wpa.sec = WIFI_SECURITY_MODE_WPA2_PERSONAL;
|
||||
request.cred = &_cred.wpa.sec;
|
||||
}
|
||||
}
|
||||
if (user) {
|
||||
if (strlen(user) > sizeof(_cred.eap.user) - 1) {
|
||||
printf("username too long\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
strcpy(_cred.eap.user, user);
|
||||
_cred.eap.sec = WIFI_SECURITY_MODE_WPA2_ENTERPRISE;
|
||||
request.cred = &_cred.eap.sec;
|
||||
explicit_bzero(_cred.eap.pwd, sizeof(_cred.eap.pwd));
|
||||
}
|
||||
if (pwd) {
|
||||
if (strlen(pwd) > sizeof(_cred.eap.pwd) - 1) {
|
||||
printf("password too long\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
strcpy(_cred.eap.pwd, pwd);
|
||||
}
|
||||
return _iw_connect(iface, &request);
|
||||
}
|
||||
|
||||
static int _iw_scan(netif_t *iface, wifi_scan_request_t *request)
|
||||
{
|
||||
int ret;
|
||||
/* this should not block! */
|
||||
mutex_lock(&_sync);
|
||||
if ((ret = netif_set_opt(iface, NETOPT_SCAN, 0, request, sizeof(*request))) < 0) {
|
||||
mutex_unlock(&_sync);
|
||||
return ret;
|
||||
}
|
||||
/* callback unlocks mutex */
|
||||
ztimer_mutex_lock_timeout(ZTIMER_SEC, &_sync, SC_IW_AP_SCAN_TIMEOUT_SEC_MAX);
|
||||
mutex_unlock(&_sync);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _iw_cmd_scan(netif_t *iface, int argc, char **argv)
|
||||
{
|
||||
long ret;
|
||||
netopt_channel_t ch = NETOPT_SCAN_REQ_ALL_CH;
|
||||
uint32_t timeout_ms = 0;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (argv[i][0] != '-') {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!strcmp("-c", argv[i])) {
|
||||
if (++i >= argc) {
|
||||
printf("-c requires an argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!fmt_is_number(argv[i]) ||
|
||||
(ret = strtol(argv[i], NULL, 10)) < 0) {
|
||||
printf("-c argument %s is not a number\n", argv[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
ch = (netopt_channel_t)ret;
|
||||
}
|
||||
else if (!strcmp("-t", argv[i])) {
|
||||
if (++i >= argc) {
|
||||
printf("-t requires an argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!fmt_is_number(argv[i]) ||
|
||||
(ret = strtol(argv[i], NULL, 10)) < 0) {
|
||||
printf("-t argument %s is not a number\n", argv[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
timeout_ms = (uint32_t)ret;
|
||||
}
|
||||
else {
|
||||
printf("unknown option %s\n", argv[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
wifi_scan_request_t request = WIFI_SCAN_REQUEST_INITIALIZER(ch, _wifi_scan_cb, timeout_ms);
|
||||
return _iw_scan(iface, &request);
|
||||
}
|
||||
|
||||
static void _iw_usage(const char *cmd)
|
||||
{
|
||||
printf("usage: %s <if_id> <command>\n", cmd);
|
||||
printf("commands:\n"
|
||||
" scan [-c <channel>] [-t <channel timeout ms>]\n"
|
||||
" connect <SSID> [-u <username> -p <password>] | -k [WEP] <psk>]\n"
|
||||
" disconnect\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void _iw_error(const char *cmd, int error)
|
||||
{
|
||||
printf("%s: error (%d) %s\n", cmd, error, strerror(error));
|
||||
}
|
||||
|
||||
int _iw_cmd(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
goto exit_help;
|
||||
}
|
||||
int ret = -EINVAL;
|
||||
netif_t *iface = netif_get_by_name(argv[1]);
|
||||
if (!iface) {
|
||||
printf("%s: invalid interface given\n", argv[0]);
|
||||
goto exit_failure;
|
||||
}
|
||||
if ((ret = _iw_probe(iface)) != 0) {
|
||||
if (ret == -ENOTSUP) {
|
||||
printf("%s: interface is not Wi-Fi\n", argv[0]);
|
||||
goto exit_failure;
|
||||
}
|
||||
else {
|
||||
printf("%s: interface communication error\n", argv[0]);
|
||||
goto exit_failure;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(argv[2], "scan")) {
|
||||
if ((ret = _iw_cmd_scan(iface, argc - 3, argv + 3)) != 0) {
|
||||
goto exit_failure;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(argv[2], "connect")) {
|
||||
if ((ret = _iw_cmd_connect(iface, argc - 3, argv + 3)) != 0) {
|
||||
goto exit_failure;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(argv[2], "disconnect")) {
|
||||
if ((ret = _iw_cmd_disconnect(iface, argc - 3, argv + 3)) != 0) {
|
||||
goto exit_failure;
|
||||
}
|
||||
}
|
||||
else {
|
||||
goto exit_help;
|
||||
}
|
||||
|
||||
printf("%s: ok\n", argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
exit_help:
|
||||
_iw_usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
|
||||
exit_failure:
|
||||
_iw_error(argv[0], ret);
|
||||
return EXIT_FAILURE;
|
||||
|
||||
}
|
||||
|
||||
SHELL_COMMAND(iw, "Control Wi-Fi interfaces", _iw_cmd);
|
6
tests/net/l2scan_list/Makefile
Normal file
6
tests/net/l2scan_list/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
include ../Makefile.net_common
|
||||
|
||||
USEMODULE += embunit
|
||||
USEMODULE += l2scan_list
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
152
tests/net/l2scan_list/main.c
Normal file
152
tests/net/l2scan_list/main.c
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2023 ML!PA Consulting GmbH
|
||||
*
|
||||
* 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 tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Unit tests for l2scan list
|
||||
*
|
||||
* @author Fabian Hüßler <fabian.huessler@ml-pa.com>
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "embUnit.h"
|
||||
#include "container.h"
|
||||
#include "net/netopt.h"
|
||||
#include "net/l2scan_list.h"
|
||||
|
||||
typedef struct my_scan_result_node {
|
||||
list_node_t node;
|
||||
netopt_scan_result_t result;
|
||||
} my_scan_result_node_t;
|
||||
|
||||
static const netopt_scan_result_t _test_results[] = {
|
||||
[0] = {
|
||||
.channel = 11,
|
||||
.strength = -40,
|
||||
},
|
||||
[1] = {
|
||||
.channel = 11,
|
||||
.strength = -52,
|
||||
},
|
||||
[2] = {
|
||||
.channel = 6,
|
||||
.strength = -53,
|
||||
},
|
||||
[3] = {
|
||||
.channel = 6,
|
||||
.strength = -54,
|
||||
},
|
||||
[4] = {
|
||||
.channel = 6,
|
||||
.strength = -55,
|
||||
},
|
||||
[5] = {
|
||||
.channel = 1,
|
||||
.strength = -60,
|
||||
},
|
||||
[6] = {
|
||||
.channel = 1,
|
||||
.strength = -70,
|
||||
},
|
||||
[7] = {
|
||||
.channel = 1,
|
||||
.strength = -80,
|
||||
}
|
||||
};
|
||||
|
||||
struct {
|
||||
my_scan_result_node_t *head;
|
||||
my_scan_result_node_t result[ARRAY_SIZE(_test_results)];
|
||||
} _scan_list;
|
||||
|
||||
static void test_l2scan_list_empty(void)
|
||||
{
|
||||
l2scan_list_empty((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t));
|
||||
TEST_ASSERT(!_scan_list.head);
|
||||
}
|
||||
|
||||
static void test_l2scan_list_not_empty(void)
|
||||
{
|
||||
l2scan_list_empty((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t));
|
||||
TEST_ASSERT(!_scan_list.head);
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[0]);
|
||||
TEST_ASSERT(_scan_list.head);
|
||||
}
|
||||
|
||||
static void test_l2scan_list_insert(void)
|
||||
{
|
||||
l2scan_list_empty((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t));
|
||||
TEST_ASSERT(!_scan_list.head);
|
||||
/* random order insert */
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[6]);
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[0]);
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[3]);
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[5]);
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[1]);
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[2]);
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[7]);
|
||||
l2scan_list_insert((l2scan_list_t *)&_scan_list, (list_node_t *)_scan_list.result,
|
||||
ARRAY_SIZE(_scan_list.result), sizeof(my_scan_result_node_t),
|
||||
&_test_results[4]);
|
||||
/* now expect it to be sorted by strength as it is in the test data */
|
||||
my_scan_result_node_t *node = _scan_list.head;
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(_test_results); i++) {
|
||||
TEST_ASSERT(node);
|
||||
TEST_ASSERT(node->result.channel == _test_results[i].channel);
|
||||
TEST_ASSERT(node->result.strength == _test_results[i].strength);
|
||||
node = (my_scan_result_node_t *)node->node.next;
|
||||
}
|
||||
}
|
||||
|
||||
static void _setup(void)
|
||||
{
|
||||
memset(&_scan_list, 0, sizeof(_scan_list));
|
||||
}
|
||||
|
||||
Test* test_l2scan_list(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(test_l2scan_list_empty),
|
||||
new_TestFixture(test_l2scan_list_not_empty),
|
||||
new_TestFixture(test_l2scan_list_insert),
|
||||
};
|
||||
|
||||
EMB_UNIT_TESTCALLER(tests_l2scan_list, _setup, NULL, fixtures);
|
||||
return (Test *)&tests_l2scan_list;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TESTS_START();
|
||||
TESTS_RUN(test_l2scan_list());
|
||||
TESTS_END();
|
||||
}
|
Loading…
Reference in New Issue
Block a user