You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
698 lines
25 KiB
698 lines
25 KiB
1 year ago
|
/*
|
||
|
* 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
|