Commit graph

500 commits

Author SHA1 Message Date
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
Justin Hammond
82fb0d8d06 drivers/usbhost: Separate the xHCI driver from the PCI bus.
Nothing in the controller driver is PCI-specific beyond finding the
registers and the interrupt, so an SoC wiring an xHCI controller
directly could not use any of it.

usbhost_xhci.c is now the driver; usbhost_xhci_pci.c is the PCI
attachment, holding the ID table, the BAR mapping and the MSI-X vector.
include/nuttx/usb/xhci.h carries what passes between them: the register
base, a way to attach the interrupt, and a name to report the controller
by, since a system may have more than one.

The interrupt belongs to the bus: the bus attaches and detaches it, and
the driver never holds an interrupt number.

USBHOST_XHCI is the driver and is not selectable on its own;
USBHOST_XHCI_PCI selects it.  Another bus adds its own symbol beside it.

No functional change intended.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-18 09:35:23 -03:00
Justin Hammond
65e835c779 drivers/usbhost: Rename usbhost_xhci_pci.c to usbhost_xhci.c.
Pure rename, so that the history of the driver follows it into the file
that keeps the bulk of the code.  The PCI attachment is added back as a
new usbhost_xhci_pci.c in the commit that follows.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-18 09:35:23 -03:00
Justin Hammond
3b7e453012 drivers/usbhost: Take an xHCI command result from its completion event.
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
xhci_command() returned -ETIMEDOUT when a completion arrived without an
interrupt, although the fallback poll had already retrieved the event,
so callers unwound work the controller had completed.

Use the event's completion code whichever path retrieved it.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-17 10:10:47 -03:00
Justin Hammond
440d5f7f87 drivers/usbhost: Mask the write-one-to-clear PORTSC bits on reset.
Eight PORTSC bits are write-one-to-clear, so writing back a value just
read clears PED and every change bit that was set, disabling the port
being reset.  Mask them out using the new XHCI_PORTSC_RW1C.

The wait after reset also decided on its own counter rather than on the
port, reporting a timeout for a port that enabled on the last iteration.
Test PED, and report PORTSC when it does time out.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-17 10:10:47 -03:00
Justin Hammond
c94fbeebcb drivers/usbhost: Stop an xHCI controller only when it is running.
xhci_ctrl_halt() wrote USBCMD zero unconditionally and then waited for
HCH.  A controller that was never started is already halted, so the wait
ran to its full length, and clearing the whole register also dropped
INTE and HSEE.

Test HCH first, clear only R/S when it is set, bound the wait with
XHCI_HALT_TIMEOUT_MS, and report USBCMD and USBSTS on failure.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-17 10:10:47 -03:00
Justin Hammond
edb7d03467 drivers/usbhost: Allow an xHCI controller with no scratchpad buffers.
HCSPARAMS2 may report zero scratchpad buffers; QEMU's does.  The driver
sized the array from that count unconditionally and read the NULL from a
zero byte kmm_memalign() as -ENOMEM, so such a controller never started.

Skip the allocation when no_scratch is zero, leaving DCBAA[0] clear.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-17 10:10:47 -03:00
Justin Hammond
24132be56e drivers/usbhost: Read xHCI registers at the required access width.
xHCI requires aligned accesses of each register's own size, and a
controller may ignore narrower ones; QEMU's does.  volatile does not pin
the access width: GCC 16.1.0 at -Os narrows a 32-bit load feeding a
single bit test into a byte load, so polling USBSTS for HCH never
observes the halted state.

Launder each register value through a register with an empty asm, on
loads and stores both, so the access is the width the source specifies.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-17 10:10:47 -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
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
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
Justin Hammond
50f93a986a drivers/usbhost: Register the partitions on a mass storage device.
A drive that has been anywhere near another operating system almost
always carries a partition table rather than a filesystem starting at
sector zero, so the single block device this driver registers is usually
the one thing nobody can mount.  A USB stick written with an installer
image is a good example: sector zero holds a protective MBR, and what
somebody wants is the EFI system partition several gigabytes in.

Read the table and give each partition a block device of its own beside
the whole drive, named the way every other system names them.  The
parsing is already in the tree and understands both MBR and GPT; this
only calls it and registers what it finds.

The whole-drive node stays exactly where it was, for anyone who wants
the raw thing or whose drive really does hold a bare filesystem.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 14:40:52 -03:00
Jorge Guzman
f0a870baab drivers/usbhost: report the HID keyboard through the keyboard upper half
The driver kept a character device, a ring buffer, a poll waiter list
and an encoder of its own, in parallel with everything the keyboard
upper half already provides.  A USB keyboard was therefore the one
keyboard an application could not read like any other.

Register with keyboard_register() and report with keyboard_event(),
which removes the private character device and the four hundred lines
that served it.  Special keys are reported with the SPEC event types
carrying a keycode, so an application no longer has to guess whether a
value in the character range is a character or an arrow key.

HIDKBD_ENCODED and HIDKBD_NODEBOUNCE go away with the code they guarded.
Encoding is now inherent to the event, and the previous report is no
longer an optimisation:  a HID keyboard reports the keys that are down
rather than the transitions, so it is what tells a new press from a key
still held, and what tells that a key has been released.

Reporting the modifiers as keys is new, so it is behind
HIDKBD_REPORT_MODIFIERS and off by default.

Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
2026-07-31 11:02:20 -03:00
Ansh Rai
54b0066a69 libs/libc: Fix divide-by-zero in stat() with large filesystem block sizes
blksize_t is currently defined as int16_t, which overflows when a
filesystem reports a block size larger than 32767 bytes. This causes
st_blksize to become zero, leading to an integer divide-by-zero when
st_blocks is calculated in stat().

Widen blksize_t to int32_t to support larger filesystem block sizes.

Update nuttx_blksize_t in include/nuttx/fs/hostfs.h to keep it
consistent with include/sys/types.h.

struct geometry.geo_sectorsize (include/nuttx/fs/ioctl.h) is also
typed blksize_t, so every debug print of that field using a 16-bit
format specifier is updated to PRId32 to match the new width:
drivers/misc/ramdisk.c, drivers/mmcsd/mmcsd_spi.c, drivers/mtd/ftl.c,
fs/driver/fs_blockmerge.c, drivers/mtd/smart.c,
drivers/usbhost/usbhost_storage.c, drivers/mmcsd/mmcsd_sdio.c,
arch/arm/src/s32k1xx/s32k1xx_eeeprom.c,
arch/arm/src/lc823450/lc823450_mmcl.c.

Signed-off-by: Ansh Rai <anshrai331@gmail.com>
Signed-off-by: root <root@LAPTOP-9C7LKDC5.localdomain>
2026-07-16 23:37:22 +08:00
Catalin Visinescu
081e4c478a drivers/: Multiple Drivers Are Registered With World Writable - Part 2
Permissions (Part 2)

Description:

In kernel builds, any unprivileged process running on the NuttX
device can open /dev/efuse and attempt to read/write fuse content.
Reading the fuses may provide valuable information to an attacker
controlling the user process. The write operation, in extreme cases
where the fuse blocks are not locked, may brick the device.

DISCLAIMER: I tried to be strict with the settings, better to relax them
later if it's needed.

This is part of https://github.com/apache/nuttx/issues/19410

See https://github.com/apache/nuttx/issues/19410

Compiles ok.

Signed-off-by: Catalin Visinescu <catalin_visinescu@yahoo.com>
2026-07-15 15:27:28 +08:00
Lwazi Dube
2b0c59bcf4 drivers/usbhost_hidmouse: fix button detection in touchscreen example
Fix an issue where button presses were missed in the touchscreen
example due to incorrect packet processing.

Previously, the driver waited to accumulate a batch of packets but only
processed the first one, effectively discarding the rest. The driver
now reads and processes packets one at a time to ensure no input
events are lost.

Also fixed typo: usbhost_xythreshold does not exist.

Signed-off-by: Lwazi Dube <lwazeh@gmail.com>
2026-05-22 10:38:25 +08:00
daniellizewski
fe67544d5f drivers/usbhost/usbhost_cdcecm.c: Converted CDC-ECM to lower half
Converted USB Host CDC-ECM to a lower half driver.
Removed existing full network driver callbacks and replaced with lower
half.

Signed-off-by: daniellizewski <daniellizewski@geotab.com>
2026-05-06 06:20:03 +08:00
daniellizewski
9b18160893 drivers/usbhost/usbhost_cdcecm.c: Added support for Host CDC-ECM
Added support for USB host to use an USB CDC-ECM device.
This class is used for usb-ethernet adapters as well as many modems.

Signed-off-by: daniellizewski <daniellizewski@geotab.com>
2026-05-06 06:20:03 +08:00
daniellizewski
761c17f1e0 drivers/usbhost/usbhost_enumerate.c: Allow selecting USB configuration
Added support for selecting a different USB configuration.
Certain USB devices offer different classes using different
configurations. This allows a board file to provide a callback
to select the proper configuration for a given USB device.

Signed-off-by: daniellizewski <daniellizewski@geotab.com>
2026-05-06 06:20:03 +08:00
daniellizewski
1632beaede arch/arm/src/stm32h5/stm32_usbdrdhost.c: Fixed Hub support
Fixed a few bugs in the initial stm32_usbdrdhost.c implementation
when using a USB hub. Fixed fault when freeing control endpoints.
Fixed crash when disconnecting devices from hubs due ot use after free.

Signed-off-by: daniellizewski <daniellizewski@geotab.com>
2026-04-19 14:24:40 +08:00
Piyush Patle
0dccc8ba21 include/debug.h: Move to include/nuttx/debug.h
debug.h is a NuttX-specific, non-POSIX header. Placing it in the
top-level include/ directory creates naming conflicts with external
projects that define their own debug.h.
This commit moves the canonical header to include/nuttx/debug.h,
following the NuttX convention for non-POSIX/non-standard headers,
and updates all in-tree references.

A backward-compatibility shim is left at include/debug.h that
emits a deprecation #warning and re-includes <nuttx/debug.h>,
allowing out-of-tree code to continue building while migrating.

Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>
2026-04-07 07:50:06 -03:00
Peter Barada
efdbdfe409 arch/arm/stm32*: Use PRIx32 format specifier where appropriate
Replace 'x' printf format specifier with PRIx32 where
corresponding value is uint32_t type.

Signed-off-by Peter Barada <peter.barada@gmail.com>
2026-04-01 13:48:58 +08:00
Huang Qi
e3eeaefd6d style: Fix "the the" typo across the codebase.
Fix 269 occurrences of duplicate "the" word typo found in 209 files
across source code, header files, and configuration.

Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
2026-03-23 11:07:49 +01:00
zhanghongyu
a7567677a8 netdev_driver: add carrier_on to xxx_ifup where carrier_on is absent
since the judgment for network card selection was changed from IS_UP to
IS_RUNNING, drivers that lack carrier_on need to add the carrier_on
operation; otherwise, network access issues will occur.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
2026-02-02 13:23:05 +08:00
Alan Carvalho de Assis
0e5323370f drivers/usbhost_hidkbd: Fix small typo in usbhost_hidkbd.c
There were missing quotes for key "backspace" for scancodes.
This issue was found by github user @StagiaireAbritek

He opened an PR in github to fix it:
https://github.com/apache/nuttx/pull/15917

However he decided to abandon and close the PR, but because it is
an important fix I decided submit it upstream.

Signed-off-by: Alan C. Assis <acassis@gmail.com>
2026-01-18 11:57:58 -05:00
zhanghongyu
fa6c8579dd driver/net: replace net_lock with netdev_lock
modify the code of the adapted protocol stack to avoid deadlocks and the
logic that cannot be protected by locks after modification.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
2026-01-06 16:08:30 +08:00
yangsong8
a87ad98343 usbhost: cdcacm exit wq after copy all received data
Fix issue: When the SIM CDCACM host receives data, the second packet
data reception is incorrect.

Signed-off-by: yangsong8 <yangsong8@xiaomi.com>
2025-12-14 10:37:53 -03:00