1 /*
2  * Copyright (c) 2023 Antmicro
3  * Copyright (c) 2024 Silicon Laboratories Inc.
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #define DT_DRV_COMPAT silabs_siwx91x_wifi
7 
8 #include <zephyr/logging/log.h>
9 #include <zephyr/sys/__assert.h>
10 
11 #include "siwx91x_wifi.h"
12 #include "siwx91x_wifi_socket.h"
13 
14 #include "sl_rsi_utility.h"
15 #include "sl_net_constants.h"
16 #include "sl_wifi_types.h"
17 #include "sl_wifi_callback_framework.h"
18 #include "sl_wifi.h"
19 #include "sl_net.h"
20 
21 #define SIWX91X_INTERFACE_MASK (0x03)
22 
23 LOG_MODULE_REGISTER(siwx91x_wifi);
24 
25 NET_BUF_POOL_FIXED_DEFINE(siwx91x_tx_pool, 1, _NET_ETH_MAX_FRAME_SIZE, 0, NULL);
26 
siwx91x_on_join(sl_wifi_event_t event,char * result,uint32_t result_size,void * arg)27 static unsigned int siwx91x_on_join(sl_wifi_event_t event,
28 				    char *result, uint32_t result_size, void *arg)
29 {
30 	struct siwx91x_dev *sidev = arg;
31 
32 	if (*result != 'C') {
33 		/* TODO: report the real reason of failure */
34 		wifi_mgmt_raise_connect_result_event(sidev->iface, WIFI_STATUS_CONN_FAIL);
35 		sidev->state = WIFI_STATE_INACTIVE;
36 		return 0;
37 	}
38 
39 	wifi_mgmt_raise_connect_result_event(sidev->iface, WIFI_STATUS_CONN_SUCCESS);
40 	sidev->state = WIFI_STATE_COMPLETED;
41 
42 	if (IS_ENABLED(CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE)) {
43 		net_if_dormant_off(sidev->iface);
44 	}
45 
46 	siwx91x_on_join_ipv4(sidev);
47 	siwx91x_on_join_ipv6(sidev);
48 
49 	return 0;
50 }
51 
siwx91x_connect(const struct device * dev,struct wifi_connect_req_params * params)52 static int siwx91x_connect(const struct device *dev, struct wifi_connect_req_params *params)
53 {
54 	sl_wifi_client_configuration_t wifi_config = {
55 		.bss_type = SL_WIFI_BSS_TYPE_INFRASTRUCTURE,
56 		.encryption = SL_WIFI_DEFAULT_ENCRYPTION,
57 		.credential_id = SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID,
58 	};
59 	int ret;
60 
61 	switch (params->security) {
62 	case WIFI_SECURITY_TYPE_NONE:
63 		wifi_config.security = SL_WIFI_OPEN;
64 		break;
65 	case WIFI_SECURITY_TYPE_WPA_PSK:
66 		wifi_config.security = SL_WIFI_WPA;
67 		break;
68 	case WIFI_SECURITY_TYPE_PSK:
69 		/* This case is meant to fall through to the next */
70 	case WIFI_SECURITY_TYPE_PSK_SHA256:
71 		/* Use WPA2 security as the device supports only SHA256
72 		 * key derivation for WPA2-PSK
73 		 */
74 		wifi_config.security = SL_WIFI_WPA2;
75 		break;
76 	case WIFI_SECURITY_TYPE_SAE_AUTO:
77 		/* Use WPA3 security as the device supports only HNP and H2E
78 		 * methods for SAE
79 		 */
80 		wifi_config.security = SL_WIFI_WPA3;
81 		break;
82 	/* Zephyr WiFi shell doesn't specify how to pass credential for these
83 	 * key managements.
84 	 */
85 	case WIFI_SECURITY_TYPE_WEP: /* SL_WIFI_WEP/SL_WIFI_WEP_ENCRYPTION */
86 	case WIFI_SECURITY_TYPE_EAP: /* SL_WIFI_WPA2_ENTERPRISE/<various> */
87 	case WIFI_SECURITY_TYPE_WAPI:
88 	default:
89 		return -ENOTSUP;
90 	}
91 
92 	if (params->band != WIFI_FREQ_BAND_UNKNOWN && params->band != WIFI_FREQ_BAND_2_4_GHZ) {
93 		return -ENOTSUP;
94 	}
95 
96 	if (params->psk_length) {
97 		sl_net_set_credential(SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID, SL_NET_WIFI_PSK,
98 				      params->psk, params->psk_length);
99 	}
100 
101 	if (params->sae_password_length) {
102 		sl_net_set_credential(SL_NET_DEFAULT_WIFI_CLIENT_CREDENTIAL_ID, SL_NET_WIFI_PSK,
103 				      params->sae_password, params->sae_password_length);
104 	}
105 
106 	if (params->channel != WIFI_CHANNEL_ANY) {
107 		wifi_config.channel.channel = params->channel;
108 	}
109 
110 	wifi_config.ssid.length = params->ssid_length,
111 	memcpy(wifi_config.ssid.value, params->ssid, params->ssid_length);
112 
113 	ret = sl_wifi_connect(SL_WIFI_CLIENT_INTERFACE, &wifi_config, 0);
114 	if (ret != SL_STATUS_IN_PROGRESS) {
115 		return -EIO;
116 	}
117 
118 	return 0;
119 }
120 
siwx91x_disconnect(const struct device * dev)121 static int siwx91x_disconnect(const struct device *dev)
122 {
123 	struct siwx91x_dev *sidev = dev->data;
124 	int ret;
125 
126 	ret = sl_wifi_disconnect(SL_WIFI_CLIENT_INTERFACE);
127 	if (ret) {
128 		return -EIO;
129 	}
130 	if (IS_ENABLED(CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE)) {
131 		net_if_dormant_on(sidev->iface);
132 	}
133 	sidev->state = WIFI_STATE_DISCONNECTED;
134 	return 0;
135 }
136 
siwx91x_report_scan_res(struct siwx91x_dev * sidev,sl_wifi_scan_result_t * result,int item)137 static void siwx91x_report_scan_res(struct siwx91x_dev *sidev, sl_wifi_scan_result_t *result,
138 				    int item)
139 {
140 	static const struct {
141 		int sl_val;
142 		int z_val;
143 	} security_convert[] = {
144 		{ SL_WIFI_OPEN,            WIFI_SECURITY_TYPE_NONE    },
145 		{ SL_WIFI_WEP,             WIFI_SECURITY_TYPE_WEP     },
146 		{ SL_WIFI_WPA,             WIFI_SECURITY_TYPE_WPA_PSK },
147 		{ SL_WIFI_WPA2,            WIFI_SECURITY_TYPE_PSK     },
148 		{ SL_WIFI_WPA3,            WIFI_SECURITY_TYPE_SAE     },
149 		{ SL_WIFI_WPA3_TRANSITION, WIFI_SECURITY_TYPE_SAE     },
150 		{ SL_WIFI_WPA_ENTERPRISE,  WIFI_SECURITY_TYPE_EAP     },
151 		{ SL_WIFI_WPA2_ENTERPRISE, WIFI_SECURITY_TYPE_EAP     },
152 	};
153 
154 	struct wifi_scan_result tmp = {
155 		.channel = result->scan_info[item].rf_channel,
156 		.rssi = result->scan_info[item].rssi_val,
157 		.ssid_length = strlen(result->scan_info[item].ssid),
158 		.mac_length = sizeof(result->scan_info[item].bssid),
159 		.security = WIFI_SECURITY_TYPE_UNKNOWN,
160 		.mfp = WIFI_MFP_UNKNOWN,
161 		.band = WIFI_FREQ_BAND_2_4_GHZ,
162 	};
163 
164 	if (result->scan_count == 0) {
165 		return;
166 	}
167 
168 	if (result->scan_info[item].rf_channel <= 0 || result->scan_info[item].rf_channel > 14) {
169 		LOG_WRN("Unexpected scan result");
170 		tmp.band = WIFI_FREQ_BAND_UNKNOWN;
171 	}
172 
173 	memcpy(tmp.ssid, result->scan_info[item].ssid, tmp.ssid_length);
174 	memcpy(tmp.mac, result->scan_info[item].bssid, tmp.mac_length);
175 
176 	ARRAY_FOR_EACH(security_convert, i) {
177 		if (security_convert[i].sl_val == result->scan_info[item].security_mode) {
178 			tmp.security = security_convert[i].z_val;
179 		}
180 	}
181 
182 	sidev->scan_res_cb(sidev->iface, 0, &tmp);
183 }
184 
siwx91x_on_scan(sl_wifi_event_t event,sl_wifi_scan_result_t * result,uint32_t result_size,void * arg)185 static unsigned int siwx91x_on_scan(sl_wifi_event_t event, sl_wifi_scan_result_t *result,
186 				    uint32_t result_size, void *arg)
187 {
188 	struct siwx91x_dev *sidev = arg;
189 	int i, scan_count;
190 
191 	if (!sidev->scan_res_cb) {
192 		return -EFAULT;
193 	}
194 
195 	if (event & SL_WIFI_EVENT_FAIL_INDICATION) {
196 		memset(result, 0, sizeof(*result));
197 	}
198 
199 	if (sidev->scan_max_bss_cnt) {
200 		scan_count = MIN(result->scan_count, sidev->scan_max_bss_cnt);
201 	} else {
202 		scan_count = result->scan_count;
203 	}
204 
205 	for (i = 0; i < scan_count; i++) {
206 		siwx91x_report_scan_res(sidev, result, i);
207 	}
208 
209 	sidev->scan_res_cb(sidev->iface, 0, NULL);
210 	sidev->state = sidev->scan_prev_state;
211 
212 	return 0;
213 }
214 
siwx91x_scan(const struct device * dev,struct wifi_scan_params * z_scan_config,scan_result_cb_t cb)215 static int siwx91x_scan(const struct device *dev, struct wifi_scan_params *z_scan_config,
216 			scan_result_cb_t cb)
217 {
218 	sl_wifi_scan_configuration_t sl_scan_config = { };
219 	struct siwx91x_dev *sidev = dev->data;
220 	sl_wifi_interface_t interface;
221 	sl_wifi_ssid_t ssid = {};
222 	int ret;
223 
224 	__ASSERT(z_scan_config, "z_scan_config cannot be NULL");
225 
226 	interface = sl_wifi_get_default_interface();
227 	if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) {
228 		LOG_ERR("Interface not in STA mode");
229 		return -EINVAL;
230 	}
231 
232 	if (sidev->state != WIFI_STATE_DISCONNECTED && sidev->state != WIFI_STATE_INACTIVE &&
233 	    sidev->state != WIFI_STATE_COMPLETED) {
234 		LOG_ERR("Command given in invalid state");
235 		return -EBUSY;
236 	}
237 
238 	if (z_scan_config->bands & ~(BIT(WIFI_FREQ_BAND_UNKNOWN) | BIT(WIFI_FREQ_BAND_2_4_GHZ))) {
239 		LOG_ERR("Invalid band entered");
240 		return -EINVAL;
241 	}
242 
243 	if (z_scan_config->scan_type == WIFI_SCAN_TYPE_ACTIVE) {
244 		sl_scan_config.type = SL_WIFI_SCAN_TYPE_ACTIVE;
245 		ret = sl_si91x_configure_timeout(SL_SI91X_CHANNEL_ACTIVE_SCAN_TIMEOUT,
246 						 z_scan_config->dwell_time_active);
247 	} else {
248 		sl_scan_config.type = SL_WIFI_SCAN_TYPE_PASSIVE;
249 		ret = sl_si91x_configure_timeout(SL_SI91X_CHANNEL_PASSIVE_SCAN_TIMEOUT,
250 						 z_scan_config->dwell_time_passive);
251 	}
252 	if (ret) {
253 		return -EINVAL;
254 	}
255 
256 	for (int i = 0; i < ARRAY_SIZE(z_scan_config->band_chan); i++) {
257 		/* End of channel list */
258 		if (z_scan_config->band_chan[i].channel == 0) {
259 			break;
260 		}
261 
262 		if (z_scan_config->band_chan[i].band == WIFI_FREQ_BAND_2_4_GHZ) {
263 			sl_scan_config.channel_bitmap_2g4 |=
264 				BIT(z_scan_config->band_chan[i].channel - 1);
265 		}
266 	}
267 
268 	if (z_scan_config->band_chan[0].channel && !sl_scan_config.channel_bitmap_2g4) {
269 		LOG_ERR("No supported channels in the request");
270 		return -EINVAL;
271 	}
272 
273 	if (IS_ENABLED(CONFIG_WIFI_MGMT_SCAN_SSID_FILT_MAX)) {
274 		if (z_scan_config->ssids[0]) {
275 			strncpy(ssid.value, z_scan_config->ssids[0], WIFI_SSID_MAX_LEN);
276 			ssid.length = strlen(z_scan_config->ssids[0]);
277 		}
278 	}
279 
280 	sidev->scan_max_bss_cnt = z_scan_config->max_bss_cnt;
281 	sidev->scan_res_cb = cb;
282 	ret = sl_wifi_start_scan(SL_WIFI_CLIENT_2_4GHZ_INTERFACE, (ssid.length > 0) ? &ssid : NULL,
283 				 &sl_scan_config);
284 	if (ret != SL_STATUS_IN_PROGRESS) {
285 		return -EIO;
286 	}
287 	sidev->scan_prev_state = sidev->state;
288 	sidev->state = WIFI_STATE_SCANNING;
289 
290 	return 0;
291 }
292 
siwx91x_status(const struct device * dev,struct wifi_iface_status * status)293 static int siwx91x_status(const struct device *dev, struct wifi_iface_status *status)
294 {
295 	struct siwx91x_dev *sidev = dev->data;
296 	int32_t rssi = -1;
297 
298 	memset(status, 0, sizeof(*status));
299 	status->state = sidev->state;
300 	sl_wifi_get_signal_strength(SL_WIFI_CLIENT_INTERFACE, &rssi);
301 	status->rssi = rssi;
302 	return 0;
303 }
304 
305 #ifdef CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE
306 
siwx91x_send(const struct device * dev,struct net_pkt * pkt)307 static int siwx91x_send(const struct device *dev, struct net_pkt *pkt)
308 {
309 	size_t pkt_len = net_pkt_get_len(pkt);
310 	struct net_buf *buf = NULL;
311 	int ret;
312 
313 	if (net_pkt_get_len(pkt) > _NET_ETH_MAX_FRAME_SIZE) {
314 		LOG_ERR("unexpected buffer size");
315 		return -ENOBUFS;
316 	}
317 	buf = net_buf_alloc(&siwx91x_tx_pool, K_FOREVER);
318 	if (!buf) {
319 		return -ENOBUFS;
320 	}
321 	if (net_pkt_read(pkt, buf->data, pkt_len)) {
322 		net_buf_unref(buf);
323 		return -ENOBUFS;
324 	}
325 	net_buf_add(buf, pkt_len);
326 
327 	ret = sl_wifi_send_raw_data_frame(SL_WIFI_CLIENT_INTERFACE, buf->data, pkt_len);
328 	if (ret) {
329 		net_buf_unref(buf);
330 		return -EIO;
331 	}
332 
333 	net_pkt_unref(pkt);
334 	net_buf_unref(buf);
335 
336 	return 0;
337 }
338 
339 /* Receive callback. Keep the name as it is declared weak in WiseConnect */
sl_si91x_host_process_data_frame(sl_wifi_interface_t interface,sl_wifi_buffer_t * buffer)340 sl_status_t sl_si91x_host_process_data_frame(sl_wifi_interface_t interface,
341 					     sl_wifi_buffer_t *buffer)
342 {
343 	sl_si91x_packet_t *si_pkt = sl_si91x_host_get_buffer_data(buffer, 0, NULL);
344 	struct net_if *iface = net_if_get_first_wifi();
345 	struct net_pkt *pkt;
346 	int ret;
347 
348 	pkt = net_pkt_rx_alloc_with_buffer(iface, buffer->length, AF_UNSPEC, 0, K_NO_WAIT);
349 	if (!pkt) {
350 		LOG_ERR("net_pkt_rx_alloc_with_buffer() failed");
351 		return SL_STATUS_FAIL;
352 	}
353 	ret = net_pkt_write(pkt, si_pkt->data, si_pkt->length);
354 	if (ret < 0) {
355 		LOG_ERR("net_pkt_write(): %d", ret);
356 		goto unref;
357 	}
358 	ret = net_recv_data(iface, pkt);
359 	if (ret < 0) {
360 		LOG_ERR("net_recv_data((): %d", ret);
361 		goto unref;
362 	}
363 	return 0;
364 
365 unref:
366 	net_pkt_unref(pkt);
367 	return SL_STATUS_FAIL;
368 }
369 
370 #endif
371 
siwx91x_ethernet_init(struct net_if * iface)372 static void siwx91x_ethernet_init(struct net_if *iface)
373 {
374 	struct ethernet_context *eth_ctx;
375 
376 	if (IS_ENABLED(CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE)) {
377 		eth_ctx = net_if_l2_data(iface);
378 		eth_ctx->eth_if_type = L2_ETH_IF_TYPE_WIFI;
379 		ethernet_init(iface);
380 	}
381 }
382 
siwx91x_iface_init(struct net_if * iface)383 static void siwx91x_iface_init(struct net_if *iface)
384 {
385 	struct siwx91x_dev *sidev = iface->if_dev->dev->data;
386 	sl_status_t status;
387 
388 	sidev->state = WIFI_STATE_INTERFACE_DISABLED;
389 	sidev->iface = iface;
390 
391 	sl_wifi_set_scan_callback(siwx91x_on_scan, sidev);
392 	sl_wifi_set_join_callback(siwx91x_on_join, sidev);
393 
394 	status = sl_wifi_get_mac_address(SL_WIFI_CLIENT_INTERFACE, &sidev->macaddr);
395 	if (status) {
396 		LOG_ERR("sl_wifi_get_mac_address(): %#04x", status);
397 		return;
398 	}
399 	net_if_set_link_addr(iface, sidev->macaddr.octet, sizeof(sidev->macaddr.octet),
400 			     NET_LINK_ETHERNET);
401 	siwx91x_sock_init(iface);
402 	siwx91x_ethernet_init(iface);
403 
404 	sidev->state = WIFI_STATE_INACTIVE;
405 }
406 
siwx91x_dev_init(const struct device * dev)407 static int siwx91x_dev_init(const struct device *dev)
408 {
409 	return 0;
410 }
411 
412 static const struct wifi_mgmt_ops siwx91x_mgmt = {
413 	.scan         = siwx91x_scan,
414 	.connect      = siwx91x_connect,
415 	.disconnect   = siwx91x_disconnect,
416 	.iface_status = siwx91x_status,
417 };
418 
419 static const struct net_wifi_mgmt_offload siwx91x_api = {
420 	.wifi_iface.iface_api.init = siwx91x_iface_init,
421 #ifdef CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE
422 	.wifi_iface.send = siwx91x_send,
423 #else
424 	.wifi_iface.get_type = siwx91x_get_type,
425 #endif
426 	.wifi_mgmt_api = &siwx91x_mgmt,
427 };
428 
429 static struct siwx91x_dev sidev;
430 
431 #ifdef CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_NATIVE
432 ETH_NET_DEVICE_DT_INST_DEFINE(0, siwx91x_dev_init, NULL, &sidev, NULL,
433 			      CONFIG_WIFI_INIT_PRIORITY, &siwx91x_api, NET_ETH_MTU);
434 #else
435 NET_DEVICE_DT_INST_OFFLOAD_DEFINE(0, siwx91x_dev_init, NULL, &sidev, NULL,
436 				  CONFIG_WIFI_INIT_PRIORITY, &siwx91x_api, NET_ETH_MTU);
437 #endif
438