/** * Copyright (c) 2018 Texas Instruments, Incorporated * * SPDX-License-Identifier: Apache-2.0 */ #include "simplelink_log.h" LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include #include #include #include #include #ifdef CONFIG_NET_SOCKETS_OFFLOAD #include #endif #include #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, struct wifi_scan_params *params, scan_result_cb_t cb) { int err; int status; ARG_UNUSED(params); /* 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 enum offloaded_net_if_types simplelink_get_type(void) { return L2_OFFLOADED_NET_IF_TYPE_WIFI; } static const struct wifi_mgmt_ops simplelink_mgmt = { .scan = simplelink_mgmt_scan, .connect = simplelink_mgmt_connect, .disconnect = simplelink_mgmt_disconnect, }; static const struct net_wifi_mgmt_offload simplelink_api = { .wifi_iface.iface_api.init = simplelink_iface_init, .wifi_iface.get_type = simplelink_get_type, .wifi_mgmt_api = &simplelink_mgmt, }; 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);