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:
parent
dc5ea0c77d
commit
ecedd78302
|
@ -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
|
||||
```
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue