mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2026-10-12 08:54:55 -05:00
Fix save-to-USB fails after drive hotplug or replug
Validate the cached MSD and rescan if stale, detect a replug via CCS+PED on xHCI, retry msd_init once via port-reset re-enumeration, clear unit attention with REQUEST SENSE
This commit is contained in:
1 parent
e18aa349ff
commit
269fbcceea
6 files changed
+88
-11
No files matched your search
@@ -405,9 +405,12 @@ static int slog_pct(int ticks, int total)
|
||||
|
||||
void save_results_to_usb(void)
|
||||
{
|
||||
bool reenumerated = false;
|
||||
|
||||
// The drive may have been plugged in after boot - scan for it now. This must be
|
||||
// done before the heap mark below is recorded, so that anything allocated for a
|
||||
// newly found drive is not freed when the save completes.
|
||||
restart:
|
||||
if (!usb_mass_storage_found) {
|
||||
prints(POP_R+14, POP_LI, "Scanning for USB drive... ");
|
||||
(void)usb_scan_for_msd();
|
||||
@@ -429,6 +432,12 @@ void save_results_to_usb(void)
|
||||
prints(POP_R+14, POP_LI, "Initializing USB drive... ");
|
||||
|
||||
if (!msd_init(&msd)) {
|
||||
// A port reset + re-enumeration is the last-resort recovery for a wedged drive, so retry once.
|
||||
if (!reenumerated && msd.hcd->methods->scan_for_msd != NULL) {
|
||||
reenumerated = true;
|
||||
usb_forget_msd();
|
||||
goto restart;
|
||||
}
|
||||
prints(POP_R+14, POP_LI, "USB drive init failed. ");
|
||||
usleep(2000 * MILLISEC);
|
||||
goto cleanup;
|
||||
|
||||
+10
-2
@@ -238,6 +238,7 @@ typedef struct {
|
||||
// State needed to rescan the root ports after initialisation.
|
||||
int num_hs_devices;
|
||||
uint8_t num_ports;
|
||||
uint8_t msd_port; // 1-based root port owning the found drive, 0 if none
|
||||
bool i_have_companions;
|
||||
bool port_in_use[EHCI_MAX_PORTS];
|
||||
} workspace_t __attribute__ ((aligned (256)));
|
||||
@@ -626,9 +627,11 @@ static bool scan_for_msd(const usb_hcd_t *hcd)
|
||||
// Check the port is powered up.
|
||||
if (~port_status & EHCI_PORT_SC_PP) continue;
|
||||
|
||||
// Skip ports owned by a device found during a previous scan, unless it was unplugged.
|
||||
// Skip ports owned by a previous scan, unless unplugged or hosting a since-forgotten drive.
|
||||
if (ws->port_in_use[port_idx]) {
|
||||
if ((port_status & (EHCI_PORT_SC_CCS | EHCI_PORT_SC_PED)) == (EHCI_PORT_SC_CCS | EHCI_PORT_SC_PED)) {
|
||||
if (ws->msd_port == 1 + port_idx) {
|
||||
ws->msd_port = 0;
|
||||
} else if ((port_status & (EHCI_PORT_SC_CCS | EHCI_PORT_SC_PED)) == (EHCI_PORT_SC_CCS | EHCI_PORT_SC_PED)) {
|
||||
continue;
|
||||
}
|
||||
ws->port_in_use[port_idx] = false;
|
||||
@@ -668,6 +671,7 @@ static bool scan_for_msd(const usb_hcd_t *hcd)
|
||||
if (find_attached_usb_keyboards(hcd, &root_hub, 1 + port_idx, USB_SPEED_HIGH, ws->num_hs_devices,
|
||||
&ws->num_hs_devices, keyboards, 0, &num_keyboards)) {
|
||||
ws->port_in_use[port_idx] = true;
|
||||
ws->msd_port = 1 + port_idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -877,9 +881,13 @@ bool ehci_probe(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
num_hs_devices++;
|
||||
|
||||
// Look for keyboards and USB drives attached directly or indirectly to this port.
|
||||
bool port_msd_before = usb_mass_storage_found;
|
||||
if (find_attached_usb_keyboards(hcd, &root_hub, 1 + port_idx, USB_SPEED_HIGH, num_hs_devices,
|
||||
&num_hs_devices, keyboards, MAX_KEYBOARDS, &num_keyboards)) {
|
||||
ws->port_in_use[port_idx] = true;
|
||||
if (usb_mass_storage_found && !port_msd_before) {
|
||||
ws->msd_port = 1 + port_idx;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+18
-1
@@ -1284,10 +1284,27 @@ bool usb_hcd_available(void)
|
||||
return num_hcd > 0;
|
||||
}
|
||||
|
||||
void usb_forget_msd(void)
|
||||
{
|
||||
usb_mass_storage_found = false;
|
||||
usb_msd_hcd_idx = -1;
|
||||
usb_msd_name[0] = '\0';
|
||||
}
|
||||
|
||||
bool usb_scan_for_msd(void)
|
||||
{
|
||||
if (usb_mass_storage_found) {
|
||||
return true;
|
||||
// An unplug or replug makes the stored device state stale, so check it still responds.
|
||||
const usb_hcd_t *hcd = &hcd_list[usb_msd_hcd_idx];
|
||||
usb_setup_pkt_t setup_pkt;
|
||||
build_setup_packet(&setup_pkt, USB_REQ_FROM_DEVICE, USB_GET_DESCRIPTOR,
|
||||
USB_DESC_DEVICE << 8, 0, sizeof(usb_device_desc_t));
|
||||
if (hcd->methods->get_data_request(hcd, &usb_msd_info.ep0, &setup_pkt,
|
||||
hcd->ws->data_buffer, sizeof(usb_device_desc_t))
|
||||
&& valid_usb_device_descriptor(hcd->ws->data_buffer)) {
|
||||
return true;
|
||||
}
|
||||
usb_forget_msd();
|
||||
}
|
||||
usb_runtime_scan = true;
|
||||
for (int i = 0; i < num_hcd && !usb_mass_storage_found; i++) {
|
||||
|
||||
@@ -373,6 +373,12 @@ extern bool usb_mass_storage_found;
|
||||
*/
|
||||
extern char usb_msd_name[64];
|
||||
|
||||
/**
|
||||
* Discards the stored mass storage device so the next usb_scan_for_msd() call
|
||||
* performs a full re-enumeration. Used to recover a wedged drive by port reset.
|
||||
*/
|
||||
void usb_forget_msd(void);
|
||||
|
||||
/**
|
||||
* Returns the mass storage device discovered during the initial USB scan.
|
||||
* If found, populates msd and returns true.
|
||||
|
||||
+33
-6
@@ -65,6 +65,13 @@ typedef struct __attribute__((packed)) {
|
||||
uint8_t status;
|
||||
} usb_csw_t;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Set when the device processed the last command but rejected it (CHECK CONDITION).
|
||||
static bool cmd_rejected = false;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -109,6 +116,8 @@ static bool msd_bot_command(usb_msd_t *msd, const uint8_t *cdb, int cdb_len,
|
||||
{
|
||||
const usb_hcd_t *hcd = msd->hcd;
|
||||
|
||||
cmd_rejected = false;
|
||||
|
||||
// Build Command Block Wrapper. cb[] not in the initializer is zero-padded.
|
||||
usb_cbw_t cbw = {
|
||||
.signature = CBW_SIGNATURE,
|
||||
@@ -153,7 +162,22 @@ static bool msd_bot_command(usb_msd_t *msd, const uint8_t *cdb, int cdb_len,
|
||||
return false;
|
||||
}
|
||||
|
||||
return data_ok && csw.status == CSW_STATUS_PASSED;
|
||||
if (data_ok && csw.status == CSW_STATUS_PASSED) {
|
||||
return true;
|
||||
}
|
||||
cmd_rejected = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Discard pending sense data: a post-reset unit attention fails every command until collected.
|
||||
static void msd_request_sense(usb_msd_t *msd)
|
||||
{
|
||||
if (!cmd_rejected) {
|
||||
return;
|
||||
}
|
||||
uint8_t cdb[6] = { SCSI_REQUEST_SENSE, 0, 0, 0, 18, 0 };
|
||||
uint8_t sense_data[18];
|
||||
(void)msd_bot_command(msd, cdb, 6, sense_data, sizeof(sense_data), true);
|
||||
}
|
||||
|
||||
// Retry transient bus errors. The command is reissued whole, so a partial op just repeats.
|
||||
@@ -164,6 +188,7 @@ static bool msd_bot_command_retry(usb_msd_t *msd, const uint8_t *cdb, int cdb_le
|
||||
if (msd_bot_command(msd, cdb, cdb_len, data, data_len, data_in)) {
|
||||
return true;
|
||||
}
|
||||
msd_request_sense(msd);
|
||||
usleep(20 * MILLISEC);
|
||||
}
|
||||
return false;
|
||||
@@ -211,13 +236,15 @@ bool msd_init(usb_msd_t *msd)
|
||||
|
||||
// TEST UNIT READY — retry a few times since the device may need time to spin up.
|
||||
uint8_t cdb_tur[6] = { SCSI_TEST_UNIT_READY };
|
||||
for (int retry = 0; retry < 5; retry++) {
|
||||
if (msd_bot_command(msd, cdb_tur, 6, NULL, 0, false)) {
|
||||
break;
|
||||
bool ready = false;
|
||||
for (int retry = 0; retry < 5 && !ready; retry++) {
|
||||
ready = msd_bot_command(msd, cdb_tur, 6, NULL, 0, false);
|
||||
if (!ready) {
|
||||
msd_request_sense(msd);
|
||||
usleep(500 * MILLISEC);
|
||||
}
|
||||
usleep(500 * MILLISEC);
|
||||
if (retry == 4) return false;
|
||||
}
|
||||
if (!ready) return false;
|
||||
|
||||
// READ CAPACITY (10) — returns 8 bytes: last LBA (4 bytes BE) + block size (4 bytes BE).
|
||||
uint8_t cdb_cap[10] = { SCSI_READ_CAPACITY_10 };
|
||||
|
||||
+12
-2
@@ -371,6 +371,7 @@ typedef struct {
|
||||
|
||||
// State needed to rescan the root ports after initialisation.
|
||||
uint8_t num_ports;
|
||||
uint8_t msd_port; // 1-based root port owning the found drive, 0 if none
|
||||
uint8_t port_type[XHCI_MAX_PORTS];
|
||||
} workspace_t __attribute__ ((aligned (64)));
|
||||
|
||||
@@ -1155,9 +1156,13 @@ static bool scan_for_msd(const usb_hcd_t *hcd)
|
||||
|
||||
uint32_t port_status = read32(&op_regs->port_regs[port_idx].sc);
|
||||
|
||||
// Skip ports owned by a device found during a previous scan, unless it was unplugged.
|
||||
// Skip ports owned by a previous scan, unless unplugged/replugged or hosting a since-forgotten drive
|
||||
if (ws->port_type[port_idx] & PORT_TYPE_IN_USE) {
|
||||
if (port_status & XHCI_PORT_SC_CCS) continue;
|
||||
if (ws->msd_port == 1 + port_idx) {
|
||||
ws->msd_port = 0;
|
||||
} else if ((port_status & (XHCI_PORT_SC_CCS | XHCI_PORT_SC_PED)) == (XHCI_PORT_SC_CCS | XHCI_PORT_SC_PED)) {
|
||||
continue;
|
||||
}
|
||||
ws->port_type[port_idx] &= ~PORT_TYPE_IN_USE;
|
||||
}
|
||||
|
||||
@@ -1190,6 +1195,7 @@ static bool scan_for_msd(const usb_hcd_t *hcd)
|
||||
if (find_attached_usb_keyboards(hcd, &root_hub, 1 + port_idx, device_speed, slot_id,
|
||||
&num_devices, keyboards, 0, &num_keyboards)) {
|
||||
ws->port_type[port_idx] |= PORT_TYPE_IN_USE;
|
||||
ws->msd_port = 1 + port_idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1492,9 +1498,13 @@ bool xhci_probe(uintptr_t base_addr, usb_hcd_t *hcd)
|
||||
if (slot_id == 0) break;
|
||||
|
||||
// Look for keyboards attached directly or indirectly to this port.
|
||||
bool port_msd_before = usb_mass_storage_found;
|
||||
if (find_attached_usb_keyboards(hcd, &root_hub, 1 + port_idx, device_speed, slot_id,
|
||||
&num_devices, keyboards, MAX_KEYBOARDS, &num_keyboards)) {
|
||||
ws->port_type[port_idx] |= PORT_TYPE_IN_USE;
|
||||
if (usb_mass_storage_found && !port_msd_before) {
|
||||
ws->msd_port = 1 + port_idx;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user