Skip to content

Kestrel: 802.11ax trigger-based UL, TWT, and HE sounding#299

Merged
josephnef merged 3 commits into
masterfrom
kestrel-236-trigger-ul-twt-command-surface
Jul 17, 2026
Merged

Kestrel: 802.11ax trigger-based UL, TWT, and HE sounding#299
josephnef merged 3 commits into
masterfrom
kestrel-236-trigger-ul-twt-command-surface

Conversation

@josephnef

Copy link
Copy Markdown
Collaborator

Adds the 802.11ax uplink-scheduling surface to the Kestrel HAL (RTL8852BU/8832BU, RTL8852CU/8832CU): the firmware command encoders, sounding hardware init and per-STA beamformee registration, and a host-injected Basic Trigger path that puts a real HE Trigger on the air.

Two transmit paths

Firmware-scheduled — F2P trigger, UL_FIXINFO, and the CL_SOUND sounding sequence (NDPA → NDP → BFRP), byte-exact against the vendor mac_ax encoders (v0 8852B / v1 8852C) and firmware-accepted (no SER). The shipped client firmware does not run the trigger-transmit scheduler: F2P is silently dropped, and the sounding command is accepted and engaged but not transmitted. Confirmed on-air — an independent monitor decoding 139k frames from the AP and station saw zero Trigger frames while the sounding command fired.

Host-injected (SendTrigger, default) — build_basic_trigger() assembles the raw HE Trigger bytes on the host and transmits them through the normal management path, bypassing the firmware scheduler. This airs a real, correctly-decodable HE Trigger: a monitor decoded the injected triggers with the exact commanded parameters (AP power, AID, RU, MCS, NSS). Group-addressed triggers are marked BMC in the TX descriptor so the firmware transmits them once instead of retrying an unacknowledged broadcast.

The host-injected trigger does not by itself elicit a hardware SIFS-timed TB PPDU response — a host-built frame does not arm the MAC receive window, and that half needs a firmware-scheduled trigger the client firmware does not air. The trigger-on-air path is the basis for an adaptive uplink (the per-user RU/MCS/target-RSSI grant is a closed-loop control channel); see docs/he-trigger-ul.md.

API

IRtlDevice (default-false on pre-AX generations): SendTrigger, ConfigureUlOfdma, ConfigureTwt/TwtBindSta/ConfigureTwtOfdma, StartSounding/RegisterBeamformee, RegisterPeerSta; AdapterCaps.{trigger_ul_ok, twt_ok, sounding_ok}.

Testing

Headless golden-byte encoder checks under ctest (kestrel_sched; 26/26 green). On-air: tests/ul_sounding_e2e.sh brings up a devourer HE software-AP on a Kestrel adapter, associates a real rtw89 station, and drives the sounding command (default) or host-injection (INJECT=1), with a separate-bus monitor and a USRP B210 as witnesses.

🤖 Generated with Claude Code

josephnef and others added 3 commits July 17, 2026 13:20
Adds the 802.11ax uplink-scheduling surface to the Kestrel HAL (RTL8852/8832):
firmware command encoders (F2P trigger, UL_FIXINFO, TWT info/act/announce/OFDMA,
and the CL_SOUND SET_SND_PARA sounding sequence — v0 8852B / v1 8852C), the
sounding hardware init and per-STA beamformee registration, and a host-injected
Basic Trigger path. The encoders are pure and byte-exact against the vendor
mac_ax layouts, unit-tested under ctest (kestrel_sched).

Two transmit paths, which behave differently on the shipped client firmware:

- Firmware-scheduled (F2P, UL_FIXINFO, sounding): byte-exact and firmware-
  accepted (no SER). The client firmware does not run the trigger-transmit
  scheduler — F2P is silently dropped, and the sounding command is accepted and
  engaged but not aired. Confirmed on-air: a monitor decoding 139k frames from
  the AP and station saw zero Trigger frames while sounding fired.

- Host-injected (SendTrigger, default): build_basic_trigger() assembles the raw
  HE Trigger bytes on the host and transmits them through the normal management
  path, bypassing the firmware scheduler. This airs a real, correctly-decodable
  HE Trigger — a monitor decoded the injected triggers with the exact commanded
  parameters. Group-addressed triggers are marked BMC in the TX descriptor so the
  firmware transmits them once instead of retrying an unacked broadcast.

The host-injected trigger does not by itself elicit a hardware SIFS-timed TB PPDU
response (a host-built frame does not arm the MAC receive window); that half needs
a firmware-scheduled trigger the client firmware does not air. The trigger-on-air
path is the basis for an adaptive uplink — the per-user RU/MCS/target-RSSI grant
is a closed-loop control channel, used software-timed between two devourer
endpoints. See docs/he-trigger-ul.md.

Device API (IRtlDevice, default-false on pre-AX): SendTrigger, ConfigureUlOfdma,
ConfigureTwt/TwtBindSta/ConfigureTwtOfdma, StartSounding/RegisterBeamformee,
RegisterPeerSta; AdapterCaps trigger_ul_ok/twt_ok/sounding_ok. Validation:
tests/ul_sounding_e2e.sh (AP + rtw89 STA + B210/monitor witnesses, sounding and
INJECT modes) plus the kestrelprobe/rxdemo demos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…air status

Land the proven half of the trigger round-trip investigation and drop the
rest as dead code.

build_basic_trigger (host-injected HE Basic Trigger, the path that actually
airs a Trigger on the shipped client firmware) was not standards-complete:
 - UL Length (Common Info) was 0 — a station computes its TB-PPDU duration
   from it, so a zero-length grant elicits no response;
 - the Basic-variant per-user "Trigger Dependent User Info" byte was missing
   (User Info is 6 bytes for a Basic Trigger, not 5);
 - no Padding field.
Add a valid UL Length (TriggerConfig::ul_length), the 6th user-info byte, and
a Padding field (AID12=4095 marker + 0xFF), and teach parse_trigger the
6-byte Basic stride. Add TriggerConfig ta/ra so SendTrigger can set the
Trigger's TA to the AP BSSID (a station rejects a Trigger whose TA is not its
AP). Golden-byte round-trip selftest updated.

Correct the sounding comments across the surface: on-air measurement (both
dies) shows the shipped client NIC firmware ACCEPTS SET_SND_PARA but does not
air the NDPA/NDP/BFRP sequence — the fw sounding-transmit engine is present
only in the vendor AP-firmware image. Like the MP-only F2P path, the sounding
H2C is a byte-exact command surface the shipped fw does not act on;
host-injected SendTrigger is what airs a Trigger. No behavioural change — the
prior comments claimed it "airs", which is not what the firmware does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@josephnef
josephnef merged commit fb58558 into master Jul 17, 2026
18 checks passed
@josephnef
josephnef deleted the kestrel-236-trigger-ul-twt-command-surface branch July 17, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant