summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authoriabdalkader <i.abdalkader@gmail.com>2023-06-21 17:13:09 +0200
committerDamien George <damien@micropython.org>2023-09-14 23:51:30 +1000
commitecedd78302cb2236a2175d4a2c9a09b482c8db03 (patch)
treef6b56f6d24a38c3795b942f6801fed13507cbc97
parentdc5ea0c77d8729244ebf8f45c2f854cc3ef3581e (diff)
downloadmicropython-ecedd78302cb2236a2175d4a2c9a09b482c8db03.tar.gz
micropython-ecedd78302cb2236a2175d4a2c9a09b482c8db03.zip
drivers/esp-hosted: Add host driver for ESP-Hosted firmware.
This is a host driver for ESP32 chips running the esp-hosted firmware, which turns ESP32s into a WLAN/BT co-processor. Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
-rw-r--r--drivers/esp-hosted/README.md24
-rw-r--r--drivers/esp-hosted/esp_hosted.proto439
-rw-r--r--drivers/esp-hosted/esp_hosted_bthci.c149
-rw-r--r--drivers/esp-hosted/esp_hosted_hal.c209
-rw-r--r--drivers/esp-hosted/esp_hosted_hal.h82
-rw-r--r--drivers/esp-hosted/esp_hosted_internal.h117
-rw-r--r--drivers/esp-hosted/esp_hosted_netif.c167
-rw-r--r--drivers/esp-hosted/esp_hosted_netif.h38
-rw-r--r--drivers/esp-hosted/esp_hosted_stack.h63
-rw-r--r--drivers/esp-hosted/esp_hosted_wifi.c697
-rw-r--r--drivers/esp-hosted/esp_hosted_wifi.h110
-rw-r--r--extmod/extmod.mk43
12 files changed, 2138 insertions, 0 deletions
diff --git a/drivers/esp-hosted/README.md b/drivers/esp-hosted/README.md
new file mode 100644
index 0000000000..f4ecfd23d5
--- /dev/null
+++ b/drivers/esp-hosted/README.md
@@ -0,0 +1,24 @@
+# esp-hosted driver
+
+This is a MicroPython driver for the Espressif
+[esp_hosted](https://github.com/espressif/esp-hosted/#readme) communications
+coprocessor, which allows creating a Wi-Fi and/or Bluetooth interface from
+MicroPython to a separate connected ESP32 compatible device running the
+`esp_hosted` firmware.
+
+## Building
+
+Enable this driver by setting `MICROPY_PY_NETWORK_ESP_HOSTED` to 1 in your
+Makefile. If `MICROPY_PY_BLUETOOTH` is set then the Bluetooth host driver will
+also be built.
+
+In addition to normal MicroPython build requirements, building this driver
+requires the [protocol buffer
+compiler](https://github.com/protocolbuffers/protobuf#protobuf-compiler-installation)
+(protoc) to be installed.
+
+On Debian/Ubuntu, it can be installed by running:
+
+```
+sudo apt-get install protobuf-compiler
+```
diff --git a/drivers/esp-hosted/esp_hosted.proto b/drivers/esp-hosted/esp_hosted.proto
new file mode 100644
index 0000000000..8a51e9502b
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted.proto
@@ -0,0 +1,439 @@
+/* Copyright (C) 2015-2023 Espressif Systems (Shanghai) PTE LTD */
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/* This file is sourced from
+ https://github.com/espressif/esp-hosted/blob/master/esp_hosted_fg/common/proto/esp_hosted_config.proto
+*/
+
+syntax = "proto3";
+
+/* Enums similar to ESP IDF */
+enum Ctrl_VendorIEType {
+ Beacon = 0;
+ Probe_req = 1;
+ Probe_resp = 2;
+ Assoc_req = 3;
+ Assoc_resp = 4;
+}
+
+enum Ctrl_VendorIEID {
+ ID_0 = 0;
+ ID_1 = 1;
+}
+
+enum Ctrl_WifiMode {
+ NONE = 0;
+ STA = 1;
+ AP = 2;
+ APSTA = 3;
+}
+
+enum Ctrl_WifiBw {
+ BW_Invalid = 0;
+ HT20 = 1;
+ HT40 = 2;
+}
+
+enum Ctrl_WifiPowerSave {
+ PS_Invalid = 0;
+ MIN_MODEM = 1;
+ MAX_MODEM = 2;
+}
+
+enum Ctrl_WifiSecProt {
+ Open = 0;
+ WEP = 1;
+ WPA_PSK = 2;
+ WPA2_PSK = 3;
+ WPA_WPA2_PSK = 4;
+ WPA2_ENTERPRISE = 5;
+ WPA3_PSK = 6;
+ WPA2_WPA3_PSK = 7;
+}
+
+/* enums for Control path */
+enum Ctrl_Status {
+ Connected = 0;
+ Not_Connected = 1;
+ No_AP_Found = 2;
+ Connection_Fail = 3;
+ Invalid_Argument = 4;
+ Out_Of_Range = 5;
+}
+
+
+enum CtrlMsgType {
+ MsgType_Invalid = 0;
+ Req = 1;
+ Resp = 2;
+ Event = 3;
+ MsgType_Max = 4;
+}
+
+enum CtrlMsgId {
+ MsgId_Invalid = 0;
+
+ /** Request Msgs **/
+ Req_Base = 100;
+
+ Req_GetMACAddress = 101;
+ Req_SetMacAddress = 102;
+ Req_GetWifiMode = 103;
+ Req_SetWifiMode = 104;
+
+ Req_GetAPScanList = 105;
+ Req_GetAPConfig = 106;
+ Req_ConnectAP = 107;
+ Req_DisconnectAP = 108;
+
+ Req_GetSoftAPConfig = 109;
+ Req_SetSoftAPVendorSpecificIE = 110;
+ Req_StartSoftAP = 111;
+ Req_GetSoftAPConnectedSTAList = 112;
+ Req_StopSoftAP = 113;
+
+ Req_SetPowerSaveMode = 114;
+ Req_GetPowerSaveMode = 115;
+
+ Req_OTABegin = 116;
+ Req_OTAWrite = 117;
+ Req_OTAEnd = 118;
+
+ Req_SetWifiMaxTxPower = 119;
+ Req_GetWifiCurrTxPower = 120;
+
+ Req_ConfigHeartbeat = 121;
+ /* Add new control path command response before Req_Max
+ * and update Req_Max */
+ Req_Max = 122;
+
+ /** Response Msgs **/
+ Resp_Base = 200;
+
+ Resp_GetMACAddress = 201;
+ Resp_SetMacAddress = 202;
+ Resp_GetWifiMode = 203;
+ Resp_SetWifiMode = 204;
+
+ Resp_GetAPScanList = 205;
+ Resp_GetAPConfig = 206;
+ Resp_ConnectAP = 207;
+ Resp_DisconnectAP = 208;
+
+ Resp_GetSoftAPConfig = 209;
+ Resp_SetSoftAPVendorSpecificIE = 210;
+ Resp_StartSoftAP = 211;
+ Resp_GetSoftAPConnectedSTAList = 212;
+ Resp_StopSoftAP = 213;
+
+ Resp_SetPowerSaveMode = 214;
+ Resp_GetPowerSaveMode = 215;
+
+ Resp_OTABegin = 216;
+ Resp_OTAWrite = 217;
+ Resp_OTAEnd = 218;
+
+ Resp_SetWifiMaxTxPower = 219;
+ Resp_GetWifiCurrTxPower = 220;
+
+ Resp_ConfigHeartbeat = 221;
+ /* Add new control path command response before Resp_Max
+ * and update Resp_Max */
+ Resp_Max = 222;
+
+ /** Event Msgs **/
+ Event_Base = 300;
+ Event_ESPInit = 301;
+ Event_Heartbeat = 302;
+ Event_StationDisconnectFromAP = 303;
+ Event_StationDisconnectFromESPSoftAP = 304;
+ /* Add new control path command notification before Event_Max
+ * and update Event_Max */
+ Event_Max = 305;
+}
+
+/* internal supporting structures for CtrlMsg */
+message ScanResult {
+ bytes ssid = 1;
+ uint32 chnl = 2;
+ int32 rssi = 3;
+ bytes bssid = 4;
+ Ctrl_WifiSecProt sec_prot = 5;
+}
+
+message ConnectedSTAList {
+ bytes mac = 1;
+ int32 rssi = 2;
+}
+
+
+/* Control path structures */
+/** Req/Resp structure **/
+message CtrlMsg_Req_GetMacAddress {
+ int32 mode = 1;
+}
+
+message CtrlMsg_Resp_GetMacAddress {
+ bytes mac = 1;
+ int32 resp = 2;
+}
+
+message CtrlMsg_Req_GetMode {
+}
+
+message CtrlMsg_Resp_GetMode {
+ int32 mode = 1;
+ int32 resp = 2;
+}
+
+message CtrlMsg_Req_SetMode {
+ int32 mode = 1;
+}
+
+message CtrlMsg_Resp_SetMode {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Req_GetStatus {
+}
+
+message CtrlMsg_Resp_GetStatus {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Req_SetMacAddress {
+ bytes mac = 1;
+ int32 mode = 2;
+}
+
+message CtrlMsg_Resp_SetMacAddress {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Req_GetAPConfig {
+}
+
+message CtrlMsg_Resp_GetAPConfig {
+ bytes ssid = 1;
+ bytes bssid = 2;
+ int32 rssi = 3;
+ int32 chnl = 4;
+ Ctrl_WifiSecProt sec_prot = 5;
+ int32 resp = 6;
+}
+
+message CtrlMsg_Req_ConnectAP {
+ string ssid = 1;
+ string pwd = 2;
+ string bssid = 3;
+ bool is_wpa3_supported = 4;
+ int32 listen_interval = 5;
+}
+
+message CtrlMsg_Resp_ConnectAP {
+ int32 resp = 1;
+ bytes mac = 2;
+}
+
+message CtrlMsg_Req_GetSoftAPConfig {
+}
+
+message CtrlMsg_Resp_GetSoftAPConfig {
+ bytes ssid = 1;
+ bytes pwd = 2;
+ int32 chnl = 3;
+ Ctrl_WifiSecProt sec_prot = 4;
+ int32 max_conn = 5;
+ bool ssid_hidden = 6;
+ int32 bw = 7;
+ int32 resp = 8;
+}
+
+message CtrlMsg_Req_StartSoftAP {
+ string ssid = 1;
+ string pwd = 2;
+ int32 chnl = 3;
+ Ctrl_WifiSecProt sec_prot = 4;
+ int32 max_conn = 5;
+ bool ssid_hidden = 6;
+ int32 bw = 7;
+}
+
+message CtrlMsg_Resp_StartSoftAP {
+ int32 resp = 1;
+ bytes mac = 2;
+}
+
+message CtrlMsg_Req_ScanResult {
+}
+
+message CtrlMsg_Resp_ScanResult {
+ uint32 count = 1;
+ repeated ScanResult entries = 2;
+ int32 resp = 3;
+}
+
+message CtrlMsg_Req_SoftAPConnectedSTA {
+}
+
+message CtrlMsg_Resp_SoftAPConnectedSTA {
+ uint32 num = 1;
+ repeated ConnectedSTAList stations = 2;
+ int32 resp = 3;
+}
+
+message CtrlMsg_Req_OTABegin {
+}
+
+message CtrlMsg_Resp_OTABegin {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Req_OTAWrite {
+ bytes ota_data = 1;
+}
+
+message CtrlMsg_Resp_OTAWrite {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Req_OTAEnd {
+}
+
+message CtrlMsg_Resp_OTAEnd {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Req_VendorIEData {
+ int32 element_id = 1;
+ int32 length = 2;
+ bytes vendor_oui = 3;
+ int32 vendor_oui_type = 4;
+ bytes payload = 5;
+}
+
+message CtrlMsg_Req_SetSoftAPVendorSpecificIE {
+ bool enable = 1;
+ Ctrl_VendorIEType type = 2;
+ Ctrl_VendorIEID idx = 3;
+ CtrlMsg_Req_VendorIEData vendor_ie_data = 4;
+}
+
+message CtrlMsg_Resp_SetSoftAPVendorSpecificIE {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Req_SetWifiMaxTxPower {
+ int32 wifi_max_tx_power = 1;
+}
+
+message CtrlMsg_Resp_SetWifiMaxTxPower {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Req_GetWifiCurrTxPower {
+}
+
+message CtrlMsg_Resp_GetWifiCurrTxPower {
+ int32 wifi_curr_tx_power = 1;
+ int32 resp = 2;
+}
+
+message CtrlMsg_Req_ConfigHeartbeat {
+ bool enable = 1;
+ int32 duration = 2;
+}
+
+message CtrlMsg_Resp_ConfigHeartbeat {
+ int32 resp = 1;
+}
+
+/** Event structure **/
+message CtrlMsg_Event_ESPInit {
+ bytes init_data = 1;
+}
+
+message CtrlMsg_Event_Heartbeat {
+ int32 hb_num = 1;
+}
+
+message CtrlMsg_Event_StationDisconnectFromAP {
+ int32 resp = 1;
+}
+
+message CtrlMsg_Event_StationDisconnectFromESPSoftAP {
+ int32 resp = 1;
+ bytes mac = 2;
+}
+
+message CtrlMsg {
+ /* msg_type could be req, resp or Event */
+ CtrlMsgType msg_type = 1;
+
+ /* msg id */
+ CtrlMsgId msg_id = 2;
+
+ /* union of all msg ids */
+ oneof payload {
+ /** Requests **/
+ CtrlMsg_Req_GetMacAddress req_get_mac_address = 101;
+ CtrlMsg_Req_SetMacAddress req_set_mac_address = 102;
+ CtrlMsg_Req_GetMode req_get_wifi_mode = 103;
+ CtrlMsg_Req_SetMode req_set_wifi_mode = 104;
+
+ CtrlMsg_Req_ScanResult req_scan_ap_list = 105;
+ CtrlMsg_Req_GetAPConfig req_get_ap_config = 106;
+ CtrlMsg_Req_ConnectAP req_connect_ap = 107;
+ CtrlMsg_Req_GetStatus req_disconnect_ap = 108;
+
+ CtrlMsg_Req_GetSoftAPConfig req_get_softap_config = 109;
+ CtrlMsg_Req_SetSoftAPVendorSpecificIE req_set_softap_vendor_specific_ie = 110;
+ CtrlMsg_Req_StartSoftAP req_start_softap = 111;
+ CtrlMsg_Req_SoftAPConnectedSTA req_softap_connected_stas_list = 112;
+ CtrlMsg_Req_GetStatus req_stop_softap = 113;
+
+ CtrlMsg_Req_SetMode req_set_power_save_mode = 114;
+ CtrlMsg_Req_GetMode req_get_power_save_mode = 115;
+
+ CtrlMsg_Req_OTABegin req_ota_begin = 116;
+ CtrlMsg_Req_OTAWrite req_ota_write = 117;
+ CtrlMsg_Req_OTAEnd req_ota_end = 118;
+
+ CtrlMsg_Req_SetWifiMaxTxPower req_set_wifi_max_tx_power = 119;
+ CtrlMsg_Req_GetWifiCurrTxPower req_get_wifi_curr_tx_power = 120;
+ CtrlMsg_Req_ConfigHeartbeat req_config_heartbeat = 121;
+
+ /** Responses **/
+ CtrlMsg_Resp_GetMacAddress resp_get_mac_address = 201;
+ CtrlMsg_Resp_SetMacAddress resp_set_mac_address = 202;
+ CtrlMsg_Resp_GetMode resp_get_wifi_mode = 203;
+ CtrlMsg_Resp_SetMode resp_set_wifi_mode = 204;
+
+ CtrlMsg_Resp_ScanResult resp_scan_ap_list = 205;
+ CtrlMsg_Resp_GetAPConfig resp_get_ap_config = 206;
+ CtrlMsg_Resp_ConnectAP resp_connect_ap = 207;
+ CtrlMsg_Resp_GetStatus resp_disconnect_ap = 208;
+
+ CtrlMsg_Resp_GetSoftAPConfig resp_get_softap_config = 209;
+ CtrlMsg_Resp_SetSoftAPVendorSpecificIE resp_set_softap_vendor_specific_ie = 210;
+ CtrlMsg_Resp_StartSoftAP resp_start_softap = 211;
+ CtrlMsg_Resp_SoftAPConnectedSTA resp_softap_connected_stas_list = 212;
+ CtrlMsg_Resp_GetStatus resp_stop_softap = 213;
+
+ CtrlMsg_Resp_SetMode resp_set_power_save_mode = 214;
+ CtrlMsg_Resp_GetMode resp_get_power_save_mode = 215;
+
+ CtrlMsg_Resp_OTABegin resp_ota_begin = 216;
+ CtrlMsg_Resp_OTAWrite resp_ota_write = 217;
+ CtrlMsg_Resp_OTAEnd resp_ota_end = 218;
+ CtrlMsg_Resp_SetWifiMaxTxPower resp_set_wifi_max_tx_power = 219;
+ CtrlMsg_Resp_GetWifiCurrTxPower resp_get_wifi_curr_tx_power = 220;
+ CtrlMsg_Resp_ConfigHeartbeat resp_config_heartbeat = 221;
+
+ /** Notifications **/
+ CtrlMsg_Event_ESPInit event_esp_init = 301;
+ CtrlMsg_Event_Heartbeat event_heartbeat = 302;
+ CtrlMsg_Event_StationDisconnectFromAP event_station_disconnect_from_AP = 303;
+ CtrlMsg_Event_StationDisconnectFromESPSoftAP event_station_disconnect_from_ESP_SoftAP = 304;
+ }
+}
diff --git a/drivers/esp-hosted/esp_hosted_bthci.c b/drivers/esp-hosted/esp_hosted_bthci.c
new file mode 100644
index 0000000000..003054460d
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_bthci.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ESP-Hosted Bluetooth HCI driver.
+ */
+
+#include "py/mphal.h"
+
+#if MICROPY_PY_BLUETOOTH && MICROPY_PY_NETWORK_ESP_HOSTED
+
+#include <stdio.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "extmod/mpbthci.h"
+#include "esp_hosted_hal.h"
+
+#define HCI_COMMAND_PACKET (0x01)
+#define HCI_ACLDATA_PACKET (0x02)
+#define HCI_EVENT_PACKET (0x04)
+
+#define HCI_COMMAND_COMPLETE (0x0e)
+#define HCI_COMMAND_TIMEOUT (3000)
+
+#define OGF_LINK_CTL (0x01)
+#define OGF_HOST_CTL (0x03)
+
+#define OCF_SET_EVENT_MASK (0x0001)
+#define OCF_RESET (0x0003)
+
+// Provided by the port, and also possibly shared with the stack.
+extern uint8_t mp_bluetooth_hci_cmd_buf[4 + 256];
+
+int esp_hosted_hci_cmd(int ogf, int ocf, size_t param_len, const uint8_t *param_buf) {
+ uint8_t *buf = mp_bluetooth_hci_cmd_buf;
+
+ buf[0] = HCI_COMMAND_PACKET;
+ buf[1] = ocf;
+ buf[2] = ogf << 2 | ocf >> 8;
+ buf[3] = param_len;
+
+ if (param_len) {
+ memcpy(buf + 4, param_buf, param_len);
+ }
+
+ debug_printf("HCI Command: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]);
+
+ mp_bluetooth_hci_uart_write(buf, 4 + param_len);
+
+ // Receive HCI event packet, initially reading 3 bytes (HCI Event, Event code, Plen).
+ for (mp_uint_t start = mp_hal_ticks_ms(), size = 3, i = 0; i < size;) {
+ while (!mp_bluetooth_hci_uart_any()) {
+ MICROPY_EVENT_POLL_HOOK
+ // Timeout.
+ if ((mp_hal_ticks_ms() - start) > HCI_COMMAND_TIMEOUT) {
+ error_printf("timeout waiting for HCI packet\n");
+ return -1;
+ }
+ }
+
+ buf[i] = mp_bluetooth_hci_uart_readchar();
+
+ // There seems to be a sync issue with this fw/module.
+ if (i == 0 && buf[0] == 0xFF) {
+ continue;
+ }
+
+ // Check for packet type.
+ if (i == 0 && buf[0] != HCI_EVENT_PACKET) {
+ error_printf("unexpected HCI packet: %02x\n", buf[0]);
+ return -1;
+ }
+
+ // Sanity check the packet parameters length.
+ if (i == 2 && ((size += buf[2]) > sizeof(mp_bluetooth_hci_cmd_buf))) {
+ error_printf("unexpected event packet length: %d\n", size);
+ return -1;
+ }
+
+ i++;
+ }
+
+ // We're only looking for command complete events.
+ if (buf[1] != HCI_COMMAND_COMPLETE || buf[4] != ocf || buf[5] != (ogf << 2 | ocf >> 8)) {
+ error_printf("response mismatch: %02x %02x\n", buf[4], buf[5]);
+ return -1;
+ }
+
+ // Log event.
+ debug_printf("HCI Event packet: %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+
+ // Status code.
+ return buf[6];
+}
+
+int mp_bluetooth_hci_controller_init(void) {
+ // Low-level pins init, memory pool allocation etc...
+ esp_hosted_hal_init(ESP_HOSTED_MODE_BT);
+
+ mp_uint_t start = mp_hal_ticks_ms();
+ // Skip bootloader messages.
+ while ((mp_hal_ticks_ms() - start) < 2500) {
+ if (mp_bluetooth_hci_uart_any()) {
+ mp_bluetooth_hci_uart_readchar();
+ }
+ MICROPY_EVENT_POLL_HOOK
+ }
+
+ #ifdef MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY
+ mp_bluetooth_hci_uart_set_baudrate(MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY);
+ #endif
+
+ mp_hal_pin_output(MICROPY_HW_BLE_UART_RTS);
+ mp_hal_pin_write(MICROPY_HW_BLE_UART_RTS, 0);
+
+ // Send reset command
+ // It seems that nothing else is needed for now.
+ return esp_hosted_hci_cmd(OGF_HOST_CTL, OCF_RESET, 0, NULL);
+}
+
+int mp_bluetooth_hci_controller_deinit(void) {
+ esp_hosted_hal_deinit();
+ return 0;
+}
+
+#endif
diff --git a/drivers/esp-hosted/esp_hosted_hal.c b/drivers/esp-hosted/esp_hosted_hal.c
new file mode 100644
index 0000000000..d8e2e6ece4
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_hal.c
@@ -0,0 +1,209 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ESP-Hosted WiFi HAL.
+ */
+
+#include "py/mphal.h"
+
+#if MICROPY_PY_NETWORK_ESP_HOSTED
+
+#include <stdint.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "modmachine.h"
+#include "extmod/machine_spi.h"
+#include "mpconfigboard.h"
+
+#include "esp_hosted_hal.h"
+#include "esp_hosted_wifi.h"
+
+#ifndef MICROPY_HW_WIFI_IRQ
+#define MICROPY_HW_WIFI_IRQ MICROPY_HW_WIFI_HANDSHAKE
+#endif
+
+STATIC mp_obj_t esp_hosted_pin_irq_callback(mp_obj_t self_in) {
+ extern void mod_network_poll_events(void);
+ mod_network_poll_events();
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_hosted_pin_irq_callback_obj, esp_hosted_pin_irq_callback);
+
+MP_WEAK int esp_hosted_hal_init(uint32_t mode) {
+ // Perform a hard reset and set pins to their defaults.
+ esp_hosted_hal_deinit();
+
+ if (mode == ESP_HOSTED_MODE_BT) {
+ // For Bluetooth mode, init is done.
+ return 0;
+ }
+
+ mp_hal_pin_input(MICROPY_HW_WIFI_HANDSHAKE);
+ mp_hal_pin_input(MICROPY_HW_WIFI_DATAREADY);
+
+ // Enable Pin-IRQ for the handshake PIN to call esp_hosted_wifi_poll()
+ mp_obj_t irq_rising_attr[2];
+ mp_load_method_maybe((mp_obj_t)MICROPY_HW_WIFI_IRQ, MP_QSTR_IRQ_RISING, irq_rising_attr);
+
+ if (irq_rising_attr[0] != MP_OBJ_NULL && irq_rising_attr[1] == MP_OBJ_NULL) { // value for IRQ rising found
+ mp_obj_t pin_args[] = {
+ NULL, // Method pointer
+ (mp_obj_t)MICROPY_HW_WIFI_IRQ, // Pin object
+ (mp_obj_t)&esp_hosted_pin_irq_callback_obj, // Callback function object
+ NULL, // The Rising edge value is set below.
+ mp_const_true, // Hard IRQ, since the actual polling is scheduled.
+ };
+ pin_args[3] = irq_rising_attr[0];
+ mp_load_method_maybe((mp_obj_t)MICROPY_HW_WIFI_IRQ, MP_QSTR_irq, pin_args);
+ if (pin_args[0] != MP_OBJ_NULL && pin_args[1] != MP_OBJ_NULL) {
+ mp_call_method_n_kw(3, 0, pin_args);
+ }
+ }
+
+ // Initialize SPI.
+ mp_obj_t args[] = {
+ MP_OBJ_NEW_SMALL_INT(MICROPY_HW_WIFI_SPI_ID),
+ MP_OBJ_NEW_SMALL_INT(MICROPY_HW_WIFI_SPI_BAUDRATE),
+ MP_OBJ_NEW_QSTR(MP_QSTR_polarity), MP_OBJ_NEW_SMALL_INT(1),
+ };
+
+ MP_STATE_PORT(mp_wifi_spi) =
+ MP_OBJ_TYPE_GET_SLOT(&machine_spi_type, make_new)((mp_obj_t)&machine_spi_type, 2, 1, args);
+
+ // SPI might change the direction/mode of CS pin,
+ // set it to GPIO again just in case.
+ mp_hal_pin_output(MICROPY_HW_WIFI_SPI_CS);
+ mp_hal_pin_write(MICROPY_HW_WIFI_SPI_CS, 1);
+ return 0;
+}
+
+MP_WEAK int esp_hosted_hal_deinit(void) {
+ // Disable Pin-IRQ for the handshake PIN
+ mp_obj_t pin_args[] = {
+ NULL, // Method pointer
+ (mp_obj_t)MICROPY_HW_WIFI_IRQ, // Pin object
+ mp_const_none // Set to None
+ };
+ mp_load_method_maybe((mp_obj_t)MICROPY_HW_WIFI_IRQ, MP_QSTR_irq, pin_args);
+ if (pin_args[0] && pin_args[1]) {
+ mp_call_method_n_kw(1, 0, pin_args);
+ }
+
+ // Remove all network interfaces and reset wifi state.
+ esp_hosted_wifi_deinit();
+
+ mp_hal_pin_output(MICROPY_HW_ESP_HOSTED_GPIO0);
+ mp_hal_pin_output(MICROPY_HW_ESP_HOSTED_RESET);
+
+ #ifndef MICROPY_HW_ESP_HOSTED_SHARED_PINS
+ mp_hal_pin_output(MICROPY_HW_WIFI_SPI_CS);
+ mp_hal_pin_write(MICROPY_HW_WIFI_SPI_CS, 1);
+ #endif
+
+ // Perform a hard reset
+ mp_hal_pin_write(MICROPY_HW_ESP_HOSTED_GPIO0, 1);
+ mp_hal_pin_write(MICROPY_HW_ESP_HOSTED_RESET, 0);
+ mp_hal_delay_ms(100);
+ mp_hal_pin_write(MICROPY_HW_ESP_HOSTED_RESET, 1);
+ mp_hal_delay_ms(500);
+
+ MP_STATE_PORT(mp_wifi_spi) = MP_OBJ_NULL;
+ return 0;
+}
+
+MP_WEAK int esp_hosted_hal_atomic_enter(void) {
+ #if MICROPY_ENABLE_SCHEDULER
+ mp_sched_lock();
+ #endif
+ return 0;
+}
+
+MP_WEAK int esp_hosted_hal_atomic_exit(void) {
+ #if MICROPY_ENABLE_SCHEDULER
+ mp_sched_unlock();
+ #endif
+ return 0;
+}
+
+MP_WEAK bool esp_hosted_hal_data_ready(void) {
+ return mp_hal_pin_read(MICROPY_HW_WIFI_DATAREADY) && mp_hal_pin_read(MICROPY_HW_WIFI_HANDSHAKE);
+}
+
+MP_WEAK int esp_hosted_hal_spi_transfer(const uint8_t *tx_buf, uint8_t *rx_buf, uint32_t size) {
+ mp_obj_t mp_wifi_spi = MP_STATE_PORT(mp_wifi_spi);
+ const mp_machine_spi_p_t *spi_proto = MP_OBJ_TYPE_GET_SLOT(&machine_spi_type, protocol);
+
+ // Wait for handshake pin to go high.
+ for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) {
+ if (mp_hal_pin_read(MICROPY_HW_WIFI_HANDSHAKE)) {
+ break;
+ }
+ if ((mp_hal_ticks_ms() - start) >= 1000) {
+ error_printf("timeout waiting for handshake\n");
+ return -1;
+ }
+ }
+
+ mp_hal_pin_write(MICROPY_HW_WIFI_SPI_CS, 0);
+ mp_hal_delay_us(10);
+ spi_proto->transfer(mp_wifi_spi, size, tx_buf, rx_buf);
+ mp_hal_pin_write(MICROPY_HW_WIFI_SPI_CS, 1);
+ return 0;
+}
+
+MP_WEAK void *esp_hosted_hal_alloc(void *user, size_t size) {
+ (void)user;
+ void *mem = m_malloc0(size);
+ return mem;
+}
+
+MP_WEAK void esp_hosted_hal_free(void *user, void *ptr) {
+ (void)user;
+ m_free(ptr);
+}
+
+MP_WEAK void *esp_hosted_hal_calloc(size_t nmemb, size_t size) {
+ return NULL;
+}
+
+MP_WEAK void *esp_hosted_hal_realloc(void *ptr, size_t size) {
+ return NULL;
+}
+
+// Those are provided for protobuf-c's internally
+// defined allocator, and are not actually used.
+MP_WEAK void *malloc(size_t size) {
+ (void)size;
+ debug_printf("system malloc called\n");
+ return NULL;
+}
+
+MP_WEAK void free(void *ptr) {
+ (void)ptr;
+ debug_printf("system free called\n");
+}
+
+#endif // MICROPY_PY_NETWORK_NINAW10
diff --git a/drivers/esp-hosted/esp_hosted_hal.h b/drivers/esp-hosted/esp_hosted_hal.h
new file mode 100644
index 0000000000..abf060c893
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_hal.h
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ESP-Hosted WiFi HAL.
+ */
+
+#ifndef MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_HAL_H
+#define MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_HAL_H
+
+#ifndef ESP_HOSTED_DEBUG
+#define ESP_HOSTED_DEBUG (0)
+#endif
+
+#if ESP_HOSTED_DEBUG
+#define PROTOBUF_C_UNPACK_ERROR(...) error_printf(__VA_ARGS__);
+#endif
+
+#define ANSI_C_RED "\x1B[31m"
+#define ANSI_C_GREEN "\x1B[32m"
+#define ANSI_C_YELLOW "\x1B[33m"
+#define ANSI_C_BLUE "\x1B[34m"
+#define ANSI_C_MAGENTA "\x1B[35m"
+#define ANSI_C_CYAN "\x1B[36m"
+#define ANSI_C_WHITE "\x1B[37m"
+#define ANSI_C_DEFAULT "\x1B[0m"
+
+#if ESP_HOSTED_DEBUG
+#define do_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__)
+#else
+#define do_printf(...)
+#endif
+
+// Logging macros.
+#define debug_printf(...) do_printf(ANSI_C_BLUE); do_printf(__VA_ARGS__); do_printf(ANSI_C_DEFAULT);
+#define info_printf(...) do_printf(ANSI_C_GREEN); do_printf(__VA_ARGS__); do_printf(ANSI_C_DEFAULT);
+#define warn_printf(...) do_printf(ANSI_C_YELLOW); do_printf(__VA_ARGS__); do_printf(ANSI_C_DEFAULT);
+#define error_printf(...) do_printf(ANSI_C_RED); do_printf(__VA_ARGS__); do_printf(ANSI_C_DEFAULT);
+#define crit_printf(...) do_printf(ANSI_C_MAGENTA); do_printf(__VA_ARGS__); do_printf(ANSI_C_DEFAULT);
+
+typedef enum {
+ ESP_HOSTED_MODE_BT,
+ ESP_HOSTED_MODE_WIFI,
+} esp_hosted_mode_t;
+
+// HAL functions to be implemented by ports.
+// Note A default machine-based implementation is provided in esp_hosted_hal.c.
+int esp_hosted_hal_init(uint32_t mode);
+int esp_hosted_hal_deinit(void);
+bool esp_hosted_hal_data_ready(void);
+int esp_hosted_hal_atomic_enter(void);
+int esp_hosted_hal_atomic_exit(void);
+int esp_hosted_hal_spi_transfer(const uint8_t *tx_buf, uint8_t *rx_buf, uint32_t size);
+
+// Memory management functions.
+// Note alloc/free need to match the Protobuf allocator signature.
+void *esp_hosted_hal_alloc(void *user, size_t size);
+void esp_hosted_hal_free(void *user, void *ptr);
+void *esp_hosted_hal_calloc(size_t nmemb, size_t size);
+void *esp_hosted_hal_realloc(void *ptr, size_t size);
+#endif // MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_HAL_H
diff --git a/drivers/esp-hosted/esp_hosted_internal.h b/drivers/esp-hosted/esp_hosted_internal.h
new file mode 100644
index 0000000000..f57cd210a1
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_internal.h
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ESP-Hosted internal.
+ */
+
+#ifndef MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_INTERNAL_H
+#define MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_INTERNAL_H
+
+#define ESP_HOSTED_DEBUG (0)
+
+#define ESP_FRAME_MAX_SIZE (1600)
+#define ESP_FRAME_MAX_PAYLOAD (ESP_FRAME_MAX_SIZE - sizeof(esp_header_t))
+#define ESP_FRAME_FLAGS_FRAGMENT (1 << 0)
+
+#define ESP_STATE_NUM_ITFS (2)
+#define ESP_STATE_BUF_SIZE (ESP_FRAME_MAX_SIZE * 2)
+#define ESP_STACK_CAPACITY (32)
+#define ESP_SYNC_REQ_TIMEOUT (5000)
+
+#define TLV_HEADER_TYPE_EP (1)
+#define TLV_HEADER_TYPE_DATA (2)
+#define TLV_HEADER_EP_RESP "ctrlResp"
+#define TLV_HEADER_EP_EVENT "ctrlEvnt"
+
+typedef enum {
+ ESP_HOSTED_FLAGS_RESET = (0 << 0),
+ ESP_HOSTED_FLAGS_INIT = (1 << 0),
+ ESP_HOSTED_FLAGS_ACTIVE = (1 << 1),
+ ESP_HOSTED_FLAGS_STATIC_IP = (1 << 2),
+ ESP_HOSTED_FLAGS_AP_STARTED = (1 << 3),
+ ESP_HOSTED_FLAGS_STA_CONNECTED = (1 << 4),
+} esp_hosted_flags_t;
+
+typedef enum {
+ ESP_PACKET_TYPE_EVENT,
+} esp_hosted_priv_packet_t;
+
+typedef enum {
+ ESP_PRIV_EVENT_INIT,
+} esp_hosted_priv_event_t;
+
+typedef struct esp_hosted_stack {
+ size_t capacity;
+ size_t top;
+ void *buff[ESP_STACK_CAPACITY];
+} esp_hosted_stack_t;
+
+typedef struct esp_hosted_state {
+ uint8_t chip_id;
+ uint8_t spi_clk;
+ uint8_t chip_flags;
+ uint8_t flags;
+ uint16_t seq_num;
+ uint32_t last_hb_ms;
+ struct netif netif[ESP_STATE_NUM_ITFS];
+ struct dhcp dhcp_client;
+ dhcp_server_t dhcp_server;
+ esp_hosted_stack_t stack;
+ uint8_t buf[ESP_STATE_BUF_SIZE];
+} esp_hosted_state_t;
+
+typedef struct __attribute__((packed)) {
+ uint8_t ep_type;
+ uint16_t ep_length;
+ uint8_t ep_value[8];
+ uint8_t data_type;
+ uint16_t data_length;
+ uint8_t data[];
+} tlv_header_t;
+
+typedef struct __attribute__((packed)) {
+ uint8_t event_type;
+ uint8_t event_len;
+ uint8_t event_data[];
+} esp_event_t;
+
+typedef struct __attribute__((packed)) {
+ uint8_t if_type : 4;
+ uint8_t if_num : 4;
+ uint8_t flags;
+ uint16_t len;
+ uint16_t offset;
+ uint16_t checksum;
+ uint16_t seq_num;
+ uint8_t reserved2;
+ union {
+ uint8_t hci_pkt_type;
+ uint8_t priv_pkt_type;
+ };
+ uint8_t payload[];
+} esp_header_t;
+
+uint16_t esp_hosted_checksum(esp_header_t *esp_header);
+#endif // MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_INTERNAL_H
diff --git a/drivers/esp-hosted/esp_hosted_netif.c b/drivers/esp-hosted/esp_hosted_netif.c
new file mode 100644
index 0000000000..b471c3ad4c
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_netif.c
@@ -0,0 +1,167 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ESP-Hosted LWIP netif functions.
+ */
+
+#include "py/mphal.h"
+#include "py/mperrno.h"
+
+#if MICROPY_PY_NETWORK_ESP_HOSTED
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "lwip/err.h"
+#include "lwip/dns.h"
+#include "lwip/dhcp.h"
+#include "netif/etharp.h"
+
+#include "shared/netutils/netutils.h"
+#include "shared/netutils/dhcpserver.h"
+
+#include "esp_hosted_hal.h"
+#include "esp_hosted_wifi.h"
+#include "esp_hosted_netif.h"
+#include "esp_hosted_internal.h"
+
+static err_t netif_struct_init(struct netif *netif) {
+ netif->linkoutput = esp_hosted_netif_output;
+ netif->output = etharp_output;
+ netif->mtu = 1500;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP;
+ esp_hosted_wifi_get_mac(netif->name[1] - '0', netif->hwaddr);
+ netif->hwaddr_len = sizeof(netif->hwaddr);
+ info_printf("netif_init() netif initialized\n");
+ return ERR_OK;
+}
+
+int esp_hosted_netif_init(esp_hosted_state_t *state, uint32_t itf) {
+ struct netif *netif = &state->netif[itf];
+
+ ip_addr_t ipconfig[4];
+ ipconfig[0].addr = 0;
+ ipconfig[1].addr = 0;
+ ipconfig[2].addr = 0;
+ ipconfig[3].addr = 0;
+
+ if (itf == ESP_HOSTED_AP_IF) {
+ ipconfig[0].addr = PP_HTONL(ESP_HOSTED_AP_ADDRESS);
+ ipconfig[1].addr = PP_HTONL(ESP_HOSTED_AP_NETMASK);
+ ipconfig[2].addr = PP_HTONL(ESP_HOSTED_AP_GATEWAY);
+ }
+
+ netif->name[0] = 'w';
+ netif->name[1] = '0' + itf;
+
+ netif_add(netif, ip_2_ip4(&ipconfig[0]), ip_2_ip4(&ipconfig[1]),
+ ip_2_ip4(&ipconfig[2]), state, netif_struct_init, ethernet_input);
+
+ netif_set_hostname(netif, ESP_HOSTED_HOSTNAME);
+ netif_set_default(netif);
+ netif_set_up(netif);
+ netif_set_link_up(netif);
+ dns_setserver(0, &ipconfig[3]);
+
+ if (itf == ESP_HOSTED_STA_IF) {
+ dhcp_set_struct(netif, &state->dhcp_client);
+ dhcp_start(netif);
+ } else {
+ dhcp_server_init(&state->dhcp_server, &ipconfig[0], &ipconfig[1]);
+ }
+ return 0;
+}
+
+int esp_hosted_netif_deinit(esp_hosted_state_t *state, uint32_t itf) {
+ struct netif *netif = &state->netif[itf];
+
+ if (netif_is_link_up(netif)) {
+ if (itf == ESP_HOSTED_STA_IF) {
+ dhcp_stop(netif);
+ } else {
+ dhcp_server_deinit(&state->dhcp_server);
+ }
+ }
+
+ for (struct netif *netifp = netif_list; netif != NULL; netif = netif->next) {
+ if (netif == netifp) {
+ netif_remove(netif);
+ netif->flags = 0;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int esp_hosted_netif_input(esp_hosted_state_t *state, uint32_t itf, const void *buf, size_t len) {
+ struct netif *netif = &state->netif[itf];
+
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+ if (p == NULL) {
+ error_printf("esp_hosted_netif_input() failed to alloc pbuf %d\n", len);
+ return -1;
+ }
+ // Copy buf to pbuf
+ pbuf_take(p, buf, len);
+
+ if (netif->input(p, netif) != ERR_OK) {
+ error_printf("esp_hosted_netif_input() netif input failed\n");
+ pbuf_free(p);
+ return -1;
+ }
+
+ debug_printf("esp_hosted_netif_input() eth frame input %d\n", len);
+ return 0;
+}
+
+err_t esp_hosted_netif_output(struct netif *netif, struct pbuf *p) {
+ esp_hosted_state_t *state = netif->state;
+
+ if (p->tot_len > ESP_FRAME_MAX_PAYLOAD) {
+ error_printf("esp_hosted_netif_output() pbuf len > SPI buf len\n");
+ return ERR_IF;
+ }
+
+ esp_header_t *esp_header = (esp_header_t *)(state->buf);
+ esp_header->if_type = netif->name[1] - '0';
+ esp_header->if_num = 0;
+ esp_header->flags = 0;
+ esp_header->len = p->tot_len;
+ esp_header->offset = sizeof(esp_header_t);
+ esp_header->seq_num = 0;
+ pbuf_copy_partial(p, esp_header->payload, p->tot_len, 0);
+ esp_header->checksum = esp_hosted_checksum(esp_header);
+
+ size_t frame_size = (sizeof(esp_header_t) + esp_header->len + 3) & ~3U;
+ if (esp_hosted_hal_spi_transfer(state->buf, NULL, frame_size) != 0) {
+ error_printf("esp_hosted_netif_output() failed to send eth frame\n");
+ return ERR_IF;
+ }
+ debug_printf("esp_hosted_netif_output() if %d pbuf len %d\n", esp_header->if_type, esp_header->len);
+ return ERR_OK;
+}
+#endif
diff --git a/drivers/esp-hosted/esp_hosted_netif.h b/drivers/esp-hosted/esp_hosted_netif.h
new file mode 100644
index 0000000000..656f90d313
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_netif.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ESP-Hosted LWIP netif functions.
+ */
+
+#ifndef MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_NETIF_H
+#define MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_NETIF_H
+
+typedef struct esp_hosted_state esp_hosted_state_t;
+int esp_hosted_netif_init(esp_hosted_state_t *state, uint32_t itf);
+int esp_hosted_netif_deinit(esp_hosted_state_t *state, uint32_t itf);
+int esp_hosted_netif_input(esp_hosted_state_t *state, uint32_t itf, const void *buf, size_t len);
+err_t esp_hosted_netif_output(struct netif *netif, struct pbuf *p);
+
+#endif // MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_NETIF_H
diff --git a/drivers/esp-hosted/esp_hosted_stack.h b/drivers/esp-hosted/esp_hosted_stack.h
new file mode 100644
index 0000000000..8fa45cc43b
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_stack.h
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Simple stack for control messages.
+ */
+
+#ifndef MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_STACK_H
+#define MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_STACK_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "esp_hosted_internal.h"
+
+static inline void esp_hosted_stack_init(esp_hosted_stack_t *stack) {
+ stack->capacity = ESP_STACK_CAPACITY;
+ stack->top = 0;
+}
+
+static inline bool esp_hosted_stack_empty(esp_hosted_stack_t *stack) {
+ return stack->top == 0;
+}
+
+static inline bool esp_hosted_stack_push(esp_hosted_stack_t *stack, void *data) {
+ if (stack->top < stack->capacity) {
+ stack->buff[stack->top++] = data;
+ return true;
+ }
+ return false;
+}
+
+static inline void *esp_hosted_stack_pop(esp_hosted_stack_t *stack, bool peek) {
+ void *data = NULL;
+ if (stack->top > 0) {
+ data = stack->buff[stack->top - 1];
+ if (!peek) {
+ stack->top -= 1;
+ }
+ }
+ return data;
+}
+#endif // MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_STACK_H
diff --git a/drivers/esp-hosted/esp_hosted_wifi.c b/drivers/esp-hosted/esp_hosted_wifi.c
new file mode 100644
index 0000000000..3cc153205c
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_wifi.c
@@ -0,0 +1,697 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ESP-Hosted WiFi driver.
+ */
+
+#include "py/mphal.h"
+#include "py/mperrno.h"
+
+#if MICROPY_PY_NETWORK_ESP_HOSTED
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "lwip/err.h"
+#include "lwip/dns.h"
+#include "lwip/dhcp.h"
+#include "netif/etharp.h"
+
+#include "shared/netutils/netutils.h"
+#include "shared/netutils/dhcpserver.h"
+
+#include "esp_hosted.pb-c.h"
+
+#include "esp_hosted_hal.h"
+#include "esp_hosted_stack.h"
+#include "esp_hosted_netif.h"
+#include "esp_hosted_wifi.h"
+#include "esp_hosted_internal.h"
+
+static esp_hosted_state_t esp_state;
+
+static ProtobufCAllocator protobuf_alloc = {
+ .alloc = &esp_hosted_hal_alloc,
+ .free = &esp_hosted_hal_free,
+ .allocator_data = NULL,
+};
+
+static void esp_hosted_macstr_to_bytes(const uint8_t *mac_str, size_t mac_len, uint8_t *mac_out) {
+ uint8_t byte = 0;
+ for (int i = 0; i < mac_len; i++) {
+ char c = mac_str[i];
+ if (c >= '0' && c <= '9') {
+ byte = (byte << 4) | (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ byte = (byte << 4) | (c - 'a' + 10);
+ } else if (c >= 'A' && c <= 'F') {
+ byte = (byte << 4) | (c - 'A' + 10);
+ }
+ if (c == ':' || (i + 1) == mac_len) {
+ *mac_out++ = byte;
+ byte = 0;
+ }
+ }
+}
+
+// to avoid bleeding the protocol buffer API into the public interface, convert esp_hosted_security_t
+// to/from CtrlWifiSecProt here.
+
+static esp_hosted_security_t sec_prot_to_hosted_security(CtrlWifiSecProt sec_prot)
+{
+ switch (sec_prot) {
+ case CTRL__WIFI_SEC_PROT__Open:
+ return ESP_HOSTED_SEC_OPEN;
+ case CTRL__WIFI_SEC_PROT__WEP:
+ return ESP_HOSTED_SEC_WEP;
+ case CTRL__WIFI_SEC_PROT__WPA_PSK:
+ return ESP_HOSTED_SEC_WPA_PSK;
+ case CTRL__WIFI_SEC_PROT__WPA2_PSK:
+ return ESP_HOSTED_SEC_WPA2_PSK;
+ case CTRL__WIFI_SEC_PROT__WPA_WPA2_PSK:
+ return ESP_HOSTED_SEC_WPA_WPA2_PSK;
+ case CTRL__WIFI_SEC_PROT__WPA2_ENTERPRISE:
+ return ESP_HOSTED_SEC_WPA2_ENTERPRISE;
+ case CTRL__WIFI_SEC_PROT__WPA3_PSK:
+ return ESP_HOSTED_SEC_WPA3_PSK;
+ case CTRL__WIFI_SEC_PROT__WPA2_WPA3_PSK:
+ return ESP_HOSTED_SEC_WPA2_WPA3_PSK;
+ default:
+ return ESP_HOSTED_SEC_INVALID;
+ }
+}
+
+static CtrlWifiSecProt hosted_security_to_sec_prot(esp_hosted_security_t hosted_security)
+{
+ switch (hosted_security) {
+ case ESP_HOSTED_SEC_OPEN:
+ return CTRL__WIFI_SEC_PROT__Open;
+ case ESP_HOSTED_SEC_WEP:
+ return CTRL__WIFI_SEC_PROT__WEP;
+ case ESP_HOSTED_SEC_WPA_PSK:
+ return CTRL__WIFI_SEC_PROT__WPA_PSK;
+ case ESP_HOSTED_SEC_WPA2_PSK:
+ return CTRL__WIFI_SEC_PROT__WPA2_PSK;
+ case ESP_HOSTED_SEC_WPA_WPA2_PSK:
+ return CTRL__WIFI_SEC_PROT__WPA_WPA2_PSK;
+ case ESP_HOSTED_SEC_WPA2_ENTERPRISE:
+ return CTRL__WIFI_SEC_PROT__WPA2_ENTERPRISE;
+ case ESP_HOSTED_SEC_WPA3_PSK:
+ return CTRL__WIFI_SEC_PROT__WPA3_PSK;
+ case ESP_HOSTED_SEC_WPA2_WPA3_PSK:
+ return CTRL__WIFI_SEC_PROT__WPA2_WPA3_PSK;
+ default:
+ abort(); // Range should be checked by the caller, making this unreachable
+ }
+}
+
+uint16_t esp_hosted_checksum(esp_header_t *esp_header) {
+ uint16_t checksum = 0;
+ esp_header->checksum = 0;
+ uint8_t *buf = (uint8_t *)esp_header;
+ for (size_t i = 0; i < (esp_header->len + sizeof(esp_header_t)); i++) {
+ checksum += buf[i];
+ }
+ return checksum;
+}
+
+#if ESP_HOSTED_DEBUG
+static void esp_hosted_dump_header(esp_header_t *esp_header) {
+ static const char *if_strs[] = { "STA", "AP", "SERIAL", "HCI", "PRIV", "TEST" };
+ if (esp_header->if_type > ESP_HOSTED_MAX_IF) {
+ return;
+ }
+ debug_printf("esp header: if %s_IF length %d offset %d checksum %d seq %d flags %x\n",
+ if_strs[esp_header->if_type], esp_header->len, esp_header->offset,
+ esp_header->checksum, esp_header->seq_num, esp_header->flags);
+
+ if (esp_header->if_type == ESP_HOSTED_SERIAL_IF) {
+ tlv_header_t *tlv_header = (tlv_header_t *)(esp_header->payload);
+ debug_printf("tlv header: ep_type %d ep_length %d ep_value %.8s data_type %d data_length %d\n",
+ tlv_header->ep_type, tlv_header->ep_length,
+ tlv_header->ep_value, tlv_header->data_type, tlv_header->data_length);
+ }
+}
+#endif
+
+static int32_t esp_hosted_resp_value(CtrlMsg *ctrl_msg) {
+ // Each response struct return value is located at a different offset,
+ // the following array maps response CtrlMsgs to return values (resp)
+ // offsets within each response struct.
+ const static size_t ctrl_msg_resp_offset[] = {
+ offsetof(CtrlMsgRespGetMacAddress, resp),
+ offsetof(CtrlMsgRespSetMacAddress, resp),
+ offsetof(CtrlMsgRespGetMode, resp),
+ offsetof(CtrlMsgRespSetMode, resp),
+ offsetof(CtrlMsgRespScanResult, resp),
+ offsetof(CtrlMsgRespGetAPConfig, resp),
+ offsetof(CtrlMsgRespConnectAP, resp),
+ offsetof(CtrlMsgRespGetStatus, resp),
+ offsetof(CtrlMsgRespGetSoftAPConfig, resp),
+ offsetof(CtrlMsgRespSetSoftAPVendorSpecificIE, resp),
+ offsetof(CtrlMsgRespStartSoftAP, resp),
+ offsetof(CtrlMsgRespSoftAPConnectedSTA, resp),
+ offsetof(CtrlMsgRespGetStatus, resp),
+ offsetof(CtrlMsgRespSetMode, resp),
+ offsetof(CtrlMsgRespGetMode, resp),
+ offsetof(CtrlMsgRespOTABegin, resp),
+ offsetof(CtrlMsgRespOTAWrite, resp),
+ offsetof(CtrlMsgRespOTAEnd, resp),
+ offsetof(CtrlMsgRespSetWifiMaxTxPower, resp),
+ offsetof(CtrlMsgRespGetWifiCurrTxPower, resp),
+ offsetof(CtrlMsgRespConfigHeartbeat, resp),
+ };
+
+ int32_t resp = -1;
+ size_t index = ctrl_msg->msg_id - CTRL_MSG_ID__Resp_Base;
+
+ // All types of messages share the same payload base address.
+ if (ctrl_msg->resp_get_mac_address != NULL &&
+ ctrl_msg->msg_type == CTRL_MSG_TYPE__Resp &&
+ index > 0 && index <= MP_ARRAY_SIZE(ctrl_msg_resp_offset)) {
+ // Return the response struct's return value.
+ size_t offset = ctrl_msg_resp_offset[index - 1];
+ resp = *((int32_t *)((char *)ctrl_msg->resp_get_mac_address + offset));
+ }
+ return resp;
+}
+
+static int esp_hosted_request(CtrlMsgId msg_id, void *ctrl_payload) {
+ CtrlMsg ctrl_msg = {0};
+ ctrl_msg__init(&ctrl_msg);
+ ctrl_msg.msg_id = msg_id;
+ ctrl_msg.payload_case = msg_id;
+
+ // All types of messages share the same payload base address.
+ ctrl_msg.req_get_mac_address = ctrl_payload;
+
+ // Pack protobuf
+ size_t payload_size = ctrl_msg__get_packed_size(&ctrl_msg);
+ if ((payload_size + sizeof(tlv_header_t)) > ESP_FRAME_MAX_PAYLOAD) {
+ error_printf("esp_hosted_request() payload size > max payload %d\n", msg_id);
+ return -1;
+ }
+
+ esp_header_t *esp_header = (esp_header_t *)(esp_state.buf);
+ tlv_header_t *tlv_header = (tlv_header_t *)(esp_header->payload);
+
+ esp_header->if_type = ESP_HOSTED_SERIAL_IF;
+ esp_header->if_num = 0;
+ esp_header->flags = 0;
+ esp_header->len = payload_size + sizeof(tlv_header_t);
+ esp_header->offset = sizeof(esp_header_t);
+ esp_header->seq_num = esp_state.seq_num++;
+
+ tlv_header->ep_type = TLV_HEADER_TYPE_EP;
+ tlv_header->ep_length = 8;
+ memcpy(tlv_header->ep_value, TLV_HEADER_EP_RESP, 8);
+ tlv_header->data_type = TLV_HEADER_TYPE_DATA;
+ tlv_header->data_length = payload_size;
+ ctrl_msg__pack(&ctrl_msg, tlv_header->data);
+ esp_header->checksum = esp_hosted_checksum(esp_header);
+
+ size_t frame_size = (sizeof(esp_header_t) + esp_header->len + 3) & ~3U;
+ if (esp_hosted_hal_spi_transfer(esp_state.buf, NULL, frame_size) != 0) {
+ error_printf("esp_hosted_request() request %d failed\n", msg_id);
+ return -1;
+ }
+ return 0;
+}
+
+static CtrlMsg *esp_hosted_response(CtrlMsgId msg_id, uint32_t timeout) {
+ CtrlMsg *ctrl_msg = NULL;
+ for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(10)) {
+ if (!esp_hosted_stack_empty(&esp_state.stack)) {
+ ctrl_msg = esp_hosted_stack_pop(&esp_state.stack, true);
+ if (ctrl_msg->msg_id == msg_id) {
+ ctrl_msg = esp_hosted_stack_pop(&esp_state.stack, false);
+ break;
+ }
+
+ debug_printf("esp_hosted_response() waiting for id %lu last id %lu\n", msg_id, ctrl_msg->msg_id);
+ ctrl_msg = NULL;
+ }
+
+ if (timeout == 0) {
+ // Request expected a sync response.
+ return NULL;
+ }
+
+ // Check timeout.
+ if ((mp_hal_ticks_ms() - start) >= timeout) {
+ return NULL;
+ }
+
+ MICROPY_EVENT_POLL_HOOK
+ }
+
+ // If message type is a response, check the response struct's return value.
+ if (ctrl_msg->msg_type == CTRL_MSG_TYPE__Resp && esp_hosted_resp_value(ctrl_msg) != 0) {
+ error_printf("esp_hosted_response() response %d failed %d\n", msg_id, esp_hosted_resp_value(ctrl_msg));
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+ return NULL;
+ }
+
+ return ctrl_msg;
+}
+
+static int esp_hosted_ctrl(CtrlMsgId req_id, void *req_payload, CtrlMsg **resp_msg) {
+ if (esp_hosted_request(req_id, req_payload) != 0) {
+ return -1;
+ }
+ uint32_t resp_id = (req_id - CTRL_MSG_ID__Req_Base) + CTRL_MSG_ID__Resp_Base;
+ if ((*resp_msg = esp_hosted_response(resp_id, ESP_SYNC_REQ_TIMEOUT)) == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+int esp_hosted_wifi_poll(void) {
+ size_t offset = 0;
+ esp_header_t *esp_header = (esp_header_t *)(esp_state.buf);
+ tlv_header_t *tlv_header = (tlv_header_t *)(esp_header->payload);
+
+ if (!(esp_state.flags & ESP_HOSTED_FLAGS_INIT) || !esp_hosted_hal_data_ready()) {
+ return 0;
+ }
+
+ do {
+ esp_header_t *frag_header = (esp_header_t *)(esp_state.buf + offset);
+ if ((ESP_STATE_BUF_SIZE - offset) < ESP_FRAME_MAX_SIZE) {
+ // This shouldn't happen, but if it did stop polling.
+ error_printf("esp_hosted_poll() spi buffer overflow offs %d\n", offset);
+ return -1;
+ }
+
+ if (esp_hosted_hal_spi_transfer(NULL, esp_state.buf + offset, ESP_FRAME_MAX_SIZE) != 0) {
+ error_printf("esp_hosted_poll() spi transfer failed\n");
+ return 0;
+ }
+
+ if (frag_header->len == 0 ||
+ frag_header->len > ESP_FRAME_MAX_PAYLOAD ||
+ frag_header->offset != sizeof(esp_header_t)) {
+ // Invalid or empty packet, just ignore it silently.
+ warn_printf("esp_hosted_poll() invalid frame size %d offset %d\n",
+ esp_header->len, esp_header->offset);
+ return 0;
+ }
+
+ uint16_t checksum = frag_header->checksum;
+ frag_header->checksum = esp_hosted_checksum(frag_header);
+ if (frag_header->checksum != checksum) {
+ warn_printf("esp_hosted_poll() invalid checksum, expected %d\n", checksum);
+ return 0;
+ }
+
+ if (offset) {
+ // Combine fragmented packet
+ if ((esp_header->seq_num + 1) != frag_header->seq_num) {
+ error_printf("esp_hosted_poll() fragmented frame sequence mismatch\n");
+ return 0;
+ }
+ esp_header->len += frag_header->len;
+ esp_header->seq_num = frag_header->seq_num;
+ esp_header->flags = frag_header->flags;
+ info_printf("esp_hosted_poll() received fragmented packet %d\n", frag_header->len);
+ // Append the current fragment's payload to the previous one.
+ memcpy(esp_state.buf + offset, frag_header->payload, frag_header->len);
+ }
+
+ offset = sizeof(esp_header_t) + esp_header->len;
+ } while ((esp_header->flags & ESP_FRAME_FLAGS_FRAGMENT));
+
+ #if ESP_HOSTED_DEBUG
+ esp_hosted_dump_header(esp_header);
+ #endif
+
+ switch (esp_header->if_type) {
+ case ESP_HOSTED_STA_IF:
+ case ESP_HOSTED_AP_IF: {
+ // Networking traffic
+ uint32_t itf = esp_header->if_type;
+ if (netif_is_link_up(&esp_state.netif[itf])) {
+ if (esp_hosted_netif_input(&esp_state, itf, esp_header->payload, esp_header->len) != 0) {
+ error_printf("esp_hosted_poll() netif input failed\n");
+ return -1;
+ }
+ debug_printf("esp_hosted_poll() eth frame input %d\n", esp_header->len);
+ }
+ return 0;
+ }
+ case ESP_HOSTED_PRIV_IF: {
+ esp_event_t *priv_event = (esp_event_t *)(esp_header->payload);
+ if (esp_header->priv_pkt_type == ESP_PACKET_TYPE_EVENT &&
+ priv_event->event_type == ESP_PRIV_EVENT_INIT) {
+ esp_state.chip_id = priv_event->event_data[2];
+ esp_state.spi_clk = priv_event->event_data[5];
+ esp_state.chip_flags = priv_event->event_data[8];
+ info_printf("esp_hosted_poll() chip id %d spi_mhz %d caps 0x%x\n",
+ esp_state.chip_id, esp_state.spi_clk, esp_state.chip_flags);
+ }
+ return 0;
+ }
+ case ESP_HOSTED_HCI_IF:
+ case ESP_HOSTED_TEST_IF:
+ case ESP_HOSTED_MAX_IF:
+ error_printf("esp_hosted_poll() unexpected interface type %d\n", esp_header->if_type);
+ return 0;
+ case ESP_HOSTED_SERIAL_IF:
+ // Requires further processing
+ break;
+ }
+
+ CtrlMsg *ctrl_msg = ctrl_msg__unpack(&protobuf_alloc, tlv_header->data_length, tlv_header->data);
+ if (ctrl_msg == NULL) {
+ error_printf("esp_hosted_poll() failed to unpack protobuf\n");
+ return 0;
+ }
+
+ if (ctrl_msg->msg_type == CTRL_MSG_TYPE__Event) {
+ switch (ctrl_msg->msg_id) {
+ case CTRL_MSG_ID__Event_ESPInit:
+ esp_state.flags |= ESP_HOSTED_FLAGS_ACTIVE;
+ break;
+ case CTRL_MSG_ID__Event_Heartbeat:
+ esp_state.last_hb_ms = mp_hal_ticks_ms();
+ info_printf("esp_hosted_poll() heartbeat %lu\n", esp_state.last_hb_ms);
+ return 0;
+ case CTRL_MSG_ID__Event_StationDisconnectFromAP:
+ esp_state.flags &= ~ESP_HOSTED_FLAGS_STA_CONNECTED;
+ return 0;
+ case CTRL_MSG_ID__Event_StationDisconnectFromESPSoftAP:
+ return 0;
+ default:
+ error_printf("esp_hosted_poll() unexpected event %d\n", ctrl_msg->msg_id);
+ return 0;
+ }
+ }
+
+ // Responses that should be handled here.
+ if (ctrl_msg->msg_type == CTRL_MSG_TYPE__Resp) {
+ switch (ctrl_msg->msg_id) {
+ case CTRL_MSG_ID__Resp_ConnectAP: {
+ if (esp_hosted_resp_value(ctrl_msg) == 0) {
+ esp_state.flags |= ESP_HOSTED_FLAGS_STA_CONNECTED;
+ }
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+ debug_printf("esp_hosted_poll() state %d\n", esp_state.flags);
+ return 0;
+ }
+ default:
+ break;
+ }
+ }
+
+ // A control message resp/event will be pushed on the stack for further processing.
+ if (!esp_hosted_stack_push(&esp_state.stack, ctrl_msg)) {
+ error_printf("esp_hosted_poll() message stack full\n");
+ return -1;
+ }
+
+ debug_printf("esp_hosted_poll() pushed msg_type %lu msg_id %lu\n", ctrl_msg->msg_type, ctrl_msg->msg_id);
+ return 0;
+}
+
+int esp_hosted_wifi_init(uint32_t itf) {
+ if (esp_state.flags == ESP_HOSTED_FLAGS_RESET) {
+ // Init state
+ memset(&esp_state, 0, sizeof(esp_hosted_state_t));
+ esp_hosted_stack_init(&esp_state.stack);
+
+ // Low-level pins and SPI init, memory pool allocation etc...
+ if (esp_hosted_hal_init(ESP_HOSTED_MODE_WIFI) != 0) {
+ return -1;
+ }
+
+ // Allow polling the bus.
+ esp_state.flags |= ESP_HOSTED_FLAGS_INIT;
+
+ CtrlMsg *ctrl_msg = NULL;
+
+ // Wait for an ESPInit control event.
+ ctrl_msg = esp_hosted_response(CTRL_MSG_ID__Event_ESPInit, ESP_SYNC_REQ_TIMEOUT);
+ if (ctrl_msg == NULL) {
+ return -1;
+ }
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+
+ // Set WiFi mode to STA/AP.
+ CtrlMsgReqSetMode ctrl_payload;
+ ctrl_msg__req__set_mode__init(&ctrl_payload);
+ ctrl_payload.mode = CTRL__WIFI_MODE__APSTA;
+ if (esp_hosted_ctrl(CTRL_MSG_ID__Req_SetWifiMode, &ctrl_payload, &ctrl_msg) != 0) {
+ return -1;
+ }
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+
+ info_printf("esp_hosted_init() device initialized\n");
+ }
+
+ if (!netif_is_link_up(&esp_state.netif[itf])) {
+ // Init lwip netif, and start DHCP client/server.
+ esp_hosted_netif_init(&esp_state, itf);
+ info_printf("esp_hosted_init() initialized itf %lu\n", itf);
+ }
+ return 0;
+}
+
+int esp_hosted_wifi_disable(uint32_t itf) {
+ // Remove netif
+ esp_hosted_netif_deinit(&esp_state, itf);
+
+ if (itf == ESP_HOSTED_STA_IF) {
+ esp_state.flags &= ~ESP_HOSTED_FLAGS_STA_CONNECTED;
+ } else {
+ esp_state.flags &= ~ESP_HOSTED_FLAGS_AP_STARTED;
+ }
+
+ info_printf("esp_hosted_deinit() deinitialized itf %lu\n", itf);
+ return 0;
+}
+
+int esp_hosted_wifi_deinit(void) {
+ if (esp_state.flags & ESP_HOSTED_FLAGS_INIT) {
+ // Remove network interfaces
+ esp_hosted_wifi_disable(ESP_HOSTED_STA_IF);
+ esp_hosted_wifi_disable(ESP_HOSTED_AP_IF);
+
+ // Reset state
+ memset(&esp_state, 0, sizeof(esp_hosted_state_t));
+ esp_hosted_stack_init(&esp_state.stack);
+
+ info_printf("esp_hosted_deinit() deinitialized\n");
+ }
+ return 0;
+}
+
+void *esp_hosted_wifi_get_netif(uint32_t itf) {
+ return &esp_state.netif[itf];
+}
+
+int esp_hosted_wifi_get_mac(int itf, uint8_t *mac) {
+ CtrlMsgReqGetMacAddress ctrl_payload;
+ ctrl_msg__req__get_mac_address__init(&ctrl_payload);
+ ctrl_payload.mode = (itf == ESP_HOSTED_STA_IF) ? CTRL__WIFI_MODE__STA : CTRL__WIFI_MODE__AP;
+
+ CtrlMsg *ctrl_msg = NULL;
+ if (esp_hosted_ctrl(CTRL_MSG_ID__Req_GetMACAddress, &ctrl_payload, &ctrl_msg) != 0) {
+ error_printf("esp_hosted_get_mac() request failed\n");
+ return -1;
+ }
+
+ ProtobufCBinaryData macstr = ctrl_msg->resp_get_mac_address->mac;
+ if (macstr.data) {
+ esp_hosted_macstr_to_bytes(macstr.data, macstr.len, mac);
+ }
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+ return 0;
+}
+
+int esp_hosted_wifi_connect(const char *ssid, const char *bssid, esp_hosted_security_t security, const char *key, uint16_t channel) {
+ CtrlMsgReqConnectAP ctrl_payload;
+ ctrl_msg__req__connect_ap__init(&ctrl_payload);
+
+ if (security >= ESP_HOSTED_SEC_MAX) {
+ // Note: this argument is otherwise unused(!)
+ return -1;
+ }
+
+ ctrl_payload.ssid = (char *)ssid;
+ ctrl_payload.bssid = (char *)bssid;
+ ctrl_payload.pwd = (char *)key;
+ ctrl_payload.is_wpa3_supported = false;
+ ctrl_payload.listen_interval = 0;
+
+ if (esp_hosted_request(CTRL_MSG_ID__Req_ConnectAP, &ctrl_payload) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int esp_hosted_wifi_start_ap(const char *ssid, esp_hosted_security_t security, const char *key, uint16_t channel) {
+ CtrlMsgReqStartSoftAP ctrl_payload;
+ ctrl_msg__req__start_soft_ap__init(&ctrl_payload);
+
+ if (security >= ESP_HOSTED_SEC_MAX) {
+ return -1;
+ }
+
+ ctrl_payload.ssid = (char *)ssid;
+ ctrl_payload.pwd = (char *)key;
+ ctrl_payload.chnl = channel;
+ ctrl_payload.sec_prot = hosted_security_to_sec_prot(security);
+ ctrl_payload.max_conn = ESP_HOSTED_MAX_AP_CLIENTS;
+ ctrl_payload.ssid_hidden = false;
+ ctrl_payload.bw = CTRL__WIFI_BW__HT40;
+
+ CtrlMsg *ctrl_msg = NULL;
+ if (esp_hosted_ctrl(CTRL_MSG_ID__Req_StartSoftAP, &ctrl_payload, &ctrl_msg) != 0) {
+ return -1;
+ }
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+ esp_state.flags |= ESP_HOSTED_FLAGS_AP_STARTED;
+ return 0;
+}
+
+int esp_hosted_wifi_disconnect(uint32_t itf) {
+ CtrlMsg *ctrl_msg = NULL;
+ CtrlMsgReqGetStatus ctrl_payload;
+ ctrl_msg__req__get_status__init(&ctrl_payload);
+
+ if (itf == ESP_HOSTED_STA_IF) {
+ esp_state.flags &= ~ESP_HOSTED_FLAGS_STA_CONNECTED;
+ if (esp_hosted_ctrl(CTRL_MSG_ID__Req_DisconnectAP, &ctrl_payload, &ctrl_msg) != 0) {
+ return -1;
+ }
+ } else {
+ esp_state.flags &= ~ESP_HOSTED_FLAGS_AP_STARTED;
+ if (esp_hosted_ctrl(CTRL_MSG_ID__Req_StopSoftAP, &ctrl_payload, &ctrl_msg) != 0) {
+ return -1;
+ }
+ }
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+ return 0;
+}
+
+int esp_hosted_wifi_link_status(uint32_t itf) {
+ return netif_is_link_up(&esp_state.netif[itf]);
+}
+
+int esp_hosted_wifi_is_connected(uint32_t itf) {
+ if (!esp_hosted_wifi_link_status(itf)) {
+ return false;
+ }
+ if (itf == ESP_HOSTED_AP_IF) {
+ return esp_state.flags & ESP_HOSTED_FLAGS_AP_STARTED;
+ }
+ if ((esp_state.flags & ESP_HOSTED_FLAGS_STA_CONNECTED) &&
+ ((esp_state.flags & ESP_HOSTED_FLAGS_STATIC_IP) ||
+ dhcp_supplied_address(&esp_state.netif[itf]))) {
+ return true;
+ }
+ return false;
+}
+
+int esp_hosted_wifi_get_stations(uint8_t *sta_list, size_t *sta_count) {
+ CtrlMsgReqSoftAPConnectedSTA ctrl_payload;
+ ctrl_msg__req__soft_apconnected_sta__init(&ctrl_payload);
+
+ CtrlMsg *ctrl_msg = NULL;
+ if (esp_hosted_ctrl(CTRL_MSG_ID__Req_GetSoftAPConnectedSTAList, &ctrl_payload, &ctrl_msg) != 0) {
+ return -1;
+ }
+
+ CtrlMsgRespSoftAPConnectedSTA *resp = ctrl_msg->resp_softap_connected_stas_list;
+ *sta_count = resp->n_stations;
+ for (size_t i = 0; i < resp->n_stations; i++) {
+ ProtobufCBinaryData mac = resp->stations[i]->mac;
+ esp_hosted_macstr_to_bytes(mac.data, mac.len, &sta_list[i * 6]);
+ }
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+ return 0;
+}
+
+int esp_hosted_wifi_netinfo(esp_hosted_netinfo_t *netinfo) {
+ CtrlMsgReqGetAPConfig ctrl_payload;
+ ctrl_msg__req__get_apconfig__init(&ctrl_payload);
+
+ CtrlMsg *ctrl_msg = NULL;
+ if (esp_hosted_ctrl(CTRL_MSG_ID__Req_GetAPConfig, &ctrl_payload, &ctrl_msg) != 0) {
+ return -1;
+ }
+
+ netinfo->rssi = ctrl_msg->resp_get_ap_config->rssi;
+ netinfo->security = sec_prot_to_hosted_security(ctrl_msg->resp_get_ap_config->sec_prot);
+ netinfo->channel = ctrl_msg->resp_get_ap_config->chnl;
+
+ ProtobufCBinaryData ssid = ctrl_msg->resp_get_ap_config->ssid;
+ if (ssid.data) {
+ size_t ssid_len = MIN(ssid.len, (ESP_HOSTED_MAX_SSID_LEN - 1));
+ memcpy(netinfo->ssid, ssid.data, ssid_len);
+ netinfo->ssid[ssid_len] = 0;
+ }
+
+ ProtobufCBinaryData bssid = ctrl_msg->resp_get_ap_config->bssid;
+ if (bssid.data) {
+ esp_hosted_macstr_to_bytes(bssid.data, bssid.len, netinfo->bssid);
+ }
+
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+ return 0;
+}
+
+int esp_hosted_wifi_scan(esp_hosted_scan_callback_t scan_callback, void *arg, uint32_t timeout) {
+ CtrlMsgReqScanResult ctrl_payload;
+ ctrl_msg__req__scan_result__init(&ctrl_payload);
+
+ CtrlMsg *ctrl_msg = NULL;
+ if (esp_hosted_ctrl(CTRL_MSG_ID__Req_GetAPScanList, &ctrl_payload, &ctrl_msg) != 0) {
+ return -MP_ETIMEDOUT;
+ }
+
+ CtrlMsgRespScanResult *rp = ctrl_msg->resp_scan_ap_list;
+ for (int i = 0; i < rp->count; i++) {
+ esp_hosted_scan_result_t result = {0};
+ result.rssi = rp->entries[i]->rssi;
+ result.security = sec_prot_to_hosted_security(rp->entries[i]->sec_prot);
+ result.channel = rp->entries[i]->chnl;
+ if (rp->entries[i]->bssid.data) {
+ esp_hosted_macstr_to_bytes(rp->entries[i]->bssid.data, rp->entries[i]->bssid.len, result.bssid);
+ }
+
+ if (rp->entries[i]->ssid.len) {
+ size_t ssid_len = MIN(rp->entries[i]->ssid.len, (ESP_HOSTED_MAX_SSID_LEN - 1));
+ memcpy(result.ssid, rp->entries[i]->ssid.data, ssid_len);
+ result.ssid[ssid_len] = 0;
+ }
+ scan_callback(&result, arg);
+ }
+
+ ctrl_msg__free_unpacked(ctrl_msg, &protobuf_alloc);
+ return 0;
+}
+#endif // MICROPY_PY_NETWORK_ESP_HOSTED
diff --git a/drivers/esp-hosted/esp_hosted_wifi.h b/drivers/esp-hosted/esp_hosted_wifi.h
new file mode 100644
index 0000000000..03d225d88d
--- /dev/null
+++ b/drivers/esp-hosted/esp_hosted_wifi.h
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Arduino SA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ESP-Hosted WiFi driver.
+ */
+
+#ifndef MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_WIFI_H
+#define MICROPY_INCLUDED_DRIVERS_ESP_HOSTED_WIFI_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define ESP_HOSTED_IPV4_ADDR_LEN (4)
+#define ESP_HOSTED_MAC_ADDR_LEN (6)
+#define ESP_HOSTED_MAC_STR_LEN (18)
+#define ESP_HOSTED_MAX_SSID_LEN (32)
+#define ESP_HOSTED_MAX_WEP_LEN (13)
+#define ESP_HOSTED_MAX_WPA_LEN (63)
+#define ESP_HOSTED_MAX_AP_CLIENTS (3)
+
+#define ESP_HOSTED_AP_GATEWAY LWIP_MAKEU32(192, 168, 1, 1)
+#define ESP_HOSTED_AP_ADDRESS LWIP_MAKEU32(192, 168, 1, 1)
+#define ESP_HOSTED_AP_NETMASK LWIP_MAKEU32(255, 255, 255, 0)
+#define ESP_HOSTED_HOSTNAME "esphosted"
+
+typedef enum {
+ ESP_HOSTED_STA_IF = 0,
+ ESP_HOSTED_AP_IF,
+ ESP_HOSTED_SERIAL_IF,
+ ESP_HOSTED_HCI_IF,
+ ESP_HOSTED_PRIV_IF,
+ ESP_HOSTED_TEST_IF,
+ ESP_HOSTED_MAX_IF,
+} esp_hosted_interface_t;
+
+typedef enum {
+ ESP_HOSTED_SEC_INVALID = -1,
+ ESP_HOSTED_SEC_OPEN,
+ ESP_HOSTED_SEC_WEP,
+ ESP_HOSTED_SEC_WPA_PSK,
+ ESP_HOSTED_SEC_WPA2_PSK,
+ ESP_HOSTED_SEC_WPA_WPA2_PSK,
+ ESP_HOSTED_SEC_WPA2_ENTERPRISE,
+ ESP_HOSTED_SEC_WPA3_PSK,
+ ESP_HOSTED_SEC_WPA2_WPA3_PSK,
+ ESP_HOSTED_SEC_MAX,
+} esp_hosted_security_t;
+
+typedef struct {
+ uint8_t ip_addr[ESP_HOSTED_IPV4_ADDR_LEN];
+ uint8_t subnet_addr[ESP_HOSTED_IPV4_ADDR_LEN];
+ uint8_t gateway_addr[ESP_HOSTED_IPV4_ADDR_LEN];
+ uint8_t dns_addr[ESP_HOSTED_IPV4_ADDR_LEN];
+} esp_hosted_ifconfig_t;
+
+typedef struct {
+ int32_t rssi;
+ esp_hosted_security_t security;
+ uint8_t channel;
+ char ssid[ESP_HOSTED_MAX_SSID_LEN];
+ uint8_t bssid[ESP_HOSTED_MAC_ADDR_LEN];
+} esp_hosted_scan_result_t;
+
+typedef struct {
+ int32_t rssi;
+ esp_hosted_security_t security;
+ uint8_t channel;
+ char ssid[ESP_HOSTED_MAX_SSID_LEN];
+ uint8_t bssid[ESP_HOSTED_MAC_ADDR_LEN];
+} esp_hosted_netinfo_t;
+
+typedef int (*esp_hosted_scan_callback_t)(esp_hosted_scan_result_t *, void *);
+
+int esp_hosted_wifi_poll(void);
+int esp_hosted_wifi_init(uint32_t itf);
+int esp_hosted_wifi_deinit(void);
+int esp_hosted_wifi_disable(uint32_t itf);
+void *esp_hosted_wifi_get_netif(uint32_t itf);
+int esp_hosted_wifi_get_mac(int itf, uint8_t *mac);
+int esp_hosted_wifi_connect(const char *ssid, const char *bssid, esp_hosted_security_t security, const char *key, uint16_t channel);
+int esp_hosted_wifi_start_ap(const char *ssid, esp_hosted_security_t security, const char *key, uint16_t channel);
+int esp_hosted_wifi_disconnect(uint32_t itf);
+int esp_hosted_wifi_link_status(uint32_t itf);
+int esp_hosted_wifi_is_connected(uint32_t itf);
+int esp_hosted_wifi_get_stations(uint8_t *sta_list, size_t *sta_count);
+int esp_hosted_wifi_netinfo(esp_hosted_netinfo_t *netinfo);
+int esp_hosted_wifi_scan(esp_hosted_scan_callback_t scan_callback, void *arg, uint32_t timeout);
+#endif // MICROPY_INCLUDED_DRIVERS_ESPHOST_WIFI_H
diff --git a/extmod/extmod.mk b/extmod/extmod.mk
index a15b7e4a5f..d5b2230348 100644
--- a/extmod/extmod.mk
+++ b/extmod/extmod.mk
@@ -337,8 +337,51 @@ SRC_THIRDPARTY_C += $(addprefix $(WIZNET5K_DIR)/,\
Internet/DHCP/dhcp.c \
)
endif
+endif # MICROPY_PY_NETWORK_WIZNET5K
+
+ifeq ($(MICROPY_PY_NETWORK_ESP_HOSTED),1)
+ESP_HOSTED_DIR = drivers/esp-hosted
+PROTOBUF_C_DIR = lib/protobuf-c
+PROTOC ?= protoc-c
+GIT_SUBMODULES += $(PROTOBUF_C_DIR)
+
+CFLAGS += -DMICROPY_PY_NETWORK_ESP_HOSTED=1
+CFLAGS_EXTMOD += -DMICROPY_PY_NETWORK_ESP_HOSTED=1
+INC += -I$(TOP)/$(ESP_HOSTED_DIR)
+
+ESP_HOSTED_SRC_C = $(addprefix $(ESP_HOSTED_DIR)/,\
+ esp_hosted_wifi.c \
+ esp_hosted_netif.c \
+ esp_hosted_hal.c \
+ )
+
+ifeq ($(MICROPY_PY_BLUETOOTH),1)
+ESP_HOSTED_SRC_C += $(ESP_HOSTED_DIR)/esp_hosted_bthci.c
endif
+# Include the protobuf-c support functions
+ESP_HOSTED_SRC_C += $(addprefix $(PROTOBUF_C_DIR)/,\
+ protobuf-c/protobuf-c.c \
+ )
+
+$(BUILD)/$(PROTOBUF_C_DIR)/%.o: CFLAGS += -Wno-unused-but-set-variable
+
+# Generate esp_hosted-pb-c.c|h from esp_hosted.proto
+PROTO_GEN_SRC = $(BUILD)/extmod/esp_hosted.pb-c.c
+ESP_HOSTED_SRC_C += $(PROTO_GEN_SRC)
+
+$(PROTO_GEN_SRC): $(TOP)/$(ESP_HOSTED_DIR)/esp_hosted.proto
+ $(PROTOC) --proto_path=$(dir $<) --c_out=$(dir $@) $<
+
+# Scope the protobuf include paths to the esp_hosted source files, only
+ESP_HOSTED_OBJS = $(addprefix $(BUILD)/, $(ESP_HOSTED_SRC_C:.c=.o))
+$(ESP_HOSTED_OBJS): $(PROTO_GEN_SRC)
+$(ESP_HOSTED_OBJS): CFLAGS += -I$(dir $(PROTO_GEN_SRC)) -I$(TOP)/$(PROTOBUF_C_DIR)
+
+DRIVERS_SRC_C += $(ESP_HOSTED_SRC_C)
+
+endif # MICROPY_PY_NETWORK_ESP_HOSTED
+
################################################################################
# bluetooth