@typecad/framework-zephyr 1.0.0-alpha.13 → 1.0.0-alpha.15

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 (191) hide show
  1. package/README.md +22 -35
  2. package/dist/as-built.d.ts +33 -0
  3. package/dist/as-built.js +188 -0
  4. package/dist/async/timer-polyfill.js +1 -1
  5. package/dist/boardgen.d.ts +128 -0
  6. package/dist/boardgen.js +1366 -0
  7. package/dist/chips/board-overrides.d.ts +7 -0
  8. package/dist/chips/board-overrides.js +11 -0
  9. package/dist/chips/controllers.d.ts +28 -8
  10. package/dist/chips/controllers.js +49 -12
  11. package/dist/chips/esp32s3.js +49 -0
  12. package/dist/chips/index.d.ts +6 -12
  13. package/dist/chips/index.js +18 -34
  14. package/dist/chips/resolve.d.ts +16 -2
  15. package/dist/chips/resolve.js +298 -15
  16. package/dist/chips/soc/.d.ts +2 -0
  17. package/dist/chips/soc/.js +129 -0
  18. package/dist/chips/soc/esp32.d.ts +2 -0
  19. package/dist/chips/soc/esp32.js +120 -0
  20. package/dist/chips/soc/esp32c3.d.ts +2 -0
  21. package/dist/chips/soc/esp32c3.js +90 -0
  22. package/dist/chips/soc/esp32c6.d.ts +2 -0
  23. package/dist/chips/soc/esp32c6.js +109 -0
  24. package/dist/chips/soc/esp32s3.d.ts +2 -0
  25. package/dist/chips/soc/esp32s3.js +189 -0
  26. package/dist/chips/soc/index.d.ts +2 -0
  27. package/dist/chips/soc/index.js +23 -0
  28. package/dist/chips/soc/nrf52840.d.ts +2 -0
  29. package/dist/chips/soc/nrf52840.js +130 -0
  30. package/dist/chips/soc/rp2040.d.ts +2 -0
  31. package/dist/chips/soc/rp2040.js +141 -0
  32. package/dist/chips/soc/rp2350a.d.ts +2 -0
  33. package/dist/chips/soc/rp2350a.js +145 -0
  34. package/dist/chips/soc/samd21g18a.d.ts +2 -0
  35. package/dist/chips/soc/samd21g18a.js +143 -0
  36. package/dist/chips/soc/stm32f411xe.d.ts +2 -0
  37. package/dist/chips/soc/stm32f411xe.js +251 -0
  38. package/dist/chips/types.d.ts +382 -12
  39. package/dist/chips/xiao-ble.js +33 -0
  40. package/dist/display/profiles.js +1 -1
  41. package/dist/doctor.js +38 -4
  42. package/dist/dt-config/custom-board.d.ts +23 -0
  43. package/dist/dt-config/custom-board.js +227 -0
  44. package/dist/dt-config/kconfig.d.ts +83 -1
  45. package/dist/dt-config/kconfig.js +116 -27
  46. package/dist/dt-config/overlay.js +623 -18
  47. package/dist/framework.manifest.d.ts +20 -30
  48. package/dist/framework.manifest.js +124 -178
  49. package/dist/index.d.ts +4 -1
  50. package/dist/index.js +8 -2
  51. package/dist/lowering/adc.d.ts +26 -5
  52. package/dist/lowering/adc.js +137 -31
  53. package/dist/lowering/ble.js +31 -28
  54. package/dist/lowering/dac.js +9 -9
  55. package/dist/lowering/fs.js +18 -10
  56. package/dist/lowering/gpio.d.ts +2 -0
  57. package/dist/lowering/gpio.js +90 -10
  58. package/dist/lowering/http.js +39 -14
  59. package/dist/lowering/hwtimer.d.ts +4 -0
  60. package/dist/lowering/hwtimer.js +35 -10
  61. package/dist/lowering/i2c.js +46 -54
  62. package/dist/lowering/index.d.ts +5 -5
  63. package/dist/lowering/index.js +15 -16
  64. package/dist/lowering/interrupts.d.ts +22 -5
  65. package/dist/lowering/interrupts.js +104 -22
  66. package/dist/lowering/mqtt.js +12 -6
  67. package/dist/lowering/power.js +0 -4
  68. package/dist/lowering/preferences.js +49 -77
  69. package/dist/lowering/pulse.js +5 -36
  70. package/dist/lowering/pwm.d.ts +32 -3
  71. package/dist/lowering/pwm.js +105 -17
  72. package/dist/lowering/random.js +3 -3
  73. package/dist/lowering/sensor.d.ts +37 -0
  74. package/dist/lowering/sensor.js +119 -0
  75. package/dist/lowering/spi.d.ts +12 -0
  76. package/dist/lowering/spi.js +65 -40
  77. package/dist/lowering/thread.d.ts +14 -0
  78. package/dist/lowering/thread.js +55 -0
  79. package/dist/lowering/timing.js +20 -46
  80. package/dist/lowering/tone.js +18 -5
  81. package/dist/lowering/uart.d.ts +13 -0
  82. package/dist/lowering/uart.js +72 -51
  83. package/dist/lowering/usb.d.ts +34 -0
  84. package/dist/lowering/usb.js +206 -0
  85. package/dist/lowering/wdt.d.ts +1 -1
  86. package/dist/lowering/wdt.js +17 -7
  87. package/dist/lowering/wifi.d.ts +3 -2
  88. package/dist/lowering/wifi.js +120 -111
  89. package/dist/sdk/board-catalog-sync.d.ts +16 -0
  90. package/dist/sdk/board-catalog-sync.js +41 -0
  91. package/dist/sdk/board-data.generated.d.ts +2 -0
  92. package/dist/sdk/board-data.generated.js +4 -0
  93. package/dist/sdk/catalog-walker.d.ts +90 -0
  94. package/dist/sdk/catalog-walker.js +682 -0
  95. package/dist/sdk/dts-reader.d.ts +83 -0
  96. package/dist/sdk/dts-reader.js +596 -0
  97. package/dist/strategy.d.ts +136 -17
  98. package/dist/strategy.js +881 -212
  99. package/dist/toolchain/bossac-touch.d.ts +18 -0
  100. package/dist/toolchain/bossac-touch.js +88 -0
  101. package/dist/toolchain/compat.d.ts +2 -2
  102. package/dist/toolchain/compat.js +8 -3
  103. package/dist/toolchain/debug-config.d.ts +21 -26
  104. package/dist/toolchain/debug-config.js +212 -72
  105. package/dist/toolchain/env-check.d.ts +4 -4
  106. package/dist/toolchain/env-check.js +5 -5
  107. package/dist/toolchain/index.d.ts +66 -1
  108. package/dist/toolchain/index.js +713 -58
  109. package/dist/toolchain/scaffold.d.ts +9 -0
  110. package/dist/toolchain/scaffold.js +84 -7
  111. package/dist/toolchain/west-discover.d.ts +8 -1
  112. package/dist/toolchain/west-discover.js +19 -6
  113. package/dist/toolchain/west-spawn.js +17 -5
  114. package/installer/CHANGELOG.md +473 -0
  115. package/installer/README.md +328 -0
  116. package/installer/environment.yml +46 -0
  117. package/installer/etc/conda/activate.d/zephyr.bat +7 -0
  118. package/installer/etc/conda/activate.d/zephyr.ps1 +12 -0
  119. package/installer/etc/conda/activate.d/zephyr.sh +20 -0
  120. package/installer/etc/conda/deactivate.d/zephyr.bat +6 -0
  121. package/installer/etc/conda/deactivate.d/zephyr.ps1 +6 -0
  122. package/installer/etc/conda/deactivate.d/zephyr.sh +4 -0
  123. package/installer/install.mjs +580 -0
  124. package/installer/install.ps1 +619 -0
  125. package/installer/install.sh +310 -0
  126. package/installer/lib/detect-platform.sh +62 -0
  127. package/installer/lib/fetch-sdk.sh +304 -0
  128. package/installer/lib/init-workspace.sh +153 -0
  129. package/installer/lib/write-activation.sh +76 -0
  130. package/installer/patches/tfm-c-include-separators.patch +19 -0
  131. package/installer/patches/zephyr-tfm-nordic-zephyr-include.patch +24 -0
  132. package/installer/templates/project/.typecad/activate-zephyr.ps1 +31 -0
  133. package/installer/templates/project/.typecad/activate-zephyr.sh +31 -0
  134. package/installer/templates/project/.vscode/settings.json +30 -0
  135. package/installer/templates/project/README.md +58 -0
  136. package/installer/versions.env +110 -0
  137. package/package.json +16 -16
  138. package/src/as-built.ts +206 -0
  139. package/src/boardgen.ts +1428 -0
  140. package/src/chips/controllers.ts +61 -12
  141. package/src/chips/index.ts +35 -54
  142. package/src/chips/resolve.ts +316 -14
  143. package/src/chips/types.ts +388 -13
  144. package/src/display/profiles.ts +1 -1
  145. package/src/doctor.ts +110 -77
  146. package/src/dt-config/custom-board.ts +325 -0
  147. package/src/dt-config/kconfig.ts +177 -19
  148. package/src/dt-config/overlay.ts +1058 -450
  149. package/src/framework.manifest.ts +125 -178
  150. package/src/index.ts +57 -41
  151. package/src/lowering/adc.ts +222 -102
  152. package/src/lowering/ble.ts +32 -28
  153. package/src/lowering/dac.ts +82 -82
  154. package/src/lowering/fs.ts +18 -10
  155. package/src/lowering/gpio.ts +242 -152
  156. package/src/lowering/http.ts +512 -488
  157. package/src/lowering/hwtimer.ts +139 -101
  158. package/src/lowering/i2c.ts +116 -126
  159. package/src/lowering/index.ts +91 -93
  160. package/src/lowering/interrupts.ts +113 -21
  161. package/src/lowering/mqtt.ts +12 -6
  162. package/src/lowering/preferences.ts +327 -354
  163. package/src/lowering/pwm.ts +167 -74
  164. package/src/lowering/random.ts +3 -3
  165. package/src/lowering/sensor.ts +155 -0
  166. package/src/lowering/spi.ts +164 -124
  167. package/src/lowering/thread.ts +63 -0
  168. package/src/lowering/timing.ts +46 -72
  169. package/src/lowering/uart.ts +73 -51
  170. package/src/lowering/usb.ts +221 -0
  171. package/src/lowering/wdt.ts +95 -84
  172. package/src/lowering/wifi.ts +120 -111
  173. package/src/sdk/board-catalog-sync.ts +62 -0
  174. package/src/strategy.ts +888 -229
  175. package/src/toolchain/bossac-touch.ts +112 -0
  176. package/src/toolchain/compat.ts +8 -3
  177. package/src/toolchain/debug-config.ts +666 -522
  178. package/src/toolchain/env-check.ts +9 -9
  179. package/src/toolchain/index.ts +765 -44
  180. package/src/toolchain/scaffold.ts +81 -7
  181. package/src/toolchain/west-discover.ts +334 -316
  182. package/src/toolchain/west-spawn.ts +17 -5
  183. package/src/async/timer-polyfill.ts +0 -107
  184. package/src/chips/esp32.ts +0 -74
  185. package/src/chips/esp32s3.ts +0 -57
  186. package/src/chips/xiao-ble.ts +0 -70
  187. package/src/lowering/power.ts +0 -70
  188. package/src/lowering/pulse.ts +0 -93
  189. package/src/lowering/tone.ts +0 -60
  190. package/src/lowering/worker-backing.ts +0 -94
  191. package/src/lowering/worker.ts +0 -17
@@ -1,13 +1,9 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // Timing lowering — Zephyr kernel timing
3
3
  //
4
- // delay/millis/delay_microseconds/micros/free_heap lower directly. The four
5
- // timer ops (set_interval/set_timeout/clear_interval/clear_timeout) call the
6
- // timer_methods polyfill helpers (k_timer + k_work pool), declared as 'polyfill'
7
- // status in the manifest — the validator skips the resolver probe for these
8
- // (they legitimately return polyfill-helper calls, not direct lowering).
9
- // free_heap has no portable Zephyr query without CONFIG_SYS_HEAP_RUNTIME_STATS;
10
- // it returns 0 with a comment (honest limitation).
4
+ // sleep/now/now_us/busy_wait_us lower directly to kernel calls. Periodic or
5
+ // deferred work is NOT a timing op: it is a Thread (k_thread) or a Counter
6
+ // (hardware timer), each with its own lowering.
11
7
  // ---------------------------------------------------------------------------
12
8
  /**
13
9
  * Resolve a HAL timing.* op to Zephyr C++.
@@ -16,46 +12,24 @@
16
12
  export function lowerTiming(op) {
17
13
  const o = op;
18
14
  switch (op.operation) {
19
- case 'timing.delay': {
20
- // Millisecondsk_msleep is the blocking Zephyr call. Because
21
- // isRtosTarget() returns true, the transpiler does not warn about
22
- // blocking delays inside loop().
23
- const ms = o.ms;
24
- return { code: `k_msleep(${ms});` };
25
- }
26
- case 'timing.delay_microseconds': {
27
- // k_busy_wait spins (does not yield); for cooperative μs delays.
15
+ case 'timing.sleep':
16
+ // Time.sleepyielding ms sleep (k_msleep), delay()'s replacement.
17
+ return { code: `k_msleep(${o.ms});` };
18
+ case 'timing.now':
19
+ // Time.now — ms since boot as a double (k_uptime_get is int64_t ms).
20
+ return { expression: 'static_cast<double>(k_uptime_get())' };
21
+ case 'timing.now_us':
22
+ // Time.nowUs — µs since boot, uptime-derived for EVERY board: the
23
+ // cycle-counter form (k_cyc_to_us_floor64(k_cycle_get_64())) reads a
24
+ // constant on SoCs without a free-running 64-bit counter, so the one
25
+ // uniform expression that is monotonic and advancing everywhere is
26
+ // the kernel uptime scaled to µs. Resolution is therefore the uptime
27
+ // tick (millisecond), uniformly. A double holds µs exactly for ~285
28
+ // years.
29
+ return { expression: 'static_cast<double>(k_uptime_get() * 1000)' };
30
+ case 'timing.busy_wait_us':
31
+ // Time.busyWaitUs — spin, no yield; delayMicroseconds's honest name.
28
32
  return { code: `k_busy_wait(${o.us});` };
29
- }
30
- case 'timing.millis':
31
- // k_uptime_get_32() returns int64_t milliseconds since boot. Cast to the
32
- // Arduino-millis() return type (uint32_t) so overflow wraps identically.
33
- return { expression: 'static_cast<uint32_t>(k_uptime_get_32())' };
34
- case 'timing.micros': {
35
- // Convert hardware cycles to microseconds. k_cycle_get_32 + the cycles/sec
36
- // macro (note: sys_clock_hw_cycles_per_sec is a MACRO — needs parens).
37
- return {
38
- expression: '(uint32_t)(((uint64_t)k_cycle_get_32() * 1000000ULL) / sys_clock_hw_cycles_per_sec())',
39
- };
40
- }
41
- case 'timing.free_heap':
42
- // No portable free-heap query without CONFIG_SYS_HEAP_RUNTIME_STATS.
43
- // Return 0 with a comment so callers don't get a link error.
44
- return { expression: '(0 /* free_heap: enable CONFIG_SYS_HEAP_RUNTIME_STATS for real value */)' };
45
- case 'timing.set_interval':
46
- // Backed by the timer_methods polyfill (k_timer + k_work). The handler is
47
- // the resolved C++ callback name; timeout is the repeat period (ms).
48
- // NOTE: these ops are declared 'polyfill' in the manifest, so setInterval
49
- // is rewritten to __tc_setInterval before reaching the lowering — these
50
- // cases are a fallback/defense and use the real op fields (handler/timeout).
51
- return { expression: `__tc_setInterval(${o.handler}, ${o.timeout})` };
52
- case 'timing.set_timeout':
53
- // One-shot: k_timer with K_FOREVER period.
54
- return { expression: `__tc_setTimeout(${o.handler}, ${o.timeout})` };
55
- case 'timing.clear_interval':
56
- return { code: `__tc_clearInterval(${o.id});` };
57
- case 'timing.clear_timeout':
58
- return { code: `__tc_clearTimeout(${o.id});` };
59
33
  default:
60
34
  throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
61
35
  `Open an issue or use rawCpp() to emit it manually.`);
@@ -11,6 +11,16 @@
11
11
  // matching the synchronous Arduino tone() semantics. For non-blocking tone,
12
12
  // a workqueue would be needed (deferred).
13
13
  // ---------------------------------------------------------------------------
14
+ import { findPwmSpec, pwmDtAliasToken } from './pwm.js';
15
+ /**
16
+ * The PWM spec a tone op drives: the op's own pin when it maps to a spec
17
+ * (matrix chips — ESP32 LEDC — resolve any driven pad), else the board's
18
+ * first spec (the aliased LED channel, e.g. pwm-led0 on the XIAO).
19
+ */
20
+ function toneSpec(chip, pin) {
21
+ return (typeof pin === 'number' ? findPwmSpec(chip, pin) : undefined)
22
+ ?? chip.pwm?.specs[0];
23
+ }
14
24
  /**
15
25
  * Resolve a HAL tone.* op to Zephyr C++ via PWM.
16
26
  * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
@@ -22,14 +32,17 @@ export function lowerTone(op, chip) {
22
32
  // period_ns = 1e9 / freq; pulse_ns = period / 2 (50% duty).
23
33
  // Build a fake pwm.write would need the spec var directly; emit the
24
34
  // pwm_set_dt call against the same spec var the PWM lowering uses.
25
- const spec = chip.pwm?.specs[0];
35
+ const spec = toneSpec(chip, o.pin);
26
36
  if (!spec) {
27
37
  return { code: `/* tone.play(${o.frequency}): no PWM spec in chip descriptor */` };
28
38
  }
29
- const v = `__tc_pwm_${spec.dtSpec.replace(/-/g, '_')}`;
39
+ const v = `__tc_pwm_${pwmDtAliasToken(spec)}`;
30
40
  const freq = o.frequency;
31
41
  const duration = o.duration;
32
- const setTone = `uint32_t __period = (${freq} > 0) ? (1000000000ULL / static_cast<uint64_t>(${freq})) : 0; pwm_set_dt(&${v}, __period, __period / 2);`;
42
+ // Braced: two tone ops in one statement-scope (e.g. tone(440).for(50)
43
+ // lowers tone.play + tone.play_for back-to-back) would otherwise
44
+ // redeclare __period.
45
+ const setTone = `{ uint32_t __period = (${freq} > 0) ? (1000000000ULL / static_cast<uint64_t>(${freq})) : 0; pwm_set_dt(&${v}, __period, __period / 2); }`;
33
46
  if (duration !== undefined) {
34
47
  // Blocking tone for the requested duration, then stop.
35
48
  return { code: `${setTone} k_msleep(${duration}); pwm_set_pulse_dt(&${v}, 0);` };
@@ -37,10 +50,10 @@ export function lowerTone(op, chip) {
37
50
  return { code: setTone };
38
51
  }
39
52
  case 'tone.stop': {
40
- const spec = chip.pwm?.specs[0];
53
+ const spec = toneSpec(chip, o.pin);
41
54
  if (!spec)
42
55
  return { code: `/* tone.stop: no PWM spec */` };
43
- const v = `__tc_pwm_${spec.dtSpec.replace(/-/g, '_')}`;
56
+ const v = `__tc_pwm_${pwmDtAliasToken(spec)}`;
44
57
  return { code: `pwm_set_pulse_dt(&${v}, 0);` };
45
58
  }
46
59
  default:
@@ -5,6 +5,12 @@ import type { ZephyrChipDescriptor } from '../chips/types.js';
5
5
  * when the program uses UART.
6
6
  */
7
7
  export declare function uartInitLines(chip: ZephyrChipDescriptor, controllerIndex: number): string[];
8
+ /** Render a string/number/boolean value to a per-byte poll_out loop via the
9
+ * __tc_dev_put overloads (the const char* overload streams a string; the
10
+ * double overload formats a number with __tc_fmt_num_buf). The value passes
11
+ * through verbatim — C++ overload resolution picks the right overload for a
12
+ * string literal, a number literal, or a runtime variable of either type. */
13
+ export declare function renderWrite(dev: string, value: string, newline: boolean): string;
8
14
  /**
9
15
  * Resolve a HAL uart.* op to Zephyr C++.
10
16
  * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
@@ -13,3 +19,10 @@ export declare function lowerUart(op: HALOpIR): {
13
19
  code?: string;
14
20
  expression?: string;
15
21
  };
22
+ /** The per-port RX ring state: a construction-sized static buffer, free-running
23
+ * head/tail counters (available = head - tail, no wrap ambiguity), and the
24
+ * ISR that drains the FIFO. A byte arriving with a full ring is dropped —
25
+ * the embedded-honest answer (no unbounded buffering). Emitted only for
26
+ * ports whose uart.rx_* ops were emitted (collectUartRings), so an unused
27
+ * ISR never trips -Wunused-function. */
28
+ export declare function uartRingStateLines(index: number, ring: number): string[];
@@ -2,9 +2,8 @@
2
2
  // UART lowering — uart_poll_out / uart_poll_in per-byte
3
3
  //
4
4
  // Zephyr's UART API is byte-oriented (uart_poll_out / uart_poll_in). The HAL's
5
- // uart.print/println/write lower to per-byte poll_out loops. For console output
6
- // the strategy's transformConsoleCall routes to printk; these uart.* ops are
7
- // for a specific UART port (the XIAO exposes uart0 on D6/D7).
5
+ // uart.print/println/write lower to per-byte poll_out loops; these uart.* ops
6
+ // are for a specific UART port (the XIAO exposes uart0 on D6/D7).
8
7
  //
9
8
  // The device resolves at compile time via DEVICE_DT_GET(DT_NODELABEL(uart0)).
10
9
  // uart.begin configures the baud via uart_configure.
@@ -33,16 +32,14 @@ export function uartInitLines(chip, controllerIndex) {
33
32
  '// CUTTLEFISH_UART_END',
34
33
  ];
35
34
  }
36
- /** Render a string-literal or expression to a per-byte poll_out loop. */
37
- function renderWrite(dev, value, newline) {
38
- // String literal emit a char-array loop (known length). Otherwise fall back
39
- // to a const char* cast with a strlen-guarded loop.
40
- if (/^".*"$/.test(value)) {
41
- const body = `for (size_t __i = 0; __i < sizeof(${value}) - 1; __i++) { uart_poll_out(${dev}, (${value})[__i]); }`;
42
- return newline ? `${body} uart_poll_out(${dev}, '\\n');` : body;
43
- }
44
- const body = `for (const char* __s = (const char*)(${value}); *__s; __s++) { uart_poll_out(${dev}, *__s); }`;
45
- return newline ? `${body} uart_poll_out(${dev}, '\\n');` : body;
35
+ /** Render a string/number/boolean value to a per-byte poll_out loop via the
36
+ * __tc_dev_put overloads (the const char* overload streams a string; the
37
+ * double overload formats a number with __tc_fmt_num_buf). The value passes
38
+ * through verbatim C++ overload resolution picks the right overload for a
39
+ * string literal, a number literal, or a runtime variable of either type. */
40
+ export function renderWrite(dev, value, newline) {
41
+ const put = `__tc_dev_put(${dev}, ${value});`;
42
+ return newline ? `${put} uart_poll_out(${dev}, '\\n');` : put;
46
43
  }
47
44
  /**
48
45
  * Resolve a HAL uart.* op to Zephyr C++.
@@ -54,49 +51,73 @@ export function lowerUart(op) {
54
51
  const p = prefix(idx);
55
52
  const dev = `${p}_dev`;
56
53
  switch (op.operation) {
57
- case 'uart.begin':
58
- return { code: `${p}_init(static_cast<uint32_t>(${o.baud}));` };
59
- case 'uart.end':
60
- return { code: `(void)${dev};` };
61
- case 'uart.print':
62
- return { code: renderWrite(dev, o.value, false) };
63
- case 'uart.println':
64
- return { code: renderWrite(dev, o.value, true) };
65
- case 'uart.write':
66
- return { code: renderWrite(dev, o.data, false) };
67
- case 'uart.printf': {
68
- // snprintf into a buffer, then poll_out each byte.
69
- const fmt = o.format;
70
- const args = (o.args ?? []).join(', ');
71
- const argList = args ? `, ${args}` : '';
54
+ case 'uart.poll_write': {
55
+ const pre = uartBaudGuard(p, o.baud);
56
+ return { code: `${pre} ${renderWrite(dev, o.data, false)}` };
57
+ }
58
+ // ── Thin UART RX (hal/uart-port.ts) — interrupt-drained ring ──────────
59
+ // The first receive call arms the driver IRQ (guarded); the ISR drains
60
+ // the FIFO into the construction-sized static ring (uartRingStateLines).
61
+ // available/peek/read are then pure ring arithmetic — the calls the poll
62
+ // API honestly could not support.
63
+ case 'uart.rx_arm':
64
+ case 'uart.rx_available':
65
+ case 'uart.rx_peek':
66
+ case 'uart.rx_read': {
67
+ const r = `__tc_uartrx${idx}`;
68
+ const arm = `{ static bool ${r}_armed = false; if (!${r}_armed) { uart_irq_callback_user_data_set(${dev}, ${r}_isr, NULL); uart_irq_rx_enable(${dev}); ${r}_armed = true; } }`;
69
+ if (op.operation === 'uart.rx_arm')
70
+ return { code: arm };
71
+ if (op.operation === 'uart.rx_available') {
72
+ return { expression: `({ ${arm} (${r}_head - ${r}_tail); })` };
73
+ }
74
+ if (op.operation === 'uart.rx_peek') {
75
+ return { expression: `({ ${arm} (${r}_tail < ${r}_head ? ${r}_buf[${r}_tail % ${o.ring}] : -1); })` };
76
+ }
72
77
  return {
73
- code: `char __buf[128]; int __n = snprintk(__buf, sizeof(__buf), ${fmt}${argList}); for (int __i = 0; __i < __n; __i++) { uart_poll_out(${dev}, __buf[__i]); }`,
78
+ expression: `({ ${arm} (${r}_tail < ${r}_head ? ${r}_buf[(${r}_tail)++ % ${o.ring}] : -1); })`,
74
79
  };
75
80
  }
76
- case 'uart.read':
77
- // Non-blocking poll; returns the byte or -1 if none available.
78
- return { expression: `({ unsigned char __b = 0; (uart_poll_in(${dev}, &__b) == 0) ? (int)__b : -1; })` };
79
- case 'uart.peek':
80
- // The poll API has no buffered-byte store, so there is no true peek.
81
- // Return -1 (the Arduino "no data" sentinel) rather than blocking. This
82
- // is an honest limitation of the byte-level poll driver; an interrupt- or
83
- // DMA-backed UART driver would be needed for real peek semantics.
84
- return { expression: '(-1)' };
85
- case 'uart.available':
86
- // The poll API reports only "at least one byte ready" via uart_poll_in's
87
- // return code — it has no buffered-byte count, and probing with poll_in
88
- // would DRAIN the very byte the caller next wants to read. So we cannot
89
- // honestly report availability. Return 0 (Arduino's "no data" value)
90
- // rather than the old truthy -1, so `if (uart.available())` loops don't
91
- // spin forever on a false-positive. Callers should instead just call
92
- // uart.read() directly (it returns -1 when no byte is ready). For true
93
- // buffered availability, use an interrupt/DMA-backed UART driver.
94
- return { expression: '(0)' };
95
- case 'uart.flush':
96
- // poll_out is synchronous (blocking until sent); flush is a no-op.
97
- return { code: `(void)${dev};` };
98
81
  default:
99
82
  throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
100
83
  `Open an issue or use rawCpp() to emit it manually.`);
101
84
  }
102
85
  }
86
+ /** The thin-port baud preamble: apply the construction baud once via the
87
+ * shim's `_init` helper (uart_configure). Guarded per controller. */
88
+ function uartBaudGuard(p, baud) {
89
+ const n = typeof baud === 'number' ? baud : parseInt(String(baud ?? 0), 10);
90
+ if (!n || isNaN(n))
91
+ return '';
92
+ const done = `${p}_baud_done`;
93
+ return `{ static bool ${done} = false; if (!${done}) { ${p}_init(static_cast<uint32_t>(${n})); ${done} = true; } } `;
94
+ }
95
+ /** The per-port RX ring state: a construction-sized static buffer, free-running
96
+ * head/tail counters (available = head - tail, no wrap ambiguity), and the
97
+ * ISR that drains the FIFO. A byte arriving with a full ring is dropped —
98
+ * the embedded-honest answer (no unbounded buffering). Emitted only for
99
+ * ports whose uart.rx_* ops were emitted (collectUartRings), so an unused
100
+ * ISR never trips -Wunused-function. */
101
+ export function uartRingStateLines(index, ring) {
102
+ const r = `__tc_uartrx${index}`;
103
+ const dev = `__tc_uart${index}_dev`;
104
+ return [
105
+ '// CUTTLEFISH_UARTRX_BEGIN',
106
+ `static uint8_t ${r}_buf[${ring}];`,
107
+ `static volatile uint32_t ${r}_head = 0;`,
108
+ `static volatile uint32_t ${r}_tail = 0;`,
109
+ `static void ${r}_isr(const struct device* dev, void* user_data) {`,
110
+ ` (void)user_data;`,
111
+ ` uart_irq_update(dev);`,
112
+ ` while (uart_irq_rx_ready(dev)) {`,
113
+ ` uint8_t __c = 0;`,
114
+ ` (void)uart_fifo_read(dev, &__c, 1);`,
115
+ ` if ((${r}_head - ${r}_tail) < ${ring}) {`,
116
+ ` ${r}_buf[${r}_head % ${ring}] = __c;`,
117
+ ` ${r}_head++;`,
118
+ ` }`,
119
+ ` }`,
120
+ `}`,
121
+ '// CUTTLEFISH_UARTRX_END',
122
+ ];
123
+ }
@@ -0,0 +1,34 @@
1
+ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
2
+ import type { ZephyrChipDescriptor } from '../chips/types.js';
3
+ /**
4
+ * Emit the shared USB device context (USBD_DEVICE_DEFINE + string/config
5
+ * descriptors + configuration + class registration + usbd_enable). Emitted
6
+ * ONCE per program when the chip declares `usb` — the next stack has one
7
+ * device context on the UDC controller, regardless of how many CDC instances
8
+ * hang off it.
9
+ *
10
+ * VID/PID default to the Zephyr-project testing IDs (0x2fe3/0x0001); a board
11
+ * overrides via `zephyr.usb.vid`/`zephyr.usb.pid`. Full-speed only — the
12
+ * boards this framework targets (STM32 OTG_FS, nRF USBD) are FS controllers,
13
+ * and the HS path would need a second USBD_CONFIGURATION_DEFINE.
14
+ *
15
+ * Symbol/API shapes verified against Zephyr 4.3:
16
+ * subsys/usb/device_next/Kconfig, include/zephyr/usb/usbd.h, and
17
+ * samples/subsys/usb/common/sample_usbd_init.c.
18
+ */
19
+ export declare function usbdDeviceLines(chip: ZephyrChipDescriptor): string[];
20
+ /**
21
+ * Emit the per-instance CDC device + init helper. Called from shimLines when
22
+ * the program uses USB and the chip declares `usb`; must be paired with one
23
+ * usbdDeviceLines() block. The init starts the device stack (guarded) and
24
+ * applies the line coding.
25
+ */
26
+ export declare function usbInitLines(chip: ZephyrChipDescriptor, instanceIndex: number): string[];
27
+ /**
28
+ * Resolve a HAL usb.* op to Zephyr C++.
29
+ * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
30
+ */
31
+ export declare function lowerUsb(op: HALOpIR, chip: ZephyrChipDescriptor): {
32
+ code?: string;
33
+ expression?: string;
34
+ };
@@ -0,0 +1,206 @@
1
+ // ---------------------------------------------------------------------------
2
+ // USB CDC-ACM serial lowering — Zephyr "next" USB device stack
3
+ //
4
+ // A CDC-ACM instance (cdc_acm_uart<N>, composed in the generated overlay as a
5
+ // child of the UDC controller node) registers as a UART-class device, so the
6
+ // lowering drives it with the plain uart_* API — the same per-byte poll_out
7
+ // loops as lowering/uart.ts. The differences from a hardware UART:
8
+ // - usb.begin must start the device stack (usbdDeviceLines' one-shot
9
+ // __tc_usbd_start) before the port carries traffic;
10
+ // - "connected" is a real query (DTR line ctrl), not a constant;
11
+ // - baud is a line-coding hint — recorded, but CDC has no wire baud.
12
+ //
13
+ // If the chip descriptor declares no `usb`, every op throws with a clear
14
+ // "board does not expose USB" message (the diagnostic contract the framework
15
+ // manifest's board-gated support entry promises).
16
+ // ---------------------------------------------------------------------------
17
+ import { parseControllerIndex } from './util.js';
18
+ import { renderWrite } from './uart.js';
19
+ /** Render a HAL field: pass through (already rendered by the resolver). */
20
+ function s(v) {
21
+ return String(v);
22
+ }
23
+ /** The C variable prefix for a CDC instance's state. */
24
+ function prefix(idx) {
25
+ return `__tc_usb${idx}`;
26
+ }
27
+ /** Instance N resolves to the DT nodelabel the overlay generator emits. */
28
+ function nodeLabel(idx) {
29
+ return `cdc_acm_uart${idx}`;
30
+ }
31
+ /**
32
+ * Emit the shared USB device context (USBD_DEVICE_DEFINE + string/config
33
+ * descriptors + configuration + class registration + usbd_enable). Emitted
34
+ * ONCE per program when the chip declares `usb` — the next stack has one
35
+ * device context on the UDC controller, regardless of how many CDC instances
36
+ * hang off it.
37
+ *
38
+ * VID/PID default to the Zephyr-project testing IDs (0x2fe3/0x0001); a board
39
+ * overrides via `zephyr.usb.vid`/`zephyr.usb.pid`. Full-speed only — the
40
+ * boards this framework targets (STM32 OTG_FS, nRF USBD) are FS controllers,
41
+ * and the HS path would need a second USBD_CONFIGURATION_DEFINE.
42
+ *
43
+ * Symbol/API shapes verified against Zephyr 4.3:
44
+ * subsys/usb/device_next/Kconfig, include/zephyr/usb/usbd.h, and
45
+ * samples/subsys/usb/common/sample_usbd_init.c.
46
+ */
47
+ export function usbdDeviceLines(chip) {
48
+ if (!chip.usb)
49
+ return [];
50
+ const vid = chip.usb.vid ?? '0x2fe3';
51
+ const pid = chip.usb.pid ?? '0x0001';
52
+ // 1200-baud touch-to-reset (BOSSA-bootloader boards): a usbd message
53
+ // callback that reboots into the bootloader when the host sets the CDC
54
+ // baud rate to 1200. Zephyr's CDC-ACM class publishes
55
+ // USBD_MSG_CDC_ACM_LINE_CODING on every host SET_LINE_CODING (the message
56
+ // carries the CDC uart device; its line coding is readable via
57
+ // uart_cfg_get). The reboot writes the bootloader's stay-resident magic
58
+ // (Arduino SAMD scheme: the bootloader checks the last word of SRAM after
59
+ // reset) so the board waits in the bootloader instead of booting the app.
60
+ const touch = chip.usb.touchReset;
61
+ const touchLines = touch
62
+ ? [
63
+ `// 1200-baud touch-to-reset: reboot into the bootloader (flag 0x${touch.magic.toString(16)} @ 0x${touch.flagAddress.toString(16)}).`,
64
+ 'static void __tc_usbd_msg_cb(struct usbd_context* ctx, const struct usbd_msg* msg) {',
65
+ ' (void)ctx;',
66
+ ' if (msg->type != USBD_MSG_CDC_ACM_LINE_CODING) { return; }',
67
+ ' struct uart_config __tc_cfg;',
68
+ ' if (uart_config_get(msg->dev, &__tc_cfg) == 0 && __tc_cfg.baudrate == 1200) {',
69
+ ' // Delay the reboot so the host touch (which applies 1200 as part',
70
+ ' // of opening the port) can close its handle first — resetting under',
71
+ ' // an open host handle can wedge the Windows usbser driver and block',
72
+ ' // re-enumeration until a physical replug.',
73
+ ' k_msleep(250);',
74
+ ` *(volatile uint32_t*)0x${touch.flagAddress.toString(16)} = 0x${touch.magic.toString(16)};`,
75
+ ' NVIC_SystemReset();',
76
+ ' }',
77
+ '}',
78
+ ]
79
+ : [];
80
+ const touchRegister = touch
81
+ ? [' usbd_msg_register_cb(&__tc_usbd, __tc_usbd_msg_cb);']
82
+ : [];
83
+ return [
84
+ '// CUTTLEFISH_USBD_BEGIN',
85
+ 'USBD_DEVICE_DEFINE(__tc_usbd,',
86
+ ` DEVICE_DT_GET(DT_NODELABEL(${chip.usb.controller})),`,
87
+ ` ${vid}, ${pid});`,
88
+ 'USBD_DESC_LANG_DEFINE(__tc_usbd_lang);',
89
+ 'USBD_DESC_MANUFACTURER_DEFINE(__tc_usbd_mfr, "typecad");',
90
+ 'USBD_DESC_PRODUCT_DEFINE(__tc_usbd_product, "cuttlefish app");',
91
+ // Serial number from hwinfo: Windows keys USB devnodes without a serial
92
+ // on the physical port, so flash-cycle re-enumerations reuse stale
93
+ // port-keyed nodes (the "access denied" open wedge). Identity-based
94
+ // instance paths are stable across ports and never hit that pool.
95
+ 'USBD_DESC_SERIAL_NUMBER_DEFINE(__tc_usbd_sn);',
96
+ 'USBD_DESC_CONFIG_DEFINE(__tc_usbd_cfg_desc, "cuttlefish");',
97
+ 'USBD_CONFIGURATION_DEFINE(__tc_usbd_cfg, 0, 250, &__tc_usbd_cfg_desc);',
98
+ 'static bool __tc_usbd_started = false;',
99
+ ...touchLines,
100
+ 'static void __tc_usbd_start(void) {',
101
+ ' if (__tc_usbd_started) { return; }',
102
+ ' __tc_usbd_started = true;',
103
+ ' int err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_lang);',
104
+ ' if (err != 0) { printk("cuttlefish usb: lang descriptor failed: %d\\n", err); return; }',
105
+ ' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_sn);',
106
+ ' if (err != 0) { printk("cuttlefish usb: serial descriptor failed: %d\\n", err); return; }',
107
+ ' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_mfr);',
108
+ ' if (err != 0) { printk("cuttlefish usb: manufacturer descriptor failed: %d\\n", err); return; }',
109
+ ' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_product);',
110
+ ' if (err != 0) { printk("cuttlefish usb: product descriptor failed: %d\\n", err); return; }',
111
+ ' err = usbd_add_configuration(&__tc_usbd, USBD_SPEED_FS, &__tc_usbd_cfg);',
112
+ ' if (err != 0) { printk("cuttlefish usb: add configuration failed: %d\\n", err); return; }',
113
+ ' err = usbd_register_all_classes(&__tc_usbd, USBD_SPEED_FS, 1, NULL);',
114
+ ' if (err != 0) { printk("cuttlefish usb: register classes failed: %d\\n", err); return; }',
115
+ // Registered before usbd_init so no line-coding event can be missed.
116
+ ...touchRegister,
117
+ // usbd_init builds the descriptor tables from the registered
118
+ // configuration/classes and marks the context initialized — usbd_enable
119
+ // refuses with -EPERM ("not initialized") without it (sample_usbd_init.c
120
+ // calls it between setup and enable).
121
+ ' err = usbd_init(&__tc_usbd);',
122
+ ' if (err != 0) { printk("cuttlefish usb: init failed: %d\\n", err); return; }',
123
+ // Boards with VBUS detection start on the VBUS event in the Zephyr
124
+ // sample, via a message callback we do not register; enabling
125
+ // unconditionally (and reporting the error) is the sample's own path
126
+ // for boards without detection (STM32 OTG_FS).
127
+ ' err = usbd_enable(&__tc_usbd);',
128
+ ' if (err != 0) { printk("cuttlefish usb: enable failed: %d\\n", err); return; }',
129
+ ' printk("cuttlefish usb: device enabled\\n");',
130
+ '}',
131
+ '// CUTTLEFISH_USBD_END',
132
+ ];
133
+ }
134
+ /**
135
+ * Emit the per-instance CDC device + init helper. Called from shimLines when
136
+ * the program uses USB and the chip declares `usb`; must be paired with one
137
+ * usbdDeviceLines() block. The init starts the device stack (guarded) and
138
+ * applies the line coding.
139
+ */
140
+ export function usbInitLines(chip, instanceIndex) {
141
+ if (!chip.usb)
142
+ return [];
143
+ const p = prefix(instanceIndex);
144
+ const dev = `${p}_dev`;
145
+ return [
146
+ '// CUTTLEFISH_USB_BEGIN',
147
+ `static const struct device* ${dev} = DEVICE_DT_GET(DT_NODELABEL(${nodeLabel(instanceIndex)}));`,
148
+ `static void ${p}_init(void) {`,
149
+ ` __tc_usbd_start(); // CDC line coding is the host's business`,
150
+ `}`,
151
+ '// CUTTLEFISH_USB_END',
152
+ ];
153
+ }
154
+ /** Throw the board-capability diagnostic. Centralized so every op reads identically. */
155
+ function requireUsb(chip, op) {
156
+ if (!chip.usb) {
157
+ throw new Error(`framework-zephyr: \`${op}\` needs a USB device stack, but this board's chip data declares no \`zephyr.usb\` ` +
158
+ `(controller + cdcInstances). Boards whose Zephyr DTS has no enabled UDC controller node ` +
159
+ `(zephyr_udc0) cannot provide USB CDC serial.`);
160
+ }
161
+ }
162
+ /**
163
+ * Resolve a HAL usb.* op to Zephyr C++.
164
+ * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
165
+ */
166
+ export function lowerUsb(op, chip) {
167
+ requireUsb(chip, op.operation);
168
+ const o = op;
169
+ const idx = parseControllerIndex(o.port);
170
+ if (chip.usb && idx >= chip.usb.cdcInstances) {
171
+ throw new Error(`framework-zephyr: usb port "${o.port}" (instance ${idx}) is out of range — this board composes ` +
172
+ `${chip.usb.cdcInstances} CDC-ACM instance(s) (USB0..USB${chip.usb.cdcInstances - 1}).`);
173
+ }
174
+ const p = prefix(idx);
175
+ const dev = `${p}_dev`;
176
+ switch (op.operation) {
177
+ case 'usb.begin':
178
+ return { code: `${p}_init();` };
179
+ case 'usb.wait_ready': {
180
+ // Bounded DTR poll, sliced in the shim-side expression (no user-code
181
+ // busy loop). timeoutMs 0 = wait forever.
182
+ return { expression: `({ bool __ok = false; uint32_t __t = 0U; while (true) { uint32_t __dtr = 0U; if ((uart_line_ctrl_get(${dev}, UART_LINE_CTRL_DTR, &__dtr) == 0) && (__dtr != 0U)) { __ok = true; break; } if ((${s(o.timeoutMs ?? 0)}) != 0 && __t >= (uint32_t)(${s(o.timeoutMs ?? 0)})) { break; } k_msleep(10); __t += 10U; } __ok; })` };
183
+ }
184
+ case 'usb.end':
185
+ // CDC has no per-port disable short of tearing down the device stack
186
+ // (which would drop every other instance); make end observable but safe.
187
+ return { code: `(void)${dev};` };
188
+ case 'usb.print':
189
+ return { code: renderWrite(dev, o.value, false) };
190
+ case 'usb.println':
191
+ return { code: renderWrite(dev, o.value, true) };
192
+ case 'usb.read':
193
+ // Same poll semantics as uart.read: the byte or -1 if none available.
194
+ return { expression: `({ unsigned char __b = 0; (uart_poll_in(${dev}, &__b) == 0) ? (int)__b : -1; })` };
195
+ case 'usb.available':
196
+ // uart_poll_in reports only "one byte ready" and probing would drain it;
197
+ // same honest limitation as uart.available.
198
+ return { expression: '(0)' };
199
+ case 'usb.connected':
200
+ // DTR asserted = the host actually opened the port.
201
+ return { expression: `({ uint32_t __dtr = 0; (uart_line_ctrl_get(${dev}, UART_LINE_CTRL_DTR, &__dtr) == 0) && (__dtr != 0); })` };
202
+ default:
203
+ throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
204
+ `Open an issue or use rawCpp() to emit it manually.`);
205
+ }
206
+ }
@@ -9,7 +9,7 @@ export declare function wdtInitLines(chip: ZephyrChipDescriptor): string[];
9
9
  * Resolve a HAL wdt.* op to Zephyr C++.
10
10
  * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
11
11
  */
12
- export declare function lowerWdt(op: HALOpIR): {
12
+ export declare function lowerWdt(op: HALOpIR, chip: ZephyrChipDescriptor): {
13
13
  code?: string;
14
14
  expression?: string;
15
15
  };
@@ -10,12 +10,15 @@
10
10
  // constant or a number; we parse to ms in the lowering.
11
11
  // ---------------------------------------------------------------------------
12
12
  /** Parse a HAL wdt timeout ("250ms", WDTO_2S, or a bare number) to ms.
13
- * Tolerates undefined (the manifest validator's probe sends a minimal op). */
13
+ * Tolerates undefined (the manifest validator's probe sends a minimal op)
14
+ * and quote-wrapped strings (resolveSemanticArg can hand the literal's
15
+ * quoted source text through). */
14
16
  function timeoutToMs(timeout) {
15
17
  if (typeof timeout === 'number')
16
18
  return timeout;
17
19
  if (!timeout)
18
20
  return 1000; // default 1s when absent (e.g. the validator probe)
21
+ timeout = timeout.replace(/^['"]|['"]$/g, '');
19
22
  // Arduino WDTO_* constants.
20
23
  const wdto = {
21
24
  WDTO_15MS: 15, WDTO_30MS: 30, WDTO_60MS: 60, WDTO_120MS: 120,
@@ -51,13 +54,20 @@ export function wdtInitLines(chip) {
51
54
  * Resolve a HAL wdt.* op to Zephyr C++.
52
55
  * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
53
56
  */
54
- export function lowerWdt(op) {
57
+ export function lowerWdt(op, chip) {
55
58
  const o = op;
59
+ // No watchdog node on this target (e.g. SAM D21 — Zephyr's samd21 dtsi
60
+ // exposes none). Comment + (in profileDiagnostics) a clear error, mirroring
61
+ // the hwtimer unavailable pattern. Without this the wdt_* calls reference
62
+ // the shim vars that wdtInitLines (gated on chip.wdt) never emitted.
63
+ if (!chip.wdt) {
64
+ return { code: `/* ${op.operation}: no watchdog device on ${chip.id} */` };
65
+ }
56
66
  switch (op.operation) {
57
- case 'wdt.enable': {
58
- const ms = timeoutToMs(o.timeout);
59
- // Install a timeout then set up the watchdog. Both are idempotent via
60
- // the static flags. WDT_FLAG_RESET_CPU_CORE = full reset on timeout.
67
+ case 'wdt.setup': {
68
+ // Same install+setup sequence as wdt.enable, but the timeout is a
69
+ // plain ms number from construction no WDTO_*/string parsing.
70
+ const ms = Number(o.timeoutMs ?? 1000);
61
71
  return {
62
72
  code: [
63
73
  `if (!__tc_wdt_setup_done) {`,
@@ -69,7 +79,7 @@ export function lowerWdt(op) {
69
79
  ].join(' '),
70
80
  };
71
81
  }
72
- case 'wdt.reset':
82
+ case 'wdt.feed':
73
83
  return { code: `if (__tc_wdt_channel >= 0) { wdt_feed(__tc_wdt_dev, __tc_wdt_channel); }` };
74
84
  case 'wdt.disable':
75
85
  return { code: `wdt_disable(__tc_wdt_dev); __tc_wdt_setup_done = false;` };
@@ -5,8 +5,9 @@ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
5
5
  *
6
6
  * State is driven by two net_mgmt event handlers:
7
7
  * - L4 connectivity (NET_EVENT_L4_CONNECTED/DISCONNECTED) → connected flag.
8
- * This reflects IP connectivity (post-DHCP), not just the WiFi link — the
9
- * events are raised by conn_mgr's monitoring layer (conn_mgr_monitor.c).
8
+ * This reflects IP connectivity (post-DHCP, or right after static IPv4
9
+ * facts are applied) — the events are raised by conn_mgr's monitoring
10
+ * layer (conn_mgr_monitor.c).
10
11
  * - WiFi scan (NET_EVENT_WIFI_SCAN_RESULT/SCAN_DONE) → scan_results pool.
11
12
  */
12
13
  export declare function wifiInitLines(): string[];