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 56typedef 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/* 64static 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 76int 77lws_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 109static void 110lws_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 179next: 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 190static wifi_scan_config_t scan_config = { 191 .ssid = 0, 192 .bssid = 0, 193 .channel = 0, 194 .show_hidden = true 195}; 196 197void 198lws_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 214int 215lws_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 283static void 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 320static int 321espip_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 339static void 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 */ 382int 383lws_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 */ 395int 396lws_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 413struct lws_netdev_instance * 414lws_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 430int 431lws_netdev_wifi_configure_plat(struct lws_netdev_instance *nd, 432 lws_netdev_config_t *config) 433{ 434 return 0; 435} 436 437int 438lws_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 465int 466lws_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 491void 492lws_netdev_wifi_destroy_plat(struct lws_netdev_instance **pnd) 493{ 494 lws_free(*pnd); 495 *pnd = NULL; 496} 497