This is an automated email from the ASF dual-hosted git repository. jerpelea pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit b9de1e7364a91498f13a9804a3aa3622e79f4d0e Author: p-szafonimateusz <[email protected]> AuthorDate: Tue Nov 10 09:13:07 2026 +0100 drivers/serial: Add console over netdev. Add a console that sends and receives the NuttX console stream over UDP instead of a serial port. The console is a serial upper-half device registered as /dev/console, so NSH and syslog work unchanged. UDP is a datagram protocol, but the UART DMA RX path exposes only the current window of a ring buffer. A datagram larger than that window is kept in the driver and drained over several DMA windows, so no input bytes are lost when the peer sends more than one byte per datagram. Signed-off-by: p-szafonimateusz <[email protected]> --- Documentation/os/networking/console.rst | 14 + Documentation/os/networking/index.rst | 1 + drivers/drivers_initialize.c | 3 + drivers/serial/CMakeLists.txt | 6 + drivers/serial/Kconfig | 37 +++ drivers/serial/Make.defs | 6 + drivers/serial/netdev_console.c | 435 ++++++++++++++++++++++++++++++++ include/nuttx/serial/netdev_console.h | 47 ++++ 8 files changed, 549 insertions(+) diff --git a/Documentation/os/networking/console.rst b/Documentation/os/networking/console.rst new file mode 100644 index 00000000000..c0a06c94d18 --- /dev/null +++ b/Documentation/os/networking/console.rst @@ -0,0 +1,14 @@ +=================== +Console over NETDEV +=================== + +Console over NETDEV provides a remote interactive system console (stdin/stdout) +over a network instead of a serial port. It use bidirectional communication over +UDP. + +To connect to the console on the default port 55000 you can use ``socat``:: + + socat -d -d UDP-LISTEN:55000,reuseaddr STDOUT + +NOTE: you should first start ``socat``, then start NuttX, so that the console +driver has a chance to connect to your host. diff --git a/Documentation/os/networking/index.rst b/Documentation/os/networking/index.rst index ab26697fa55..c8e55d2f0d0 100644 --- a/Documentation/os/networking/index.rst +++ b/Documentation/os/networking/index.rst @@ -80,6 +80,7 @@ Network Support tcp_network_perf.rst delay_act_and_tcp_perf.rst tcp_state_machine.rst + console.rst .. toctree:: :maxdepth: 1 diff --git a/drivers/drivers_initialize.c b/drivers/drivers_initialize.c index 4715024fc24..87bbb80fbcc 100644 --- a/drivers/drivers_initialize.c +++ b/drivers/drivers_initialize.c @@ -48,6 +48,7 @@ #include <nuttx/rpmsg/rpmsg.h> #include <nuttx/segger/rtt.h> #include <nuttx/sensors/sensor.h> +#include <nuttx/serial/netdev_console.h> #include <nuttx/serial/pty.h> #include <nuttx/serial/uart_hostfs.h> #include <nuttx/serial/uart_ram.h> @@ -202,6 +203,8 @@ void drivers_initialize(void) lwlconsole_init(); #elif defined(CONFIG_CONSOLE_SYSLOG) syslog_console_init(); +#elif defined(CONFIG_CONSOLE_NETDEV) + netdev_console_init(); #endif #ifdef CONFIG_UART_HOSTFS diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 80e381b159d..31ec32a1fa2 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -97,4 +97,10 @@ if(CONFIG_RAM_UART) list(APPEND SRCS uart_ram.c) endif() +# Netdev console + +if(CONFIG_CONSOLE_NETDEV) + list(APPEND SRCS netdev_console.c) +endif() + target_sources(drivers PRIVATE ${SRCS}) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 2de72b20869..a6c1c246f2f 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1024,4 +1024,41 @@ config UART_BTH5_NPOLLWAITERS endif # UART_BTH5 +config CONSOLE_NETDEV + bool "Console on netdev" + default n + depends on NETDEVICES && SCHED_LPWORK + select SERIAL_RXDMA + select SERIAL_TXDMA + ---help--- + Use the netdev as a console device + +if CONSOLE_NETDEV + +config CONSOLE_NETDEV_IPADDR + hex "CONSOLE netdev destination IP address" + default 0xffffffff + ---help--- + Address where where netdev sends CONSOLE data. + +config CONSOLE_NETDEV_PORT + int "CONSOLE netdev port" + default 55000 + ---help--- + UDP port for CONSOLE input/output. + +config CONSOLE_NETDEV_RXBUFSIZE + int "CONSOLE netdev receive buffer size" + default 256 + +config CONSOLE_NETDEV_TXBUFSIZE + int "CONSOLE netdev transmit buffer size" + default 256 + +config CONSOLE_NETDEV_DELAY_USEC + int "CONSOLE netdev polling interval in microseconds" + default 1000 + +endif + endif # SERIAL diff --git a/drivers/serial/Make.defs b/drivers/serial/Make.defs index 12b35636460..3a2ea3b6bb0 100644 --- a/drivers/serial/Make.defs +++ b/drivers/serial/Make.defs @@ -97,6 +97,12 @@ ifeq ($(CONFIG_16550_PCI_UART),y) CSRCS += uart_pci_16550.c endif +# Netdev console + +ifeq ($(CONFIG_CONSOLE_NETDEV),y) + CSRCS += netdev_console.c +endif + # Include serial build support DEPPATH += --dep-path serial diff --git a/drivers/serial/netdev_console.c b/drivers/serial/netdev_console.c new file mode 100644 index 00000000000..ecf861e76cc --- /dev/null +++ b/drivers/serial/netdev_console.c @@ -0,0 +1,435 @@ +/**************************************************************************** + * drivers/serial/netdev_console.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <errno.h> +#include <debug.h> +#include <string.h> + +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <nuttx/signal.h> +#include <nuttx/net/net.h> +#include <nuttx/serial/serial.h> +#include <nuttx/wqueue.h> + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +#define CONSOLE_WORK_DELAY USEC2TICK(CONFIG_CONSOLE_NETDEV_DELAY_USEC) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct netdev_consol_s +{ + struct uart_dev_s dev; + struct socket psock; + uint16_t rxdgram_off; + uint16_t rxdgram_len; + uint8_t rxdgram[CONFIG_CONSOLE_NETDEV_RXBUFSIZE]; + bool rxint; + struct work_s work; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int netdev_console_setup(FAR struct uart_dev_s *dev); +static void netdev_console_shutdown(FAR struct uart_dev_s *dev); +static int netdev_console_attach(FAR struct uart_dev_s *dev); +static void netdev_console_detach(FAR struct uart_dev_s *dev); +static void netdev_console_rxint(FAR struct uart_dev_s *dev, bool enable); +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool netdev_console_rxflowcontrol(FAR struct uart_dev_s *dev, + unsigned int nbuffered, + bool upper); +#endif +static void netdev_console_dmasend(FAR struct uart_dev_s *dev); +static void netdev_console_dmareceive(FAR struct uart_dev_s *dev); +static void netdev_console_dmarxfree(FAR struct uart_dev_s *dev); +static void netdev_console_dmatxavail(FAR struct uart_dev_s *dev); +static void netdev_console_send(FAR struct uart_dev_s *dev, int ch); +static void netdev_console_txint(FAR struct uart_dev_s *dev, bool enable); +static bool netdev_console_txready(FAR struct uart_dev_s *dev); +static bool netdev_console_txempty(FAR struct uart_dev_s *dev); +static void netdev_console_work(FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char g_netdev_console_rxbuf[CONFIG_CONSOLE_NETDEV_RXBUFSIZE]; +static char g_netdev_console_txbuf[CONFIG_CONSOLE_NETDEV_TXBUFSIZE]; + +static const struct uart_ops_s g_netdev_console_ops = +{ + .setup = netdev_console_setup, + .shutdown = netdev_console_shutdown, + .attach = netdev_console_attach, + .detach = netdev_console_detach, + .rxint = netdev_console_rxint, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = netdev_console_rxflowcontrol, +#endif + .dmasend = netdev_console_dmasend, + .dmareceive = netdev_console_dmareceive, + .dmarxfree = netdev_console_dmarxfree, + .dmatxavail = netdev_console_dmatxavail, + .send = netdev_console_send, + .txint = netdev_console_txint, + .txready = netdev_console_txready, + .txempty = netdev_console_txempty, +}; + +static struct netdev_consol_s g_netdev_console = +{ + .dev = + { + .ops = &g_netdev_console_ops, + .xmit = + { + .size = CONFIG_CONSOLE_NETDEV_TXBUFSIZE, + .buffer = g_netdev_console_txbuf, + }, + .recv = + { + .size = CONFIG_CONSOLE_NETDEV_RXBUFSIZE, + .buffer = g_netdev_console_rxbuf, + }, + .isconsole = true + }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netdev_console_setup + ****************************************************************************/ + +static int netdev_console_setup(FAR struct uart_dev_s *dev) +{ + return OK; +} + +/**************************************************************************** + * Name: netdev_console_shutdown + ****************************************************************************/ + +static void netdev_console_shutdown(FAR struct uart_dev_s *dev) +{ +} + +/**************************************************************************** + * Name: netdev_console_attach + ****************************************************************************/ + +static int netdev_console_attach(FAR struct uart_dev_s *dev) +{ + FAR struct netdev_consol_s *priv = (FAR struct netdev_consol_s *)dev; + struct sockaddr_in addr; + int ret; + + priv->rxdgram_off = 0; + priv->rxdgram_len = 0; + + /* Initialize UDP socket for CONSOLE TX/RX */ + + ret = psock_socket(PF_INET, SOCK_DGRAM, 0, &priv->psock); + if (ret < 0) + { + nerr("ERROR: Failed to create a socket: %d\n", ret); + return ret; + } + + /* Bind with source socket */ + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(CONFIG_CONSOLE_NETDEV_IPADDR); + addr.sin_port = htons(CONFIG_CONSOLE_NETDEV_PORT); + + ret = psock_connect(&priv->psock, (FAR struct sockaddr *)&addr, + sizeof(addr)); + if (ret < 0) + { + psock_close(&priv->psock); + nerr("ERROR: Failed to connect socket: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: netdev_console_detach + ****************************************************************************/ + +static void netdev_console_detach(FAR struct uart_dev_s *dev) +{ + FAR struct netdev_consol_s *priv = (FAR struct netdev_consol_s *)dev; + + priv->rxdgram_off = 0; + priv->rxdgram_len = 0; + psock_close(&priv->psock); +} + +/**************************************************************************** + * Name: netdev_console_rxint + ****************************************************************************/ + +static void netdev_console_rxint(FAR struct uart_dev_s *dev, bool enable) +{ + FAR struct netdev_consol_s *priv = (FAR struct netdev_consol_s *)dev; + + priv->rxint = enable; + if (enable) + { + work_queue(LPWORK, &priv->work, netdev_console_work, dev, 0); + } + else + { + work_cancel(LPWORK, &priv->work); + } +} + +#ifdef CONFIG_SERIAL_IFLOWCONTROL +/**************************************************************************** + * Name: netdev_console_rxflowcontrol + ****************************************************************************/ + +static bool netdev_console_rxflowcontrol(FAR struct uart_dev_s *dev, + unsigned int nbuffered, bool upper) +{ + return false; +} +#endif + +/**************************************************************************** + * Name: netdev_console_dmasend + ****************************************************************************/ + +static void netdev_console_dmasend(FAR struct uart_dev_s *dev) +{ + FAR struct netdev_consol_s *priv = (FAR struct netdev_consol_s *)dev; + int retry = 3; + ssize_t ret; + + do + { + ret = psock_send(&priv->psock, dev->dmatx.buffer, dev->dmatx.length, + 0); + + /* Wait for ARP */ + + if (ret < 0) + { + nxsig_sleep(1); + } + } + while (ret < 0 && retry--); + + dev->dmatx.nbytes = ret > 0 ? ret : 0; + uart_xmitchars_done(dev); +} + +/**************************************************************************** + * Name: netdev_console_dmareceive + ****************************************************************************/ + +static void netdev_console_dmareceive(FAR struct uart_dev_s *dev) +{ + FAR struct netdev_consol_s *priv = (FAR struct netdev_consol_s *)dev; + uint16_t space; + uint16_t copylen; + uint16_t first; + uint16_t second; + ssize_t ret; + + space = dev->dmarx.length + dev->dmarx.nlength; + if (space == 0) + { + dev->dmarx.nbytes = 0; + uart_recvchars_done(dev); + return; + } + + /* Console expects a continuous byte stream. UDP/ISO-TP recv returns one + * datagram, and any bytes that do not fit in this DMA window are lost. + * Keep one datagram here and drain it over multiple DMA windows. + */ + + if (priv->rxdgram_off >= priv->rxdgram_len) + { + ret = psock_recv(&priv->psock, priv->rxdgram, sizeof(priv->rxdgram), + MSG_DONTWAIT); + if (ret <= 0) + { + dev->dmarx.nbytes = 0; + uart_recvchars_done(dev); + return; + } + + priv->rxdgram_off = 0; + priv->rxdgram_len = ret; + } + + copylen = space; + if (copylen > priv->rxdgram_len - priv->rxdgram_off) + { + copylen = priv->rxdgram_len - priv->rxdgram_off; + } + + /* First DMA segment */ + + first = copylen > dev->dmarx.length ? dev->dmarx.length : copylen; + memcpy(dev->dmarx.buffer, &priv->rxdgram[priv->rxdgram_off], first); + + /* Second DMA segment */ + + second = copylen - first; + if (second > 0 && dev->dmarx.nbuffer != NULL) + { + memcpy(dev->dmarx.nbuffer, &priv->rxdgram[priv->rxdgram_off + first], + second); + } + + priv->rxdgram_off += copylen; + if (priv->rxdgram_off >= priv->rxdgram_len) + { + priv->rxdgram_off = 0; + priv->rxdgram_len = 0; + } + + dev->dmarx.nbytes = copylen; + uart_recvchars_done(dev); +} + +/**************************************************************************** + * Name: netdev_console_dmarxfree + ****************************************************************************/ + +static void netdev_console_dmarxfree(FAR struct uart_dev_s *dev) +{ + uart_recvchars_dma(dev); +} + +/**************************************************************************** + * Name: netdev_console_dmatxavail + ****************************************************************************/ + +static void netdev_console_dmatxavail(FAR struct uart_dev_s *dev) +{ + uart_xmitchars_dma(dev); +} + +/**************************************************************************** + * Name: netdev_console_send + ****************************************************************************/ + +static void netdev_console_send(FAR struct uart_dev_s *dev, int ch) +{ + FAR struct netdev_consol_s *priv = (FAR struct netdev_consol_s *)dev; + char tmp = ch; + + psock_send(&priv->psock, &tmp, 1, 0); +} + +/**************************************************************************** + * Name: netdev_console_txint + ****************************************************************************/ + +static void netdev_console_txint(FAR struct uart_dev_s *dev, bool enable) +{ +} + +/**************************************************************************** + * Name: netdev_console_txready + ****************************************************************************/ + +static bool netdev_console_txready(FAR struct uart_dev_s *dev) +{ + return true; +} + +/**************************************************************************** + * Name: netdev_console_txempty + ****************************************************************************/ + +static bool netdev_console_txempty(FAR struct uart_dev_s *dev) +{ + return true; +} + +/**************************************************************************** + * Name: netdev_console_work + ****************************************************************************/ + +static void netdev_console_work(FAR void *arg) +{ + FAR struct netdev_consol_s *priv = arg; + + if (priv->rxint) + { + uart_dmarxfree(&priv->dev); + } + + work_queue_next(LPWORK, &priv->work, netdev_console_work, arg, + CONSOLE_WORK_DELAY); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netdev_console_init + * + * Description: + * Register a NETDEV console channel + * + * Input Parameters: + * dev - The lower half device driver structure to be registered. + * + * Returned Value: + * Zero on success. A negated errno value is returned on a failure. + * + ****************************************************************************/ + +int netdev_console_init(void) +{ + /* Register as console */ + + return uart_register("/dev/console", &g_netdev_console.dev); +} diff --git a/include/nuttx/serial/netdev_console.h b/include/nuttx/serial/netdev_console.h new file mode 100644 index 00000000000..146ea3d2bda --- /dev/null +++ b/include/nuttx/serial/netdev_console.h @@ -0,0 +1,47 @@ +/**************************************************************************** + * include/nuttx/serial/netdev_console.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SERIAL_NETDEV_CONSOLE_H +#define __INCLUDE_NUTTX_SERIAL_NETDEV_CONSOLE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +int netdev_console_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_SERIAL_NETDEV_CONSOLE_H */
