Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve USB MSC initiator support #496

Merged
merged 6 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ SECTIONS
/* Put only non-timecritical code in flash
* This includes e.g. floating point math routines.
*/
.pio/build/$project_name/src/ZuluSCSI_initiator.cpp.o(.text .text*)
.pio/build/$project_name/src/ZuluSCSI_msc_initiator.cpp.o(.text .text*)
.pio/build/$project_name/src/ZuluSCSI_log.cpp.o(.text .text*)
.pio/build/$project_name/src/ZuluSCSI_log_trace.cpp.o(.text .text*)
.pio/build/$project_name/src/ZuluSCSI_settings.cpp.o(.text .text*)
Expand Down
26 changes: 20 additions & 6 deletions lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,27 @@ uint32_t scsiHostRead(uint8_t *data, uint32_t count)
{
for (uint32_t i = 0; i < count; i++)
{
while (!SCSI_IN(REQ))
uint32_t start = millis();
while (!SCSI_IN(REQ) && (millis() - start) < 10000)
{
if (g_scsiHostPhyReset || !SCSI_IN(IO) || SCSI_IN(CD) != cd_start || SCSI_IN(MSG) != msg_start)
{
// Target switched out of DATA_IN mode
count = i;
}
// Wait for REQ asserted
}

int io = SCSI_IN(IO);
int cd = SCSI_IN(CD);
int msg = SCSI_IN(MSG);

if (g_scsiHostPhyReset)
{
dbgmsg("sciHostRead: aborting due to reset request");
count = i;
break;
}
else if (!io || cd != cd_start || msg != msg_start)
{
dbgmsg("scsiHostRead: aborting because target switched transfer phase (IO: ", io, ", CD: ", cd, ", MSG: ", msg, ")");
count = i;
break;
}

data[i] = scsiHostReadOneByte(&parityError);
Expand Down
41 changes: 38 additions & 3 deletions lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,50 @@ uint32_t scsi_accel_host_read(uint8_t *buf, uint32_t count, int *parityError, vo
uint8_t *dst = buf;
uint8_t *end = buf + count;
uint32_t paritycheck = 0;
uint32_t prev_rx_time = millis();
while (dst < end)
{
uint32_t available = pio_sm_get_rx_fifo_level(SCSI_PIO, SCSI_SM);

if (available == 0)
{
if (*resetFlag || !SCSI_IN(IO) || SCSI_IN(CD) != cd_start || SCSI_IN(MSG) != msg_start)
// No new data has been received by PIO, check if there is a need to abort

bool abort = false;
if (*resetFlag)
{
dbgmsg("scsi_accel_host_read: Aborting due to reset request");
abort = true;
}
else if ((millis() - prev_rx_time) > 10000)
{
dbgmsg("scsi_accel_host_read: Aborting due to timeout");
abort = true;
}
else
{
// Some drives such as ST-296N may have glitches on phase signals in between
// byte transfers. This is allowed by SCSI spec, and officially we should only
// check the phase signals when REQ is active. However the PIO logic currently
// does not do this. Instead, when we detect a phase change, wait for 10 milliseconds
// to see if it is real.
int debounce = 100;
while (debounce > 0 && (!SCSI_IN(IO) || SCSI_IN(CD) != cd_start || SCSI_IN(MSG) != msg_start))
{
debounce--;
delayMicroseconds(100);
}

if (debounce == 0)
{
dbgmsg("scsi_accel_host_read: aborting because target switched transfer phase (IO: ",
(int)SCSI_IN(IO), ", CD: ", (int)SCSI_IN(CD), ", MSG: ", (int)SCSI_IN(MSG), ")");
abort = true;
}
}

if (abort)
{
// Target switched out of DATA_IN mode
count = dst - buf;
break;
}
Expand Down Expand Up @@ -171,4 +206,4 @@ void scsi_accel_host_init()
sm_config_set_in_shift(&g_scsi_host.pio_cfg_async_read, true, true, 32);
}

#endif // PLATFORM_HAS_INITIATOR_MODE
#endif // PLATFORM_HAS_INITIATOR_MODE
41 changes: 35 additions & 6 deletions src/ZuluSCSI_initiator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ bool scsiInitiatorReadCapacity(int target_id, uint32_t *sectorcount, uint32_t *s
{
uint8_t sense_key;
scsiRequestSense(target_id, &sense_key);
logmsg("READ CAPACITY on target ", target_id, " failed, sense key ", sense_key);
scsiLogInitiatorCommandFailure("READ CAPACITY", target_id, status, sense_key);
return false;
}
else
Expand Down Expand Up @@ -748,7 +748,7 @@ bool scsiStartStopUnit(int target_id, bool start)
{
uint8_t sense_key;
scsiRequestSense(target_id, &sense_key);
dbgmsg("START STOP UNIT on target ", target_id, " failed, sense key ", sense_key);
scsiLogInitiatorCommandFailure("START STOP UNIT", target_id, status, sense_key);
}

return status == 0;
Expand Down Expand Up @@ -790,13 +790,13 @@ bool scsiTestUnitReady(int target_id)
uint8_t sense_key;
scsiRequestSense(target_id, &sense_key);

if (sense_key == 6)
if (sense_key == UNIT_ATTENTION)
{
uint8_t inquiry[36];
dbgmsg("Target ", target_id, " reports UNIT_ATTENTION, running INQUIRY");
scsiInquiry(target_id, inquiry);
}
else if (sense_key == 2)
else if (sense_key == NOT_READY)
{
dbgmsg("Target ", target_id, " reports NOT_READY, running STARTSTOPUNIT");
scsiStartStopUnit(target_id, true);
Expand Down Expand Up @@ -957,7 +957,7 @@ bool scsiInitiatorReadDataToFile(int target_id, uint32_t start_sector, uint32_t
uint8_t sense_key;
scsiRequestSense(target_id, &sense_key);

logmsg("scsiInitiatorReadDataToFile: READ failed: ", status, " sense key ", sense_key);
scsiLogInitiatorCommandFailure("scsiInitiatorReadDataToFile command phase", target_id, status, sense_key);
scsiHostPhyRelease();
return false;
}
Expand Down Expand Up @@ -1033,7 +1033,36 @@ bool scsiInitiatorReadDataToFile(int target_id, uint32_t start_sector, uint32_t

scsiHostPhyRelease();

return status == 0 && g_initiator_transfer.all_ok;
if (!g_initiator_transfer.all_ok)
{
dbgmsg("scsiInitiatorReadDataToFile: Incomplete transfer");
return false;
}
else if (status == 2)
{
uint8_t sense_key;
scsiRequestSense(target_id, &sense_key);

if (sense_key == RECOVERED_ERROR)
{
dbgmsg("scsiInitiatorReadDataToFile: RECOVERED_ERROR at ", (int)start_sector);
return true;
}
else if (sense_key == UNIT_ATTENTION)
{
dbgmsg("scsiInitiatorReadDataToFile: UNIT_ATTENTION");
return true;
}
else
{
scsiLogInitiatorCommandFailure("scsiInitiatorReadDataToFile data phase", target_id, status, sense_key);
return false;
}
}
else
{
return status == 0;
}
}


Expand Down
28 changes: 28 additions & 0 deletions src/ZuluSCSI_log_trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,31 @@ void scsiLogDataOut(const uint8_t *buf, uint32_t length)

g_OutByteCount += length;
}

static const char *get_sense_key_name(uint8_t sense_key)
{
switch (sense_key)
{
case RECOVERED_ERROR: return "RECOVERED_ERROR";
case NOT_READY : return "NOT_READY";
case MEDIUM_ERROR : return "MEDIUM_ERROR";
case HARDWARE_ERROR : return "HARDWARE_ERROR";
case ILLEGAL_REQUEST: return "ILLEGAL_REQUEST";
case UNIT_ATTENTION : return "UNIT_ATTENTION";
case DATA_PROTECT : return "DATA_PROTECT";
case BLANK_CHECK : return "BLANK_CHECK";
case VENDOR_SPECIFIC: return "VENDOR_SPECIFIC";
case COPY_ABORTED : return "COPY_ABORTED";
case ABORTED_COMMAND: return "ABORTED_COMMAND";
case EQUAL : return "EQUAL";
case VOLUME_OVERFLOW: return "VOLUME_OVERFLOW";
case MISCOMPARE : return "MISCOMPARE";
case RESERVED : return "RESERVED";
default: return "UNKNOWN";
}
}

void scsiLogInitiatorCommandFailure(const char *command_text, int target_id, int status, uint8_t sense_key)
{
logmsg("-- ", command_text, " on target ", target_id, " failed with status ", status, " and sense_key ", sense_key, " (", get_sense_key_name(sense_key), ")");
}
3 changes: 2 additions & 1 deletion src/ZuluSCSI_log_trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@
void scsiLogPhaseChange(int new_phase);
void scsiLogInitiatorPhaseChange(int new_phase);
void scsiLogDataIn(const uint8_t *buf, uint32_t length);
void scsiLogDataOut(const uint8_t *buf, uint32_t length);
void scsiLogDataOut(const uint8_t *buf, uint32_t length);
void scsiLogInitiatorCommandFailure(const char *command_text, int target_id, int status, uint8_t sense_key);
Loading
Loading