From cf14d4f1fd6d3049e3417d82ab18a2c9aa43b370 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Thu, 4 Jan 2024 16:43:57 +0000 Subject: [PATCH] samples: drivers: uart: Add a 'passthrough' example This sample can be used as a "virtual wire", allowing direct access to devices connected to the target processor - for example: GPS, Cellular, etc... This is also useful as a utility - no other connectivity options exist for UART, where other interfaces like SPI and I2C have shell commands. Signed-off-by: Attie Grande --- .../drivers/uart/passthrough/CMakeLists.txt | 7 + samples/drivers/uart/passthrough/README.rst | 50 ++++++ .../passthrough/boards/nucleo_l476rg.overlay | 12 ++ samples/drivers/uart/passthrough/prj.conf | 3 + samples/drivers/uart/passthrough/sample.yml | 14 ++ samples/drivers/uart/passthrough/src/main.c | 150 ++++++++++++++++++ 6 files changed, 236 insertions(+) create mode 100644 samples/drivers/uart/passthrough/CMakeLists.txt create mode 100644 samples/drivers/uart/passthrough/README.rst create mode 100644 samples/drivers/uart/passthrough/boards/nucleo_l476rg.overlay create mode 100644 samples/drivers/uart/passthrough/prj.conf create mode 100644 samples/drivers/uart/passthrough/sample.yml create mode 100644 samples/drivers/uart/passthrough/src/main.c diff --git a/samples/drivers/uart/passthrough/CMakeLists.txt b/samples/drivers/uart/passthrough/CMakeLists.txt new file mode 100644 index 000000000000..ca7c7f7faa51 --- /dev/null +++ b/samples/drivers/uart/passthrough/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(passthrough) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/drivers/uart/passthrough/README.rst b/samples/drivers/uart/passthrough/README.rst new file mode 100644 index 000000000000..94d58f3dca17 --- /dev/null +++ b/samples/drivers/uart/passthrough/README.rst @@ -0,0 +1,50 @@ +.. zephyr:code-sample:: uart-passthrough + :name: UART Passthrough + :relevant-api: uart_interface + + Pass data directly between the console and another UART interface. + +Overview +******** + +This sample will virtually connect two UART interfaces together, 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/drivers/uart/passthrough`. + +Requirements +************ + +#. One UART interface, identified as Zephyr's console. +#. 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 diff --git a/samples/drivers/uart/passthrough/boards/nucleo_l476rg.overlay b/samples/drivers/uart/passthrough/boards/nucleo_l476rg.overlay new file mode 100644 index 000000000000..5292b54e5f5a --- /dev/null +++ b/samples/drivers/uart/passthrough/boards/nucleo_l476rg.overlay @@ -0,0 +1,12 @@ +/ { + chosen { + uart,passthrough = &uart4; + }; +}; + +&uart4 { + pinctrl-0 = <&uart4_tx_pa0 &uart4_rx_pa1>; + pinctrl-names = "default"; + current-speed = <9600>; + status = "okay"; +}; diff --git a/samples/drivers/uart/passthrough/prj.conf b/samples/drivers/uart/passthrough/prj.conf new file mode 100644 index 000000000000..70eec2fbac3d --- /dev/null +++ b/samples/drivers/uart/passthrough/prj.conf @@ -0,0 +1,3 @@ +CONFIG_SERIAL=y +CONFIG_RING_BUFFER=y +CONFIG_UART_INTERRUPT_DRIVEN=y diff --git a/samples/drivers/uart/passthrough/sample.yml b/samples/drivers/uart/passthrough/sample.yml new file mode 100644 index 000000000000..5e4abf91f4d0 --- /dev/null +++ b/samples/drivers/uart/passthrough/sample.yml @@ -0,0 +1,14 @@ +sample: + name: UART Passthrough +tests: + sample.drivers.uart: + integration_platforms: + - qemu_x86 + tags: + - serial + - uart + filter: CONFIG_SERIAL and + CONFIG_UART_INTERRUPT_DRIVEN and + dt_chosen_enabled("zephyr,console") and + dt_chosen_enabled("uart,passthrough") + harness: keyboard diff --git a/samples/drivers/uart/passthrough/src/main.c b/samples/drivers/uart/passthrough/src/main.c new file mode 100644 index 000000000000..2168207a7516 --- /dev/null +++ b/samples/drivers/uart/passthrough/src/main.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024 Argentum Systems Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include + +struct patch_info { + const uint8_t * const name; + + const struct device *rx_dev; + struct ring_buf *rx_ring_buf; + bool rx_error; + bool rx_overflow; + + const struct device *tx_dev; +}; + +#define DEV_CONSOLE DEVICE_DT_GET(DT_CHOSEN(zephyr_console)) +#define DEV_OTHER DEVICE_DT_GET(DT_CHOSEN(uart_passthrough)) + +#define RING_BUF_SIZE 64 + +RING_BUF_DECLARE(rb_console, RING_BUF_SIZE); +struct patch_info patch_c2o = { + .name = "c2o", + + .rx_dev = DEV_CONSOLE, + .rx_ring_buf = &rb_console, + .rx_error = false, + .rx_overflow = false, + + .tx_dev = DEV_OTHER, +}; + +RING_BUF_DECLARE(rb_other, RING_BUF_SIZE); +struct patch_info patch_o2c = { + .name = "o2c", + + .rx_dev = DEV_OTHER, + .rx_ring_buf = &rb_other, + .rx_error = false, + .rx_overflow = false, + + .tx_dev = DEV_CONSOLE, +}; + +static void uart_cb(const struct device *dev, void *ctx) +{ + struct patch_info *patch = (struct patch_info *)ctx; + int ret; + uint8_t *buf; + uint32_t len; + + while (uart_irq_update(patch->rx_dev) > 0) { + ret = uart_irq_rx_ready(patch->rx_dev); + if (ret < 0) { + patch->rx_error = true; + } + if (ret <= 0) { + break; + } + + len = ring_buf_put_claim(patch->rx_ring_buf, &buf, RING_BUF_SIZE); + if (len == 0) { + /* no space for Rx, disable the IRQ */ + uart_irq_rx_disable(patch->rx_dev); + patch->rx_overflow = true; + break; + } + + ret = uart_fifo_read(patch->rx_dev, buf, len); + if (ret < 0) { + patch->rx_error = true; + } + if (ret <= 0) { + break; + } + len = ret; + + ret = ring_buf_put_finish(patch->rx_ring_buf, len); + if (ret != 0) { + patch->rx_error = true; + break; + } + } +} + +static void passthrough(struct patch_info *patch) +{ + int ret; + uint8_t *buf; + uint32_t len; + + if (patch->rx_error) { + printk("<<%s: Rx Error!>>\n", patch->name); + patch->rx_error = false; + } + + if (patch->rx_overflow) { + printk("<<%s: Rx Overflow!>>\n", patch->name); + patch->rx_overflow = false; + } + + len = ring_buf_get_claim(patch->rx_ring_buf, &buf, RING_BUF_SIZE); + if (len == 0) { + goto done; + } + + ret = uart_fifo_fill(patch->tx_dev, buf, len); + if (ret < 0) { + goto error; + } + len = ret; + + ret = ring_buf_get_finish(patch->rx_ring_buf, len); + if (ret < 0) { + goto error; + } + +done: + uart_irq_rx_enable(patch->rx_dev); + return; + +error: + printk("<<%s: Tx Error!>>\n", patch->name); +} + +int main(void) +{ + printk("Console Device: %p\n", patch_c2o.rx_dev); + printk("Other Device: %p\n", patch_o2c.rx_dev); + + uart_irq_callback_user_data_set(patch_c2o.rx_dev, uart_cb, (void *)&patch_c2o); + uart_irq_callback_user_data_set(patch_o2c.rx_dev, uart_cb, (void *)&patch_o2c); + + for (;;) { + passthrough(&patch_c2o); + passthrough(&patch_o2c); + } + + return 0; +}