wifi-esp32.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /*
  2. * libwebsockets - esp32 wifi -> lws_netdev_wifi
  3. *
  4. * Copyright (C) 2010 - 2020 Andy Green <[email protected]>
  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. #include "private-lib-core.h"
  42. #include "esp_system.h"
  43. #include "esp_spi_flash.h"
  44. #include "esp_wifi.h"
  45. #include <nvs_flash.h>
  46. #include <esp_netif.h>
  47. /*
  48. * lws_netdev_instance_t:
  49. * lws_netdev_instance_wifi_t:
  50. * lws_netdev_instance_wifi_esp32_t
  51. */
  52. typedef struct lws_netdev_instance_wifi_esp32 {
  53. lws_netdev_instance_wifi_t wnd;
  54. esp_event_handler_instance_t instance_any_id;
  55. esp_event_handler_instance_t instance_got_ip;
  56. wifi_config_t sta_config;
  57. } lws_netdev_instance_wifi_esp32_t;
  58. /*
  59. static wifi_config_t config = {
  60. .ap = {
  61. .channel = 6,
  62. .authmode = WIFI_AUTH_OPEN,
  63. .max_connection = 1,
  64. } };
  65. */
  66. /*
  67. * Platform-specific connect / associate
  68. */
  69. int
  70. lws_netdev_wifi_connect_plat(lws_netdev_instance_t *nd, const char *ssid,
  71. const char *passphrase, uint8_t *bssid)
  72. {
  73. lws_netdev_instance_wifi_esp32_t *wnde32 =
  74. (lws_netdev_instance_wifi_esp32_t *)nd;
  75. wnde32->wnd.inst.ops->up(&wnde32->wnd.inst);
  76. wnde32->wnd.flags |= LNDIW_MODE_STA;
  77. esp_wifi_set_mode(WIFI_MODE_STA);
  78. #if 0
  79. /* we will do our own dhcp */
  80. tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
  81. #endif
  82. lws_strncpy((char *)wnde32->sta_config.sta.ssid, ssid,
  83. sizeof(wnde32->sta_config.sta.ssid));
  84. lws_strncpy((char *)wnde32->sta_config.sta.password, passphrase,
  85. sizeof(wnde32->sta_config.sta.password));
  86. esp_wifi_set_config(WIFI_IF_STA, &wnde32->sta_config);
  87. esp_wifi_connect();
  88. return 0;
  89. }
  90. /*
  91. * This is called from the SMD / lws thread context, after we heard there were
  92. * scan results on this netdev
  93. */
  94. static void
  95. lws_esp32_scan_update(lws_netdev_instance_wifi_t *wnd)
  96. {
  97. // lws_netdevs_t *netdevs = lws_netdevs_from_ndi(&wnd->inst);
  98. wifi_ap_record_t ap_records[LWS_WIFI_MAX_SCAN_TRACK], *ar;
  99. uint32_t now = lws_now_secs();
  100. uint16_t count_ap_records;
  101. int n;
  102. count_ap_records = LWS_ARRAY_SIZE(ap_records);
  103. if (esp_wifi_scan_get_ap_records(&count_ap_records, ap_records)) {
  104. lwsl_err("%s: failed\n", __func__);
  105. return;
  106. }
  107. if (!count_ap_records)
  108. return;
  109. if (wnd->state != LWSNDVWIFI_STATE_SCAN)
  110. return;
  111. /*
  112. * ... let's collect the OS-specific scan results, and convert then to
  113. * lws_netdev sorted by rssi. If we already have it in the scan list,
  114. * keep it and keep a little ringbuffer of its rssi along with an
  115. * averaging. If it's new, add it into the linked-list sorted by rssi.
  116. */
  117. ar = &ap_records[0];
  118. for (n = 0; n < count_ap_records; n++) {
  119. lws_wifi_sta_t *w;
  120. int m;
  121. m = strlen((const char *)ar->ssid);
  122. if (!m)
  123. goto next;
  124. /*
  125. * We know this guy from before?
  126. */
  127. w = lws_netdev_wifi_scan_find(wnd, (const char *)ar->ssid,
  128. ar->bssid);
  129. if (!w) {
  130. w = lws_zalloc(sizeof(*w) + m + 1, __func__);
  131. if (!w)
  132. goto next;
  133. w->ssid = (char *)&w[1];
  134. memcpy(w->ssid, ar->ssid, m + 1);
  135. w->ssid_len = m;
  136. memcpy(w->bssid, ar->bssid, 6);
  137. lws_dll2_add_sorted(&w->list, &wnd->scan,
  138. lws_netdev_wifi_rssi_sort_compare);
  139. }
  140. if (w->rssi_count == LWS_ARRAY_SIZE(w->rssi))
  141. w->rssi_avg -= w->rssi[w->rssi_next];
  142. else
  143. w->rssi_count++;
  144. w->rssi[w->rssi_next] = ar->rssi;
  145. w->rssi_avg += w->rssi[w->rssi_next++];
  146. w->rssi_next = w->rssi_next & (LWS_ARRAY_SIZE(w->rssi) - 1);
  147. w->ch = ar->primary;
  148. w->authmode = ar->authmode;
  149. w->last_seen = now;
  150. next:
  151. ar++;
  152. }
  153. /*
  154. * We can do the rest of it using the generic scan list and credentials
  155. */
  156. lws_netdev_wifi_scan_select(wnd);
  157. }
  158. static wifi_scan_config_t scan_config = {
  159. .ssid = 0,
  160. .bssid = 0,
  161. .channel = 0,
  162. .show_hidden = true
  163. };
  164. void
  165. lws_netdev_wifi_scan_plat(lws_netdev_instance_t *nd)
  166. {
  167. lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)nd;
  168. if (esp_wifi_scan_start(&scan_config, false))
  169. lwsl_err("%s: %s scan failed\n", __func__, wnd->inst.name);
  170. }
  171. /*
  172. * Platform-private interface events turn up here after going through SMD and
  173. * passed down by matching network interface name via generic lws_netdev. All
  174. * that messing around gets us from an OS-specific thread with an event to back
  175. * here in lws event loop thread context, with the same event bound to a the
  176. * netdev it belongs to.
  177. */
  178. int
  179. lws_netdev_wifi_event_plat(struct lws_netdev_instance *nd, lws_usec_t timestamp,
  180. void *buf, size_t len)
  181. {
  182. lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)nd;
  183. struct lws_context *ctx = netdev_instance_to_ctx(&wnd->inst);
  184. size_t al;
  185. /*
  186. * netdev-private sync messages?
  187. */
  188. if (!lws_json_simple_strcmp(buf, len, "\"type\":", "priv")) {
  189. const char *ev = lws_json_simple_find(buf, len, "\"ev\":", &al);
  190. if (!ev)
  191. return 0;
  192. lwsl_notice("%s: smd priv ev %.*s\n", __func__, (int)al, ev);
  193. switch (atoi(ev)) {
  194. case WIFI_EVENT_STA_START:
  195. wnd->state = LWSNDVWIFI_STATE_INITIAL;
  196. if (!lws_netdev_wifi_redo_last(wnd))
  197. break;
  198. /*
  199. * if the "try last successful" one fails, start the
  200. * scan by falling through
  201. */
  202. case WIFI_EVENT_STA_DISCONNECTED:
  203. lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
  204. "{\"type\":\"linkdown\","
  205. "\"if\":\"%s\"}", wnd->inst.name);
  206. wnd->state = LWSNDVWIFI_STATE_SCAN;
  207. /*
  208. * We do it via the sul so we don't get timed scans
  209. * on top of each other
  210. */
  211. lws_sul_schedule(ctx, 0, &wnd->sul_scan,
  212. lws_netdev_wifi_scan, 1);
  213. break;
  214. case WIFI_EVENT_STA_CONNECTED:
  215. lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
  216. "{\"type\":\"linkup\","
  217. "\"if\":\"%s\"}", wnd->inst.name);
  218. break;
  219. case WIFI_EVENT_SCAN_DONE:
  220. lws_esp32_scan_update(wnd);
  221. break;
  222. default:
  223. return 0;
  224. }
  225. return 0;
  226. }
  227. return 0;
  228. }
  229. /*
  230. * This is coming from a thread context unrelated to lws... the first order is
  231. * to turn these into lws_smd events synchronized on lws thread, since we want
  232. * to change correspsonding lws netdev object states without locking.
  233. */
  234. static void
  235. _event_handler_wifi(void *arg, esp_event_base_t event_base, int32_t event_id,
  236. void *event_data)
  237. {
  238. lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)arg;
  239. struct lws_context *ctx = netdev_instance_to_ctx(&wnd->inst);
  240. switch (event_id) {
  241. case WIFI_EVENT_STA_START:
  242. case WIFI_EVENT_STA_DISCONNECTED:
  243. case WIFI_EVENT_SCAN_DONE:
  244. case WIFI_EVENT_STA_CONNECTED:
  245. /*
  246. * These are events in the platform's private namespace,
  247. * interpreted only by the lws_smd handler above, ** in the lws
  248. * event thread context **. The point of this is to requeue the
  249. * event in the lws thread context like a bottom-half.
  250. *
  251. * To save on registrations, the context's NETWORK smd
  252. * participant passes messages to lws_netdev, who passes ones
  253. * that have if matching the netdev name to that netdev's
  254. * (*event) handler.
  255. *
  256. * The other handler may emit generic network state SMD events
  257. * for other things to consume.
  258. */
  259. lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
  260. "{\"type\":\"priv\",\"if\":\"%s\",\"ev\":%d}",
  261. wnd->inst.name, event_id);
  262. break;
  263. default:
  264. return;
  265. }
  266. }
  267. #if 0
  268. static int
  269. espip_to_sa46(lws_sockaddr46 *sa46, esp_ip_addr_t *eip)
  270. {
  271. memset(sa46, 0, sizeof(sa46));
  272. switch (eip->type) {
  273. case ESP_IPADDR_TYPE_V4:
  274. sa46->sa4.sin_family = AF_INET;
  275. memcpy(sa46->sa4.sin_addr, &eip->u_addr.ip4.addr, );
  276. return;
  277. case ESP_IPADDR_TYPE_V6:
  278. }
  279. }
  280. #endif
  281. /*
  282. * This is coming from a thread context unrelated to lws
  283. */
  284. static void
  285. _event_handler_ip(void *arg, esp_event_base_t event_base, int32_t event_id,
  286. void *event_data)
  287. {
  288. lws_netdev_instance_wifi_t *wnd = (lws_netdev_instance_wifi_t *)arg;
  289. lws_netdevs_t *netdevs = lws_netdevs_from_ndi(&wnd->inst);
  290. struct lws_context *ctx = lws_context_from_netdevs(netdevs);
  291. if (event_id == IP_EVENT_STA_GOT_IP) {
  292. ip_event_got_ip_t *e = (ip_event_got_ip_t *)event_data;
  293. char ip[16];
  294. #if 0
  295. tcpip_adapter_dns_info_t e32ip;
  296. /*
  297. * Since atm we get this via DHCP, presumably we can get ahold
  298. * of related info set by the router
  299. */
  300. if (tcpip_adapter_get_dns_info(TCPIP_ADAPTER_IF_STA,
  301. TCPIP_ADAPTER_DNS_MAIN,
  302. /* also _BACKUP, _FALLBACK */
  303. &e32ip)) {
  304. lwsl_err("%s: there's no dns server set\n", __func__);
  305. e32ip.ip.u_addr.ipv4 = 0x08080808;
  306. e32ip.ip.type = ESP_IPADDR_TYPE_V4;
  307. }
  308. netdevs->sa46_dns_resolver.
  309. #endif
  310. lws_write_numeric_address((void *)&e->ip_info.ip, 4, ip,
  311. sizeof(ip));
  312. lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
  313. "{\"type\":\"ipacq\",\"if\":\"%s\","
  314. "\"ipv4\":\"%s\"}", wnd->inst.name, ip);
  315. }
  316. }
  317. /*
  318. * This is the platform (esp-idf) init for any kind of networking to be
  319. * available at all
  320. */
  321. int
  322. lws_netdev_plat_init(void)
  323. {
  324. nvs_flash_init();
  325. esp_netif_init();
  326. ESP_ERROR_CHECK(esp_event_loop_create_default());
  327. return 0;
  328. }
  329. /*
  330. * This is the platform (esp-idf) init for any wifi to be available at all
  331. */
  332. int
  333. lws_netdev_plat_wifi_init(void)
  334. {
  335. wifi_init_config_t wic = WIFI_INIT_CONFIG_DEFAULT();
  336. int n;
  337. esp_netif_create_default_wifi_sta();
  338. n = esp_wifi_init(&wic);
  339. if (n) {
  340. lwsl_err("%s: wifi init fail: %d\n", __func__, n);
  341. return 1;
  342. }
  343. return 0;
  344. }
  345. struct lws_netdev_instance *
  346. lws_netdev_wifi_create_plat(struct lws_context *ctx,
  347. const lws_netdev_ops_t *ops,
  348. const char *name, void *platinfo)
  349. {
  350. lws_netdev_instance_wifi_esp32_t *wnde32 = lws_zalloc(
  351. sizeof(*wnde32), __func__);
  352. if (!wnde32)
  353. return NULL;
  354. wnde32->wnd.inst.type = LWSNDTYP_WIFI;
  355. lws_netdev_instance_create(&wnde32->wnd.inst, ctx, ops, name, platinfo);
  356. return &wnde32->wnd.inst;
  357. }
  358. int
  359. lws_netdev_wifi_configure_plat(struct lws_netdev_instance *nd,
  360. lws_netdev_config_t *config)
  361. {
  362. return 0;
  363. }
  364. int
  365. lws_netdev_wifi_up_plat(struct lws_netdev_instance *nd)
  366. {
  367. lws_netdev_instance_wifi_esp32_t *wnde32 =
  368. (lws_netdev_instance_wifi_esp32_t *)nd;
  369. struct lws_context *ctx = netdev_instance_to_ctx(&wnde32->wnd.inst);
  370. if (wnde32->wnd.flags & LNDIW_UP)
  371. return 0;
  372. ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
  373. IP_EVENT_STA_GOT_IP, &_event_handler_ip, nd,
  374. &wnde32->instance_got_ip));
  375. ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
  376. ESP_EVENT_ANY_ID, &_event_handler_wifi, nd,
  377. &wnde32->instance_any_id));
  378. esp_wifi_start();
  379. wnde32->wnd.flags |= LNDIW_UP;
  380. lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
  381. "{\"type\":\"up\",\"if\":\"%s\"}",
  382. wnde32->wnd.inst.name);
  383. return 0;
  384. }
  385. int
  386. lws_netdev_wifi_down_plat(struct lws_netdev_instance *nd)
  387. {
  388. lws_netdev_instance_wifi_esp32_t *wnde32 =
  389. (lws_netdev_instance_wifi_esp32_t *)nd;
  390. struct lws_context *ctx = netdev_instance_to_ctx(&wnde32->wnd.inst);
  391. if (!(wnde32->wnd.flags & LNDIW_UP))
  392. return 0;
  393. lws_smd_msg_printf(ctx, LWSSMDCL_NETWORK,
  394. "{\"type\":\"down\",\"if\":\"%s\"}",
  395. wnde32->wnd.inst.name);
  396. esp_wifi_stop();
  397. esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP,
  398. &wnde32->instance_got_ip);
  399. esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID,
  400. &wnde32->instance_any_id);
  401. wnde32->wnd.flags &= ~LNDIW_UP;
  402. return 0;
  403. }
  404. void
  405. lws_netdev_wifi_destroy_plat(struct lws_netdev_instance **pnd)
  406. {
  407. lws_free(*pnd);
  408. *pnd = NULL;
  409. }