blob: a60ac990b6367a3ea461a9bd9c2c8739127856ca [file] [log] [blame]
/**
* Copyright (c) 2018 Texas Instruments, Incorporated
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "simplelink_log.h"
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/wifi_mgmt.h>
#include <zephyr/net/net_offload.h>
#ifdef CONFIG_NET_SOCKETS_OFFLOAD
#include <zephyr/net/socket_offload.h>
#endif
#include <ti/drivers/net/wifi/wlan.h>
#include "simplelink_support.h"
#include "simplelink_sockets.h"
#define SCAN_RETRY_DELAY 2000 /* ms */
#define FC_TIMEOUT K_SECONDS(CONFIG_WIFI_SIMPLELINK_FAST_CONNECT_TIMEOUT)
#define SIMPLELINK_IPV4 0x1
#define SIMPLELINK_IPV6 0x2
struct simplelink_data {
struct net_if *iface;
unsigned char mac[6];
/* Fields for scan API to emulate an asynchronous scan: */
struct k_work_delayable work;
scan_result_cb_t cb;
int num_results_or_err;
int scan_retries;
bool initialized;
uint8_t mask;
};
static struct simplelink_data simplelink_data;
static K_SEM_DEFINE(ip_acquired, 0, 1);
/* Handle connection events from the SimpleLink Event Handlers: */
static void simplelink_wifi_cb(uint32_t event, struct sl_connect_state *conn)
{
int status;
/*
* Once Zephyr wifi_mgmt wifi_status codes are defined, will need
* to map from SimpleLink error codes. For now, just return -EIO.
*/
status = (conn->error ? -EIO : 0);
switch (event) {
case SL_WLAN_EVENT_CONNECT:
/* Only get this event if connect succeeds: */
wifi_mgmt_raise_connect_result_event(simplelink_data.iface,
status);
break;
case SL_WLAN_EVENT_DISCONNECT:
/* Could be during a connect, disconnect, or async error: */
wifi_mgmt_raise_disconnect_result_event(simplelink_data.iface,
status);
break;
case SIMPLELINK_WIFI_CB_IPACQUIRED:
simplelink_data.mask &= ~SIMPLELINK_IPV4;
if ((simplelink_data.mask == 0) &&
(!simplelink_data.initialized)) {
simplelink_data.initialized = true;
k_sem_give(&ip_acquired);
}
break;
case SIMPLELINK_WIFI_CB_IPV6ACQUIRED:
simplelink_data.mask &= ~SIMPLELINK_IPV6;
if ((simplelink_data.mask == 0) &&
(!simplelink_data.initialized)) {
simplelink_data.initialized = true;
k_sem_give(&ip_acquired);
}
break;
default:
LOG_DBG("Unrecognized mgmt event: 0x%x", event);
break;
}
}
static void simplelink_scan_work_handler(struct k_work *work)
{
if (simplelink_data.num_results_or_err > 0) {
int index = 0;
struct wifi_scan_result scan_result;
/* Iterate over the table, and call the scan_result callback. */
while (index < simplelink_data.num_results_or_err) {
z_simplelink_get_scan_result(index, &scan_result);
simplelink_data.cb(simplelink_data.iface, 0,
&scan_result);
/* Yield, to ensure notifications get delivered: */
k_yield();
index++;
}
/* Sending a NULL entry indicates e/o results, and
* triggers the NET_EVENT_WIFI_SCAN_DONE event:
*/
simplelink_data.cb(simplelink_data.iface, 0, NULL);
} else if ((simplelink_data.num_results_or_err ==
SL_ERROR_WLAN_GET_NETWORK_LIST_EAGAIN) &&
(simplelink_data.scan_retries++ <
CONFIG_WIFI_SIMPLELINK_MAX_SCAN_RETRIES)) {
int32_t delay;
/* Try again: */
simplelink_data.num_results_or_err = z_simplelink_start_scan();
simplelink_data.scan_retries++;
delay = (simplelink_data.num_results_or_err > 0 ? 0 :
SCAN_RETRY_DELAY);
if (delay > 0) {
LOG_DBG("Retrying scan...");
}
k_work_reschedule(&simplelink_data.work, K_MSEC(delay));
} else {
/* Encountered an error, or max retries exceeded: */
LOG_ERR("Scan failed: retries: %d; err: %d",
simplelink_data.scan_retries,
simplelink_data.num_results_or_err);
simplelink_data.cb(simplelink_data.iface, -EIO, NULL);
}
}
static int simplelink_mgmt_scan(const struct device *dev, scan_result_cb_t cb)
{
int err;
int status;
/* Cancel any previous scan processing in progress: */
k_work_cancel_delayable(&simplelink_data.work);
/* "Request" the scan: */
err = z_simplelink_start_scan();
/* Now, launch a delayed work handler to do retries and reporting.
* Indicate (to the work handler) either a positive number of results
* already returned, or indicate a retry is required:
*/
if ((err > 0) || (err == SL_ERROR_WLAN_GET_NETWORK_LIST_EAGAIN)) {
int32_t delay = (err > 0 ? 0 : SCAN_RETRY_DELAY);
/* Store for later reference by delayed work handler: */
simplelink_data.cb = cb;
simplelink_data.num_results_or_err = err;
simplelink_data.scan_retries = 0;
k_work_reschedule(&simplelink_data.work, K_MSEC(delay));
status = 0;
} else {
status = -EIO;
}
return status;
}
static int simplelink_mgmt_connect(const struct device *dev,
struct wifi_connect_req_params *params)
{
int ret;
ret = z_simplelink_connect(params);
return ret ? -EIO : ret;
}
static int simplelink_mgmt_disconnect(const struct device *dev)
{
int ret;
ret = z_simplelink_disconnect();
return ret ? -EIO : ret;
}
static int simplelink_dummy_get(sa_family_t family,
enum net_sock_type type,
enum net_ip_protocol ip_proto,
struct net_context **context)
{
LOG_ERR("NET_SOCKETS_OFFLOAD must be configured for this driver");
return -1;
}
/* Placeholders, until Zephyr IP stack updated to handle a NULL net_offload */
static struct net_offload simplelink_offload = {
.get = simplelink_dummy_get,
.bind = NULL,
.listen = NULL,
.connect = NULL,
.accept = NULL,
.send = NULL,
.sendto = NULL,
.recv = NULL,
.put = NULL,
};
static void simplelink_iface_init(struct net_if *iface)
{
int ret;
simplelink_data.iface = iface;
simplelink_data.mask = 0;
simplelink_data.mask |= IS_ENABLED(CONFIG_NET_IPV4) ?
SIMPLELINK_IPV4 : 0;
simplelink_data.mask |= IS_ENABLED(CONFIG_NET_IPV6) ?
SIMPLELINK_IPV6 : 0;
/* Direct socket offload used instead of net offload: */
iface->if_dev->offload = &simplelink_offload;
/* Initialize and configure NWP to defaults: */
ret = z_simplelink_init(simplelink_wifi_cb);
if (ret) {
LOG_ERR("z_simplelink_init failed!");
return;
}
ret = k_sem_take(&ip_acquired, FC_TIMEOUT);
if (ret < 0) {
simplelink_data.initialized = false;
LOG_ERR("FastConnect timed out connecting to previous AP.");
LOG_ERR("Please re-establish WiFi connection.");
}
/* Grab our MAC address: */
z_simplelink_get_mac(simplelink_data.mac);
LOG_DBG("MAC Address %02X:%02X:%02X:%02X:%02X:%02X",
simplelink_data.mac[0], simplelink_data.mac[1],
simplelink_data.mac[2],
simplelink_data.mac[3], simplelink_data.mac[4],
simplelink_data.mac[5]);
net_if_set_link_addr(iface, simplelink_data.mac,
sizeof(simplelink_data.mac),
NET_LINK_ETHERNET);
#ifdef CONFIG_NET_SOCKETS_OFFLOAD
/* Direct socket offload: */
socket_offload_dns_register(&simplelink_dns_ops);
simplelink_sockets_init();
net_if_socket_offload_set(iface, simplelink_socket_create);
#endif
}
static const struct net_wifi_mgmt_offload simplelink_api = {
.wifi_iface.init = simplelink_iface_init,
.scan = simplelink_mgmt_scan,
.connect = simplelink_mgmt_connect,
.disconnect = simplelink_mgmt_disconnect,
};
static int simplelink_init(const struct device *dev)
{
ARG_UNUSED(dev);
/* We use system workqueue to deal with scan retries: */
k_work_init_delayable(&simplelink_data.work,
simplelink_scan_work_handler);
LOG_DBG("SimpleLink driver Initialized");
return 0;
}
NET_DEVICE_OFFLOAD_INIT(simplelink, CONFIG_WIFI_SIMPLELINK_NAME,
simplelink_init, NULL,
&simplelink_data, NULL,
CONFIG_WIFI_INIT_PRIORITY, &simplelink_api,
CONFIG_WIFI_SIMPLELINK_MAX_PACKET_SIZE);