Major enhancements for Jaz drives

- Added command to spin down cartridges to command line
- Improved console diagnostics
  - Report SCSI short reads
  - Report last error
  - Report media change
  - Report disk at speed
  - Report spare sectors
  - Report detailed testing steps
- Supress unsuported commands on Jaz drive while:
  - Enabling early recovery
  - Reading defects list
- Fix short reads on Jaz drives during:
  - Cartridge status
  - Set error recovery
This commit is contained in:
Marcio T 2022-02-13 17:34:14 -07:00
parent dcfa0958a6
commit c719c8d7dd
7 changed files with 132 additions and 27 deletions

View File

@ -47,6 +47,7 @@ bool process_command() {
case 'h': print_help(); break;
case 'l': scan_bus(); break;
case 's': iomega_spin_up_cartridge(arg_val); break;
case 'p': iomega_spin_down_cartridge(arg_val); break;
case 'r': scsi_reset(); break;
case 'e': mac_eject(arg_val); break;
case 'i': dev_info(arg_val); break;
@ -83,6 +84,7 @@ void print_help() {
"\nIomega device operations on SCSI device:\n"
" spin [n] : spin up a cartridge\n"
" pause [n] : spin down a cartridge\n"
);
}

View File

@ -29,6 +29,19 @@ OSErr iomega_spin_down_and_eject( int id ) {
return scsi_cmd(id, cmd, sizeof(cmd), 0, 0, 0, 0);
}
OSErr iomega_spin_down_cartridge( int id ) {
// issue an Asynchronous STOP command to induce spindown
char cmd[6] = {
SCSI_Cmd_StartStopUnit,
1, // set the IMMED bit for offline
0,
0,
0,
0
};
return scsi_cmd(id, cmd, sizeof(cmd), 0, 0, 0, 0);
}
OSErr iomega_set_prevent_removal( int id, bool lock) {
OSErr err;
char cmd[6] = {

View File

@ -4,5 +4,6 @@ typedef Boolean bool;
OSErr iomega_spin_up_cartridge(int id);
OSErr iomega_spin_down_and_eject(int id);
OSErr iomega_spin_down_cartridge(int id);
OSErr iomega_set_prevent_removal(int id, bool lock);
OSErr iomega_eject_cartridge(int id);

View File

@ -61,8 +61,11 @@ OSErr scsi_cmd(int id, void *cmd, size_t clen, void *buff, size_t siz, size_t cn
case SCSI_READ: io_err = SCSIRead( (Ptr) TIB ); break;
default: break;
}
if (io_err != noErr) {
printf("SCSI Read/Write Error: %d\n", io_err);
if (io_err == scPhaseErr && flags & SCSI_READ) {
printf("\nSCSI phase error; less data delivered than requested\n");
}
else if (io_err != noErr) {
printf("\nSCSI Read/Write Error: %d\n", io_err);
}
} else {
printf("SCSICmd Error: %d\n", err);

View File

@ -161,7 +161,7 @@ void WndProc(long iMessage, uint16_t wParam) {
SpinUpIomegaCartridge(CurrentDevice);
break;
case DISK_AT_SPEED:
printf("Testing the disk\n");
printf("\nTesting the disk\n");
if(TestingPhase != READY_TO_TEST) {
PrepareToBeginTesting();
}
@ -339,6 +339,15 @@ void PaintCenteredValue(int Xleft, int Ytop, int XWidth, int YHeight, long value
*
* This paints the two columns of testing statistics on the test minitor window.
*******************************************************************************/
char *FindErrorString(long error) {
char *errStr = 0;
for (int i = 0; errorTypeList[i].str; i++) {
errStr = errorTypeList[i].str;
if (errorTypeList[i].code == error) break;
}
return errStr;
}
void PaintTestStatistics(bool Active) {
char szString[40];
// assemble and paint the sector testing range
@ -361,12 +370,7 @@ void PaintTestStatistics(bool Active) {
PaintCenteredString(76, 155, 126, 14, szString, Active);
// show the LastError
char *errStr = 0;
for (int i = 0; errorTypeList[i].str; i++) {
errStr = errorTypeList[i].str;
if (errorTypeList[i].code == LastError) break;
}
PaintCenteredString(76, 172, 126, 14, errStr, Active);
PaintCenteredString(76, 172, 126, 14, FindErrorString(LastError), Active);
// show the elapsed time
CvrtSecondsToHMSstring(szString, SecondsElapsed);

View File

@ -282,6 +282,8 @@ void PaintTheBarGraphs(bool Active);
void PaintTestStatistics(bool Active);
void CvrtSecondsToHMSstring(char *szString, long seconds);
char *FindErrorString(long error);
void UpdateCurrentSector();
void UpdateRunTimeDisplay();
void UpdateRunPhaseDisplay();
@ -301,9 +303,9 @@ void GetCommandDetails(char command, char &cmd_flags, char &cmd_length);
long SCSICommand(short Device, char *lpCmdBlk, void *lpIoBuf, size_t IoBufLen);
long EnumerateIomegaDevices(uint8_t *DrivesSkipped);
long GetModePage(short Device, short PageToGet, void *pBuffer, short BufLen);
long SetModePage(short Device, void *pBuffer);
long SetModePage(short Device, void *pBuffer, short BufLen);
void ModifyModePage(char *PageBuff, char eec, char retries);
void SetErrorRecovery(bool Retries, bool ECC, bool Testing);
long SetErrorRecovery(bool Retries, bool ECC, bool Testing);
long GetNonSenseData(short Device, short DataPage, void *Buffer, short BufLen);
long LockCurrentDrive();
long UnlockCurrentDrive();

View File

@ -6,8 +6,28 @@
#include "tip.h"
//#define DEMO
/* The original TIP seems to request more data than is supplied by
* certain commands. While this appears to be allowed, it causes
* SCSI phase errors to be reported. Setting NO_EXCESS_READS will
* adjust the reads to to the max size before such errors occur.
*/
#define NO_EXCESS_READS
/* The original TIP will always try to enable Early Recovery. This
* fails on certain Jaz drives. While the original TIP will then
* retry without Early Recovery, this will cause many errors to be
* reported. Enable SUPRESS_ER_ERRORS to prevent this from problem
* from happening as frequently
*/
#define SUPRESS_ER_ERRORS
/* The original TIP will always try to read the defects list, but
* not all drives support this, causing many errors to be shown.
* Setting SUPPRESS_DEFECTS_ERROR will silence these errors.
*/
#define SUPPRESS_DEFECTS_ERROR
#define MAKE_LITTLE_ENDIAN(a) a // Don't do anything on 68000
#define MAKE_BIG_ENDIAN(a) a // Don't do anything on 68000
@ -96,6 +116,13 @@ long DriveCount = 0;
long JazDrive = 0; // true if the current drive
long CartridgeStatus = DISK_NOT_PRESENT;
#ifdef SUPRESS_ER_ERRORS
Boolean SupressEarlyRecovery = false;
#endif
#ifdef SUPPRESS_DEFECTS_ERROR
Boolean SupressDefectsError = false;
#endif
unsigned long StartingInstant;
// ----------------------------- Run Time Variables ------------------------------
@ -215,6 +242,7 @@ long SCSICommand(short Device, char *lpCmdBlk, void *lpIoBuf, size_t IoBufLen) {
(long(sense_data.ascq) << 8) |
(long(sense_data.key) );
if(res == MEDIA_CHANGE_CODE) {
printf("Media change signalled. Most recent error can be ignored\n\n");
int index = GetDriveEntryOffset(Device);
DriveArray[index].flags |= MEDIA_CHANGED;
return 0;
@ -312,13 +340,18 @@ long GetModePage(short Device, short PageToGet, void *pBuffer, short BufLen) {
/*******************************************************************************
* SET MODE PAGE
*******************************************************************************/
long SetModePage(short Device, void *pBuffer) {
char* ebx = (char*) pBuffer; // get a pointer to the top of buffer
char ecx = ebx[0] + 1; // adjust it up by one
long SetModePage(short Device, void *pBuffer, short BufLen) {
unsigned char* ebx = (unsigned char*) pBuffer; // get a pointer to the top of buffer
unsigned char ecx = ebx[0] + 1; // adjust it up by one
ebx[0] = 0; // now clear the two reserved bytes
ebx[2] = 0;
if(ecx != BufLen) {
printf("Length error in SetModePage %d != %d\n\n", BufLen, (int) ecx);
return 0;
}
char Scsi[6] = {0}; // init the SCSI parameter block
Scsi[0] = SCSI_Cmd_ModeSelect; // set the command
Scsi[1] = 0x10; // set the Page Format bit
@ -343,16 +376,21 @@ void ModifyModePage(char *PageBuff, char ecc, char retries) {
ebx[8] = retries; // then set the write count too
}
void SetErrorRecovery(bool Retries, bool ECC, bool Testing) {
long SetErrorRecovery(bool Retries, bool ECC, bool Testing) {
char PageBuff[40];
#ifdef NO_EXCESS_READS
// Limit reads to 20 bytes on Zip to prevent controller errors
GetModePage(CurrentDevice, ERROR_RECOVERY_PAGE, PageBuff, JazDrive ? sizeof(PageBuff) : 20);
// Limit reads to 20 bytes on Zip (24 bytes on Jaz) to prevent controller errors
const short pageBuffLen = JazDrive ? 24 : 20;
#else
GetModePage(CurrentDevice, ERROR_RECOVERY_PAGE, PageBuff, sizeof(PageBuff));
const short pageBuffLen = sizeof(PageBuff);
#endif
long eax = GetModePage(CurrentDevice, ERROR_RECOVERY_PAGE, PageBuff, pageBuffLen);
if(eax) {
printf("SetErrorRecovery failed\n");
return eax;
}
#define EARLY_RECOVERY 0x08
#define PER 0x04
#define SUPPRESS_ECC 0x01
@ -360,6 +398,9 @@ void SetErrorRecovery(bool Retries, bool ECC, bool Testing) {
// set the ECC fields
char ecc = SUPPRESS_ECC; // presume ECC suppression
if(ECC) {
#ifdef SUPRESS_ER_ERRORS
if(!SupressEarlyRecovery)
#endif
ecc = EARLY_RECOVERY; // enable ECC and Early Recovery
if(Testing) {
ecc = EARLY_RECOVERY | PER; // we're testing, so EER & PER
@ -374,14 +415,22 @@ void SetErrorRecovery(bool Retries, bool ECC, bool Testing) {
retries = 0;
ModifyModePage(PageBuff, ecc, retries);
const long eax = SetModePage(CurrentDevice, PageBuff);
eax = SetModePage(CurrentDevice, PageBuff, pageBuffLen);
// if we had an invalid field in the CDB (the EER bit was on)
if (eax == 0x00260005) {
GetModePage(CurrentDevice, ERROR_RECOVERY_PAGE, PageBuff, sizeof(PageBuff));
ecc &= ~0x08; // same, *BUT*NOT* Early Recovery
GetModePage(CurrentDevice, ERROR_RECOVERY_PAGE, PageBuff, pageBuffLen);
ecc &= ~EARLY_RECOVERY; // same, *BUT*NOT* Early Recovery
ModifyModePage(PageBuff, ecc, retries);
SetModePage(CurrentDevice, PageBuff);
eax = SetModePage(CurrentDevice, PageBuff, pageBuffLen);
#ifdef SUPRESS_ER_ERRORS
if(!eax) {
printf(" Early recovery not supported on this drive. Ignoring.\n\n");
SupressEarlyRecovery = true;
}
#endif
}
return eax;
}
/*******************************************************************************
@ -468,7 +517,18 @@ long GetSpareSectorCounts(char checkPassword) {
Scsi[0] = SCSI_Cmd_ReadDefectData;
Scsi[2] = 0x1e; // 0b00011110 defect format, G/P bits
Scsi[8] = 4; // ask for only FOUR bytes
#ifdef SUPPRESS_DEFECTS_ERROR
if(SupressDefectsError)
eax = INCOMPATIBLE_MEDIA;
else
#endif
eax = SCSICommand(CurrentDevice, Scsi, &DefectHeader, sizeof(DefectHeader));
#ifdef SUPPRESS_DEFECTS_ERROR
if(!SupressDefectsError && eax == INCOMPATIBLE_MEDIA) {
printf("Defects list not supported on this drive. Ignoring.\n\n");
SupressDefectsError = true;
}
#endif
if ((!eax) || (eax == INCOMPATIBLE_MEDIA)) {
// we could read its defect list ... so show it!
// --------------------------------------------------------------------------
@ -635,7 +695,7 @@ uint8_t GetCartridgeStatus(long Device, uint8_t flags) {
long eax;
char DiskStat[72];
#ifdef NO_EXCESS_READS
eax = GetNonSenseData(Device, DISK_STATUS_PAGE, DiskStat, (flags & JAZ_DRIVE) ? sizeof(DiskStat) : 63);
eax = GetNonSenseData(Device, DISK_STATUS_PAGE, DiskStat, 4);
if (eax) return DISK_STATUS_UNKNOWN;
#else
eax = GetNonSenseData(Device, DISK_STATUS_PAGE, DiskStat, sizeof(DiskStat));
@ -674,6 +734,7 @@ void SetCartridgeStatusToEAX(long eax, uint8_t flags) {
SetRichEditText(szNotRunning);
goto DisableActions;
case DISK_AT_SPEED:
printf("Disk at speed\n");
eax = GetSpareSectorCounts(true); // update the Cart Condition
if(eax == MEDIA_NOT_PRESENT) {
goto DisableActions;
@ -686,10 +747,14 @@ void SetCartridgeStatusToEAX(long eax, uint8_t flags) {
FirmErrors = 0;
// check to see if we have enough spares to start
if(JazDrive) {
printf("Spare Sectors: %ld/%d\n", Side_0_SparesCount, MAXIMUM_JAZ_SPARES);
if(Side_0_SparesCount < MINIMUM_JAZ_SPARES)
goto InsufficientSpares;
}
else {
printf("Spare Sectors:\n");
printf(" Side 1: %ld/%d\n", Side_0_SparesCount, MAXIMUM_ZIP_SPARES);
printf(" Side 2: %ld/%d\n", Side_1_SparesCount, MAXIMUM_ZIP_SPARES);
if(Side_0_SparesCount < MINIMUM_ZIP_SPARES) {
goto InsufficientSpares;
}
@ -761,6 +826,12 @@ void PrepareToBeginTesting() {
HardErrors = 0;
UserInterrupt = 0;
LastError = 0;
#ifdef SUPRESS_ER_ERRORS
SupressEarlyRecovery = false;
#endif
#ifdef SUPPRESS_DEFECTS_ERROR
SupressDefectsError = false;
#endif
#ifdef DEMO
LastLBAOnCartridge = 99999;
SoftErrors = 6;
@ -820,12 +891,12 @@ long PerformRegionTransfer(short XferCmd, void *pBuffer) {
char Scsi[10] = {0}; // clear out the SCSI CDB
const long InitialHardErrors = HardErrors;
SetErrorRecovery(false, false, true); // disable Retries & ECC
long eax = SetErrorRecovery(false, false, true); // disable Retries & ECC
Scsi[0] = XferCmd;
SET_DWORD_AT(Scsi, 2, MAKE_BIG_ENDIAN(FirstLBASector)); // WHICH LBA's to read, BIG endian
SET_WORD_AT (Scsi, 7, MAKE_BIG_ENDIAN(NumberOfLBAs)); // HOW MANY to read, BIG endian
long eax = SCSICommand(CurrentDevice, Scsi, pBuffer, NumberOfLBAs * BYTES_PER_SECTOR);
eax = SCSICommand(CurrentDevice, Scsi, pBuffer, NumberOfLBAs * BYTES_PER_SECTOR);
// if we failed somewhere during our transfer ... let's zero in on it
if (eax) {
if ( eax == SS_ERR || // if it's a CONTROLLER ERROR, skip!
@ -834,6 +905,8 @@ long PerformRegionTransfer(short XferCmd, void *pBuffer) {
goto Exit;
}
printf("Starting detailed search...\n");
//--------------------------------------------------------------------------
// Save error and current Soft + Hard Error count to see if we do FIND the glitch ...
const long GlitchError = eax; // save the error which stopped us!
@ -863,6 +936,7 @@ long PerformRegionTransfer(short XferCmd, void *pBuffer) {
if (eax == SS_ERR) goto Exit; // if it's a CONTROLLER ERROR, skip!
if (eax & 0xFF == 1) goto PostTheError; // did we recover?
printf(" Found error, retesting with retries\n");
SetErrorRecovery(true, false, true); // enable retries
eax = SCSICommand(CurrentDevice, Scsi, LocalBuffer, BYTES_PER_SECTOR);
if (eax) {
@ -870,7 +944,8 @@ long PerformRegionTransfer(short XferCmd, void *pBuffer) {
if (eax == SS_ERR) goto Exit; // if it's a CONTROLLER ERROR, skip!
if (eax & 0xFF == 1) goto PostTheError; // did we recover?
SetErrorRecovery(true, true, true); // enable retries AND EEC
printf(" Found error, retesting with retries & ECC\n");
eax = SetErrorRecovery(true, true, true); // enable retries AND EEC
eax = SCSICommand(CurrentDevice, Scsi, LocalBuffer, BYTES_PER_SECTOR);
if (eax) {
// failed with retries and EEC
@ -888,6 +963,8 @@ long PerformRegionTransfer(short XferCmd, void *pBuffer) {
}
PostTheError:
printf(" %s (Sector %ld)\n", FindErrorString(eax), SingleTransferLBA);
printf("--------------------------------------------\n");
BumpErrorCounts(eax); // given eax, count the errors
GetSpareSectorCounts(false); // update the Cart's Condition
UpdateRunTimeDisplay();
@ -897,6 +974,7 @@ long PerformRegionTransfer(short XferCmd, void *pBuffer) {
ProcessPendingMessages();
}
printf("... detailed search finished\n");
// now see whether we *did* found something to complain about ...
eax = SoftErrors + HardErrors;
if (eax == GlitchCount) {
@ -907,6 +985,7 @@ long PerformRegionTransfer(short XferCmd, void *pBuffer) {
long ebx = eax & 0x00FF00FF; // strip the ASCQ byte
if(ebx == 0x00110003) // if we're about to say "unrecovered read"
eax = 0x170101; // change it to: "Read with Retries"
printf("%s\n", FindErrorString(eax));
BumpErrorCounts(eax); // given eax, count the errors
HardErrors = SavedHardErrors; // restore the counts
SoftErrors = SavedSoftErrors;
@ -917,6 +996,7 @@ long PerformRegionTransfer(short XferCmd, void *pBuffer) {
eax = 0; // now let's return happiness to our caller
if (HardErrors != InitialHardErrors) // UNRECOVERABLE errors!
eax = -1;
printf("\n");
}
Exit: