-
-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
1545 lines (1388 loc) · 72.2 KB
/
Copy pathCMakeLists.txt
File metadata and controls
1545 lines (1388 loc) · 72.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
cmake_minimum_required(VERSION 3.15)
# Set the toolchain file for vcpkg on Windows (only if VCPKG_ROOT is set;
# on mingw we get libusb from pkg-config directly).
if(WIN32 AND NOT "$ENV{VCPKG_ROOT}" STREQUAL "")
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
endif()
project(devourer)
enable_testing()
# ---------------------------------------------------------------------------
# Per-chip build options. All default ON (backward compatible: a plain
# `cmake -S . -B build` builds every chip). Turn off the ones you don't need to
# drop their firmware blobs + generated PHY tables and shrink the binary — e.g.
# an 8812AU-only build:
# cmake -S . -B build -DDEVOURER_8814=OFF \
# -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF \
# -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF
# ---------------------------------------------------------------------------
option(DEVOURER_JAGUAR1 "RTL8812AU / 8811AU / 8821AU (Jaguar1 core)" ON)
option(DEVOURER_8814 "RTL8814AU (4T4R; requires DEVOURER_JAGUAR1)" ON)
option(DEVOURER_JAGUAR2_8822B "RTL8822BU (Jaguar2, rtl8822b, 2T2R USB)" ON)
option(DEVOURER_JAGUAR2_8821C "RTL8811CU / 8821CU (Jaguar2, rtl8821c, 1T1R)" ON)
option(DEVOURER_JAGUAR3_8822C "RTL8812CU / 8822CU (Jaguar3, rtl8822c)" ON)
option(DEVOURER_JAGUAR3_8822E "RTL8812EU / 8822EU (Jaguar3, rtl8822e)" ON)
option(DEVOURER_8733B "RTL8731BU / RTL8733BU USB support" ON)
# Kestrel — the Wi-Fi 6 / 802.11ax generation (Realtek "G6 phl" architecture,
# vendor trees reference/rtl8852bu + reference/rtl8852cu): monitor RX, TX
# injection and channel/BW on-air validated on both chips (see
# src/kestrel/CLAUDE.md).
option(DEVOURER_KESTREL_8852B "RTL8852BU / 8832BU (Kestrel 11ax, G6)" ON)
option(DEVOURER_KESTREL_8852C "RTL8852CU / 8832CU (Kestrel 11ax, G6)" ON)
# PCIe transport (vfio-pci). Linux-only. The transport implements the HalMAC-
# generation (Jaguar2/3) 88xx buffer-descriptor DMA rings; the RTL8821CE is
# the only chip with a PCIe bring-up implemented + hardware-validated so far,
# hence the DEVOURER_JAGUAR2_8821C requirement below. Other HalMAC PCIe parts
# (RTL8822BE / RTL8822CE) could ride the same rings with per-chip HAL gates;
# Jaguar1 PCIe parts (RTL8812AE/8821AE) CANNOT — they are pre-HalMAC silicon
# with the older rtlwifi-style own-bit descriptor DMA, a different engine.
# OFF by default — libusb-only builds are bit-identical.
option(DEVOURER_PCIE "PCIe transport via vfio-pci (Linux; RTL8821CE)" OFF)
# MediaTek MT7612U / MT7662U — the first non-Realtek part. Compiles the
# src/mt7612u subtree (its own C++ library with a C ABI, landed unwired in #412)
# into libdevourer along with Mt7612uRadio, the IRadio backend WiFiDriver
# constructs for a MediaTek adapter. OFF by default, but it DOES count as chip
# support in the "No chip support selected" check below: with the backend behind
# it an MT7612U-only build opens, receives and transmits, which is exactly what
# that check exists to require.
#
# Turning it on also compiles the subtree across the whole platform matrix —
# gcc, clang, MSVC, mingw, macOS — where it was previously only ever built by
# its own Makefile, on Linux. The option does NOT gate the factory's MediaTek id
# gate, which is unconditional: with it OFF, such an adapter is refused rather
# than misdetected as Realtek.
option(DEVOURER_MT7612U "MediaTek MT7612U / MT7662U (2T2R 11ac USB)" OFF)
# Compile-time diagnostics floor (src/logger.h). Calls below the floor —
# including their argument expressions at DVR_TRACE/DVR_DEBUG sites — compile
# to nothing. Empty (the default) keeps the NDEBUG-derived behavior: release
# builds drop trace/debug, debug builds keep everything. Example production
# firmware build: -DDEVOURER_LOG_MAX_LEVEL=WARN
set(DEVOURER_LOG_MAX_LEVEL "" CACHE STRING
"Compile-time log floor: TRACE, DEBUG, INFO, WARN, ERROR or SILENT (empty = NDEBUG-derived)")
set_property(CACHE DEVOURER_LOG_MAX_LEVEL PROPERTY STRINGS
"" TRACE DEBUG INFO WARN ERROR SILENT)
if(NOT DEVOURER_LOG_MAX_LEVEL STREQUAL "")
set(_devourer_ll_valid TRACE DEBUG INFO WARN ERROR SILENT)
if(NOT DEVOURER_LOG_MAX_LEVEL IN_LIST _devourer_ll_valid)
message(FATAL_ERROR
"DEVOURER_LOG_MAX_LEVEL=${DEVOURER_LOG_MAX_LEVEL} — must be one of "
"TRACE, DEBUG, INFO, WARN, ERROR, SILENT (or empty).")
endif()
endif()
if(DEVOURER_8814 AND NOT DEVOURER_JAGUAR1)
message(FATAL_ERROR
"DEVOURER_8814=ON requires DEVOURER_JAGUAR1=ON (8814 reuses the Jaguar1 HAL).")
endif()
# Convenience: the shared Jaguar2 HAL compiles when either chip variant is on.
if(DEVOURER_JAGUAR2_8822B OR DEVOURER_JAGUAR2_8821C)
set(DEVOURER_JAGUAR2 ON)
else()
set(DEVOURER_JAGUAR2 OFF)
endif()
# Convenience: the shared Jaguar3 port compiles when either variant is on.
if(DEVOURER_JAGUAR3_8822C OR DEVOURER_JAGUAR3_8822E)
set(DEVOURER_JAGUAR3 ON)
else()
set(DEVOURER_JAGUAR3 OFF)
endif()
# Convenience: the shared Kestrel HAL compiles when either variant is on.
if(DEVOURER_KESTREL_8852B OR DEVOURER_KESTREL_8852C)
set(DEVOURER_KESTREL ON)
else()
set(DEVOURER_KESTREL OFF)
endif()
if(NOT DEVOURER_JAGUAR1 AND NOT DEVOURER_JAGUAR2 AND NOT DEVOURER_JAGUAR3
AND NOT DEVOURER_8733B
AND NOT DEVOURER_KESTREL
AND NOT DEVOURER_MT7612U)
message(FATAL_ERROR
"No chip support selected. Enable at least one of DEVOURER_JAGUAR1 / "
"DEVOURER_JAGUAR2_8822B / DEVOURER_JAGUAR2_8821C / DEVOURER_JAGUAR3_8822C "
"/ DEVOURER_JAGUAR3_8822E / DEVOURER_8733B / DEVOURER_KESTREL_8852B "
"/ DEVOURER_KESTREL_8852C.")
endif()
if(DEVOURER_PCIE)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(FATAL_ERROR "DEVOURER_PCIE=ON requires Linux (vfio-pci).")
endif()
if(NOT DEVOURER_JAGUAR2_8821C)
message(FATAL_ERROR
"DEVOURER_PCIE=ON requires DEVOURER_JAGUAR2_8821C=ON: the RTL8821CE "
"(Jaguar2 8821C) is currently the only chip with a PCIe bring-up "
"implemented and hardware-validated, so a PCIe build without it has "
"no usable chip. Not an architectural limit for the HalMAC families "
"— RTL8822BE (Jaguar2) / RTL8822CE (Jaguar3) could reuse the same "
"buffer-descriptor ring transport with per-chip HAL gates. Jaguar1 "
"PCIe parts (RTL8812AE/8821AE) are out of reach of this transport: "
"pre-HalMAC silicon with rtlwifi-style own-bit descriptor DMA, a "
"different engine entirely.")
endif()
endif()
# ---------------------------------------------------------------------------
# Sanitizers. Off by default; the value applies to the library, the examples
# and the selftests alike, so `ctest` and an on-hardware demo run share one
# instrumented build. The vendored halbb/halrf C is exempted from UBSan below
# (unaligned loads / signed shifts are pervasive in Realtek's sources and are
# not ours to fix).
# cmake -S . -B build-asan -DDEVOURER_SANITIZE=address+undefined
# ---------------------------------------------------------------------------
set(DEVOURER_SANITIZE "" CACHE STRING
"Sanitizer build: address, address+undefined, thread (empty = off)")
set_property(CACHE DEVOURER_SANITIZE PROPERTY STRINGS
"" address address+undefined thread)
if(NOT DEVOURER_SANITIZE STREQUAL "")
if(MSVC)
# MSVC ships AddressSanitizer only — no UBSan, no TSan, no LSan.
# Degrade loudly rather than silently building something weaker than
# the caller asked for.
if(NOT DEVOURER_SANITIZE STREQUAL "address")
message(FATAL_ERROR
"DEVOURER_SANITIZE=${DEVOURER_SANITIZE} is unsupported on MSVC — "
"it implements /fsanitize=address only. Use "
"-DDEVOURER_SANITIZE=address, or build with GCC/Clang for "
"undefined/thread.")
endif()
add_compile_options(/fsanitize=address)
# ASan is incompatible with the runtime checks and with incremental
# linking; both are on by default in Debug.
add_compile_options($<$<CONFIG:Debug>:/Oy->)
add_link_options(/INCREMENTAL:NO)
else()
if(DEVOURER_SANITIZE STREQUAL "address")
set(_devourer_san "address")
elseif(DEVOURER_SANITIZE STREQUAL "address+undefined")
set(_devourer_san "address,undefined")
elseif(DEVOURER_SANITIZE STREQUAL "thread")
set(_devourer_san "thread")
else()
message(FATAL_ERROR
"DEVOURER_SANITIZE=${DEVOURER_SANITIZE} — must be one of "
"address, address+undefined, thread (or empty).")
endif()
add_compile_options(-fsanitize=${_devourer_san} -fno-omit-frame-pointer -g)
add_link_options(-fsanitize=${_devourer_san})
endif()
message(STATUS "devourer: sanitizer build (${DEVOURER_SANITIZE})")
endif()
# Find pkg-config and then use it to locate libusb.
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
add_library(devourer
# --- generation-agnostic core (always compiled) ---
src/logger.h
src/Event.h
hal/basic_types.h
hal/hal_com_reg.h
src/ieee80211_radiotap.h
src/HalmacCfgParam.cpp
src/HalmacCfgParam.h
src/ParsedRadioPacket.cpp
src/Radiotap.c
src/RadiotapBuilder.cpp
src/RadiotapBuilder.h
src/Transport.h
src/RtlAdapter.cpp
src/RtlAdapter.h
src/RtlUsbAdapter.h # compat alias shim (RtlUsbAdapter = RtlAdapter)
src/UsbTransport.cpp
src/UsbTransport.h
src/UsbDeviceLock.cpp
src/UsbDeviceLock.h
src/UsbOpen.cpp
src/UsbOpen.h
src/SelectedChannel.h
src/RateDefinitions.h
src/RxPacket.h
src/TxDescBits.h
src/BeamformingSounder.h
src/TxMode.cpp
src/TxMode.h
src/TxPower.cpp
src/TxPower.h
src/TxStats.h
src/ThermalStatus.h
src/TxCaps.h
src/LinkHealth.cpp
src/LinkHealth.h
src/RxQuality.h
src/chanmig/ChannelScore.cpp
src/chanmig/ChannelScore.h
src/chanmig/MigGate.cpp
src/chanmig/MigGate.h
src/chanmig/ChannelDef.h
src/chanmig/ActiveLink.h
src/chanmig/EvidenceStore.h
src/chanmig/ScanPlan.h
src/chanmig/SurveyRecord.h
src/chanmig/SurveyJsonl.h
src/chanmig/JsonlLite.h
src/chanmig/MigTypes.h
src/chanmig/MigWire.h
src/chanmig/MigConfig.h
src/chanmig/MigClock.h
src/chanmig/MigProposer.h
src/chanmig/MigResponder.h
src/sensing/SenseWindow.h
src/sensing/SenseWindow.cpp
src/sensing/SurveyFrameAgg.h
src/sensing/DwellExecutor.h
src/hopset/HopsetState.h
src/hopset/HopsetTypes.h
src/hopset/HopsetWire.h
src/hopset/HopsetAuthority.h
src/hopset/HopsetFollower.h
src/hopset/HopsetPolicy.h
src/hopset/HopsetSense.h
src/hopset/HopsetFusion.h
src/hopset/HopsetEvents.h
src/IRadio.h
src/IRtlRadio.h
src/SignalStop.cpp
src/SignalStop.h
src/WiFiDriver.cpp
src/WiFiDriver.h
src/registry_priv.h
# LA-mode (phydm logic-analyzer) IQ capture — one implementation serves
# all three generations (LaRegs carries the per-chip parameters).
src/LaCapture.cpp
src/LaCapture.h
# phydm check_positive table walker — shared by Jaguar1 and Jaguar2
# (both use the older addr/value + check_positive table format).
src/PhyTableLoader.cpp
src/PhyTableLoader.h
)
target_compile_features(devourer PUBLIC cxx_std_20)
# PUBLIC: logger.h is header-only, so the library and its consumers must agree
# on the compile-time log floor (ODR).
if(NOT DEVOURER_LOG_MAX_LEVEL STREQUAL "")
target_compile_definitions(devourer PUBLIC
DEVOURER_LOG_MAX_LEVEL=DEVOURER_LL_${DEVOURER_LOG_MAX_LEVEL})
endif()
# Link devourer with libusb as found via pkg-config.
target_link_libraries(devourer PUBLIC PkgConfig::libusb)
# Android (Termux / NDK): the diagnostics plane routes to logcat via
# __android_log_write (src/logger.h), which lives in the system `log` library.
if(ANDROID)
target_link_libraries(devourer PUBLIC log)
endif()
target_include_directories(devourer PUBLIC hal)
target_include_directories(devourer PUBLIC hal/phydm/rtl8814a)
target_include_directories(devourer PUBLIC hal/phydm/rtl8822c)
target_include_directories(devourer PUBLIC hal/phydm/rtl8822e)
target_include_directories(devourer PUBLIC hal/phydm/rtl8822b)
target_include_directories(devourer PUBLIC hal/phydm/rtl8821c)
target_include_directories(devourer PUBLIC src)
# --- per-chip source groups (see options above) ---
if(DEVOURER_JAGUAR1)
target_sources(devourer PRIVATE
hal/hal8812a_fw.c hal/hal8812a_fw.h
hal/hal8821a_fw.c hal/hal8821a_fw.h
hal/Hal8812PwrSeq.c hal/Hal8812PwrSeq.h
hal/Hal8821APwrSeq.c hal/Hal8821APwrSeq.h
hal/Hal8812a_TxPwrTrack.cpp hal/Hal8812a_TxPwrTrack.h
hal/Hal8812PhyReg.h hal/Hal8821PhyReg.h
hal/rtl8812a_hal.h hal/rtl8812a_recv.h hal/rtl8812a_spec.h
src/Firmware.h
src/jaguar1/RtlJaguarDevice.cpp src/jaguar1/RtlJaguarDevice.h
src/jaguar1/HalModule.cpp src/jaguar1/HalModule.h
src/jaguar1/RadioManagementModule.cpp src/jaguar1/RadioManagementModule.h
src/jaguar1/EepromManager.cpp src/jaguar1/EepromManager.h
src/jaguar1/FirmwareManager.cpp src/jaguar1/FirmwareManager.h
src/jaguar1/Iqk8812a.cpp src/jaguar1/Iqk8812a.h
src/jaguar1/PhydmWatchdog.cpp src/jaguar1/PhydmWatchdog.h
src/jaguar1/PowerTracking8812a.cpp src/jaguar1/PowerTracking8812a.h
src/jaguar1/FrameParser.cpp src/jaguar1/FrameParser.h
# NB: PhyTableLoader was promoted to src/ (shared with Jaguar2).
src/jaguar1/BbDbgportReader.cpp src/jaguar1/BbDbgportReader.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR1=1)
endif()
if(DEVOURER_8814)
target_sources(devourer PRIVATE
hal/hal8814a_fw.c hal/hal8814a_fw.h
hal/Hal8814PwrSeq.c hal/Hal8814PwrSeq.h
hal/Hal8814PhyReg.h
hal/phydm/rtl8814a/Hal8814_PhyTables.c
hal/phydm/rtl8814a/Hal8814_PhyTables.h
src/jaguar1/Iqk8814a.cpp src/jaguar1/Iqk8814a.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_8814=1)
endif()
# --- Jaguar2 (RTL8822BU / RTL8811CU-8821CU) ---
# Hybrid: HalMAC firmware/MAC-init/pwr-seq (like Jaguar3) + the older
# check_positive phydm table format (shared src/PhyTableLoader, like Jaguar1).
# Multi-chip via ChipVariant strategy dispatch (Jaguar2PhyTables / Jaguar2-
# Calibration), mirroring the Jaguar3 8822C/8822E split. The shared block below
# holds the orchestrator/HAL/halmac/strategy-trio + the per-variant dispatcher
# .cpp files (self-guarded on DEVOURER_HAVE_JAGUAR2_8822B/_8821C); each chip's
# firmware blob + generated tables live in its own variant block.
if(DEVOURER_JAGUAR2)
target_sources(devourer PRIVATE
src/jaguar2/RtlJaguar2Device.cpp src/jaguar2/RtlJaguar2Device.h
src/jaguar2/HalJaguar2.cpp src/jaguar2/HalJaguar2.h
src/jaguar2/HalmacJaguar2Fw.cpp src/jaguar2/HalmacJaguar2Fw.h
src/jaguar2/HalmacJaguar2MacInit.cpp src/jaguar2/HalmacJaguar2MacInit.h
src/jaguar2/HalmacJaguar2Regs.h
src/jaguar2/FrameParserJaguar2.h
src/jaguar2/ChipVariant.h
src/jaguar2/Jaguar2PhyTables.h
src/jaguar2/Jaguar2Calibration.h
src/jaguar2/Phy8822bTables.cpp
src/jaguar2/Halrf8822b.cpp src/jaguar2/Halrf8822b.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR2=1)
endif()
# 8822B variant data (2T2R): firmware blob + generated phydm tables + txpwr_lmt.
if(DEVOURER_JAGUAR2_8822B)
target_sources(devourer PRIVATE
hal/hal8822b_fw.c hal/hal8822b_fw.h
hal/phydm/rtl8822b/Hal8822b_PhyTables.c hal/phydm/rtl8822b/Hal8822b_PhyTables.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR2_8822B=1)
endif()
# 8821C variant (1T1R): per-variant dispatcher impls (Phy8821cTables / Halrf8821c)
# + firmware blob + generated phydm tables. Mirrors the Jaguar3 8822E block.
if(DEVOURER_JAGUAR2_8821C)
target_sources(devourer PRIVATE
src/jaguar2/Phy8821cTables.cpp
src/jaguar2/Halrf8821c.cpp src/jaguar2/Halrf8821c.h
hal/hal8821c_fw.c hal/hal8821c_fw.h
hal/phydm/rtl8821c/Hal8821c_PhyTables.c hal/phydm/rtl8821c/Hal8821c_PhyTables.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR2_8821C=1)
endif()
# --- MediaTek MT7612U (MT7662 MAC) ---
# Note the option gates only the COMPILE. Mt7612uUsbIds.h is header-only and is
# included unconditionally by WiFiDriver.cpp, so a default OFF build still
# refuses MediaTek adapters and still runs the mt7612u_usb_ids test; it just
# links none of the subtree.
# The subtree is self-contained: it talks to libusb directly rather than through
# RtlAdapter/ITransport, because 32-bit registers and an in-band MCU over EP8/EP5
# do not fit the Realtek shape. Its public surface is the C ABI in
# include/mt7612u/mt7612u.h; nothing here exports its internal headers, so the
# include directory is PRIVATE until the backend needs it.
if(DEVOURER_MT7612U)
target_sources(devourer PRIVATE
src/mt7612u/async.cpp
src/mt7612u/beacon.cpp
src/mt7612u/caps.cpp
src/mt7612u/eeprom.cpp
src/mt7612u/fw.cpp
src/mt7612u/init.cpp
src/mt7612u/mcu.cpp
src/mt7612u/phy.cpp
src/mt7612u/radiotap.cpp
src/mt7612u/rx.cpp
src/mt7612u/tx.cpp
src/mt7612u/usb.cpp
src/mt7612u/Mt7612uRadio.cpp src/mt7612u/Mt7612uRadio.h
src/mt7612u/Mt7612uMapping.h
src/mt7612u/Mt7612uRxQueue.h
src/mt7612u/Mt7612uTsfRead.h
src/mt7612u/internal.h
src/mt7612u/regs.h
src/mt7612u/initvals.h
src/mt7612u/Mt7612uUsbIds.h) # listed for IDEs; compiled unconditionally
target_include_directories(devourer PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/mt7612u)
# PUBLIC: Mt7612uRadio.h is reached as "mt7612u/Mt7612uRadio.h" and pulls in
# <mt7612u/mt7612u.h>, so a consumer compiling against this target needs the
# subtree's public include root too.
target_include_directories(devourer PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/mt7612u/include)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_MT7612U=1)
endif()
# --- PCIe transport (vfio-pci; see src/PcieTransport.h) ---
if(DEVOURER_PCIE)
target_sources(devourer PRIVATE
src/PcieTransport.cpp src/PcieTransport.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_PCIE=1)
endif()
if(DEVOURER_JAGUAR3)
# Shared Jaguar3 port (compiled when either variant is on). Phy8822cTables.cpp
# and Halrf8822c.cpp also carry the make_jaguar3_*() dispatchers, so they are
# always built when Jaguar3 is on; their per-variant bodies are #if-guarded.
target_sources(devourer PRIVATE
src/jaguar3/Jaguar3PhyTables.h
src/jaguar3/Jaguar3Calibration.h
src/jaguar3/ChipVariant.h
src/jaguar3/RtlJaguar3Device.cpp src/jaguar3/RtlJaguar3Device.h
src/jaguar3/HalJaguar3.cpp src/jaguar3/HalJaguar3.h
src/jaguar3/HalmacJaguar3Fw.cpp src/jaguar3/HalmacJaguar3Fw.h
src/jaguar3/HalmacJaguar3MacInit.cpp src/jaguar3/HalmacJaguar3MacInit.h
src/jaguar3/HalmacJaguar3Regs.h
src/jaguar3/RadioManagementJaguar3.cpp src/jaguar3/RadioManagementJaguar3.h
src/jaguar3/PhydmRuntimeJaguar3.cpp src/jaguar3/PhydmRuntimeJaguar3.h
src/jaguar3/FrameParserJaguar3.h
src/jaguar3/PhyTableLoaderJaguar3.cpp src/jaguar3/PhyTableLoaderJaguar3.h
src/jaguar3/Phy8822cTables.cpp
src/jaguar3/Halrf8822c.cpp src/jaguar3/Halrf8822c.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR3=1)
endif()
if(DEVOURER_JAGUAR3_8822C)
target_sources(devourer PRIVATE
hal/hal8822c_fw.c hal/hal8822c_fw.h
hal/phydm/rtl8822c/Hal8822c_PhyTables.c hal/phydm/rtl8822c/Hal8822c_PhyTables.h
hal/phydm/rtl8822c/Hal8822c_IqkNctl.c hal/phydm/rtl8822c/Hal8822c_IqkNctl.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR3_8822C=1)
endif()
if(DEVOURER_JAGUAR3_8822E)
target_sources(devourer PRIVATE
hal/hal8822e_fw.c hal/hal8822e_fw.h
hal/phydm/rtl8822e/Hal8822e_PhyTables.c hal/phydm/rtl8822e/Hal8822e_PhyTables.h
src/jaguar3/Phy8822eTables.cpp
src/jaguar3/Halrf8822e.cpp src/jaguar3/Halrf8822e.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR3_8822E=1)
endif()
# RTL8733B is an 802.11n HALMAC 87xx part, not a Jaguar3 variant. Keep the
# backend isolated: it has its own power/FW/MAC/PHY path, descriptors, and
# conservative capabilities.
if(DEVOURER_8733B)
target_sources(devourer PRIVATE
src/rtl8733b/Rtl8733bDevice.cpp src/rtl8733b/Rtl8733bDevice.h
src/rtl8733b/Rtl8733bBringup.cpp src/rtl8733b/Rtl8733bBringup.h
src/rtl8733b/Halmac8733bMac.cpp src/rtl8733b/Halmac8733bMac.h
src/rtl8733b/Phy8733b.cpp src/rtl8733b/Phy8733b.h
src/rtl8733b/Rtl8733bUsbIds.h
hal/hal8733b_fw.c hal/hal8733b_fw.h
hal/hal8733b_tables.c hal/hal8733b_tables.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_8733B=1)
endif()
# --- Kestrel (Wi-Fi 6 / 802.11ax: RTL8852BU / RTL8852CU) ---
# Fourth generation, ported from the vendor "G6 phl" trees (reference/
# rtl8852bu, reference/rtl8852cu): mac_ax MAC/FW plane, halbb PHY tables,
# halrf calibration. Dispatched PID-first in WiFiDriver (the 0x00FC byte is
# R_AX_SYS_CHIPINFO on this silicon — see src/kestrel/ChipVariant.h). Shared
# core below; per-variant firmware blobs + tables live in their variant
# blocks.
if(DEVOURER_KESTREL)
target_sources(devourer PRIVATE
src/kestrel/ChipVariant.h
src/kestrel/KestrelUsbIds.h
src/kestrel/MacRegAx.h
src/kestrel/HalKestrel.cpp src/kestrel/HalKestrel.h
src/kestrel/KestrelFw.cpp src/kestrel/KestrelFw.h
src/kestrel/KestrelFwSched.cpp
src/kestrel/KestrelLe.h
src/kestrel/RtlKestrelDevice.cpp src/kestrel/RtlKestrelDevice.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_KESTREL=1)
endif()
if(DEVOURER_KESTREL_8852B)
target_sources(devourer PRIVATE
# Generated by tools/extract_8852b_fw.py (two NICCE images, cut-selected
# at runtime: CBV -> u2, CCV+ -> u3). Consumed by KestrelFw.
hal/hal8852b_fw.c hal/hal8852b_fw.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_KESTREL_8852B=1)
endif()
if(DEVOURER_KESTREL_8852C)
target_sources(devourer PRIVATE
# Generated by tools/extract_8852c_fw.py (two NIC images, cut-selected
# at runtime: CAV -> u1, CBV+ -> u2).
hal/hal8852c_fw.c hal/hal8852c_fw.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_KESTREL_8852C=1)
endif()
# Vendored halbb/halrf G6 planes — Realtek's baseband + RF-calibration C
# compiled VERBATIM behind a thin shim (hal/halbb/hal_headers_le.h supplies a
# minimal PHL surface) + a C glue layer (kestrel_halbb_glue.h /
# kestrel_halrf_glue.h), instead of hand-transcribed C++. One chip-agnostic
# core (vendored from reference/rtl8852cu by tools/vendor_hal{bb,rf}_8852c.sh)
# carries the vendor's own per-chip runtime dispatch; the per-chip backends
# (halbb_8852b/halrf_8852b from reference/rtl8852bu, halbb_8852c/halrf_8852c
# from reference/rtl8852cu) join it per the DEVOURER_KESTREL_* options. All
# compilers: the vendor C is portable (audited: no GNU-only constructs); the
# dead-code stripping is -ffunction-sections/--gc-sections on GNU/Clang and
# /Gy + /OPT:REF on MSVC.
if(DEVOURER_KESTREL)
if(MSVC)
set(_kestrel_vendor_cflags /W0 /Gy)
else()
set(_kestrel_vendor_cflags -w -ffunction-sections -fdata-sections)
# Realtek's sources are full of unaligned loads, signed shifts and
# out-of-range enum stores. They are vendored verbatim (edit the
# extractors, never these files), so UBSan on them reports noise we
# would never act on and drowns the findings from our own code.
if(DEVOURER_SANITIZE MATCHES "undefined")
list(APPEND _kestrel_vendor_cflags -fno-sanitize=undefined)
endif()
endif()
file(GLOB KESTREL_HALBB_C "${CMAKE_CURRENT_SOURCE_DIR}/hal/halbb/g6/vendor/*.c")
file(GLOB KESTREL_HALRF_C "${CMAKE_CURRENT_SOURCE_DIR}/hal/halrf/g6/vendor/*.c")
if(DEVOURER_KESTREL_8852B)
file(GLOB _halbb_8852b "${CMAKE_CURRENT_SOURCE_DIR}/hal/halbb/g6/vendor/halbb_8852b/*.c")
file(GLOB _halrf_8852b "${CMAKE_CURRENT_SOURCE_DIR}/hal/halrf/g6/vendor/halrf_8852b/*.c")
list(APPEND KESTREL_HALBB_C ${_halbb_8852b})
list(APPEND KESTREL_HALRF_C ${_halrf_8852b})
list(APPEND _kestrel_halbb_defs CONFIG_RTL8852B=1 BB_8852B_SUPPORT=1)
list(APPEND _kestrel_halrf_defs CONFIG_RTL8852B=1 RF_8852B_SUPPORT=1)
endif()
if(DEVOURER_KESTREL_8852C)
file(GLOB _halbb_8852c "${CMAKE_CURRENT_SOURCE_DIR}/hal/halbb/g6/vendor/halbb_8852c/*.c")
file(GLOB _halrf_8852c "${CMAKE_CURRENT_SOURCE_DIR}/hal/halrf/g6/vendor/halrf_8852c/*.c")
list(APPEND KESTREL_HALBB_C ${_halbb_8852c})
list(APPEND KESTREL_HALRF_C ${_halrf_8852c})
list(APPEND _kestrel_halbb_defs CONFIG_RTL8852C=1 BB_8852C_SUPPORT=1)
list(APPEND _kestrel_halrf_defs CONFIG_RTL8852C=1 RF_8852C_SUPPORT=1)
endif()
add_library(kestrel_halbb OBJECT
${KESTREL_HALBB_C}
hal/halbb/g6/kestrel_halbb_glue.c
hal/halbb/g6/kestrel_halbb_helpers.c)
set_target_properties(kestrel_halbb PROPERTIES C_STANDARD 11
POSITION_INDEPENDENT_CODE ON)
target_include_directories(kestrel_halbb PRIVATE
hal/halbb hal/halbb/g6
hal/halbb/g6/vendor
hal/halbb/g6/vendor/halbb_8852b hal/halbb/g6/vendor/halbb_8852c)
# HALBB_DBCC_SUPPORT / HALBB_FW_OFLD_SUPPORT / HALBB_DBG_TRACE_SUPPORT are
# #ifdef-tested in the vendor code, so being OFF means staying UNDEFINED —
# a `=0` define still compiles the feature branches in (with fw-offload
# compiled in, the 8852B BBCR writer routes every table write through the
# fwofld wrapper and applies nothing).
target_compile_definitions(kestrel_halbb PRIVATE
${_kestrel_halbb_defs}
PLATFOM_IS_LITTLE_ENDIAN=1)
target_compile_options(kestrel_halbb PRIVATE ${_kestrel_vendor_cflags})
target_sources(devourer PRIVATE $<TARGET_OBJECTS:kestrel_halbb>)
# C++ side includes only kestrel_halbb_glue.h (opaque handle + bridge).
target_include_directories(devourer PRIVATE hal/halbb/g6)
# Strip unreferenced vendored sections at link. ld64 (macOS) spells it
# -dead_strip; MSVC's /OPT:REF is implied in Release via /Gy.
if(APPLE)
target_link_options(devourer INTERFACE -Wl,-dead_strip)
elseif(NOT MSVC)
target_link_options(devourer INTERFACE -Wl,--gc-sections)
endif()
# Vendored halrf-G6 RF calibrations (DACK / RX-DCK / IQK / LCK-RCK / efuse
# trim + TSSI-DE load), compiled VERBATIM behind the SAME shim
# (hal/halrf/hal_headers_le.h just reuses the halbb one + the bridge's
# RF-register plane). Driven via kestrel_halrf_glue.h.
add_library(kestrel_halrf OBJECT
${KESTREL_HALRF_C}
hal/halrf/g6/kestrel_halrf_glue.c
hal/halrf/g6/kestrel_halrf_helpers.c)
set_target_properties(kestrel_halrf PROPERTIES C_STANDARD 11
POSITION_INDEPENDENT_CODE ON)
target_include_directories(kestrel_halrf PRIVATE
hal/halrf hal/halbb hal/halbb/g6
hal/halrf/g6/vendor
hal/halrf/g6/vendor/halrf_8852b hal/halrf/g6/vendor/halrf_8852c
hal/halbb/g6/vendor
hal/halbb/g6/vendor/halbb_8852b hal/halbb/g6/vendor/halbb_8852c)
target_compile_definitions(kestrel_halrf PRIVATE
${_kestrel_halrf_defs} PLATFOM_IS_LITTLE_ENDIAN=1
KESTREL_HALRF_PRESENT=1)
target_compile_options(kestrel_halrf PRIVATE ${_kestrel_vendor_cflags})
target_sources(devourer PRIVATE $<TARGET_OBJECTS:kestrel_halrf>)
target_include_directories(devourer PRIVATE hal/halrf/g6)
endif()
# pcieprobe — staged PCIe bring-up driver for the RTL8821CE milestones
# (chip-id/MMIO sanity -> power-on + EFUSE -> firmware DLFW). rxdemo's
# DEVOURER_PCIE_BDF branch is the full-RX consumer; this probe validates the
# layers below it one at a time. Linux/vfio only.
if(DEVOURER_PCIE)
add_executable(pcieprobe
examples/pcieprobe/main.cpp
)
target_link_libraries(pcieprobe PUBLIC devourer)
endif()
# kestrelprobe — staged USB bring-up prober for the Kestrel (RTL8852B/C):
# AX identity (die-id/cut) -> power-on + EFUSE -> firmware download, the
# pcieprobe sibling. Validates each layer before the demos use it.
if(DEVOURER_KESTREL)
add_executable(kestrelprobe
examples/kestrelprobe/main.cpp
)
target_link_libraries(kestrelprobe PUBLIC devourer PRIVATE PkgConfig::libusb)
# DeviceSession.h (shared teardown-order holder) lives in examples/common/.
target_include_directories(kestrelprobe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
endif()
# rtl8733bprobe — staged USB bring-up prober for the RTL8733B (HALMAC 87xx),
# the pcieprobe/kestrelprobe sibling: identity -> power-on + EFUSE -> firmware
# -> MAC/PHY -> TSSI audit stages, used alongside normal factory dispatch. It
# never substitutes for the production IRadio path.
if(DEVOURER_8733B)
add_executable(rtl8733bprobe
examples/rtl8733bprobe/main.cpp
)
target_link_libraries(rtl8733bprobe PUBLIC devourer PRIVATE PkgConfig::libusb)
# DeviceSession.h (shared teardown-order holder) lives in examples/common/.
target_include_directories(rtl8733bprobe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
endif()
# mt7612uprobe — the MediaTek bring-up harness, the rtl8733bprobe/kestrelprobe
# sibling: one subcommand per verified hardware gate (registers, firmware, MAC,
# channel, TX, RX, ACK responder, the wedge experiments). Same source the
# subtree's own Makefile builds as ./bringup, built here so the chip-specific
# tool is not the one part of this backend that only a second build system can
# produce — and so it picks up CMAKE_C_COMPILER and DEVOURER_SANITIZE, which
# shelling out to that Makefile would silently ignore.
#
# It talks to the C library directly, NOT through Mt7612uRadio: it predates the
# backend and its whole purpose is to exercise the layer underneath one. It
# never substitutes for the production IRadio path.
# UNIX-only, unlike its kestrelprobe/rtl8733bprobe siblings. Those are portable
# C++; this one is the hardware bench harness and uses <unistd.h>,
# <sys/resource.h> and getrusage() unguarded, so on the MSVC cell of the
# multi-platform matrix - which now passes DEVOURER_MT7612U=ON - an unguarded
# add_executable would put it in `all` and break the build. The LIBRARY is
# MSVC-clean; this tool is not, and does not need to be.
if(DEVOURER_MT7612U AND UNIX)
add_executable(mt7612uprobe
src/mt7612u/tools/bringup.cpp
)
target_link_libraries(mt7612uprobe PRIVATE devourer PkgConfig::libusb)
target_include_directories(mt7612uprobe PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/mt7612u)
endif()
# reglat — register round-trip latency microbench (USB ctrl-xfer vs PCIe MMIO).
# Times RtlAdapter::rtw_read32; the PCIe path compiles only with DEVOURER_PCIE
# (inherited via the devourer PUBLIC DEVOURER_HAVE_PCIE define). See its header.
add_executable(reglat tests/reglat.cpp)
target_link_libraries(reglat PUBLIC devourer PRIVATE PkgConfig::libusb)
add_executable(rxdemo
examples/rx/main.cpp
examples/common/env_config.cpp
examples/common/usb_select.cpp
)
target_link_libraries(rxdemo PUBLIC devourer)
target_include_directories(rxdemo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
add_executable(txdemo
examples/tx/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(txdemo PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(txdemo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# dwell-1 A/B injection over the firmware channel switch (issue #272 data
# plane): one admitted data-frame opportunity per wall-clock slot, with
# bounded admission so a frame for context A can never air on B.
add_executable(dwelltx
examples/dwelltx/main.cpp
examples/common/env_config.cpp
examples/common/usb_select.cpp
)
target_link_libraries(dwelltx PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(dwelltx PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# Pre-modulator subcarrier PoC: transmits PSDU bytes shaped by
# tools/precoder/encode_subcarriers.py so chosen OFDM data subcarriers carry
# chosen bits. Single-stream BPSK / HT MCS0 / BCC on 8812/8821/8811.
add_executable(precoder
examples/precoder/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(precoder PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(precoder PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# Stream-link TX: reads length-prefixed PSDU bodies from stdin and transmits
# one probe-request per body. Driven by tools/precoder/stream_tx.py — the
# C++/Python split keeps libusb out of the framing math.
add_executable(streamtx
examples/streamtx/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(streamtx PUBLIC devourer PRIVATE PkgConfig::libusb)
# stream_stdin.h (shared binary-stdin framing) lives in examples/common/.
target_include_directories(streamtx PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# SVC-T UEP injector: maps each HEVC NAL's temporal_id -> a robust/fast TxMode
# (svc_tx.h), so base/IDR layers fly robust and enhancement layers ride fast.
add_executable(svctx
examples/svctx/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(svctx PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(svctx PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# tdma — burst-level bandwidth TDMA demo (docs/narrowband.md). Alternates
# narrowband/wide BURSTS via FastSetBandwidth; roles tx / rx-sync (lockstep,
# wallclock or TX-marker sync) / rx-camp (fixed band). tdma.h is header-only.
add_executable(tdma
examples/tdma/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(tdma PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(tdma PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/examples/common
${CMAKE_CURRENT_SOURCE_DIR}/examples/tdma)
# timesync — LTE-eNB-style over-the-air time distribution. One MASTER broadcasts
# its hardware TSF; SLAVES lock to it from the beacons alone (no GPS at the
# slaves). Reuses the tdma TD frame tag. timesync.h is header-only.
add_executable(timesync
examples/timesync/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(timesync PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(timesync PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/examples/common
${CMAKE_CURRENT_SOURCE_DIR}/examples/tdma
${CMAKE_CURRENT_SOURCE_DIR}/examples/timesync)
# Stream-link DUPLEX: one binary, one chip, both directions. Combines the RX
# loop from rxdemo and the stdin-driven TX from streamtx —
# stdin = length-prefixed PSDU bodies, stdout = <devourer-stream> lines.
# tools/precoder/tun_p2p.py spawns this for --mode=duplex.
add_executable(duplex
examples/duplex/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(duplex PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(duplex PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# txpower — reference consumer of the runtime TX-power API
# (IRadio::SetTxPowerOffsetQdb / SetTxPowerIndexOverride / GetTxPowerState /
# GetThermalStatus): opens one adapter, steps the knobs from CLI args (no env
# vars), and prints machine-readable state markers. The register-level
# validation driver for tests/txpwr_offset_regcheck.sh and the shape of an
# adaptive-link controller's power leg.
add_executable(txpower
examples/txpower/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(txpower PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(txpower PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# sense — Wi-Fi motion/presence sensor from beamforming reports. Drives
# two adapters (a sounder + a beamformee) on one host, self-captures the reports,
# decodes the per-tone Givens angles (src/BfReportDecode.h), and shows a live
# motion readout. See examples/sense/README.md and docs/beamforming-victim-sensing.md.
add_executable(sense
examples/sense/main.cpp
examples/common/env_config.cpp
)
target_link_libraries(sense PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(sense PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# doctor — adapter health triage (src/AdapterHealth.h): EFUSE read-stability
# probe + firmware-boot outcome + RX smoke, classified into
# HEALTHY / SUSPECT / FAILING with reasons. Detects dying dongles that
# enumerate and init green but read stochastic EFUSE garbage / never boot the
# MCU (the #205 failure mode). Pure CLI; exit code carries the verdict. See
# docs/adapter-doctor.md.
# chanmig — the coordinated channel-migration endpoint (ground proposes /
# drone commits). Duplex RX+TX on one adapter, the pure Mig* state machines
# driving an authenticated control protocol over own 802.11 frames.
add_executable(chanmig
examples/chanmig/main.cpp
examples/common/env_config.cpp
examples/common/usb_select.cpp
)
target_link_libraries(chanmig PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(chanmig PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
# chanscout — the passive second-adapter ground spectrum scout (adaptive
# channel migration): plan-driven candidate dwells with counter-hygiene
# discard barriers, emitting versioned survey.dwell / scout.health JSONL
# while a separate primary receiver owns the video channel. Pure logic in
# src/chanmig/ (selftested); this binary is the device I/O shell.
add_executable(chanscout
examples/chanscout/main.cpp
examples/common/env_config.cpp
examples/common/usb_select.cpp
)
target_link_libraries(chanscout PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(chanscout PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
add_executable(doctor
examples/doctor/main.cpp
)
# caps_event.h (shared adapter.caps emitter) lives in examples/common/.
target_include_directories(doctor PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
target_link_libraries(doctor PUBLIC devourer PRIVATE PkgConfig::libusb)
# chipstate — read a chip's registers without touching it: no USB reset, no
# bring-up, no calibration. The only way to inspect the state a previous
# session left behind, since every other entry point reconfigures the chip on
# the way in and destroys exactly that evidence.
add_executable(chipstate
examples/chipstate/main.cpp
)
target_include_directories(chipstate PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
target_link_libraries(chipstate PUBLIC devourer PRIVATE PkgConfig::libusb)
# The in-tree caller for the carrier-sense gate split. Needs hardware, so it is
# a tool rather than an add_test — tests/cca_gates_regcheck.sh drives it and
# cross-checks the registers with chipstate.
add_executable(CcaGatesProbe
tests/cca_gates_probe.cpp
)
target_include_directories(CcaGatesProbe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
target_link_libraries(CcaGatesProbe PUBLIC devourer PRIVATE PkgConfig::libusb)
# On-air driver for IRadio::ArmChannelBusy, through the public contract only
# (arm + GetChannelBusy). tests/busy_window_probe.sh runs its arms and asserts
# the separations; a spoiled window must come back INVALID with its reason,
# never as a plausible number.
add_executable(BusyWindowProbe
tests/busy_window_probe.cpp
)
target_include_directories(BusyWindowProbe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
target_link_libraries(BusyWindowProbe PUBLIC devourer PRIVATE PkgConfig::libusb)
# Headless regression guard for the binary-stdin framing shared by the two
# stream demos above (examples/common/stream_stdin.h). No libusb, no radio — just the
# set_stdin_binary() + read_exact() path, so a text-mode regression (e.g. the
# _setmode gate slipping back to _MSC_VER, which is invisible to a build-only
# check) fails `ctest` on the Windows/mingw jobs. See tests/stream_stdin_test.cmake.
add_executable(StreamStdinSelftest
examples/common/stream_stdin_selftest.cpp
)
target_compile_features(StreamStdinSelftest PRIVATE cxx_std_20)
target_include_directories(StreamStdinSelftest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
add_test(
NAME stream_stdin_binary
COMMAND ${CMAKE_COMMAND}
-DSELFTEST_EXE=$<TARGET_FILE:StreamStdinSelftest>
-P ${CMAKE_CURRENT_SOURCE_DIR}/tests/stream_stdin_test.cmake
)
# Headless guard for the ToneMask tone-index math (src/ToneMask.h) — the fc
# derivation + subcarrier enumeration behind DEVOURER_RX_CSI_MASK /
# DEVOURER_RX_NBI. Pure math only; the register encodings are hardware-checked
# via the apply-time readback logging.
add_executable(ToneMaskSelftest
tests/tone_mask_selftest.cpp
)
target_link_libraries(ToneMaskSelftest PRIVATE devourer)
add_test(NAME tone_mask_math COMMAND ToneMaskSelftest)
# Headless guard for the radio contract: a radio implementing only IRadio's
# pure-virtual core must compile without any Realtek type, its not-ported
# defaults must hold, and it must not be an IRtlRadio. A Realtek-typed pure
# virtual added to IRadio stops this compiling, which is the point.
add_executable(RadioIfaceSelftest
tests/radio_iface_selftest.cpp
)
target_link_libraries(RadioIfaceSelftest PRIVATE devourer)
add_test(NAME radio_iface COMMAND RadioIfaceSelftest)
# Headless guard for the MediaTek USB-id gate. CreateRadio consults it BEFORE
# the Realtek SYS_CFG2 read, so an id it wrongly claims is refused outright with
# no second chance. This pins the one property that makes that safe: the
# MediaTek pair set never claims a device a Realtek table owns. Header-only, so
# it costs a compile and no hardware.
add_executable(Mt7612uUsbIdsSelftest
tests/mt7612u_usb_ids_selftest.cpp
)
target_link_libraries(Mt7612uUsbIdsSelftest PRIVATE devourer)
add_test(NAME mt7612u_usb_ids COMMAND Mt7612uUsbIdsSelftest)
# Headless guard for the MT7612U <-> devourer descriptor translations. Pure
# lookups, so no hardware and no subtree compile — this cell runs whether or
# not DEVOURER_MT7612U is on, which is the point: the header it covers is the
# one place a wrong RSSI base or a per-chain copy that publishes the noise
# floor would be introduced, and both have happened.
add_executable(Mt7612uMappingSelftest
tests/mt7612u_mapping_selftest.cpp
)
target_link_libraries(Mt7612uMappingSelftest PRIVATE devourer)
target_include_directories(Mt7612uMappingSelftest PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/mt7612u/include)
add_test(NAME mt7612u_mapping COMMAND Mt7612uMappingSelftest)
# Headless guard for the RX hand-off queue, the one piece of backend BEHAVIOUR
# (as opposed to descriptor lookups) that can be exercised without a radio. Its
# two load-bearing properties - a full ring drops the newest frame and counts
# it, and a popped slot outlives the lock so user code can run unlocked - are
# both invisible on a healthy bench and both turn into a wedge, a reorder or a
# use-after-free when broken. Like the mapping cell it compiles regardless of
# DEVOURER_MT7612U: it is header-only and needs no libusb.
add_executable(Mt7612uRxQueueSelftest
tests/mt7612u_rx_queue_selftest.cpp
)
# The first selftest in the tree to use <thread> directly. It links today
# without this because libdevourer already pulls the thread runtime in, and on
# glibc >= 2.34 pthread is part of libc anyway — but that is two accidents, not
# a dependency, and this costs nothing.
find_package(Threads REQUIRED)
target_link_libraries(Mt7612uRxQueueSelftest PRIVATE devourer Threads::Threads)
add_test(NAME mt7612u_rx_queue COMMAND Mt7612uRxQueueSelftest)
# Headless guard for the MT7612U coherent TSF read. The two register halves are
# not latched, so the read order is the whole correctness argument, and on a
# bench a wrong order only shows for a few hundred microseconds every 71.6 min.
# The cell sweeps a scripted counter across the low-word wrap and requires the
# pre-fix order to tear in the same sweep. Header-only; no libusb.
add_executable(Mt7612uTsfReadSelftest
tests/mt7612u_tsf_read_selftest.cpp
)
target_link_libraries(Mt7612uTsfReadSelftest PRIVATE devourer)
add_test(NAME mt7612u_tsf_read COMMAND Mt7612uTsfReadSelftest)
# The C entry points' failure contract, which the cell above cannot reach: a
# failed read must be distinguishable from a value, because 0xffffffff is a
# legitimate register word on this part. Needs the subtree's symbols, so it is
# gated on DEVOURER_MT7612U (CI's MediaTek jobs turn it on).
if(DEVOURER_MT7612U)
add_executable(Mt7612uTsfApiSelftest
tests/mt7612u_tsf_api_selftest.cpp
)
target_link_libraries(Mt7612uTsfApiSelftest PRIVATE devourer)
target_include_directories(Mt7612uTsfApiSelftest PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/mt7612u/include)
add_test(NAME mt7612u_tsf_api COMMAND Mt7612uTsfApiSelftest)
endif()
# Headless guard for the TX quiesce seam (ITransport::quiesce_tx via
# RtlAdapter): the explicit "stop TX and wait it out" call every device makes
# before anything is released. UsbTransport's cancel/drain is validated on
# hardware under ASan — the test header says what it does and does not cover.
add_executable(TxQuiesceSelftest
tests/tx_quiesce_selftest.cpp
)
target_link_libraries(TxQuiesceSelftest PRIVATE devourer PkgConfig::libusb)
add_test(NAME tx_quiesce_seam COMMAND TxQuiesceSelftest)
# Headless guard for the ACK-responder recipe (src/AckResponder.h): the arm,
# the gate-only clear staying gate-only, and the retarget seam. This test does
# not model silicon or instantiate Rtl8733bDevice; those are covered by the
# RTL8733B-only `disarmed` phase in ack_txreport_matrix.sh. Its legacy `off`
# phase remains a never-armed control.
add_executable(AckResponderSelftest
tests/ack_responder_selftest.cpp
)
target_link_libraries(AckResponderSelftest PRIVATE devourer PkgConfig::libusb)
add_test(NAME ack_responder_recipe COMMAND AckResponderSelftest)
# Headless guard for the windowed RX-receipt primitives (src/cell/RxReceipt.h):
# TLV round-trip, ring eviction, late accounting, ledger merge idempotence,
# strict-parse rejections. The on-air halves are tests/receipt_verify.py over
# an arq_e2e run.
add_executable(ReceiptSelftest
tests/receipt_selftest.cpp
)
target_link_libraries(ReceiptSelftest PRIVATE devourer)
add_test(NAME receipt_roundtrip COMMAND ReceiptSelftest)