Commit graph

7714 commits

Author SHA1 Message Date
rongbaichuan
00a379c3f7 sched/semaphore: Fix pre-existing nxstyle issues in the touched files
The CI style check runs nxstyle over every file a pull request touches,
so the files changed by the previous two commits have to comply even
where the problems were not introduced here.  504 errors in 25 files are
fixed: whitespace, blank lines, brace placement, switch/case indentation,
label indentation and comment blocks only, with no functional change.

Assisted-by: DeepSeek Harness:deepseek-flash
Signed-off-by: rongbaichuan <rongbaichuan1027@163.com>
2026-09-25 10:37:43 +02:00
rongbaichuan
59d5ce0f31 sched/semaphore: Remove the return value check of nxsem_init/nxmutex_init
nxsem_init(), nxsem_destroy(), nxmutex_init() and nxmutex_destroy()
always return OK, so checking the result only leaves dead code: the
compiler cannot remove it, because these are cross-translation-unit calls
and the nxrmutex_destroy() test is duplicated into every inlined call
site.

Apply the convention already established in commit a47a36bc5b (PR #7473)
to the two definitions which still test the value and to the 54 remaining
call sites. No signature or prototype is changed.

Testing: stm32f103-minimum:nsh builds with -Os without new warnings.

Assisted-by: DeepSeek Harness:deepseek-flash
Signed-off-by: rongbaichuan <rongbaichuan1027@163.com>
2026-09-25 10:37:43 +02:00
raiden00pl
f021af714d drivers/ioexpander/sx1509: include nuttx/arch.h for up_mdelay
up_mdelay() is used in the reset sequence but nuttx/arch.h was not
included, causing an implicit-declaration build error.

Signed-off-by: raiden00pl <raiden00@railab.me>
2026-09-24 20:16:12 +08:00
raiden00pl
1a0032105d drivers/ioexpander/sx1509: implement the pin PWM operation
Implement ioe_setpwm for the SX1509 by mapping the duty cycle to the
LED driver ON intensity of the pin.

Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
2026-09-24 20:16:12 +08:00
raiden00pl
c31b87ee1e drivers/ioexpander: add an optional pin PWM operation
Add an ioe_setpwm operation (guarded by CONFIG_IOEXPANDER_PWM) for
expanders that can modulate their outputs, e.g. through a LED driver
engine.

Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
2026-09-24 20:16:12 +08:00
Felipe Moura
4818198a0d drivers/lsm6ds3trc_uorb.c: keep the reset comment board/arch-agnostic
#20231 added a comment ahead of the sensor's power-on SW_RESET that
named esp32s3-specific things in otherwise generic driver code:
esptool/RTS-pin reset vocabulary, a literal path to
boards/xtensa/esp32s3/common/src/esp32s3_board_lsm6ds3trc.c, and the
espressif-arch esp_gpioirqenable() function.

None of that is specific to this driver's actual logic, which is
reached by any board wiring this sensor's INT1 through its own
config->attach() callback, whatever the arch. Reworded to describe
the reset/level-trigger requirement in those generic terms instead,
and dropped an ESP32S3-collar bring-up anecdote that does not belong
in driver documentation.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Assisted-by: Claude:claude-sonnet-5
2026-09-23 08:22:52 +02:00
Felipe Moura
c4c9eab46f sensors/lsm6ds3trc: recover from a failed FIFO drain instead of wedging
A single failed burst read of FIFO_DATA_OUT was enough to take the board
down.

The drain path gave up on error, unlocked and returned, leaving the FIFO
above its watermark.  INT1 is level triggered on exactly that condition,
so the line stayed asserted, the worker was re-entered the instant the
IRQ was re-enabled, failed again, and that hot loop starved every other
task until the board wedged -- console cut off mid-line, no crash dump.
Observed killing a board within seconds of the first failure.

Fix: if the read fails, empty the FIFO through Bypass and restore the
previous mode bits, which deasserts INT1.  That costs one batch of
samples and acquisition resumes on the next watermark.  Restoring the
saved bits rather than recomputing them preserves the FIFO-only ODR set
by fifo_configure().

Losing a batch is a far better outcome than losing the board.

The underlying cause of these timeouts on esp32s3 was light sleep cutting
the transfer in half; that is fixed separately in esp32s3_i2c.c.  This
commit is the driver recovering gracefully from a failed read whatever
its cause, which it was not before.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
2026-09-22 13:50:01 +08:00
Felipe Moura
08ee713b4d sensors/lsm6ds3trc: move the FIFO drain buffer off the stack
The worker declared its drain buffer as int16_t raw[FIFO_MAX_WORDS], and
FIFO_MAX_WORDS scales with CONFIG_SENSORS_LSM6DS3TRC_FIFO_WATERMARK.

At the Kconfig default watermark of 8 that is 192 bytes and nobody ever
noticed.  At the watermark this collar uses, 250, it is 6000 bytes inside
an 8192-byte HPWORK stack -- 73% of it, before the call frame and the
whole I2C stack underneath.  Any board raising the watermark walks into a
stack overflow in a shared work queue, which is about the worst place to
find one.

Allocated once at registration so the drain path stays allocation-free,
and the driver fails registration cleanly if it cannot get the memory.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
2026-09-22 13:50:01 +08:00
Felipe Moura
159ef8105b sensors/lsm6ds3trc: reset the sensor before arming its interrupt
lsm6ds3trc_register() attached the INT1 handler without ever putting the
sensor into a known state, which made every reboot a coin toss.

The LSM6DS3TR-C has its own supply and its own reset.  An MCU reset --
watchdog, RTS pin, esptool, a plain "reboot" -- does not reset it, so it
comes back still holding whatever the previous session configured: for
this driver, INT1_CTRL.INT1_FTH still set and a FIFO still over its
watermark, i.e. INT1 already asserted at registration time.

With the (correct) ONHIGH level trigger, arming an already-active line
storms immediately.  The board then wedges during bring-up with no
console output and no crash dump -- it looked like a boot that stopped
right after Wi-Fi init and never reached NSH.  That symptom cost a long
detour: it was blamed in turn on a stuck I2C bus, on corrupted NVS/Wi-Fi
calibration, and finally on a failing USB-serial adapter, because the one
thing that reliably cleared it was unplugging the board -- which is
simply the only way to power-cycle the *sensor*.

SW_RESET (CTRL3_C bit 0) clears INT1_CTRL and FIFO_CTRL back to 0, which
deasserts INT1.  It self-clears in ~50 us; poll for it rather than
assume, and retry the write a few times, since the bus has been seen to
return -EIO on the very first transaction after a cold boot.

Carry on if the reset never takes.  An unreset sensor risks the storm
this exists to prevent, but refusing to register leaves the application
with no /dev/uorb/sensor_accel0 at all, which is fatal to it -- a single
-EIO here took the whole collar down once.  Losing the sensor to guard
against a maybe-storm is the wrong trade.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
2026-09-22 13:50:01 +08:00
Felipe Moura
3d267aab2a espressif/esp_irq.c: fix up_disable_irq()/up_enable_irq() for GPIO IRQs
esp_gpio_irq() registers per-pin GPIO interrupts through
gpio_isr_handler_add(), never through esp_setup_irq(), so
esp_get_handle() never finds them and up_disable_irq()/up_enable_irq()
silently no-op for any GPIO-derived irq number. Fall back to
esp_gpioirqdisable()/esp_gpioirqenable() (translating irq back to a
pin via ESP_IRQ2PIN()) when the normal interrupt-matrix lookup misses.

This surfaced through drivers/sensors/lsm6ds3trc_uorb.c: its ISR
schedules a worker to drain the sensor's FIFO over I2C and disables
its own IRQ until the worker re-enables it, so a level-triggered
source (e.g. a PM GPIO wake source left in level mode) doesn't
refire continuously and starve every task, HPWORK included, before
the worker ever gets to run. That disable/enable only works now that
up_disable_irq()/up_enable_irq() actually do something for GPIO irqs.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Assisted-by: Claude:claude-sonnet-5
2026-09-22 13:50:01 +08:00
Ulaş Sertan Kemeç
fa935ecae1 drivers/vhost: Add vhost-net, a device-role virtio network driver.
Implements the device end of virtio-net, so a peer running the stock
virtio-net driver sees this side as a network card, and registers a netdev
lowerhalf.

Ring layout follows the peer's numbering: vq[0] is its RX queue, which we fill
to transmit, and vq[1] its TX queue, which we harvest.  No features are
negotiated, so every frame carries the zeroed legacy virtio_net_hdr.

Peer buffers are reached by raw 64-bit address through an arch-provided
translation window -- the AM67 RAT, identity mapping elsewhere -- splitting
copies that straddle it.

Also gives DRIVERS_VHOST a prompt; it was promptless and so unselectable
without a driver forcing it.

Verified on t3-gem-o1 against an unmodified Linux virtio_net: eth0 registers,
ifup brings it to RUNNING, and the peer pings it 5/5 at 0.27 ms and 60/60 with
0% loss.

Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Ulaş Sertan Kemeç <sertan.usk@gmail.com>
2026-09-21 10:40:13 -03:00
Ulaş Sertan Kemeç
c77c981850 drivers/vhost: Add vhost_get_vq_buffers_pa().
vhost_get_vq_buffers() converts descriptor addresses through the shared-memory
I/O region, which truncates silently when the CPU cannot address all of the
peer's memory -- a 32-bit remote core against a 64-bit host, where
metal_phys_addr_t is 32-bit and Linux posts buffers above 4 GB.

Returns the raw 64-bit address and length instead, so class drivers can
translate through platform window hardware.  Completion is unchanged.

Assisted-by: Claude Code:claude-fable-5
Signed-off-by: Ulaş Sertan Kemeç <sertan.usk@gmail.com>
2026-09-21 10:40:13 -03:00
Daniel P. Carvalho
f7e3b0fa89 drivers/timers/ptp_clock_dummy: fix the nanoseconds of the monotonic time.
ptp_clock_dummy_getcrosststamp() stored the seconds of the monotonic
clock in the nanoseconds field of the monoraw member, so the
monotonic time of the cross timestamp was wrong. Store the nanoseconds.

Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
Assisted-by: Claude:claude-sonnet-5
2026-09-20 17:54:48 -03:00
Justin Hammond
c7d6f51b9c drivers/usbhost: Stop retrying an xHCI port that will not enumerate.
xhci_enumerate() reports failure by marking the hub port disconnected,
which is what makes xhci_wait() return and the attempt repeat.  The root
port is still connected, so the two disagree again immediately and the
attempt repeats for as long as the device stays plugged in.  A device that
fails every time is retried forever: 1055 attempts in 90 seconds on an
EIC7700X board, enough console traffic to make the board unusable.

Count consecutive failures per root port and stop at
CONFIG_USBHOST_XHCI_ENUM_RETRIES, leaving the port as it is so xhci_wait()
blocks until something physically changes.  A new connection clears the
count, as does a successful enumeration, so a device needing a second
attempt still gets one.  The default of three rides out a slow device or a
marginal reset.

The same board now makes three attempts, reports that it has given up and
falls silent, while a keyboard on the other port enumerates throughout.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
18c834b7d3 drivers/usbhost: Release the xHCI slot when enumeration fails.
A device slot is a finite controller resource: HCSPARAMS1 reports how many
exist and Enable Slot fails with No Slots Available once they are gone.
Two paths took one and returned without giving it back.

xhci_device_init() enables a slot before initialising the transfer ring,
the slot context and the device address, and each of those returned
directly on failure.  It also treated a slot number larger than the
controller supports as success, since Enable Slot itself had succeeded.

xhci_enumerate() is the larger leak: the device is addressed by the time
usbhost_enumerate() runs, so a device whose descriptor cannot be read, or
that no class driver claims, leaves the slot held.  That path clears
hport->connected so the port is retried, taking another slot each time.

Release the slot on both paths with xhci_device_deinit(), which issues
Disable Slot, clears the DCBAA entry and resets the context.  The endpoint
ring is left allocated; xhci_ring_init() reuses an existing one.

Tested on an EIC7700X board with a device no class driver claims, so the
port retries indefinitely: previously the eighth attempt failed with
completion code 9 and the controller enumerated nothing further on either
port; now 1104 consecutive attempts produced no slot failure.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
fc58227802 drivers/usbhost: Serialise xHCI transfers per endpoint.
xhci_ctrl_xfer() and xhci_transfer() release the controller lock before
xhci_transfer_wait(), so the lock does not cover the interval in which a
transfer is outstanding.  Two threads issuing requests on the same
endpoint both reach xhci_ioc_setup(), and the second trips the
DEBUGASSERT(!epinfo->iocwait) that guards it, or overwrites the first
thread's completion state where assertions are compiled out.

A default control endpoint reaches this readily: every interface driver on
a composite device speaks through endpoint 0, so a two interface HID
keyboard runs two poll threads both issuing GET_REPORT.

Other host controller drivers hold the controller lock across the wait,
which here would serialise the whole controller and give up the per
endpoint rings xHCI provides.  Add a mutex to struct xhci_epinfo_s and
hold that instead.  It is taken before the controller lock on both paths,
so the order is endpoint then controller.

xhci_epfree() also freed the endpoint container without destroying iocsem.
Destroy both.

Reachable on any xHCI controller, independently of the preceding commits.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
a5431319bb drivers/usbhost: Make xHCI asynchronous transfers deliver their data.
Submitting an asynchronous transfer refused any buffer needing a cache
line stand-in, and that test also refuses every buffer whose length is not
a whole number of cache lines, which an interrupt transfer's rarely is: a
HID keyboard reads eight bytes.  Every submission returned -EFAULT before
a descriptor was written, and a class driver resubmitting from its
completion callback never sees a second chance.

The refusal existed because the copy out of a stand-in is done by the
blocked caller, and an asynchronous transfer has none.  The work queue
thread handling the completion will do: a buffer given to DRVR_ASYNCH
comes from DRVR_ALLOC, so it is kernel memory reachable from any thread.
Use the same stand-in machinery as every other transfer and finish the DMA
in the completion, just before the callback.  A cancelled transfer returns
its stand-in on cancellation.

The callback also moves outside the spinlock.  It is class driver code
that queues work and takes its own locks, and it may now free a stand-in.
Whether a completion is synchronous is still decided under the lock, since
a posted waiter may be carrying a new transfer immediately.

The asynchronous setup now records the requested length, as the
synchronous setup does.  The byte count handed to the callback is worked
out from it and the residue, and was previously whatever the endpoint held
from an earlier transfer.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
62a7507427 drivers/usbhost: Check for the device before allocating an endpoint.
A root hub port whose enumeration failed is enumerated again, and the slot
the failed attempt used has been given back by then, so the port has no
device context behind it.  xhci_epalloc() took that pointer and wrote the
new endpoint through it without looking, so the retry stored through NULL
and took the system down in answer to a device that had merely failed to
come up.

Check for the device, and free the endpoint that has no home rather than
leaking it.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
a11cecd100 drivers/usbhost: Convert the xHCI endpoint interval from the descriptor.
The Interval field of an endpoint context is an exponent: the controller
services the endpoint every 2^Interval microframes.  An endpoint
descriptor states its period differently depending on device speed, so the
number cannot be copied across, which is what this did.  A low speed
keyboard asking to be polled every 10ms was programmed as 2^10
microframes, which the controller would not accept: Configure Endpoint
went unanswered and allocation failed with -EIO.

Low and full speed interrupt endpoints state a period in frames, so the
exponent is the highest bit of that period in microframes, clamped to the
range the specification allows.  Other periodic endpoints already state an
exponent, one greater than the one wanted here.  Control and bulk
endpoints are not periodic and the field means nothing to them.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
f8ea0f3d02 drivers/usbhost: Copy an xHCI stand-in buffer in the caller's context.
The copy out of a stand-in was done in the completion handler, which runs
on a work queue, while the buffer it copies into may belong to a user
process whose addresses mean nothing there.  Reading a block device
directly from a user program faulted.  The caller is blocked until the
transfer finishes, so the copy belongs there.

An asynchronous transfer has no blocked caller to come back to, so a
buffer that would need a stand-in is refused for that path.  Its callers
are class drivers using kernel memory, which do not need one.  The
refusal is lifted once the completion path can do the copy itself.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
a4475ca284 drivers/usbhost: Announce what an xHCI port has attached.
Report each device as it comes up, and report it going away.

The announcement is made at the end of the port enable rather than at
connect, because the PORTSC speed field means nothing until the port has
been reset: a USB2 port reports its reset default, full speed, until then,
so every device would be announced at 12Mbps regardless of what it
negotiates a moment later.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
6b24a0cac5 drivers/usbhost: Carry the xHCI transfer chain across the ring join.
A transfer described by more than one TRB can reach the end of the ring
part way through, so the link that sends the controller back to the
beginning falls inside the transfer rather than between two of them.
Written without the chain bit, that link ends the transfer where it
stands: the controller follows it, considers the work finished, and
reports nothing, because the TRB that asked for the completion interrupt
is on the far side of the join.  Nothing waiting is woken, and transfers
have no timeout, so the symptom is a read that never returns.

Carry the chain bit onto the link when the TRB it follows has it.

Reading 1MiB from a USB drive, where the last two sizes did not complete
at all before:

    512 byte blocks     166 KB/s
    4 KiB blocks       1333 KB/s
    32 KiB blocks     10666 KB/s
    64 KiB blocks     15515 KB/s

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
432ef71ed7 drivers/usbhost: Describe devices to an xHCI controller correctly.
What a controller is told about a device before it will accept it.  A DWC3
core validates these where QEMU's controller does not.

- HCCPARAMS1 says whether context structures are 32 or 64 bytes, and the
  wider form was refused outright with -EIO; the EIC7700X reports
  0x0220fe45 on both of its controllers, so this driver could not have
  driven either.  A wide context is the same fields with reserved space
  after them, so only the stride changes.  Read it at start up and use it
  wherever a context array is walked.
- Contexts must be 64 byte aligned, since every device context base
  address array entry points at one, and the output context came from
  kmm_zalloc().
- The slot context never carried the device speed, which has no valid
  zero, so a validating controller answers Address Device with a parameter
  error.  The speed was already implied by the endpoint context's maximum
  packet size.  The numbering is xHCI's own, hence the mapping.
- The output device context was cleared and never flushed.  That context
  is the controller's to write, so what stays behind is a dirty line of
  zeros written back over the slot state, and the next command against the
  slot is refused with a context state error.  Enumeration reached
  SET_ADDRESS and stopped.
- A buffer copied through an aligned stand-in was copied back using buflen,
  which control transfers deliberately leave zero, so a descriptor read
  copied nothing back and the caller was handed whatever its buffer held
  before.  Keep the requested length separately, and maintain the cache
  over the whole stand-in rather than the part in use.
- A buffer the controller cannot reach is now copied through a stand-in
  rather than refused.  -EFAULT works for a caller with somewhere better
  to put the data, and fails outright for one without: reading a block
  device directly from a user program returned an error where the transfer
  could have gone through a stand-in.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
c288d9f9e4 drivers/usbhost: Compute the event ring segment count at full width.
The number of event ring segments a controller allows is a power of two
reported as its exponent, and the exponent can reach 15.  Computing
1 << exponent into the uint8_t that holds it wraps to zero on any
controller offering more than 128 segments, and a controller told its
event ring table holds no entries has nowhere to report anything: every
command times out.

Work it out at full width and narrow afterwards.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
ecbd1870d3 drivers/usbhost: Report which xHCI command was rejected.
A failed command logged only its completion code.  The difference between
a refused Address Device and a refused Evaluate Context is most of the
diagnosis, and the completion code does not give it.

Keep the command type before the result overwrites the TRB, and name it in
the message.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
cf93053f74 drivers/usbhost: Read xHCI HCIVERSION with an aligned access.
The register dump read HCIVERSION with a 32-bit access at offset two.  It
is a 16-bit register sharing a word with CAPLENGTH, so that is an
unaligned read of a device register: harmless where the bus permits it and
a fault where it does not.

Read the word once and take both fields from it.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
0ed5e61bcf drivers/usbhost: Maintain the cache over xHCI data buffers.
The controller moves every byte itself, so on a machine whose caches are
not coherent with it the driver must flush before the controller reads and
invalidate before the processor does.  Data buffers got no maintenance at
all: nothing pushed before an OUT, nothing dropped after an IN.

Cache operations act a whole line at a time, which is unsafe for a buffer
that does not own its lines: invalidating drops whatever else shares the
line, and a writeback lands on top of what the controller has just put
there.  Mass storage passes a 31 byte command block and a 13 byte status
out of its instance structure.  Such a buffer is copied through an aligned
stand-in; anything large comes from a filesystem or from xhci_ioalloc(),
which now rounds its length up as well as aligning its start, so what it
returns owns its last line.

Whether the controller can reach a buffer at all is asked of the platform
through a new dmacapable operation, since it is a property of the system
the controller was fitted into rather than of the controller.  A platform
that does not supply it is taken to accept every address, which is what
existing users have.  A refused buffer gives -EFAULT, which the FAT
filesystem answers by retrying through its own DMA-safe sector buffer.

The device output context is also invalidated before the assigned address
is read out of it; the controller wrote that address, and reading without
invalidating returns whatever the processor had cached.

Compiles to nothing where there is no cache to maintain, and dmacapable is
NULL on PCI, so the existing user is unaffected.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
9e1e3a272a drivers/usbhost: Chain xHCI TRBs across a 64K boundary.
A Normal TRB describes one run of memory that may not cross a 64K
boundary, and the block layer hands down whole multi-sector reads whose
length is bounded by nothing here.  One TRB was programmed regardless, so
a long enough transfer, or merely one starting near the wrong side of a
boundary, produced a descriptor the controller is entitled to reject or to
satisfy in part.

Program as many as the run needs, chained, asking for the completion
interrupt only on the last so one event still arrives for the transfer.
A transfer needing more TRBs than the ring holds is refused.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
cd30b682a4 drivers/usbhost: Flush the xHCI rings and structures by address.
xhci_ctrl_start() published the event ring segment table, the device
context base address array and the scratchpad pointers with
up_flush_dcache_all(), which an architecture whose cache can only be
maintained by address implements as a barrier and nothing more, so none of
them reached memory.  The controller then reads whatever those addresses
held before, which presents as every command timing out with no events
arriving.  Flush each structure by address.

xhci_ring_init() has the same fault from the other direction: it clears a
whole ring and flushes only the link entry it writes afterwards, leaving
the rest of the clearing in the cache.  The controller writes into that
memory itself, so a line written back later lands on top of an event
somebody is waiting for.  Flush the whole ring.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
8f88d73275 drivers/usbhost: Set the xHCI interrupter moderation interval.
The interval was left at its reset value of 4000, a millisecond, which is
how long the controller waits after an event before reporting it.  Every
completion paid that, and mass storage spends three transfers on a
request.

Set it to 160, which is 40us, as Linux does.  Zero puts no bound on how
often a controller may interrupt: a keyboard on an interrupt endpoint then
takes them continuously and occupies a processor.

Measured on a DWC3 with a USB 2.0 drive, doorbell to interrupt 986-1021us
before and 13-56us after:

    reading 1MiB          before        after
    512 byte blocks      166 KB/s     775 KB/s
    32 KiB blocks      10666 KB/s   18618 KB/s

    mounting a FAT32 volume: 92.7s before, 21.1s after

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
de440f910a drivers/usbhost: Silence the xHCI interrupter until its worker has run.
The handler read the status, queued the work that would answer it, and
returned with the source still asserted.  On a level triggered line the
interrupt controller sees the condition still true and raises it again at
once, so the work that would have cleared it never runs.

Mask the interrupter in the handler and let the worker unmask when it is
done.  The unmask clears the pending flag in the same write, because a
message is sent on that flag's clear to set transition and events that
arrived while the interrupter was masked have already set it.

Clearing opens its own window, so the worker drains the ring again after
unmasking and repeats while a drain finds anything; xhci_events_poll()
returns how many events it handled for that purpose.  A drain that finds
nothing is the only state in which no event can have been lost.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
f78decb390 drivers/usbhost: Acknowledge xHCI events before walking the ring.
The event ring was acknowledged after being walked.  An event arriving
during the walk sets the pending bit again, and clearing the bit
afterwards discards it.  Transfers have no timeout, so the transfer that
event belonged to waits forever.

Acknowledge first.  A spurious second pass over an empty ring costs
nothing.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
e03c23c4ae drivers/usbhost: Do not disable an xHCI port while probing it.
xhci_probe_ports() wrote PORTSC back to clear the change bits, including
PED, which is write-one-to-clear.  A port that came up enabled, which is
what a device attached at power up produces, was switched off by the act
of reading it.

Mask PED out of the value written back.  The port status worker already
does this.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Justin Hammond
648aa30e37 drivers/usbhost: Attach the xHCI interrupt after the controller starts.
The handler defers to a worker that walks the event ring, and the ring is
not allocated until the controller is started, several steps later.  A
controller left running by a boot loader has an interrupt pending as soon
as the line is enabled, so attaching earlier is a race with nothing able
to answer it.

Attach after the start, and clear USBSTS and the interrupter pending flag
once the handler is in place: a message signalled interrupt is sent on the
flag's clear to set transition, so a flag raised before the handler
existed would never produce another.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-09-20 22:28:07 +08:00
Daniel P. Carvalho
efec7da49f drivers/timers: Add TMRDEPPATH/TMRVPATH for PTP clock drivers.
Every other timer driver block in this Make.defs sets TMRDEPPATH and
TMRVPATH so DEPPATH/VPATH include this directory. CONFIG_PTP_CLOCK
and CONFIG_PTP_CLOCK_DUMMY were the only two missing it, leaving
ptp_clock.c/ptp_clock_dummy.c unreachable via VPATH and without a
generated dependency file when no other timer driver in this file is
also selected.

Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
(cherry picked from commit 6d3812229d1c527971bf3a2b9d7d2675796fc688)
2026-09-19 18:32:45 -03:00
Abhishek Mishra
b180dc17ae Documentation,drivers/aie: align machine learning docs with current code
The tflm tool registered DEPTHWISE_CONV_2D in nuttx-apps#3773, but the
docs still listed eight operators. Document the unused -C compile path,
that the sim helper uses heap I/O, and the pinned TFLM/CMSIS/NNABLA
versions. Add missing gemmlowp, KissFFT, Ruy, and FlatBuffers pages,
document the AI-engine character driver, and wire it into CMake.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-09-19 15:16:18 -03:00
raiden00pl
ce3dbf6539 drivers/serial/serial.c: fix nxstyle
drivers/serial/serial.c: fix nxstyle

Signed-off-by: raiden00pl <raiden00@railab.me>
2026-09-18 08:36:25 -03:00
raiden00pl
96c5330ea6 drivers/serial: bulk-copy raw output into the TX buffer
uart_writev() queues output one byte at a time via uart_putxmitchar().
Add uart_putxmitbuf() that memcpy()s a whole run into the TX ring buffer
and use it when no per-byte processing is needed (OPOST and ECHO clear,
not a console).  On a full buffer fall back to uart_putxmitchar(), which
keeps the blocking and error handling unchanged.

8 MiB write() to /dev/ttyACM0 on nRF52840: 455 -> 573 KB/s.
Guarded by CONFIG_SERIAL_TXBULK, default !DEFAULT_SMALL.

Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
2026-09-18 08:36:25 -03:00
Daniel P. Carvalho
1b172fb8d2 drivers/sensors: add Microchip TC74 temperature sensor driver
Add support for the Microchip TC74 digital temperature sensor using the
Sensor Driver Framework (uORB). The TC74 is an 8-bit I2C temperature
sensor with a measurement range from -40C to +125C and a resolution
of 1C.

The driver registers as a uORB topic (/dev/uorb/sensor_temp<n>) and
polls on the low-priority work queue. It supports dynamic interval
configuration and automatically enters low-power standby mode when
the topic is deactivated.

Validated against a real TC74A5-3.3 on a custom STM32H743BI board.

Assisted-by: Gemini:gemini-3.8-pro
Signed-off-by: Daniel P. Carvalho <danieloak@gmail.com>
2026-09-18 16:04:32 +08:00
Arnav Sharma
493031e7b1 drivers/mtd/mtd_config: fix UAF in mtdconfig_unregister_by_path
mtdconfig_unregister_by_path() opened the device with file_open(),
which runs mtdconfig_open() and therefore holds dev->lock for the
whole lifetime of the temporary file reference.  It then destroyed
the mutex and freed the private device structure while that
reference was still open, so the subsequent file_close() reached
mtdconfig_close(), which performs nxmutex_unlock() on freed memory.
Destroying a held mutex and unlocking it after free corrupt the heap;
on sim this crashes deterministically in the next allocation
(EXC_BAD_ACCESS in mm_malloc).  Both file_close() and
unregister_driver() return values were also discarded and the
function unconditionally returned OK, masking legitimate errors.

Reorder the teardown to close -> unregister -> destroy/free and
propagate errors, so that:

- file_close() (driver close callback and inode release) runs while
  the private device structure is still valid, releasing the
  exclusive access taken by mtdconfig_open(),
- the private structure is destroyed and freed only after
  unregister_driver() succeeds.  On failure the inode (and with it
  i_private) may still be referenced, so freeing would be wrong.
  Returning the error also honors the documented API contract
  (zero on success, negated errno on failure).

This matches the established close -> unregister -> teardown ordering
used by e.g. bchdev_unregister().

Verified with sim:configdata plus a register/unregister lifetime
exercise in examples/configdata: 934706/934706 checks pass with the
fix; with the fix stashed the same run dies with SIGSEGV right after
mtdconfig_unregister_by_path() returns.

Fixes: https://github.com/apache/nuttx/issues/20166
Signed-off-by: Arnav Sharma <2006arnavsharma@gmail.com>
2026-09-17 11:30:34 -03:00
arnavsharma990
0ccd15d166 drivers/sensors/sensor: cancel fetch watchdog on close to fix UAF
Some checks failed
Build Documentation / build-html (push) Has been cancelled
MemBrowse Memory Report / changes-filter (push) Has been cancelled
MemBrowse Memory Report / load-targets (push) Has been cancelled
MemBrowse Memory Report / identical (push) Has been cancelled
MemBrowse Memory Report / analyze (push) Has been cancelled
sensor_poll() arms a per-subscriber watchdog for fetch()-only sensors
with a requested interval. The watchdog handler sensor_fetch_expired()
dereferences the subscriber and re-arms itself unless user->fds is NULL.

sensor_poll() teardown clears user->fds and cancels the watchdog, but
sensor_close() removed the subscriber from the user list and freed it
without doing either. A close() racing an armed timer therefore lets
the handler run after the subscriber is freed, causing a timer-context
use-after-free and re-arm of a freed watchdog.

Mirror the poll teardown in sensor_close(): clear user->fds and cancel
user->wdog under upper->lock before notifying other users and freeing
the subscriber.

Fixes #20145.

Signed-off-by: arnavsharma990 <2006arnavsharma@gmail.com>
2026-09-15 09:41:11 +08:00
likun17
7a0e839ca4 drivers/sensors: fix nxstyle errors in sensor.c
Running `./tools/checkpatch.sh -g` on this file reported seven pre-existing
style errors.  Fix them so that the file passes nxstyle cleanly:

 - Add the missing blank line after the declarations in sensor_lock(),
   sensor_unlock(), sensor_update_interval() and sensor_generate_timing().
 - Indent the SNIOC_GET_EVENTS and SNIOC_FLUSH case labels with six spaces
   like the other twelve case labels of the same switch.
 - Drop the two extra spaces in front of the poll_notify() call in
   sensor_poll().

Whitespace only, no functional change: the file is byte identical once all
whitespace is stripped.

Signed-off-by: likun17 <likun17@xiaomi.com>
2026-09-13 10:29:08 +08:00
likun17
29a536f53e drivers/sensors: add resistance, conductivity, energy and charge types
Cover the remaining electrical quantities so that they do not have to fork
into driver private namespaces later.  Add SENSOR_TYPE_RESISTANCE (Ohm),
SENSOR_TYPE_CONDUCTIVITY (S/m), SENSOR_TYPE_ENERGY (J) and
SENSOR_TYPE_CHARGE (C), the last two matching the native unit of the
accumulator registers in power and energy monitors.

Signed-off-by: likun17 <likun17@xiaomi.com>
2026-09-13 10:29:08 +08:00
likun17
cd225a0e56 drivers/sensors: add voltage, current and power sensor types
uORB has no type for electrical quantities, so power monitors can only use
the legacy character drivers, which are deprecated and report their values
in three incompatible unit systems.  Add SENSOR_TYPE_VOLTAGE,
SENSOR_TYPE_CURRENT and SENSOR_TYPE_POWER with their message structs in SI
units (V, A, W).

Signed-off-by: likun17 <likun17@xiaomi.com>
2026-09-13 10:29:08 +08:00
raiden00pl
a3d4802ff8 drivers/serial: read the consumer index once in uart_recvchars()
Same issue as the previous commit, on the receive side: uart_read()
advances recv.tail from thread context without holding the critical
section, but the recvbuf batch path of uart_recvchars() reads recv.tail
several times (the full check, the watermark count and the free-space
computation).  If uart_read() moves and wraps the index in between, the
computed free space goes negative and is passed to recvbuf() as a huge
size_t, which lets the driver store past the end of the ring buffer.

Read recv.tail once per loop iteration and derive everything from that
snapshot.  The consumer only ever moves the index forward, so a stale
snapshot merely stores less now.

Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
2026-09-10 23:09:16 +08:00
raiden00pl
a6393fa320 drivers/serial: sample xmit.head once per iteration in uart_xmitchars()
uart_putxmitchar() advances xmit.head from thread context without
holding the critical section, so on SMP the head index can move, and
wrap around, while uart_xmitchars() runs in the TX interrupt on another
CPU.  Since commit b319c27f03 ("serial: Added APIs for receiving and
sending multiple chars") the sendbuf path of uart_xmitchars() reads
xmit.head twice: once to decide whether the pending data is contiguous
and again to compute its length.  If the producer wraps the index in
between, the computed length goes negative, is passed to sendbuf() as a
huge size_t and the driver transmits memory far beyond the ring buffer.

The per-byte path reads the index only once and is not affected, which
is why this went unnoticed: the batch path is only used by drivers that
implement sendbuf, and the 16550 driver gained it in commit 45c38d8592
("drivers/serial/16550: add polling mode support for serial drivers").
qemu-intel64 with SMP is the first configuration combining a sendbuf
driver with a producer running on another CPU.

On qemu-intel64 SMP this shows up as an endless stream of NUL bytes on
the console (captured with gdb: head = 1, tail = 8, size = 16, and
u16550_sendbuf() called with size = (size_t)-7), which makes the ntfc
test harness fail every test that runs while the flood lasts.

Read the head index once per loop iteration and use that snapshot for
both the contiguity test and the length.  The producer only ever moves
the index forward, so a stale snapshot merely sends less now.

Assisted-by: Claude Code
Signed-off-by: raiden00pl <raiden00@railab.me>
2026-09-10 23:09:16 +08:00
Michal Lenc
af2a8c6412 drivers/mtd/gd25.c: ensure the device is not in power down mode
Commit 2a7cf05 added support for QSPI control but removed functions
gd25_purdid (leave power down state) and gd25_pd (enter power down).
It's likely ok to avoid putting the device in power down state after
every operation, but we need to wake it up from the power down state
before first accessing it.

Without the fix the flashes used with NuttX prior to 2a7cf05 commit
don't work anymore as they are in power down state. The fix ensures
we wake from this state during the initialization.

Also fixes various coding style errors.

Signed-off-by: Michal Lenc <michallenc@seznam.cz>
2026-09-08 08:59:33 +08:00
ouyangxiangzhen
f1837981fa style: add missing blank line after declarations
Some checks are pending
Build Documentation / build-html (push) Waiting to run
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
Fix checkpatch "Missing blank line after declarations" errors in
drivers/timers/arch_timer.c and sched/sched/sched_processtickless.c.
These are pre-existing issues, not introduced by the recent tickless
RR series.

Assisted-by: Zhipu GLM-5.3
Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
2026-09-07 10:17:46 -03:00
ouyangxiangzhen
9ab05ec3f5 drivers/timers: fix UB and mask width in up_timer_getmask
The mask computation introduced by "fix infinite loop in
up_timer_getmask when maxticks == CLOCK_MAX" has two problems:

1. If maxticks == 0, flsx(0) expands to __builtin_clz(0), which is
   undefined behavior, and the shift count becomes 8 * sizeof(clock_t)
   = 64 for a 64-bit clock_t, which is undefined behavior as well.
   The loop-based code that was replaced kept *mask = 0 in this case.

2. CLOCK_MAX is INT64_MAX, i.e. 63 one bits, not a full-width bit
   pattern. The resulting mask is always one bit narrower than the
   one produced by the original loop; e.g. a 32-bit timer got
   0x7fffffff instead of 0xffffffff, so counter deltas >= 2^31 were
   truncated in the clock timekeeping code.

Fix this by keeping *mask = 0 when maxticks == 0 and by deriving the
mask from the full-width unsigned constant (uint64_t)-1, which
restores the all-ones semantics of the original loop and still covers
the maxticks == CLOCK_MAX case.

Also initialize maxticks in arch_timer.c: if the lower half does not
implement the maxtimeout ops, the value is left untouched and would
otherwise be read uninitialized.

Assisted-by: Zhipu GLM-5.3
Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
2026-09-07 10:17:46 -03:00
ouyangxiangzhen
72db205050 drivers/timers: fix infinite loop in up_timer_getmask when maxticks == CLOCK_MAX
When maxticks equals CLOCK_MAX (all bits set), the loop that builds
the mask by (*mask << 1) | 1 never terminates because the shifted
value wraps around to the same mask value, making next > maxticks
always false.
Replace the loop with a single flsx-based expression that computes
the mask directly, which naturally covers the CLOCK_MAX case.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
2026-09-07 10:17:46 -03:00