summaryrefslogtreecommitdiffstatshomepage
path: root/shared
Commit message (Collapse)AuthorAge
* shared/runtime/pyexec: Add helper function to execute a vstr.iabdalkader2025-03-14
| | | | | | Add `pyexec_vstr()` to execute Python code from a vstr source. Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
* shared/timeutils: Add missing mp_uint_t casts.Yoctopuce2025-01-02
| | | | | | To prevent compiler warnings. Signed-off-by: Yoctopuce <dev@yoctopuce.com>
* shared/runtime/gchelper_generic: Fix AArch32 build on Clang.Alessandro Gatti2024-12-10
| | | | | | | | | | | | | This commit fixes a compile error happening on Clang when building the generic gchelper code for AArch32. Clang would raise a warning regarding undefined variable access when aliasing a variable to an existing CPU register. The fix is pretty crude but it works - it simply disables the warning in question for the AArch32 gchelper collection function. Care was taken to make sure the code would also compile on GCC without warnings of sorts. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
* shared/tinyusb: Set MSC max endpoint size based on device speed.iabdalkader2024-12-10
| | | | Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
* shared/timeutils: Document the range of year/month/day etc input values.Damien George2024-10-24
| | | | | | These differ to, eg, the standard `mktime()` function. Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb: Wake main task if needed at end of USB ISR.Andrew Leech2024-10-07
| | | | Signed-off-by: Andrew Leech <andrew@alelec.net>
* esp32: Add automatic bootloader handling for S2 and S3.Andrew Leech2024-10-07
| | | | | | | | | Enables support for the ESP standard DTR/RTS based reboot to bootloader. Switches from OTG to Serial/Jtag mode to workaround issue discussed in: https://github.com/espressif/arduino-esp32/issues/6762 Signed-off-by: Andrew Leech <andrew@alelec.net>
* shared/tinyusb: Remove MICROPY_HW_USB_EXTERNAL_TINYUSB.Andrew Leech2024-10-07
| | | | | | No longer needed as shared tinyusb is now used by the esp32 port. Signed-off-by: Andrew Leech <andrew@alelec.net>
* shared/runtime/gchelper_rv64i: Fix opcode sw/sd typo.Alessandro Gatti2024-10-03
| | | | | | | | | | The version of the assembly code for the GC helper that was committed ended up being a version that had an opcode typo in. The code was tested and working, but an undo operation too many when cleaning up the file before committing checked in the wrong version. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
* shared/tinyusb: Use new persistent-tx-fifo configure interface.Damien George2024-09-26
| | | | | | The old configuration option has been removed from TinyUSB. Signed-off-by: Damien George <damien@micropython.org>
* all: Remove tinytest component.Damien George2024-09-19
| | | | | | | | | | | | | | | | | | | | With the recent qemu (d9a0fdda9a7b0db55c1115b55bb1b83cd5ce739c and 0426934969d06aa649ba903f5408cb331b5b9c2d) and zephyr (05cad7b56f5d460db26a468a05bfdeabe4a656db) changes to how their tests are run, two things became unused: - The tinytest framework, which embedded a set of tests and their expected output within firmware, so these tests could be run stand-alone. - The `--write-exp` and `--list-tests` options to `tests/run-tests.py`, which were needed primarily to generated the expected test output for tinytest (also the associated `tests/run-tests-exp.py/.sh` scripts are now unused). This commit removes the tinytest component and all its helper code. This eliminates a maintenance burden. Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb: Only run TinyUSB on the main thread if GIL is disabled.Angus Gratton2024-09-19
| | | | | | | | | | | | | | | | | | | If GIL is disabled then there's threat of a race condition if some other code specifically requests USB processing (i.e. to unblock stdio), while a scheduled TinyUSB callback is already running on another thread. Relies on the change in the parent commit, where scheduler is restricted to main thread if GIL is disabled. Fixes #15390 - "TinyUSB callback can't recurse" exceptions on rp2 when using _thread module and USB serial I/O. Adds a unit test for stdin functioning correctly in threads (fails on rp2 port without this fix). This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* shared/runtime/sys_stdio_mphal: Fix printed type for stdio streams.timdechant2024-09-06
| | | | | | | | | The printed type for stdio streams indicates "FileIO", which is a binary IO stream. Stdio is not binary by design, and its printed type should indicate a text stream. "TextIOWrapper" suits that purpose, and is used by VfsPosix files. Signed-off-by: timdechant <timdechant.git@gmail.com>
* shared/runtime/semihosting_arm: Add mp_semihosting_exit.Damien George2024-08-28
| | | | Signed-off-by: Damien George <damien@micropython.org>
* shared/runtime/semihosting_arm: Add mp_semihosting_rx_chars.Damien George2024-08-28
| | | | Signed-off-by: Damien George <damien@micropython.org>
* shared/runtime/semihosting_arm: Support semihosting on non-Thumb ARM.Damien George2024-08-28
| | | | Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb: Allow ports to define CDC TX/RX buffer sizes.iabdalkader2024-08-26
| | | | Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
* shared/runtime/gchelper: Add RISC-V RV64I native gchelper.Alessandro Gatti2024-08-07
| | | | | | | | | | | | | Add native gchelper support for 64 bits RISC-V RV64I targets. Now that RV64 is under CI, this also enables platform-specific ghelper in the Unix port. Also changes the data type holding the register contents to something more appropriate, so in the remote eventuality somebody wants to use this with RV128 all they have to do is update the `__riscv_xlen` check. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
* shared/runtime/pyexec: Make a raised SystemExit always do a forced exit.Damien George2024-07-20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The current situation with SystemExit and soft reset is the following: - `sys.exit()` follows CPython and just raises `SystemExit`. - On the unix port, raising `SystemExit` quits the application/MicroPython, whether at the REPL or in code (this follows CPython behaviour). - On bare-metal ports, raising `SystemExit` at the REPL does nothing, raising it in code will stop the code and drop into the REPL. - `machine.soft_reset()` raises `SystemExit` but with a special flag set, and bare-metal targets check this flag when it propagates to the top-level and do a soft reset when they receive it. The original idea here was that a bare-metal target can't "quit" like the unix port can, and so dropping to the REPL was considered the same as "quit". But this bare-metal behaviour is arguably inconsistent with unix, and "quit" should mean terminate everything, including REPL access. This commit changes the behaviour to the following, which is more consistent: - Raising `SystemExit` on a bare-metal port will do a soft reset (unless the exception is caught by the application). - `machine.soft_reset()` is now equivalent to `sys.exit()`. - unix port behaviour remains unchanged. Tested running the test suite on an stm32 board and everything still passes, in particular tests that skip by raising `SystemExit` still correctly skip. Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb/mp_usbd_cdc: Skip writing to an uninitialized USB device.robert-hh2024-07-20
| | | | | | | | | | | During execution of `boot.py` the USB device is not yet initialized. Any attempt to write to the CDC (eg calling `print()`) would lock up the device. This commit skips writing when the USB device is not initialized. Any output from `boot.py` is lost, but the device does not lock up. Also removed unnecessary declaration of `tusb_init()`. Signed-off-by: robert-hh <robert@hammelrath.com>
* shared/tinyusb/mp_usbd_cdc: Fix short CDC TX timeouts.Damien George2024-06-26
| | | | | | | | | | | | | | The `mp_event_wait_ms()` function may return earlier than the requested timeout, and if that happens repeatedly (eg due to lots of USB data and IRQs) then the loop waiting for CDC TX FIFO space to become available may exit much earlier than MICROPY_HW_USB_CDC_TX_TIMEOUT, even when there is no space. Fix this by using `mp_hal_ticks_ms()` to compute a more accurate timeout. The `basics/int_big_mul.py` test fails on RPI_PICO without this fix. Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb/mp_usbd_runtime: Fix pointer comparison in assert.Peter Harper2024-06-26
| | | | | | | | | Addresses build warning "comparison of distinct pointer types lacks a cast". Fixes issue #15276. Signed-off-by: Peter Harper <peter.harper@raspberrypi.com>
* shared/runtime/semihosting: Add RISC-V semihosting support.Alessandro Gatti2024-06-06
| | | | | | | This adds a RISC-V RV32 semihosting implementation, with all defined system calls exposed to the user. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
* shared/runtime/semihosting: Rename ARM semihosting files.Alessandro Gatti2024-06-06
| | | | | | | Make room for RISC-V semihosting code, by renaming the existing `semihosting.[ch]` files into `semihosting_arm.[ch]`. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
* shared/runtime/gchelper: Add RISC-V RV32I native gchelper.Alessandro Gatti2024-06-06
| | | | | | Add native gchelper support for 32 bits RISC-V RV32I targets. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
* shared/tinyusb: Buffer startup CDC data to send to host on connection.Andrew Leech2024-06-04
| | | | | | | | | | | | | | | | | | | | At startup, buffer initial stdout / MicroyPthon banner so that it can be sent to the host on initial connection of the USB serial port. This buffering also works for when the CDC becomes disconnected and the device is still printing to stdout, and when CDC is reconnected the most recent part of stdout (depending on how big the internal USB FIFO is) is flushed to the host. This change is most obvious when you've first plugged in a MicroPython device (or hit reset), when it's a board that uses USB (CDC) serial in the chip itself for the REPL interface. This doesn't apply to UART going via a separate USB-serial chip. The stm32 port already has this buffering behaviour (it doesn't use TinyUSB) and this commit extends such behaviour to rp2, mimxrt, samd and renesas-ra ports, which do use TinyUSB. Signed-off-by: Andrew Leech <andrew@alelec.net>
* extmod/network_ninaw10: Implement the ipconfig methods for ninaw10.robert-hh2024-06-04
| | | | | | | | | | | | | This implements network.ipconfig() and network.WLAN.ipconfig() when the ninaw10 driver is used for WLAN. Due to a omission in the ninaw10 driver stack, setting the DNS address has no effect. But the interface is kept here just in case it's fixed eventually. dhcp4 and has_dhcp4 are dummy arguments. Ninaw10 seems to always use DHCP. Signed-off-by: robert-hh <robert@hammelrath.com>
* shared/tinyusb: Allow ports to use 1200bps-touch without other CDC code.Damien George2024-06-02
| | | | | | | | | | | | | | | | | | This fixes the build for some esp32 and nrf boards (for example `ARDUINO_NANO_33_BLE_SENSE` and `ARDUINO_NANO_ESP32`) due to commit c98789a6d8e05acb608afe4b30cf3ca563419b2d. Changes are: - Allow the CDC TX/RX functions in `mp_usbd_cdc.c` to be enabled separately to those needed for `MICROPY_HW_USB_CDC_1200BPS_TOUCH`. - Add `MICROPY_EXCLUDE_SHARED_TINYUSB_USBD_CDC` option as a temporary workaround for the nrf port to use. - Declare `mp_usbd_line_state_cb()` in a header as a public function. - Fix warning with type cast of `.callback_line_state_changed`. Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb: Add common CDC TX/RX functions.Andrew Leech2024-05-31
| | | | | | | | | | | | There are a few TinyUSB CDC functions used for stdio that are currently replicated across a number of ports. Not surprisingly in a couple of cases these have started to diverge slightly, with additional features added to one of them. This commit consolidates a couple of key shared functions used directly by TinyUSB based ports, and makes those functions available to all. Signed-off-by: Andrew Leech <andrew@alelec.net>
* rp2: Refactor to not use pico-sdk alarm pool functions for sleeping.Angus Gratton2024-05-31
| | | | | | | | | | | | | | The best_effort_wfe_or_timeout() and sleep_us() pico-sdk functions use the pico-sdk alarm pool internally, and that has a bug. Some usages inside pico-sdk (notably multicore_lockout_start_blocking()) will still end up calling best_effort_wfe_or_timeout(), although usually with "end_of_time" as the timeout value so it should avoid any alarm pool race conditions. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* rp2: Refactor soft timer to use hardware timer alarm.Angus Gratton2024-05-31
| | | | | | | | Progress towards removing pico-sdk alarm pool, due to a known issue. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* shared/tinyusb: Stall the CDC IN endpoint while disconnecting.Angus Gratton2024-05-09
| | | | | | | | | | | | | | | | | | | Only when dynamic USB devices are enabled. The issue here is that when the USB reset triggers, the dynamic USB device reset callback is called from inside the TinyUSB task. If that callback tries to print something then it'll call through to tud_cdc_write_flush(), but TinyUSB hasn't finished updating state yet to know it's no longer configured. Subsequently it may try to queue a transfer and then the low-level DCD layer panics. By explicitly stalling the endpoint first, usbd_edpt_claim() will fail and tud_cdc_write_flush() returns immediately. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* shared/tinyusb: Fix dynamic USB control callbacks for wLength==0.Angus Gratton2024-04-17
| | | | | | | | | | | | | | | | | | | | In the case where an OUT control transfer triggers with wLength==0 (i.e. all data sent in the SETUP phase, and no additional data phase) the callbacks were previously implemented to return b"" (i.e. an empty buffer for the data phase). However this didn't actually work as intended because b"" can't provide a RW buffer (needed for OUT transfers with a data phase to write data into), so actually the endpoint would stall. The symptom was often that the device process the request (if processing it in the SETUP phase when all information was already available), but the host sees the endpoint stall and eventually returns an error. This commit changes the behaviour so returning True from the SETUP phase of a control transfer queues a zero length status response. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* shared/tinyusb: Increase default string descr max length to 40 chars.Damien George2024-03-27
| | | | | | | | When defining custom USB devices, longer strings may be needed. Eventually the memory for string descriptors can be allocated on demand, but for now this bigger value should be reasonable. Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb: Update some code comments for runtime USB.Angus Gratton2024-03-27
| | | | | | | | | Updates a few code comments that were out of date or poorly worded. No code changes. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* shared/tinyusb: Don't disconnect on soft reset unless USB was active.Angus Gratton2024-03-27
| | | | | | | | | | | | | | | Previously, constructing the singleton USBDevice object was enough to trigger a USB disconnect on soft reset. Now it also has to be active. The only case where this changes the behaviour is if the USBDevice object has been constructed but never set to active (no more disconnect in this case). Otherwise, behaviour is the same. This change was requested by hippy on the raspberrypi forums. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* extmod/machine_usb_device: Add support for Python USB devices.Angus Gratton2024-03-15
| | | | | | | | | | | | | This new machine-module driver provides a "USBDevice" singleton object and a shim TinyUSB "runtime" driver that delegates the descriptors and all of the TinyUSB callbacks to Python functions. This allows writing arbitrary USB devices in pure Python. It's also possible to have a base built-in USB device implemented in C (eg CDC, or CDC+MSC) and a Python USB device added on top of that. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* all: Remove the "STATIC" macro and just use "static" instead.Angus Gratton2024-03-07
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The STATIC macro was introduced a very long time ago in commit d5df6cd44a433d6253a61cb0f987835fbc06b2de. The original reason for this was to have the option to define it to nothing so that all static functions become global functions and therefore visible to certain debug tools, so one could do function size comparison and other things. This STATIC feature is rarely (if ever) used. And with the use of LTO and heavy inline optimisation, analysing the size of individual functions when they are not static is not a good representation of the size of code when fully optimised. So the macro does not have much use and it's simpler to just remove it. Then you know exactly what it's doing. For example, newcomers don't have to learn what the STATIC macro is and why it exists. Reading the code is also less "loud" with a lowercase static. One other minor point in favour of removing it, is that it stops bugs with `STATIC inline`, which should always be `static inline`. Methodology for this commit was: 1) git ls-files | egrep '\.[ch]$' | \ xargs sed -Ei "s/(^| )STATIC($| )/\1static\2/" 2) Do some manual cleanup in the diff by searching for the word STATIC in comments and changing those back. 3) "git-grep STATIC docs/", manually fixed those cases. 4) "rg -t python STATIC", manually fixed codegen lines that used STATIC. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* py/emitglue: Introduce mp_proto_fun_t as a more general mp_raw_code_t.Damien George2024-02-16
| | | | | | | | | | Allows bytecode itself to be used instead of an mp_raw_code_t in the simple and common cases of a bytecode function without any children. This can be used to further reduce frozen code size, and has the potential to optimise other areas like importing. Signed-off-by: Damien George <damien@micropython.org>
* extmod/modlwip: Support IPv6.Felix Dörre2024-02-16
| | | | | | | | | | | | | | | | | | | | | | | | With these changes IPv6 works on the rp2 port (and possibly others that use the lwIP socket implementation). Things that have been tested and work: - Neighbour solicitation for v6 link local address. - Ping of v6 link-local address. - Receiving a SLAAC address via router advertisement. - Ping a v6 address allocated via SLAAC. - Perform an outgoing connection to a routed v6-address (via default gateway). - Create a listening IPv6 wildcard socked bound to ::, and trying to access it via link-local, SLAAC, and IPv4 (to ensure the dual-stack binding works). Things that could be improved: - socket.socket().getaddrinfo only returns the v4 address. It could also return v6 addresses (getaddrinfo is actively programmed to only return a single address, and this is the v4-address by default, with fallback to the v6 address if both are enabled). Signed-off-by: Felix Dörre <felix@dogcraft.de>
* ports: On cold boot, enable USB after boot.py completes.Angus Gratton2024-02-15
| | | | | | | | | | | | | | | | For mimxrt, nrf, renesas-ra, rp2 and samd ports, this commit implements similar behaviour to the stm32 port, where USB is only brought up after boot.py completes execution. Currently this doesn't add any useful functionality (and may break workflows that depend on USB-CDC being live in boot.py), however it's a precondition for more usable workflows with USB devices defined in Python (allows setting up USB interfaces in boot.py before the device enumerates for the first time). This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* shared/timeutils: Remove useless void-return.Yoctopuce2024-02-15
| | | | | | | | | | | | | | The C99 standard states: 6.8.6.4 The return statement Constraints A return statement with an expression shall not appear in a function whose return type is void. A return statement without an expression shall only appear in a function whose return type is void. And when `-pedantic` is enabled the compiler gives an error. Signed-off-by: Yoctopuce <dev@yoctopuce.com>
* ports: Fix sys.stdout.buffer.write() return value.Maarten van der Schrieck2023-12-22
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | MicroPython code may rely on the return value of sys.stdout.buffer.write() to reflect the number of bytes actually written. While in most scenarios a write() operation is successful, there are cases where it fails, leading to data loss. This problem arises because, currently, write() merely returns the number of bytes it was supposed to write, without indication of failure. One scenario where write() might fail, is where USB is used and the receiving end doesn't read quickly enough to empty the receive buffer. In that case, write() on the MicroPython side can timeout, resulting in the loss of data without any indication, a behavior observed notably in communication between a Pi Pico as a client and a Linux host using the ACM driver. A complex issue arises with mp_hal_stdout_tx_strn() when it involves multiple outputs, such as USB, dupterm and hardware UART. The challenge is in handling cases where writing to one output is successful, but another fails, either fully or partially. This patch implements the following solution: mp_hal_stdout_tx_strn() attempts to write len bytes to all of the possible destinations for that data, and returns the minimum successful write length. The implementation of this is complicated by several factors: - multiple outputs may be enabled or disabled at compiled time - multiple outputs may be enabled or disabled at runtime - mp_os_dupterm_tx_strn() is one such output, optionally containing multiple additional outputs - each of these outputs may or may not be able to report success - each of these outputs may or may not be able to report partial writes As a result, there's no single strategy that fits all ports, necessitating unique logic for each instance of mp_hal_stdout_tx_strn(). Note that addressing sys.stdout.write() is more complex due to its data modification process ("cooked" output), and it remains unchanged in this patch. Developers who are concerned about accurate return values from write operations should use sys.stdout.buffer.write(). This patch might disrupt some existing code, but it's also expected to resolve issues, considering that the peculiar return value behavior of sys.stdout.buffer.write() is not well-documented and likely not widely known. Therefore, it's improbable that much existing code relies on the previous behavior. Signed-off-by: Maarten van der Schrieck <maarten@thingsconnected.nl>
* extmod/modmachine: Provide common Python bindings for bootloader().Damien George2023-11-30
| | | | Signed-off-by: Damien George <damien@micropython.org>
* shared/runtime/softtimer: Generalise soft_timer to work without SysTick.Damien George2023-11-29
| | | | | | | | | | | | | If a port defines MICROPY_SOFT_TIMER_TICKS_MS then soft_timer assumes a SysTick back end, and provides a soft_timer_next variable that sets when the next call to soft_timer_handler() should occur. Otherwise, a port should provide soft_timer_get_ms() and soft_timer_schedule_at_ms() with appropriate semantics (see comments). Existing users of soft_timer should continue to work as they did. Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb: Add a helper for hex string conversion.Angus Gratton2023-11-16
| | | | | | | | | | | Change the rp2 and renesas-ra ports to use the helper function. Saves copy-pasta, at the small cost of one more function call in the firmware (if not using LTO). This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* shared/tinyusb: Expose mp_usbd_task as a public function.Damien George2023-11-09
| | | | Signed-off-by: Damien George <damien@micropython.org>
* shared/tinyusb: Schedule TinyUSB task function from dcd_event_handler.Angus Gratton2023-11-09
| | | | | | | | | | | | | | dcd_event_handler() is called from the IRQ when a new DCD event is queued for processing by the TinyUSB thread mode task. This lets us queue the handler to run immediately when MicroPython resumes. Currently this relies on a linker --wrap hack to work, but a PR has been submitted to TinyUSB to allow the function to be called inline from dcd_event_handler() itself. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
* shared/libc/string0: Don't deref args for n==0 case.Jim Mussared2023-11-07
| | | | | | | | | | | | | | | | | | | | C99 says that strncmp has UB for either string being NULL, so the current behavior is technically correct, but it's an easy fix to handle this case correctly. 7.1.4: "unless explicitly stated otherwise in the detailed description... if an argument to a function has ...null pointer.. the behavior is undefined". 7.21.1: "Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values, as described in 7.1.4". Also make the same change for the minimal version in bare-arm/lib.c. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
* py/mkrules.mk: Add rule for compiling auto-generated source files.Jim Mussared2023-11-03
| | | | | | | | | This prevents each port Makefile from having to add an explicit rule for `build-BOARD/pins_BOARD.c`. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>