diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 8cba82888e..d04829226e 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -537,6 +537,7 @@ PSEUDOMODULES += soft_uart_modecfg PSEUDOMODULES += stdin PSEUDOMODULES += stdio_available PSEUDOMODULES += stdio_cdc_acm +PSEUDOMODULES += stdio_dispatch PSEUDOMODULES += stdio_ethos PSEUDOMODULES += stdio_nimble_debug PSEUDOMODULES += stdio_telnet diff --git a/makefiles/stdio.inc.mk b/makefiles/stdio.inc.mk index 9749c34177..0e0cf46752 100644 --- a/makefiles/stdio.inc.mk +++ b/makefiles/stdio.inc.mk @@ -14,11 +14,29 @@ STDIO_MODULES = \ stdio_usb_serial_jtag \ # +STDIO_LEGACY_MODULES = \ + ethos_stdio \ + stdio_ethos \ + stdio_native # requires #19002 \ + # + # select stdio_uart if no other stdio module is slected ifeq (,$(filter $(STDIO_MODULES),$(USEMODULE))) USEMODULE += stdio_uart endif +ifeq (,$(filter $(STDIO_LEGACY_MODULES),$(USEMODULE))) + USEMODULE += stdio +endif + +ifneq (,$(filter stdin,$(USEMODULE))) + USEMODULE += isrpipe +endif + +ifneq (1, $(words $(filter $(STDIO_MODULES),$(USEMODULE)))) + USEMODULE += stdio_dispatch +endif + ifneq (,$(filter stdio_cdc_acm,$(USEMODULE))) USEMODULE += usbus_cdc_acm USEMODULE += isrpipe diff --git a/sys/include/stdio_base.h b/sys/include/stdio_base.h index a98ca1fd7f..f11ffce6b1 100644 --- a/sys/include/stdio_base.h +++ b/sys/include/stdio_base.h @@ -18,6 +18,7 @@ * * @author Kaspar Schleiser * @author Hauke Petersen + * @author Benjamin Valentin */ #ifndef STDIO_BASE_H @@ -26,11 +27,64 @@ #include #include "modules.h" +#include "isrpipe.h" +#include "xfa.h" #ifdef __cplusplus extern "C" { #endif +#ifndef STDIO_RX_BUFSIZE +/** + * @brief Buffer size for STDIO + */ +#define STDIO_RX_BUFSIZE (64) +#endif + +enum { + STDIO_NULL, /**< dummy stdio */ + STDIO_UART, /**< stdio via UART */ + STDIO_RTT, /**< stdio via Segger RTT */ + STDIO_SEMIHOSTING, /**< stdio via Semihosting */ + STDIO_USBUS_CDC_ACM, /**< stdio via USB CDC ACM (usbus) */ + STDIO_TINYUSB_CDC_ACM, /**< tdio via USB CDC ACM (TinyUSB) */ + STDIO_ESP32_SERIAL_JTAG, /**< stdio via ESP32 debug Serial/JTAG */ + STDIO_NIMBLE, /**< stdio via BLE (NimBLE) */ + STDIO_UDP, /**< stdio via UDP */ + STDIO_TELNET, /**< stdio via telnet */ + STDIO_ETHOS, /**< stdio via ethos (mutiplex) */ + STDIO_SLIP, /*<< stdio via SLIP (mutiplex) */ +}; + +/** + * @brief stdio provider struct + */ +typedef struct { + /** + * @brief Initialize and attach the stdio provider + */ + void (*open)(void); + /** + * @brief Detach the stdio provider + */ + void (*close)(void); + /** + * @brief Write @p len bytes from @p src into stdout + * + * @param[in] src buffer to read from + * @param[in] len nr of bytes to write + * + * @return nr of bytes written + * @return <0 on error + */ + ssize_t (*write)(const void *src, size_t len); +} stdio_provider_t; + +/** + * @brief isrpipe for writing stdin input to + */ +extern isrpipe_t stdin_isrpipe; + /** * @brief initialize the module */ @@ -60,7 +114,11 @@ int stdio_available(void); ssize_t stdio_read(void* buffer, size_t max_len); /** - * @brief write @p len bytes from @p buffer into uart + * @brief write @p len bytes from @p buffer into STDOUT + * + * @note Depending on the stdio backend(s) used, not all bytes might + * be written to stdout and accounted for if multiple backends are + * active, as not all stdout backends will do a blocking write. * * @param[in] buffer buffer to read from * @param[in] len nr of bytes to write @@ -70,6 +128,45 @@ ssize_t stdio_read(void* buffer, size_t max_len); */ ssize_t stdio_write(const void* buffer, size_t len); +/** + * @brief Disable stdio and detach stdio providers + */ +void stdio_close(void); + +#if defined(MODULE_STDIO_DISPATCH) || DOXYGEN +/** + * @brief stdio implementation methods + * + * @param _type stdio provider type, for identification + * @param _open attach / init function + * @param _close close / disable function + * @param _write write function + */ +#define STDIO_PROVIDER(_type, _open, _close, _write) \ + XFA_CONST(stdio_provider_xfa, 0) stdio_provider_t stdio_ ##_type = { \ + .open = _open, \ + .close = _close, \ + .write = _write, \ + }; +#else +#define STDIO_PROVIDER(_type, _open, _close, _write) \ + void stdio_init(void) { \ + void (*f)(void) = _open; \ + if (f != NULL) { \ + f(); \ + } \ + } \ + void stdio_close(void) { \ + void (*f)(void) = _close; \ + if (f != NULL) { \ + f(); \ + } \ + } \ + ssize_t stdio_write(const void* buffer, size_t len) { \ + return _write(buffer, len); \ + } +#endif + #ifdef __cplusplus } #endif diff --git a/sys/stdio/Makefile b/sys/stdio/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/stdio/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/stdio/stdio.c b/sys/stdio/stdio.c new file mode 100644 index 0000000000..11fb6c5cf7 --- /dev/null +++ b/sys/stdio/stdio.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_stdio + * @{ + * + * @file + * @brief STDIO common layer + * + * @author Benjamin Valentin + * + * @} + */ + +#include "errno.h" +#include "isrpipe.h" +#include "stdio_base.h" +#include "macros/utils.h" +#include "xfa.h" + +static uint8_t _rx_buf_mem[STDIO_RX_BUFSIZE]; +isrpipe_t stdin_isrpipe = ISRPIPE_INIT(_rx_buf_mem); + +#ifdef MODULE_STDIO_DISPATCH +XFA_INIT_CONST(stdio_provider_t, stdio_provider_xfa); + +void stdio_init(void) +{ + for (unsigned i = 0; i < XFA_LEN(stdio_provider_t, stdio_provider_xfa); ++i) { + if (stdio_provider_xfa[i].open) { + stdio_provider_xfa[i].open(); + } + } +} + +ssize_t stdio_write(const void* buffer, size_t len) +{ + for (unsigned i = 0; i < XFA_LEN(stdio_provider_t, stdio_provider_xfa); ++i) { + stdio_provider_xfa[i].write(buffer, len); + } + + return len; +} + +void stdio_close(void) { + for (unsigned i = 0; i < XFA_LEN(stdio_provider_t, stdio_provider_xfa); ++i) { + if (stdio_provider_xfa[i].close) { + stdio_provider_xfa[i].close(); + } + } +} + +#define MAYBE_WEAK +#else +#define MAYBE_WEAK __attribute__((weak)) +#endif + +MAYBE_WEAK +ssize_t stdio_read(void* buffer, size_t len) +{ + if (!IS_USED(MODULE_STDIN)) { + return -ENOTSUP; + } + + return isrpipe_read(&stdin_isrpipe, buffer, len); +} + +#if IS_USED(MODULE_STDIO_AVAILABLE) +MAYBE_WEAK +int stdio_available(void) +{ + return tsrb_avail(&stdin_isrpipe.tsrb); +} +#endif