Commit graph

62700 commits

Author SHA1 Message Date
raiden00pl
2c79c9951b arch/x86_64: run the signal trampoline on the thread kernel stack
For a thread interrupted in user mode the trampoline ran on the user
stack, where the signal handler then grows over its frame. Run it on
the thread kernel stack, unused while the thread is in user mode. The
stack cannot be selected from the saved CS: up_initial_state() records
the caller CS, a kernel selector even for user threads.

Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
2026-08-11 22:06:32 +08:00
raiden00pl
4405b12155 arch/x86_64: restore the kernel stack when a signal handler returns
SYS_signal_handler_return restored RSP from saved_rsp, which is not
written when a task signals itself: synchronous dispatch skips
up_schedule_sigaction(), so the kernel stack pointer was set to zero
and the next push faulted. Save the kernel stack pointer at dispatch
in xcp.kstkptr, as risc-v does, and restore that.

Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
2026-08-11 22:06:32 +08:00
Ricard Rosson
3d30272ce7 arch/xtensa/espressif: initialize the HR Timer from the Wi-Fi init path
The Espressif Wi-Fi stack cannot work unless the esp_timer subsystem has
been initialized, but nothing in the Wi-Fi code does that: it is left to
each board's bringup to call esp_hr_timer_init() first.  Any board that
does not happen to make that call dies on the first RF enable.

The dependency is not visible from the Wi-Fi sources.  The path is:

  board_wlan_init() -> esp_wlan_sta_initialize() -> esp_wlan_initialize()
    -> esp_wifi_initialize() -> esp_wifi_api_adapter_init()

and later, when the radio is first powered up:

  esp_phy_enable_wrapper() -> esp_phy_enable() (esp-hal-3rdparty,
  components/esp_phy/src/phy_init.c) -> phy_track_pll_init()
  (components/esp_phy/src/phy_common.c)

phy_track_pll_init() calls esp_timer_create() and
esp_timer_start_periodic() wrapped in ESP_ERROR_CHECK().  Both return
ESP_ERR_INVALID_STATE while esp_timer is uninitialized, because the HAL's
own esp_timer_init_os() startup hook is compiled out on NuttX
(#ifndef __NuttX__ in components/esp_timer/src/esp_timer.c), so the timer
task and the timer ISR only ever get created from NuttX's
esp_hr_timer_init() -> esp_timer_init().

Initialize the HR Timer at the top of esp_wifi_api_adapter_init(), where
the requirement actually originates.  esp_hr_timer_init() is idempotent
(it early-returns once the subsystem is up), so boards that already call
it during bringup are unaffected.  Also make ESPRESSIF_WIRELESS select
ESPRESSIF_HR_TIMER explicitly instead of inheriting it through the
deprecated ESP32{,S2,S3}_RT_TIMER symbols, so the timer adapter is
guaranteed to be built whenever the radio is.

This is deliberately limited to Xtensa.  The RISC-V common-espressif tree
has the same unenforced dependency, but nothing is broken there today: its
ESPRESSIF_WIRELESS already selects both ESPRESSIF_HR_TIMER and RTC_DRIVER,
and esp_rtc.c initializes the timer.  The mirror change can follow from
someone able to test it on RISC-V hardware.

This was diagnosed on an out-of-tree ESP32-S3 board whose bringup lacked
the call.  The failure gives no panic output at all and looks exactly like
a CPU lockup: the system tick stops, the console dies mid-line and USB
stays enumerated but unresponsive.  It was tracked down with ROM-level
ets_printf() breadcrumbs along the init path plus a high-priority thread
that busy-waits on ets_delay_us(): the breadcrumb trail ends inside
phy_track_pll_init() and never reaches the print after it, and the
busy-wait thread keeps printing while every sleep()-based thread stops
waking, showing the tick is gone.  Initializing the timer ahead of Wi-Fi
init makes the same image associate to an AP, obtain a DHCP lease and
serve telnet.  Validated on ESP32-S3 silicon (240 MHz, no PSRAM, 16 MiB
flash).

esp32s3-devkit:wifi builds clean with the change.

Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude Opus 5 (Claude Code)
2026-08-11 10:43:39 -03:00
22078360
23ea1503a1 mips/Makefile: Add nuttx build with CONFIG_ALLSYMS enabled.
Change the arch/mips/src/Makefile to build nuttx with CONFIG_ALLSYMS
enabled in MIPS architecture. This enables symbol name showing in
system, such as 'dumpstack 3' shows both functions name and addresses.

This change is referred to arch/tricore/src/Makefile and updated to
work well with MIPS. And it works with and without CONFIG_ALLSYMS enabled.

Fixes apache#19728

Signed-off-by: wangtao <twangpicasso@gmail.com>
2026-08-11 09:53:38 -03:00
Marco Casaroli
8f69f27b4d boards/esp32-devkitc: Let ostest finish on the knsh configuration.
A full ostest run never reached the end on esp32-devkitc:knsh.  It stopped in
the barrier test:

  barrier_test: ERROR thread 6 create, status=12
  ostest_main: Exiting with status 256

The cause is the interaction of two settings that are each reasonable on their
own.  CONFIG_TLS_ALIGNED is set and CONFIG_TLS_LOG2_MAXSTACK is 13, so every
pthread stack must start on an 8 KiB boundary.  The barrier threads take the
2 KiB default stack, so each one occupies an 8 KiB aligned slot.  Eight of them
do not fit the 96 KiB user heap of a protected build once the tests before them
have fragmented it, and up_create_stack() fails:

  up_create_stack: ERROR: Failed to allocate stack, size 2048

The flat build has the same two settings and passes, because its heap is
320 KiB against 96 KiB here.

So this lowers the barrier thread count for this configuration only.  Four
threads still test a barrier, and they leave margin:  six was the most that
ever started, so six would pass with none.

The user heap cannot grow far.  User data has to sit in the MMU governed window
of SRAM2, which is 128 KiB in total, and the kernel holds the first 32 KiB of
it.

Verified on an ESP32-DevKitC V4, ESP32-D0WD-V3 revision 3.1.  All four threads
reach the barrier and ostest reports "Exiting with status 0".

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-11 09:52:16 -03:00
Lwazi Dube
30a3769057 arch/mips: Add support for loadable ELF modules
Implement architecture-specific ELF header definitions and relocation handling
for the MIPS architecture to enable loadable modules.

Fixes #19178.

Changes include:
- Add `arch/mips/include/elf.h` with MIPS ELF relocation types and
  architecture-specific ELF data structures (`arch_elfdata_s`).
- Implement `libs/libc/machine/mips/arch_elf.c` containing `up_checkarch`,
  `up_relocate`, and `up_relocateadd` functions handling `R_MIPS_NONE`,
  `R_MIPS_32`, `R_MIPS_26`, `R_MIPS_HI16`, and `R_MIPS_LO16` relocations.
- Integrate MIPS machine-specific C library support in
  `libs/libc/machine/mips/Make.defs`.
- Update `LDMODULEFLAGS` in `arch/mips/src/mips32/Toolchain.defs` to include the
  little-endian (`-EL`) flag.
- Update `up_coherent_dcache` for proper cache synchronization on JZ4780.

Signed-off-by: Lwazi Dube <lwazeh@gmail.com>
2026-08-11 09:46:46 -03:00
Filipe Cavalcanti
2567b729cf arch/risc-v/espressif: fix SPI IOMUX false positive without SPI2
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
SPI_VIA_IOMUX used SPI2 IOMUX pin macros that are undefined when SPI2
is disabled or on chips without IOMUX SPI pins (e.g. ESP32-P4), so the
driver took the IOMUX path and never routed SPI3 via the GPIO matrix.

Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
2026-08-11 13:56:50 +08:00
raiden00pl
45c75f3bda fs/romfs: fix node cache overflow in directories with >256 entries
romfs_cachenode() tracked the allocated size of rn_child in a uint8_t
while rn_count is a uint16_t. Past 256 entries the size wraps to zero,
the grow condition rn_count == num - 1 can never be true again and the
array is not reallocated: entries are written beyond the allocation,
corrupting the heap.

Track the allocated size in a size_t.

Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
2026-08-11 10:53:19 +08:00
Darryl Ring
894a08e35f arch/arm/stm32h5: Fix handling of GPIO port I
Correct the number of GPIO ports (STM32_NPORTS) from 8 to 9 and include
GPIOI in the g_gpiobase array. Also fix the comparison that would
prevent the GPIOI clock from being enabled (this is really a no-op,
though).

Signed-off-by: Darryl Ring <darryl@bluerobotics.com>
2026-08-11 10:51:46 +08:00
zhangyu117
e9e93c3b2c arch/tricore: idle donot depend on illd
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
Replace the iLLD Ifx_Ssw_infiniteLoop() helper in the idle path with
a self-contained 'loopu' instruction wrapper (tricore_idle_loop()).
The 'loopu' (loop unconditional) instruction branches back to itself
until an interrupt is taken, which is the standard TriCore low-power
idle sequence; the GNU and Tasking assemblers spell the backward
label differently, so the macro dispatches on the toolchain.

This removes the arch/tricore idle path's dependency on the Infineon
iLLD/Ssw layer.  No behavior change: the loop is still interrupted by
any IRQ that causes a context switch away from the idle task.

Signed-off-by: zhangyu117 <zhangyu117@xiaomi.com>
2026-08-11 03:41:03 +08:00
Marco Casaroli
7f6a0a30da boards/arm/stm32l4/b-l475e-iot01a: Add a QEMU nsh configuration.
Some checks failed
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
Build Documentation / build-html (push) Has been cancelled
QEMU's b-l475e-iot01a machine models the STM32L4x5 core peripherals, but
not the QUADSPI controller or the on-board MX25R6435F flash.  The nsh
configuration therefore panics during board bring-up, inside
stm32_qspi_initialize() -> mx25rxx_initialize() -> qspi_command(), before
the console has produced any output.

Add a qemu configuration that is nsh without CONFIG_B_L475E_IOT01A_MTD_FLASH
and the QSPI/MTD/SMARTFS chain that symbol selects.  It boots to an NSH
prompt on USART1 under:

  qemu-system-arm -M b-l475e-iot01a -nographic -kernel nuttx

Document the new configuration, including the fact that QEMU's STM32L4x5
USART model never calls qemu_chr_fe_accept_input() after the guest reads
RDR.  Console input consequently stalls after the first byte or two when a
line is pasted or piped in, although typing at human speed works.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-10 22:46:31 +08:00
dechao_gong
9e33dc077a Documentation/rtl8721f: add rtl8721f_evb board photo
Add the board photo and replace the placeholder todo in the RTL8721F EVB
documentation index with a figure directive so the board page renders the
hardware image, matching the other Realtek board pages.

Signed-off-by: dechao_gong <dechao_gong@realsil.com.cn>
Assisted-by: Claude <noreply@anthropic.com>
2026-08-10 09:51:02 -03:00
dechao_gong
b5ec07b19b arch/arm/rtl8721f: add UART character driver support
Expose the RTL8721F general-purpose UARTs through the shared Ameba serial
driver in arch/arm/src/common/ameba.  Only the chip-specific glue is added:
a new ameba_uart_chip.h supplying the green2 register bases, IRQs, clock
masks and UART TX/RX pin-mux function codes, plus the build wiring and a
board port table registering UART0 at /dev/ttyS1.  The common serial layer
is reused unchanged.

A new "uart" board config enables the driver with the serialrx and
serialblaster examples and runtime TERMIOS support.

Verified on RTL8721F EVB hardware with a PA24/PA25 loopback: single-message
echo, 2600-byte serialrx/serialblaster throughput with no loss, and TERMIOS
reconfiguration (CS7 data-bit truncation, parity and stop-bit ioctl
round-trip, and 9600 baud reprogramming) all pass.

Signed-off-by: dechao_gong <dechao_gong@realsil.com.cn>
Assisted-by: Claude <noreply@anthropic.com>
2026-08-10 09:51:02 -03:00
Marco Casaroli
067e30e14f arch/x86_64: Build fork() children from the caller's syscall frame.
In a kernel build vfork() is reached through a system call, so the return
address and stack pointer the architecture's entry point can see for itself
are the kernel's, not the caller's.  A child built from those resumes at a
kernel address, which is why x86_64 selected the fork family only for the
flat build.

x86_64_syscall() now publishes the caller's frame in xcp.sregs for the
duration of the stub call, and x86_64_fork() builds the child from it:

  x86_64_fork_syscall()  when xcp.sregs is non-NULL, so that the child
                         returns from the very same `syscall' instruction as
                         the parent, in user mode, on its own stack;
  x86_64_fork_direct()   otherwise, which is the flat build and any kernel
                         thread that calls the entry point as a plain
                         function.

The discriminator is xcp.sregs rather than TCB_FLAG_SYSCALL, which arm64 and
RISC-V use:  that flag also defers signal actions, x86_64 has never raised it,
and its kernel-build signal path does not survive being made to -- a
pre-existing problem that does not belong to this work.

Two properties of SYSCALL/SYSRET shape the child's frame.  The instruction
leaves the caller's RIP and RFLAGS in RCX and R11 rather than on a stack, so
they are moved into the RIP and RFLAGS slots of the interrupt frame the child
is resumed from; and the hardware never records the caller's CS and SS at all,
SYSRETQ reconstructing them from IA32_STAR, so the child's are filled in with
the user code and data selectors at RPL 3.  The frame is therefore not copied
wholesale:  the extended state and the general registers are inherited, while
the segment registers and the thread pointer stay as up_initial_state() left
them, the child's stack being a fresh allocation the parent's FS base does not
describe.

x86_64_fork_relocfp() is new and is not optional here.  A function returns
with `leave', which feeds the frame pointer into the stack pointer, so
relocating only the RBP the child resumes with gets it exactly one frame:
the next return loads a saved RBP still pointing into the parent's stack.

With that in place ARCH_X86_64 can select ARCH_HAVE_VFORK unconditionally.

Build-verified on qemu-intel64:knsh_romfs and qemu-intel64:ostest.  NuttX on
qemu-intel64 requires tsc-deadline and pcid, which TCG does not implement, so
it cannot be run on this host.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-10 08:57:30 -03:00
Marco Casaroli
3557791ae2 arch/arm64: Build fork() children from the caller's syscall frame.
In a kernel or protected build vfork() is reached through a system call, so
the return address and stack pointer that the entry point in
arm64_fork_func.S can snapshot for itself belong to the kernel-side stub, not
to the caller.  A child built from that snapshot resumes at a kernel address
on a kernel stack.  This is why arm64 selected the fork family only for the
flat build.

Record what the caller was actually doing instead.  arm64_sync_exc passes the
exception frame to dispatch_syscall() in x7 -- x0-x6 carry the call number and
its six parameters, so x7 is free -- and dispatch_syscall() stores it in
xcp.sregs, mirroring what riscv_swint.c does.

arm64_fork() then chooses where the caller's registers live:

  arm64_fork_syscall()  when TCB_FLAG_SYSCALL is set, rebuilding the child
                        from xcp.sregs so that it returns from the very same
                        SVC as the parent;
  arm64_fork_direct()   otherwise, which is the flat build and any kernel
                        thread that calls the entry point as a plain function.

The stack copy and the relocation of pointers into it are shared by both
paths in arm64_fork_stack() and arm64_fork_reloc().

With that in place ARCH_ARM64 can select ARCH_HAVE_VFORK unconditionally.

Verified on qemu-armv8a:knsh (BUILD_KERNEL), qemu-armv8a:nsh (BUILD_FLAT) and
qemu-armv8a:citest_smp under qemu-system-aarch64:  ostest's vfork_test passes
on all three, and it was absent from knsh before the change.  The protected
configurations are build-verified only (fvp-armv8r:pnsh), there being no
emulator for them here.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-10 08:57:30 -03:00
Marco Casaroli
0aaa04d055 arch/arm: Restore vfork() on 32-bit ARM kernel builds.
ARCH_ARM has selected ARCH_HAVE_VFORK only "if !BUILD_KERNEL" since the
fork()/vfork() split.  That condition was deliberate but temporary:  it was
added because the fork family had never worked on a 32-bit ARM kernel build --
the entry point in fork.S snapshots the kernel-side stub rather than the
caller, so a child resumes at a kernel address -- and said in as many words
that "arch/arm takes the condition off again in the patch that adds its
saved-syscall-frame path".

That patch is the one before this.  arm_syscall() records the caller's
exception frame in xcp.sregs and arm_fork() builds the child from it, so the
condition has nothing left to protect against.

Cortex-M is unaffected either way -- BUILD_KERNEL depends on ARCH_USE_MMU,
which it does not have -- so the only configurations this changes are the
MMU-capable ARM ports, which are exactly the ones the previous commit fixed.

Verified on qemu-armv7a:knsh under qemu-system-arm:  ostest's vfork_test
passes, where before the change vfork() was absent.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-10 08:57:30 -03:00
Marco Casaroli
608d1e269a arch/arm: Build fork() children from the caller's syscall frame on armv7-a.
In a kernel build the cloning primitives are reached through a system call,
and armv7-a dispatches one by re-pointing the caller's own exception frame at
dispatch_syscall() and switching to the task's kernel stack.  The snapshot the
entry point in fork.S takes for itself therefore describes the kernel-side
stub, and the frames below it are on a stack the child gets no copy of:  a
child built from that snapshot resumes at a kernel address with a stack
pointer into its own user stack.  It faulted with a prefetch abort at PC 0 on
qemu-armv7a:knsh, which is why the fork family had never been run there.

Record what the caller was actually doing instead.  arm_syscall() stores the
exception frame of the outermost system call in xcp.sregs, mirroring
riscv_swint.c, and arm_fork() chooses where the caller's registers live:

  arm_fork_syscall()  when a user stack pointer is saved, rebuilding the child
                      from xcp.sregs so that it returns from the very same SVC
                      as the parent, in the same mode, on its own stack and
                      with no inherited system call nesting;
  arm_fork_direct()   otherwise -- the flat build, a kernel thread in any
                      build, and a build without a kernel stack, where the
                      call is dispatched on the caller's own stack so the
                      caller's frames are copied along with the kernel-side
                      ones.

Note that the discriminator is xcp.ustkptr rather than TCB_FLAG_SYSCALL.  On
armv7-a the caller is the task that runs the kernel side of its own system
call, so being in a system call is not by itself a reason to distrust the
snapshot; the switch to the kernel stack is.  Because arm_syscall() has
already re-pointed the frame by the time arm_fork() runs, the caller's PC,
CPSR and SP come from where arm_syscall() put them -- syscall[0].sysreturn,
syscall[0].cpsr and ustkptr -- and the rest from the frame itself.

Nothing selects the primitives on an ARM kernel build yet, so this commit
changes no configuration; it is what the next one needs to be correct.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-10 08:57:30 -03:00
Marco Casaroli
aae62ceaae !Documentation: Describe the fork()/vfork() split.
Documentation/guides/fork_vfork_migration.rst is new.  It says what changed
and why, gives the two primitives as a table, states plainly what breaks, and
answers "which replacement do I want?" from the reader's own reason for having
called fork() -- posix_spawn() or vfork() to run a program, pthread_create()
for a second flow of control that shares memory, fork() itself for an
independent copy.  It also documents the two configuration symbols, what an
architecture has to implement to gain real fork(), and the one visible
consequence of moving the vfork() suspension into the kernel: a waitpid()
after a child that _exit()s can only report status where
CONFIG_SCHED_CHILD_STATUS is enabled.

reference/user/01_task_control.rst gains an entry for fork() and rewrites the
one for vfork(), which described NuttX's limitations rather than the
interface's contract.  standards/posix.rst moves fork() from "No" to "Cond."
and vfork() from "Yes" to "Cond.", both being conditional on the configuration
now.  implementation/memory_configurations.rst no longer lists fork() as
unimplementable in the presence of address environments, which was the whole
point of that section's wish list.  Three long-standing typos in that file are
corrected while touching it, since codespell checks the whole of any file a
patch modifies.

BREAKING CHANGE: this commit carries no code; it is the migration guide for
the fork() withdrawal in the commit before it, and is marked so that every
commit in the series carries the marker CONTRIBUTING.md 1.13 requires.  The
quick fixes are in Documentation/guides/fork_vfork_migration.rst.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-10 08:57:30 -03:00
Marco Casaroli
70c2ef5911 !sched/arch/libc: Give fork() and vfork() their real, separate semantics.
NuttX implemented fork() and vfork() as the same function.  Both were libc
wrappers around a single up_fork() syscall; vfork() differed only by a
trailing waitpid().  Underneath, the child joined the parent's address
environment -- the same addrenv_join() that pthread_create() uses -- and got
a private copy of the stack.  So the child shared .data, .bss and the heap
with its parent and ran concurrently with it.

That is not fork().  It is vfork()-with-a-private-stack under fork()'s name,
and the history says so: today's fork() is NuttX's old vfork(), renamed in
c33d1c9c97 (2023) without any change of behaviour.  The failure was silent --
a program written against POSIX fork() compiled, ran, and had its child's
writes land in the parent's variables.

Separate them into two primitives, chosen by which function the caller
called rather than by what the hardware happens to be:

  fork()   child gets its own copy of the parent's memory at the same
           virtual addresses; runs concurrently.  Only where an address
           environment can be duplicated -- elsewhere it is not declared at
           all, so calling it is a build error naming the function.
  vfork()  child shares the parent's memory; parent suspended until the
           child _exit()s or exec()s.  Implementable everywhere.

Below libc there is still one syscall.  up_fork() gains a bool saying which
primitive the caller used, since the per-architecture register snapshot is
the same for both, and passes it to nxtask_setup_fork(), which is the single
place the memory semantics are decided.  The argument arrives in the first
argument register and is never touched:  each architecture's snapshot takes
some other call-clobbered register for its scratch, so the flag is simply
still there when the C worker is called.

The vfork() parent suspension moves out of libc into nxtask_start_fork(),
released from nxsched_release_tcb() by nxtask_resume_vfork().  Two things
follow: the parent is resumed at exec(), since exec_swap() has already handed
the child's pid to the loaded program by the time the vfork stub exits, and
vfork() no longer depends on CONFIG_SCHED_WAITPID.

Releasing there requires one fix in nxtask_exit().  It raises rtcb->lockcount
directly rather than through sched_lock() while it tears the TCB down, so the
nxsem_post() that wakes the vfork() parent leaves it queued where a blocked
task collects while pre-emption is off -- g_pendingtasks, or g_readytorun on
SMP -- and the matching raw lockcount-- does not publish it the way
sched_unlock() would, leaving the parent stranded with nothing to move it on.
The fix mirrors sched_unlock() for each case:  nxsched_merge_pending(), or
nxsched_deliver_task() under CONFIG_SMP.  Both are no-ops while pre-emption is
still disabled, and up_exit() re-reads this_task() afterwards, so a change of
the ready-to-run head is honoured.  Without it vfork() deadlocks wherever no
other task happens to call sched_unlock() afterwards -- rv-virt:nsh64 and
rv-virt:pnsh64, where NSH is blocked in waitpid() holding the lock, and
qemu-armv8a:citest_smp, which hangs the moment the vfork() test runs.

fork() is built on a new addrenv_fork(), backed by an up_addrenv_fork() hook
that duplicates an address environment into freshly allocated pages mapped at
the same virtual addresses -- unlike up_addrenv_clone(), which copies only
the representation and leaves both pointing at the same page tables.  The
child then adopts the parent's stack geometry rather than being given a
relocated copy: a pointer to a stack local taken before fork() must name the
same object in the child that it named in the parent, and the parent's stack
is already in the duplicate, with its contents, at the parent's address.

No architecture implements up_addrenv_fork() yet, so this commit leaves
fork() unavailable everywhere.  That is the intended state.  It withdraws
fork() from ARCH_ARM, flat ARCH_ARM64, ARCH_RISCV, ARCH_SIM and ARCH_X86_64,
where until now it named the sharing primitive; per-architecture patches
restore it, with POSIX semantics, as up_addrenv_fork() lands.  In the
meantime the sharing primitive is still there under the name that describes
it: vfork() for a child that runs a program, pthread_create() for a second
flow of control that shares memory, posix_spawn() for both at once.

Kconfig: ARCH_HAVE_VFORK inherits ARCH_HAVE_FORK's select lines, conditions
included, so no configuration gains machinery; ARCH_HAVE_FORK is redefined to
mean "can provide POSIX fork() semantics" and now depends on ARCH_ADDRENV.

There is one deliberate departure from "verbatim".  ARCH_ARM selected the
fork family unconditionally, BUILD_KERNEL included, and that has never
worked:  on a kernel build the architecture's fork entry point sees the
kernel's return address and stack pointer rather than the caller's, so the
child resumes at a kernel address.  On qemu-armv7a:knsh master faults in
ostest's fork case with "Child did not run" and then a data abort; without
the condition this change faults the same way through vfork().  ARCH_ARM64
and ARCH_X86_64 already carried "if !BUILD_KERNEL" for exactly this reason --
ARM was the outlier.  Conditioning it turns a runtime fault into an honest
absence, which is the whole point of the change; arch/arm takes the condition
off again in the patch that adds its saved-syscall-frame path.  Only the
MMU-capable ARM ports are affected, since Cortex-M cannot build BUILD_KERNEL
at all.

Also fixes two latent syntax errors found on the way: a missing comma in
riscv_fork.c and mips_fork.c, both in *_FRAMEPOINTER && !SAVE_GP branches
that are never compiled today.

BREAKING CHANGE: fork() is withdrawn from every architecture.  It is no
longer declared in unistd.h, so code that calls it fails to build with an error
naming the function, and the sharing behaviour it used to have is gone rather
than renamed.  CONFIG_ARCH_HAVE_FORK no longer means "fork() exists"; it means
"this configuration can provide POSIX fork() semantics", and no architecture
selects it yet.

Quick fix, chosen by why the call was made:

  to run a program                vfork() + exec*(), or better posix_spawn()
  a second flow of control that   pthread_create()
  shares the caller's memory
  a genuinely independent copy    keep fork(), and wait for the per-arch patch
  of the process                  that implements up_addrenv_fork() and selects
                                  CONFIG_ARCH_HAVE_FORK

Out-of-tree code that tests CONFIG_ARCH_HAVE_FORK to decide whether a
fork-then-exec path is available wants CONFIG_ARCH_HAVE_VFORK instead, which is
selected in exactly the places CONFIG_ARCH_HAVE_FORK used to be.  The full
migration guide is Documentation/guides/fork_vfork_migration.rst.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-08-10 08:57:30 -03:00
Alan Carvalho de Assis
87260499e1 cmake: Use NUTTX(_DIR/_BIN_DIR) instead CMAKE(_SRC_DIR/_BIN_DIR)
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
This change fixes NuttX’s CMake support when NuttX is embedded
in another project via add_subdirectory(). CMake’s CMAKE_SOURCE_DIR
and CMAKE_BINARY_DIR refer to the outermost project, causing NuttX
to access its .config, generated files, host tools, and build artifacts
in the parent project’s directories. The fix introduces NUTTX_DIR and
NUTTX_BINARY_DIR, based on CMAKE_CURRENT_SOURCE_DIR and
CMAKE_CURRENT_BINARY_DIR, and consistently uses them for NuttX
self-references while preserving existing standalone builds. It fixes
the Kconfig initialization failure reported in #19697 and allows an
embedded sim:nsh build to configure, build, and boot successfully.
The change affects only the CMake build system (not Make or Kconfig
defaults), requires the corresponding nuttx-apps change, and does not
extend add_subdirectory() support to cross-compiled non-sim boards due
to CMake’s toolchain-file limitation.

Fixes #19697.

Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Alan Carvalho de Assis <acassis@gmail.com>
2026-08-09 11:13:08 -03:00
Justin Hammond
b8e26b127e drivers/usbhost: Let the HID keyboard pick its interrupt pipe.
HIDKBD_NOGETREPORT reads keyboard reports with DRVR_ASYNCH(), and that
macro is only defined when USBHOST_ASYNCH is set.  The option selected
neither, so turning it on by itself fails at the call site with no hint
that a second option was meant to come with it.

Select it.  Every in-tree configuration that sets NOGETREPORT already
resolves USBHOST_ASYNCH: ci20:jumbo and sama5d3-xplained:bluetooth
through USBHOST_HUB, and the two linum-stm32h753bi configurations by
setting it directly.  No existing build changes.

The two that set it directly no longer can, since a selected symbol is
no longer settable, so savedefconfig drops the line.  Their defconfigs
are normalized here to keep them canonical.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-09 20:57:41 +08:00
Justin Hammond
a023f61a38 drivers/usbhost: Do not unregister a HID keyboard that never registered.
usbhost_destroy() unregisters the keyboard unconditionally, and it runs
for a device that never got as far as being registered as well: an
enumeration that failed part way through, or a device unplugged while it
was still being set up.

The upper half does not tolerate that call.  It asserts that the lower
half carries the state keyboard_register() puts there, so a keyboard that
fails to come up takes the system down with an assertion rather than
being cleaned up and forgotten.  Seen on a low speed keyboard that
attaches and then does not finish enumerating.

The state the registration leaves behind is what says whether there is
anything to undo, so look at it first.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-09 20:57:41 +08:00
Justin Hammond
31fbb99218 sched/semaphore: Keep a negative task id out of the mutex holder.
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
A mutex records its holder as a task id in the low 31 bits of a word
whose top bit means "someone is blocked on this".  The id was stored
without masking, so an id with its top bit set became a holder with the
blocking bit raised.

Task ids are normally small and positive, but not always.
nxsched_gettid() reports -ESRCH for a context that no longer maps to a
running task, and there is a window where that is exactly what the
running context is: nxtask_exit() marks the next task ready to run
while the dying task is still executing on its own stack, and only then
releases the TCB.  Freeing the group inside that release takes and
drops the group's mutexes, so the lock stores 0xfffffffd and the unlock
compares 0x7ffffffd, which are not equal.

With assertions enabled the unlock trips its holder check, and every
exit of a process that frees memory panics.  In a kernel build that is
every exit, so no program could be run twice, and running one at all
took the shell down with it.  Without assertions the failure is silent:
the accidental blocking bit sends the unlock looking for a waiter that
never existed.

Encode the id the same way everywhere it is stored or compared, so that
a lock and an unlock from one context agree whatever the id's sign.
The masked forms of -1 and -2 would alias the "no holder" and "reset"
values, but nxsched_gettid() yields only valid ids and -ESRCH.

mm_lock() already sidesteps this window with a note that gettid() may
return -ESRCH during a context switch; this gives the generic mutex the
same footing rather than a second special case.

Test case, on the EIC7700 EVB, which is a kernel build with assertions:

  nsh> hello
  Hello, World!!

Before, that printed and then panicked in sem_post, taking the shell
with it, every time.  After, five runs in a row complete and the shell
survives.  ps over telnet still completes.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-09 10:05:30 +08:00
raiden00pl
753d2a3466 drivers/serial: fetch uart_writev data from the iovec segment
Some checks failed
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
Build Documentation / build-html (push) Has been cancelled
Since 00010089b8 uart_writev() takes the data one byte at a time with
uio_copyto() plus uio_advance().  Both of them walk the iovec list and
redo the byte counters for every single byte, so most of the work is
bookkeeping rather than copying.  On slow cores this is what limits how
fast the TX buffer can be filled.

Take a pointer to the current iovec segment and read the bytes straight
from it, and move the uio forward once per segment instead of once per
byte.  nseg counts only the bytes that really went into the buffer: it
is increased at the end of a loop pass, and that step is skipped when
uart_putxmitchar() fails.

Measured on nRF52840 (Cortex-M4, 64 MHz), 8 MiB write() to a CDC/ACM
port: 223 KB/s to 481 KB/s.

Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
2026-08-09 03:23:28 +08:00
guanyi3
e7ef45d39a Documentation: add devfreq framework documentation
Document the device frequency scaling framework: the QoS/governor arbitration model, the lower-half driver interface, built-in governors, in-kernel QoS requests, change notifications, procfs, and suspend/resume.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
3839a11f2b drivers/devfreq: add ondemand governor build support
Add Kconfig, Make.defs, and CMakeLists.txt entries for the ondemand governor so it can be enabled via CONFIG_DEVFREQ_GOV_ONDEMAND.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
2d66436185 drivers/devfreq: guard backtrace code with CONFIG_LIBC_BACKTRACE_DEPTH
When CONFIG_LIBC_BACKTRACE_DEPTH is not set or <= 0, backtrace_get()
is a macro that always sets depth to 0, making the for-loop body
unreachable (Coverity CID 8405332 DEADCODE).

Wrap backtrace_get() call, the loop, and related variable declarations
with #if CONFIG_LIBC_BACKTRACE_DEPTH > 0 to eliminate the dead code
and avoid unused variable warnings.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
31041f84ea drivers/devfreq: fix qos_get_value returning wrong min/max aggregation
QOS_REQ_MIN should return the highest value among all min requests
(most restrictive lower bound), but plist_first returns the lowest.
QOS_REQ_MAX should return the lowest value among all max requests
(most restrictive upper bound), but plist_last returns the highest.

This caused qos constraints to be ineffective. For example, two
requests (32, 208000) and (104000, 104000) would merge to (32, 208000)
instead of the correct (104000, 104000).

Fix by using plist_last for QOS_REQ_MIN and plist_first for QOS_REQ_MAX.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
674e5ef4d8 drivers/devfreq: use hardware frequency instead of cached value in driver_target
The cached devfreq->cur may become stale when the hardware frequency is
changed externally (e.g. by another core or governor). This causes
driver_target to incorrectly skip frequency transitions when the target
matches the cached value but differs from the actual hardware frequency.

Use driver->get_frequency() to read the real hardware frequency for the
unchanged check, and sync devfreq->cur on match to keep the cache correct.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
414694b780 devfreq/ondemand: fix use-after-free in ondemand worker
When devfreq_gov_ondemand_stop() is called from idle task context,
work_cancel() is used instead of work_cancel_sync(), which does not
wait for the currently running worker to complete. If
devfreq_gov_ondemand_exit() then frees governor_data, the worker
may still be accessing it, causing a use-after-free crash.

Fix this by:
- Nullifying dev->governor_data under dev->lock in exit before freeing.
- Moving the governor_data read inside dev->lock in the worker and
  adding a NULL check to bail out early if data has been freed.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
77cb20f041 drivers/devfreq: add conflict_policy to devfreq_driver_s
When multiple QoS requests have no overlapping frequency range (min > max), the previous behavior always clamped to the lower frequency. Add a conflict_policy field to devfreq_driver_s so callers can choose between DEVFREQ_CONFLICT_PREFER_HIGH (default, choose higher freq) and DEVFREQ_CONFLICT_PREFER_LOW (choose lower freq) at registration.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
7a1c62911d devfreq/procfs: add write support for frequency QoS constraints
Add the ability to set frequency constraints via procfs write.
Supported formats:
  echo <min>,<max> > /proc/devfreq/<name>  - set frequency range
  echo 0,0 > /proc/devfreq/<name>          - remove constraint

The QoS request is bound to the devfreq device lifetime so that
shell commands like echo (which open, write, close immediately)
work correctly. Leading whitespace in the write buffer is skipped
to handle extra writes from nsh echo (e.g. trailing newline).

Also add write permissions in devfreq_stat() and a procfs_qos
field in devfreq_s guarded by CONFIG_DEVFREQ_PROCFS.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
(cherry picked from commit 70ae195c84f35a4d0b85fcc14187989b60fc0280)
2026-08-08 15:23:54 -03:00
guanyi3
ecd64e04c0 drivers/devfreq: replace mutex to spinlock
we may call devfreq_find_by_name() in pm_callback, and shouldn't call nxmutex_lock() in idle_loop, so replace mutex to spinlock.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
072cae5193 drivers/devfreq: ondemand should init governor_data before use it
devfreq_qos_add_request -> devfreq_refresh_limit -> devfreq_limit_governor -> devfreq_gov_ondemand_limit, here use governor_data but it's 0x0

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
4f6a0bd36c drivers/devfreq: add ondemand governor
Add devfreq ondemand governor that scales device frequency based on CPU load. When CPU load exceeds the configured threshold, frequency is set to maximum; otherwise it is scaled proportionally.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
20cb512617 drivers/devfreq: add const to devfreq_governor_s and devfreq_driver_s
we do not hope the governor and driver in devfreq to be modified.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi3
1e2f9745cb drivers/devfreq: remove default governor
It's better not to use global governor, as modifying one device will cause all devices' governor to be modified.

Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi
827b455f68 driver/devfreq: add procfs for devfreq
> ls /proc/devfreq
 /proc/devfreq:
 test_devfreq
> cat /proc/devfreq/test_devfreq
 devfreq:     test_devfreq
 governor:    test_devfreq_governor
 cur_freq:    500
 suspended:   False
 freq_table:  100 300 500 700 900
 qos_list(min, max, backtrace):
 195, 829, 0x4007c26 0x40a0e0e 0x405c706 0x4011186 0x4010dca 0x42777cc 0x4062f7e 0x409da6a

Signed-off-by: guanyi <guanyi@xiaomi.com>
2026-08-08 15:23:54 -03:00
guanyi
e29a7bcb07 driver/devfreq: DVFS framework for devices
This commit introduces a devfreq framework to manage device frequency
scaling. The framework includes the following features:
1.devfreq governor
  - provide governor ops, including init, start, stop, exit
  - default governor, performance & powersave
  - customized governor, device can provide governor when register
2.runtime register and unregister
  - device can runtime register & unregister, search by name
3.suspend and resume
  - suspend and resume frequency scaling
4.notify
  - register & unregister notifier callback, notify frequency changes
5.qos support
  - simplified QoS, manage multiple freq range request
  - including init, add/remove/update request, get value

Signed-off-by: guanyi <guanyi@xiaomi.com>
2026-08-08 15:23:54 -03:00
Justin Hammond
875e86bd35 syslog/ramlog: Survive writes made before the OS is ready.
The RAM log is the natural home for boot messages, yet writing to it
during early boot could crash the system it was meant to describe.
ramlog_addbuf took the critical section on every write, and
enter_critical_section consults the current task; on ports whose
first syslog output happens before the task lists exist, that lookup
walks uninitialized state and faults.  The notification path was
worse still, locking a scheduler that did not exist yet.

Guard both.  Before the task lists exist, plain interrupt masking
protects the buffer just as well, since there is only one thread of
control; and readers are only notified once there is an operating
system to notify them through.  The bytes land in the buffer either
way, so nothing logged before the OS is ready is lost.

Found on the EIC7700X port, which logs from its start routine before
the MMU is up: enabling RAMLOG_SYSLOG there turned the boot into a
silent wedge two characters in.  With this change the same
configuration boots and `dmesg` replays the full early history.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 15:20:54 -03:00
Justin Hammond
d95d8c0fb1 usbhost: Report each device as it is enumerated.
A host that enumerates a device says nothing about it unless the whole of
CONFIG_DEBUG_USB_INFO is on, and then it says a great deal else besides.
The quietest case is the one that matters most: a device no class driver
claims produces no output at all, so a user with an unsupported device
sees exactly what a user with no device sees.

Add CONFIG_USBHOST_ANNOUNCE, reporting each device once, in the shape a
reader is likely to recognise from other systems: where it is, what it is,
its vendor, product and release, and the maker, product and serial number
it reports in its own string descriptors.  Those cost a control transfer
each, so they are read only where a report was asked for, and only once
the device is addressed.

The report is made after binding rather than from within it, because a
composite device never reaches the class lookup: usbhost_composite() is
tried first and binds it.  Whether a driver claimed the device is tracked
rather than read from the returned status, which the per interface loop
sets to OK whatever happened.

The port is given as the path from the root hub, and the path names the
bus, because a device on the first port of a hub and one on the first port
of a controller are otherwise reported identically.  struct
usbhost_roothubport_s gains that bus number for the purpose; a driver that
does not set it reports zero, which is the only bus it has.

Class codes are translated where a name is more use than a number, which
includes the HID boot protocols, so a keyboard is reported as a keyboard.

Default n, so no existing configuration changes.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-09 02:09:47 +08:00
raiden00pl
58d1d9f9d6 drivers/usbdev/cdcacm: serialize the TX ring drain with the class spinlock
cdcacm_sndpacket() runs from task context and from the bulk IN
completion callback, which may be interrupt context.  cdcuart_dmasend()
advances the xmit tail non-atomically, so a completion arriving mid
setup re-sends the same region and advances the tail past the head,
re-transmitting a ring of stale data.

c497c5feb0 dropped the critical section that used to cover this.
Restore it with priv->lock held across the setup and EP_SUBMIT; the
submit must stay inside to keep request order.  cdcuart_dmasend() now
runs with the lock held, so its own acquisition is removed.

The race needs the writer to keep the ring non-empty across
completions, so it only appears at high sustained write rates.  On
nRF52840, 131072-byte writes were received as ~147600 bytes - one extra
CDCACM_TXBUFSIZE of stale data per hit.  With this change the host
receives exactly what was sent.

Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
2026-08-09 02:04:26 +08:00
Lwazi Dube
60abe4744f arch/mips/jz4780: Add hardware Random Number Generator (RNG) support
Add support for the hardware Random Number Generator (RNG) module found
on the Ingenic JZ4780 SoC.

Changes include:
- Update jz4780 chip.h with power management controllor base
  address JZPMC_BASE and register offsets for the RNG registers.
- A new file jz4780_rng.c to provide character driver interfaces
  for `/dev/random` and `/dev/urandom` utilizing the hardware
  RNG block.

Signed-off-by: Lwazi Dube <lwazeh@gmail.com>
2026-08-08 15:03:44 -03:00
Justin Hammond
cda33ae4fc libs/libc/netdb: Size an answer header by its header, not by its union.
dns_recv_response() checked for room using sizeof(struct dns_answer_s),
but that structure is the 10-byte header plus a union holding the largest
address it can carry.  With IPv6 built the union is 16 bytes, so the check
demanded 26 bytes where 10 were needed, and any answer sitting at the end
of a response was rejected as truncated.

An A record answer supplies 14 bytes, so whether a lookup worked depended
on how much padding the server happened to send after it:

  $ dig +noedns @10.1.1.2 github.com A      # ANSWER 1, AUTHORITY 0, ADDITIONAL 0
  -> answer is last in the packet, 14 bytes remain, rejected

  $ dig +noedns @10.11.5.254 github.com A   # ANSWER 1, AUTHORITY 13, ADDITIONAL 7
  -> 26+ bytes remain, accepted

On the board, before and after, against the first of those servers:

  nsh> nslookup apache.org
  [CPU1] dns_recv_response: DNS answer header truncated
  Host: apache.org Addr: 2a04:4e42::644                 <- A record lost

  nsh> nslookup apache.org
  Host: apache.org Addr: 2a04:4e42::644
  Host: apache.org Addr: 151.101.2.132                  <- both returned

The address that follows the header is already bounds checked separately,
where its real length is known, so only the header check was wrong.  The
size is now a named constant next to the structure, since the rest of this
function already used the literal 10 for the same quantity.

Only IPv4-only builds escaped it, where sizeof happens to equal 14 and an
A record fits exactly.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 15:02:15 -03:00
Justin Hammond
27ff1e74e1 sched/signal: Unblock sigtimedwait through kernel memory.
nxsig_timedwait parked a pointer to the caller's siginfo buffer in the
TCB, for whoever eventually posts the signal to fill in.  But the
poster fills it in from its own context (another task, a kernel
thread, an interrupt), and in a kernel build the caller's buffer is
an address in the caller's private address space, which the poster
does not share.  The write lands wherever the currently active
mappings put it: the waiter wakes to find garbage where the signal
number should be, and some other process is left with a corrupted
page.  Flat builds share one address space, which is why this never
showed there.

Park the stack local in the TCB instead.  That is kernel memory,
mapped in every context, and it is copied out to the caller's buffer
after waking, in the caller's own context, exactly where the
pending-signal path already does the same thing.

Found on the EIC7700X port by an RTC alarm: the alarm signal, posted
from the low-priority work queue, woke a sigwaitinfo caller into an
assertion on the unblocking signal number while the init process,
whose address space had received the stray write, died of a jump to
address zero.  With this change the same test arms, waits and wakes
cleanly, repeatedly.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 14:59:21 -03:00
raiden00pl
0b3848ef6b arch/nrf52: add SAADC continuous sampling mode
Add CONFIG_NRF52_SAADC_CONTINUOUS for gapless timer-triggered sampling
with double-buffered EasyDMA. The SAADC is auto-restarted directly from
the END event through a PPI channel (no CPU in the loop) and each
completed buffer is delivered to the ADC upper half via the batch
interface. Enables high-rate single-channel streaming.

Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
2026-08-08 14:56:52 -03:00
Justin Hammond
8279a7e755 drivers/usbhost: Refuse to register the same class driver twice.
The registry is a singly linked list of static structures, so registering
one of them a second time does not add a second entry: it points that
entry's own link at itself, and the list stops having an end.

Nothing notices while every device that turns up matches something near
the head, because the search returns before it reaches the loop.  The
first device that matches nothing at all, meaning anything without a
class driver built in, walks the list to look for it and never comes back,
holding the registry lock.  On a multiprocessor the rest of the system
follows it down: every other processor that touches the registry spins,
and on the one measured here that included the console, so a board with a
USB keyboard and no keyboard driver came up and then answered nothing.

Registering twice is easy to do by accident.  drivers_initialize() calls
usbhost_drivers_initialize(), which registers every class the
configuration selected, and board code that also registers one, which
many boards do, gets a second call for free.

So look before linking, and treat a repeat registration as the no-op the
caller expected it to be.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 14:55:34 -03:00
Justin Hammond
dd1b57577c drivers/usbhost: Correct the xHCI controller name in a trace string.
The control transfer trace entry names the controller "HXCI", so a log of
an enumeration reads as though a different controller were involved.  One
neighbouring entry also spells the port "RHport" where the rest of the
table spells it "RHPort".

Text only.  No trace identifier or argument changes.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 14:55:00 -03:00
Justin Hammond
4e7703792f drivers/usbhost: Define the xHCI endpoint-allocation trace.
The xHCI driver traces endpoint allocation with XHCI_VTRACE2_EPALLOC, but
that id is in neither the enumeration nor the string table, so the driver
does not compile once verbose tracing is turned on:

  usbhost_xhci_pci.c:3285: 'XHCI_VTRACE2_EPALLOC' undeclared

It builds today only because usbhost_vtrace2() collapses to a macro that
discards its arguments unless HAVE_USBHOST_TRACE_VERBOSE is defined, which
is what stops anyone finding this until they go looking for a trace.

Add the id and the string to match, keeping the two tables in step.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 14:55:00 -03:00
raiden00pl
a4e2723ea6 boards/nrf53/thingy53: configure XOSC32MCAP
configure XOSC32MCAP for thingy53 XTAL

Signed-off-by: raiden00pl <raiden00@railab.me>
2026-08-08 14:43:09 -03:00