Repository navigation
Description
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
hathach commentedon Nov 10, 2026
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:362returns false for a CBW that completes STALLED, FAILED or short.usbh.c:815ignoresxfer_cb's return value after clearing BUSY|CLAIMED (usbh.c:786), socomplete_cbnever runs. For an enumeration command (config_get_maxlun_complete-> Test Unit Ready, Request Sense, Read Capacity) that meansconfig_abort()/usbh_driver_set_config_complete()is never reached andenumerating_daddrstays 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 withepbuf->cswwithout checkingevent, the length (13),dCSWSignatureordCSWTag(BOT §6.3.1, p17).epbuf->cswis not cleared per command (msc_host.c:170-175rewrites only the CBW), so a CSW read that fails, or returns short, reports the previous command's CSW, usually PASSED.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):
config_read_capacity_complete()(msc_host.c:557-574) mounts with the capacity left inenum_buf.scsi_dataholds garbage.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.