• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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