368 lines
10 KiB
C
368 lines
10 KiB
C
|
/*
|
||
|
* Copyright (c) 2023 Antmicro
|
||
|
* Copyright (c) 2024 Silicon Laboratories Inc.
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*/
|
||
|
#define DT_DRV_COMPAT silabs_siwx91x_wifi
|
||
|
|
||
|
#include <zephyr/logging/log.h>
|
||
|
#include <zephyr/sys/__assert.h>
|
||
|
|
||
|
#include "siwx91x_wifi.h"
|
||
|
#include "siwx91x_wifi_socket.h"
|
||
|
|
||
|
#include "sl_rsi_utility.h"
|
||
|
#include "sl_net_constants.h"
|
||
|
#include "sl_wifi_types.h"
|
||
|
#include "sl_wifi_callback_framework.h"
|
||
|
#include "sl_wifi.h"
|
||
|
#include "sl_net.h"
|
||
|
|
||
|
LOG_MODULE_REGISTER(siwx91x_wifi);
|
||
|
|
||
|
NET_BUF_POOL_FIXED_DEFINE(siwx91x_tx_pool, 1, _NET_ETH_MAX_FRAME_SIZE, 0, NULL);
|
||
|
|
||
|
static unsigned int siwx91x_on_join(sl_wifi_event_t event,
|
||
|
char *result, uint32_t result_size, void *arg)
|
||
|
{
|
||
|
struct siwx91x_dev *sidev = arg;
|
||
|
|
||
|
if (*result != 'C') {
|
||
|
/* TODO: report the real reason of failure */
|
||
|
wifi_mgmt_raise_connect_result_event(sidev->iface, WIFI_STATUS_CONN_FAIL);
|
||
|
sidev->state = WIFI_STATE_INACTIVE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
wifi_mgmt_raise_connect_result_event(sidev->iface, WIFI_STATUS_CONN_SUCCESS);
|
||
|
sidev->state = WIFI_STATE_COMPLETED;
|
||
|
|
||
|
if (IS_ENABLED(CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE)) {
|
||
|
net_if_dormant_off(sidev->iface);
|
||
|
}
|
||
|
|
||
|
siwx91x_on_join_ipv4(sidev);
|
||
|
siwx91x_on_join_ipv6(sidev);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int siwx91x_connect(const struct device *dev, struct wifi_connect_req_params *params)
|
||
|
{
|
||
|
sl_wifi_client_configuration_t wifi_config = {
|
||
|
.bss_type = SL_WIFI_BSS_TYPE_INFRASTRUCTURE,
|
||
|
};
|
||
|
int ret;
|
||
|
|
||
|
switch (params->security) {
|
||
|
case WIFI_SECURITY_TYPE_NONE:
|
||
|
wifi_config.security = SL_WIFI_OPEN;
|
||
|
wifi_config.encryption = SL_WIFI_NO_ENCRYPTION;
|
||
|
break;
|
||
|
case WIFI_SECURITY_TYPE_WPA_PSK:
|
||
|
wifi_config.security = SL_WIFI_WPA;
|
||
|
wifi_config.encryption = SL_WIFI_DEFAULT_ENCRYPTION;
|
||
|
wifi_config.credential_id = SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID;
|
||
|
break;
|
||
|
case WIFI_SECURITY_TYPE_PSK:
|
||
|
wifi_config.security = SL_WIFI_WPA2;
|
||
|
wifi_config.encryption = SL_WIFI_TKIP_ENCRYPTION;
|
||
|
wifi_config.credential_id = SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID;
|
||
|
break;
|
||
|
case WIFI_SECURITY_TYPE_PSK_SHA256:
|
||
|
wifi_config.security = SL_WIFI_WPA2;
|
||
|
wifi_config.encryption = SL_WIFI_CCMP_ENCRYPTION;
|
||
|
wifi_config.credential_id = SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID;
|
||
|
break;
|
||
|
case WIFI_SECURITY_TYPE_SAE:
|
||
|
/* TODO: Support the case where MFP is not required */
|
||
|
wifi_config.security = SL_WIFI_WPA3;
|
||
|
wifi_config.credential_id = SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID;
|
||
|
break;
|
||
|
case WIFI_SECURITY_TYPE_WPA_AUTO_PERSONAL:
|
||
|
wifi_config.security = SL_WIFI_WPA2;
|
||
|
wifi_config.encryption = SL_WIFI_DEFAULT_ENCRYPTION;
|
||
|
wifi_config.credential_id = SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID;
|
||
|
break;
|
||
|
/* Zephyr WiFi shell doesn't specify how to pass credential for these
|
||
|
* key managements.
|
||
|
*/
|
||
|
case WIFI_SECURITY_TYPE_WEP: /* SL_WIFI_WEP/SL_WIFI_WEP_ENCRYPTION */
|
||
|
case WIFI_SECURITY_TYPE_EAP: /* SL_WIFI_WPA2_ENTERPRISE/<various> */
|
||
|
case WIFI_SECURITY_TYPE_WAPI:
|
||
|
default:
|
||
|
return -ENOTSUP;
|
||
|
}
|
||
|
|
||
|
if (params->band != WIFI_FREQ_BAND_UNKNOWN && params->band != WIFI_FREQ_BAND_2_4_GHZ) {
|
||
|
return -ENOTSUP;
|
||
|
}
|
||
|
|
||
|
if (params->psk_length) {
|
||
|
sl_net_set_credential(SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID, SL_NET_WIFI_PSK,
|
||
|
params->psk, params->psk_length);
|
||
|
}
|
||
|
|
||
|
if (params->sae_password_length) {
|
||
|
sl_net_set_credential(SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID, SL_NET_WIFI_PSK,
|
||
|
params->sae_password, params->sae_password_length);
|
||
|
}
|
||
|
|
||
|
if (params->channel != WIFI_CHANNEL_ANY) {
|
||
|
wifi_config.channel.channel = params->channel;
|
||
|
}
|
||
|
|
||
|
wifi_config.ssid.length = params->ssid_length,
|
||
|
memcpy(wifi_config.ssid.value, params->ssid, params->ssid_length);
|
||
|
|
||
|
ret = sl_wifi_connect(SL_WIFI_CLIENT_INTERFACE, &wifi_config, 0);
|
||
|
if (ret != SL_STATUS_IN_PROGRESS) {
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int siwx91x_disconnect(const struct device *dev)
|
||
|
{
|
||
|
struct siwx91x_dev *sidev = dev->data;
|
||
|
int ret;
|
||
|
|
||
|
ret = sl_wifi_disconnect(SL_WIFI_CLIENT_INTERFACE);
|
||
|
if (ret) {
|
||
|
return -EIO;
|
||
|
}
|
||
|
if (IS_ENABLED(CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE)) {
|
||
|
net_if_dormant_on(sidev->iface);
|
||
|
}
|
||
|
sidev->state = WIFI_STATE_INACTIVE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void siwx91x_report_scan_res(struct siwx91x_dev *sidev, sl_wifi_scan_result_t *result,
|
||
|
int item)
|
||
|
{
|
||
|
static const struct {
|
||
|
int sl_val;
|
||
|
int z_val;
|
||
|
} security_convert[] = {
|
||
|
{ SL_WIFI_OPEN, WIFI_SECURITY_TYPE_NONE },
|
||
|
{ SL_WIFI_WEP, WIFI_SECURITY_TYPE_WEP },
|
||
|
{ SL_WIFI_WPA, WIFI_SECURITY_TYPE_WPA_PSK },
|
||
|
{ SL_WIFI_WPA2, WIFI_SECURITY_TYPE_PSK },
|
||
|
{ SL_WIFI_WPA3, WIFI_SECURITY_TYPE_SAE },
|
||
|
{ SL_WIFI_WPA3_TRANSITION, WIFI_SECURITY_TYPE_SAE },
|
||
|
{ SL_WIFI_WPA_ENTERPRISE, WIFI_SECURITY_TYPE_EAP },
|
||
|
{ SL_WIFI_WPA2_ENTERPRISE, WIFI_SECURITY_TYPE_EAP },
|
||
|
};
|
||
|
struct wifi_scan_result tmp = {
|
||
|
.channel = result->scan_info[item].rf_channel,
|
||
|
.rssi = result->scan_info[item].rssi_val,
|
||
|
.ssid_length = strlen(result->scan_info[item].ssid),
|
||
|
.mac_length = sizeof(result->scan_info[item].bssid),
|
||
|
.security = WIFI_SECURITY_TYPE_UNKNOWN,
|
||
|
.mfp = WIFI_MFP_UNKNOWN,
|
||
|
/* FIXME: fill .mfp, .band and .channel */
|
||
|
};
|
||
|
|
||
|
memcpy(tmp.ssid, result->scan_info[item].ssid, tmp.ssid_length);
|
||
|
memcpy(tmp.mac, result->scan_info[item].bssid, tmp.mac_length);
|
||
|
ARRAY_FOR_EACH(security_convert, i) {
|
||
|
if (security_convert[i].sl_val == result->scan_info[item].security_mode) {
|
||
|
tmp.security = security_convert[i].z_val;
|
||
|
}
|
||
|
}
|
||
|
sidev->scan_res_cb(sidev->iface, 0, &tmp);
|
||
|
}
|
||
|
|
||
|
static unsigned int siwx91x_on_scan(sl_wifi_event_t event, sl_wifi_scan_result_t *result,
|
||
|
uint32_t result_size, void *arg)
|
||
|
{
|
||
|
struct siwx91x_dev *sidev = arg;
|
||
|
int i;
|
||
|
|
||
|
if (!sidev->scan_res_cb) {
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
for (i = 0; i < result->scan_count; i++) {
|
||
|
siwx91x_report_scan_res(sidev, result, i);
|
||
|
}
|
||
|
sidev->scan_res_cb(sidev->iface, 0, NULL);
|
||
|
sidev->state = WIFI_STATE_INACTIVE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int siwx91x_scan(const struct device *dev, struct wifi_scan_params *z_scan_config,
|
||
|
scan_result_cb_t cb)
|
||
|
{
|
||
|
sl_wifi_scan_configuration_t sl_scan_config = { };
|
||
|
struct siwx91x_dev *sidev = dev->data;
|
||
|
int ret;
|
||
|
|
||
|
__ASSERT(z_scan_config, "z_scan_config cannot be NULL");
|
||
|
|
||
|
if (sidev->state != WIFI_STATE_INACTIVE) {
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
/* The enum values are same, no conversion needed */
|
||
|
sl_scan_config.type = z_scan_config->scan_type;
|
||
|
|
||
|
sl_scan_config.channel_bitmap_2g4 = 0xFFFF;
|
||
|
memset(sl_scan_config.channel_bitmap_5g, 0xFF, sizeof(sl_scan_config.channel_bitmap_5g));
|
||
|
|
||
|
sidev->scan_res_cb = cb;
|
||
|
ret = sl_wifi_start_scan(SL_WIFI_CLIENT_INTERFACE, NULL, &sl_scan_config);
|
||
|
if (ret != SL_STATUS_IN_PROGRESS) {
|
||
|
return -EIO;
|
||
|
}
|
||
|
sidev->state = WIFI_STATE_SCANNING;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int siwx91x_status(const struct device *dev, struct wifi_iface_status *status)
|
||
|
{
|
||
|
struct siwx91x_dev *sidev = dev->data;
|
||
|
int32_t rssi = -1;
|
||
|
|
||
|
memset(status, 0, sizeof(*status));
|
||
|
status->state = sidev->state;
|
||
|
sl_wifi_get_signal_strength(SL_WIFI_CLIENT_INTERFACE, &rssi);
|
||
|
status->rssi = rssi;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE
|
||
|
|
||
|
static int siwx91x_send(const struct device *dev, struct net_pkt *pkt)
|
||
|
{
|
||
|
size_t pkt_len = net_pkt_get_len(pkt);
|
||
|
struct net_buf *buf = NULL;
|
||
|
int ret;
|
||
|
|
||
|
if (net_pkt_get_len(pkt) > _NET_ETH_MAX_FRAME_SIZE) {
|
||
|
LOG_ERR("unexpected buffer size");
|
||
|
return -ENOBUFS;
|
||
|
}
|
||
|
buf = net_buf_alloc(&siwx91x_tx_pool, K_FOREVER);
|
||
|
if (!buf) {
|
||
|
return -ENOBUFS;
|
||
|
}
|
||
|
if (net_pkt_read(pkt, buf->data, pkt_len)) {
|
||
|
net_buf_unref(buf);
|
||
|
return -ENOBUFS;
|
||
|
}
|
||
|
net_buf_add(buf, pkt_len);
|
||
|
|
||
|
ret = sl_wifi_send_raw_data_frame(SL_WIFI_CLIENT_INTERFACE, buf->data, pkt_len);
|
||
|
if (ret) {
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
net_pkt_unref(pkt);
|
||
|
net_buf_unref(buf);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Receive callback. Keep the name as it is declared weak in WiseConnect */
|
||
|
sl_status_t sl_si91x_host_process_data_frame(sl_wifi_interface_t interface,
|
||
|
sl_wifi_buffer_t *buffer)
|
||
|
{
|
||
|
sl_si91x_packet_t *si_pkt = sl_si91x_host_get_buffer_data(buffer, 0, NULL);
|
||
|
struct net_if *iface = net_if_get_first_wifi();
|
||
|
struct net_pkt *pkt;
|
||
|
int ret;
|
||
|
|
||
|
pkt = net_pkt_rx_alloc_with_buffer(iface, buffer->length, AF_UNSPEC, 0, K_NO_WAIT);
|
||
|
if (!pkt) {
|
||
|
LOG_ERR("net_pkt_rx_alloc_with_buffer() failed");
|
||
|
return SL_STATUS_FAIL;
|
||
|
}
|
||
|
ret = net_pkt_write(pkt, si_pkt->data, si_pkt->length);
|
||
|
if (ret < 0) {
|
||
|
LOG_ERR("net_pkt_write(): %d", ret);
|
||
|
goto unref;
|
||
|
}
|
||
|
ret = net_recv_data(iface, pkt);
|
||
|
if (ret < 0) {
|
||
|
LOG_ERR("net_recv_data((): %d", ret);
|
||
|
goto unref;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
unref:
|
||
|
net_pkt_unref(pkt);
|
||
|
return SL_STATUS_FAIL;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
static void siwx91x_ethernet_init(struct net_if *iface)
|
||
|
{
|
||
|
struct ethernet_context *eth_ctx;
|
||
|
|
||
|
if (IS_ENABLED(CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE)) {
|
||
|
eth_ctx = net_if_l2_data(iface);
|
||
|
eth_ctx->eth_if_type = L2_ETH_IF_TYPE_WIFI;
|
||
|
ethernet_init(iface);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void siwx91x_iface_init(struct net_if *iface)
|
||
|
{
|
||
|
struct siwx91x_dev *sidev = iface->if_dev->dev->data;
|
||
|
sl_status_t status;
|
||
|
|
||
|
sidev->state = WIFI_STATE_INTERFACE_DISABLED;
|
||
|
sidev->iface = iface;
|
||
|
|
||
|
sl_wifi_set_scan_callback(siwx91x_on_scan, sidev);
|
||
|
sl_wifi_set_join_callback(siwx91x_on_join, sidev);
|
||
|
|
||
|
status = sl_wifi_get_mac_address(SL_WIFI_CLIENT_INTERFACE, &sidev->macaddr);
|
||
|
if (status) {
|
||
|
LOG_ERR("sl_wifi_get_mac_address(): %#04x", status);
|
||
|
return;
|
||
|
}
|
||
|
net_if_set_link_addr(iface, sidev->macaddr.octet, sizeof(sidev->macaddr.octet),
|
||
|
NET_LINK_ETHERNET);
|
||
|
siwx91x_sock_init(iface);
|
||
|
siwx91x_ethernet_init(iface);
|
||
|
|
||
|
sidev->state = WIFI_STATE_INACTIVE;
|
||
|
}
|
||
|
|
||
|
static int siwx91x_dev_init(const struct device *dev)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct wifi_mgmt_ops siwx91x_mgmt = {
|
||
|
.scan = siwx91x_scan,
|
||
|
.connect = siwx91x_connect,
|
||
|
.disconnect = siwx91x_disconnect,
|
||
|
.iface_status = siwx91x_status,
|
||
|
};
|
||
|
|
||
|
static const struct net_wifi_mgmt_offload siwx91x_api = {
|
||
|
.wifi_iface.iface_api.init = siwx91x_iface_init,
|
||
|
#ifdef CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE
|
||
|
.wifi_iface.send = siwx91x_send,
|
||
|
#else
|
||
|
.wifi_iface.get_type = siwx91x_get_type,
|
||
|
#endif
|
||
|
.wifi_mgmt_api = &siwx91x_mgmt,
|
||
|
};
|
||
|
|
||
|
static struct siwx91x_dev sidev;
|
||
|
|
||
|
#ifdef CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE
|
||
|
ETH_NET_DEVICE_DT_INST_DEFINE(0, siwx91x_dev_init, NULL, &sidev, NULL,
|
||
|
CONFIG_WIFI_INIT_PRIORITY, &siwx91x_api, NET_ETH_MTU);
|
||
|
#else
|
||
|
NET_DEVICE_DT_INST_OFFLOAD_DEFINE(0, siwx91x_dev_init, NULL, &sidev, NULL,
|
||
|
CONFIG_WIFI_INIT_PRIORITY, &siwx91x_api, NET_ETH_MTU);
|
||
|
#endif
|