Makefile 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. # OpenPuck developer convenience targets.
  2. # Pin to clang-format 18 to match CI; output differs between versions, so a
  3. # mismatch would make CI reject locally-formatted code. Resolve the binary in
  4. # order of preference: the apt-style versioned name (CI / Linux), the Homebrew
  5. # llvm@18 keg (macOS), then a bare clang-format as a last resort. Override with
  6. # `make format CLANG_FORMAT=/path/to/clang-format` if yours lives elsewhere.
  7. CLANG_FORMAT ?= $(shell command -v clang-format-18 \
  8. || ([ -x /opt/homebrew/opt/llvm@18/bin/clang-format ] && echo /opt/homebrew/opt/llvm@18/bin/clang-format) \
  9. || ([ -x /usr/local/opt/llvm@18/bin/clang-format ] && echo /usr/local/opt/llvm@18/bin/clang-format) \
  10. || command -v clang-format)
  11. # All hand-written C/C++ sources. Generated headers (git_version.h) are
  12. # gitignored and excluded here so formatting never touches them.
  13. FORMAT_FILES := $(shell find OpenPuck ReversePuckFirmware puck_sniffer pairtui \
  14. \( -name '*.c' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' -o -name '*.ino' \) \
  15. -not -name 'git_version.h')
  16. # --- firmware build ---------------------------------------------------------
  17. # The OpenPuck firmware needs two TinyUSB config values that differ from the Adafruit nRF52 core defaults:
  18. # CFG_TUD_HID=6 -- four HID interfaces (Steam mode exposes four puck slots) plus one for mouse
  19. # and one for WebUSB; core default is 2.
  20. # CFG_TUD_TASK_QUEUE_SZ=64 -- deeper usbd event queue; the default 16 deadlocks the loop task under comms
  21. # load -> watchdog reset (see OpenPuck.ino + docs/BUILD_AND_DEPLOY.md).
  22. # CFG_TUD_VENDOR_TX_BUFSIZE=256 -- the WebUSB status blob is ~118 B; the core default 64 can't hold it whole,
  23. # so the panel send (which drops rather than blocks when the FIFO is full) could
  24. # never fit a frame -> blank panel. Sized to hold a full blob with headroom.
  25. # They're baked in here so a normal build is just `make build` -- no need to remember the flags. Override any
  26. # on the command line, e.g. make build CFG_TUD_HID=8 CFG_TUD_TASK_QUEUE_SZ=128
  27. # or add your own defines: make build EXTRA_FLAGS="-DOPK_LOG=1"
  28. FQBN ?= adafruit:nrf52:feather52840
  29. CFG_TUD_HID ?= 6
  30. # Optional output paths; when set, --clean is implied and build artifacts land in OUTPUT_DIR.
  31. # Used by CI: make build BUILD_PATH=build/cache/openpuck OUTPUT_DIR=build/openpuck
  32. BUILD_PATH ?=
  33. OUTPUT_DIR ?=
  34. # Deep so a loop()-context sendReport never blocks on a full usbd event queue (the watchdog path). Sends run
  35. # from loop() now (usbTxPump) for stable RF timing, so this depth is what keeps that block from happening.
  36. CFG_TUD_TASK_QUEUE_SZ ?= 512
  37. # Must hold a whole WebUSB status blob (~118 B) so the drop-on-full panel send can fit one; core default 64 is
  38. # too small and would silently drop every frame (blank panel / stale mappings).
  39. CFG_TUD_VENDOR_TX_BUFSIZE ?= 256
  40. EXTRA_FLAGS ?=
  41. # TinyUSB dispatches every vendor-type control request through one global callback, which the Adafruit core
  42. # already defines to serve WebUSB. Original Xbox mode needs the XID requests that arrive on it.
  43. # Overriding the weak symbol would claim the whole hook and force a copy of Adafruit's WebUSB body,
  44. # so we wrap: webusb_config.cpp takes XID and passes everything else to __real_.
  45. OPENPUCK_LINK_FLAGS ?= -Wl,--wrap=tud_vendor_control_xfer_cb -Wl,--wrap=tud_descriptor_device_cb -Wl,--wrap=tud_hid_descriptor_report_cb -Wl,--wrap=tud_hid_get_report_cb -Wl,--wrap=tud_hid_set_report_cb
  46. # {build.flags.usb} is expanded by arduino-cli (VID/PID/strings); pass it through verbatim.
  47. USB_EXTRA_FLAGS = -DNRF52840_XXAA {build.flags.usb} -DCFG_TUD_HID=$(CFG_TUD_HID) -DCFG_TUD_TASK_QUEUE_SZ=$(CFG_TUD_TASK_QUEUE_SZ) -DCFG_TUD_VENDOR_TX_BUFSIZE=$(CFG_TUD_VENDOR_TX_BUFSIZE) $(EXTRA_FLAGS)
  48. # When BUILD_PATH is set, --clean + path flags are injected; omitted for fast incremental dev builds.
  49. _PATH_FLAGS = $(if $(BUILD_PATH),--clean --build-path $(BUILD_PATH) --output-dir $(OUTPUT_DIR))
  50. # `uf2` needs a KNOWN output location (unlike plain `build`, which is free to use arduino-cli's own internal
  51. # cache) so gen_uf2.sh can find the .hex afterward. Same convention CI uses. Override on the command line if
  52. # you want the .uf2 somewhere else: make uf2 UF2_OUTPUT_DIR=somewhere/else
  53. UF2_BUILD_PATH ?= build/cache/openpuck
  54. UF2_OUTPUT_DIR ?= build/openpuck
  55. # `flash`/`deploy` take the serial port as a REQUIRED POSITIONAL arg: `make flash /dev/cu.usbmodem1101`.
  56. # (No auto-detect -- uploading to a guessed serial port risks writing to the wrong device. List with
  57. # `arduino-cli board list`.) FLASH_PORT = whatever goal isn't one of our real targets; the catch-all rule at
  58. # the bottom swallows it so make doesn't try to build the port path as a target.
  59. FLASH_PORT := $(filter-out format format-check check build build-raytac \
  60. package-raytac flash-raytac deploy-raytac provision-raytac-softdevice \
  61. build-recovery reversepuck reversepuck-flash reversepuck-deploy flash deploy,$(MAKECMDGOALS))
  62. UPLOAD = arduino-cli upload -b $(FQBN) -p "$(FLASH_PORT)" OpenPuck
  63. # ReversePuck (controller dongle, 28DE:1302) build flags. It has ONE HID interface (core default 2 is fine),
  64. # so it doesn't need CFG_TUD_HID; it DOES need the deeper vendor TX FIFO to hold the 0xAC paired-pucks list
  65. # whole, and the deeper usbd task queue keeps loop()-context WebUSB/CDC/HID sends off the watchdog path.
  66. RP_USB_FLAGS = -DNRF52840_XXAA {build.flags.usb} -DCFG_TUD_TASK_QUEUE_SZ=$(CFG_TUD_TASK_QUEUE_SZ) -DCFG_TUD_VENDOR_TX_BUFSIZE=$(CFG_TUD_VENDOR_TX_BUFSIZE) $(EXTRA_FLAGS)
  67. RP_UPLOAD = arduino-cli upload -b $(FQBN) -p "$(FLASH_PORT)" ReversePuckFirmware
  68. .PHONY: format format-check check build build-raytac uf2 package-raytac \
  69. flash-raytac deploy-raytac provision-raytac-softdevice build-recovery \
  70. reversepuck reversepuck-flash reversepuck-deploy flash deploy
  71. ## Compile the firmware with the required USB flags baked in. Override CFG_TUD_HID / CFG_TUD_TASK_QUEUE_SZ /
  72. ## EXTRA_FLAGS / FQBN as make variables if needed.
  73. build:
  74. arduino-cli compile -b $(FQBN) $(_PATH_FLAGS) --build-property "build.extra_flags=$(USB_EXTRA_FLAGS)" --build-property "compiler.c.elf.extra_flags=$(OPENPUCK_LINK_FLAGS)" OpenPuck
  75. ## Build for the Raytac MDBT50Q-CX-40 without replacing its Open DFU bootloader.
  76. ## Like `build`, this does NOT run gen_version.sh -- run it yourself first if you want version provenance
  77. ## baked in. (It must not run here: CI generates git_version.h once with OPK_BUILD_VERSION set, and a
  78. ## regeneration mid-workflow would strip the release version out of every build that follows.)
  79. build-raytac:
  80. mkdir -p build/raytac build/cache/raytac
  81. arduino-cli compile --clean -b adafruit:nrf52:mdbt50qrx \
  82. --build-path build/cache/raytac \
  83. --output-dir build/raytac \
  84. --build-property "build.extra_flags=$(USB_EXTRA_FLAGS) -DOPK_BOARD_MDBT50Q_CX_40=1" \
  85. --build-property "compiler.c.elf.extra_flags=$(OPENPUCK_LINK_FLAGS)" OpenPuck
  86. ## Package an existing Raytac build for its factory Nordic Open DFU bootloader.
  87. package-raytac:
  88. @test -f build/raytac/OpenPuck.ino.hex || { \
  89. echo "run 'make build-raytac' first"; exit 1; }
  90. $(RM) build/raytac/OpenPuck-mdbt50q-cx-40.zip
  91. nrfutil nrf5sdk-tools pkg generate --hw-version 52 --sd-req 0xB6 \
  92. --application-version 1 \
  93. --application build/raytac/OpenPuck.ino.hex \
  94. build/raytac/OpenPuck-mdbt50q-cx-40.zip
  95. ## Program the application package with the device in physical Open DFU mode.
  96. flash-raytac:
  97. @test -f build/raytac/OpenPuck-mdbt50q-cx-40.zip || { \
  98. echo "run 'make package-raytac' first"; exit 1; }
  99. nrfutil device program \
  100. --firmware build/raytac/OpenPuck-mdbt50q-cx-40.zip \
  101. --traits nordicDfu
  102. ## Build, package, and program the application without changing the SoftDevice.
  103. deploy-raytac:
  104. $(MAKE) build-raytac
  105. $(MAKE) package-raytac
  106. $(MAKE) flash-raytac
  107. ## One-time S140 6.1.1 provisioning; re-enter Open DFU before flashing the app.
  108. provision-raytac-softdevice:
  109. @test -f "$(SOFTDEVICE_HEX)" || { \
  110. echo "set SOFTDEVICE_HEX to Nordic's S140 6.1.1 HEX"; \
  111. exit 1; }
  112. mkdir -p build/raytac
  113. $(RM) build/raytac/s140-6.1.1.zip
  114. nrfutil nrf5sdk-tools pkg generate --hw-version 52 \
  115. --sd-req 0xAE,0xB6 --sd-id 0xB6 \
  116. --softdevice "$(SOFTDEVICE_HEX)" \
  117. build/raytac/s140-6.1.1.zip
  118. nrfutil device program --firmware build/raytac/s140-6.1.1.zip \
  119. --traits nordicDfu
  120. ## One-time factory-reset recovery image (wipes persistent storage once on first boot). See §6 of the build doc.
  121. build-recovery:
  122. $(MAKE) build BUILD_PATH=$(BUILD_PATH) OUTPUT_DIR=$(OUTPUT_DIR) EXTRA_FLAGS="$(EXTRA_FLAGS) -DOPK_FACTORY_RESET=1"
  123. ## Build OpenPuck and convert the .hex into a drag-and-drop .uf2 (build/openpuck/OpenPuck.ino.uf2 by default).
  124. ## Same overrides as `build` (CFG_TUD_HID / EXTRA_FLAGS / FQBN), e.g. a recovery UF2:
  125. ## make uf2 EXTRA_FLAGS="-DOPK_FACTORY_RESET=1"
  126. uf2:
  127. $(MAKE) build BUILD_PATH=$(UF2_BUILD_PATH) OUTPUT_DIR=$(UF2_OUTPUT_DIR) EXTRA_FLAGS="$(EXTRA_FLAGS)"
  128. ./gen_uf2.sh $(UF2_OUTPUT_DIR)/OpenPuck.ino.hex $(UF2_OUTPUT_DIR)/OpenPuck.ino.uf2
  129. ## Compile the ReversePuck controller dongle firmware (28DE:1302) with its WebUSB vendor flags baked in.
  130. reversepuck:
  131. arduino-cli compile -b $(FQBN) $(_PATH_FLAGS) --build-property "build.extra_flags=$(RP_USB_FLAGS)" ReversePuckFirmware
  132. ## Upload the most recent ReversePuck build. Usage: make reversepuck-flash <port>
  133. reversepuck-flash:
  134. @[ -n "$(FLASH_PORT)" ] || { echo "usage: make reversepuck-flash <port> e.g. make reversepuck-flash /dev/cu.usbmodem1101"; exit 1; }
  135. $(RP_UPLOAD)
  136. ## Build then upload the ReversePuck dongle in one step. Usage: make reversepuck-deploy <port>
  137. reversepuck-deploy:
  138. @[ -n "$(FLASH_PORT)" ] || { echo "usage: make reversepuck-deploy <port> e.g. make reversepuck-deploy /dev/cu.usbmodem1101"; exit 1; }
  139. $(MAKE) reversepuck BUILD_PATH=$(BUILD_PATH) OUTPUT_DIR=$(OUTPUT_DIR)
  140. $(RP_UPLOAD)
  141. ## Upload the most recent build. Usage: make flash <port> e.g. make flash /dev/cu.usbmodem1101
  142. ## The board may need DFU/bootloader mode first (double-tap RST): puck (Steam/Lizard) mode drops the CDC port
  143. ## so arduino-cli can't auto-reset it -- see docs/BUILD_AND_DEPLOY.md.
  144. flash:
  145. @[ -n "$(FLASH_PORT)" ] || { echo "usage: make flash <port> e.g. make flash /dev/cu.usbmodem1101 (list ports: arduino-cli board list)"; exit 1; }
  146. $(UPLOAD)
  147. ## Build then upload in one step. Usage: make deploy <port> (same build overrides as `build`).
  148. deploy:
  149. @[ -n "$(FLASH_PORT)" ] || { echo "usage: make deploy <port> e.g. make deploy /dev/cu.usbmodem1101"; exit 1; }
  150. $(MAKE) build BUILD_PATH=$(BUILD_PATH) OUTPUT_DIR=$(OUTPUT_DIR)
  151. $(UPLOAD)
  152. # Swallow the positional <port> arg so make doesn't error trying to build it as a target. Scoped to things
  153. # that look like a serial port (so a real typo like `make buld` still errors instead of silently no-op'ing).
  154. /dev/% COM% tty% cu.%:
  155. @:
  156. ## Reformat all C/C++ sources in place using the Linux kernel style.
  157. format:
  158. $(CLANG_FORMAT) -i $(FORMAT_FILES)
  159. ## Verify all C/C++ sources are formatted; exit non-zero if any differ.
  160. ## Used by CI to reject unformatted code.
  161. format-check:
  162. $(CLANG_FORMAT) --dry-run --Werror $(FORMAT_FILES)
  163. ## Everything CI gates on.
  164. check: format-check