/* * SPDX-License-Identifier: Apache-2.0 * ZephCore - USB CDC ACM with 1200 baud touch detection (Repeater) * * This module initializes USB CDC ACM with a message callback to detect * 1200 baud touch for entering UF2 bootloader mode. */ #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(zephcore_usb_repeater, CONFIG_ZEPHCORE_BOARD_LOG_LEVEL); #include #include "ZephyrRepeaterUSB.h" /* * USB Device Configuration * Since we're not using CDC_ACM_SERIAL_INITIALIZE_AT_BOOT, we define our own values. */ #define ZEPHCORE_USB_VID 0x2fe3 /* Zephyr Project VID */ #define ZEPHCORE_USB_PID 0x0004 /* CDC ACM PID */ #define ZEPHCORE_USB_MAX_POWER 125 /* 250mA in 2mA units */ /* * USB Device Context - based on Zephyr's cdc_acm_serial.c * but with message callback for 1200 baud touch detection. */ USBD_DEVICE_DEFINE(zephcore_usbd, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), ZEPHCORE_USB_VID, ZEPHCORE_USB_PID); USBD_DESC_LANG_DEFINE(zephcore_lang); USBD_DESC_MANUFACTURER_DEFINE(zephcore_mfr, "ZephCore"); USBD_DESC_PRODUCT_DEFINE(zephcore_product, "ZephCore Repeater"); USBD_DESC_SERIAL_NUMBER_DEFINE(zephcore_sn); USBD_DESC_CONFIG_DEFINE(zephcore_fs_cfg_desc, "FS Configuration"); USBD_DESC_CONFIG_DEFINE(zephcore_hs_cfg_desc, "HS Configuration"); USBD_CONFIGURATION_DEFINE(zephcore_fs_config, 0, /* attributes: bus-powered */ ZEPHCORE_USB_MAX_POWER, &zephcore_fs_cfg_desc); USBD_CONFIGURATION_DEFINE(zephcore_hs_config, 0, /* attributes: bus-powered */ ZEPHCORE_USB_MAX_POWER, &zephcore_hs_cfg_desc); /* ZephyrBoard instance for bootloader entry */ static mesh::ZephyrBoard usb_board; /* Track DTR state for Arduino-style 1200 baud touch detection. * Arduino triggers on DTR drop (high→low) while baud rate is 1200. * adafruit-nrfutil --touch 1200 opens port at 1200 baud (DTR high), * then closes it (DTR low). We detect both the baud rate change and * the DTR drop to cover all host tool implementations. */ static bool dtr_was_active; static void enter_bootloader(void) { LOG_WRN("Entering bootloader (1200 baud touch)"); /* Small delay to let USB disconnect cleanly */ k_msleep(100); usb_board.rebootToBootloader(); /* Never returns */ } /* * USB message callback - detect 1200 baud touch for bootloader entry. * Two detection methods (matching Arduino behavior): * 1. Baud rate set to 1200 (SET_LINE_CODING) * 2. DTR drop while baud is 1200 (SET_CONTROL_LINE_STATE) */ static void usbd_msg_callback(struct usbd_context *const ctx, const struct usbd_msg *msg) { if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) { uint32_t baudrate = 0; int ret = uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_BAUD_RATE, &baudrate); if (ret == 0) { LOG_DBG("CDC ACM baud rate: %u", baudrate); if (baudrate == 1200) { enter_bootloader(); } } } else if (msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE) { uint32_t dtr = 0; uint32_t baudrate = 0; uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_DTR, &dtr); uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_BAUD_RATE, &baudrate); LOG_DBG("CDC ACM DTR=%u baud=%u", dtr, baudrate); /* Arduino method: DTR drop (high→low) while baud is 1200 */ if (dtr_was_active && !dtr && baudrate == 1200) { enter_bootloader(); } dtr_was_active = (dtr != 0); } } static int register_cdc_acm(struct usbd_context *const uds_ctx, const enum usbd_speed speed) { struct usbd_config_node *cfg_nd; int err; if (speed == USBD_SPEED_HS) { cfg_nd = &zephcore_hs_config; } else { cfg_nd = &zephcore_fs_config; } err = usbd_add_configuration(uds_ctx, speed, cfg_nd); if (err) { LOG_ERR("Failed to add configuration"); return err; } err = usbd_register_class(&zephcore_usbd, "cdc_acm_0", speed, 1); if (err) { LOG_ERR("Failed to register CDC ACM class"); return err; } return usbd_device_set_code_triple(uds_ctx, speed, USB_BCC_MISCELLANEOUS, 0x02, 0x01); } extern "C" int zephcore_usbd_init(void) { int err; LOG_INF("Initializing ZephCore USB CDC with 1200 baud touch detection"); /* Add string descriptors */ err = usbd_add_descriptor(&zephcore_usbd, &zephcore_lang); if (err) { LOG_ERR("Failed to add language descriptor (%d)", err); return err; } err = usbd_add_descriptor(&zephcore_usbd, &zephcore_mfr); if (err) { LOG_ERR("Failed to add manufacturer descriptor (%d)", err); return err; } err = usbd_add_descriptor(&zephcore_usbd, &zephcore_product); if (err) { LOG_ERR("Failed to add product descriptor (%d)", err); return err; } err = usbd_add_descriptor(&zephcore_usbd, &zephcore_sn); if (err) { LOG_ERR("Failed to add serial number descriptor (%d)", err); return err; } /* Register configurations */ if (USBD_SUPPORTS_HIGH_SPEED && usbd_caps_speed(&zephcore_usbd) == USBD_SPEED_HS) { err = register_cdc_acm(&zephcore_usbd, USBD_SPEED_HS); if (err) { return err; } } err = register_cdc_acm(&zephcore_usbd, USBD_SPEED_FS); if (err) { return err; } /* Register message callback for 1200 baud detection */ err = usbd_msg_register_cb(&zephcore_usbd, usbd_msg_callback); if (err) { LOG_ERR("Failed to register message callback (%d)", err); return err; } /* Initialize USB device */ err = usbd_init(&zephcore_usbd); if (err) { LOG_ERR("Failed to initialize USB device (%d)", err); return err; } /* Enable USB device */ err = usbd_enable(&zephcore_usbd); if (err) { LOG_ERR("Failed to enable USB device (%d)", err); return err; } LOG_INF("ZephCore USB CDC initialized"); return 0; }