1 /**
2 * Copyright (c) 2018 Texas Instruments, Incorporated
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "simplelink_log.h"
8 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
9
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <zephyr/net/net_if.h>
13 #include <zephyr/net/wifi_mgmt.h>
14 #include <zephyr/net/net_offload.h>
15 #include <zephyr/net/conn_mgr/connectivity_wifi_mgmt.h>
16 #ifdef CONFIG_NET_SOCKETS_OFFLOAD
17 #include <zephyr/net/socket_offload.h>
18 #endif
19
20 #include <ti/drivers/net/wifi/wlan.h>
21 #include "simplelink_support.h"
22 #include "simplelink_sockets.h"
23
24 #define SCAN_RETRY_DELAY 2000 /* ms */
25 #define FC_TIMEOUT K_SECONDS(CONFIG_WIFI_SIMPLELINK_FAST_CONNECT_TIMEOUT)
26
27 #define SIMPLELINK_IPV4 0x1
28 #define SIMPLELINK_IPV6 0x2
29
30 struct simplelink_data {
31 struct net_if *iface;
32 unsigned char mac[6];
33
34 /* Fields for scan API to emulate an asynchronous scan: */
35 struct k_work_delayable work;
36 scan_result_cb_t cb;
37 int num_results_or_err;
38 int scan_retries;
39 bool initialized;
40 uint8_t mask;
41 };
42
43 static struct simplelink_data simplelink_data;
44 static K_SEM_DEFINE(ip_acquired, 0, 1);
45
46 /* Handle connection events from the SimpleLink Event Handlers: */
simplelink_wifi_cb(uint32_t event,struct sl_connect_state * conn)47 static void simplelink_wifi_cb(uint32_t event, struct sl_connect_state *conn)
48 {
49 int status;
50
51 /*
52 * Once Zephyr wifi_mgmt wifi_status codes are defined, will need
53 * to map from SimpleLink error codes. For now, just return -EIO.
54 */
55 status = (conn->error ? -EIO : 0);
56
57 switch (event) {
58 case SL_WLAN_EVENT_CONNECT:
59 /* Only get this event if connect succeeds: */
60 wifi_mgmt_raise_connect_result_event(simplelink_data.iface,
61 status);
62 break;
63
64 case SL_WLAN_EVENT_DISCONNECT:
65 /* Could be during a connect, disconnect, or async error: */
66 wifi_mgmt_raise_disconnect_result_event(simplelink_data.iface,
67 status);
68 break;
69
70 case SIMPLELINK_WIFI_CB_IPACQUIRED:
71 simplelink_data.mask &= ~SIMPLELINK_IPV4;
72 if ((simplelink_data.mask == 0) &&
73 (!simplelink_data.initialized)) {
74 simplelink_data.initialized = true;
75 k_sem_give(&ip_acquired);
76 }
77 break;
78
79 case SIMPLELINK_WIFI_CB_IPV6ACQUIRED:
80 simplelink_data.mask &= ~SIMPLELINK_IPV6;
81 if ((simplelink_data.mask == 0) &&
82 (!simplelink_data.initialized)) {
83 simplelink_data.initialized = true;
84 k_sem_give(&ip_acquired);
85 }
86 break;
87
88 default:
89 LOG_DBG("Unrecognized mgmt event: 0x%x", event);
90 break;
91 }
92 }
93
simplelink_scan_work_handler(struct k_work * work)94 static void simplelink_scan_work_handler(struct k_work *work)
95 {
96 if (simplelink_data.num_results_or_err > 0) {
97 int index = 0;
98 struct wifi_scan_result scan_result;
99
100 /* Iterate over the table, and call the scan_result callback. */
101 while (index < simplelink_data.num_results_or_err) {
102 z_simplelink_get_scan_result(index, &scan_result);
103 simplelink_data.cb(simplelink_data.iface, 0,
104 &scan_result);
105 /* Yield, to ensure notifications get delivered: */
106 k_yield();
107 index++;
108 }
109
110 /* Sending a NULL entry indicates e/o results, and
111 * triggers the NET_EVENT_WIFI_SCAN_DONE event:
112 */
113 simplelink_data.cb(simplelink_data.iface, 0, NULL);
114
115 } else if ((simplelink_data.num_results_or_err ==
116 SL_ERROR_WLAN_GET_NETWORK_LIST_EAGAIN) &&
117 (simplelink_data.scan_retries++ <
118 CONFIG_WIFI_SIMPLELINK_MAX_SCAN_RETRIES)) {
119 int32_t delay;
120
121 /* Try again: */
122 simplelink_data.num_results_or_err = z_simplelink_start_scan();
123 simplelink_data.scan_retries++;
124 delay = (simplelink_data.num_results_or_err > 0 ? 0 :
125 SCAN_RETRY_DELAY);
126 if (delay > 0) {
127 LOG_DBG("Retrying scan...");
128 }
129 k_work_reschedule(&simplelink_data.work, K_MSEC(delay));
130
131 } else {
132 /* Encountered an error, or max retries exceeded: */
133 LOG_ERR("Scan failed: retries: %d; err: %d",
134 simplelink_data.scan_retries,
135 simplelink_data.num_results_or_err);
136 simplelink_data.cb(simplelink_data.iface, -EIO, NULL);
137 }
138 }
139
simplelink_mgmt_scan(const struct device * dev,struct wifi_scan_params * params,scan_result_cb_t cb)140 static int simplelink_mgmt_scan(const struct device *dev,
141 struct wifi_scan_params *params,
142 scan_result_cb_t cb)
143 {
144 int err;
145 int status;
146
147 ARG_UNUSED(params);
148
149 /* Cancel any previous scan processing in progress: */
150 k_work_cancel_delayable(&simplelink_data.work);
151
152 /* "Request" the scan: */
153 err = z_simplelink_start_scan();
154
155 /* Now, launch a delayed work handler to do retries and reporting.
156 * Indicate (to the work handler) either a positive number of results
157 * already returned, or indicate a retry is required:
158 */
159 if ((err > 0) || (err == SL_ERROR_WLAN_GET_NETWORK_LIST_EAGAIN)) {
160 int32_t delay = (err > 0 ? 0 : SCAN_RETRY_DELAY);
161
162 /* Store for later reference by delayed work handler: */
163 simplelink_data.cb = cb;
164 simplelink_data.num_results_or_err = err;
165 simplelink_data.scan_retries = 0;
166
167 k_work_reschedule(&simplelink_data.work, K_MSEC(delay));
168 status = 0;
169 } else {
170 status = -EIO;
171 }
172
173 return status;
174 }
175
simplelink_mgmt_connect(const struct device * dev,struct wifi_connect_req_params * params)176 static int simplelink_mgmt_connect(const struct device *dev,
177 struct wifi_connect_req_params *params)
178 {
179 int ret;
180
181 ret = z_simplelink_connect(params);
182
183 return ret ? -EIO : ret;
184 }
185
simplelink_mgmt_disconnect(const struct device * dev)186 static int simplelink_mgmt_disconnect(const struct device *dev)
187 {
188 int ret;
189
190 ret = z_simplelink_disconnect();
191
192 return ret ? -EIO : ret;
193 }
194
simplelink_dummy_get(sa_family_t family,enum net_sock_type type,enum net_ip_protocol ip_proto,struct net_context ** context)195 static int simplelink_dummy_get(sa_family_t family,
196 enum net_sock_type type,
197 enum net_ip_protocol ip_proto,
198 struct net_context **context)
199 {
200
201 LOG_ERR("NET_SOCKETS_OFFLOAD must be configured for this driver");
202
203 return -1;
204 }
205
206 /* Placeholders, until Zephyr IP stack updated to handle a NULL net_offload */
207 static struct net_offload simplelink_offload = {
208 .get = simplelink_dummy_get,
209 .bind = NULL,
210 .listen = NULL,
211 .connect = NULL,
212 .accept = NULL,
213 .send = NULL,
214 .sendto = NULL,
215 .recv = NULL,
216 .put = NULL,
217 };
218
simplelink_iface_init(struct net_if * iface)219 static void simplelink_iface_init(struct net_if *iface)
220 {
221 int ret;
222
223 simplelink_data.iface = iface;
224 simplelink_data.mask = 0;
225
226 simplelink_data.mask |= IS_ENABLED(CONFIG_NET_IPV4) ?
227 SIMPLELINK_IPV4 : 0;
228 simplelink_data.mask |= IS_ENABLED(CONFIG_NET_IPV6) ?
229 SIMPLELINK_IPV6 : 0;
230
231 /* Direct socket offload used instead of net offload: */
232 iface->if_dev->offload = &simplelink_offload;
233
234 /* Initialize and configure NWP to defaults: */
235 ret = z_simplelink_init(simplelink_wifi_cb);
236 if (ret) {
237 LOG_ERR("z_simplelink_init failed!");
238 return;
239 }
240
241 ret = k_sem_take(&ip_acquired, FC_TIMEOUT);
242 if (ret < 0) {
243 simplelink_data.initialized = false;
244 LOG_ERR("FastConnect timed out connecting to previous AP.");
245 LOG_ERR("Please re-establish WiFi connection.");
246 }
247
248 /* Grab our MAC address: */
249 z_simplelink_get_mac(simplelink_data.mac);
250
251 LOG_DBG("MAC Address %02X:%02X:%02X:%02X:%02X:%02X",
252 simplelink_data.mac[0], simplelink_data.mac[1],
253 simplelink_data.mac[2],
254 simplelink_data.mac[3], simplelink_data.mac[4],
255 simplelink_data.mac[5]);
256
257 net_if_set_link_addr(iface, simplelink_data.mac,
258 sizeof(simplelink_data.mac),
259 NET_LINK_ETHERNET);
260
261 #ifdef CONFIG_NET_SOCKETS_OFFLOAD
262 /* Direct socket offload: */
263 socket_offload_dns_register(&simplelink_dns_ops);
264 simplelink_sockets_init();
265
266 net_if_socket_offload_set(iface, simplelink_socket_create);
267 #endif
268
269 }
270
simplelink_get_type(void)271 static enum offloaded_net_if_types simplelink_get_type(void)
272 {
273 return L2_OFFLOADED_NET_IF_TYPE_WIFI;
274 }
275
276 static const struct wifi_mgmt_ops simplelink_mgmt = {
277 .scan = simplelink_mgmt_scan,
278 .connect = simplelink_mgmt_connect,
279 .disconnect = simplelink_mgmt_disconnect,
280 };
281
282 static const struct net_wifi_mgmt_offload simplelink_api = {
283 .wifi_iface.iface_api.init = simplelink_iface_init,
284 .wifi_iface.get_type = simplelink_get_type,
285 .wifi_mgmt_api = &simplelink_mgmt,
286 };
287
simplelink_init(const struct device * dev)288 static int simplelink_init(const struct device *dev)
289 {
290 ARG_UNUSED(dev);
291
292 /* We use system workqueue to deal with scan retries: */
293 k_work_init_delayable(&simplelink_data.work,
294 simplelink_scan_work_handler);
295
296 LOG_DBG("SimpleLink driver Initialized");
297
298 return 0;
299 }
300
301 NET_DEVICE_OFFLOAD_INIT(simplelink, CONFIG_WIFI_SIMPLELINK_NAME,
302 simplelink_init, NULL,
303 &simplelink_data, NULL,
304 CONFIG_WIFI_INIT_PRIORITY, &simplelink_api,
305 CONFIG_WIFI_SIMPLELINK_MAX_PACKET_SIZE);
306
307 CONNECTIVITY_WIFI_MGMT_BIND(simplelink);
308