-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
samples: usb: passthrough sample application using only irq handlers
sample application which connects a physical uart to a usb cdc acm uart The complete process in handled in a single interrupt handler without interference of the application code. Signed-off-by: Vincent van der Locht <[email protected]>
- Loading branch information
1 parent
8c32a82
commit 00ad9ca
Showing
7 changed files
with
314 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
|
||
|
||
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) | ||
{ | ||
|
||
struct ring_buf *ringbuf_rx; | ||
struct ring_buf *ringbuf_tx; | ||
const struct device *peer = user_data; | ||
|
||
if(dev == usb_dev) { | ||
ringbuf_rx = &uart_ringbuf; | ||
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 | ||
|
||
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; | ||
} |