Переглянути джерело

Zephyr port: real USBD backing (HID pool + CDC console), retire stub

Replace the inert usb_stub with the functional Zephyr USBD backing:
- app.overlay declares a 5-node zephyr,hid-device pool the TinyUSB shim binds
  Adafruit_USBD_HID objects to in begin() order.
- usb_device_setup.cpp defines the usbd_context, registers all DT classes (HID
  pool + the board CDC ACM console), tracks mount/suspend, and applies the
  per-mode VID/PID/bcdDevice the firmware set via the shim.
- usb_glue.cpp: HID get/set/output report callbacks dispatch to the modes;
  sendReport -> hid_device_submit_report; remoteWakeup -> usbd_wakeup_request.
- serial_cdc_glue.cpp wires the Arduino Serial shim (incl. the debug console
  input) to the CDC ACM UART.

Composite enumerates as HID + CDC. WebUSB vendor pipe and the XInput custom
class remain inert (need dedicated Zephyr classes); flagged in code + doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jari 4 місяців тому
батько
коміт
f4254f76ff

+ 3 - 1
OpenPuckZephyr/CMakeLists.txt

@@ -22,7 +22,9 @@ target_sources(app PRIVATE
   # compatibility layer
   src/compat/arduino_compat.cpp
   src/compat/littlefs_shim.cpp
-  src/compat/usb_stub.cpp        # inert USB backing (swap for usb_glue.cpp)
+  src/compat/serial_cdc_glue.cpp
+  src/compat/usb_glue.cpp           # Zephyr USBD backing for the TinyUSB shim
+  src/compat/usb_device_setup.cpp   # usbd_context (HID pool + CDC) bring-up
 
   # ported firmware modules
   src/config.cpp

+ 59 - 0
OpenPuckZephyr/app.overlay

@@ -0,0 +1,59 @@
+/*
+ * OpenPuck USB device topology for the Zephyr USBD stack.
+ *
+ * The TinyUSB shim binds Adafruit_USBD_HID objects to this pool of HID device
+ * nodes in begin() order (see usb_glue.cpp). The puck mode needs the most: a
+ * wake mouse + four bond-slot HID interfaces = 5. The board already declares a
+ * CDC ACM console (board_cdc_acm_uart) on zephyr_udc0, which usbd_register_all_
+ * classes picks up; serial_console is wired to it.
+ *
+ * in-report-size 64 covers the largest puck HID report; 1 ms polling matches the
+ * ~250 Hz+ RF cadence (the RF link, not USB, is the rate limit).
+ */
+/ {
+	hid_dev_0: hid_dev_0 {
+		compatible = "zephyr,hid-device";
+		label = "OPK_HID0";
+		protocol-code = "none";
+		in-polling-period-us = <1000>;
+		in-report-size = <64>;
+		out-polling-period-us = <1000>;
+		out-report-size = <64>;
+	};
+	hid_dev_1: hid_dev_1 {
+		compatible = "zephyr,hid-device";
+		label = "OPK_HID1";
+		protocol-code = "none";
+		in-polling-period-us = <1000>;
+		in-report-size = <64>;
+		out-polling-period-us = <1000>;
+		out-report-size = <64>;
+	};
+	hid_dev_2: hid_dev_2 {
+		compatible = "zephyr,hid-device";
+		label = "OPK_HID2";
+		protocol-code = "none";
+		in-polling-period-us = <1000>;
+		in-report-size = <64>;
+		out-polling-period-us = <1000>;
+		out-report-size = <64>;
+	};
+	hid_dev_3: hid_dev_3 {
+		compatible = "zephyr,hid-device";
+		label = "OPK_HID3";
+		protocol-code = "none";
+		in-polling-period-us = <1000>;
+		in-report-size = <64>;
+		out-polling-period-us = <1000>;
+		out-report-size = <64>;
+	};
+	hid_dev_4: hid_dev_4 {
+		compatible = "zephyr,hid-device";
+		label = "OPK_HID4";
+		protocol-code = "none";
+		in-polling-period-us = <1000>;
+		in-report-size = <64>;
+		out-polling-period-us = <1000>;
+		out-report-size = <64>;
+	};
+};

+ 12 - 1
OpenPuckZephyr/prj.conf

@@ -13,10 +13,21 @@ CONFIG_REQUIRES_FULL_LIBCPP=n
 CONFIG_CPP_EXCEPTIONS=n
 CONFIG_CPP_RTTI=n
 
-# Console/logging over RTT or UART during bring-up (USB CDC console added later).
+# Console/logging.
 CONFIG_LOG=y
 CONFIG_PRINTK=y
 
+# USB device stack (next/USBD). The HID pool (app.overlay) + the board CDC ACM
+# console register as classes; the firmware brings the stack up via the TinyUSB
+# shim (USBDevice.attach -> opk_usbd_enable), not at boot.
+CONFIG_USB_DEVICE_STACK_NEXT=y
+CONFIG_USBD_HID_SUPPORT=y
+CONFIG_USBD_CDC_ACM_CLASS=y
+CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n
+CONFIG_HWINFO=y
+CONFIG_USBD_LOG_LEVEL_ERR=y
+CONFIG_USBD_HID_LOG_LEVEL_ERR=y
+
 # Core subsystems used by the port.
 CONFIG_GPIO=y
 CONFIG_FLASH=y

+ 4 - 0
OpenPuckZephyr/src/compat/Adafruit_TinyUSB.h

@@ -79,6 +79,9 @@ typedef struct __attribute__((packed)) {
 } hid_mouse_report_t;
 
 // ---- HID keyboard usage IDs (lizard keyboard map) ----
+// Guarded against Zephyr's <zephyr/usb/class/hid.h>, which defines the same
+// standard usage IDs; in TUs that pull in both, Zephyr's win (identical values).
+#ifndef ZEPHYR_INCLUDE_USB_CLASS_HID_H_
 enum {
 	HID_KEY_NONE = 0x00,
 	HID_KEY_A = 0x04,
@@ -94,6 +97,7 @@ enum {
 	HID_KEY_ARROW_DOWN = 0x51,
 	HID_KEY_ARROW_UP = 0x52,
 };
+#endif
 
 // ---- packed descriptor structs used by the XInput class ----
 typedef struct __attribute__((packed)) {

+ 52 - 0
OpenPuckZephyr/src/compat/serial_cdc_glue.cpp

@@ -0,0 +1,52 @@
+// serial_cdc_glue.cpp -- routes the Arduino Serial shim to the USB CDC ACM.
+//
+// The board's chosen console is the CDC ACM UART (board_cdc_acm_uart), so
+// Serial output already reaches USB via printk. This unit additionally wires the
+// input side (serial_console's single-letter command reader) and a direct write
+// path by strongly overriding the weak opk_serial_* hooks in arduino_compat.cpp.
+#include <zephyr/device.h>
+#include <zephyr/drivers/uart.h>
+#include <stdint.h>
+#include <stddef.h>
+
+static const struct device *const cdc = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
+
+// One-byte pushback so available()+read() work without a UART peek primitive.
+static int s_have = -1;
+
+extern "C" int opk_serial_available(void)
+{
+	if (s_have >= 0)
+		return 1;
+	unsigned char c;
+	if (device_is_ready(cdc) && uart_poll_in(cdc, &c) == 0) {
+		s_have = c;
+		return 1;
+	}
+	return 0;
+}
+
+extern "C" int opk_serial_read(void)
+{
+	if (s_have >= 0) {
+		int c = s_have;
+		s_have = -1;
+		return c;
+	}
+	unsigned char c;
+	if (device_is_ready(cdc) && uart_poll_in(cdc, &c) == 0)
+		return c;
+	return -1;
+}
+
+extern "C" int opk_serial_avail_write(void)
+{
+	return 64;
+}
+
+extern "C" size_t opk_serial_write(uint8_t b)
+{
+	if (device_is_ready(cdc))
+		uart_poll_out(cdc, b);
+	return 1;
+}

+ 117 - 0
OpenPuckZephyr/src/compat/usb_device_setup.cpp

@@ -0,0 +1,117 @@
+// usb_device_setup.cpp -- the Zephyr usbd_context for the OpenPuck port.
+//
+// Builds one full-speed device, registers all class instances picked up from
+// devicetree (the HID pool in app.overlay + the board CDC ACM console), and
+// brings the stack up. The TinyUSB shim's USBDevice.attach()/detach() call
+// opk_usbd_enable()/disable(); the per-mode VID/PID/bcdDevice the firmware set
+// through the shim (g_opk_usb_id) are applied here just before enable.
+#include <zephyr/usb/usbd.h>
+#include <zephyr/usb/usb_device.h>
+#include <zephyr/device.h>
+#include <zephyr/devicetree.h>
+#include <zephyr/logging/log.h>
+
+#include "Adafruit_TinyUSB.h"
+#include "usb_identity.h"
+
+LOG_MODULE_REGISTER(opk_usbd, LOG_LEVEL_INF);
+
+extern "C" void opk_usb_msg(const enum usbd_msg_type type);
+
+USBD_DEVICE_DEFINE(opk_usbd, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), 0x28DE,
+		   0x1304);
+
+USBD_DESC_LANG_DEFINE(opk_lang);
+USBD_DESC_MANUFACTURER_DEFINE(opk_mfr, "OpenPuck");
+USBD_DESC_PRODUCT_DEFINE(opk_product, "OpenPuck");
+USBD_DESC_SERIAL_NUMBER_DEFINE(opk_sn);
+
+USBD_DESC_CONFIG_DEFINE(opk_fs_cfg_desc, "OpenPuck FS");
+
+// Bus-powered + remote-wakeup (the firmware always arms remote wakeup).
+static const uint8_t opk_attr = USB_SCD_REMOTE_WAKEUP;
+USBD_CONFIGURATION_DEFINE(opk_fs_config, opk_attr, 250, &opk_fs_cfg_desc);
+
+static bool s_setup_done;
+static bool s_enabled;
+
+static void msg_cb(struct usbd_context *const ctx, const struct usbd_msg *const msg)
+{
+	(void)ctx;
+	opk_usb_msg(msg->type);
+}
+
+static int do_setup(void)
+{
+	int err;
+
+	if (s_setup_done)
+		return 0;
+
+	if ((err = usbd_add_descriptor(&opk_usbd, &opk_lang)) ||
+	    (err = usbd_add_descriptor(&opk_usbd, &opk_mfr)) ||
+	    (err = usbd_add_descriptor(&opk_usbd, &opk_product)) ||
+	    (err = usbd_add_descriptor(&opk_usbd, &opk_sn))) {
+		LOG_ERR("descriptor add failed (%d)", err);
+		return err;
+	}
+
+	err = usbd_add_configuration(&opk_usbd, USBD_SPEED_FS, &opk_fs_config);
+	if (err) {
+		LOG_ERR("add configuration failed (%d)", err);
+		return err;
+	}
+
+	// Register every class instance from devicetree (HID pool + board CDC ACM).
+	err = usbd_register_all_classes(&opk_usbd, USBD_SPEED_FS, 1, NULL);
+	if (err) {
+		LOG_ERR("register classes failed (%d)", err);
+		return err;
+	}
+
+	usbd_device_set_code_triple(&opk_usbd, USBD_SPEED_FS, 0, 0, 0);
+	usbd_msg_register_cb(&opk_usbd, msg_cb);
+	s_setup_done = true;
+	return 0;
+}
+
+extern "C" struct usbd_context *opk_usbd_ctx(void)
+{
+	return &opk_usbd;
+}
+
+extern "C" int opk_usbd_enable(void)
+{
+	int err = do_setup();
+	if (err)
+		return err;
+
+	// Apply the per-mode identity the firmware staged through the shim.
+	usbd_device_set_vid(&opk_usbd, g_opk_usb_id.vid);
+	usbd_device_set_pid(&opk_usbd, g_opk_usb_id.pid);
+	usbd_device_set_bcd_device(&opk_usbd, g_opk_usb_id.bcd_device);
+
+	if (s_enabled)
+		return 0;
+	err = usbd_init(&opk_usbd);
+	if (err && err != -EALREADY) {
+		LOG_ERR("usbd_init failed (%d)", err);
+		return err;
+	}
+	err = usbd_enable(&opk_usbd);
+	if (err && err != -EALREADY) {
+		LOG_ERR("usbd_enable failed (%d)", err);
+		return err;
+	}
+	s_enabled = true;
+	return 0;
+}
+
+extern "C" int opk_usbd_disable(void)
+{
+	if (!s_enabled)
+		return 0;
+	int err = usbd_disable(&opk_usbd);
+	s_enabled = false;
+	return err;
+}

+ 96 - 20
OpenPuckZephyr/src/compat/usb_glue.cpp

@@ -9,8 +9,8 @@
 // (device state, HID submit/callbacks) are functional; the WebUSB vendor pipe
 // and the XInput custom class are scaffolded to compile and are completed in
 // their own glue units (see usb_webusb_glue / mode_xinput port).
-#include "Adafruit_TinyUSB.h"
-
+// Zephyr USB headers FIRST so their HID enums/keys are defined; the shim header
+// then suppresses its duplicate definitions via the matching include guards.
 #include <zephyr/usb/usbd.h>
 #include <zephyr/usb/usb_device.h>
 #include <zephyr/usb/class/usbd_hid.h>
@@ -18,6 +18,9 @@
 #include <zephyr/devicetree.h>
 #include <zephyr/kernel.h>
 #include <zephyr/sys/atomic.h>
+#include <string.h>
+
+#include "Adafruit_TinyUSB.h"
 
 // ---- device state, updated from the USBD message callback ----
 static atomic_t s_mounted = ATOMIC_INIT(0);
@@ -213,37 +216,37 @@ bool Adafruit_USBD_HID::sendReport(uint8_t report_id, const void *report,
 #include "usb_identity.h"
 opk_usb_identity g_opk_usb_id;
 
-void TinyUSBDevice::setID(uint16_t vid, uint16_t pid)
+void Adafruit_USBD_Device::setID(uint16_t vid, uint16_t pid)
 {
 	g_opk_usb_id.vid = vid;
 	g_opk_usb_id.pid = pid;
 }
-void TinyUSBDevice::setVersion(uint16_t bcd)
+void Adafruit_USBD_Device::setVersion(uint16_t bcd)
 {
 	g_opk_usb_id.bcd_usb = bcd;
 }
-void TinyUSBDevice::setDeviceVersion(uint16_t bcd)
+void Adafruit_USBD_Device::setDeviceVersion(uint16_t bcd)
 {
 	g_opk_usb_id.bcd_device = bcd;
 }
-void TinyUSBDevice::setManufacturerDescriptor(const char *s)
+void Adafruit_USBD_Device::setManufacturerDescriptor(const char *s)
 {
 	g_opk_usb_id.manufacturer = s;
 }
-void TinyUSBDevice::setProductDescriptor(const char *s)
+void Adafruit_USBD_Device::setProductDescriptor(const char *s)
 {
 	g_opk_usb_id.product = s;
 }
-void TinyUSBDevice::setSerialDescriptor(const char *s)
+void Adafruit_USBD_Device::setSerialDescriptor(const char *s)
 {
 	g_opk_usb_id.serial = s;
 }
-void TinyUSBDevice::setConfigurationBuffer(uint8_t *buf, uint32_t buflen)
+void Adafruit_USBD_Device::setConfigurationBuffer(uint8_t *buf, uint32_t buflen)
 {
 	(void)buf;
 	(void)buflen; // Zephyr builds its own descriptor set
 }
-void TinyUSBDevice::setConfigurationAttribute(uint8_t attr)
+void Adafruit_USBD_Device::setConfigurationAttribute(uint8_t attr)
 {
 	g_opk_usb_id.cfg_attr = attr;
 }
@@ -251,24 +254,24 @@ void TinyUSBDevice::setConfigurationAttribute(uint8_t attr)
 // Interface/endpoint allocation + dynamic add: the Zephyr descriptor set is
 // fixed at init, so these are recorded for the XInput class glue and are no-ops
 // against the live stack (see the reboot-to-reenumerate concession).
-uint8_t TinyUSBDevice::allocInterface(uint8_t count)
+uint8_t Adafruit_USBD_Device::allocInterface(uint8_t count)
 {
 	uint8_t base = g_opk_usb_id.next_itf;
 	g_opk_usb_id.next_itf += count;
 	return base;
 }
-uint8_t TinyUSBDevice::allocEndpoint(uint8_t dir)
+uint8_t Adafruit_USBD_Device::allocEndpoint(uint8_t dir)
 {
 	uint8_t n = ++g_opk_usb_id.next_ep;
 	return (dir ? 0x80 : 0x00) | n;
 }
-bool TinyUSBDevice::addInterface(Adafruit_USBD_Interface &itf)
+bool Adafruit_USBD_Device::addInterface(Adafruit_USBD_Interface &itf)
 {
 	(void)itf;
 	return true;
 }
 
-void TinyUSBDevice::clearConfiguration()
+void Adafruit_USBD_Device::clearConfiguration()
 {
 	s_hid_claimed = 0;
 	g_opk_usb_id.next_itf = 0;
@@ -276,23 +279,23 @@ void TinyUSBDevice::clearConfiguration()
 }
 
 // detach/attach map to disabling/enabling the usbd stack (opk_usbd_*, above).
-bool TinyUSBDevice::detach()
+bool Adafruit_USBD_Device::detach()
 {
 	return opk_usbd_disable() == 0;
 }
-bool TinyUSBDevice::attach()
+bool Adafruit_USBD_Device::attach()
 {
 	return opk_usbd_enable() == 0;
 }
-bool TinyUSBDevice::mounted()
+bool Adafruit_USBD_Device::mounted()
 {
 	return opk_usb_mounted();
 }
-bool TinyUSBDevice::suspended()
+bool Adafruit_USBD_Device::suspended()
 {
 	return opk_usb_suspended();
 }
-bool TinyUSBDevice::remoteWakeup()
+bool Adafruit_USBD_Device::remoteWakeup()
 {
 	struct usbd_context *ctx = opk_usbd_ctx();
 	if (!ctx)
@@ -300,7 +303,8 @@ bool TinyUSBDevice::remoteWakeup()
 	return usbd_wakeup_request(ctx) == 0;
 }
 
-TinyUSBDevice USBDevice;
+Adafruit_USBD_Device USBDevice;
+Adafruit_USBD_Device &TinyUSBDevice = USBDevice;
 
 // USBD message callback: track mount/suspend. Registered by usb_device_setup.
 extern "C" void opk_usb_msg(const enum usbd_msg_type type)
@@ -322,3 +326,75 @@ extern "C" void opk_usb_msg(const enum usbd_msg_type type)
 		break;
 	}
 }
+
+// ===================== WebUSB (vendor) =====================
+// Scaffolded inert: the WebUSB vendor pipe needs a Zephyr custom vendor class +
+// BOS/MS-OS-2.0 descriptors (the config panel won't connect until then). The
+// firmware tolerates connected()==false (it simply skips the panel).
+Adafruit_USBD_WebUSB::Adafruit_USBD_WebUSB()
+{
+}
+bool Adafruit_USBD_WebUSB::begin()
+{
+	return true;
+}
+bool Adafruit_USBD_WebUSB::connected()
+{
+	return false;
+}
+int Adafruit_USBD_WebUSB::available()
+{
+	return 0;
+}
+int Adafruit_USBD_WebUSB::read()
+{
+	return -1;
+}
+uint32_t Adafruit_USBD_WebUSB::write(const void *, uint32_t n)
+{
+	return n;
+}
+void Adafruit_USBD_WebUSB::flush()
+{
+}
+
+// ===================== DFU entry =====================
+#include "Arduino.h"
+void enterUf2Dfu(void)
+{
+	NRF_POWER->GPREGRET = 0x57; // Adafruit bootloader: UF2 mass-storage
+	NVIC_SystemReset();
+}
+void enterSerialDfu(void)
+{
+	NRF_POWER->GPREGRET = 0x4E; // Adafruit bootloader: serial (OTA) DFU
+	NVIC_SystemReset();
+}
+
+// ===================== XInput custom-class endpoint stubs =====================
+// XInput needs a real Zephyr custom USBD class to enumerate (see mode_xinput).
+// These keep the class driver's report logic compiling/inert until then.
+#include "device/usbd_pvt.h"
+extern "C" bool tud_mounted(void)
+{
+	return opk_usb_mounted();
+}
+bool usbd_edpt_open(uint8_t, const tusb_desc_endpoint_t *)
+{
+	return false;
+}
+bool usbd_edpt_xfer(uint8_t, uint8_t, uint8_t *, uint16_t)
+{
+	return false;
+}
+bool usbd_edpt_busy(uint8_t, uint8_t)
+{
+	return false;
+}
+bool usbd_edpt_claim(uint8_t, uint8_t)
+{
+	return false;
+}
+void usbd_edpt_release(uint8_t, uint8_t)
+{
+}

+ 0 - 213
OpenPuckZephyr/src/compat/usb_stub.cpp

@@ -1,213 +0,0 @@
-// usb_stub.cpp -- inert TinyUSB-shim backing used during core bring-up.
-//
-// Lets the non-USB firmware (radio/protocol/modes-logic/data) compile and link
-// before the real Zephyr USBD backing (usb_glue.cpp + DT HID nodes + usbd
-// context) is wired in. Every method is a safe no-op: not mounted/suspended,
-// HIDs never ready so sendReport()s are skipped. Swapped out for usb_glue.cpp
-// once the USBD stack is up.
-#include "Adafruit_TinyUSB.h"
-#include "usb_identity.h"
-#include <string.h>
-
-opk_usb_identity g_opk_usb_id;
-
-Adafruit_USBD_HID::Adafruit_USBD_HID()
-{
-}
-void Adafruit_USBD_HID::setReportDescriptor(const uint8_t *desc, uint16_t len)
-{
-	_desc = desc;
-	_desc_len = len;
-}
-void Adafruit_USBD_HID::setReportCallback(hid_get_report_cb_t g,
-					  hid_set_report_cb_t s)
-{
-	_get_cb = g;
-	_set_cb = s;
-}
-void Adafruit_USBD_HID::setStringDescriptor(const char *s)
-{
-	_str = s;
-}
-void Adafruit_USBD_HID::setPollInterval(uint8_t i)
-{
-	_poll_ms = i;
-}
-void Adafruit_USBD_HID::setBootProtocol(uint8_t p)
-{
-	_boot_proto = p;
-}
-void Adafruit_USBD_HID::enableOutEndpoint(bool e)
-{
-	_has_out = e;
-}
-bool Adafruit_USBD_HID::begin()
-{
-	return true;
-}
-bool Adafruit_USBD_HID::ready()
-{
-	return false;
-}
-bool Adafruit_USBD_HID::sendReport(uint8_t, const void *, uint16_t)
-{
-	return false;
-}
-
-Adafruit_USBD_WebUSB::Adafruit_USBD_WebUSB()
-{
-}
-bool Adafruit_USBD_WebUSB::begin()
-{
-	return true;
-}
-bool Adafruit_USBD_WebUSB::connected()
-{
-	return false;
-}
-int Adafruit_USBD_WebUSB::available()
-{
-	return 0;
-}
-int Adafruit_USBD_WebUSB::read()
-{
-	return -1;
-}
-uint32_t Adafruit_USBD_WebUSB::write(const void *, uint32_t n)
-{
-	return n;
-}
-void Adafruit_USBD_WebUSB::flush()
-{
-}
-
-void Adafruit_USBD_Device::setID(uint16_t v, uint16_t p)
-{
-	g_opk_usb_id.vid = v;
-	g_opk_usb_id.pid = p;
-}
-void Adafruit_USBD_Device::setVersion(uint16_t b)
-{
-	g_opk_usb_id.bcd_usb = b;
-}
-void Adafruit_USBD_Device::setDeviceVersion(uint16_t b)
-{
-	g_opk_usb_id.bcd_device = b;
-}
-void Adafruit_USBD_Device::setManufacturerDescriptor(const char *s)
-{
-	g_opk_usb_id.manufacturer = s;
-}
-void Adafruit_USBD_Device::setProductDescriptor(const char *s)
-{
-	g_opk_usb_id.product = s;
-}
-void Adafruit_USBD_Device::setSerialDescriptor(const char *s)
-{
-	g_opk_usb_id.serial = s;
-}
-void Adafruit_USBD_Device::setConfigurationBuffer(uint8_t *, uint32_t)
-{
-}
-void Adafruit_USBD_Device::setConfigurationAttribute(uint8_t a)
-{
-	g_opk_usb_id.cfg_attr = a;
-}
-uint8_t Adafruit_USBD_Device::allocInterface(uint8_t count)
-{
-	uint8_t b = g_opk_usb_id.next_itf;
-	g_opk_usb_id.next_itf += count;
-	return b;
-}
-uint8_t Adafruit_USBD_Device::allocEndpoint(uint8_t dir)
-{
-	uint8_t n = ++g_opk_usb_id.next_ep;
-	return (dir ? 0x80 : 0x00) | n;
-}
-bool Adafruit_USBD_Device::addInterface(Adafruit_USBD_Interface &)
-{
-	return true;
-}
-void Adafruit_USBD_Device::clearConfiguration()
-{
-	g_opk_usb_id.next_itf = 0;
-	g_opk_usb_id.next_ep = 0;
-}
-bool Adafruit_USBD_Device::detach()
-{
-	return true;
-}
-bool Adafruit_USBD_Device::attach()
-{
-	return true;
-}
-bool Adafruit_USBD_Device::mounted()
-{
-	return true;
-}
-bool Adafruit_USBD_Device::suspended()
-{
-	return false;
-}
-bool Adafruit_USBD_Device::remoteWakeup()
-{
-	return true;
-}
-Adafruit_USBD_Device USBDevice;
-Adafruit_USBD_Device &TinyUSBDevice = USBDevice;
-
-// DFU entry: stash the Adafruit-bootloader magic in GPREGRET and reset. 0x57 ==
-// UF2 mass-storage; 0x4E == serial (OTA) DFU. (Inert here only in that the host
-// reconnects to the bootloader; functional once flashed.)
-#include "Arduino.h"
-void enterUf2Dfu(void)
-{
-	NRF_POWER->GPREGRET = 0x57;
-	NVIC_SystemReset();
-}
-void enterSerialDfu(void)
-{
-	NRF_POWER->GPREGRET = 0x4E;
-	NVIC_SystemReset();
-}
-
-// ---- TinyUSB custom-class-driver stubs (XInput) ----
-// The XInput interface is a custom TinyUSB class driver; these are the endpoint
-// primitives it calls. Stubbed during bring-up (XInput will not enumerate until
-// the Zephyr custom class is implemented — see mode_xinput port).
-#include "device/usbd_pvt.h"
-extern "C" bool tud_mounted(void)
-{
-	return USBDevice.mounted();
-}
-bool usbd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *ep)
-{
-	(void)rhport;
-	(void)ep;
-	return false;
-}
-bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep, uint8_t *buf, uint16_t n)
-{
-	(void)rhport;
-	(void)ep;
-	(void)buf;
-	(void)n;
-	return false;
-}
-bool usbd_edpt_busy(uint8_t rhport, uint8_t ep)
-{
-	(void)rhport;
-	(void)ep;
-	return false;
-}
-bool usbd_edpt_claim(uint8_t rhport, uint8_t ep)
-{
-	(void)rhport;
-	(void)ep;
-	return false;
-}
-void usbd_edpt_release(uint8_t rhport, uint8_t ep)
-{
-	(void)rhport;
-	(void)ep;
-}