1 /*
2 * libwebsockets - esp32 wifi -> lws_netdev_wifi
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 *
25 * These are the esp platform wifi-specific netdev pieces. Nothing else should
26 * know any esp-specific apis.
27 *
28 * Operations happen via the generic lws_detdev instantiation for the platform
29 * wifi device, which point in here for operations. We also set up native OS
30 * event hooks per device for wifi and IP stack events, and post them as lws_smd
31 * NETWORK events on the if in the "platform private" namespace. We then
32 * service the events in the lws event loop thread context, which may again
33 * generate lws_smd NETWORK events in the public namespace depending on what
34 * happened.
35 *
36 * Scan requests go through a sul to make sure we don't get "piling on" from
37 * scheduled, timed scans. Scan results go through the lws_smd "washing" and
38 * are actually parsed in lws thread context, where they are converted to lws
39 * netdev scan results and processed by generic code.
40 */
41
42 #include "private-lib-core.h"
43
44 #include "esp_system.h"
45 #include "esp_spi_flash.h"
46 #include "esp_wifi.h"
47 #include <nvs_flash.h>
48 #include <esp_netif.h>
49
50 /*
51 * lws_netdev_instance_t:
52 * lws_netdev_instance_wifi_t:
53 * lws_netdev_instance_wifi_esp32_t
54 */
55
56 typedef struct lws_netdev_instance_wifi_esp32 {
57 lws_netdev_instance_wifi_t wnd;
58 esp_event_handler_instance_t instance_any_id;
59 esp_event_handler_instance_t instance_got_ip;
60 wifi_config_t sta_config;
61 } lws_netdev_instance_wifi_esp32_t;
62
63 /*
64 static wifi_config_t config = {
65 .ap = {
66 .channel = 6,
67 .authmode = WIFI_AUTH_OPEN,
68 .max_connection = 1,
69 } };
70 */
71
72 /*
73 * Platform-specific connect / associate
74 */
75
76 int
lws_netdev_wifi_connect_plat(lws_netdev_instance_t * nd,const char * ssid,const char * passphrase,uint8_t * bssid)77 lws_netdev_wifi_connect_plat(lws_netdev_instance_t *nd, const char *ssid,
78 const char *passphrase, uint8_t *bssid)
79 {
80 lws_netdev_instance_wifi_esp32_t *wnde32 =
81 (lws_netdev_instance_wifi_esp32_t *)nd;
82
83 wnde32->wnd.inst.ops->up(&wnde32->wnd.inst);
84
85 wnde32->wnd.flags |= LNDIW_MODE_STA;
86 esp_wifi_set_mode(WIFI_MODE_STA);
87
88 #if 0
89 /* we will do our own dhcp */
90 tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
91 #endif
92
93 lws_strncpy((char *)wnde32->sta_config.sta.ssid, ssid,
94 sizeof(wnde32->sta_config.sta.ssid));
95 lws_strncpy((char *)wnde32->sta_config.sta.password, passphrase,
96 sizeof(wnde32->sta_config.sta.password));
97
98 esp_wifi_set_config(WIFI_IF_STA, &wnde32->sta_config);
99 esp_wifi_connect();
100
101 return 0;
102 }
103
104 /*
105 * This is called from the SMD / lws thread context, after we heard there were
106 * scan results on this netdev
107 */
108
109 static void
lws_esp32_scan_update(lws_netdev_instance_wifi_t * wnd)110 lws_esp32_scan_update(lws_netdev_instance_wifi_t *wnd)
111 {
112 // lws_netdevs_t *netdevs = lws_netdevs_from_ndi(&wnd->inst);
113 wifi_ap_record_t ap_records[LWS_WIFI_MAX_SCAN_TRACK], *ar;
114 uint32_t now = lws_now_secs();
115 uint16_t count_ap_records;
116 int n;
117
118 count_ap_records = LWS_ARRAY_SIZE(ap_records);
119 if (esp_wifi_scan_get_ap_records(&count_ap_records, ap_records)) {
120 lwsl_err("%s: failed\n", __func__);
121 return;
122 }
123
124 if (!count_ap_records)
125 return;
126
127 if (wnd->state != LWSNDVWIFI_STATE_SCAN)
128 return;
129
130 /*
131 * ... let's collect the OS-specific scan results, and convert then to
132 * lws_netdev sorted by rssi. If we already have it in the scan list,
133 * keep it and keep a little ringbuffer of its rssi along with an
134 * averaging. If it's new, add it into the linked-list sorted by rssi.
135 */
136
137 ar = &ap_records[0];
138 for (n = 0; n < count_ap_records; n++) {
139 lws_wifi_sta_t *w;
140 int m;
141
142 m = strlen((const char *)ar->ssid);
143 if (!m)
144 goto next;
145
146 /*
147 * We know this guy from before?
148 */
149
150 w = lws_netdev_wifi_scan_find(wnd, (const char *)ar->ssid,
151 ar->bssid);
152 if (!w) {
153 w = lws_zalloc(sizeof(*w) + m + 1, __func__);
154 if (!w)
155 goto next;
156
157 w->ssid = (char *)&w[1];
158 memcpy(w->ssid, ar->ssid, m + 1);
159 w->ssid_len = m;
160
161 memcpy(w->bssid, ar->bssid, 6);
162
163 lws_dll2_add_sorted(&w->list, &wnd->scan,
164 lws_netdev_wifi_rssi_sort_compare);
165 }
166
167 if (w->rssi_count == LWS_ARRAY_SIZE(w->rssi))
168 w->rssi_avg -= w->rssi[w->rssi_next];
169 else
170 w->rssi_count++;
171 w->rssi[w->rssi_next] = ar->rssi;
172 w->rssi_avg += w->rssi[w->rssi_next++];
173 w->rssi_next = w->rssi_next & (LWS_ARRAY_SIZE(w->rssi) - 1);
174
175 w->ch = ar->primary;
176 w->authmode = ar->authmode;
177 w->last_seen = now;
178
179 next:
180 ar++;
181 }
182
183 /*
184 * We can do the rest of it using the generic scan list and credentials
185 */
186
187 lws_netdev_wifi_scan_select(wnd);
188 }
189
190 static wifi_scan_config_t scan_config = {
191 .ssid = 0,
192 .bssid = 0,
193 .channel = 0,
194 .show_hidden = true
195 };
196
197 void
lws_netdev_wifi_scan_plat(lws_netdev_instance_t * nd)198 lws_netdev_wifi_scan_plat(lws_netdev_instance_t *nd)
199 {
200 lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)nd;
201
202 if (esp_wifi_scan_start(&scan_config, false))
203 lwsl_err("%s: %s scan failed\n", __func__, wnd->inst.name);
204 }
205
206 /*
207 * Platform-private interface events turn up here after going through SMD and
208 * passed down by matching network interface name via generic lws_netdev. All
209 * that messing around gets us from an OS-specific thread with an event to back
210 * here in lws event loop thread context, with the same event bound to a the
211 * netdev it belongs to.
212 */
213
214 int
lws_netdev_wifi_event_plat(struct lws_netdev_instance * nd,lws_usec_t timestamp,void * buf,size_t len)215 lws_netdev_wifi_event_plat(struct lws_netdev_instance *nd, lws_usec_t timestamp,
216 void *buf, size_t len)
217 {
218 lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)nd;
219 struct lws_context *ctx = netdev_instance_to_ctx(&wnd->inst);
220 size_t al;
221
222 /*
223 * netdev-private sync messages?
224 */
225
226 if (!lws_json_simple_strcmp(buf, len, "\"type\":", "priv")) {
227 const char *ev = lws_json_simple_find(buf, len, "\"ev\":", &al);
228
229 if (!ev)
230 return 0;
231
232 lwsl_notice("%s: smd priv ev %.*s\n", __func__, (int)al, ev);
233
234 switch (atoi(ev)) {
235 case WIFI_EVENT_STA_START:
236 wnd->state = LWSNDVWIFI_STATE_INITIAL;
237 if (!lws_netdev_wifi_redo_last(wnd))
238 break;
239
240 /*
241 * if the "try last successful" one fails, start the
242 * scan by falling through
243 */
244
245 case WIFI_EVENT_STA_DISCONNECTED:
246 lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
247 "{\"type\":\"linkdown\","
248 "\"if\":\"%s\"}", wnd->inst.name);
249 wnd->state = LWSNDVWIFI_STATE_SCAN;
250 /*
251 * We do it via the sul so we don't get timed scans
252 * on top of each other
253 */
254 lws_sul_schedule(ctx, 0, &wnd->sul_scan,
255 lws_netdev_wifi_scan, 1);
256 break;
257
258 case WIFI_EVENT_STA_CONNECTED:
259 lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
260 "{\"type\":\"linkup\","
261 "\"if\":\"%s\"}", wnd->inst.name);
262 break;
263
264 case WIFI_EVENT_SCAN_DONE:
265 lws_esp32_scan_update(wnd);
266 break;
267 default:
268 return 0;
269 }
270
271 return 0;
272 }
273
274 return 0;
275 }
276
277 /*
278 * This is coming from a thread context unrelated to lws... the first order is
279 * to turn these into lws_smd events synchronized on lws thread, since we want
280 * to change correspsonding lws netdev object states without locking.
281 */
282
283 static void
_event_handler_wifi(void * arg,esp_event_base_t event_base,int32_t event_id,void * event_data)284 _event_handler_wifi(void *arg, esp_event_base_t event_base, int32_t event_id,
285 void *event_data)
286 {
287 lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)arg;
288 struct lws_context *ctx = netdev_instance_to_ctx(&wnd->inst);
289
290 switch (event_id) {
291 case WIFI_EVENT_STA_START:
292 case WIFI_EVENT_STA_DISCONNECTED:
293 case WIFI_EVENT_SCAN_DONE:
294 case WIFI_EVENT_STA_CONNECTED:
295 /*
296 * These are events in the platform's private namespace,
297 * interpreted only by the lws_smd handler above, ** in the lws
298 * event thread context **. The point of this is to requeue the
299 * event in the lws thread context like a bottom-half.
300 *
301 * To save on registrations, the context's NETWORK smd
302 * participant passes messages to lws_netdev, who passes ones
303 * that have if matching the netdev name to that netdev's
304 * (*event) handler.
305 *
306 * The other handler may emit generic network state SMD events
307 * for other things to consume.
308 */
309
310 lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
311 "{\"type\":\"priv\",\"if\":\"%s\",\"ev\":%d}",
312 wnd->inst.name, (int)event_id);
313 break;
314 default:
315 return;
316 }
317 }
318
319 #if 0
320 static int
321 espip_to_sa46(lws_sockaddr46 *sa46, esp_ip_addr_t *eip)
322 {
323 memset(sa46, 0, sizeof(sa46));
324
325 switch (eip->type) {
326 case ESP_IPADDR_TYPE_V4:
327 sa46->sa4.sin_family = AF_INET;
328 memcpy(sa46->sa4.sin_addr, &eip->u_addr.ip4.addr, );
329 return;
330 case ESP_IPADDR_TYPE_V6:
331 }
332 }
333 #endif
334
335 /*
336 * This is coming from a thread context unrelated to lws
337 */
338
339 static void
_event_handler_ip(void * arg,esp_event_base_t event_base,int32_t event_id,void * event_data)340 _event_handler_ip(void *arg, esp_event_base_t event_base, int32_t event_id,
341 void *event_data)
342 {
343 lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)arg;
344 lws_netdevs_t *netdevs = lws_netdevs_from_ndi(&wnd->inst);
345 struct lws_context *ctx = lws_context_from_netdevs(netdevs);
346
347 if (event_id == IP_EVENT_STA_GOT_IP) {
348 ip_event_got_ip_t *e = (ip_event_got_ip_t *)event_data;
349 char ip[16];
350 #if 0
351 tcpip_adapter_dns_info_t e32ip;
352
353 /*
354 * Since atm we get this via DHCP, presumably we can get ahold
355 * of related info set by the router
356 */
357
358 if (tcpip_adapter_get_dns_info(TCPIP_ADAPTER_IF_STA,
359 TCPIP_ADAPTER_DNS_MAIN,
360 /* also _BACKUP, _FALLBACK */
361 &e32ip)) {
362 lwsl_err("%s: there's no dns server set\n", __func__);
363 e32ip.ip.u_addr.ipv4 = 0x08080808;
364 e32ip.ip.type = ESP_IPADDR_TYPE_V4;
365 }
366
367 netdevs->sa46_dns_resolver.
368 #endif
369
370 lws_write_numeric_address((void *)&e->ip_info.ip, 4, ip,
371 sizeof(ip));
372 lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
373 "{\"type\":\"ipacq\",\"if\":\"%s\","
374 "\"ipv4\":\"%s\"}", wnd->inst.name, ip);
375 }
376 }
377
378 /*
379 * This is the platform (esp-idf) init for any kind of networking to be
380 * available at all
381 */
382 int
lws_netdev_plat_init(void)383 lws_netdev_plat_init(void)
384 {
385 nvs_flash_init();
386 esp_netif_init();
387 ESP_ERROR_CHECK(esp_event_loop_create_default());
388
389 return 0;
390 }
391
392 /*
393 * This is the platform (esp-idf) init for any wifi to be available at all
394 */
395 int
lws_netdev_plat_wifi_init(void)396 lws_netdev_plat_wifi_init(void)
397 {
398 wifi_init_config_t wic = WIFI_INIT_CONFIG_DEFAULT();
399 int n;
400
401 esp_netif_create_default_wifi_sta();
402
403 n = esp_wifi_init(&wic);
404 if (n) {
405 lwsl_err("%s: wifi init fail: %d\n", __func__, n);
406 return 1;
407 }
408
409 return 0;
410 }
411
412
413 struct lws_netdev_instance *
lws_netdev_wifi_create_plat(struct lws_context * ctx,const lws_netdev_ops_t * ops,const char * name,void * platinfo)414 lws_netdev_wifi_create_plat(struct lws_context *ctx,
415 const lws_netdev_ops_t *ops,
416 const char *name, void *platinfo)
417 {
418 lws_netdev_instance_wifi_esp32_t *wnde32 = lws_zalloc(
419 sizeof(*wnde32), __func__);
420
421 if (!wnde32)
422 return NULL;
423
424 wnde32->wnd.inst.type = LWSNDTYP_WIFI;
425 lws_netdev_instance_create(&wnde32->wnd.inst, ctx, ops, name, platinfo);
426
427 return &wnde32->wnd.inst;
428 }
429
430 int
lws_netdev_wifi_configure_plat(struct lws_netdev_instance * nd,lws_netdev_config_t * config)431 lws_netdev_wifi_configure_plat(struct lws_netdev_instance *nd,
432 lws_netdev_config_t *config)
433 {
434 return 0;
435 }
436
437 int
lws_netdev_wifi_up_plat(struct lws_netdev_instance * nd)438 lws_netdev_wifi_up_plat(struct lws_netdev_instance *nd)
439 {
440 lws_netdev_instance_wifi_esp32_t *wnde32 =
441 (lws_netdev_instance_wifi_esp32_t *)nd;
442 struct lws_context *ctx = netdev_instance_to_ctx(&wnde32->wnd.inst);
443
444 if (wnde32->wnd.flags & LNDIW_UP)
445 return 0;
446
447 ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
448 IP_EVENT_STA_GOT_IP, &_event_handler_ip, nd,
449 &wnde32->instance_got_ip));
450
451 ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
452 ESP_EVENT_ANY_ID, &_event_handler_wifi, nd,
453 &wnde32->instance_any_id));
454
455 esp_wifi_start();
456 wnde32->wnd.flags |= LNDIW_UP;
457
458 lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
459 "{\"type\":\"up\",\"if\":\"%s\"}",
460 wnde32->wnd.inst.name);
461
462 return 0;
463 }
464
465 int
lws_netdev_wifi_down_plat(struct lws_netdev_instance * nd)466 lws_netdev_wifi_down_plat(struct lws_netdev_instance *nd)
467 {
468 lws_netdev_instance_wifi_esp32_t *wnde32 =
469 (lws_netdev_instance_wifi_esp32_t *)nd;
470 struct lws_context *ctx = netdev_instance_to_ctx(&wnde32->wnd.inst);
471
472 if (!(wnde32->wnd.flags & LNDIW_UP))
473 return 0;
474
475 lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
476 "{\"type\":\"down\",\"if\":\"%s\"}",
477 wnde32->wnd.inst.name);
478
479 esp_wifi_stop();
480
481 esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP,
482 &wnde32->instance_got_ip);
483 esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID,
484 &wnde32->instance_any_id);
485
486 wnde32->wnd.flags &= ~LNDIW_UP;
487
488 return 0;
489 }
490
491 void
lws_netdev_wifi_destroy_plat(struct lws_netdev_instance ** pnd)492 lws_netdev_wifi_destroy_plat(struct lws_netdev_instance **pnd)
493 {
494 lws_free(*pnd);
495 *pnd = NULL;
496 }
497