This is an automated email from the ASF dual-hosted git repository.
jerzy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git
The following commit(s) were added to refs/heads/master by this push:
new 1a6c6d9fc tinyusb/cdc_uart: Add package for USB to UART bridge
1a6c6d9fc is described below
commit 1a6c6d9fc6c1ebcc7ea936b5b3d7f1c7a82f9670
Author: Jerzy Kasenberg <[email protected]>
AuthorDate: Tue Mar 17 15:14:05 2026 +0100
tinyusb/cdc_uart: Add package for USB to UART bridge
With this package it's possible to redirect USB CDC
interface to one of UART's on the board.
Signed-off-by: Jerzy Kasenberg <[email protected]>
---
hw/usb/tinyusb/cdc_uart/pkg.yml | 33 ++++
hw/usb/tinyusb/cdc_uart/src/cdc_uart.c | 328 +++++++++++++++++++++++++++++++++
hw/usb/tinyusb/cdc_uart/syscfg.yml | 54 ++++++
3 files changed, 415 insertions(+)
diff --git a/hw/usb/tinyusb/cdc_uart/pkg.yml b/hw/usb/tinyusb/cdc_uart/pkg.yml
new file mode 100644
index 000000000..c46cee671
--- /dev/null
+++ b/hw/usb/tinyusb/cdc_uart/pkg.yml
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/usb/tinyusb/cdc_uart
+pkg.description: Package implements USB to UART interface.
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+ - cdc
+ - uart
+
+pkg.deps:
+ - "@apache-mynewt-core/hw/usb/tinyusb"
+ - "@apache-mynewt-core/hw/usb/tinyusb/cdc"
+
+pkg.init:
+ cdc_uart_init: $before:tinyusb_start
diff --git a/hw/usb/tinyusb/cdc_uart/src/cdc_uart.c
b/hw/usb/tinyusb/cdc_uart/src/cdc_uart.c
new file mode 100644
index 000000000..11f0ff6c5
--- /dev/null
+++ b/hw/usb/tinyusb/cdc_uart/src/cdc_uart.c
@@ -0,0 +1,328 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <syscfg/syscfg.h>
+#include <sysinit/sysinit.h>
+#include <bsp/bsp.h>
+#include <hal/hal_gpio.h>
+#include <class/cdc/cdc_device.h>
+#include <cdc/cdc.h>
+#include <uart/uart.h>
+
+#define TX_LED_PIN MYNEWT_VAL(CDC_UART_TX_LED_PIN)
+#define RX_LED_PIN MYNEWT_VAL(CDC_UART_RX_LED_PIN)
+#define TX_LED_ON_VALUE MYNEWT_VAL(CDC_UART_TX_LED_ON_VALUE)
+#define RX_LED_ON_VALUE MYNEWT_VAL(CDC_UART_RX_LED_ON_VALUE)
+
+static struct os_event tx_flush_event;
+static const struct cdc_callbacks cdc_cdc_uart_callback;
+static struct uart_dev *uart_dev;
+static bool tx_led_state;
+static bool rx_led_state;
+
+static int cdc_uart_tx_char(void *arg);
+static int cdc_uart_rx_char(void *arg, uint8_t b);
+static void cdc_uart_tx_done(void *arg);
+
+struct uart_conf cdc_uart_conf = {
+ .uc_speed = 1000000,
+ .uc_databits = 8,
+ .uc_stopbits = 1,
+ .uc_parity = UART_PARITY_NONE,
+ .uc_flow_ctl = UART_FLOW_CTL_NONE,
+ .uc_tx_char = cdc_uart_tx_char,
+ .uc_rx_char = cdc_uart_rx_char,
+ .uc_tx_done = cdc_uart_tx_done,
+ .uc_cb_arg = NULL,
+};
+
+typedef struct fifo {
+ uint8_t *buf;
+ int uart_rx_wr;
+ int uart_rx_rd;
+ int buf_size;
+ int mask;
+} fifo_t;
+
+static uint8_t uart_rx_fifo_buf[MYNEWT_VAL(CDC_UART_RX_FIFO_SIZE)];
+fifo_t uart_rx_fifo = {
+ .buf = uart_rx_fifo_buf,
+ .buf_size = sizeof(uart_rx_fifo_buf),
+ .mask = (sizeof(uart_rx_fifo_buf) * 2) - 1,
+};
+
+static uint8_t uart_tx_fifo_buf[MYNEWT_VAL(CDC_UART_TX_FIFO_SIZE)];
+fifo_t uart_tx_fifo = {
+ .buf = uart_tx_fifo_buf,
+ .buf_size = sizeof(uart_tx_fifo_buf),
+ .mask = (sizeof(uart_tx_fifo_buf) * 2) - 1,
+};
+
+int
+fifo_put(fifo_t *fifo, uint8_t b)
+{
+ int rd = fifo->uart_rx_rd;
+ int wr = fifo->uart_rx_wr;
+ int mask2 = fifo->mask;
+ int mask1 = mask2 >> 1;
+ if (((wr - rd) & mask2) < fifo->buf_size) {
+ fifo->buf[wr & mask1] = b;
+ fifo->uart_rx_wr = (wr + 1) & fifo->mask;
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+int
+fifo_get(fifo_t *fifo)
+{
+ int ret = -1;
+ int rd = fifo->uart_rx_rd;
+ int wr = fifo->uart_rx_wr;
+ int mask2 = fifo->mask;
+ int mask1 = mask2 >> 1;
+ if (rd != wr) {
+ ret = fifo->buf[rd & mask1];
+ fifo->uart_rx_rd = (rd + 1) & mask2;
+ }
+ return ret;
+}
+
+int
+fifo_available(const fifo_t *fifo)
+{
+ int rd = fifo->uart_rx_rd;
+ int wr = fifo->uart_rx_wr;
+ int mask2 = fifo->mask;
+ int mask1 = mask2 >> 1;
+ return (wr - rd) & mask1;
+}
+
+int
+fifo_space(const fifo_t *fifo)
+{
+ return fifo->buf_size - fifo_available(fifo);
+}
+
+typedef struct cdc_cdc_uart_itf {
+ /* CDC Interface */
+ cdc_itf_t cdc_itf;
+ bool uart_tx_in_progress;
+} cdc_cdc_uart_itf_t;
+
+static cdc_cdc_uart_itf_t cdc_cdc_uart_itf = {
+ .cdc_itf.callbacks = &cdc_cdc_uart_callback,
+};
+
+static void
+tx_led_on(void)
+{
+ tx_led_state = true;
+ if (TX_LED_PIN >= 0) {
+ if (TX_LED_PIN != RX_LED_PIN) {
+ hal_gpio_write(TX_LED_PIN, TX_LED_ON_VALUE);
+ } else {
+ hal_gpio_write(TX_LED_PIN, TX_LED_ON_VALUE);
+ }
+ }
+}
+
+static void
+tx_led_off(void)
+{
+ tx_led_state = false;
+ if (TX_LED_PIN >= 0) {
+ if (TX_LED_PIN != RX_LED_PIN) {
+ hal_gpio_write(TX_LED_PIN, !TX_LED_ON_VALUE);
+ } else {
+ if (!rx_led_state) {
+ hal_gpio_write(TX_LED_PIN, !TX_LED_ON_VALUE);
+ }
+ }
+ }
+}
+
+static void
+rx_led_on(void)
+{
+ rx_led_state = true;
+ if (RX_LED_PIN >= 0) {
+ if (TX_LED_PIN != RX_LED_PIN) {
+ hal_gpio_write(RX_LED_PIN, RX_LED_ON_VALUE);
+ } else {
+ hal_gpio_write(RX_LED_PIN, RX_LED_ON_VALUE);
+ }
+ }
+}
+
+static void
+rx_led_off(void)
+{
+ rx_led_state = false;
+ if (RX_LED_PIN >= 0) {
+ if (TX_LED_PIN != RX_LED_PIN) {
+ hal_gpio_write(RX_LED_PIN, !RX_LED_ON_VALUE);
+ } else {
+ if (!tx_led_state) {
+ hal_gpio_write(RX_LED_PIN, !RX_LED_ON_VALUE);
+ }
+ }
+ }
+}
+
+static void
+cdc_cdc_uart_schedule_tx_flush(void)
+{
+ os_eventq_put(os_eventq_dflt_get(), &tx_flush_event);
+}
+
+static void
+tx_flush_ev_cb(struct os_event *ev)
+{
+ int n;
+
+ while (fifo_available(&uart_rx_fifo)) {
+ n = tud_cdc_n_write_char(cdc_cdc_uart_itf.cdc_itf.cdc_num,
+ (char)fifo_get(&uart_rx_fifo));
+ if (n == 0) {
+ break;
+ }
+ }
+ uart_start_rx(uart_dev);
+ tud_cdc_n_write_flush(cdc_cdc_uart_itf.cdc_itf.cdc_num);
+}
+
+static int
+cdc_uart_tx_char(void *arg)
+{
+ int ret = -1;
+
+ tx_led_on();
+ if (fifo_available(&uart_tx_fifo)) {
+ ret = fifo_get(&uart_tx_fifo);
+ }
+ return ret;
+}
+
+static int
+cdc_uart_rx_char(void *arg, uint8_t b)
+{
+ int ret = fifo_put(&uart_rx_fifo, b);
+
+ rx_led_on();
+ cdc_cdc_uart_schedule_tx_flush();
+
+ return ret;
+}
+
+static void
+cdc_uart_tx_done(void *arg)
+{
+ cdc_cdc_uart_itf.uart_tx_in_progress = false;
+ tx_led_off();
+}
+
+static void
+cdc_cdc_uart_rx_cb(cdc_itf_t *itf)
+{
+ cdc_cdc_uart_itf_t *cdc_itf = (cdc_cdc_uart_itf_t *)itf;
+ int b;
+
+ while (tud_cdc_n_available(itf->cdc_num) && fifo_space(&uart_tx_fifo)) {
+ b = tud_cdc_n_read_char(itf->cdc_num);
+ fifo_put(&uart_tx_fifo, b);
+ }
+ if (!cdc_itf->uart_tx_in_progress) {
+ cdc_itf->uart_tx_in_progress = true;
+ uart_start_tx(uart_dev);
+ }
+}
+
+static void
+cdc_cdc_uart_line_state_cb(cdc_itf_t *itf, bool dtr, bool rts)
+{
+}
+
+static uint8_t _cdc_stop_bits[] = { 1, 1, 2, 1, 1 };
+static uint8_t
+stop_bits(uint8_t cdc_stop_bits)
+{
+ return _cdc_stop_bits[cdc_stop_bits];
+}
+
+static void
+cdc_cdc_uart_coding_cb(cdc_itf_t *itf, cdc_line_coding_t const *p_line_coding)
+{
+ (void)itf;
+
+ if (uart_dev) {
+ if (cdc_uart_conf.uc_speed == p_line_coding->bit_rate &&
+ cdc_uart_conf.uc_databits == stop_bits(p_line_coding->data_bits) &&
+ cdc_uart_conf.uc_parity == p_line_coding->parity) {
+ return;
+ }
+ os_dev_close(&uart_dev->ud_dev);
+ }
+ cdc_uart_conf.uc_parity = p_line_coding->parity;
+ cdc_uart_conf.uc_databits = p_line_coding->data_bits;
+ cdc_uart_conf.uc_speed = p_line_coding->bit_rate;
+ cdc_uart_conf.uc_stopbits = stop_bits(p_line_coding->stop_bits);
+ uart_dev = uart_open(MYNEWT_VAL(CDC_UART_UART_DEV_NAME), &cdc_uart_conf);
+}
+
+static void
+cdc_cdc_uart_send_break_cb(cdc_itf_t *itf, uint16_t duration_ms)
+{
+}
+
+static void
+cdc_cdc_uart_tx_complete_cb(cdc_itf_t *itf)
+{
+ rx_led_off();
+ cdc_cdc_uart_schedule_tx_flush();
+}
+
+static const struct cdc_callbacks cdc_cdc_uart_callback = {
+ .cdc_rx_cb = cdc_cdc_uart_rx_cb,
+ .cdc_line_coding_cb = cdc_cdc_uart_coding_cb,
+ .cdc_line_state_cb = cdc_cdc_uart_line_state_cb,
+ .cdc_rx_wanted_cb = NULL,
+ .cdc_send_break_cb = cdc_cdc_uart_send_break_cb,
+ .cdc_tx_complete_cb = cdc_cdc_uart_tx_complete_cb,
+};
+
+void
+cdc_uart_init(void)
+{
+ SYSINIT_ASSERT_ACTIVE();
+
+ if (TX_LED_PIN >= 0) {
+ hal_gpio_init_out(TX_LED_PIN, !TX_LED_ON_VALUE);
+ }
+ if (RX_LED_PIN >= 0 && RX_LED_PIN != TX_LED_PIN) {
+ hal_gpio_init_out(RX_LED_PIN, !RX_LED_ON_VALUE);
+ }
+ tx_flush_event.ev_cb = tx_flush_ev_cb;
+ uart_dev = uart_open(MYNEWT_VAL(CDC_UART_UART_DEV_NAME), &cdc_uart_conf);
+
+ cdc_itf_add(&cdc_cdc_uart_itf.cdc_itf);
+}
diff --git a/hw/usb/tinyusb/cdc_uart/syscfg.yml
b/hw/usb/tinyusb/cdc_uart/syscfg.yml
new file mode 100644
index 000000000..bdc8c2c0a
--- /dev/null
+++ b/hw/usb/tinyusb/cdc_uart/syscfg.yml
@@ -0,0 +1,54 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+ CDC_UART_UART_DEV_NAME:
+ description: Device name of UART device for CDC_UART"
+ value: '"uart0"'
+
+ CDC_UART_RX_FIFO_SIZE:
+ description: >
+ RX FIFO size. FIFO that keeps data received over UART,
+ before they are transported out of USB.
+ value: 2048
+
+ CDC_UART_TX_FIFO_SIZE:
+ description: >
+ TX FIFO size. FIFO that keeps data received from USB,
+ before they are transported out of UART.
+ value: 2048
+
+ CDC_UART_TX_LED_PIN:
+ description: TX LED pin that.
+ value: -1
+
+ CDC_UART_TX_LED_ON_VALUE:
+ description: Value set to CDC_UART_TX_LED_PIN to lit LED.
+ value: 1
+
+ CDC_UART_RX_LED_PIN:
+ description: RX LED pin that.
+ value: -1
+
+ CDC_UART_RX_LED_ON_VALUE:
+ description: Value set to USB2UART_RX_LED_PIN to lit LED.
+ value: 1
+
+syscfg.vals:
+ USBD_CDC: 1