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