1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
/*
* The Kconfig surface ESP-Hosted's C compiles against in this project.
*
* Two parts, deliberately separated:
*
* sdkconfig_idf.h ESP-IDF v6.0.2's generated header, verbatim, from the build in
* 02-esp32p4-m3-radio that reached transport state "active" on this die.
* Unedited, so its provenance is checkable.
*
* this file that header, plus a short list of overrides. Each one states what it changes
* and why, so the delta from the proven configuration is reviewable rather than
* buried in 1,400 generated lines.
*
* The generated header is used rather than a hand-picked subset because ESP-Hosted's headers derive
* struct layouts and the slave target from these symbols: CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6 is
* what defines H_SLAVE_TARGET_ESP32C6, and CONFIG_ESP_HOSTED_USE_MEMPOOL is what decides the length
* of hosted_osi_funcs_t (see abi_assert.c). Hand-picking would be a second, unproven configuration.
*
* Nothing here starts a FreeRTOS kernel or an IDF component. The CONFIG_FREERTOS_* values only
* shape type declarations; src/net/port.zig and src/io/p4.zig supply the runtime.
*/
#pragma once
#include "sdkconfig_idf.h"
/* ------------------------------------------------------------------------------------------------
* Override 1: SDIO queue depths, 20 -> 4 each.
*
* IDF's build ran with 20 TX and 20 RX descriptors. That is a reasonable number when the heap is
* PSRAM-backed; here it is not. Forty in-flight buffers at MAX_SDIO_BUFFER_SIZE (1536 B) reserve
* ~60 KB before a single task stack exists, and this image has ~128 KB of L2MEM in total with
* nothing initialising the 32 MB of PSRAM.
*
* Four each was the first attempt and it was measured wrong. Once the board associated, the AP's
* ordinary broadcast traffic filled a four-deep queue immediately: the console filled with
* "task still writing Rx data to queue!", the receive counter froze at 8 frames, and the board
* stopped answering ARP - so it took a DHCP lease and then went silent, which looked like a bug in
* the IP stack rather than a queue two sizes too small.
*
* Sixteen each was then too many, for the reason that makes this setting awkward: with the mempool
* off (override 3) every frame is a fresh `_h_malloc_align(MAX_TRANSPORT_BUFFER_SIZE, 64)` from our
* heap, so the depths bound peak heap demand at (tx + rx) x 1536 bytes. At sixteen each that is
* ~49 KB of a 56 KB heap, and the board duly ran out: "mempool OOM start (RX)" at 11 s, then
* "STA TX: mempool_alloc failed, dropping pkt", after which nothing moved in either direction.
*
* Eight each: ~24.5 KB peak, against a heap sized well above it in examples/http.zig. Deep enough
* that ordinary broadcast traffic does not fill the queue between two `tick`s, shallow enough that a
* burst cannot exhaust the heap and stop the transmit path as collateral damage. That second
* property is the one worth protecting: a receive queue that overflows drops a frame, but a heap
* that empties takes the whole radio down.
* ---------------------------------------------------------------------------------------------- */
#undef CONFIG_ESP_HOSTED_SDIO_TX_Q_SIZE
#define CONFIG_ESP_HOSTED_SDIO_TX_Q_SIZE 8
#undef CONFIG_ESP_HOSTED_SDIO_RX_Q_SIZE
#define CONFIG_ESP_HOSTED_SDIO_RX_Q_SIZE 8
/* These two are aliases the transport reads; they must follow the values above rather than the
* originals, or the queues and the descriptors disagree about their own depth. */
#undef CONFIG_ESP_SDIO_TX_Q_SIZE
#define CONFIG_ESP_SDIO_TX_Q_SIZE CONFIG_ESP_HOSTED_SDIO_TX_Q_SIZE
#undef CONFIG_ESP_SDIO_RX_Q_SIZE
#define CONFIG_ESP_SDIO_RX_Q_SIZE CONFIG_ESP_HOSTED_SDIO_RX_Q_SIZE
/* ------------------------------------------------------------------------------------------------
* Override 2: Bluetooth off.
*
* The IDF build this configuration came from used BLE through the C6, so it enabled NimBLE and the
* VHCI transport. This project does not do Bluetooth, and leaving it on is not free: ESP-Hosted's
* transport calls hci_drv_init() unconditionally (transport_drv.c:126), and with NimBLE enabled that
* pulls in the real vhci_drv.c and the whole NimBLE host - ble_transport_*, os_mbuf_*,
* ble_hs_mbuf_to_flat - which is another stack this image has no reason to carry.
*
* With these off, ESP-Hosted's own host/drivers/bt/hci_stub_drv.c compiles to a no-op hci_drv_init
* and a drop-everything hci_rx_handler. That file is in the source list in build.zig, which is why
* this is a configuration change rather than a Zig stub: the C already ships the right answer for a
* host without Bluetooth, and using it keeps one fewer thing for us to get wrong.
*
* The C6 still reports HCI capability in its capability byte (0x0d on this board). That is the
* coprocessor saying what it can do, not a request; declining is the host's decision.
* ---------------------------------------------------------------------------------------------- */
#undef CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE
#undef CONFIG_ESP_HOSTED_NIMBLE_HCI_VHCI
#undef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
#undef CONFIG_ESP_HOSTED_BLUEDROID_HCI_VHCI
#undef CONFIG_BT_ENABLED
#undef CONFIG_BT_NIMBLE_ENABLED
/* ------------------------------------------------------------------------------------------------
* Override 3: mempool off.
*
* ESP-Hosted's mempool recycles fixed-size packet buffers instead of going to malloc each time. It
* needs a backend, supplied by `os_mempool_get_ops()`, and on ESP-IDF that comes from FreeRTOS's own
* pool implementation. This image has no FreeRTOS, and mempool.c treats a null ops table as a hard
* failure rather than a fallback (mempool.c:63-65, "hosted mempool init failed: no mempool ops") -
* which is what the second run of examples/radio.zig printed.
*
* The choice is to write a pool backend or to switch the optimisation off. Off, for now: the
* allocator behind _h_malloc (src/net/heap.zig) is a coalescing free list over a static buffer, so
* the same-size churn mempool exists to avoid is already cheap and cannot fragment the way a
* general-purpose heap would. If profiling later says otherwise, the backend is a small job and this
* is the one line to flip back.
*
* Layout note, because this looks dangerous and is not: turning this off does NOT change
* hosted_osi_funcs_t. port_esp_hosted_host_config.h:127-131 defines H_USE_MEMPOOL to 1 or to 0, and
* the struct's four mempool members are guarded by `#ifdef`, which only asks whether the name is
* defined. Both ways the struct is 284 bytes. src/net/hosted/abi_assert.c asserts that directly.
* ---------------------------------------------------------------------------------------------- */
#undef CONFIG_ESP_HOSTED_USE_MEMPOOL
#define CONFIG_ESP_HOSTED_USE_MEMPOOL 0
/* ------------------------------------------------------------------------------------------------
* Override 4: the ESP-Hosted CLI off.
*
* A console command set for poking the transport at runtime. It needs IDF's `console` component -
* esp_console_cmd_register, a line editor, and a UART driver - none of which exists in this image,
* and none of which this project wants: the serial line here is a log, not a shell.
*
* `H_ESP_HOSTED_CLI_ENABLED` is an `#ifdef` on the *value* of this symbol being defined
* (transport_drv.c:804), so it must be #undef'd rather than defined to 0.
* ---------------------------------------------------------------------------------------------- */
#undef CONFIG_ESP_HOSTED_CLI_ENABLED
/* ------------------------------------------------------------------------------------------------
* Override 5: compile DEBUG-level logging in.
*
* The generated configuration stops at CONFIG_LOG_MAXIMUM_LEVEL 3 (INFO), which compiles ESP_LOGD
* away entirely. That hides exactly the lines needed to tell a stalled receive path apart from a
* silent slave: sdio_drv.c:1190 logs "--- Wait for SDIO intr ---" at DEBUG on every pass of
* sdio_read_task, so its presence or absence answers "is the read task still looping?" directly.
*
* 4, not 5: VERBOSE adds a per-interrupt line that floods a 115200 baud console and changes the
* timing of the thing being measured.
*
* The runtime filter in src/net/hosted_glue.zig is separate and independent - this only decides what
* exists in the image to be filtered.
* ---------------------------------------------------------------------------------------------- */
#undef CONFIG_LOG_MAXIMUM_LEVEL
#define CONFIG_LOG_MAXIMUM_LEVEL 4
|