Files
ZephCore/zephcore/adapters/usb/ZephyrRepeaterUSB.cpp
T
2026-03-29 09:51:53 +02:00

204 lines
5.6 KiB
C++

/*
* 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 <stdint.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/usb/usbd.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(zephcore_usb_repeater, CONFIG_ZEPHCORE_BOARD_LOG_LEVEL);
#include <adapters/board/ZephyrBoard.h>
#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;
}