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