| Age | Commit message (Collapse) | Author |
|
The SIE command protocol (CmdCode + CCEMPTY/CDFULL handshake), the
slave-mode Ctrl/RxData/TxData window, the EpIntEn read-modify-writes, and
set_ep_size's ReEp/EP_RLZED handshake are all shared between thread-mode
API calls and dcd_int_handler, and none are reentrant: an ISR preempting a
thread-mode sequence consumes its handshake flags or, in set_ep_size's
case, a bus reset's DevIntClr = 0xFFFFFFFF eats the EP_RLZED flag the
spin waits on, hanging it forever. Guard them by masking only the USB IRQ
(nestable, ISR-safe; CMSIS NVIC_DisableIRQ already ends with DSB+ISB).
control_xact keeps the mask across its in_isr=true event push, since
osal_none skips queue locking for in_isr.
Hardening, not a fix for an observed failure: the ea4088 usbtest 30/30 +
HIL 14/14 results were reproduced with and without it. The windows are a
few instructions wide and most exposed on RTOS builds where class drivers
queue transfers from tasks concurrent with the USB IRQ.
|
|
EP0 control-OUT fix (usbtest 14/21, errno 110/-74): usbd queues the
status-stage OUT ZLP of every control read with buffer=NULL, so the ISR's
`if (out_buffer)` check missed it and marked the arriving ZLP as
out_received instead. The stale flag poisoned the next control-OUT with
data: its first chunk "completed" instantly from an empty EP0 buffer and
the host's real DATA NAKed forever. Track queued transfers with an
explicit out_queued flag and void half-finished control state on a new
SETUP.
Isochronous support (UM10562 12.15.6): 5-word DMA descriptors with
per-packet size memory, buflen/present_count in packets, one packet per
FRAME (no DMARSet/EpIntEn involvement), completion at EOT for both
directions. Details that matter:
- the iso machinery (5th DD word + packet-size memory) is compiled only
when an iso-capable class is enabled (CFG_TUD_AUDIO/VIDEO/VENDOR), so
non-iso builds pay nothing: _dcd stays 648 B vs 1032 B with iso
- ISR dispatch keys on the hardware's fixed ep-number/type map
(ep_id_is_iso), never on dd fields that thread mode rebuilds
- iso OUT honors Packet_valid (bit 16) and prefills the hardware
writeback slots with 0, so a missed frame counts as 0 bytes instead of
reading back stale buffer contents as data
- packet count is validated (tu_div_ceil <= ISO_MAX_PACKETS) before the
DD is touched, so an oversized transfer is refused without leaving a
serviceable half-built descriptor armed for the frame engine
- dcd_edpt_iso_alloc and iso_activate both enforce the fixed iso endpoint
numbers (3/6/9/12); classes ignore alloc's return value, so activate
must not trust it
Un-skip LPC40XX in the usbtest example; tier 4 now enumerates and passes
iso cases 15/16/22/23. cdc_msc_throughput and printer_to_cdc had bulk on
iso-only EP3 (SET_CONFIGURATION failed with -32); add the LPC17/40 EPNUM
block (bulk on EP2/EP5) like other fixed-EP examples.
Verified on ea4088_quickstart: usbtest tier-4 battery 30/30 repeatedly
and the full device HIL suite 14/14 (incl. audio_test iso).
|
|
and mimxrt1015
edpt_schedule_packets() advanced xfer->buffer past each armed EP0
chunk, so the OUT-complete handler invalidated the cache at the
ADVANCED pointer: one line past the received data. The CPU then read
stale cached bytes instead of the DMA'd packet, and the misplaced
invalidate discarded a dirty line of whatever variable follows the
buffer - random neighbor corruption on every control-OUT data stage.
Found by usbtest ctrl_out (cases 14/21) on espressif_p4_function_ev
with DMA enabled, the first DWC2 target combining buffer DMA with a
data cache: usbd control state wedged after the first control write
(every later request stalled), and one build layout panicked in the
usbd memcpy with a wild pointer.
Rework the EP0 chunk bookkeeping so xfer->buffer always points at the
un-consumed position: the arm no longer advances it; instead the EP0
re-arm paths advance past each completed (full) chunk, invalidating it
first on the OUT side. The final OUT completion invalidates exactly
the received bytes of its last chunk, taken from DOEPDMA ("incremented
on every AHB transaction", databook 7.1.83 - the same semantics the
SETUP path relies on) before dma_setup_prepare() re-targets it. EP0
chunking state (ep0_pending) is now also dropped on bus reset and on
a new SETUP, so a stale latched completion can no longer re-arm EP0
DMA from dead state. No behavior change for targets without dcache.
While root-causing, the FIFO layout was cross-checked against the
DWC2 databook/programming guide v4.20a: the existing GDFIFOCFG
programming (EPInfoBaseAddr = otg_dfifo_depth - 2*ep_count, one SPRAM
word per endpoint direction for buffer DMA) is conformant and needs
no change; the P4 HS instance's reset GDFIFOCFG (0x03800400) merely
reflects a scatter/gather-sized EP_LOC_CNT of 128 that buffer DMA
does not need.
With the fix in place, enable the usbtest battery on the espressif
fleet: tools/build.py allowlists device/usbtest (a plain IDF component
like board_test/video_capture) and both espressif boards' only-lists
gain device/usbtest. Also re-enable device/usbtest on mimxrt1015_evk:
its skip predated the dcd_ci_hs stale-ACTIVE-overlay fix (already on
this branch), which cured the battery that previously killed the
uPD720201 host controller twice (2026-07-11 ROM fw, 2026-07-13 case 27
on fw 2.0.2.6); rig-validated 30/30 three consecutive runs.
Validated on rig (all 30/30): espressif_p4_function_ev(-DMA) (was
22/30 under DMA), espressif_s3_devkitm(-DMA), stm32f723disco(-DMA),
mimxrt1015_evk; p4/s3 slave-mode unaffected (DMA-only code path);
compile-checked stm32h743nucleo +TUD DMA, stm32f407disco,
stm32l476disco (device ports currently on the dead hub).
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017TQZrFfU3K4Y198aLsUpBC
|
|
- dcd_edpt_stall flushes the primed buffer (ENDPTFLUSH), but the aborted
transfer's dQH overlay can be left ACTIVE with mid-transfer state; the
next prime after clear-halt then resumes the stale overlay instead of
loading the fresh qtd, so post-halt IN reads return mid-buffer data
(usbtest case 13 'buf[32] = 56 (not 0)', with case 18 failing
downstream of the same corruption in the full battery). qhd_start_xfer
now clears overlay.active alongside overlay.halted before linking the
new qtd.
- test/hil(hfp): drop lpcxpresso43s67's device/usbtest skip - the
historical first-case wedge no longer reproduces on this branch, and
with the overlay fix the board runs 30/30 on its Fresco xHCI host
(previously 28/30 with deterministic case 13/18 failures).
mimxrt1064_evk (imxrt dcache path) 30/30 regression-clean.
- docs(hil skill): document the external hifiphile rig - pool
test/hil/hfp.json, SSH-reachable from htpc/ci with no outbound SSH,
exercised by the CI hil-tinyusb (hfp.json) job; never run HIL against
it during development unless the user explicitly asks.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017TQZrFfU3K4Y198aLsUpBC
|
|
# Conflicts:
# .claude/skills/hil/SKILL.md
# test/hil/hil_test.py
|
|
- EP0 OUT: park a back-to-back data-stage packet the DCP accepted before
PID could go NAK and deliver it into the next armed chunk; flow-control
the single-buffer control pipe between chunks (usbtest ctrl_out
corruption); discard a packet parked while an OUT pipe was halted so
BOT reset recovery's fresh CBW read can't receive stale WRITE data
- HS UTMI PHY power-up per the FSP sequence, shared by dcd/hcd: CLKSEL
programmed from the board XTAL (EK-RA8M1 runs 20 MHz; the 24 MHz reset
default never locks) while DIRPD holds the PHY down, then timed release
- hw/bsp(ra8m1_ek): fix U60CK divider macro - BSP_CFG_U60CK_DIV used the
generic USB_CLOCK_DIV_8 encoding (7), which USB60CKDIVCR rejects,
leaving the USBHS link domain at 480 MHz; the USB60-specific
BSP_CLOCKS_USB60_CLOCK_DIV_8 (4) sticks and yields the required 60 MHz
from PLL1P
- support FS-only builds on the high-speed port: gate SYSCFG.HSE on
TUD_OPT_HIGH_SPEED (RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED was a
silent no-op) and always compile both hwfifo access widths - the FIFO
width belongs to the module, not the link speed (FS builds corrupted
odd-length tails: 16-bit access against MBW-32)
- iso activate: reset stale pipe bookkeeping so a BRDY firing before the
class re-arms can't replay a pre-SET_INTERFACE transfer; write PIPEBUF
after PIPESEL selects the pipe (PIPESEL-windowed register)
- clear-halt: re-assert BUF on a still-armed OUT pipe (usbtest case 29)
- bound the D0FIFO ready spin so an undrained double-buffered IN pipe
can't freeze the stack with the IRQ masked
- usbtest example: cap interrupt mps at 64 on RUSB2 high speed (pipes
6-9 have a fixed 64-byte buffer, RA6M5 UM 29.1)
Verified: usbtest 30/30 on ra6m5_ek (HS), ra4m1_ek (FS) and ra8m1_ek
(FS-forced build on the HS port).
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
The no-op activate left a transfer armed before SET_INTERFACE valid in
data.xfer, letting the ISR complete it against the old buffer. Drop the
descriptor and NAK the endpoint (mirrors the nrf5x fix).
Verified: usbtest 30/30 on ch32v103r, nanoch32v203, ch582m_evt.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
EPSKIP raises a transfer completion, so using it on the stall path let
the class re-arm the endpoint and Active+Stall never actually stalled
(usbtest case 13); write bare Active=0 instead, and retire skipped
transfers on endpoint reopen where the completion is wanted.
Verified: usbtest 30/30 on lpcxpresso11u37.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
nrf_clock_is_running() was added in nrfx 2.1.0 (MDK 8.30.2), but the
version autodetect puts 2.0.0 (MDK 8.29.0) in the v2 bucket, so
hfclk_running() fails to compile on that nrfx.
Route v2 through nrf_clock_hf_is_running(), which all of 2.x provides.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
Double-buffered iso, EP3 1023-byte packets on V20x/V30x (10-bit
R16_UEP3_T_LEN), CH583 and V103 enabled at 64 B.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
USBD drops tasks during EasyDMA without the 0x40027C1C latch (anomaly 199);
matches the nrfx reference driver.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
Clear Active before Stall so a queued endpoint actually halts (UM11126
41.8.1); use the sanctioned EPSKIP+wait sequence for stall/reopen/activate.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
An unpolled full iso-IN pipe keeps FRDY low forever and froze the stack
with IRQs masked; bound the spin and abort the FIFO access.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
Reserve the bank SIZE bucket once, re-enable per altsetting, and scrub the
bank-ready state so a stale armed bank cannot send before the class re-arms.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
max3421: prevent USB host hang at startup
|
|
* license: use SPDX identifiers for src/ headers
Replace the full ~20-line MIT license boilerplate on every src/ file with a
two-line SPDX tag (SPDX-FileCopyrightText + SPDX-License-Identifier), following
the REUSE convention used by CircuitPython and the Linux kernel. Removes ~3500
lines of duplicated boilerplate.
|
|
stabilization timing
|
|
On some boards the MAX3421E doesn't report its oscillator ready after a
soft reset (CHIPRES), so hcd_init() waits on USBIRQ_OSCOK forever and the
host hangs at startup before any USB device can be used. Bound the wait so
it proceeds after a timeout; it is a no-op where OSCOK arrives normally.
Needed on the Adafruit Feather ESP32 V2 (classic ESP32) + USB Host
FeatherWing, which otherwise hangs on essentially every cold boot.
Relates to adafruit/circuitpython#10053.
|
|
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
typec/stm32: fix UCPD sink bugs affecting compatibility with newer PD sources
|
|
When a Low-Speed device is connected through the internal FS PHY,
the effective PHY clock is 6MHz (HCFG_FSLS_PHYCLK_SEL_6MHZ),
but phy_clock was incorrectly left at 48. This caused HFIR to be
calculated as 47999 (~8ms SOF interval) instead of the correct 5999
(1ms SOF interval), breaking periodic endpoint scheduling.
Fixes: LS mouse only receiving first HID report on OTG_HS + FS PHY
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
dcd/stm32_fsdev: workaround CH32 EP0 premature OUT ACK
|
|
|
|
|
|
The BSP family and MCU option were named "ch58x"/"CH58X", but the supported part is
the CH583/CH582 (and the SDK repo is openwch/ch583); CH585 is a separate MCU family,
so the CH58x umbrella was misleading. Rename to the specific family:
- hw/bsp/ch58x -> hw/bsp/ch583 (dir), and the BSP-local files ch58x_it.* ->
ch583_it.*, system_ch58x.* -> system_ch583.* (include guards/refs updated). The
vendor SDK files (CH58x_common.h, CH58x_*.c in hw/mcu/wch/ch583) keep their names.
- OPT_MCU_CH58X -> OPT_MCU_CH583 in tusb_option.h, tusb_mcu.h, and the shared WCH
USBFS driver (ch32_usbfs_reg.h, dcd_ch32_usbfs.c). OPT_MCU_CH582 is kept as an
alias (same value), so either name selects the same code.
- FAMILY_MCUS CH58X -> CH583, CFG_TUSB_MCU=OPT_MCU_CH583, mcu:CH58X -> mcu:CH583 in
the example skip lists, the CI build matrix (ci_set_matrix.py), the get_deps family
tag, and docs/reference/boards.rst.
Board names (ch582m_evt, yd-ch582m) are unchanged. Verified: make + cmake build for
ch582m_evt, and ci.lan HIL (all device examples pass).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The OUT data-toggle check -- drop a packet whose DATA0/DATA1 doesn't match the
expected toggle (a host retransmit after a lost ACK, or a host that doesn't
alternate the toggle) -- only ran on CH58x. The auto-toggle parts (V103/V20x/V307/
X035) never checked it, so a duplicate/retransmitted OUT was processed twice.
HiFiPhile confirmed it: a host patched to send DATA0-only had CH32V305 accept every
packet. Move the TOG_OK gate out of the CH58x-only block so it runs on every variant;
the manual toggle flip stays CH58x-only. EP0 keeps its own toggle via the SETUP/status
flow and is exempt.
Verified on ci.lan HIL: ch582m_evt (CH58x), ch32v103r_r1_1v0 (V103), nanoch32v203
(V203) all pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
dcd_edpt0_status_complete() wrote the full SET_ADDRESS wValue into R8_USB_DEV_AD,
clobbering bit 7, which on CH58x is a user general-purpose flag (only bits [6:0]
are the device address). Mask to 7 bits and preserve bit 7, matching the removed
dcd_ch58x_usbfs.c. CH58x-scoped; other parts keep the full write.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The PID_SETUP handler armed the new control transfer but left any in-flight EP0
transfer from the previous request marked valid, so a spurious EP0 IN/OUT could run
update_in()/update_out() against stale state (the removed dcd_ch58x_usbfs.c invalidated
both EP0 directions on every SETUP). Clear xfer[0] IN/OUT validity when a SETUP arrives.
Applies to all WCH USBFS parts -- a new SETUP always supersedes a pending control xfer.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The USBFS SUSPEND interrupt fires on both the suspend and the resume edge, but the
handler unconditionally posted DCD_EVENT_SUSPEND. On CH58x tud_resume_cb() therefore
never ran, and a device that lowered clocks/power in tud_suspend_cb() was never told
to restore them. Read MIS_ST's suspend bit (1 while suspended, 0 once resumed) to emit
DCD_EVENT_RESUME on the wake edge -- what the removed dcd_ch58x_usbfs.c did. Scoped to
CH58x via #if; the CH32 parts keep their existing behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The manual-toggle ISR skipped EP0 entirely (if (ep != 0)), so EP0's RX data toggle
was set to DATA1 once at SETUP and never advanced. A control-OUT whose data stage
exceeds the EP0 packet size (a vendor/WebUSB OUT, a large HID SET_REPORT, or an
HS DFU download) desynced on the second packet and stalled.
EP0 has no hardware auto-toggle on CH58x (per the datasheet RB_UEP_AUTO_TOG applies
only to EP1/2/3/5/6/7), so flip its RX toggle on every OUT and always process the
packet -- restoring what the removed dcd_ch58x_usbfs.c did. The HIL examples keep
their control-OUT data stages within a single packet, so this was latent.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
dcd_edpt_xfer() re-enabled the USB interrupt before update_in() / ep_rx_set_response(),
which read-modify-write the (combined) EP control register. On CH58x the ISR RMWs that
same register to flip the manual data toggle, so a transfer interrupt landing mid-RMW
could drop the toggle flip and desync the endpoint. Move dcd_int_enable() to after the
arming so the whole sequence is atomic w.r.t. the ISR (matching the CH32X035 port #3703).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
Tidy the CH58x register/buffer layout the initial port left rough.
Register map (USBOTG_FS_TypeDef):
- Extend the struct to the full CH583/582 datasheet Table 17-2 map instead of
stopping at UEP567_MOD (0x0E) with the per-endpoint registers living only in
raw-address macros.
- Express the per-endpoint DMA/length/control registers as arrays of 4-byte
slots (ch58x_ep_dma_t / ch58x_ep_ctrl_t): EP0-3 DMA at 0x10, EP0-4 ctrl at
0x20, EP5-7 DMA/ctrl split to 0x54/0x64 (EP4 has no DMA register of its own;
it shares EP0's). TU_VERIFY_STATIC pins the slot sizes and block offsets, so
the EP_TX_LEN/EP_CTRL/EP_DMA macros walk each block by the 4-byte stride
(pointer arithmetic off slot 0, so the unused ternary branch can't trip
-Warray-bounds).
- Gate the two driver sites on CFG_TUSB_MCU == OPT_MCU_CH58X directly rather
than the CH32_USBFS_EP_REGS_CUSTOM alias, which was only ever defined in the
CH58x branch.
EP buffers (the data struct):
- Replace buffer[EP_MAX][2][64] on CH58x with named per-endpoint buffers: EP0/EP4
use the dedicated 192B ep0_ep4_buffer, so the old array left buffer[0]/buffer[4]
allocated-but-unused.
- Drop EP3's oversized iso buffer (out[64] + in[1023]); EP3 is bulk-only on CH58x,
so it uses a plain 128-byte buffer like the others. The data struct shrinks from
~2636 to 1292 bytes.
- Keep the now uniformly-64-byte buffers safe: dcd_edpt_iso_alloc()/iso_activate()
refuse isochronous on CH58x (no iso support; 8-bit T_LEN caps a packet at 255B),
and update_in()/update_out() additionally cap each packet copy to 64 bytes so a
class that ignores the iso-alloc result cannot run a memcpy past a buffer into a
neighbour's.
Non-CH58x parts (e.g. ch32v103) keep the struct-based macros, buffer[EP_MAX], and
the iso buffer unchanged. Verified on ch582m_evt HIL (ci.lan): all device examples
pass; ch32v103 build unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
fix(stm32_fsdev): don't enable the unused USB wakeup EXTI IRQ (F1/F3/G4/L1)
|
|
The classic-USB STM32 fsdev driver enabled the EXTI-line USB wakeup interrupt
(USBWakeUp_IRQn, and USBWakeUp_RMP_IRQn on the F3 remap path) in the NVIC, but
never uses it: resume is serviced in-band via ISTR.WKUP in the USB_LP/HP ISR,
and the driver never arms or clears that EXTI line. The wakeup EXTI interrupt is
only needed to wake the core from STOP mode, which TinyUSB does not implement.
Leaving its NVIC vector enabled lets it fire spuriously into an unhandled or
looping vector -- the freeze reported in #3696 on STM32G473.
USBWakeUp_IRQn is a valid, dedicated USB-wakeup-via-EXTI interrupt (e.g.
stm32g473xx.h: =42 "USB Wakeup through EXTI line"), not an "unrelated
interrupt"; it is simply unused here.
- Comment out USBWakeUp_IRQn for F1/F3/G4/L1 and USBWakeUp_RMP_IRQn on the F3
remap path, kept in place so STOP-mode wakeup is a one-line re-enable.
- Keep the STM32L1 USBWakeUp_IRQn -> USB_FS_WKUP_IRQn alias for that re-enable.
- Document the rationale in fsdev_stm32.h with a TODO.
- Comment out the matching USBWakeUp(_RMP)_IRQHandler in the F1/F3/G4 BSPs, and
the FreeRTOS NVIC_SetPriority(USBWakeUp_IRQn) on F1/G4.
Fixes #3696
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
Replace PR #3515's separate dcd_ch58x_usbfs.c / hcd_ch58x_usbfs.c with the
shared WCH USBFS device driver (combined per-endpoint control, like CH32V103),
adding two CH58x-specific behaviors guarded so CH32V103/V20x/V307 are unchanged:
- CH32_USBFS_EP_MANUAL_TOG: CH58x's hardware AUTO_TOG does not stay in sync, so
the ISR toggles DATA0/DATA1 manually and discards toggle-mismatched OUT
packets. Fixes multi-packet bulk-IN (e.g. MSC READ10) that otherwise hung.
- CH32_USBFS_EP4_SHARES_EP0: EP4 has no DMA register and overlays EP0's region
(EP0[0:63] + EP4 OUT[64:127] + EP4 IN[128:191]); add a 192-byte shared buffer
and buffer-pointer helpers (transparent for the other parts). Fixes
cdc_dual_ports (Port1 is on EP4).
Add the ch582m_evt board. Device only on USB0 (rhport 0): the shared
hcd_ch32_usbfs.c is CH32V20x-specific and cannot drive CH58x, so host / USB2
(rhport 1) is left commented out in the BSP for easy re-add.
Verified on ch582m_evt via local HIL: all device examples pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The CH32 workaround only restored EP0 to CONTROL in edpt_xfer(). When
the stack rejects a control write with data (handle_ctr_setup having
already switched EP0 to BULK) it stalls EP0 via dcd_edpt_stall() without
ever calling dcd_edpt_xfer(), leaving EP0 typed BULK+STALL. As SETUP
recognition is tied to CONTROL-typed endpoints, the host's recovery
SETUP (which should auto-clear the stall) would be ignored, wedging EP0
until a bus reset.
Restore CONTROL for EP0 in the same exclusive write that sets the STALL
status. DFU download-in-wrong-state is a concrete trigger.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
edpt_xfer() restored EP0 to CONTROL at the top of the function, before
the OUT stage was armed (rx bufsize + STAT_RX=VALID written further
down, with interrupts enabled in between). That re-enabled the CH32
blind OUT ACK while STAT_RX was still NAK and the buffer size stale, so
a host-retried DATA OUT / status ZLP could be ACKed into the wrong
buffer in the gap.
Fold the CONTROL restore into the single exclusive write that programs
STAT_RX=VALID for the OUT direction, after the buffer size is set, so
type and arming go live together. The IN direction keeps the (now
atomic) early restore, where no pending OUT exists to be blind-ACKed.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
Parse the setup packet via tusb_control_request_t instead of hand-rolled byte
indexing; drop a stray blank line and fix indentation. No functional change.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
ep0_set_type() read the EP0 register outside any critical section and
only ep_write() masked the USB IRQ around the store. A USB interrupt
landing between the read and the write (e.g. a new SETUP whose handler
installs the BULK gate) was silently undone when the task resumed and
wrote back its pre-interrupt snapshot with the type forced to CONTROL,
re-exposing the unsolicited EP0 OUT ACK the workaround blocks.
Bracket the whole read-modify-write with fsdev_int_disable/enable when
called with need_exclusive (task context). ISR-context callers pass
false and are unaffected (the ISR cannot preempt itself).
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
|
|
CH32V103 uses the older USBFS IP: a single combined UEPn_CTRL register per
endpoint (IN response in bits [1:0], OUT response in [3:2], shared auto-toggle,
separate IN/OUT toggles) instead of the separate UEPn_TX_CTRL/UEPn_RX_CTRL
bytes of the newer IP (CH32V20x/V307). The shared driver was written for the
newer IP, so EP0 control transfers never worked on V103: the OUT response was
written to a reserved byte and the IN write clobbered the OUT bits.
- ch32_usbfs_reg.h: annotate the V103 register struct with byte offsets and add
a union exposing the combined UEPn_CTRL at the UEPn_TX_CTRL offset; define
CH32_USBFS_EP_CTRL_COMBINED and the combined-register bit positions.
- dcd_ch32_usbfs.c: abstract EP control access behind ep_tx/rx_ctrl_set() (full
write) and ep_tx/rx_set_response() (response-only RMW). The newer-IP path is
unchanged; the combined path read-modify-writes the single register and arms
the post-SETUP data stage at DATA1.
- bsp/ch32v10x: implement board_get_unique_id() (real chip UID) and drop the
CSR 0x800 writes that corrupted the QingKe V3 interrupt config and left all
interrupts disabled (the USB ISR never ran).
Verified on ch32v103r_r1_1v0: enumerates and passes HIL for cdc_msc, hid,
msc, midi, mtp, dfu, etc.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|