Skip to content

msc host: no BOT reset recovery, example hang on submit failure, enumeration left unfinished on failure #3985

Open

Description

@hathach

MSC host gaps found while reviewing #3968 and #4009. All of them already exist on master, and neither PR makes them worse. They were found by reading the code and have not been reproduced on hardware.

1. No BOT reset recovery. tuh_msc_reset() is #if 0 (src/class/msc/msc_host.c:296). If a stage transfer fails to submit, msch_xfer_cb() hits a TU_ASSERT and the command never completes. #4009 fixes that: it completes the command with a synthesized PHASE_ERROR CSW, and after that tuh_msc_scsi_command() and tuh_msc_ready() refuse the interface until the device re-enumerates. Refusing is required: without it, the next CBW reaches a device that is still mid-transfer, and on a TinyUSB device a failed WRITE10 then receives that CBW as data. The missing piece is Bulk-Only Mass Storage Reset plus Clear Feature HALT on both bulk endpoints (BOT 1.0 §5.3.4), which would let the interface recover without re-enumerating.

2. msc_file_explorer hangs when a submit fails. examples/host/msc_file_explorer/src/msc_app.c:225-226, 242-243, 393-394 set _disk_busy and then ignore a false return from tuh_msc_read10/tuh_msc_write10. No completion ever arrives, so disk_wait (line 193) spins forever. The freertos variant already handles this (examples/host/msc_file_explorer_freertos/src/msc_app.c:455-458: clear _disk_busy and report the error). A non-PASSED CSW could also map to RES_ERROR.

3. A failed enumeration step never finishes set-config. In msc_host.c, the config_*_complete chain (lines 488-535) returns early through TU_ASSERT when a Request Sense or Read Capacity CSW is not PASSED (lines 513, 521) or when a re-issue fails. The TUR return in config_get_maxlun_complete is also ignored. On all of these paths usbh_driver_set_config_complete() never runs, and usbh keeps its single enumeration slot until the device is removed. It needs one explicit abort path for every failure.

4. A CBW that completes with an error hangs the command. This is point 1's sibling: the CBW was submitted, but its transfer completes with an error (a STALL, say) or a short length. msch_xfer_cb() checks that with TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t)) in MSC_STAGE_CMD (master src/class/msc/msc_host.c:337) and returns without completing the command, so the caller waits forever. #4009's completion and fencing cover only a failed submit. The recovery is the same as point 1's BOT reset.

5. The TUR / Request Sense retry during enumeration has no bound. config_test_unit_ready_complete() answers every failed Test Unit Ready with Request Sense, and config_request_sense_complete() issues TUR again (master msc_host.c:471-499, // TODO limit number of retries). A device that never becomes ready loops forever: usbh_driver_set_config_complete() never runs, and usbh keeps its enumeration slot, as in point 3. A bare retry count would also give up on slow devices that work today, so the bound should be time-based, or a count with a delay between tries, and then end the configuration unmounted.

Deferred from #3968, which is limited to 16-bit transfer-length wraps, and from #4009, which is limited to stage submit failures.

Activity

  1. hathach commented on Nov 10, 2026

    @hathach
    OwnerAuthor

    New evidence for points 1 and 4, found reviewing #4009 at 2b8f03adf. #4009 limits itself to stage submit failures, so all of this is outside it.

    Point 4 also strands enumeration. msc_host.c:362 returns false for a CBW that completes STALLED, FAILED or short. usbh.c:815 ignores xfer_cb's return value after clearing BUSY|CLAIMED (usbh.c:786), so complete_cb never runs. For an enumeration command (config_get_maxlun_complete -> Test Unit Ready, Request Sense, Read Capacity) that means config_abort() / usbh_driver_set_config_complete() is never reached and enumerating_daddr stays held until the device is removed; later attaches are deferred until then. BOT 1.0 §5.3.1 (p15): on a Bulk-Out STALL during command transport the host shall perform Reset Recovery.

    A new point: the completion path trusts the status stage.

    • MSC_STAGE_STATUS (msc_host.c:382-384) completes with epbuf->csw without checking event, the length (13), dCSWSignature or dCSWTag (BOT §6.3.1, p17).
    • epbuf->csw is not cleared per command (msc_host.c:170-175 rewrites only the CBW), so a CSW read that fails, or returns short, reports the previous command's CSW, usually PASSED.
    • A STALLED or FAILED data stage (msc_host.c:372-379) goes straight to the CSW read without clearing the halt. BOT §5.3.2 (p16) and Figure 2 / §5.3.3 require Clear Feature HALT and a CSW retry (a second failure means Reset Recovery). host: undefined endpoint state after STALL/FAILED, no way to clear a halt #4315 covers the missing halt-clear primitive.

    Consequences (source trace, not reproduced on hardware):

    • A READ CAPACITY data stage STALL during enumeration reads a stale PASSED CSW, so config_read_capacity_complete() (msc_host.c:557-574) mounts with the capacity left in enum_buf.
    • A failed READ10 reports PASSED while scsi_data holds garbage.
    • On a stalled bulk-IN endpoint every later IN command keeps STALLing.

    Remaining work for the same fix as point 1: validate event, length, signature and tag at the status stage; on a data-stage stall clear the halt and retry the CSW once, then Reset Recovery; on a CBW error go to Reset Recovery and complete the command (or config_abort() during enumeration).

    Verified against the BOT spec Rev 1.0 (§5.3.1, §5.3.2, §5.3.3, §6.3.1). Related: #4315 (halt clear), #4270 (completion contract), PR #4009.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Prio 📌follow-upDeferred work from a PR or investigation

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions