Skip to content

Tools: keep Renode OTG EP0 setup data until the data stage is armed - #34934

Open
qamar-gmail wants to merge 1 commit into
ArduPilot:masterfrom
qamar-gmail:renode-otg-ep0-setup-data-stage
Open

qamar-gmail wants to merge 1 commit into
ArduPilot:masterfrom
qamar-gmail:renode-otg-ep0-setup-data-stage

Conversation

@qamar-gmail

Copy link
Copy Markdown

Summary

Fix the Renode STM32 OTG model so a control-OUT data stage (for example CDC SET_LINE_CODING) is not completed with a spurious zero-length packet. Before this fix, SET_LINE_CODING over USB/IP was acknowledged but never reached the firmware.

Classification & Testing (check all that apply and add your own)

  • Checked by a human programmer
  • Non-functional change
  • No-binary change
  • Infrastructure change (e.g. unit tests, helper scripts)
  • Automated test(s) verify changes (e.g. unit test, autotest)
  • Tested manually, description below (e.g. SITL)
  • Tested on hardware
  • Logs attached
  • Logs available on request

Tested in Renode with an unmodified official firmware:

  • Board/firmware: CubeOrangePlus, ArduPlane V4.8.0-dev (7246115b), ELF SHA-256 98c099b06bcc25208b322932199f92dba69bf94d767e36aad7babe8abab61375
  • Renode: 1.16.1+20261016gitfbea4f40 portable, as published under firmware.ardupilot.org/Tools/Renode
  • ArduPilot tree: 6743a33579. AP_STM32_OTG.cs there is identical to current master (blob b57d1a3)
  • Launch: Tools/renode/run.py CubeOrangePlus --vehicle arduplane --usb --usbip-port N, inside a private user/network namespace, with logLevel 0 sysbus.usbOtg
  • Host side: Tools/renode/tests/usb_debug_transport.USB (userspace USB/IP, no VHCI). After an initialized heartbeat on the CDC MAVLink interface it sends, on interface 0, GET_LINE_CODING, then SET_LINE_CODING 9600 8N1, then GET_LINE_CODING
GET before SET 9600 GET after
before fix 00960000000008 (38400) ACKed empty or URB timeout; device answers 00-96-00-00-00-00-08 (unchanged)
with fix 00960000000008 (38400) ACKed 80250000000008 (9600)

No firmware or ChibiOS change is involved.

Description

Symptom. USB enumeration, the CDC MAVLink heartbeat and parameter traffic work over USB/IP. A SET_LINE_CODING followed by GET_LINE_CODING does not round-trip on either CDC interface. The GET returns empty data, times out, or returns the old coding.

Root cause. HandleSetupPacket stores the control-OUT payload in setupAdditionalData and queues the SETUP. At that point firmware still has EP0 OUT enabled for SETUP reception with DOEPTSIZ.XFRSIZ = 0. When TryDeliverOut(0) runs, DeliverOutData computes remaining = 0 and therefore length = 0. It treats that as a complete transfer, disables the endpoint and queues a zero-byte OUT-data RX status with XFRC. ChibiOS sees the data stage complete with no data and sends the status-stage IN ZLP, so the host's URB completes. The real 7 bytes are delivered only when firmware next re-arms EP0 OUT, by which point they are no longer part of the request.

usbOtg debug log for SET_LINE_CODING before the fix:

Firmware consumed RX status 0x000C0080 (8 bytes)   <- SETUP
Firmware consumed RX status 0x00040000 (0 bytes)   <- spurious zero-length OUT data
USB OUT endpoint 0 interrupt 0x8                   <- STUP
USB OUT endpoint 0 interrupt 0x1                   <- XFRC on the empty packet
USB IN endpoint 0:                                 <- status ZLP, host URB completes
Firmware consumed RX status 0x00040070 (7 bytes)   <- the real line coding, too late
USB OUT endpoint 0 interrupt 0x1

With the fix:

Firmware consumed RX status 0x000C0080 (8 bytes)   <- SETUP
USB OUT endpoint 0 interrupt 0x8                   <- STUP
Firmware consumed RX status 0x00040070 (7 bytes)   <- data stage after firmware arms 7 bytes
USB OUT endpoint 0 interrupt 0x1                   <- XFRC
USB IN endpoint 0:                                 <- status ZLP

Fix. In DeliverOutData, when the endpoint has no transfer space left (remaining == 0) but host data is still pending, return without queuing anything. The existing EPENA handler in WriteEndpoint already calls TryDeliverOut with Transferred = 0 once firmware arms the data-stage transfer. Genuine zero-length host packets (data.Length == 0) still go through unchanged, because the guard requires offset < data.Length. The guard also applies to non-zero endpoints. There, data is likewise only meaningful once firmware has armed a transfer, and holding it in activeOut/pendingOut matches the existing flow-control intent described in the comment beside state.Enabled = false.

This was found while qualifying production USB CDC behaviour with unmodified firmware in a Renode-based lab. A standalone reproducer script, built on usb_debug_transport.py, is available on request.

This change and the description were prepared with AI assistance (Claude Code) and reproduced on the setup above. The "Checked by a human programmer" box is deliberately left for the reviewer and submitter.

🤖 Generated with Claude Code

When EP0 OUT is armed only for SETUP reception (transfer size 0),
DeliverOutData queued a zero-length OUT packet ahead of a control-OUT
data stage. ChibiOS completed the stage with no data and sent the status
ZLP, so the real bytes arrived late and were discarded. CDC
SET_LINE_CODING over USB/IP was acknowledged but never applied.

Leave pending data queued until firmware arms a non-zero transfer;
the existing EPENA path then delivers it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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