Module device_node

Module device_node 

Source
Expand description

The device node: a full umsh-mac/umsh-node stack running on the device itself, alongside the ULCP session.

The device identity “exists even when no phone is attached” (ULCP spec §Identities); this module is what makes that true. It is an ordinary MAC + Host pump with the device’s constraints baked into DeviceNodePlatform:

  • Radio is a LoraphyRadio over the node’s virtual mux bundle (NODE_CH, mux client B) behind the shared duty-ledger gate: the session and the node share one physical radio through radio_mux and draw from one combined PROP_PHY_DUTY_LIMIT budget. A refused transmit is shed via the MAC’s CAD-backoff path rather than killing the pump.
  • Rng is a ChaCha20 CSPRNG seeded from the board’s hardware TRNG at boot (NodeRng): project policy forbids non-crypto RNGs, and under BLE builds the RNG peripheral is not ours to read at runtime.
  • The counter store is the board’s — the CS parameter — so TX reservation boundaries for the device identity and per-peer RX replay boundaries survive power cycles, flushed from inside the MAC pump one whole-map record per persist block.

The node always exists: a device identity is generated and persisted at first boot, so bring-up is unconditional and the only question is whether the node is transmitting. That is configuration — the PHY enable state and the forwarding switch — plus the NODE_ACTIVE gate, which closes while a factory reset is in flight (the identity has been erased from storage but the running MAC still holds it until the reboot that completes the wipe).

Beacon requests arrive through BEACON_TRIGGER rather than from any specific button handler: the trigger is an input, so a button press, bring-up, and the advertisement-policy timers in advert_loop all reach the radio by one path.

§Board seam

Embassy task functions cannot be generic, so the spawnable tasks stay in each firmware as thin shims around the *_loop functions here, and the board owns the two statics whose types depend on CS: the MAC cell and its counter store. Everything else — every static whose type is fixed, and every line of logic — is here once.

Structs§

AdvertPolicy
What the device announces without being asked, mirrored from the device domain’s advertisement-policy properties.
DeviceNodeParts
Everything bring-up produced, for the board to spawn its task shims around. Embassy tasks cannot be generic, so the spawning itself stays board-side.
DeviceNodePlatform
umsh_mac::Platform bundle for the device node, generic only over the board’s counter store — the one piece of the platform that is genuinely per-board, because it is backed by that board’s flash.
NodeHooks
Board couplings the node cannot express itself.
NodeRng
ChaCha20 CSPRNG adapter implementing the rand 0.10 traits the MAC requires (Platform::Rng: rand::CryptoRng). Seeded once at boot from the board’s hardware TRNG, exactly like the session’s IdentityRng, while that source is still ours to read.

Enums§

BeaconTrigger
Why a beacon was requested. Carried through BEACON_TRIGGER so the send path never assumes a button.

Statics§

BEACON_TRIGGER
Beacon requests into the node. On a boot that skipped node bring-up the queue is never drained and requests are dropped at the try_send in request_beacon, leaving the slot inert rather than blocking the caller.
DEV_SYNC
Latest-wins hand-off from the session driver to the sync loop. A Signal rather than a queue: intermediate table states are irrelevant, only convergence on the newest snapshot matters. On a boot that skipped node bring-up (a crash-report boot) a pending snapshot just sits here unconsumed.
NODE_CH
The node’s virtual radio bundle (mux client B). Static regardless of whether the node is running: the mux fans RX out to it either way, and a full queue just drops frames per the mux’s per-client policy.

Functions§

advert_loop
Emits the device’s unsolicited announcements: one beacon at bring-up under PROP_STARTUP_BEACON, then whatever PROP_ADVERT_INTERVAL and PROP_BEACON_INTERVAL ask for.
beacon_loop
Turns beacon triggers into node sends on the device identity: a signed advertisement either way — unsolicited for the button slot, echoing a nonce for an Advertisement Request.
bring_up
Construct the MAC around the device identity and wire up the node. Call at most once. The identity is never absent — boot generates and persists one when the journal is empty — so there is no “unprovisioned” path here.
dev_sync_loop
Reconciles the node’s MAC against each DevDomainSnapshot: joins newly provisioned channels, removes de-provisioned ones (dropping their replay state), and registers peers. Peer removal is not propagated — MAC registry entries carry no key material, so a stale entry is inert, and the registry is rebuilt from the live table at the next boot.
identity_blob_loop
Answers [IDENT_REQUEST] with the node’s current signed identity.
identity_profile_loop
Keeps the Identity Request responder’s profile name synced to the live device name. The responder builds replies synchronously and cannot await, so the current name is pushed in here on each change rather than read at reply time.
location_profile_loop
Keeps the advertised identity’s position synced to what the receiver has settled on, under PROP_GNSS_IDENT_UPDATE.
mac_counters
The most recently published MAC tallies.
pump_loop
Drives the device node’s MAC pump. Never returns while healthy; an exit means the MAC hit an unrecoverable radio error, and rebooting through the panic handler beats silently losing the device identity.
request_beacon
Fire-and-forget beacon request. A full queue means a beacon (or advertisement) is already pending, so dropping the extra request loses nothing — bursts of Advertisement Requests coalesce here.
set_device_name
Publish the live device name to the node. Call at boot and whenever the session’s device name changes.
set_tx_power_dbm
Publish the transmit power the board just applied to the radio.
sign_identity_blob
Build and sign this node’s identity blob into out, returning its length.
tx_power_dbm
The applied PROP_PHY_TX_POWER, or None before the first Effect::ApplyRadio.

Type Aliases§

DeviceNode
DeviceNodeHandle
DeviceNodeHost
DeviceNodeMac
Device-node MAC sized to the session’s device-domain tables, which are the only provisioning source it has: 1 identity (the device identity; no PFS ephemerals on the device node), MAX_DEV_PEERS peers, MAX_CHANNEL_KEYS channels (a smaller MAC table would refuse channels the property surface accepted), 4 pending ACKs, 4 TX slots (beacons and future acks — no application traffic), 255-byte frames, 32-entry dup cache. The per-channel replay maps are the RAM hot spot (~330 bytes per tracked sender): 4 full-key + 2 hint-only senders per channel keeps the whole table ~2 KiB/channel; extra concurrent senders on one channel fail closed (dropped, never accepted unchecked).
DeviceNodeMacCell
The StaticCell a board declares for its MAC. Board-side because its type depends on CS, and a static cannot be generic.
DeviceNodeRadio
The node’s radio path: its virtual mux bundle behind the shared duty-ledger admission gate.
NodeMutex