@typecad/framework-zephyr 1.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -0
  3. package/dist/async/timer-polyfill.d.ts +10 -0
  4. package/dist/async/timer-polyfill.js +95 -0
  5. package/dist/chips/controllers.d.ts +21 -0
  6. package/dist/chips/controllers.js +66 -0
  7. package/dist/chips/esp32.d.ts +2 -0
  8. package/dist/chips/esp32.js +59 -0
  9. package/dist/chips/esp32s3.d.ts +2 -0
  10. package/dist/chips/esp32s3.js +54 -0
  11. package/dist/chips/index.d.ts +15 -0
  12. package/dist/chips/index.js +45 -0
  13. package/dist/chips/resolve.d.ts +9 -0
  14. package/dist/chips/resolve.js +121 -0
  15. package/dist/chips/types.d.ts +154 -0
  16. package/dist/chips/types.js +14 -0
  17. package/dist/chips/xiao-ble.d.ts +2 -0
  18. package/dist/chips/xiao-ble.js +61 -0
  19. package/dist/debug-codegen.d.ts +33 -0
  20. package/dist/debug-codegen.js +162 -0
  21. package/dist/display/gfx.d.ts +17 -0
  22. package/dist/display/gfx.js +171 -0
  23. package/dist/display/index.d.ts +20 -0
  24. package/dist/display/index.js +53 -0
  25. package/dist/display/profiles.d.ts +25 -0
  26. package/dist/display/profiles.js +41 -0
  27. package/dist/display/touch-adapter.d.ts +9 -0
  28. package/dist/display/touch-adapter.js +95 -0
  29. package/dist/display/ui-adapter.d.ts +21 -0
  30. package/dist/display/ui-adapter.js +534 -0
  31. package/dist/doctor.d.ts +6 -0
  32. package/dist/doctor.js +48 -0
  33. package/dist/dt-config/kconfig.d.ts +27 -0
  34. package/dist/dt-config/kconfig.js +283 -0
  35. package/dist/dt-config/overlay.d.ts +35 -0
  36. package/dist/dt-config/overlay.js +246 -0
  37. package/dist/framework.manifest.d.ts +101 -0
  38. package/dist/framework.manifest.js +536 -0
  39. package/dist/index.d.ts +6 -0
  40. package/dist/index.js +17 -0
  41. package/dist/lowering/adc.d.ts +19 -0
  42. package/dist/lowering/adc.js +76 -0
  43. package/dist/lowering/ble.d.ts +20 -0
  44. package/dist/lowering/ble.js +419 -0
  45. package/dist/lowering/board.d.ts +9 -0
  46. package/dist/lowering/board.js +33 -0
  47. package/dist/lowering/gpio.d.ts +12 -0
  48. package/dist/lowering/gpio.js +123 -0
  49. package/dist/lowering/http.d.ts +23 -0
  50. package/dist/lowering/http.js +482 -0
  51. package/dist/lowering/i2c.d.ts +15 -0
  52. package/dist/lowering/i2c.js +114 -0
  53. package/dist/lowering/index.d.ts +29 -0
  54. package/dist/lowering/index.js +93 -0
  55. package/dist/lowering/interrupts.d.ts +21 -0
  56. package/dist/lowering/interrupts.js +91 -0
  57. package/dist/lowering/mqtt.d.ts +19 -0
  58. package/dist/lowering/mqtt.js +366 -0
  59. package/dist/lowering/power.d.ts +9 -0
  60. package/dist/lowering/power.js +64 -0
  61. package/dist/lowering/preferences.d.ts +23 -0
  62. package/dist/lowering/preferences.js +345 -0
  63. package/dist/lowering/pulse.d.ts +7 -0
  64. package/dist/lowering/pulse.js +82 -0
  65. package/dist/lowering/pwm.d.ts +15 -0
  66. package/dist/lowering/pwm.js +59 -0
  67. package/dist/lowering/random.d.ts +18 -0
  68. package/dist/lowering/random.js +129 -0
  69. package/dist/lowering/spi.d.ts +17 -0
  70. package/dist/lowering/spi.js +113 -0
  71. package/dist/lowering/timing.d.ts +9 -0
  72. package/dist/lowering/timing.js +63 -0
  73. package/dist/lowering/tone.d.ts +10 -0
  74. package/dist/lowering/tone.js +50 -0
  75. package/dist/lowering/uart.d.ts +15 -0
  76. package/dist/lowering/uart.js +102 -0
  77. package/dist/lowering/util.d.ts +4 -0
  78. package/dist/lowering/util.js +12 -0
  79. package/dist/lowering/wdt.d.ts +15 -0
  80. package/dist/lowering/wdt.js +80 -0
  81. package/dist/lowering/wifi.d.ts +21 -0
  82. package/dist/lowering/wifi.js +447 -0
  83. package/dist/lowering/worker-backing.d.ts +14 -0
  84. package/dist/lowering/worker-backing.js +79 -0
  85. package/dist/lowering/worker.d.ts +6 -0
  86. package/dist/lowering/worker.js +14 -0
  87. package/dist/strategy.d.ts +168 -0
  88. package/dist/strategy.js +1094 -0
  89. package/dist/toolchain/compat.d.ts +38 -0
  90. package/dist/toolchain/compat.js +133 -0
  91. package/dist/toolchain/debug-config.d.ts +82 -0
  92. package/dist/toolchain/debug-config.js +359 -0
  93. package/dist/toolchain/index.d.ts +73 -0
  94. package/dist/toolchain/index.js +444 -0
  95. package/dist/toolchain/scaffold.d.ts +15 -0
  96. package/dist/toolchain/scaffold.js +196 -0
  97. package/dist/toolchain/west-discover.d.ts +40 -0
  98. package/dist/toolchain/west-discover.js +203 -0
  99. package/dist/toolchain/west-spawn.d.ts +39 -0
  100. package/dist/toolchain/west-spawn.js +117 -0
  101. package/package.json +82 -0
  102. package/src/async/timer-polyfill.ts +107 -0
  103. package/src/chips/controllers.ts +74 -0
  104. package/src/chips/esp32.ts +62 -0
  105. package/src/chips/esp32s3.ts +57 -0
  106. package/src/chips/index.ts +54 -0
  107. package/src/chips/resolve.ts +146 -0
  108. package/src/chips/types.ts +163 -0
  109. package/src/chips/xiao-ble.ts +64 -0
  110. package/src/debug-codegen.ts +207 -0
  111. package/src/display/gfx.ts +190 -0
  112. package/src/display/index.ts +70 -0
  113. package/src/display/profiles.ts +63 -0
  114. package/src/display/touch-adapter.ts +100 -0
  115. package/src/display/ui-adapter.ts +558 -0
  116. package/src/doctor.ts +56 -0
  117. package/src/dt-config/kconfig.ts +308 -0
  118. package/src/dt-config/overlay.ts +293 -0
  119. package/src/framework.manifest.ts +558 -0
  120. package/src/index.ts +29 -0
  121. package/src/lowering/adc.ts +102 -0
  122. package/src/lowering/ble.ts +426 -0
  123. package/src/lowering/board.ts +38 -0
  124. package/src/lowering/gpio.ts +148 -0
  125. package/src/lowering/http.ts +488 -0
  126. package/src/lowering/i2c.ts +126 -0
  127. package/src/lowering/index.ts +86 -0
  128. package/src/lowering/interrupts.ts +114 -0
  129. package/src/lowering/mqtt.ts +371 -0
  130. package/src/lowering/power.ts +70 -0
  131. package/src/lowering/preferences.ts +354 -0
  132. package/src/lowering/pulse.ts +93 -0
  133. package/src/lowering/pwm.ts +74 -0
  134. package/src/lowering/random.ts +135 -0
  135. package/src/lowering/spi.ts +124 -0
  136. package/src/lowering/timing.ts +72 -0
  137. package/src/lowering/tone.ts +60 -0
  138. package/src/lowering/uart.ts +111 -0
  139. package/src/lowering/util.ts +12 -0
  140. package/src/lowering/wdt.ts +84 -0
  141. package/src/lowering/wifi.ts +452 -0
  142. package/src/lowering/worker-backing.ts +94 -0
  143. package/src/lowering/worker.ts +17 -0
  144. package/src/strategy.ts +1265 -0
  145. package/src/toolchain/compat.ts +145 -0
  146. package/src/toolchain/debug-config.ts +399 -0
  147. package/src/toolchain/index.ts +482 -0
  148. package/src/toolchain/scaffold.ts +196 -0
  149. package/src/toolchain/west-discover.ts +233 -0
  150. package/src/toolchain/west-spawn.ts +141 -0
@@ -0,0 +1,447 @@
1
+ // ---------------------------------------------------------------------------
2
+ // WiFi lowering — net_mgmt (connect/scan/radio) + conn_mgr monitor (L4 events)
3
+ //
4
+ // Hybrid: the WiFi connect/disconnect/scan/radio-query requests go directly
5
+ // through net_mgmt + wifi_mgmt.h (NET_REQUEST_WIFI_CONNECT/DISCONNECT/SCAN/...),
6
+ // taking the iface admin-up with net_if_up first. IP-level connectivity is
7
+ // still signaled by conn_mgr's monitoring layer (conn_mgr_monitor.c raises
8
+ // NET_EVENT_L4_CONNECTED/DISCONNECTED post-DHCP), so CONFIG_NET_CONNECTION_MANAGER
9
+ // + the monitor headers remain load-bearing even though conn_mgr_if_connect is
10
+ // not used. Two net_mgmt event handlers feed a single __tc_wifi state struct
11
+ // that the poll ops read.
12
+ //
13
+ // EMIT BOUNDARY: emitted bytes land in user firmware. Covered by the TypeCAD
14
+ // Runtime Exception (RUNTIME_EXCEPTION.md at the repo root).
15
+ // ---------------------------------------------------------------------------
16
+ /** Render a HAL field: pass through (already rendered by the resolver). */
17
+ function s(v) {
18
+ return String(v);
19
+ }
20
+ /**
21
+ * The WiFi runtime shim. All helpers are `static` so unused ones don't trip
22
+ * -Wunused-function in the single generated TU (mirrors ble.ts).
23
+ *
24
+ * State is driven by two net_mgmt event handlers:
25
+ * - L4 connectivity (NET_EVENT_L4_CONNECTED/DISCONNECTED) → connected flag.
26
+ * This reflects IP connectivity (post-DHCP), not just the WiFi link — the
27
+ * events are raised by conn_mgr's monitoring layer (conn_mgr_monitor.c).
28
+ * - WiFi scan (NET_EVENT_WIFI_SCAN_RESULT/SCAN_DONE) → scan_results pool.
29
+ */
30
+ export function wifiInitLines() {
31
+ return [
32
+ `// CUTTLEFISH_WIFI_BEGIN`,
33
+ `#define __TC_WIFI_MAX_SCAN 16`,
34
+ ``,
35
+ `// ── core (always needed when wifi.* is used) ────────────────────────────`,
36
+ `// CUTTLEFISH_WIFI_CORE_BEGIN`,
37
+ `// WiFi event callback signature (wifi.on_event). One slot per supported`,
38
+ `// event — a second registration overwrites the first (matches framework-esp32).`,
39
+ `typedef void (*__tc_wifi_cb_t)(void);`,
40
+ ``,
41
+ `// WiFi state. connected reflects IP connectivity (L4), not just link —`,
42
+ `// connect takes the iface admin-up (net_if_up) and conn_mgr_monitor raises`,
43
+ `// the L4 event once DHCP completes. is_connected polls this flag.`,
44
+ `static struct {`,
45
+ ` bool inited; // net_mgmt handlers registered + iface resolved`,
46
+ ` bool connected; // set by NET_EVENT_L4_CONNECTED/DISCONNECTED`,
47
+ ` bool scanning; // scan kicked, awaiting NET_EVENT_WIFI_SCAN_DONE`,
48
+ ` int32_t scan_count; // filled by SCAN_DONE`,
49
+ ` struct wifi_scan_result scan_results[__TC_WIFI_MAX_SCAN];`,
50
+ ` char ssid_buf[__TC_WIFI_MAX_SCAN][33]; // SSIDs ≤32 chars + NUL`,
51
+ ` uint8_t mac[6]; // filled lazily by wifi.mac`,
52
+ ` struct net_if* iface; // resolved WiFi iface`,
53
+ ` __tc_wifi_cb_t on_disconnect; // fired by NET_EVENT_L4_DISCONNECTED`,
54
+ ` __tc_wifi_cb_t on_connect; // fired by NET_EVENT_IPV4_ADDR_ADD (DHCP)`,
55
+ ` struct k_work disconnect_work; // deferred on_disconnect (k_work_submit)`,
56
+ `} __tc_wifi = { false, false, false, 0, {}, {}, {}, nullptr, nullptr, nullptr, {} };`,
57
+ ``,
58
+ `// ── net_mgmt event handler ──────────────────────────────────────────────`,
59
+ `// One callback for both event families. L4 events drive \`connected\`; WiFi`,
60
+ `// scan events fill the scan pool. Registered once at first use.`,
61
+ `static void __tc_wifi_disconnect_work_handler(struct k_work* w) {`,
62
+ ` (void)w;`,
63
+ ` if (__tc_wifi.on_disconnect != nullptr) __tc_wifi.on_disconnect();`,
64
+ `}`,
65
+ ``,
66
+ `static void __tc_wifi_mgmt_cb(struct net_mgmt_event_callback* cbb, uint64_t mgmt_event,`,
67
+ ` struct net_if* iface) {`,
68
+ ` (void)cbb;`,
69
+ ` (void)iface;`,
70
+ ` if (mgmt_event == NET_EVENT_L4_CONNECTED) {`,
71
+ ` __tc_wifi.connected = true;`,
72
+ ` } else if (mgmt_event == NET_EVENT_L4_DISCONNECTED) {`,
73
+ ` __tc_wifi.connected = false;`,
74
+ ` // Defer on_disconnect via k_work_submit so the user callback (which`,
75
+ ` // typically calls connectAsync) runs outside the net_mgmt event`,
76
+ ` // chain. Calling NET_REQUEST_WIFI_CONNECT from within the disconnect`,
77
+ ` // callback causes re-entrancy that prevents re-association.`,
78
+ ` if (__tc_wifi.on_disconnect != nullptr) {`,
79
+ ` (void)k_work_submit(&__tc_wifi.disconnect_work);`,
80
+ ` }`,
81
+ ` } else if (mgmt_event == NET_EVENT_IPV4_ADDR_ADD) {`,
82
+ ` // DHCP assigned an IPv4 address — the true "got IP" signal. Fires`,
83
+ ` // after NET_EVENT_L4_CONNECTED on a successful DHCP join.`,
84
+ ` if (__tc_wifi.on_connect != nullptr) __tc_wifi.on_connect();`,
85
+ ` } else if (mgmt_event == NET_EVENT_WIFI_SCAN_RESULT) {`,
86
+ ` // Per-result during a scan. Bounds-check against the pool cap.`,
87
+ ` const struct wifi_scan_result* res = static_cast<const struct wifi_scan_result*>(cbb->info);`,
88
+ ` if (res != nullptr && __tc_wifi.scan_count < __TC_WIFI_MAX_SCAN) {`,
89
+ ` int32_t i = __tc_wifi.scan_count;`,
90
+ ` __tc_wifi.scan_results[i] = *res;`,
91
+ ` // Copy the SSID into the flat ssid_buf (≤32 chars + NUL).`,
92
+ ` uint32_t len = (res->ssid_length < 32U) ? res->ssid_length : 32U;`,
93
+ ` for (uint32_t b = 0; b < len; b++) { __tc_wifi.ssid_buf[i][b] = static_cast<char>(res->ssid[b]); }`,
94
+ ` __tc_wifi.ssid_buf[i][len] = '\\0';`,
95
+ ` __tc_wifi.scan_count = i + 1;`,
96
+ ` }`,
97
+ ` } else if (mgmt_event == NET_EVENT_WIFI_SCAN_DONE) {`,
98
+ ` __tc_wifi.scanning = false;`,
99
+ ` }`,
100
+ `}`,
101
+ ``,
102
+ `static struct net_mgmt_event_callback __tc_wifi_l4_cb;`,
103
+ `static struct net_mgmt_event_callback __tc_wifi_ipv4_cb;`,
104
+ `static struct net_mgmt_event_callback __tc_wifi_scan_cb;`,
105
+ ``,
106
+ `// Register the handlers + resolve the WiFi iface. Idempotent (inited guard).`,
107
+ `// Three callback structs: L4 (connect/disconnect), IPv4 (got-IP / DHCP), scan.`,
108
+ `static void __tc_wifi_ensure_init(void) {`,
109
+ ` if (__tc_wifi.inited) return;`,
110
+ ` k_work_init(&__tc_wifi.disconnect_work, __tc_wifi_disconnect_work_handler);`,
111
+ ` __tc_wifi.iface = net_if_get_default();`,
112
+ ` if (__tc_wifi.iface != nullptr) {`,
113
+ ` net_mgmt_init_event_callback(&__tc_wifi_l4_cb, __tc_wifi_mgmt_cb,`,
114
+ ` NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED);`,
115
+ ` net_mgmt_add_event_callback(&__tc_wifi_l4_cb);`,
116
+ ` net_mgmt_init_event_callback(&__tc_wifi_ipv4_cb, __tc_wifi_mgmt_cb,`,
117
+ ` NET_EVENT_IPV4_ADDR_ADD);`,
118
+ ` net_mgmt_add_event_callback(&__tc_wifi_ipv4_cb);`,
119
+ ` net_mgmt_init_event_callback(&__tc_wifi_scan_cb, __tc_wifi_mgmt_cb,`,
120
+ ` NET_EVENT_WIFI_SCAN_RESULT | NET_EVENT_WIFI_SCAN_DONE);`,
121
+ ` net_mgmt_add_event_callback(&__tc_wifi_scan_cb);`,
122
+ ` }`,
123
+ ` __tc_wifi.inited = true;`,
124
+ `}`,
125
+ `// CUTTLEFISH_WIFI_CORE_END`,
126
+ ``,
127
+ `// ── connect / disconnect (net_mgmt — conn_mgr monitor supplies L4 events) ─`,
128
+ `// CUTTLEFISH_WIFI_CONNECT_BEGIN`,
129
+ `// SSID/PSK are staged into static buffers (wifi_connect_req_params.ssid/psk`,
130
+ `// are const uint8_t* — they point at caller-owned storage that must outlive`,
131
+ `// the request), then net_if_up takes the iface admin-up and NET_REQUEST_WIFI_CONNECT`,
132
+ `// is issued. conn_mgr_monitor raises the L4 event once DHCP completes.`,
133
+ `static struct wifi_connect_req_params __tc_wifi_conn_params;`,
134
+ `static uint8_t __tc_wifi_ssid_buf[33]; // SSID ≤32 + slack`,
135
+ `static uint8_t __tc_wifi_psk_buf[64]; // PSK ≤63 + slack`,
136
+ ``,
137
+ `static void __tc_wifi_stage_creds(const char* ssid, const char* password) {`,
138
+ ` __tc_wifi_ensure_init();`,
139
+ ` if (__tc_wifi.iface == nullptr) return;`,
140
+ ` uint32_t ssid_len = strlen(ssid);`,
141
+ ` if (ssid_len > 32U) ssid_len = 32U;`,
142
+ ` for (uint32_t b = 0; b < ssid_len; b++) { __tc_wifi_ssid_buf[b] = static_cast<uint8_t>(ssid[b]); }`,
143
+ ` __tc_wifi_conn_params.ssid = __tc_wifi_ssid_buf;`,
144
+ ` __tc_wifi_conn_params.ssid_length = static_cast<uint8_t>(ssid_len);`,
145
+ ` if (password != nullptr) {`,
146
+ ` uint32_t pw_len = strlen(password);`,
147
+ ` if (pw_len > 63U) pw_len = 63U;`,
148
+ ` for (uint32_t b = 0; b < pw_len; b++) { __tc_wifi_psk_buf[b] = static_cast<uint8_t>(password[b]); }`,
149
+ ` __tc_wifi_conn_params.psk = __tc_wifi_psk_buf;`,
150
+ ` __tc_wifi_conn_params.psk_length = static_cast<uint8_t>(pw_len);`,
151
+ ` } else {`,
152
+ ` __tc_wifi_conn_params.psk_length = 0U;`,
153
+ ` }`,
154
+ ` __tc_wifi_conn_params.security = WIFI_SECURITY_TYPE_PSK;`,
155
+ ` __tc_wifi_conn_params.channel = WIFI_CHANNEL_ANY;`,
156
+ ` __tc_wifi_conn_params.band = WIFI_FREQ_BAND_2_4_GHZ;`,
157
+ ` // Stage the connection request (wifi_connect). The L4 handler sets`,
158
+ ` // \`connected\` once DHCP completes.`,
159
+ ` (void)net_mgmt(NET_REQUEST_WIFI_CONNECT, __tc_wifi.iface,`,
160
+ ` &__tc_wifi_conn_params, sizeof(__tc_wifi_conn_params));`,
161
+ `}`,
162
+ ``,
163
+ `static void __tc_wifi_connect_start(const char* ssid, const char* password) {`,
164
+ ` __tc_wifi_ensure_init();`,
165
+ ` if (__tc_wifi.iface == nullptr) {`,
166
+ ` printk("tc-wifi: no WiFi iface found\\n");`,
167
+ ` return;`,
168
+ ` }`,
169
+ ` net_if_up(__tc_wifi.iface);`,
170
+ ` __tc_wifi_stage_creds(ssid, password);`,
171
+ ` __tc_wifi.connected = false;`,
172
+ ` printk("tc-wifi: connecting to %s\\n", ssid);`,
173
+ `}`,
174
+ ``,
175
+ `// CUTTLEFISH_WIFI_CONNECT_BLOCKING_BEGIN`,
176
+ `static void __tc_wifi_connect(const char* ssid, const char* password, int32_t timeout_ms) {`,
177
+ ` __tc_wifi_connect_start(ssid, password);`,
178
+ ` // Block until the L4 handler signals connectivity or the deadline passes.`,
179
+ ` int32_t waited = 0;`,
180
+ ` while (!__tc_wifi.connected && waited < timeout_ms) { k_msleep(100); waited += 100; }`,
181
+ ` printk("tc-wifi: connect %s after %dms\\n", __tc_wifi.connected ? "ok" : "timeout", waited);`,
182
+ `}`,
183
+ `// CUTTLEFISH_WIFI_CONNECT_BLOCKING_END`,
184
+ ``,
185
+ `static void __tc_wifi_disconnect(void) {`,
186
+ ` __tc_wifi_ensure_init();`,
187
+ ` __tc_wifi.connected = false;`,
188
+ ` if (__tc_wifi.iface != nullptr) {`,
189
+ ` (void)net_mgmt(NET_REQUEST_WIFI_DISCONNECT, __tc_wifi.iface, nullptr, 0);`,
190
+ ` }`,
191
+ `}`,
192
+ `// CUTTLEFISH_WIFI_CONNECT_END`,
193
+ ``,
194
+ `// ── status / radio queries ───────────────────────────────────────────────`,
195
+ `// CUTTLEFISH_WIFI_QUERY_BEGIN`,
196
+ `static int32_t __tc_wifi_status(void) {`,
197
+ ` return __tc_wifi.connected ? 3 : 0; // 3=Connected in the HAL status enum`,
198
+ `}`,
199
+ ``,
200
+ `static const char* __tc_wifi_local_ip(void) {`,
201
+ ` __tc_wifi_ensure_init();`,
202
+ ` if (__tc_wifi.iface == nullptr || !__tc_wifi.connected) return "0.0.0.0";`,
203
+ ` if (__tc_wifi.iface->config.ip.ipv4 == nullptr) return "0.0.0.0";`,
204
+ ` static char ipbuf[16]; // "255.255.255.255" + NUL`,
205
+ ` struct in_addr* addr = &__tc_wifi.iface->config.ip.ipv4->unicast[0].ipv4.address.in_addr;`,
206
+ ` (void)net_addr_ntop(AF_INET, addr, ipbuf, sizeof(ipbuf));`,
207
+ ` return ipbuf;`,
208
+ `}`,
209
+ ``,
210
+ `static int32_t __tc_wifi_rssi(void) {`,
211
+ ` __tc_wifi_ensure_init();`,
212
+ ` struct wifi_iface_status status = { 0 };`,
213
+ ` // RSSI is a field on the iface-status struct, fetched via the status`,
214
+ ` // request (there is no standalone NET_REQUEST_WIFI_RSSI constant).`,
215
+ ` int32_t ret = net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, __tc_wifi.iface,`,
216
+ ` &status, sizeof(status));`,
217
+ ` if (ret != 0) return 0;`,
218
+ ` return static_cast<int32_t>(status.rssi);`,
219
+ `}`,
220
+ ``,
221
+ `static uint64_t __tc_wifi_mac(void) {`,
222
+ ` __tc_wifi_ensure_init();`,
223
+ ` struct wifi_iface_status status = { 0 };`,
224
+ ` (void)net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, __tc_wifi.iface,`,
225
+ ` &status, sizeof(status));`,
226
+ ` // Pack the 6 MAC bytes (status.bssid) into a uint64 (HAL returns a number).`,
227
+ ` uint64_t m = 0;`,
228
+ ` for (int32_t b = 0; b < 6; b++) { m = (m << 8) | static_cast<uint64_t>(status.bssid[b]); }`,
229
+ ` return m;`,
230
+ `}`,
231
+ `// CUTTLEFISH_WIFI_QUERY_END`,
232
+ ``,
233
+ `// ── scan (net_mgmt — conn_mgr has no scan surface) ───────────────────────`,
234
+ `// CUTTLEFISH_WIFI_SCAN_BEGIN`,
235
+ `static void __tc_wifi_scan_start(void) {`,
236
+ ` __tc_wifi_ensure_init();`,
237
+ ` __tc_wifi.scanning = true;`,
238
+ ` __tc_wifi.scan_count = 0;`,
239
+ ` (void)net_mgmt(NET_REQUEST_WIFI_SCAN, __tc_wifi.iface, nullptr, 0);`,
240
+ `}`,
241
+ ``,
242
+ `static void __tc_wifi_scan_blocking(void) {`,
243
+ ` __tc_wifi_scan_start();`,
244
+ ` while (__tc_wifi.scanning) { k_msleep(100); }`,
245
+ `}`,
246
+ ``,
247
+ `static const char* __tc_wifi_scan_ssid(int32_t i) {`,
248
+ ` if (i < 0 || i >= __tc_wifi.scan_count || i >= __TC_WIFI_MAX_SCAN) return "";`,
249
+ ` return __tc_wifi.ssid_buf[i];`,
250
+ `}`,
251
+ `static int32_t __tc_wifi_scan_rssi(int32_t i) {`,
252
+ ` if (i < 0 || i >= __tc_wifi.scan_count || i >= __TC_WIFI_MAX_SCAN) return 0;`,
253
+ ` return static_cast<int32_t>(__tc_wifi.scan_results[i].rssi);`,
254
+ `}`,
255
+ `static const char* __tc_wifi_scan_enc(int32_t i) {`,
256
+ ` if (i < 0 || i >= __tc_wifi.scan_count || i >= __TC_WIFI_MAX_SCAN) return "open";`,
257
+ ` switch (__tc_wifi.scan_results[i].security) {`,
258
+ ` case WIFI_SECURITY_TYPE_PSK: return "wpa";`,
259
+ ` case WIFI_SECURITY_TYPE_PSK_SHA256: return "wpa2";`,
260
+ ` case WIFI_SECURITY_TYPE_SAE: return "wpa3";`,
261
+ ` default: return "open";`,
262
+ ` }`,
263
+ `}`,
264
+ `static int32_t __tc_wifi_scan_channel(int32_t i) {`,
265
+ ` if (i < 0 || i >= __tc_wifi.scan_count || i >= __TC_WIFI_MAX_SCAN) return 0;`,
266
+ ` return static_cast<int32_t>(__tc_wifi.scan_results[i].channel);`,
267
+ `}`,
268
+ `// CUTTLEFISH_WIFI_SCAN_END`,
269
+ ``,
270
+ `// ── config ───────────────────────────────────────────────────────────────`,
271
+ `// CUTTLEFISH_WIFI_CONFIG_BEGIN`,
272
+ `// NOTE: wifi.set_tx_power is intentionally NOT lowered here. The Zephyr esp32`,
273
+ `// WiFi driver owns esp_wifi_start/esp_wifi_connect, and calling`,
274
+ `// esp_wifi_set_max_tx_power from this shim fights the driver for control of`,
275
+ `// the radio (brownout). The compile-time PHY ceiling (ESP_PHY_MAX_WIFI_TX_POWER`,
276
+ `// in ESP-IDF's components/esp_phy/Kconfig) is NOT sourced into Zephyr's Kconfig`,
277
+ `// tree, so it cannot be lowered via prj.conf either (zephyr#45580). TX power`,
278
+ `// stays at its default until a Zephyr-native surface exists.`,
279
+ `static void __tc_wifi_set_hostname(const char* name) {`,
280
+ ` // Best-effort: Zephyr has no net_if_set_hostname; the system hostname`,
281
+ ` // comes from the hostname subsystem (net_hostname.h) + CONFIG_NET_HOSTNAME.`,
282
+ ` // The interface name is set via net_if_set_name, which is not the same as`,
283
+ ` // a DHCP hostname. No-op until the hostname subsystem is wired.`,
284
+ ` (void)name;`,
285
+ ` (void)__tc_wifi;`,
286
+ `}`,
287
+ ``,
288
+ `// ── power save (NET_REQUEST_WIFI_PS_CONFIG) ─────────────────────────────`,
289
+ `// wifi.set_power_save: HAL modes are "default" (enabled) / "none" (disabled).`,
290
+ `// The Zephyr esp32 driver implements esp32_wifi_set_power_save, so this is a`,
291
+ `// real net_mgmt request — not a no-op like set_hostname above.`,
292
+ `static void __tc_wifi_set_power_save(const char* mode) {`,
293
+ ` __tc_wifi_ensure_init();`,
294
+ ` if (__tc_wifi.iface == nullptr) return;`,
295
+ ` struct wifi_ps_params ps = { 0 };`,
296
+ ` ps.type = WIFI_PS_PARAM_TYPE_ENABLED;`,
297
+ ` // HAL mode "default" → power save on; "none" → off. (The full HAL surface`,
298
+ ` // only exposes those two; deeper ps_mode/wakeup tuning isn't exposed.)`,
299
+ ` bool on = (mode != nullptr && strcmp(mode, "none") != 0);`,
300
+ ` ps.enabled = on ? WIFI_PS_ENABLED : WIFI_PS_DISABLED;`,
301
+ ` (void)net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, __tc_wifi.iface,`,
302
+ ` &ps, sizeof(ps));`,
303
+ `}`,
304
+ ``,
305
+ `// ── waits (block on the L4 connectivity flag) ───────────────────────────`,
306
+ `// wifi.wait_connected / wait_disconnected: poll __tc_wifi.connected (set by`,
307
+ `// the L4 net_mgmt handler) with a deadline. Same blocking pattern as`,
308
+ `// __tc_wifi_connect.`,
309
+ `static void __tc_wifi_wait_connected(int32_t timeout_ms) {`,
310
+ ` __tc_wifi_ensure_init();`,
311
+ ` int32_t waited = 0;`,
312
+ ` while (!__tc_wifi.connected && waited < timeout_ms) { k_msleep(100); waited += 100; }`,
313
+ `}`,
314
+ ``,
315
+ `static void __tc_wifi_wait_disconnected(void) {`,
316
+ ` __tc_wifi_ensure_init();`,
317
+ ` // No timeout in the HAL surface — block until the L4 handler clears the flag.`,
318
+ ` while (__tc_wifi.connected) { k_msleep(100); }`,
319
+ `}`,
320
+ ``,
321
+ `// ── AP mode (NET_REQUEST_WIFI_AP_ENABLE / DISABLE) ──────────────────────`,
322
+ `// wifi.ap_start / ap_stop: bring the iface up as an AP. The esp32 driver's`,
323
+ `// ap_enable takes the same struct as connect (ssid/psk/channel/security).`,
324
+ `// hidden/maxClients/channel-after-start have no driver hook (see manifest) —`,
325
+ `// only ssid/password/channel at start are honored.`,
326
+ `static struct wifi_connect_req_params __tc_wifi_ap_params;`,
327
+ `static uint8_t __tc_wifi_ap_ssid_buf[33];`,
328
+ `static uint8_t __tc_wifi_ap_psk_buf[64];`,
329
+ `static void __tc_wifi_ap_start(const char* ssid, const char* password,`,
330
+ ` int32_t channel) {`,
331
+ ` __tc_wifi_ensure_init();`,
332
+ ` if (__tc_wifi.iface == nullptr) return;`,
333
+ ` net_if_up(__tc_wifi.iface);`,
334
+ ` uint32_t ssid_len = strlen(ssid);`,
335
+ ` if (ssid_len > 32U) ssid_len = 32U;`,
336
+ ` for (uint32_t b = 0U; b < ssid_len; b++) { __tc_wifi_ap_ssid_buf[b] = static_cast<uint8_t>(ssid[b]); }`,
337
+ ` __tc_wifi_ap_params.ssid = __tc_wifi_ap_ssid_buf;`,
338
+ ` __tc_wifi_ap_params.ssid_length = static_cast<uint8_t>(ssid_len);`,
339
+ ` if (password != nullptr) {`,
340
+ ` uint32_t pw_len = strlen(password);`,
341
+ ` if (pw_len > 63U) pw_len = 63U;`,
342
+ ` for (uint32_t b = 0U; b < pw_len; b++) { __tc_wifi_ap_psk_buf[b] = static_cast<uint8_t>(password[b]); }`,
343
+ ` __tc_wifi_ap_params.psk = __tc_wifi_ap_psk_buf;`,
344
+ ` __tc_wifi_ap_params.psk_length = static_cast<uint8_t>(pw_len);`,
345
+ ` __tc_wifi_ap_params.security = WIFI_SECURITY_TYPE_PSK;`,
346
+ ` } else {`,
347
+ ` __tc_wifi_ap_params.psk_length = 0U;`,
348
+ ` __tc_wifi_ap_params.security = WIFI_SECURITY_TYPE_NONE;`,
349
+ ` }`,
350
+ ` __tc_wifi_ap_params.channel = (channel > 0) ? static_cast<uint8_t>(channel) : WIFI_CHANNEL_ANY;`,
351
+ ` __tc_wifi_ap_params.band = WIFI_FREQ_BAND_2_4_GHZ;`,
352
+ ` (void)net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, __tc_wifi.iface,`,
353
+ ` &__tc_wifi_ap_params, sizeof(__tc_wifi_ap_params));`,
354
+ ` printk("tc-wifi: AP started (%s)\\n", ssid);`,
355
+ `}`,
356
+ ``,
357
+ `static void __tc_wifi_ap_stop(void) {`,
358
+ ` __tc_wifi_ensure_init();`,
359
+ ` if (__tc_wifi.iface == nullptr) return;`,
360
+ ` (void)net_mgmt(NET_REQUEST_WIFI_AP_DISABLE, __tc_wifi.iface, nullptr, 0);`,
361
+ ` printk("tc-wifi: AP stopped\\n");`,
362
+ `}`,
363
+ `// CUTTLEFISH_WIFI_CONFIG_END`,
364
+ ``,
365
+ `// CUTTLEFISH_WIFI_END`,
366
+ ];
367
+ }
368
+ /**
369
+ * Resolve a HAL wifi.* op to Zephyr C++ via the __tc_wifi_* shim. Returns
370
+ * `{ code }` for statement ops, `{ expression }` for value-returning ops,
371
+ * `undefined` for out-of-scope ops (the manifest declares those unsupported).
372
+ */
373
+ export function lowerWifi(op) {
374
+ const o = op;
375
+ switch (op.operation) {
376
+ // ── Connection ──
377
+ case 'wifi.connect':
378
+ return { code: `__tc_wifi_connect(${s(o.ssid)}, ${s(o.password ?? 'nullptr')}, ${s(o.timeoutMs ?? 15000)});` };
379
+ case 'wifi.connect_start':
380
+ return { code: `__tc_wifi_connect_start(${s(o.ssid)}, ${s(o.password ?? 'nullptr')});` };
381
+ case 'wifi.disconnect':
382
+ return { code: `__tc_wifi_disconnect();` };
383
+ case 'wifi.status':
384
+ return { expression: `__tc_wifi_status()` };
385
+ case 'wifi.is_connected':
386
+ return { expression: `(__tc_wifi.connected)` };
387
+ case 'wifi.local_ip':
388
+ return { expression: `__tc_wifi_local_ip()` };
389
+ case 'wifi.rssi':
390
+ return { expression: `__tc_wifi_rssi()` };
391
+ case 'wifi.mac':
392
+ return { expression: `__tc_wifi_mac()` };
393
+ // ── Scan ──
394
+ case 'wifi.scan':
395
+ return { code: `__tc_wifi_scan_blocking();` };
396
+ case 'wifi.scan_start':
397
+ return { code: `__tc_wifi_scan_start();` };
398
+ case 'wifi.scan_done':
399
+ return { expression: `(!__tc_wifi.scanning)` };
400
+ case 'wifi.scan_count':
401
+ return { expression: `((int32_t)__tc_wifi.scan_count)` };
402
+ case 'wifi.scan_ssid':
403
+ case 'wifi.scan_rssi':
404
+ case 'wifi.scan_encryption':
405
+ case 'wifi.scan_channel': {
406
+ const helper = op.operation === 'wifi.scan_ssid' ? '__tc_wifi_scan_ssid'
407
+ : op.operation === 'wifi.scan_rssi' ? '__tc_wifi_scan_rssi'
408
+ : op.operation === 'wifi.scan_encryption' ? '__tc_wifi_scan_enc'
409
+ : '__tc_wifi_scan_channel';
410
+ return { expression: `${helper}(${s(o.index ?? 0)})` };
411
+ }
412
+ // ── Config ──
413
+ case 'wifi.set_hostname':
414
+ return { code: `__tc_wifi_set_hostname(${s(o.name)});` };
415
+ case 'wifi.set_power_save':
416
+ // HAL modes are "default" (on) / "none" (off) — see the shim's note.
417
+ return { code: `__tc_wifi_set_power_save(${s(o.mode)});` };
418
+ case 'wifi.on_event': {
419
+ // Event callbacks (wifi.on_event). 'disconnect' and 'connect' are supported.
420
+ if (o.event === 'disconnect') {
421
+ return { code: `__tc_wifi.on_disconnect = ${s(o.handler)};` };
422
+ }
423
+ if (o.event === 'connect') {
424
+ return { code: `__tc_wifi.on_connect = ${s(o.handler)};` };
425
+ }
426
+ return undefined;
427
+ }
428
+ // ── Waits (block on the L4 connectivity flag) ──
429
+ case 'wifi.wait_connected':
430
+ return { code: `__tc_wifi_wait_connected(${s(o.timeoutMs ?? 15000)});` };
431
+ case 'wifi.wait_disconnected':
432
+ return { code: `__tc_wifi_wait_disconnected();` };
433
+ // ── AP mode (esp32 driver: ap_enable/ap_disable wired; only ssid/psk/channel
434
+ // honored — hidden/maxClients have no driver hook). ──
435
+ case 'wifi.ap_start':
436
+ return { code: `__tc_wifi_ap_start(${s(o.ssid)}, ${o.password != null ? s(o.password) : 'nullptr'}, ${s(o.channel ?? 0)});` };
437
+ case 'wifi.ap_stop':
438
+ return { code: `__tc_wifi_ap_stop();` };
439
+ default:
440
+ // Out of scope (genuinely not lowered): ap_client_count/ap_ip/ap_set_*,
441
+ // credentials (save/connect_saved/clear), set_static_ip, set_auto_reconnect,
442
+ // set_tx_power. Each has no driver/Kconfig hook on Zephyr — see the manifest's
443
+ // per-op reasons. Return undefined so the resolver falls back and the
444
+ // 'unsupported' declaration is honest (the validator probes these).
445
+ return undefined;
446
+ }
447
+ }
@@ -0,0 +1,14 @@
1
+ import type { WorkerBacking } from "@typecad/cuttlefish/api/shared";
2
+ export interface ZephyrWorkerBackingOptions {
3
+ /** Initial count of each per-slot semaphore. Default 0 (empty). */
4
+ initialSemCount?: number;
5
+ }
6
+ /**
7
+ * Build the Zephyr (k_work + k_sem) worker backing.
8
+ *
9
+ * Per slot: a `struct k_work _work_<i>` and a `struct k_sem _done_sem_<i>`.
10
+ * submit resets the semaphore and submits the work to the system workqueue;
11
+ * the work handler runs the trampoline; the trampoline signals completion via
12
+ * k_sem_give; done polls via k_sem_take(K_NO_WAIT).
13
+ */
14
+ export declare function buildZephyrWorkerBacking(_options?: ZephyrWorkerBackingOptions): WorkerBacking;
@@ -0,0 +1,79 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Zephyr worker-offload backing (system workqueue + counting semaphore)
3
+ //
4
+ // Supplies the spawn/signal/poll primitives that the shared worker_runtime
5
+ // polyfill calls into, for Zephyr targets. This is the Zephyr-native analog of
6
+ // the ESP32 FreeRTOS backing (framework-esp32/src/lowering/worker-backing.ts):
7
+ //
8
+ // ESP32 (FreeRTOS) Zephyr
9
+ // ───────────────────────── ─────────────────────────────────────────
10
+ // xTaskCreate + vTaskDelete k_work_submit to the system workqueue
11
+ // xTaskNotifyGive (barrier) k_sem_give (kernel barrier)
12
+ // ulTaskNotifyTake(0) k_sem_take(K_NO_WAIT)
13
+ //
14
+ // The worker runs on the system workqueue thread (a real kernel thread, not an
15
+ // ISR), so it can execute a full function. Completion is signalled via a per-
16
+ // slot counting semaphore given from workqueue thread context (k_sem_give is
17
+ // safe from thread context — NOT k_sem_give from an ISR; workqueue handlers run
18
+ // in thread context). k_sem provides the memory barrier the contract requires.
19
+ //
20
+ // IMPORTANT: the system workqueue has a fixed stack (CONFIG_SYSTEM_WORKQUEUE_
21
+ // STACK_SIZE). The scaffold bumps it because heavy workers (TLS, sensor fusion)
22
+ // overflow the default ~2 KB. Pin a large worker to a private workqueue if it
23
+ // exceeds the system queue's stack.
24
+ // ---------------------------------------------------------------------------
25
+ /**
26
+ * Build the Zephyr (k_work + k_sem) worker backing.
27
+ *
28
+ * Per slot: a `struct k_work _work_<i>` and a `struct k_sem _done_sem_<i>`.
29
+ * submit resets the semaphore and submits the work to the system workqueue;
30
+ * the work handler runs the trampoline; the trampoline signals completion via
31
+ * k_sem_give; done polls via k_sem_take(K_NO_WAIT).
32
+ */
33
+ export function buildZephyrWorkerBacking(_options = {}) {
34
+ return {
35
+ requiredIncludes: ['<zephyr/kernel.h>', '<zephyr/work/work.h>'],
36
+ declarations: [
37
+ `// Zephyr worker backing: per-slot work item + completion semaphore.`,
38
+ `// Declared inside the typecad_worker namespace alongside the slots.`,
39
+ ],
40
+ slotDeclarations: (handleId, trampolineName) => {
41
+ // Per-slot completion semaphore (given from workqueue thread context).
42
+ // Declared before the trampoline so the trampoline's signalDoneExpr can
43
+ // reference it. The trampoline itself is forward-declared by the shared
44
+ // runtime above this block.
45
+ return [
46
+ `// Per-slot completion semaphore.`,
47
+ `K_SEM_DEFINE(_done_sem_${handleId}, 0, 1);`,
48
+ ``,
49
+ `// Per-slot work item + handler. The handler forwards to the shared-`,
50
+ `// runtime trampoline (which runs the submitted fn + signals the sem).`,
51
+ `static void ${trampolineName}_work_handler(struct k_work* work) {`,
52
+ ` (void)work;`,
53
+ ` ${trampolineName}(nullptr);`,
54
+ `}`,
55
+ `K_WORK_DEFINE(_work_${handleId}, ${trampolineName}_work_handler);`,
56
+ ];
57
+ },
58
+ spawnLines: (handleId, trampolineName, _waiterExpr) => {
59
+ // The waiter field is unused on Zephyr (the semaphore IS the signal).
60
+ // Reset the completion semaphore, then submit the work item to the
61
+ // system workqueue. The work handler (bound via K_WORK_DEFINE above)
62
+ // runs the shared-runtime trampoline.
63
+ return [
64
+ `(void)0; // waiter unused: k_sem is the signal`,
65
+ `k_sem_reset(&_done_sem_${handleId});`,
66
+ `(void)k_work_submit(&_work_${handleId}); // ${trampolineName} on sys workqueue`,
67
+ ];
68
+ },
69
+ signalDoneExpr: (handleId) =>
70
+ // Given from workqueue thread context (k_sem_give is thread-safe here).
71
+ // k_sem provides the kernel memory barrier making the worker's outputs
72
+ // visible to the consumer once k_sem_take succeeds.
73
+ `k_sem_give(&_done_sem_${handleId})`,
74
+ isDoneExpr: (handleId) =>
75
+ // Non-blocking take; its barrier makes the worker's outputs safe to read.
76
+ // The shared runtime fast-paths on the volatile done flag first.
77
+ `(k_sem_take(&_done_sem_${handleId}, K_NO_WAIT) == 0)`,
78
+ };
79
+ }
@@ -0,0 +1,6 @@
1
+ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
2
+ /** Resolve a HAL worker.* op to C++ against the shared __tc_worker contract. */
3
+ export declare function lowerWorker(op: HALOpIR): {
4
+ code?: string;
5
+ expression?: string;
6
+ } | undefined;
@@ -0,0 +1,14 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Zephyr worker-offload lowering
3
+ //
4
+ // Delegates the shared worker.* op contract to the cross-framework helper
5
+ // lowerWorkerOp (worker-runtime.ts). The per-framework primitives are supplied
6
+ // by the Zephyr worker backing (worker-backing.ts: k_work + k_sem), emitted via
7
+ // the worker_runtime polyfill when the program uses worker.* ops. See the
8
+ // Zephyr strategy's worker backing wiring + generateNativePolyfills.
9
+ // ---------------------------------------------------------------------------
10
+ import { lowerWorkerOp } from '@typecad/cuttlefish/api/shared';
11
+ /** Resolve a HAL worker.* op to C++ against the shared __tc_worker contract. */
12
+ export function lowerWorker(op) {
13
+ return lowerWorkerOp(op);
14
+ }