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>
This commit is contained in:
iabdalkader 2023-06-21 17:13:09 +02:00 committed by Damien George
parent dc5ea0c77d
commit ecedd78302
12 changed files with 2138 additions and 0 deletions

View File

@ -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
```

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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