docs: present-state build and update docs; fix the ring-size arithmetic

- kas/README.md: the real-time rationale rests on the feeder's bounded
  queue depth, not ring size; the ring is 16 MiB (~84 s at 200 kHz,
  ~28 min at the 10 kHz cloud tick), a capacity for cloud-mode preload.
- BUILD.md, kas config, release checklist, cold-build workflow: only
  forgefirm and meta-openglow (branch scarthgap) are cloned as
  siblings; every ForgeFIRM source repo is fetched by pinned SRCREV.
- UPDATE-SYSTEM.md reads as the present-state design: the cloud-mode
  compatibility baseline is the cloud client's configured firmware
  version, not release metadata; decisions and open items listed
  plainly.
- README.md states what GRBL mode still needs the Glowforge service
  for (camera-referenced homing) and what runs without it.
- BRINGUP.md: generic build-host and fwup-lab references, the retained
  reproductions of the no-fire drill, the System tab.
- LIGHTBURN.md: the arm-window timeouts are machine settings.
- forgefirm-image.bb describes forgectrl as the machine-services
  daemon and points at the right backlog entry.
- American spelling throughout.
This commit is contained in:
ScottW514
2026-08-15 06:12:51 -04:00
parent ff117c4f6b
commit 0a05b6b114
10 changed files with 141 additions and 142 deletions
+4 -10
View File
@@ -30,10 +30,10 @@ jobs:
with:
path: forgefirm
# The kas config references meta-openglow as a local sibling, and
# meta-openglow's kernel-module bbappend uses an externalsrc sibling
# (both by design during active BSP development - kas/README.md
# "Push & release order" step 4 flips these at release time).
# The kas config references meta-openglow as a local sibling
# (kas/README.md "Push & release order" step 4 flips it to the
# pinned-remote block at release time). Every source repo the recipes
# build is fetched by pinned SRCREV; no other sibling is needed.
- name: Checkout meta-openglow (sibling)
uses: actions/checkout@v4
with:
@@ -41,12 +41,6 @@ jobs:
ref: scarthgap
path: meta-openglow
- name: Checkout kernel-module-glowforge (sibling)
uses: actions/checkout@v4
with:
repository: ScottW514/kernel-module-glowforge
path: kernel-module-glowforge
- name: Host dependencies
run: |
sudo apt-get update -qq
+9 -7
View File
@@ -26,20 +26,21 @@ Do not build as root (the Yocto sanity checks refuse it).
## Get the sources
Clone the repos as siblings (the kas config references `meta-openglow` at
`../meta-openglow` while it is under active Scarthgap migration):
Clone the two repos as siblings (the kas config references `meta-openglow`,
branch `scarthgap`, at `../meta-openglow`; kas fetches the upstream Yocto
layers itself, and every ForgeFIRM source repo — the kernel module, the
controller, the daemon, the cloud apps — is fetched by its recipe at a pinned
revision):
```console
git clone https://github.com/ScottW514/forgefirm.git
git clone https://github.com/ScottW514/meta-openglow.git
git clone https://github.com/ScottW514/kernel-module-glowforge.git
git clone -b scarthgap https://github.com/ScottW514/meta-openglow.git
```
```
openglow-forgefirm/
├── forgefirm/ ← base repo, build runs here
├── meta-openglow/
└── kernel-module-glowforge/
└── meta-openglow/
```
## Build the image
@@ -86,5 +87,6 @@ cd build/tmp/deploy/images/glowforge
sudo zcat forgefirm-image-glowforge.rootfs.wic.gz | dd of=/dev/sdX bs=1M
```
To install onto the factory eMMC (dual-boot with the Glowforge firmware), see
To install onto the factory eMMC (into the unused A/B slot, with the factory
firmware archived first — one OS runs at a time), see
[`INSTALL.md`](INSTALL.md).
+6 -3
View File
@@ -24,7 +24,9 @@ machine is idle:**
for each job.
* **Cloud mode** — the machine presents itself as a stock Glowforge to the
Glowforge web service, so the phone and web apps work as they always did.
Optional, and off by default; nothing about GRBL mode needs it.
Optional, and off by default. GRBL mode jogs and cuts without it; the one
GRBL-mode function that still reaches the Glowforge service is
camera-referenced homing (below), until limit-switch homing lands.
**Around both modes:**
@@ -34,8 +36,9 @@ machine is idle:**
* Both **cameras** as MJPEG streams and full-resolution snapshots — the lid
camera feeds LightBurn's camera overlay directly.
* **Camera-referenced homing**: `$H` from any sender runs the factory-style
camera homing cycle through the Glowforge service, and the machine records
where it is.
camera homing cycle through the Glowforge service (a Glowforge account and a
live service session are required for `$H`; everything else in GRBL mode
runs without them), and the machine records where it is.
* **Installs alongside the factory firmware** in the unused A/B rootfs slot,
archiving every factory version first, so the machine can be switched back
to stock at any time without the Glowforge cloud.
+30 -22
View File
@@ -728,16 +728,16 @@ item 8.
at startup regardless), and `vs-supply = <&reg_3p3v>` on the lm75
node (was the last queued cosmetic "dummy regulator" probe line
besides the two SoC USB PHYs). Nothing else queued.
- **Build host**: WSL2 distro `forge-yocto`, tree at
`~/dev/openglow-forgefirm`. `~/src-sync.sh` rsyncs the Windows repos in
(includes `python3-gfhardware` and `grblHAL-glowforge`). Build:
`cd ~/dev/openglow-forgefirm/forgefirm && kas shell
kas/forgefirm-glowforge.yml -c 'bitbake forgefirm-image
forgefirm-image-dev'`. Artifacts:
- **Build host**: a Linux build environment (a WSL2 distro works)
holding the `forgefirm` + `meta-openglow` sibling checkout (`BUILD.md`);
the ForgeFIRM source repos are fetched by pinned `SRCREV`. Build:
`cd forgefirm && kas shell kas/forgefirm-glowforge.yml -c 'bitbake
forgefirm-image forgefirm-image-dev'`. Artifacts:
`forgefirm/build/tmp/deploy/images/glowforge/`.
- **fwup lab (host)**: `~/fwup-lab/bin/` holds host-built `fwup-0.14.2`
(factory-era) and `fwup-v1.16.0`; `~/fwup-lab/devkeys/fwup-key.{priv,pub}`
is the DEV signing keypair (`fwup-key-raw.pub` = raw 32-byte form —
- **fwup lab (host)**: a host directory (`<fwup-lab>` below) holds
host-built `fwup-0.14.2` (factory-era) and `fwup-v1.16.0` under `bin/`
and the DEV signing keypair `devkeys/fwup-key.{priv,pub}`
(`fwup-key-raw.pub` = raw 32-byte form —
what fwup 0.14.2 expects; 1.x reads both). Cross-version compat is
proven both ways (modern-packed signed archives apply with 0.14.2;
modern fwup verifies+applies the factory .fw — signer key
@@ -746,12 +746,12 @@ item 8.
release key is held offline by the operator** — the installer embeds
its public key, so releases sign with that key only.
Pack releases with `scripts/mkfw.sh`; the full pipeline is
`scripts/release.sh`, invoked on this host as:
`FWUP=~/fwup-lab/bin/fwup-v1.16.0 FWUP_COMPAT=~/fwup-lab/bin/fwup-0.14.2
FORGEFIRM_DEV_KEY=~/fwup-lab/devkeys/fwup-key.priv
`scripts/release.sh`, invoked as:
`FWUP=<fwup-lab>/bin/fwup-v1.16.0 FWUP_COMPAT=<fwup-lab>/bin/fwup-0.14.2
FORGEFIRM_DEV_KEY=<fwup-lab>/devkeys/fwup-key.priv
FORGEFIRM_SIGNING_KEY=<release key> RELEASE_STAGING_DIR=<dir>
./scripts/release.sh <version>` (gh for the publish step lives on the
Windows side; release.sh prints the exact command).
./scripts/release.sh <version>` (the publish step needs an
authenticated `gh`; release.sh prints the exact command).
- **Shell gotchas** (cost real time): PowerShell mangles embedded double
quotes in git-commit here-strings (avoid `"` in messages); `wsl -- bash
-c '...'` eats `$VAR` expansions (use script files run via PowerShell,
@@ -759,7 +759,7 @@ item 8.
## Running the controller (grblHAL-glowforge on the board)
Source: `C:\dev\openglow-forgefirm\grblHAL-glowforge` — the **canonical
Source: the `grblHAL-glowforge` sibling repo — the **canonical
grblHAL driver repo** (github.com/ScottW514/grblHAL-glowforge, branch
`main`): core as a submodule at `src/grbl` (→ ScottW514/core fork, branch
`forgefirm` = **upstream master + the step_us_min buffer fix pending
@@ -783,9 +783,11 @@ and maps step events to pulse bytes; a SCHED_FIFO shipper feeds
for interrupt masking. `GFSINK` unset = null-sink mode (full engine, no
hardware I/O — host testing).
1. Build: `wsl -d forge-yocto -- bash <repo>/forgefirm/scripts/bench/build-glowforge.sh`
(from PowerShell). Produces `build-arm/grblHAL_glowforge` in the WSL
tree (`-O1 -g`; machine constants live in `src/boards/glowforge.h`,
1. Build: `bash <repo>/forgefirm/scripts/bench/build-glowforge.sh` in
the build environment (from Windows, launch it through the WSL
distro from PowerShell — Git Bash mangles `/mnt/c` paths). Produces
`build-arm/grblHAL_glowforge` in the checkout (`-O1 -g`; machine
constants live in `src/boards/glowforge.h`,
force-included into the core: 53.333 µsteps/mm XY @ ×8, 2.832
half-steps/mm Z, 0.417" Z travel, 12000 mm/min max, 700/590 mm/s²
accel — factory-derived, see `puls_profile.py`).
@@ -843,7 +845,7 @@ jogs, $0 min 35.5 intact, $H rejected ($22=0); forgectrl streams
## The machine-services daemon (forgectrl, port 8080)
Source: `C:\dev\openglow-forgefirm\forgectrl` — the **canonical repo**
Source: the `forgectrl` sibling repo — the **canonical repo**
(github.com/ScottW514/forgectrl, branch `main`, MIT). forgectrl is the
ForgeFIRM machine-services daemon: **controller-mode supervision** (it
spawns exactly one of grblHAL / gfcloud as a direct child, respawns on
@@ -872,7 +874,9 @@ serves it all, including both OV5648 cameras as MJPEG over the
mainline imx-media pipeline:
- `GET /` — the tabbed machine control panel (Status / Machine /
GF Cloud / GRBL / Diagnostics; ui.c): status page with the
GF Cloud / GRBL / Diagnostics / System; ui.c — System carries the
A/B slot selection, ForgeFIRM updates, image install/restore, the
wireless regulatory region, and reboot): status page with the
controller-mode selector (live switch through the supervisor; the
setting persists for boot), the operational dashboard, a scaled lid snapshot +
on-demand live stream, and the settings forms for display units,
@@ -1389,7 +1393,11 @@ accordingly ("Automatic — AP country, else World").
insertions), and 534 dark steps after the last fire bit = the
entire G0 return.
- **On-board no-fire verification 15/15 PASS** (chain unarmed,
nobody at the button; `laser_arm_test.py` drill): latch locked
nobody at the button; the drill script was a bench one-off and
is not retained — the arm-window state machine is reproduced
host-side by `scripts/bench/laser_lifecycle_test.py` and grblHAL's
`tests/laser_arm_test.c`, and the latch readbacks on hardware by
`gate_a_kernel_drills.py` and `live_fire_drills.py`): latch locked
at idle and through jogs (interlock_circuit 13), M4 → prompt +
latch unlocked (5) + button LED white + run fans forced +
status served during the wait, soft-reset abort relocks + LED
@@ -1590,7 +1598,7 @@ accordingly ("Automatic — AP country, else World").
equilibrates near ambient and the heater cannot reach a
cutting-session loop temperature — 100 % duty drives the
downstream sensor past 50 °C in 30 s while the bulk barely
moves). Behaviour at 27–32 °C baselines, and under real laser
moves). Behavior at 27–32 °C baselines, and under real laser
heating, must be characterized at first light. Physics argues
the dependence is weak — with forced flow ΔT = P/(ṁ·c), which
carries no absolute-temperature term — but that is reasoning,
+3 -1
View File
@@ -38,7 +38,9 @@ The laser fires only inside an operator-armed window:
`M2`/`M30`), when the sender connection changes, or after
`laser_disarm_s` (default 60 s) with the spindle off — counting even
while a job sits paused in Hold or with the lid open; the next job
prompts again.
prompts again. Both timeouts are machine settings (keys in
`/data/forgefirm.conf`, set through the control panel's settings API);
the defaults suit normal use.
- S-value scale: `$30` defaults to 1000, so set LightBurn's S-max to
1000. 100 % power = S1000. Use M4 (variable/dynamic) mode for cuts
and engraves.
+52 -62
View File
@@ -1,11 +1,12 @@
# ForgeFIRM install, update & recovery system — implementation plan
# ForgeFIRM install, update & recovery system
Phased plan for moving ForgeFIRM from the legacy carve-out-a-partition
install to the factory's own A/B slot scheme, with signed `.fw`
packaging, a GUI update manager, factory restore, and a refreshed
recovery image. The measured ground truth this builds on (eMMC layout,
boot0/boot1 maps, saved-env location, factory `.fw`/updater internals)
is in `BRINGUP.md` → "eMMC boot & recovery architecture".
Design and contracts of the ForgeFIRM install/update/recovery system:
the factory's own A/B slot scheme, signed `.fw` packaging, the GUI
update manager, factory restore, and the recovery image. The system is
described in the implementation units ("phases") it is built from; the
bench status of each lives in `BRINGUP.md`, as does the measured ground
truth this rests on (eMMC layout, boot0/boot1 maps, saved-env location,
factory `.fw`/updater internals — "eMMC boot & recovery architecture").
## Settled decisions
@@ -152,15 +153,14 @@ demonstrably untouched.*
- One version source: `FORGEFIRM_RELEASE` = git tag =
`/etc/forgefirm-version` = `.fw` meta-version; the script enforces
agreement.
- `tested_against_gf`: pinned release metadata naming the Glowforge
service/firmware version this release validated optional cloud mode
against. `release.sh` sets it beside `FORGEFIRM_RELEASE` and writes
it into the `/etc/forgefirm-version` companion and the `.fw` fwup
meta. It is deliberately distinct from the version cloud mode
advertises to the service; forgectrl reads it to warn when the live
Glowforge service has advanced past the tested version (cloud mode
may break), and falls back to the advertised version on images that
predate the field.
- Cloud-mode compatibility baseline: the cloud client's connect-time
probe records `{latest_gf_version, tested_against_gf}` to
`/data/forgefirm/gf-latest.json`, and forgectrl's panel warns when
the live Glowforge service has moved past the tested version (cloud
mode may break). `tested_against_gf` is the cloud client's configured
firmware version (`FACTORY_FIRMWARE.FW_VERSION`, the same value it
advertises to the service); it is **not** release metadata — neither
`release.sh` nor the `.fw` meta carries such a field.
- `release.sh --dev` packs a **dev-key-signed** `forgefirm-dev.fw`
from the release rootfs for the GUI upload path (decides open
question 4: dev archives are signed with the dev key, never
@@ -170,7 +170,7 @@ demonstrably untouched.*
forgectrl (minutes, no Yocto); optional `workflow_dispatch`
cold-Yocto reproducibility build whose only product is a checksum.
## Phase 4 — forgectrl update manager (GUI) — IMPLEMENTED
## Phase 4 — forgectrl update manager (GUI)
Endpoints in `forgectrl/src/update.c`, driven from the panel's System
tab; trust anchors in `/etc/forgefirm/keys` (`forgefirm-keys` recipe:
@@ -183,17 +183,11 @@ refuse the booted root slot, verify signature before writing, and
re-verify the written filesystem. `GET /slots` inventory, `POST /boot`
(probe-gated), `POST /update/{check,download,apply,upload}`,
`POST /restore/factory` (archive md5 checked), `POST /system/reboot`.
**Bench-verified end-to-end 2026-08-08** (slot b as scratch, no
reboots): production-signed upload classified `forgefirm` and applied
(`signed:true`); dev-signed classified `unsigned`, apply refused until
`confirm_unsigned=1`; factory-2022 restore md5-verified and written;
boot-select flipped and reverted without reboot; path-traversal /
bogus-target / booted-root-slot writes all refused; `/slots` inventory
matched the physical layout. Original design notes below.
Every state-changing call is behind forgectrl's auth layer (bearer
token + origin checks; unsigned installs additionally require the
physical button held).
Backend endpoints + a panel page (OpenGlow visual identity):
Functions of the panel page:
- **Inventory**: slot contents (Phase 1 probe), current/next boot
selection, archive presence/version.
@@ -218,7 +212,7 @@ Backend endpoints + a panel page (OpenGlow visual identity):
*Exit: full loop on the bench — GUI upgrade, rollback via boot
selector, factory restore and return — without touching a shell.*
## Phase 5 — recovery refresh (reserved; build after 0–4 land)
## Phase 5 — recovery refresh (not yet built)
- **v1 scope**: replace only the boot0 recovery squashfs (boot1 `/usr`
only if needed). Never write below offset 0xC0000 in boot0 — U-Boot
@@ -252,42 +246,38 @@ selector, factory restore and return — without touching a shell.*
`factory-rootfs-<ver>.img.gz`, `boot0.img`, `boot1.img`,
`manifest` (slot versions, dates, checksums).
## Open questions / decision gates
## Decisions
1. **RESOLVED** (Phase 0): uEnv.txt keeps its `mmcargs` override with
`root=${mmcroot}` — slot-agnostic, hardware-verified.
2. **RESOLVED** (Phase 0): modern-fwup-packed signed archives apply
with the factory 0.14.2 binary (raw 32-byte pubkey form); no
shipped fwup needed on the factory side.
3. **RESOLVED** (Phase 3): size gates live in two layers — bitbake
fails past the 200 MiB slot; release.sh warns ≥ 170 MiB and fails
≥ 195 MiB.
4. **RESOLVED** (Phase 3): dev archives are always signed with the
dedicated dev key (`release.sh --dev`), never unsigned.
8. **RESOLVED** — production signing-key ceremony executed: key
generated on the build host (never in the repo, CI, or
cloud-synced plaintext; the build-host copy is the only online
copy, offline backups held by the operator), public key embedded
in the installer, and the chain verified: production-signed
archives verify with fwup 1.16 and the factory's 0.14.2 (raw
pubkey form); dev-signed archives are rejected. Custody optimizes
against compromise over loss: loss means users re-run a fresh
installer; compromise means attacker-signed firmware on fielded
machines.
5. Periodic GUI update check default-on vs opt-in (it pings the GitHub
API; proposal: on by default, apply always manual, config switch to
disable).
6. Recovery kernel modules: carried from the factory image vs rebuilt
from GPL source (Phase 5 gate).
7. U-Boot bootcount/auto-revert: **out of scope** — the recovery
ladder covers bad flips; revisit only if field incidents say
otherwise.
- uEnv.txt keeps its `mmcargs` override with `root=${mmcroot}` — the
image is slot-agnostic, steered only by the saved env.
- Modern-fwup-packed signed archives apply with the factory 0.14.2
binary (raw 32-byte pubkey form); no shipped fwup is needed on the
factory side.
- Size gates live in two layers: bitbake fails past the 200 MiB slot;
`release.sh` warns ≥ 170 MiB and fails ≥ 195 MiB.
- Dev archives are always signed with the dedicated dev key
(`release.sh --dev`), never unsigned.
- Production signing key: held offline by the operator (never in the
repo, CI, or cloud-synced plaintext), public key embedded in the
installer. Production-signed archives verify with fwup 1.16 and the
factory's 0.14.2 (raw pubkey form); dev-signed archives are rejected.
Custody optimizes against compromise over loss: loss means users
re-run a fresh installer; compromise means attacker-signed firmware
on fielded machines.
- U-Boot bootcount/auto-revert is out of scope — the recovery ladder
covers bad flips.
## Sequencing
## Open items
0 → 1 → 2 + 2b → 3 → 4 → 5, strictly: everything after Phase 0 assumes
- Periodic GUI update check default-on vs opt-in (it pings GitHub;
proposal: on by default, apply always manual, config switch to
disable).
- Recovery kernel modules: carried from the factory image vs rebuilt
from GPL source (Phase 5 gate).
## Dependencies between the phases
0 → 1 → 2 + 2b → 3 → 4 → 5: everything after Phase 0 assumes
slot-agnostic images and working `.fw` round-trips; the GUI (4) reuses
the probe (1) and pipeline (3); recovery (5) is an independent
mini-project once the slot scheme is provenly stable. First
user-visible milestone is after Phase 2: a converted machine with
instant offline factory switchback.
mini-project on top of the stable slot scheme.
+24 -19
View File
@@ -29,15 +29,15 @@ openglow-forgefirm/
│ ├── build/ ← bitbake output incl. images (gitignored)
│ ├── downloads/ sstate-cache/ ← caches (gitignored)
│ └── .gitignore
├── meta-openglow/ ← Glowforge BSP layers (local sibling, under migration)
└── kernel-module-glowforge/ ← glowforge.ko sources (pulled by recipe SRC_URI)
└── meta-openglow/ ← Glowforge BSP layers (local sibling checkout)
```
`meta-openglow` is referenced as a **local sibling** (`../meta-openglow`) while
we migrate it to Scarthgap, so its in-place edits are what gets built. Once that
migration is committed/pushed, flip it to a kas-cloned + pinned repo (commented
block in `forgefirm-glowforge.yml`) and the forgefirm repo becomes fully
self-contained.
`meta-openglow` is referenced as a **local sibling** (`../meta-openglow`), so
its in-place edits are what gets built. The commented pinned-remote block in
`forgefirm-glowforge.yml` makes the forgefirm repo fully self-contained when
flipped on. The source repos the recipes build (`kernel-module-glowforge`,
`grblHAL-glowforge`, `forgectrl`, `python3-gfhardware`, `Glowforge-Utilities`)
are fetched by pinned `SRCREV` and are not needed as local checkouts.
## Prerequisites
@@ -122,7 +122,7 @@ config move in the right order. The sequence, with current status:
All recipes fetch their pinned revision from GitHub, so an image build is
reproducible from the repos alone. For fast iteration on a source repo, bump
its pin per iteration, or add a **local, untracked** `externalsrc` bbappend
pointing at the sibling checkout — never commit one, or released images stop
pointing at a working checkout — never commit one, or released images stop
matching the pins.
## Scarthgap migration backlog
@@ -158,8 +158,8 @@ Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until:
- **`glowforge.ko`**: ported across many 6.12 API changes
(`tasklet_hrtimer`→soft hrtimer, `timer_setup`, LED-trigger API,
`pwm_get`, `spi_delay`/`controller`, `filelock.h`, void `.remove`,
1-arg i2c probe). Compiles + links, 0 undefined symbols. Built from the
local sibling via an `externalsrc` bbappend during migration.
1-arg i2c probe). Compiles + links, 0 undefined symbols; the recipe
fetches the module by pinned `SRCREV`.
- **DT**: `glowforge,cnc/thermal/pic/head` re-added with `pwms`/`pwm-names`
phandles; `glowforge.dtb` compiles with all motion nodes.
Motion polish: PWM prescaler (factory 1001) is **obsolete** — 6.12
@@ -173,7 +173,7 @@ Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until:
- **Camera — DONE and hardware-validated.** The factory
`ov5648_mipi.c` (NXP's removed `v4l2_int_device`/`mxc_v4l2_capture`) is
replaced by the mainline `ovti,ov5648` subdev + imx6 `imx-media` (IPU CSI)
+ `imx6-mipi-csi2` receiver. The factory CAM_SEL MIPI switch is modelled
+ `imx6-mipi-csi2` receiver. The factory CAM_SEL MIPI switch is modeled
with the mainline `video-mux` (gpio-mux on `gpio7 10`): both sensors →
video-mux → `mipi_csi` → IPU CSI. Sensor `xvclk` is the board's 24 MHz
fixed oscillator (matching the factory DTB); avdd/dovdd/dvdd rails are in
@@ -202,14 +202,19 @@ Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until:
5. **Real-time strategy — decided.** The kernel runs
`CONFIG_PREEMPT=y` (factory behavior; `imx_v6_v7_defconfig` alone gives only
`PREEMPT_VOLUNTARY`). **PREEMPT_RT is not selectable on arm32 6.12** (no
`ARCH_SUPPORTS_RT`) and is **not needed for the pulse feeder**: the SDMA
ring is 128 MiB draining at 1 byte per EPIT tick — ≤200–400 KB/s even at
the 200 kHz ceiling — so a full ring holds **~5–11 minutes** of stream and
a modest 1 MiB of queued data already rides out ~3–5 s of scheduling
latency, orders of magnitude beyond anything PREEMPT exhibits. Deep
buffering + `SCHED_FIFO` for the feeder is the design; revisit RT only if
the underrun bench ever contradicts this arithmetic. (Bench: 5 s of
continuous feed at a 1 s buffer depth, zero underruns.)
`ARCH_SUPPORTS_RT`) and is **not needed for the pulse feeder**. The
argument is about queue depth, not ring size: the ring drains at 1 byte
per EPIT tick (≤200 KB/s even at the 200 kHz ceiling), so the live
feeder's bounded queue depth of ~150 ms — a few KB in flight — already
rides out worst-case scheduling latency with orders of magnitude to spare
(measured: 0.2 ms worst write latency under full CPU + I/O load; the
underrun bench ran 100 kHz for 120 s with zero underruns). The ring
itself is 16 MiB (the `ring_mb` module parameter, backed by the 16 MiB
reserved pool): ~84 s of stream at 200 kHz, ~28 min at the 10 kHz
cloud-mode tick — a capacity that matters for the whole-job preload of
cloud mode, not for latency. Bounded queue depth + `SCHED_FIFO` for the
feeder is the design; revisit RT only if the underrun bench ever
contradicts this arithmetic.
6. **gfui-client → forgectrl — DONE.** The stock `gfui-client` is excluded
from `forgefirm-image` (`IMAGE_INSTALL:remove = "gfui-client"` in
+7 -13
View File
@@ -73,11 +73,10 @@ repos:
.:
# --- meta-openglow — Glowforge BSP layers ---------------------------------
# ACTIVE DEVELOPMENT: referenced as a local sibling checkout (no url => kas
# performs no git ops, so the scarthgap-migration edits we're making are what
# gets built). The kernel-module-glowforge sources are NOT a layer; they are
# pulled by the kernel-module-glowforge.bb recipe's SRC_URI, so kas does not
# manage them here.
# Referenced as a local sibling checkout (no url => kas performs no git ops,
# so in-place BSP edits are what gets built). The kernel-module-glowforge
# sources are NOT a layer; the kernel-module-glowforge.bb recipe fetches them
# by pinned SRCREV, so kas does not manage them here.
meta-openglow:
path: ../meta-openglow
layers:
@@ -85,11 +84,8 @@ repos:
meta-glowforge-bsp:
# meta-openglow-bsp (separate OpenGlow_std board) intentionally excluded.
#
# FUTURE — flip to this pinned-remote block at release time (gated on
# active BSP development settling — see kas/README.md "Push & release order".
# When flipping, also drop meta-openglow's kernel-module-glowforge.bbappend
# (externalsrc to the local sibling) so a fresh clone is self-contained; its
# perl-native DEPENDS is already carried in the base recipe):
# Pinned-remote alternative for a fully self-contained clone (see
# kas/README.md "Push & release order"):
# meta-openglow:
# url: https://github.com/ScottW514/meta-openglow.git
# branch: scarthgap # pin via kas lock / a tag at release
@@ -122,9 +118,7 @@ local_conf_header:
# release.sh gates the release rootfs against a passwordless root entry.
build-tweaks: |
# Parallelism for the 12-core / 16 GB WSL2 VM. (The earlier mid-build deaths
# were the mirrored-networking vsock relay, not memory — see .wslconfig — so
# this can comfortably use more of the VM.)
# Parallelism sized for a 12-core / 16 GB build VM; raise on larger hosts.
BB_NUMBER_THREADS = "8"
PARALLEL_MAKE = "-j 8"
# Caches kept inside the forgefirm repo (gitignored):
@@ -5,13 +5,14 @@ DESCRIPTION = "OpenGlow/ForgeFIRM image for Glowforge"
# ForgeFIRM cuts the cloud dependency: drop the Glowforge cloud client
# (gfui-client connects to Glowforge's servers). Removing it here (override
# only; the shared glowforge-image base is untouched) also sidesteps its
# do_package failure. Its slot is filled locally by forgectrl (camera MJPEG
# service; the grblHAL controller recipe is tracked in kas/README.md
# backlog #5).
# do_package failure. Its role is filled locally by forgectrl and the
# controllers below (kas/README.md backlog #6).
IMAGE_INSTALL:remove = "gfui-client"
# grblhal-glowforge: the grblHAL motion controller (Grbl over TCP:23).
# forgectrl: the ForgeFIRM control daemon (camera MJPEG service on :8080).
# forgectrl: the ForgeFIRM machine-services daemon (HTTP :8080): controller
# supervisor, pulse-device broker, cooling engine, cameras, telemetry,
# settings, diagnostics, web control panel, and A/B updates.
# gfhome: one-shot Glowforge web-service homing, invoked by the controller
# for $H when homing_mode = gfcloud (/data/forgefirm.conf).
# gfcloud: full Glowforge web-service controller daemon (the factory cloud
+1 -1
View File
@@ -186,7 +186,7 @@ cat <<EOF
Pre-publish checklist (kas/README.md "Push & release order" step 4):
- meta-openglow pushed; kas config flipped to the pinned-remote block
- externalsrc bbappend dropped; kas lock refreshed
- kas lock refreshed
- self-containment proven from a fresh clone
Publish (from a directory with an authenticated gh):