Skip to content

NUMICRO NUC126 USBv2: fixes for remote wakeup, endpoint halts, bus reset and data toggles - #458

Open
mdmower wants to merge 6 commits into
ChibiOS:chibios-21.11.xfrom
mdmower:nuc126-usb-fixes
Open

mdmower wants to merge 6 commits into
ChibiOS:chibios-21.11.xfrom
mdmower:nuc126-usb-fixes

Conversation

@mdmower

@mdmower mdmower commented Oct 24, 2026

Copy link
Copy Markdown

This is a follow-on to @doombadroid's #454: six fixes to the NUC126 USB driver (os/hal/ports/NUMICRO/LLD/USBv2). Each one has a practical effect. I run this driver in QMK on a Ducky One 3 RGB keyboard (NUC1261SG4AE; qmk/qmk_firmware#26499). Changes were directly tested where possible (2 & 3 come from spec reading) and adhere to the NUC126 Technical Reference Manual (Rev 1.04), Nuvoton's NUC126 BSP (OpenNuvoton/NUC126BSP) and the USB 2.0 specification.

Commits and associated testing:

  1. Turn the PHY on for remote wakeup. The PHY is off from SUSPEND, so RWAKEUP put no K state on the bus. usb_lld_wakeup_host also called _usb_wakeup() itself, which marked the driver active while the bus was still suspended.
    • Basis: BSP SampleCode/StdDriver/USBD_HID_Mouse2/main.c, "Enable PHY before sending Resume('K') state".
    • Test (Linux runtime suspend, then a key press): before, the host never resumed, and the keys were sent later, when the host resumed the device itself. After, a key press resumes the host and nothing is sent late.
  2. Reset the data toggle at ClearFeature(HALT). DSQSYNC was left as it was, so after a halt was cleared the endpoint could answer with DATA1, and the host would drop the packet as a repeat (USB 2.0 9.4.5).
    • Basis: BSP usbd.c, USBD_StandardRequest, which clears SSTALL and DSQSYNC together.
    • Test: QMK never halts an endpoint, so this path is reached only by a host that clears a halt. The keyboard was tested for regressions.
  3. Reset all endpoints at bus reset. usb_lld_reset set up EP0 only. The other hardware endpoints kept their configuration, data toggle, STALL and any transaction made ready before the reset, so the host could receive that data after it enumerated the device again (for example a key report from before a port reset).
    • Basis: BSP usbd.c, USBD_SwReset, which resets the data toggle of all eight endpoints. The STM32 LLDs also reset every endpoint in usb_lld_reset.
    • Test: the ready state is internal to the hardware and has no status bit, so it can't be observed. Typing and replugging were tested for regressions.
  4. Serve endpoint events before SETUP. The interrupt handler served a SETUP before the endpoint events of the same INTSTS read, although those events belong to the transfer before the SETUP. A status stage completion handled against the new request stalled it or ended it early. The commit also drops the endpoint events read together with a bus reset, which were served after _usb_reset had cleared the endpoint configurations.
    • Test: an image that waits 1 ms at the start of the USB interrupt handler (the latency a flash erase can cause) fails to enumerate (device descriptor read/64, error -32, device not accepting address). With this commit and the same 1 ms wait, it enumerates.
  5. Clear the endpoint STALL at initialization. A halt set by the host with SET_FEATURE stayed in place after SET_CONFIGURATION, which must reset the Halt feature (USB 2.0 9.4.5).
    • Test (usbfs: SET_FEATURE(ENDPOINT_HALT) on 0x81, SET_CONFIGURATION(1), GET_STATUS): before, the endpoint was still halted. After, it isn't.
  6. Drop repeated OUT packets. The hardware ACKs an OUT packet whose data toggle equals the last one accepted, and the driver passed it on as new data. A packet resent after a lost ACK was therefore received twice (USB 2.0 8.6.4). With QMK's raw HID, that runs a command twice.
    • Basis: BSP usbd.c, USBD_CtrlOut, and SampleCode/StdDriver/USBD_VCOM_and_HID_Keyboard/VCOM_and_HID_keyboard.c, which compare EPSTS with the toggle of the last OUT packet.
    • Test (raw HID OUT endpoint; the host data toggle is reset between two DATA0 packets with USBDEVFS_RESETEP): before, both packets were delivered, with EPSTS showing OUT DATA0 ACK for each. After, the repeat isn't delivered, and the next DATA1 packet is.

Apart from the endpoint mapping and register names, the NUC123 driver (LLD/USBv1) has the same code, so these faults likely apply there too. I have no NUC123 hardware, so I haven't changed it.

I made the review and these patches with Claude Code, an AI coding agent. The tests above ran on my keyboard.

The PHY is off from SUSPEND, so RWAKEUP put no K state on the bus and
the host never resumed. Turn it on first, as the vendor BSP does, and
let the RESUME bus event end the suspended state instead of the macro.
Clearing a halt left DSQSYNC as it was, so the endpoint could answer
with DATA1 and the host would drop the packet as a repeat. Clear it
with SSTALL, as the vendor BSP does.
usb_lld_reset set up EP0 only, so the other hardware endpoints kept
their configuration, data toggle, STALL and any transaction made ready
before the reset, and the host could receive that data after it
enumerated the device again. Clear the ready state, STALL and
configuration of every endpoint first. The vendor BSP also resets the
data toggle of all endpoints at a bus reset.
The interrupt handler served a SETUP before the endpoint events of the
same INTSTS read, although those events belong to the transfers before
the SETUP. A status stage completion handled against the new request
stalled it or ended it early. Serve the endpoint events first, clear
the control endpoint STALL at SETUP, and drop the endpoint events read
together with a bus reset.
usb_lld_init_endpoint left CFGP unchanged, so a halt set by the host
with SET_FEATURE stayed after SET_CONFIGURATION, which must reset the
Halt feature (USB 2.0 9.4.5). Write CLRRDY to CFGP of both hardware
endpoints first, which also clears SSTALL.
The hardware ACKs an OUT packet whose data toggle equals the last one
accepted, and the driver passed it on as new data, so a packet resent
after a lost ACK was received twice. Keep the toggle of the last
accepted packet per endpoint, read EPSTS on each OUT event, and re-arm
the endpoint without delivering a repeat (USB 2.0 8.6.4).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant