Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USB Device next cdc acm can get in a deadlock when the host doesn't pull the endpoint data #78160

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions samples/subsys/usb/passthrough_irq_usb/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, but we do not need another CDC ACM sample.


cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(passthrough)

include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
target_sources(app PRIVATE src/main.c)
9 changes: 9 additions & 0 deletions samples/subsys/usb/passthrough_irq_usb/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2023 Nordic Semiconductor ASA
# 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"
50 changes: 50 additions & 0 deletions samples/subsys/usb/passthrough_irq_usb/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.. zephyr:code-sample:: passthrough_irq_usb
:name: USB-UART Passthrough
:relevant-api: uart_interface

Pass data directly between the console and another UART interface.

Overview
********

This sample will connect a physical UART interfaces to a USB uart, as if Zephyr
and the processor were not present. Data read from the console is transmitted
to the "*other*" interface, and data read from the "*other*" interface is
relayed to the console.

The source code for this sample application can be found at:
:zephyr_file:`samples/subsys/usb/passthrough_irq_usb`.

Requirements
************

#. One USB UART interface, identified as Zephyr's first usb acm interface.
#. A second UART connected to something interesting (e.g: GPS), identified as
the chosen ``uart,passthrough`` device - the pins and baudrate will need to
be configured correctly.

Building and Running
********************

Build and flash the sample as follows, changing ``nucleo_l476rg`` for your
board:

.. zephyr-app-commands::
:zephyr-app: samples/drivers/uart/passthrough
:board: nucleo_l476rg
:goals: build flash
:compact:

Sample Output
=============

.. code-block:: console

*** Booting Zephyr OS build zephyr-v3.5.0-2988-gb84bab36b941 ***
Console Device: 0x8003940
Other Device: 0x800392c
$GNGSA,A,3,31,29,25,26,,,,,,,,,11.15,10.66,3.29,1*06
$GNGSA,A,3,,,,,,,,,,,,,11.15,10.66,3.29,2*0F
$GNGSA,A,3,,,,,,,,,,,,,11.15,10.66,3.29,3*0E
$GNGSA,A,3,,,,,,,,,,,,,11.15,10.66,3.29,4*09
$GNGSA,A,3,,,,,,,,,,,,,11.15,10.66,3.29,5*08
11 changes: 11 additions & 0 deletions samples/subsys/usb/passthrough_irq_usb/app.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

&zephyr_udc0 {
cdc_acm_uart0 {
compatible = "zephyr,cdc-acm-uart";
};
};
9 changes: 9 additions & 0 deletions samples/subsys/usb/passthrough_irq_usb/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CONFIG_SERIAL=y
CONFIG_RING_BUFFER=y
CONFIG_UART_INTERRUPT_DRIVEN=y

CONFIG_USB_DEVICE_STACK_NEXT=y
CONFIG_USBD_CDC_ACM_CLASS=y

CONFIG_SAMPLE_USBD_PID=0x0001
CONFIG_SAMPLE_USBD_PRODUCT="USBD CDC ACM sample"
12 changes: 12 additions & 0 deletions samples/subsys/usb/passthrough_irq_usb/sample.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
sample:
name: USB-UART Passthrough
tests:
sample.drivers.uart:
tags:
- serial
- uart
- usb
filter: CONFIG_SERIAL and
CONFIG_UART_INTERRUPT_DRIVEN and
dt_chosen_enabled("uart,passthrough")
harness: keyboard
215 changes: 215 additions & 0 deletions samples/subsys/usb/passthrough_irq_usb/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* Copyright (c) 2024 Argentum Systems Ltd.
* Copyright (c) 2024 SynchronicIT BV
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>

#include <stdio.h>
#include <string.h>


#include <zephyr/usb/usb_device.h>
#include <zephyr/usb/usbd.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(passthrough, LOG_LEVEL_INF);


Check notice on line 22 in samples/subsys/usb/passthrough_irq_usb/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

samples/subsys/usb/passthrough_irq_usb/src/main.c:22 - #include <zephyr/usb/usb_device.h> #include <zephyr/usb/usbd.h> #include <zephyr/logging/log.h> LOG_MODULE_REGISTER(passthrough, LOG_LEVEL_INF); -
const struct device *const usb_dev = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_uart0));
const struct device *const uart_dev = DEVICE_DT_GET(DT_CHOSEN(uart_passthrough));

#define RING_BUF_SIZE 1024

uint8_t ring_buffer[2][RING_BUF_SIZE];

struct ring_buf usb_ringbuf;
struct ring_buf uart_ringbuf;

static inline void print_baudrate(const struct device *dev)
{
uint32_t baudrate;
int ret;

ret = uart_line_ctrl_get(dev, UART_LINE_CTRL_BAUD_RATE, &baudrate);
if (ret) {
LOG_WRN("Failed to get baudrate, ret code %d", ret);
} else {
LOG_INF("Baudrate %u", baudrate);
}
}

#if defined(CONFIG_USB_DEVICE_STACK_NEXT)
static struct usbd_context *sample_usbd;
K_SEM_DEFINE(dtr_sem, 0, 1);

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_CONTROL_LINE_STATE) {
uint32_t dtr = 0U;

uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_DTR, &dtr);
if (dtr) {
k_sem_give(&dtr_sem);
}
}

if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) {
print_baudrate(msg->dev);
}
}

static int enable_usb_device_next(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");

return 0;
}
#endif /* defined(CONFIG_USB_DEVICE_STACK_NEXT) */


static void interrupt_handler(const struct device *dev, void *user_data)
{

Check failure on line 109 in samples/subsys/usb/passthrough_irq_usb/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

TRAILING_WHITESPACE

samples/subsys/usb/passthrough_irq_usb/src/main.c:109 trailing whitespace
struct ring_buf *ringbuf_rx;

Check failure on line 110 in samples/subsys/usb/passthrough_irq_usb/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

TRAILING_WHITESPACE

samples/subsys/usb/passthrough_irq_usb/src/main.c:110 trailing whitespace
struct ring_buf *ringbuf_tx;
const struct device *peer = user_data;

if(dev == usb_dev) {

Check failure on line 114 in samples/subsys/usb/passthrough_irq_usb/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

SPACING

samples/subsys/usb/passthrough_irq_usb/src/main.c:114 space required before the open parenthesis '('
ringbuf_rx = &uart_ringbuf;

Check notice on line 115 in samples/subsys/usb/passthrough_irq_usb/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

samples/subsys/usb/passthrough_irq_usb/src/main.c:115 - static void interrupt_handler(const struct device *dev, void *user_data) { - - struct ring_buf *ringbuf_rx; + + struct ring_buf *ringbuf_rx; struct ring_buf *ringbuf_tx; const struct device *peer = user_data; - if(dev == usb_dev) { + if (dev == usb_dev) {
ringbuf_tx = &usb_ringbuf;

} else {
ringbuf_rx = &usb_ringbuf;
ringbuf_tx = &uart_ringbuf;
}

while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
if (uart_irq_rx_ready(dev)) {
int recv_len, rb_len;
uint8_t buffer[64];
size_t len = MIN(ring_buf_space_get(ringbuf_rx), sizeof(buffer));

if (len == 0) {
ring_buf_get(ringbuf_rx, NULL, buffer);
len = MIN(ring_buf_space_get(ringbuf_rx), sizeof(buffer));
}

recv_len = uart_fifo_read(dev, buffer, len);
if (recv_len < 0) {
LOG_ERR("Failed to read UART FIFO");
recv_len = 0;
};

rb_len = ring_buf_put(ringbuf_rx, buffer, recv_len);
if (rb_len < recv_len) {
LOG_ERR("Drop %u bytes", recv_len - rb_len);
}

LOG_DBG("tty fifo -> ringbuf %d bytes", rb_len);
if (rb_len) {
uart_irq_tx_enable(peer);
}
}

if (uart_irq_tx_ready(dev)) {
uint8_t buffer[64];
int rb_len, send_len;

rb_len = ring_buf_get(ringbuf_tx, buffer, sizeof(buffer));
if (!rb_len) {
LOG_DBG("Ring buffer empty, disable TX IRQ");
uart_irq_tx_disable(dev);
continue;
}

send_len = uart_fifo_fill(dev, buffer, rb_len);
if (send_len < rb_len) {
LOG_ERR("Drop %d bytes", rb_len - send_len);
}

LOG_DBG("ringbuf -> tty fifo %d bytes", send_len);
}
}
}

int main(void)
{
int ret;

if (!device_is_ready(uart_dev)) {
LOG_ERR("UART device not ready");
return 0;
}

if (!device_is_ready(usb_dev)) {
LOG_ERR("CDC ACM device not ready");
return 0;
}

#if defined(CONFIG_USB_DEVICE_STACK_NEXT)
ret = enable_usb_device_next();
#else
ret = usb_enable(NULL);
#endif

Check notice on line 190 in samples/subsys/usb/passthrough_irq_usb/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

samples/subsys/usb/passthrough_irq_usb/src/main.c:190 - ret = enable_usb_device_next(); + ret = enable_usb_device_next(); #else - ret = usb_enable(NULL); + ret = usb_enable(NULL);

if (ret != 0) {
LOG_ERR("Failed to enable USB");
return 0;
}

ring_buf_init(&uart_ringbuf, sizeof(ring_buffer[0]), ring_buffer[0]);
ring_buf_init(&usb_ringbuf, sizeof(ring_buffer[1]), ring_buffer[1]);

printk("UART Device: %p\n", uart_dev);
printk("USB Device: %p\n", usb_dev);

uart_irq_callback_user_data_set(uart_dev, interrupt_handler, usb_dev);
uart_irq_callback_user_data_set(usb_dev, interrupt_handler, uart_dev);

/* Enable rx interrupts */
uart_irq_rx_enable(uart_dev);
uart_irq_rx_enable(usb_dev);

for (;;) {
k_msleep(1000);
}

return 0;
}
Loading
Loading