-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <attie.grande@argentum-systems.co.uk>
- Loading branch information
1 parent
018dbcf
commit cf14d4f
Showing
6 changed files
with
236 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,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) |
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:: 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 |
12 changes: 12 additions & 0 deletions
12
samples/drivers/uart/passthrough/boards/nucleo_l476rg.overlay
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 @@ | ||
/ { | ||
chosen { | ||
uart,passthrough = &uart4; | ||
}; | ||
}; | ||
|
||
&uart4 { | ||
pinctrl-0 = <&uart4_tx_pa0 &uart4_rx_pa1>; | ||
pinctrl-names = "default"; | ||
current-speed = <9600>; | ||
status = "okay"; | ||
}; |
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,3 @@ | ||
CONFIG_SERIAL=y | ||
CONFIG_RING_BUFFER=y | ||
CONFIG_UART_INTERRUPT_DRIVEN=y |
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,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 |
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,150 @@ | ||
/* | ||
* Copyright (c) 2024 Argentum Systems Ltd. | ||
* | ||
* 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> | ||
|
||
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; | ||
} |