From 6d8eb270fc68dec39ffd2c76b17070a69318355a Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 25 Apr 2025 16:58:25 +0100 Subject: [PATCH] samples: usb: add a cdc_acm_bridge sample Add a sample for the UART bridge driver using a USB CDC-ACM UART and a normal one. Demonstrates how to do settings propagation (bitrate changes) and multiple instances. The sample needs to know what specific UART are available on each board, so unfortunately any additional board needs its own overlay. Signed-off-by: Fabio Baltieri --- .../subsys/usb/cdc_acm_bridge/CMakeLists.txt | 9 ++ samples/subsys/usb/cdc_acm_bridge/Kconfig | 9 ++ samples/subsys/usb/cdc_acm_bridge/README.rst | 51 ++++++++++++ samples/subsys/usb/cdc_acm_bridge/app.overlay | 24 ++++++ .../cdc_acm_bridge/boards/rpi_pico.overlay | 42 ++++++++++ samples/subsys/usb/cdc_acm_bridge/prj.conf | 11 +++ samples/subsys/usb/cdc_acm_bridge/sample.yaml | 12 +++ samples/subsys/usb/cdc_acm_bridge/src/main.c | 82 +++++++++++++++++++ 8 files changed, 240 insertions(+) create mode 100644 samples/subsys/usb/cdc_acm_bridge/CMakeLists.txt create mode 100644 samples/subsys/usb/cdc_acm_bridge/Kconfig create mode 100644 samples/subsys/usb/cdc_acm_bridge/README.rst create mode 100644 samples/subsys/usb/cdc_acm_bridge/app.overlay create mode 100644 samples/subsys/usb/cdc_acm_bridge/boards/rpi_pico.overlay create mode 100644 samples/subsys/usb/cdc_acm_bridge/prj.conf create mode 100644 samples/subsys/usb/cdc_acm_bridge/sample.yaml create mode 100644 samples/subsys/usb/cdc_acm_bridge/src/main.c diff --git a/samples/subsys/usb/cdc_acm_bridge/CMakeLists.txt b/samples/subsys/usb/cdc_acm_bridge/CMakeLists.txt new file mode 100644 index 00000000000..aca90a852dc --- /dev/null +++ b/samples/subsys/usb/cdc_acm_bridge/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(cdc_acm_bridge) + +include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake) +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/subsys/usb/cdc_acm_bridge/Kconfig b/samples/subsys/usb/cdc_acm_bridge/Kconfig new file mode 100644 index 00000000000..871d1f872d7 --- /dev/null +++ b/samples/subsys/usb/cdc_acm_bridge/Kconfig @@ -0,0 +1,9 @@ +# Copyright 2025 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +# Source common USB sample options used to initialize new experimental USB +# device stack. The scope of these options is limited to USB samples in project +# tree, you cannot use them in your own application. +source "samples/subsys/usb/common/Kconfig.sample_usbd" + +source "Kconfig.zephyr" diff --git a/samples/subsys/usb/cdc_acm_bridge/README.rst b/samples/subsys/usb/cdc_acm_bridge/README.rst new file mode 100644 index 00000000000..3e10c94141c --- /dev/null +++ b/samples/subsys/usb/cdc_acm_bridge/README.rst @@ -0,0 +1,51 @@ +.. zephyr:code-sample:: usb-cdc-acm-bridge + :name: USB CDC-ACM bridge + :relevant-api: uart_interface + + Use USB CDC-ACM driver to implement a serial port bridge. + +Overview +******** + +This sample app demonstrates use of a USB CDC-ACM to bridge a standard hardware +UART on a supported board. +This sample can be found under :zephyr_file:`samples/subsys/usb/cdc_acm_bridge` in the +Zephyr project tree. + +Requirements +************ + +This project requires an USB device driver, which is available for multiple +boards supported in Zephyr. + +The board has to have an USB interface as well as hardware UART, the device +node for the hardware UART is board specific so each supported board needs an +explicit overlay. + +Building and Running +******************** + +Reel Board +=========== + +To see the console output of the app, open a serial port emulator and +attach it to the USB to TTL Serial cable. Build and flash the project: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/usb/cdc_acm_bridge + :board: reel_board + :goals: flash + :compact: + +Running +======= + +Plug the board into a host device, for example, a PC running Linux, the board +should enumerate as a CDC-ACM device, the CDC-ACM input and output should be +echoed back to the real UART, and configuration changes to the CDC-ACM uart +such as bitrate will be propagated to the hardware port. + +If the hardware port is connected to an on-board debugger then the output +should be echoed between the two ports, if it's connected to some pins on the +boards the pins can be shorted together to test that the driver is working +correctly. diff --git a/samples/subsys/usb/cdc_acm_bridge/app.overlay b/samples/subsys/usb/cdc_acm_bridge/app.overlay new file mode 100644 index 00000000000..ede7539223c --- /dev/null +++ b/samples/subsys/usb/cdc_acm_bridge/app.overlay @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + uart-bridge0 { + compatible = "zephyr,uart-bridge"; + peers = <&cdc_acm_uart0 &arduino_serial>; + }; + +}; + +&arduino_serial { + status = "okay"; +}; + +&zephyr_udc0 { + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + label = "Zephyr USB CDC-ACM"; + }; +}; diff --git a/samples/subsys/usb/cdc_acm_bridge/boards/rpi_pico.overlay b/samples/subsys/usb/cdc_acm_bridge/boards/rpi_pico.overlay new file mode 100644 index 00000000000..d78ef0060e6 --- /dev/null +++ b/samples/subsys/usb/cdc_acm_bridge/boards/rpi_pico.overlay @@ -0,0 +1,42 @@ +/ { + uart-bridge0 { + compatible = "zephyr,uart-bridge"; + peers = <&cdc_acm_uart0 &uart0>; + }; + + uart-bridge1 { + compatible = "zephyr,uart-bridge"; + peers = <&cdc_acm_uart1 &uart1>; + }; +}; + +&pinctrl { + uart1_default: uart1_default { + group1 { + pinmux = ; + }; + group2 { + pinmux = ; + input-enable; + }; + }; +}; + +&uart1 { + current-speed = <115200>; + status = "okay"; + pinctrl-0 = <&uart1_default>; + pinctrl-names = "default"; +}; + +&zephyr_udc0 { + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + label = "Zephyr USB CDC-ACM uart0"; + }; + + cdc_acm_uart1: cdc_acm_uart1 { + compatible = "zephyr,cdc-acm-uart"; + label = "Zephyr USB CDC-ACM uart1"; + }; +}; diff --git a/samples/subsys/usb/cdc_acm_bridge/prj.conf b/samples/subsys/usb/cdc_acm_bridge/prj.conf new file mode 100644 index 00000000000..e9aa6fc7d2a --- /dev/null +++ b/samples/subsys/usb/cdc_acm_bridge/prj.conf @@ -0,0 +1,11 @@ +CONFIG_UART_CONSOLE=n + +CONFIG_USB_DEVICE_STACK_NEXT=y + +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y +CONFIG_UART_USE_RUNTIME_CONFIGURE=y +CONFIG_USBD_CDC_ACM_CLASS=y + +CONFIG_SAMPLE_USBD_PID=0x0001 +CONFIG_SAMPLE_USBD_PRODUCT="USBD CDC ACM bridge sample" diff --git a/samples/subsys/usb/cdc_acm_bridge/sample.yaml b/samples/subsys/usb/cdc_acm_bridge/sample.yaml new file mode 100644 index 00000000000..81860b70e7d --- /dev/null +++ b/samples/subsys/usb/cdc_acm_bridge/sample.yaml @@ -0,0 +1,12 @@ +sample: + name: CDC ACM USB bridge +tests: + sample.usb.cdc-acm-bridge.arduino_serial: + tags: usb + build_only: true + depends_on: usbd arduino_serial + sample.usb.cdc-acm-bridge.two_devices: + tags: usb + build_only: true + platform_allow: + - rpi_pico diff --git a/samples/subsys/usb/cdc_acm_bridge/src/main.c b/samples/subsys/usb/cdc_acm_bridge/src/main.c new file mode 100644 index 00000000000..32614e01357 --- /dev/null +++ b/samples/subsys/usb/cdc_acm_bridge/src/main.c @@ -0,0 +1,82 @@ +/* + * Copyright 2025 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +LOG_MODULE_REGISTER(cdc_acm_bridge, LOG_LEVEL_INF); + +const struct device *const uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); + +static struct usbd_context *sample_usbd; + +#define DEVICE_DT_GET_COMMA(node_id) DEVICE_DT_GET(node_id), + +const struct device *uart_bridges[] = { + DT_FOREACH_STATUS_OKAY(zephyr_uart_bridge, DEVICE_DT_GET_COMMA) +}; + +static void sample_msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) +{ + LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); + + if (usbd_can_detect_vbus(ctx)) { + if (msg->type == USBD_MSG_VBUS_READY) { + if (usbd_enable(ctx)) { + LOG_ERR("Failed to enable device support"); + } + } + + if (msg->type == USBD_MSG_VBUS_REMOVED) { + if (usbd_disable(ctx)) { + LOG_ERR("Failed to disable device support"); + } + } + } + + if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING || + msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE) { + for (uint8_t i = 0; i < ARRAY_SIZE(uart_bridges); i++) { + /* update all bridges, non valid combinations are + * skipped automatically. + */ + uart_bridge_settings_update(msg->dev, uart_bridges[i]); + } + } +} + +int main(void) +{ + int err; + + sample_usbd = sample_usbd_init_device(sample_msg_cb); + if (sample_usbd == NULL) { + LOG_ERR("Failed to initialize USB device"); + return -ENODEV; + } + + if (!usbd_can_detect_vbus(sample_usbd)) { + err = usbd_enable(sample_usbd); + if (err) { + LOG_ERR("Failed to enable device support"); + return err; + } + } + + LOG_INF("USB device support enabled"); + + k_sleep(K_FOREVER); + + return 0; +}