Skip to content

Commit ae9c0e6

Browse files
author
DatanoiseTV
committed
Fix caps bitmap, add runnable host example over SDIO, repo cleanup
A real bug: phy_rpc_resp_get_caps_t.caps[] was 4 bytes (32 slots, 0x000-0x01F). But RPCs at 0x020+ (TX_80211, ESP-NOW at 0x030, 802.15.4 at 0x040) were silently outside the bitmap, so get_caps() / has_capability() returned false for everything past 0x01F. Grow to 16 bytes (128 slots, covering 0x000-0x07F). - PHY_RPC_CAPS_BYTES = 16; struct field, host stub, slave handler, and host tests all use the constant. - Slave's caps assembly split per module: phy_rpc_extras_fill_caps() and phy_rpc_wireless_fill_caps() each contribute the bits for the RPCs they own (with weak-symbol guards for chip-dependent paths like 802.15.4). - Host has_capability() now bounds-checks against PHY_RPC_CAPS_BYTES * 8. While at it: - Wrap esp_wifi_set_event_mask (PHY_RPC_REQ_SET_EVENT_MASK at 0x027, response at 0x027|0x8000). Slave handler currently logs and returns ESP_OK; the actual esp_event filter wiring is TODO and documented in-line. - Reserve msg_id slots 0x050-0x053 for the full BT shockburst sequencer (preamble, address, CRC mode, raw burst TX) so the wire protocol stays stable when those land. No handlers yet — needs scope-confirmed RE. Examples + CI: - examples/host_p4_demo/ — minimal P4 IDF project. Builds against the host stub library, exercises set_channel + get_caps + get_info + get_phy_rssi + ESP-NOW init/add_peer/send. SDIO is explicitly selected (CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y), SPI/SPI-HD/UART are explicitly disabled in sdkconfig.defaults. - .github/workflows/build.yml gains a host-example job that idf.py builds it for esp32p4 — closes the gap where a host-stub regression could pass CI. Plumbing fixes that fell out of getting the example to compile: - vendor/esp-hosted-mcu/ -> vendor/esp_hosted/ (component-name mismatch; idf_component_manager's override_path requires the leaf dir to match the dependency name). - host/ -> esp_hosted_open/ (same reason; dependents say REQUIRES esp_hosted_open). Build sizes (IDF v6.0): slave (esp32c5): 0x1631c0 (26% free in 1.875 MB factory) host example (P4): 0x90c90 (43% free in 1 MB) host tests: 3 passing, including new caps-bitmap layout Prebuilt firmware refreshed.
1 parent 26e9cca commit ae9c0e6

138 files changed

Lines changed: 2868 additions & 2576 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ jobs:
1515
- uses: actions/checkout@v4
1616
- run: make -C tests/host test
1717

18+
host-example:
19+
name: build P4 host example (SDIO)
20+
runs-on: ubuntu-latest
21+
container:
22+
image: espressif/idf:release-v5.5
23+
steps:
24+
- uses: actions/checkout@v4
25+
- name: idf.py build (esp32p4)
26+
working-directory: examples/host_p4_demo
27+
run: |
28+
. $IDF_PATH/export.sh
29+
idf.py set-target esp32p4
30+
idf.py build
31+
1832
slave:
1933
name: build slave firmware
2034
runs-on: ubuntu-latest
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ esp_err_t esp_hosted_open_set_bt_filter (uint32_t reg_value);
8181
/* Capability discovery — bitmap indexed by request msg_id low byte;
8282
* tells the host which RPCs the slave's libphy.a actually exposes
8383
* on this chip. */
84-
esp_err_t esp_hosted_open_get_caps (uint8_t caps[4]);
85-
bool esp_hosted_open_has_capability (uint32_t request_msg_id, const uint8_t caps[4]);
84+
esp_err_t esp_hosted_open_get_caps (uint8_t caps[16]);
85+
bool esp_hosted_open_has_capability (uint32_t request_msg_id, const uint8_t caps[16]);
8686

8787
/* ---------- Raw 802.11 TX / promiscuous RX ---------------------- */
8888

@@ -200,6 +200,13 @@ typedef void (*esp_hosted_open_ieee154_rx_cb_t)(const uint8_t *frame, size_t len
200200

201201
esp_err_t esp_hosted_open_register_ieee154_rx_cb(esp_hosted_open_ieee154_rx_cb_t cb, void *ctx);
202202

203+
/* ---------- misc -------------------------------------------------- */
204+
205+
/* Suppress noisy WIFI_EVENT_* notifications on the slave so the host
206+
* only gets events it cares about. mask is a bitmap of event IDs;
207+
* passing 0 re-enables everything. */
208+
esp_err_t esp_hosted_open_set_event_mask (uint64_t mask);
209+
203210
/* ---------- BLE / Bluetooth (already exposed by upstream) ----- */
204211
/*
205212
* BLE and BT classic come for free with the vendored esp-hosted:
Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@ extern "C" {
115115
#define PHY_RPC_REQ_IEEE154_SET_PROMISC PHY_RPC_MSG_REQ(0x043) /* uint8_t */
116116
#define PHY_RPC_REQ_IEEE154_TX_RAW PHY_RPC_MSG_REQ(0x044) /* raw frame */
117117

118+
/* Wi-Fi event-mask suppression */
119+
#define PHY_RPC_REQ_SET_EVENT_MASK PHY_RPC_MSG_REQ(0x027) /* uint64_t mask */
120+
121+
/* Reserved for BT-radio shockburst (nRF24L01+ emulation). The TX-gain
122+
* knob (PHY_RPC_REQ_SET_BT_TX_GAIN) and filter override
123+
* (PHY_RPC_REQ_SET_BT_FILTER) are wired; the rest of the sequence
124+
* (preamble bytes, address registers, CRC mode, raw burst TX) needs
125+
* scope-confirmed RE before slave handlers can land. msg_id slots are
126+
* reserved here so the wire protocol stays stable when they ship. */
127+
#define PHY_RPC_REQ_BT_SET_PREAMBLE PHY_RPC_MSG_REQ(0x050) /* TBD */
128+
#define PHY_RPC_REQ_BT_SET_ADDRESS PHY_RPC_MSG_REQ(0x051) /* TBD */
129+
#define PHY_RPC_REQ_BT_SET_CRC_MODE PHY_RPC_MSG_REQ(0x052) /* TBD */
130+
#define PHY_RPC_REQ_BT_BURST_TX PHY_RPC_MSG_REQ(0x053) /* TBD */
131+
118132
/* Response IDs (slave->host, sent via esp_hosted_send_custom_data
119133
* AFTER processing the matching request). Each carries a header with
120134
* the original op_id and an int32 status. */
@@ -169,6 +183,12 @@ extern "C" {
169183
#define PHY_RPC_RESP_IEEE154_SET_PROMISC PHY_RPC_MSG_RESP(0x043)
170184
#define PHY_RPC_RESP_IEEE154_TX_RAW PHY_RPC_MSG_RESP(0x044)
171185

186+
#define PHY_RPC_RESP_SET_EVENT_MASK PHY_RPC_MSG_RESP(0x027)
187+
#define PHY_RPC_RESP_BT_SET_PREAMBLE PHY_RPC_MSG_RESP(0x050)
188+
#define PHY_RPC_RESP_BT_SET_ADDRESS PHY_RPC_MSG_RESP(0x051)
189+
#define PHY_RPC_RESP_BT_SET_CRC_MODE PHY_RPC_MSG_RESP(0x052)
190+
#define PHY_RPC_RESP_BT_BURST_TX PHY_RPC_MSG_RESP(0x053)
191+
172192
/* Async events (slave->host, no request/response correlation) */
173193
#define PHY_RPC_EVT_OCB_FRAME PHY_RPC_MSG_EVT(0x001) /* OCB-filtered LLC payload (V2X) */
174194
#define PHY_RPC_EVT_PHY_STATE PHY_RPC_MSG_EVT(0x002) /* periodic stats push */
@@ -391,6 +411,11 @@ typedef struct __attribute__((packed)) {
391411
uint8_t frame[];
392412
} phy_rpc_req_ieee154_tx_raw_t;
393413

414+
typedef struct __attribute__((packed)) {
415+
phy_rpc_hdr_t hdr;
416+
uint64_t mask; /* bitmap of WIFI_EVENT_* IDs to suppress */
417+
} phy_rpc_req_set_event_mask_t;
418+
394419
/* Response bodies (after phy_rpc_resp_hdr_t) */
395420

396421
typedef struct __attribute__((packed)) {
@@ -419,14 +444,16 @@ typedef struct __attribute__((packed)) {
419444
int8_t celsius;
420445
} phy_rpc_resp_get_temperature_t;
421446

422-
/* GET_CAPS reports which RPCs the slave's libphy.a actually exposes
423-
* on this chip — bit n is set iff request id (PHY_RPC_MSG_BASE | n) is
424-
* wired and the underlying symbol resolved at link time. The host
425-
* uses this to gracefully degrade. caps[0] covers msg ids 0x000-0x007,
426-
* caps[1] covers 0x008-0x00F, ..., caps[3] covers 0x018-0x01F. */
447+
/* GET_CAPS reports which RPCs the slave actually exposes on this
448+
* chip — bit n of caps[] is set iff request id (PHY_RPC_MSG_BASE | n)
449+
* is wired and the underlying symbol resolved at link time. The host
450+
* uses this to gracefully degrade. 16 bytes covers msg ids
451+
* 0x000-0x07F, which fits the current namespace plus all currently
452+
* reserved future slots. */
453+
#define PHY_RPC_CAPS_BYTES 16
427454
typedef struct __attribute__((packed)) {
428455
phy_rpc_resp_hdr_t hdr;
429-
uint8_t caps[4];
456+
uint8_t caps[PHY_RPC_CAPS_BYTES];
430457
} phy_rpc_resp_get_caps_t;
431458

432459
typedef struct __attribute__((packed)) {
Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ esp_err_t esp_hosted_open_init(void)
324324
PHY_RPC_RESP_IEEE154_ENABLE, PHY_RPC_RESP_IEEE154_SET_CHAN,
325325
PHY_RPC_RESP_IEEE154_SET_PANID, PHY_RPC_RESP_IEEE154_SET_PROMISC,
326326
PHY_RPC_RESP_IEEE154_TX_RAW,
327+
PHY_RPC_RESP_SET_EVENT_MASK,
327328
};
328329
for (size_t i = 0; i < sizeof(resp_ids) / sizeof(*resp_ids); i++) {
329330
esp_err_t err = esp_hosted_register_custom_callback(resp_ids[i], on_response, NULL);
@@ -552,19 +553,21 @@ esp_err_t esp_hosted_open_set_bt_filter(uint32_t reg_value)
552553
&reg_value, sizeof(reg_value), NULL, 0, NULL);
553554
}
554555

555-
esp_err_t esp_hosted_open_get_caps(uint8_t caps[4])
556+
esp_err_t esp_hosted_open_get_caps(uint8_t caps[16])
556557
{
557558
if (!caps) return ESP_ERR_INVALID_ARG;
559+
memset(caps, 0, 16);
558560
size_t got = 0;
559561
return do_call(PHY_RPC_REQ_GET_CAPS, PHY_RPC_RESP_GET_CAPS,
560-
NULL, 0, caps, 4, &got);
562+
NULL, 0, caps, PHY_RPC_CAPS_BYTES, &got);
561563
}
562564

563-
bool esp_hosted_open_has_capability(uint32_t request_msg_id, const uint8_t caps[4])
565+
bool esp_hosted_open_has_capability(uint32_t request_msg_id, const uint8_t caps[16])
564566
{
565-
/* Low byte of msg_id is the index into our cap bitmap (0x000–0x01F). */
567+
/* Low byte of msg_id is the bit index into our cap bitmap. 16 bytes
568+
* × 8 bits = 128 slots, covering msg ids 0x00–0x7F. */
566569
uint32_t low = request_msg_id & 0xFFu;
567-
if (low > 0x1F) return false;
570+
if (low >= PHY_RPC_CAPS_BYTES * 8) return false;
568571
return (caps[low / 8] & (1u << (low % 8))) != 0;
569572
}
570573

@@ -724,6 +727,12 @@ esp_err_t esp_hosted_open_ieee154_set_pan_id(uint16_t pan_id)
724727
esp_err_t esp_hosted_open_ieee154_set_promiscuous(bool enable)
725728
{ ONE_BYTE_REQ(PHY_RPC_REQ_IEEE154_SET_PROMISC, PHY_RPC_RESP_IEEE154_SET_PROMISC, uint8_t, enable ? 1 : 0); }
726729

730+
esp_err_t esp_hosted_open_set_event_mask(uint64_t mask)
731+
{
732+
return do_call(PHY_RPC_REQ_SET_EVENT_MASK, PHY_RPC_RESP_SET_EVENT_MASK,
733+
&mask, sizeof(mask), NULL, 0, NULL);
734+
}
735+
727736
esp_err_t esp_hosted_open_ieee154_tx_raw(const uint8_t *frame, size_t len, bool cca)
728737
{
729738
if (!frame || !len || len > 127) return ESP_ERR_INVALID_ARG;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
3+
4+
# Point at the host stub library *as a single component* (not at its
5+
# parent — that would pull in slave/ etc. as accidental components).
6+
set(EXTRA_COMPONENT_DIRS
7+
"${CMAKE_CURRENT_LIST_DIR}/../../esp_hosted_open"
8+
)
9+
# vendor/esp_hosted/ comes in via idf_component.yml override_path
10+
11+
project(host_p4_demo)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
idf_component_register(
2+
SRCS "main.c"
3+
INCLUDE_DIRS "."
4+
REQUIRES esp_hosted_open esp_hosted nvs_flash esp_event esp_netif esp_wifi
5+
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
## Pulls esp_wifi_remote (and its deps eppp_link / esp_serial_slave_link /
2+
## wifi_remote_over_eppp) from the registry, but overrides esp_hosted to
3+
## use the vendored copy two levels up.
4+
dependencies:
5+
idf:
6+
version: ">=5.5"
7+
espressif/esp_wifi_remote:
8+
version: "1.5.1"
9+
espressif/esp_hosted:
10+
override_path: "../../../vendor/esp_hosted"

examples/host_p4_demo/main/main.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* host_p4_demo — minimal P4 application that uses esp_hosted_open
3+
* over SDIO to talk to an esp-hosted-open slave.
4+
*
5+
* What it does at boot:
6+
* 1. esp_hosted_init() — bring up SDIO transport
7+
* 2. esp_hosted_open_init() — register CITS custom-RPC handlers
8+
* 3. get_caps() — see which RPCs the slave actually
9+
* exposes on this chip
10+
* 4. get_info() — current slave state
11+
* 5. set_channel(180) — tune the C5 to CCH (5.900 GHz)
12+
* 6. get_phy_rssi() — single-shot RSSI poll
13+
* 7. esp_now_init() + add_peer +
14+
* send — fire one ESP-NOW broadcast
15+
*
16+
* Each call uses *only* SDIO — no UART / serial console traffic, no
17+
* SPI fallback. SDIO transport is asserted in sdkconfig.defaults.
18+
*/
19+
20+
#include <inttypes.h>
21+
#include <stdio.h>
22+
#include <string.h>
23+
24+
#include "esp_log.h"
25+
#include "esp_event.h"
26+
#include "esp_netif.h"
27+
#include "nvs_flash.h"
28+
#include "freertos/FreeRTOS.h"
29+
#include "freertos/task.h"
30+
31+
#include "esp_hosted.h"
32+
#include "esp_hosted_open.h"
33+
#include "phy_rpc_proto.h" /* PHY_RPC_REQ_* ids for capability checks */
34+
35+
static const char *TAG = "host_demo";
36+
37+
static void on_espnow_rx(const uint8_t *data, size_t len,
38+
const esp_hosted_open_espnow_meta_t *m, void *ctx)
39+
{
40+
ESP_LOGI(TAG, "ESP-NOW rx %u B from %02x:%02x:%02x:%02x:%02x:%02x rssi=%d",
41+
(unsigned)len,
42+
m->src_mac[0], m->src_mac[1], m->src_mac[2],
43+
m->src_mac[3], m->src_mac[4], m->src_mac[5],
44+
m->rssi_dbm);
45+
}
46+
47+
void app_main(void)
48+
{
49+
ESP_ERROR_CHECK(nvs_flash_init());
50+
ESP_ERROR_CHECK(esp_event_loop_create_default());
51+
ESP_ERROR_CHECK(esp_netif_init());
52+
53+
/* Bring up the SDIO transport to the slave. */
54+
ESP_LOGI(TAG, "starting esp-hosted SDIO link…");
55+
if (esp_hosted_init() != 0) {
56+
ESP_LOGE(TAG, "esp_hosted_init failed");
57+
return;
58+
}
59+
vTaskDelay(pdMS_TO_TICKS(1500));
60+
61+
/* Register the CITS custom-RPC layer. */
62+
ESP_ERROR_CHECK(esp_hosted_open_init());
63+
ESP_ERROR_CHECK(esp_hosted_open_register_espnow_rx_cb(on_espnow_rx, NULL));
64+
65+
/* Capability discovery — works for the full 0x000-0x07F namespace
66+
* thanks to the 16-byte bitmap. */
67+
uint8_t caps[16];
68+
if (esp_hosted_open_get_caps(caps) == ESP_OK) {
69+
ESP_LOGI(TAG, "slave caps:");
70+
ESP_LOGI(TAG, " channel set: %d", esp_hosted_open_has_capability(PHY_RPC_REQ_SET_CHANNEL, caps));
71+
ESP_LOGI(TAG, " 802.11p: %d", esp_hosted_open_has_capability(PHY_RPC_REQ_SET_PHY_11P, caps));
72+
ESP_LOGI(TAG, " raw 802.11 TX: %d", esp_hosted_open_has_capability(PHY_RPC_REQ_TX_80211, caps));
73+
ESP_LOGI(TAG, " ESP-NOW: %d", esp_hosted_open_has_capability(PHY_RPC_REQ_ESPNOW_INIT, caps));
74+
ESP_LOGI(TAG, " 802.15.4: %d", esp_hosted_open_has_capability(PHY_RPC_REQ_IEEE154_ENABLE, caps));
75+
}
76+
77+
esp_hosted_open_info_t info;
78+
if (esp_hosted_open_get_info(&info) == ESP_OK) {
79+
ESP_LOGI(TAG, "slave info: ch=%u tx=%d dBm 11p=%d cca=%d fw=%s",
80+
info.channel, info.tx_power_dbm,
81+
info.phy_11p_armed, info.cca_enabled, info.fw_version);
82+
}
83+
84+
/* Tune to ITS CCH (5.900 GHz). NOT_SUPPORTED on chips without 5 GHz. */
85+
if (esp_hosted_open_has_capability(PHY_RPC_REQ_SET_CHANNEL, caps)) {
86+
esp_err_t err = esp_hosted_open_set_channel(180);
87+
ESP_LOGI(TAG, "set_channel(180): %s", esp_err_to_name(err));
88+
}
89+
90+
int8_t rssi;
91+
if (esp_hosted_open_get_phy_rssi(&rssi) == ESP_OK) {
92+
ESP_LOGI(TAG, "PHY RSSI = %d dBm", rssi);
93+
}
94+
95+
/* ESP-NOW broadcast — universal across every Wi-Fi-capable chip. */
96+
if (esp_hosted_open_has_capability(PHY_RPC_REQ_ESPNOW_INIT, caps)) {
97+
ESP_ERROR_CHECK(esp_hosted_open_espnow_init());
98+
static const uint8_t broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
99+
esp_hosted_open_espnow_add_peer(broadcast, NULL, 0, 0, false);
100+
const char *msg = "hello from p4";
101+
esp_err_t err = esp_hosted_open_espnow_send(broadcast,
102+
(const uint8_t *)msg,
103+
strlen(msg));
104+
ESP_LOGI(TAG, "espnow_send broadcast: %s", esp_err_to_name(err));
105+
}
106+
107+
/* Idle. SDIO transport drives everything via interrupts. */
108+
while (1) vTaskDelay(pdMS_TO_TICKS(60000));
109+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
CONFIG_IDF_TARGET="esp32p4"
2+
3+
# Hosted Wi-Fi (radio lives on a remote chip)
4+
CONFIG_ESP_HOSTED_ENABLED=y
5+
CONFIG_ESP_WIFI_ENABLED=y
6+
7+
# Peer-data RPC channel — required for esp_hosted_open custom RPCs
8+
CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER=y
9+
CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS=32
10+
11+
# === SDIO host transport ===
12+
# Explicitly select SDIO over the alternatives (SPI / SPI-HD / UART).
13+
CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y
14+
# CONFIG_ESP_HOSTED_SPI_HOST_INTERFACE is not set
15+
# CONFIG_ESP_HOSTED_SPI_HD_HOST_INTERFACE is not set
16+
# CONFIG_ESP_HOSTED_UART_HOST_INTERFACE is not set
17+
18+
CONFIG_ESP_HOSTED_SDIO_SLOT_0=y
19+
CONFIG_ESP_HOSTED_SDIO_4_BIT_BUS=y
20+
CONFIG_ESP_HOSTED_SDIO_CLOCK_FREQ_KHZ=40000
21+
22+
CONFIG_FREERTOS_HZ=1000
23+
CONFIG_LOG_DEFAULT_LEVEL_INFO=y

0 commit comments

Comments
 (0)