;+-----------------------------------------------------------------------------+ ;| ASPI.ASM Aspi Functions for Trouble In Paradise? | ;+-----------------------------------------------------------------------------+ DEFECT_LIST_HEADER struct 1t ;------------------------------------------------------------------------------- DLH_reserved BYTE ? ; (00h) DLH_BitFlags BYTE ? ; [000] [P] [G] [xxx - defect list format] DLH_DefectListLength WORD ? ; ;------------------------------------------------------------------------------- DEFECT_LIST_HEADER ends ;+-----------------------------------------------------------------------------+ ;| CHECK FOR ASPI | ;|-----------------------------------------------------------------------------| ;| This locates the system's ASPI functions. If all goes well it sets two | ;| pointers to the WinASPI entrypoints and returns with EAX != 0. In the | ;| event of trouble it sets the error message into the TROUBLE window, and | ;| sets the ErrorMode flag to cause the Trouble panel to be displayed. | ;+-----------------------------------------------------------------------------+ CheckForASPI PROC USES esi edi ;------------------------------------------------------------------------------- ; suppress any DLL not found errors and try to load our DLL invoke SetErrorMode, SEM_NOOPENFILEERRORBOX mov edi, eax invoke LoadLibrary, ADDR szASPI32DLLName IF FORCE_TROUBLE zero eax ENDIF mov esi, eax invoke SetErrorMode, edi .IF (esi) mov hASPIDLL, esi invoke GetProcAddress, esi, ADDR szASPI32SupportInfo mov lpGetASPI32SupportInfo, eax invoke GetProcAddress, esi, ADDR szASPI32Command mov lpSendASPI32Command, eax invoke GetASPI32SupportInfoPtr PTR lpGetASPI32SupportInfo cmp ah, SS_COMP ; did we succeed perfectly? je Exit ; yep, so off we go ... cmp ah, SS_FAILED_INIT ; nope, did we init fail? jne No_ASPI ; nope, so some error set ErrorMode .ELSE ; set the static text for the ASPI version problem control No_ASPI: set BadASPIDrivers .ENDIF zero eax Exit: movzx eax, al ret ;------------------------------------------------------------------------------- CheckForASPI ENDP ;+-----------------------------------------------------------------------------+ ;| HOST ADAPTER INQUIRY | ;+-----------------------------------------------------------------------------+ HostAdapterInquiry PROC Adapter:DWORD ;------------------------------------------------------------------------------- ZeroASPI ; clear out our calling block's parameters mov ASPI_CmdBlock.hai.SRB_Cmd, SC_HA_INQUIRY movmov ASPI_CmdBlock.hai.SRB_HaId, al, byte ptr Adapter invoke SendASPI32CommandPtr PTR lpSendASPI32Command, ADDR ASPI_CmdBlock movzx eax, ASPI_CmdBlock.hai.SRB_Status dec eax ret ;------------------------------------------------------------------------------- HostAdapterInquiry ENDP ;+-----------------------------------------------------------------------------+ ;| GET DEVICE TYPE | ;+-----------------------------------------------------------------------------+ GetDeviceType PROC Adapter:DWORD, Device:DWORD ;------------------------------------------------------------------------------- ZeroASPI mov ASPI_CmdBlock.gdt.SRB_Cmd, SC_GET_DEV_TYPE ; w: ASPI command code movmov ASPI_CmdBlock.gdt.SRB_HaId, al, byte ptr Adapter ; w: ASPI host adapter number movmov ASPI_CmdBlock.gdt.SRB_Target, al, byte ptr Device ; w: target's SCSI ID ; do the command ... invoke SendASPI32CommandPtr PTR lpSendASPI32Command, ADDR ASPI_CmdBlock movzx eax, ASPI_CmdBlock.gdt.SRB_Status ret ;------------------------------------------------------------------------------- GetDeviceType ENDP ;+-----------------------------------------------------------------------------+ ;| GET DRIVE ENTRY OFFSET | ;| Returns the offset of the chosen drive's status word in EAX | ;+-----------------------------------------------------------------------------+ GetDriveEntryOffset PROC Adapter:DWORD, Device:DWORD ;------------------------------------------------------------------------------- mov eax, OFFSET DriveArray - SIZEOF DWORD mov bl, byte ptr Adapter mov bh, byte ptr Device Next: add eax, SIZEOF DWORD cmp [eax], bx ; did we find the right table slot? jne Next ret ;------------------------------------------------------------------------------- GetDriveEntryOffset ENDP ;+-----------------------------------------------------------------------------+ ;| GET COMMAND DETAILS | ;| Given a SCSI command byte, this returns the command | ;| block length in AL and the Command Flags in AH. | ;+-----------------------------------------------------------------------------+ GetCommandDetails PROC USES ebx esi, command:BYTE ;------------------------------------------------------------------------------- mov bl, command mov ecx, LENGTH_OF_DETAILS_TABLE mov esi, OFFSET CommandDetailsTable ; search the table for the command entry .REPEAT lodsw ; pickup a command/FLAG pair .BREAK .IF (al == bl) ; if we match we're done .IF (ecx == 1) ; if we didn't locate it ... zero eax ; return ZERO jmp Exit .ENDIF .UNTILCXZ mov al, 6 ; presume a short (6 byte) command .IF (bl > TEN_BYTE_CMDS) ; but if it's a LONG one .... mov al, 10 ; then return that .ENDIF Exit: ret ;------------------------------------------------------------------------------- GetCommandDetails ENDP ;+-----------------------------------------------------------------------------+ ;| SCSI COMMAND | ;| This executes a SCSI command through the ASPI interface. It receives a | ;| NEAR pointer to a standard SCSI command block (SCB) and a pointer and | ;| length to an IoBuffer for the command. It returns in EAX the complete | ;| three-byte sense code from the command. | ;+-----------------------------------------------------------------------------+ SCSICommand PROC USES esi edi, Adapter:DWORD, Device:DWORD, lpCmdBlk:LPVOID, lpIoBuf:LPVOID, IoBufLen:DWORD LOCAL AbortSRB:SRB_Abort ;------------------------------------------------------------------------------- ZeroASPI mov ASPI_CmdBlock.io.SRB_Cmd, SC_EXEC_SCSI_CMD ; w: ASPI command code = SC_EXEC_SCSI_CMD movmov ASPI_CmdBlock.io.SRB_HaId, al, byte ptr Adapter ; w: ASPI host adapter number movmov ASPI_CmdBlock.io.SRB_Target, al, byte ptr Device ; w: target's SCSI ID movmov ASPI_CmdBlock.io.SRB_BufPointer, eax, lpIoBuf ; w: data buffer pointer movmov ASPI_CmdBlock.io.SRB_BufLen, eax, IoBufLen ; w: data allocation length mov ASPI_CmdBlock.io.SRB_SenseLen, SIZEOF ASPI_CmdBlock.io.SRB_SenseArea ; w: sense allocation length mov ASPI_CmdBlock.io.SRB_Flags, SRB_EVENT_NOTIFY ; w: SCSI request flags movmov ASPI_CmdBlock.io.SRB_PostProc, eax, hCompletionEvent ; assemble our scsi command block mov esi, lpCmdBlk ; get a pointer to the scsi command invoke GetCommandDetails, [esi] ; al == cmd_length, ah == cmd_flags or ASPI_CmdBlock.io.SRB_Flags, ah ; w: SCSI request flags mov ASPI_CmdBlock.io.SRB_CDBLen, al ; w: CDB Length ; move the command block into the ASPI structure lea edi, ASPI_CmdBlock.io.SRB_CDBByte movzx ecx, al rep movsb ; call the ASPI manager to forward the command to the device invoke SendASPI32CommandPtr PTR lpSendASPI32Command, ADDR ASPI_CmdBlock invoke WaitForSingleObject, hCompletionEvent, SCSI_TIMEOUT movzx eax, ASPI_CmdBlock.io.SRB_Status ; if the command did not generate any Sense Data, just return NULL .IF (al == SS_COMP) NoError: zero eax ; else, if it's *NOT* a "Sense Data" error (SS_ERR) .ELSEIF (al != SS_ERR) movzx eax, al or eax, 00FFFF00h ; [00 FF FF er] ; okay, we have an SS_ERR condition, let's check the SENSE DATA .ELSE ; assemble [00 ASC ASCQ SenseKey] movzx eax, word ptr ASPI_CmdBlock.io.SRB_SenseArea[12] ; [00 00 ASCQ ASC] swap ah, al ; [00 00 ASC ASCQ] shl eax, 8 ; [00 ASC ASCQ 00] mov al, ASPI_CmdBlock.io.SRB_SenseArea[2] ; [00 ASC ASCQ xSenseKey] and al, 0Fh ; [00 ASC ASCQ SenseKey] .IF (eax == MEDIA_CHANGE_CODE) invoke GetDriveEntryOffset, Adapter, Device or dword ptr [eax], MEDIA_CHANGED jmp NoError .ELSEIF (!eax) ; if we had NO SENSE error, but SS_ERR mov eax, SS_ERR ; return SS_ERR .ENDIF .ENDIF ret ;------------------------------------------------------------------------------- SCSICommand ENDP ;+-----------------------------------------------------------------------------+ ;| ENUMERATE IOMEGA DEVICES | ;+-----------------------------------------------------------------------------+ EnumerateIomegaDevices PROC USES ebx LOCAL FirstInvalidAdapter:DWORD, Scsi[6]:BYTE, InqData[96]:CHAR, Adapter:DWORD, Device:DWORD ;------------------------------------------------------------------------------- IFE FORCE_NO_DRIVES ; get the total adapter count invoke HostAdapterInquiry, 0 movzx eax, ASPI_CmdBlock.hai.HA_Count ; see whether it completed successfully .IF (ASPI_CmdBlock.hai.SRB_Status != SS_COMP) || (!eax) jmp Exit .ENDIF mov FirstInvalidAdapter, eax reset Adapter reset DriveCount ; reset the global DriveCount variable .REPEAT ; check each adapter for "ppa3" string and small blk xfers invoke HostAdapterInquiry, Adapter .IF (ASPI_CmdBlock.hai.SRB_Status == SS_COMP) ; truncate at four chars so "PPA3NT" and "ppa3" both match! mov byte ptr ASPI_CmdBlock.hai.HA_Identifier[4], 0 invoke lstrcmpi, ADDR ASPI_CmdBlock.hai.HA_Identifier, ADDR szPPA3 .IF (!eax) mov eax, dword ptr ASPI_CmdBlock.hai.HA_Unique[4] .IF (eax < 65536) set OldPPA3Driver .ENDIF .ENDIF .ENDIF ; now scan the devices on this host adapter reset Device .REPEAT invoke GetDeviceType, Adapter, Device cmp eax, SS_COMP jne TryNextDrive ;-------------------------------------------------------------------------- varzero Scsi mov Scsi, SCSI_Cmd_Inquiry mov Scsi[4], SIZEOF InqData invoke SCSICommand, Adapter, Device, ADDR Scsi, ADDR InqData, SIZEOF InqData check eax jnz TryNextDrive ;-------------------------------------------------------------------------- mov byte ptr InqData[14], 0 invoke lstrcmpi, ADDR szIomega, ADDR InqData[8] check eax jnz TryNextDrive ;-------------------------------------------------------------------------- mov byte ptr InqData[19], 0 invoke lstrcmpi, ADDR szZip, ADDR InqData[16] check eax jz FoundZipOrJaz ;-------------------------------------------------------------------------- invoke lstrcmpi, ADDR szJaz, ADDR InqData[16] check eax jnz TryNextDrive mov eax, JAZ_DRIVE ;-------------------------------------------------------------------------- ; check for ANSI SCSI to see whether we need to play ; the Odd/Even password length game ... FoundZipOrJaz: .IF !(byte ptr InqData[2] & 07h) or eax, ODD_BYTE_COMPENSATION ; turn on compensation .ENDIF mov ebx, DriveCount mov DriveArray[ebx*4], eax movmov byte ptr DriveArray[ebx*4+0], al, byte ptr Adapter movmov byte ptr DriveArray[ebx*4+1], al, byte ptr Device inc ebx mov DriveCount, ebx cmp ebx, MAX_DRIVE_COUNT jae Exit ;-------------------------------------------------------------------------- TryNextDrive: inc Device .UNTIL (Device >= 16) inc Adapter mov eax, Adapter .UNTIL (eax >= FirstInvalidAdapter) ;------------------------------------------------------------------------------- ENDIF Exit: .IF (!DriveCount) set ErrorMode .ENDIF mov eax, DriveCount ret ;------------------------------------------------------------------------------- EnumerateIomegaDevices ENDP ;+-----------------------------------------------------------------------------+ ;| GET MODE PAGE | ;+-----------------------------------------------------------------------------+ GetModePage PROC Adapter:DWORD, Device:DWORD, PageToGet:DWORD, pBuffer:PTR, BufLen:DWORD LOCAL Scsi[6]:BYTE ;------------------------------------------------------------------------------- varzero Scsi mov Scsi, SCSI_Cmd_ModeSense movmov Scsi[2], al, byte ptr PageToGet movmov Scsi[4], al, byte ptr BufLen invoke SCSICommand, Adapter, Device, ADDR Scsi, pBuffer, BufLen ret ;------------------------------------------------------------------------------- GetModePage ENDP ;+-----------------------------------------------------------------------------+ ;| SET MODE PAGE | ;+-----------------------------------------------------------------------------+ SetModePage PROC USES ebx, Adapter:DWORD, Device:DWORD, pBuffer:PTR LOCAL Scsi[6]:BYTE ;------------------------------------------------------------------------------- varzero Scsi ; init the SCSI parameter block mov ebx, pBuffer ; get a pointer to the top of buffer movzx ecx, byte ptr [ebx] ; get the total length inc ecx ; adjust it up by one reset byte ptr [ebx] ; now clear the two reserved bytes reset byte ptr [ebx+2] ; mov Scsi, SCSI_Cmd_ModeSelect ; set the command mov Scsi[1], 10h ; set the Page Format bit mov Scsi[4], cl ; set the parameter list length invoke SCSICommand, Adapter, Device, ADDR Scsi, pBuffer, ecx ret ;------------------------------------------------------------------------------- SetModePage ENDP ;+-----------------------------------------------------------------------------+ ;| SET ERROR RECOVERY | ;+-----------------------------------------------------------------------------+ SetErrorRecovery PROC USES ebx, Retries:BOOL, ECC:BOOL, Testing:BOOL LOCAL PageBuff[40]:BYTE ;------------------------------------------------------------------------------- invoke GetModePage, CurrentAdapter, CurrentDevice, ERROR_RECOVERY_PAGE, ADDR PageBuff, SIZEOF PageBuff lea ebx, PageBuff[4] ; get just past the header address movzx eax, PageBuff[3] ; get the Block Descriptor Length ; form ebx == the offset to the top of the page we've read ... add ebx, eax ; always turn off the PS bit (parameters savable) and byte ptr [ebx], NOT 80h ; set the ECC fields mov al, 0C1h ; presume ECC suppression .IF (ECC) mov al, 0C8h ; enable ECC and Early Recovery .IF (Testing) mov al, 0CCh ; we're testing, so EER & PER .ENDIF .ENDIF mov [ebx+2], al ; and set the value ; set the retry counts mov al, 16h ; set retries to 22 for Zip drive .IF (JazDrive) mov al, 64h ; and to 100 for Jaz drive .ENDIF .IF (!Retries) ; but if we have no retries ... zero al ; then kill it. .ENDIF mov byte ptr [ebx+3], al ; set the common retry count .IF (byte ptr [ebx+1] > 6) ; if we have a large format page ... mov byte ptr [ebx+8], al ; then set the write count too .ENDIF invoke SetModePage, CurrentAdapter, CurrentDevice, ADDR PageBuff ; if we had an invalid field in the CDB (the EER bit was on) .IF (eax == 00260005h) invoke GetModePage, CurrentAdapter, CurrentDevice, ERROR_RECOVERY_PAGE, ADDR PageBuff, SIZEOF PageBuff lea ebx, PageBuff[4] ; get just past the header address movzx eax, PageBuff[3] ; get the Block Descriptor Length ; form ebx == the offset to the top of the page we've read ... add ebx, eax ; always turn off the PS bit (parameters savable) and byte ptr [ebx], NOT 80h ; set the ECC fields mov al, 0C1h ; presume ECC suppression .IF (ECC) mov al, 0C0h ; enable ECC *BUT*NOT* Early Recovery .IF (Testing) mov al, 0C4h ; we're testing, PER .ENDIF .ENDIF mov [ebx+2], al ; and set the value ; set the retry counts mov al, 16h ; set retries to 22 for Zip drive .IF (JazDrive) mov al, 64h ; and to 100 for Jaz drive .ENDIF .IF (!Retries) ; but if we have no retries ... zero al ; then kill it. .ENDIF mov byte ptr [ebx+3], al ; set the common retry count .IF (byte ptr [ebx+1] > 6) ; if we have a large format page ... mov byte ptr [ebx+8], al ; then set the write count too .ENDIF invoke SetModePage, CurrentAdapter, CurrentDevice, ADDR PageBuff .ENDIF ret ;------------------------------------------------------------------------------- SetErrorRecovery ENDP ;------------------------------------------------------------------------------- COMMENT ~ ;+-----------------------------------------------------------------------------+ ;| VENDOR SPECIFIC | ;+-----------------------------------------------------------------------------+ VendorSpecific PROC USES ebx, EnableVS:BOOL LOCAL PageBuff[40]:BYTE ;------------------------------------------------------------------------------- invoke GetModePage, CurrentAdapter, CurrentDevice, MODES_OF_OPERATION, ADDR PageBuff, SIZEOF PageBuff lea ebx, PageBuff[4] ; get just past the header address movzx eax, PageBuff[3] ; get the Block Descriptor Length add ebx, eax ; now ebx has the offset to the top of the page we've read ... .IF (EnableVS) or byte ptr [ebx+2], 20h ; VSC .ELSE and byte ptr [ebx+2], NOT 20h ; turn off VSC bit .ENDIF and byte ptr [ebx], NOT 80h ; turn off the PS bit invoke SetModePage, CurrentAdapter, CurrentDevice, ADDR PageBuff ret ;------------------------------------------------------------------------------- VendorSpecific ENDP ;+-----------------------------------------------------------------------------+ ;| DISABLE CACHING | ;+-----------------------------------------------------------------------------+ DisableCaching PROC USES ebx, Adapter:DWORD, Device:DWORD LOCAL PageBuff[40]:BYTE ;------------------------------------------------------------------------------- invoke GetModePage, Adapter, Device, CACHING_PAGE, ADDR PageBuff, SIZEOF PageBuff lea ebx, PageBuff[4] ; get just past the header address movzx eax, PageBuff[3] ; get the Block Descriptor Length add ebx, eax ; now ebx has the offset to the top of the page we've read ... mov byte ptr [ebx+2], 1 ; disable all caching invoke SetModePage, Adapter, Device, ADDR PageBuff ret ;------------------------------------------------------------------------------- DisableCaching ENDP ;------------------------------------------------------------------------------- COMMENT ~ ;------------------------------------------------------------------------------- ;+-----------------------------------------------------------------------------+ ;| GET NON-SENSE PAGE DATA | ;|-----------------------------------------------------------------------------| ;| Given Adapter, Device, DataPage, and a Buffer to receive the data, this | ;| fills the buffer we're given and returns with the SCSI Completion Code | ;+-----------------------------------------------------------------------------+ GetNonSenseData PROC Adapter:DWORD, Device:DWORD, DataPage:DWORD, Buffer:DWORD, BufLen:DWORD LOCAL Scsi[6]:BYTE ;------------------------------------------------------------------------------- varzero Scsi mov Scsi, SCSI_Cmd_NonSenseData ; do a Non-Sense Data Read movmov Scsi[2], al, byte ptr DataPage ; which page to read movmov Scsi[4], al, byte ptr BufLen ; tell drive page is this long invoke SCSICommand, Adapter, Device, ADDR Scsi, Buffer, BufLen ret ;------------------------------------------------------------------------------- GetNonSenseData ENDP ;+-----------------------------------------------------------------------------+ ;| UNLOCK ALL MEDIA | ;+-----------------------------------------------------------------------------+ UnlockAllMedia PROC USES esi ebx LOCAL DrivesLeft:DWORD, Scsi[6]:BYTE ;------------------------------------------------------------------------------- ; make sure the media is not locked as we exit ... varzero Scsi mov Scsi, SCSI_Cmd_PreventAllow ; setup the SCSI command block for the operation movmov DrivesLeft, eax, DriveCount .IF (eax) mov esi, OFFSET DriveArray .REPEAT movzx ebx, byte ptr [esi] ; pickup the Adapter movzx ecx, byte ptr [esi+1] ; pickup the DeviceID invoke SCSICommand, ebx, ecx, ADDR Scsi, NULL, 0 add esi, SIZEOF DWORD dec DrivesLeft .UNTIL (zero?) .ENDIF ret ;------------------------------------------------------------------------------- UnlockAllMedia ENDP ;+-----------------------------------------------------------------------------+ ;| LOCK CURRENT DRIVE | ;+-----------------------------------------------------------------------------+ LockCurrentDrive PROC LOCAL Scsi[6]:BYTE ;------------------------------------------------------------------------------- ; lock the media to prevent its ejection varzero Scsi mov Scsi, SCSI_Cmd_PreventAllow mov Scsi[4], 1 ; set to ONE to lock the drive invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, NULL, 0 ret ;------------------------------------------------------------------------------- LockCurrentDrive ENDP ;+-----------------------------------------------------------------------------+ ;| SPIN UP IOMEGA CARTRIDGE | ;+-----------------------------------------------------------------------------+ SpinUpIomegaCartridge PROC Adapter:DWORD, Device:DWORD LOCAL Scsi[6]:BYTE ;------------------------------------------------------------------------------- ; issue an Asynchronous START command to induce spinup varzero Scsi mov Scsi[0], SCSI_Cmd_StartStopUnit mov Scsi[1], 1 ; set the IMMED bit for offline mov Scsi[4], 1 ; start the disk spinning invoke SCSICommand, Adapter, Device, ADDR Scsi, NULL, 0 ret ;------------------------------------------------------------------------------- SpinUpIomegaCartridge ENDP ;+-----------------------------------------------------------------------------+ ;| EJECT ALL MEDIA | ;+-----------------------------------------------------------------------------+ EjectAllMedia PROC USES esi ebx LOCAL DrivesLeft:DWORD ;------------------------------------------------------------------------------- ; setup the SCSI command block for the operation movmov DrivesLeft, eax, DriveCount .IF (eax) mov esi, OFFSET DriveArray .REPEAT movzx eax, byte ptr [esi] ; pickup the Adapter movzx ebx, byte ptr [esi+1] ; pickup the DeviceID invoke EjectIomegaCartridge, eax, ebx add esi, SIZEOF DWORD dec DrivesLeft .UNTIL (zero?) .ENDIF ret ;------------------------------------------------------------------------------- EjectAllMedia ENDP ;+-----------------------------------------------------------------------------+ ;| GET SPARE SECTOR COUNTS | ;|-----------------------------------------------------------------------------| ;| This returns NON-ZERO if we have trouble and posted the error message | ;| into the RichText control, else it sets the number of spares available | ;+-----------------------------------------------------------------------------+ GetSpareSectorCounts PROC USES ebx, CheckPassword:BOOL LOCAL Scsi[10]:BYTE, DefectHeader:DEFECT_LIST_HEADER, DiskStat[72]:BYTE ;------------------------------------------------------------------------------- ; ask for the defect list to make sure we're able to read it ListChk:varzero Scsi mov Scsi[0], SCSI_Cmd_ReadDefectData mov Scsi[2], 00011110b ; defect format, G/P bits mov Scsi[8], 4 ; ask for only FOUR bytes invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, ADDR DefectHeader, SIZEOF DefectHeader .IF (!eax) || (eax == INCOMPATIBLE_MEDIA) ; we could read its defect list ... so show it! invoke GetNonSenseData, CurrentAdapter, CurrentDevice, DISK_STATUS_PAGE, ADDR DiskStat, SIZEOF DiskStat check eax jnz ListChk ;-------------------------------------------------------------------------- zero ch ; clear the DRIVE_A_SUPPORT .IF (JazDrive) movzx eax, word ptr DiskStat[JAZ_SPARES_COUNT_OFFSET] zero ebx mov cl, DiskStat[JAZ_PROTECT_MODE_OFFSET] mov edx, dword ptr DiskStat[JAZ_LAST_LBA_OFFSET] .ELSE .IF (DiskStat == DISK_STATUS_PAGE) movzx eax, word ptr DiskStat[NEW_ZIP_SIDE_0_SPARES_COUNT_OFFSET] movzx ebx, word ptr DiskStat[NEW_ZIP_SIDE_1_SPARES_COUNT_OFFSET] mov cl, DiskStat[NEW_ZIP_PROTECT_MODE_OFFSET] mov edx, dword ptr DiskStat[NEW_ZIP_LAST_LBA_OFFSET] dec ch ; set the DRIVE_A_SUPPORT .ELSE movzx eax, word ptr DiskStat[OLD_ZIP_SIDE_0_SPARES_COUNT_OFFSET] movzx ebx, word ptr DiskStat[OLD_ZIP_SIDE_1_SPARES_COUNT_OFFSET] mov cl, DiskStat[OLD_ZIP_PROTECT_MODE_OFFSET] mov edx, dword ptr DiskStat[OLD_ZIP_LAST_LBA_OFFSET] .ENDIF check ebx jz NoSpares .ENDIF ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; bswap edx ; save the last LBA in any event ror edx, 8 ; [dabc] swap dh, dl ; [dacb] ror edx, 16 ; [cbda] swap dh, dl ; [cbad] ror edx, 8 ; [dcba] ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ .IF (ch) sub edx, DRIVE_A_SUPPORT_BIAS .ENDIF mov LastLBAOnCartridge, edx swap al, ah ; make it little endian mov Side_0_SparesCount, eax swap bl, bh ; make it little endian mov Side_1_SparesCount, ebx ; compute the number of troubles we encountered during the testing push eax mov eax, Initial_Side_0_Spares sub eax, Side_0_SparesCount add eax, Initial_Side_1_Spares sub eax, Side_1_SparesCount mov FirmErrors, eax pop eax ; check to see whether we have ANY spare sectors remaining .IF (!eax) NoSpares: mov CartridgeStatus, DISK_TEST_FAILURE mov eax, OFFSET szNoSpares ; if were running give them a different error message .IF (TestingPhase) mov eax, OFFSET szOutOfSpares .ENDIF invoke SetRichEditText, ADDR hTabText, eax jmp ErrorExit .ENDIF ; now let's checkout the cartridge's protection mode ... .IF (cl & 07h) && (!(cl & 08h)) && CheckPassword ; if we have some protection mode ; if it's simply WRITE protected ... disable that ... .IF (cl == 2) invoke MessageBox, hMainWnd, ADDR szDisablingWPText, ADDR szDisablingWPTitle, MB_APPLMODAL or MB_OK mov CartridgePassword, 0 ; set pswd to null jmp TempDisablePswd .ENDIF ; we need a password from the operator ... invoke DialogBoxParam, hInst, ADDR szAppName, hMainWnd, ADDR PasswordWndProc, 0 .IF (eax) TempDisablePswd: invoke GetDriveEntryOffset, CurrentAdapter, CurrentDevice push eax ; save the table entry varzero Scsi mov Scsi, SCSI_Cmd_CartProtect mov Scsi[1], 08h ; request temporary unlock mode invoke lstrlen, ADDR CartridgePassword mov Scsi[4], al ; set THIS to the true transfer length pop ebx ; recover the entry offset .IF (dword ptr [ebx] & ODD_BYTE_COMPENSATION) && (al & 1) ; if password is ODD or Scsi[1], 10h ; set the WA bit! inc eax ; and round it up! .ENDIF invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, ADDR CartridgePassword, eax .IF (eax == SCSI_CMD_TIMED_OUT) invoke MessageBox, hMainWnd, ADDR szCantUnlockText, ADDR szCantUnlockTitle, MB_APPLMODAL or MB_OK jmp StillLocked .ENDIF jmp ListChk .ENDIF StillLocked: mov CartridgeStatus, DISK_PROTECTED invoke SetRichEditText, ADDR hTabText, ADDR szLocked jmp ErrorExit .ENDIF zero eax ; return zero since no error ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ .ELSE ; trouble of some sort ... so suppress controls and ; show the richedit control for the trouble .IF (eax == DEFECT_LIST_READ_ERROR) mov CartridgeStatus, DISK_Z_TRACK_FAILURE invoke SetRichEditText, ADDR hTabText, ADDR szDefectList ErrorExit: zero eax dec eax ; return NON-ZERO since we have an error! .ELSEIF (eax == MEDIA_NOT_PRESENT) mov CartridgeStatus, DISK_NOT_PRESENT IF DEVELOPMENT ; .ELSE ; invoke PostToScreen, eax ENDIF .ENDIF .ENDIF Exit: ret ;------------------------------------------------------------------------------- GetSpareSectorCounts ENDP ;+-----------------------------------------------------------------------------+ ;| HANDLE DRIVE CHANGING | ;|-----------------------------------------------------------------------------| ;| If we're NOT in the middle of drive testing, check for any new drives | ;| going ready, eject all others, and Select the newer drive. | ;| If we *ARE* in the middle of drive testing, EJECT any new drive that's | ;| attempting to go ready. | ;+-----------------------------------------------------------------------------+ HandleDriveChanging PROC USES esi edi ebx LOCAL DrivesLeft:DWORD, DiskStat[72]:BYTE, Selecting:BOOL, TroubleFlag:DWORD, PriorStatus:DWORD ;------------------------------------------------------------------------------- test hASPIDLL, -1 ; don't do ANYTHING if we didn't find jz Exit ; the required ASPI goodies in the system ;-------------------------------------------------------------------------- reset Selecting ; true while we're changing selections Rescan: movmov DrivesLeft, eax, DriveCount check eax jz Exit ;-------------------------------------------------------------------------- mov esi, OFFSET DriveArray .REPEAT ; query the current state of the drive .REPEAT ; clear media changed status and dword ptr [esi], NOT MEDIA_CHANGED movzx ebx, byte ptr [esi] ; pickup the Adapter movzx ecx, byte ptr [esi+1] ; pickup the DeviceID invoke GetNonSenseData, ebx, ecx, DISK_STATUS_PAGE, ADDR DiskStat, SIZEOF DiskStat .UNTIL !(dword ptr [esi] & MEDIA_CHANGED) ; do it until NO media change! ;-------------------------------------------------------------------------- .IF (DiskStat == DISK_STATUS_PAGE) movzx eax, DiskStat[NEW_DISK_STATUS_OFFSET] .ELSE movzx eax, DiskStat[OLD_DISK_STATUS_OFFSET] .ENDIF ; if the device we have is NOT the currently selected one movzx ebx, byte ptr [esi] ; pickup the Adapter movzx ecx, byte ptr [esi+1] ; pickup the DeviceID .IF (ebx != CurrentAdapter) || (ecx != CurrentDevice) ; if the disk is ANYTHING other than not present ... .IF (al != DISK_NOT_PRESENT) ; we have a PRESENT DISK in a non-current drive ; if we're testing, reject it .IF (Selecting) || (TestingPhase >= TESTING_STARTUP) invoke EjectIomegaCartridge, ebx, ecx ; flag that we're waiting for spindown or dword ptr [esi], DISK_EJECTING ; if we're not testing, and not awaiting eject ; then set the current drive ... .ELSEIF !(dword ptr [esi] & DISK_EJECTING) mov CurrentAdapter, ebx mov CurrentDevice, ecx reset TestingPhase ; show idle set Selecting jmp Rescan ; the PREVIOUS drive (if any) will be ejected on the next pass .ENDIF .ELSE ; the drive HAS spun down, so clear "waiting" and dword ptr [esi], NOT DISK_EJECTING .ENDIF .ELSE ; we're checking the current drive ... make SURE that ; it is *NOT* empty! If it *IS* empty, kill current .IF (al == DISK_NOT_PRESENT) mov CurrentAdapter, -1 mov CurrentDevice, -1 call SetCartridgeStatusToEAX .ENDIF ; if it's not already set correctly *and* either ; the cart status is one of the pre-test ones, or ; the NEW status from the cart is NOT "at speed" ... .IF (eax != CartridgeStatus) && ((CartridgeStatus <= DISK_STALLED) || (eax != DISK_AT_SPEED)) call SetCartridgeStatusToEAX .ENDIF .ENDIF add esi, SIZEOF DWORD dec DrivesLeft .UNTIL (zero?) ; if nothing was chosen ... set us to "Awaiting Cartridge" status .IF (CurrentAdapter == (-1)) && (CurrentDevice == (-1)) && (al == DISK_NOT_PRESENT) && (CartridgeStatus != DISK_NOT_PRESENT) call SetCartridgeStatusToEAX .ENDIF Exit: ret ;=============================================================================== SetCartridgeStatusToEAX: ;------------------------------------------------------------------------------- reset JazDrive .IF (dword ptr [esi] & JAZ_DRIVE) set JazDrive .ENDIF push eax push esi reset TroubleFlag ; presume everything's okay movmov PriorStatus, ebx, CartridgeStatus mov CartridgeStatus, eax ; set the text of the "action initiate button" .IF (CartridgeStatus == DISK_SPUN_DOWN) ; set the button to "Start Disk Spinning" mov esi, OFFSET szPressToSpin .ELSEIF (CartridgeStatus == DISK_TEST_UNDERWAY) ; set the button to "Stop Testing" mov esi, OFFSET szPressToStop .ELSEIF (CartridgeStatus == DISK_NOT_PRESENT) Ejecting: invoke SetRichEditText, ADDR hTabText, ADDR szNotRunning jmp DisableActions .ELSEIF (CartridgeStatus == DISK_AT_SPEED) invoke GetSpareSectorCounts, TRUE ; update the Cart Condition cmp eax, MEDIA_NOT_PRESENT je Ejecting ;-------------------------------------------------------------------------- mov TroubleFlag, eax ; decide whether we're in trouble mov esi, OFFSET szPressToEject ; presume trouble .IF (!eax) movmov Initial_Side_0_Spares, eax, Side_0_SparesCount movmov Initial_Side_1_Spares, eax, Side_1_SparesCount reset FirmErrors ; check to see if we have enough spares to start .IF (JazDrive) cmp Side_0_SparesCount, MINIMUM_JAZ_SPARES jb InsufficientSpares .ELSE cmp Side_0_SparesCount, MINIMUM_ZIP_SPARES jb InsufficientSpares .IF (Side_1_SparesCount < MINIMUM_ZIP_SPARES) InsufficientSpares: invoke SetRichEditText, ADDR hTabText, ADDR szFewSpares mov CartridgeStatus, DISK_LOW_SPARES set TroubleFlag ; show the richedit control mov esi, OFFSET szPressToProceed jmp EnableTestBtn .ENDIF .ENDIF ; if no trouble, get ready to start testing ... call PrepareToBeginTesting mov esi, OFFSET szPressToStart .ENDIF ; the disk *IS* at speed so enable the action button! EnableTestBtn: invoke EnableWindow, hTestButton, TRUE .IF (eax) mov ActionButtonFlasher, FLASH_COUNT invoke SetFocus, hTestButton .ENDIF .ELSE ; set the button to "One Moment Please" DisableActions: invoke EnableWindow, hTestButton, FALSE mov esi, OFFSET szOneMoment .ENDIF ; set the Window's text based upon setting of esi invoke SetWindowText, hTestButton, esi ; based upon the TroubleFlag, show them the proper page set invoke SetTabErrorMode, TroubleFlag ; and if CartridgeStatus has changed, refresh the entire panel! .IF (CurrentPage == PERFORM_TEST_PAGE) zero ecx .IF (PriorStatus == DISK_AT_SPEED) || (PriorStatus == DISK_SPUN_DOWN) || (PriorStatus >= DISK_LOW_SPARES) not ecx .ENDIF .IF (CartridgeStatus == DISK_AT_SPEED) || (CartridgeStatus >= DISK_LOW_SPARES) not ecx .ENDIF .IF (ecx) ; update the entire panel's data invoke InvalidateRect, hTestMonitor, NULL, FALSE .ELSE ; only paint the new cartridge status invoke GetDC, hTestMonitor mov edi, eax invoke PaintCartStatus, edi invoke ReleaseDC, hTestMonitor, edi .ENDIF ; auto change to the first tab when changing disks. ; .ELSEIF (CurrentPage == EXPLAIN_RESULTS) ; .IF (ebx == DISK_NOT_PRESENT) || (eax == DISK_NOT_PRESENT) ; invoke SendMessage, hActionTabs, TCM_SETCURSEL, 0, NULL ; .IF (eax) ; mov CurrentPage, PERFORM_TEST_PAGE ; mov ActionsSubPage, PERFORM_TEST_PAGE ; call SetCurrentWindow ; .ENDIF ; .ENDIF .ENDIF ;------------------------------------------------------------------------------- pop esi pop eax LocalReturn ;------------------------------------------------------------------------------- HandleDriveChanging ENDP ;+-----------------------------------------------------------------------------+ ;| GET ELAPSED TIME IN SECONDS | ;| Returns seconds elapsed, or -1 if we could not determine it | ;+-----------------------------------------------------------------------------+ GetElapsedTimeInSeconds PROC USES ebx LOCAL CurrentInstant:FILETIME ;------------------------------------------------------------------------------- invoke GetSystemTimeAsFileTime, ADDR CurrentInstant mov eax, dword ptr CurrentInstant mov edx, dword ptr CurrentInstant[4] sub eax, dword ptr StartingInstant sbb edx, dword ptr StartingInstant[4] mov ebx, FILE_TIME_TICKS_PER_SECOND ; verify that the result won't overflow .IF (edx < ebx) roundiv ebx .ELSE mov eax, -1 .ENDIF ret ;------------------------------------------------------------------------------- GetElapsedTimeInSeconds ENDP ;+-----------------------------------------------------------------------------+ ;| PREPARE TO BEGIN TESTING | ;+-----------------------------------------------------------------------------+ PrepareToBeginTesting PROC ;------------------------------------------------------------------------------- ; zero all of the testing variables zero eax mov ecx, TESTING_VARIABLE_COUNT mov edi, OFFSET FirstTestingVariable rep stosd ; catch up the initial spares movmov Initial_Side_0_Spares, eax, Side_0_SparesCount movmov Initial_Side_1_Spares, eax, Side_1_SparesCount reset FirmErrors ; set the flag to enable the "test" button to call "TestTheDisk" mov TestingPhase, READY_TO_TEST ret ;------------------------------------------------------------------------------- PrepareToBeginTesting ENDP ;+-----------------------------------------------------------------------------+ ;| BUMP ERROR COUNTS | ;+-----------------------------------------------------------------------------+ BumpErrorCounts PROC USES ebx, ErrorCode:DWORD ;------------------------------------------------------------------------------- mov eax, ErrorCode ; get the error code IF DEVELOPMENT invoke PostToScreen, eax ENDIF .IF (eax == BUFFER_TOO_BIG) ; if we got BUFFER TOO BIG, halt! set UserInterrupt .ENDIF mov ebx, eax and ebx, 00FF00FFh ; mask off the middle byte .IF (ebx == 00150004h) ; if it was one of the many seek mov eax, ebx ; errors, cvrt to seek error .ENDIF .IF (eax) mov LastError, eax .ENDIF .IF (eax == 320003h) || (eax == 328F03h) mov CartridgeStatus, DISK_LOW_SPARES .ENDIF .IF (al == 1) ; recovered error inc SoftErrors .ELSE inc HardErrors .ENDIF ret ;------------------------------------------------------------------------------- BumpErrorCounts ENDP ;+-----------------------------------------------------------------------------+ ;| READ SECTORS | ;+-----------------------------------------------------------------------------+ ReadSectors PROC pBuffer:LPSTR, LBAtoRead:DWORD, LBACount:DWORD LOCAL Scsi[10]:BYTE ;------------------------------------------------------------------------------- varzero Scsi mov Scsi, SCSI_Cmd_ReadMany mov eax, LBAtoRead ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; bswap eax ; [abcd] ; convert it into BIG endian format ror eax, 8 ; [dabc] swap ah, al ; [dacb] ror eax, 16 ; [cbda] swap ah, al ; [cbad] ror eax, 8 ; [dcba] ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov dword ptr Scsi[2], eax mov eax, LBACount ; and the NUMBER of LBA's to read swap ah, al ; convert it into BIG endian format mov word ptr Scsi[7], ax swap ah, al shl eax, LOG2_BYTES_PER_SECTOR invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, pBuffer, eax ret ;------------------------------------------------------------------------------- ReadSectors ENDP ;+-----------------------------------------------------------------------------+ ;| WRITE SECTORS | ;+-----------------------------------------------------------------------------+ WriteSectors PROC pBuffer:LPSTR, LBAtoWrite:DWORD, LBACount:DWORD LOCAL Scsi[10]:BYTE ;------------------------------------------------------------------------------- varzero Scsi mov Scsi, SCSI_Cmd_WriteMany mov eax, LBAtoWrite ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; bswap eax ; [abcd] ; convert it into BIG endian format ror eax, 8 ; [dabc] swap ah, al ; [dacb] ror eax, 16 ; [cbda] swap ah, al ; [cbad] ror eax, 8 ; [dcba] ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov dword ptr Scsi[2], eax mov eax, LBACount ; and the NUMBER of LBA's to read swap ah, al ; convert it into BIG endian format mov word ptr Scsi[7], ax swap ah, al shl eax, LOG2_BYTES_PER_SECTOR invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, pBuffer, eax ret ;------------------------------------------------------------------------------- WriteSectors ENDP ;-/////////////////////////////////////////////////////////////////////////////- IF COPY_BUTTON_DAMAGES ;-/////////////////////////////////////////////////////////////////////////////- LONG_IO_XFER equ 564 ; (512+52) for Zip drives ; LONG_IO_XFER equ 550 ; (512+38) for Jaz drives FIRST_SECTOR_TO_HURT equ 2246 LAST_SECTOR_TO_HURT equ 100000 SECTOR_HURT_SPACING equ 527 ;+-----------------------------------------------------------------------------+ ;| READ SECTOR LONG | ;+-----------------------------------------------------------------------------+ ReadSectorLong PROC pBuffer:LPSTR, LBAtoRead:DWORD LOCAL Scsi[10]:BYTE ;------------------------------------------------------------------------------- varzero Scsi mov Scsi, SCSI_Cmd_ReadLong mov eax, LBAtoRead ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; bswap eax ; convert it into BIG endian format ror eax, 8 ; [dabc] swap ah, al ; [dacb] ror eax, 16 ; [cbda] swap ah, al ; [cbad] ror eax, 8 ; [dcba] ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov dword ptr Scsi[2], eax mov ax, LONG_IO_XFER swap ah, al mov word ptr Scsi[7], ax ; read LONG number of bytes invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, pBuffer, LONG_IO_XFER ret ;------------------------------------------------------------------------------- ReadSectorLong ENDP ;+-----------------------------------------------------------------------------+ ;| WRITE SECTOR LONG | ;+-----------------------------------------------------------------------------+ WriteSectorLong PROC LBAtoWrite:DWORD, pBuffer:LPSTR LOCAL Scsi[10]:BYTE ;------------------------------------------------------------------------------- varzero Scsi mov Scsi, SCSI_Cmd_WriteLong mov eax, LBAtoWrite ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; bswap eax ; convert it into BIG endian format ror eax, 8 ; [dabc] swap ah, al ; [dacb] ror eax, 16 ; [cbda] swap ah, al ; [cbad] ror eax, 8 ; [dcba] ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov dword ptr Scsi[2], eax mov ax, LONG_IO_XFER swap ah, al mov word ptr Scsi[7], ax ; read LONG number of bytes invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, pBuffer, LONG_IO_XFER ret ;------------------------------------------------------------------------------- WriteSectorLong ENDP ;+-----------------------------------------------------------------------------+ ;| DAMAGE THE DISK | ;+-----------------------------------------------------------------------------+ DamageTheDisk PROC USES esi edi LOCAL IOBuffer[LONG_IO_XFER]:BYTE ;------------------------------------------------------------------------------- mov esi, FIRST_SECTOR_TO_HURT .WHILE (esi <= LAST_SECTOR_TO_HURT) ; first we repair any prior damage invoke ReadSectorLong, ADDR IOBuffer, esi invoke WriteSector, esi, ADDR IOBuffer invoke ReadSectorLong, ADDR IOBuffer, esi ; zap the buffer lea edi, IOBuffer xor dword ptr [edi + 100], 0FFFFFFFFh xor dword ptr [edi + 104], 0FFFFFFFFh xor dword ptr [edi + 108], 0FFFFFFFFh xor dword ptr [edi + 112], 0FFFFFFFFh xor dword ptr [edi + 116], 0FFFFFFFFh xor dword ptr [edi + 120], 0FFFFFFFFh xor dword ptr [edi + 124], 0FFFFFFFFh xor dword ptr [edi + 128], 0FFFFFFFFh xor dword ptr [edi + 132], 0FFFFFFFFh xor dword ptr [edi + 136], 0FFFFFFFFh invoke WriteSectorLong, esi, ADDR IOBuffer ; invoke ReadSector, ADDR IOBuffer, esi add esi, SECTOR_HURT_SPACING .ENDW ret ;------------------------------------------------------------------------------- DamageTheDisk ENDP ;-/////////////////////////////////////////////////////////////////////////////- ENDIF ;-/////////////////////////////////////////////////////////////////////////////- ;+-----------------------------------------------------------------------------+ ;| OPEN DRIVE ACCESS | ;|-----------------------------------------------------------------------------| ;| Returns TRUE on success | ;+-----------------------------------------------------------------------------+ OpenDriveAccess PROC DriveNumber:DWORD ;------------------------------------------------------------------------------- invoke SetErrorMode, SEM_FAILCRITICALERRORS push eax ; stack the original error mode ... ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov eax, DriveNumber inc eax mov OneBasedDrive, eax add al, 'A'-1 ; cvrt 0-based drive into capital letter mov DriveLetter, al mov DriveRootName, al .IF (WinNT) invoke CreateFile, ADDR DiskDrive, GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH or FILE_FLAG_NO_BUFFERING, NULL .ELSE invoke CreateFile, ADDR SystemVXD, GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH or FILE_FLAG_NO_BUFFERING, NULL .ENDIF mov hDriveAccess, eax call SetErrorMode ; restore the original error mode ... mov eax, hDriveAccess .IF (eax == INVALID_HANDLE_VALUE) zero eax .ENDIF ret ;------------------------------------------------------------------------------- OpenDriveAccess ENDP ;+-----------------------------------------------------------------------------+ ;| CLOSE DRIVE ACCESS | ;+-----------------------------------------------------------------------------+ CloseDriveAccess PROC ;------------------------------------------------------------------------------- mov eax, hDriveAccess .IF (eax) invoke CloseHandle, eax .ENDIF ret ;------------------------------------------------------------------------------- CloseDriveAccess ENDP ;+-----------------------------------------------------------------------------+ ;| PERFORM DEVICE IO | ;|-----------------------------------------------------------------------------| ;| Returns ZERO on success, and ERROR CODE on failure | ;+-----------------------------------------------------------------------------+ Win95DeviceIO PROC USES esi, _eax:DWORD, _ebx:DWORD, _ecx:DWORD, _edx:DWORD, _esi:DWORD, _edi:DWORD LOCAL Registers:DEVIOCTL_REGISTERS, BytesReturned:DWORD ;------------------------------------------------------------------------------- movmov Registers.reg_EAX, eax, _eax .IF (ah == 44h) mov esi, VWIN32_DIOC_DOS_IOCTL ; (1) .ELSEIF (ah == 25h) mov esi, VWIN32_DIOC_DOS_INT25 ; (2) .ELSEIF (ah == 26h) mov esi, VWIN32_DIOC_DOS_INT26 ; (3) .ELSEIF (ah == 73h) mov esi, VWIN32_DIOC_DOS_DRIVEINFO ; (6) .ELSE zero eax jmp Exit .ENDIF movmov Registers.reg_EBX, eax, _ebx movmov Registers.reg_ECX, eax, _ecx movmov Registers.reg_EDX, eax, _edx movmov Registers.reg_ESI, eax, _esi movmov Registers.reg_EDI, eax, _edi mov Registers.reg_Flags, 1 ; preset the carry invoke DeviceIoControl, hDriveAccess, esi, ; non-zero on success ADDR Registers, SIZEOF DEVIOCTL_REGISTERS, ADDR Registers, SIZEOF DEVIOCTL_REGISTERS, ADDR BytesReturned, NULL .IF (eax) ; DeviceIoControl succeeded, but what about the called func? zero eax ; presume success (no carry set) .IF (Registers.reg_Flags & 1) ; return was CY=1, so return the function's error val mov eax, Registers.reg_EAX .ENDIF .ELSE ; DeviceIoControl failed, so return -1 mov eax, -1 .ENDIF Exit: ret ;------------------------------------------------------------------------------- Win95DeviceIO ENDP ;+-----------------------------------------------------------------------------+ ;| QUALIFY DRIVE | ;|-----------------------------------------------------------------------------| ;| Returns TRUE if this drive might be a Local Removable Iomega | ;+-----------------------------------------------------------------------------+ QualifyDrive PROC LOCAL BytesReceived:DWORD, DriveMapInfo:DRIVE_MAP_INFO, MediaID:MEDIA_ID, DiskGeometry:DISK_GEOMETRY, PartitionInfo:PARTITION_INFORMATION ;------------------------------------------------------------------------------- ; ask Windows what it knows about the drive invoke GetDriveType, ADDR DriveRootName cmp eax, DRIVE_REMOVABLE ; if it's NOT removable or FIXED jb Disqualify ; then we're out of the game. cmp eax, DRIVE_FIXED ; but it COULD be removable MASQUERADING ja Disqualify ; as fixed ... so we need to check that! ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Windows says it's fixed or removable ... let's look closer ... .IF (WinNT) ; check to see if the media is accessible invoke DeviceIoControl, hDriveAccess, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, ADDR BytesReceived, NULL check eax ; returns FALSE if it's NOT accessible jz Exit ;-------------------------------------------------------------------------- invoke DeviceIoControl, hDriveAccess, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, ADDR DiskGeometry, SIZEOF DiskGeometry, ADDR BytesReceived, NULL mov eax, DiskGeometry.MediaType cmp eax, RemovableMedia jne Disqualify ;-------------------------------------------------------------------------- invoke DeviceIoControl, hDriveAccess, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, ADDR DiskGeometry, SIZEOF PartitionInfo, ADDR PartitionInfo, NULL movmov PartitionOffset, eax, dword ptr PartitionInfo.StartingOffset .ELSE ; look more closely at a status that ISN'T foolable mov DriveMapInfo.dmiAllocationLength, SIZEOF DRIVE_MAP_INFO invoke Win95DeviceIO, 440Dh, OneBasedDrive, 086Fh, ADDR DriveMapInfo,0,0 check eax ; returns an error code if the call failed jnz Disqualify movmov PartitionOffset, ecx, dword ptr DriveMapInfo.dmiPartitionStartRBA ;-------------------------------------------------------------------------- ; we found a drive, so see if it supports EJECT! ; if not if can't be an IOMEGA removable drive .IF (DriveMapInfo.dmiFlags & PROT_MODE_EJECT) ; yes, now can we read its MediaID ?? invoke Win95DeviceIO, 440Dh, OneBasedDrive, 0866h, ADDR MediaID,0,0 .IF (eax) ; MediaID failed, so fail our function Disqualify: zero eax .ELSE ; this looks like a PERFECT candidate ... dec eax ; set EAX to -1 for success! .ENDIF .ENDIF .ENDIF Exit: ret ;------------------------------------------------------------------------------- QualifyDrive ENDP ;+-----------------------------------------------------------------------------+ ;| EXCLUSIVE ACCESS | ;|-----------------------------------------------------------------------------| ;| Returns TRUE for Success | ;+-----------------------------------------------------------------------------+ ExclusiveAccess PROC DriveNumber:DWORD, Exclusive:BOOL LOCAL BytesReceived ;------------------------------------------------------------------------------- .IF (DriveNumber != (-1)) .IF (Exclusive) invoke OpenDriveAccess, DriveNumber .IF (WinNT) invoke DeviceIoControl, hDriveAccess, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, ADDR BytesReceived, NULL .ELSE invoke Win95DeviceIO, 440Dh, OneBasedDrive, 084Ah, 0,0,0 .IF (eax) ; MediaID failed, so fail our function zero eax .ELSE ; this looks like a PERFECT candidate ... dec eax ; set EAX to -1 for success! .ENDIF .ENDIF .ELSE .IF (WinNT) invoke DeviceIoControl, hDriveAccess, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, ADDR BytesReceived, NULL .ELSE invoke Win95DeviceIO, 440Dh, OneBasedDrive, 086Ah, 0,0,0 .IF (eax) ; MediaID failed, so fail our function zero eax .ELSE ; this looks like a PERFECT candidate ... dec eax ; set EAX to -1 for success! .ENDIF .ENDIF push eax call CloseDriveAccess pop eax .ENDIF .ENDIF ret ;------------------------------------------------------------------------------- ExclusiveAccess ENDP ;+-----------------------------------------------------------------------------+ ;| FILL BUFFER THROUGH ASPI | ;+-----------------------------------------------------------------------------+ FillBufferThroughASPI PROC LOCAL Scsi[10]:BYTE ;------------------------------------------------------------------------------- varzero Scsi mov Scsi, SCSI_Cmd_ReadMany mov eax, PartitionOffset ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; bswap eax ; convert it into BIG endian format ror eax, 8 ; [dabc] swap ah, al ; [dacb] ror eax, 16 ; [cbda] swap ah, al ; [cbad] ror eax, 8 ; [dcba] ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov dword ptr Scsi[2], eax ; specify WHICH LBA's to read mov Scsi[8], COMPARISON_SECTORS invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, pTargetBuffer, COMPARISON_BYTES ret ;------------------------------------------------------------------------------- FillBufferThroughASPI ENDP ;+-----------------------------------------------------------------------------+ ;| READ PHYSICAL SECTORS | ;|-----------------------------------------------------------------------------| ;| Returns TRUE if it succeeds | ;+-----------------------------------------------------------------------------+ ReadPhysicalSectors PROC Sector:DWORD, Count:WORD, pBuffer:LPVOID LOCAL DiskIO:DISKIO, BytesRead:DWORD ;------------------------------------------------------------------------------- ; we must first read the sector containing the current Serial Number .IF (WinNT) mov eax, Sector ; get the starting sector number shl eax, 9 ; multiply by 512 for starting BYTE invoke SetFilePointer, hDriveAccess, eax, 0, FILE_BEGIN movzx ebx, Count ; get the total sector count shl ebx, 9 ; multiply by 512 for total bytes to read invoke ReadFile, hDriveAccess, pBuffer, ebx, ADDR BytesRead, NULL .ELSE ; setup the data transfer packet movmov DiskIO.StartSector, eax, Sector movmov DiskIO.Sectors, ax, Count movmov DiskIO.Buffer, eax, pBuffer ; first try an Int25 (read) call ... for build 950 mov ecx, OneBasedDrive dec ecx ; cvrt into 0-baed drive number mov ch, 25h ; perform INT_25 direct access read invoke Win95DeviceIO, ecx, ADDR DiskIO, -1, 0, 0, 0 check eax ; zero means we succeeded! jz Success ;-------------------------------------------------------------------------- ; for SR2 and later invoke Win95DeviceIO, 7305h, ADDR DiskIO, -1, OneBasedDrive, 0,0 .IF (eax) ; non-zero means we failed, so return FALSE zero eax .ELSE ; zero means we succeeded ... to return -1 Success: dec eax ; set EAX to -1 for success! .ENDIF .ENDIF ret ;------------------------------------------------------------------------------- ReadPhysicalSectors ENDP ;+-----------------------------------------------------------------------------+ ;| COMPARE BUFFERS | ;|-----------------------------------------------------------------------------| ;| Returns TRUE if the buffers are identical | ;+-----------------------------------------------------------------------------+ CompareBuffers PROC USES esi edi ;------------------------------------------------------------------------------- zero eax mov ecx, COMPARISON_BYTES / 4 mov esi, pTargetBuffer mov edi, pSearchBuffer repe cmpsd .IF (zero?) dec eax .ENDIF ret ;------------------------------------------------------------------------------- CompareBuffers ENDP ;+-----------------------------------------------------------------------------+ ;| FIND DRIVE LETTER | ;|-----------------------------------------------------------------------------| ;| Returns the Zero-Based DOS drive letter, or -1 if not found | ;+-----------------------------------------------------------------------------+ FindDriveLetter PROC LOCAL DriveNumber:DWORD ;------------------------------------------------------------------------------- ; allocate two page-aligned read buffers for source disk reads invoke VirtualAlloc, NULL, COMPARISON_BYTES, MEM_COMMIT, PAGE_READWRITE mov pTargetBuffer, eax invoke VirtualAlloc, NULL, COMPARISON_BYTES, MEM_COMMIT, PAGE_READWRITE mov pSearchBuffer, eax ;-------------------------------------------------------------------------- ; now search the drive letter space for the same data on a valid drive ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov DriveNumber, 'Z'-'A' ; start at "Z" and work downward ... .REPEAT invoke OpenDriveAccess, DriveNumber call QualifyDrive ; returns true if it might be .IF (eax) ; read the partition's first 8 sectors through the ASPI interface AbsoluteRead: call FillBufferThroughASPI .IF (!eax) ; now read the sectors through the device driver interface invoke ReadPhysicalSectors, 0, COMPARISON_SECTORS, pSearchBuffer .IF (eax) ; got a TRUE return, so compare'em call CompareBuffers ; returns TRUE for equality .IF (eax) ; yipeee! we found the drive! ... call CloseDriveAccess jmp Exit .ELSEIF (PartitionOffset == 0) add PartitionOffset, 20h jmp AbsoluteRead .ENDIF .ENDIF .ENDIF .ENDIF call CloseDriveAccess dec DriveNumber .UNTIL (sign?) ; 0-based, so this is past the end ; if DriveNumber == -1, we never found the drive Exit: invoke VirtualFree, pTargetBuffer, 0, MEM_RELEASE invoke VirtualFree, pSearchBuffer, 0, MEM_RELEASE mov eax, DriveNumber ret ;------------------------------------------------------------------------------- FindDriveLetter ENDP ;+-----------------------------------------------------------------------------+ ;| EJECT IOMEGA CARTRIDGE | ;+-----------------------------------------------------------------------------+ EjectIomegaCartridge PROC Adapter:DWORD, Device:DWORD LOCAL UnlockParams:DWORD, BytesReceived:DWORD LOCAL Scsi[6]:BYTE ;------------------------------------------------------------------------------- push CurrentAdapter push CurrentDevice movmov CurrentAdapter, eax, Adapter movmov CurrentDevice, eax, Device call FindDriveLetter .IF (eax <= 'Z'-'A') invoke OpenDriveAccess, eax .IF (WinNT) invoke DeviceIoControl, hDriveAccess, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, ADDR BytesReceived, NULL .ELSE mov UnlockParams, 1 invoke Win95DeviceIO, 440Dh, OneBasedDrive, 0848h, ADDR UnlockParams,0,0 invoke Win95DeviceIO, 440Dh, OneBasedDrive, 0849h, 0,0,0 .ENDIF invoke CloseDriveAccess .ELSE ; Could NOT do it through the IOCTL layer ... so eject with SCSI ; make sure the media is not locked ... varzero Scsi mov Scsi, SCSI_Cmd_PreventAllow invoke SCSICommand, Adapter, Device, ADDR Scsi, NULL, 0 ; issue an Asynchronous STOP command to induce spindown and ejection varzero Scsi mov Scsi[0], SCSI_Cmd_StartStopUnit mov Scsi[1], 1 ; set the IMMED bit for offline mov Scsi[4], 2 ; eject a Jaz disk after stopping invoke SCSICommand, Adapter, Device, ADDR Scsi, NULL, 0 .ENDIF pop CurrentDevice pop CurrentAdapter ret ;------------------------------------------------------------------------------- EjectIomegaCartridge ENDP ;+-----------------------------------------------------------------------------+ ;| PERFORM REGION TRANSFER | ;+-----------------------------------------------------------------------------+ PerformRegionTransfer PROC USES ebx, XferCmd:DWORD, pBuffer:DWORD LOCAL Scsi[10]:BYTE, LocalBuffer:DWORD, LocalCount:DWORD, InitialHardErrors:DWORD, GlitchCount:DWORD, GlitchError:DWORD ;------------------------------------------------------------------------------- reset SingleTransferLBA ; show we're testing a range ... movmov InitialHardErrors, eax, HardErrors varzero Scsi ; clear out the SCSI CDB movmov Scsi, al, byte ptr XferCmd ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov eax, FirstLBASector ; setup the first block to read ; bswap eax ; convert it into BIG endian format ror eax, 8 ; [dabc] swap ah, al ; [dacb] ror eax, 16 ; [cbda] swap ah, al ; [cbad] ror eax, 8 ; [dcba] mov dword ptr Scsi[2], eax ; specify WHICH LBA's to read ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov eax, NumberOfLBAs ; and the NUMBER of LBA's to read swap ah, al ; convert it into BIG endian format mov word ptr Scsi[7], ax ; invoke SetErrorRecovery, FALSE, FALSE, TRUE ; disable Retries & ECC invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, pBuffer, AdapterMaxBytes ; if we failed somewhere during our transfer ... let's zero in on it .IF (eax) cmp eax, SS_ERR ; if it's a CONTROLLER ERROR, skip! je Exit cmp eax, BUFFER_TOO_BIG ; if we need to reduce the size je Exit cmp eax, LBA_TOO_LARGE je Exit ;-------------------------------------------------------------------------- mov GlitchError, eax ; save the error which stopped us! mov eax, SoftErrors ; get the current Soft + Hard Error count add eax, HardErrors mov GlitchCount, eax ; save it to see if we do FIND the glitch ... movmov SingleTransferLBA, eax, FirstLBASector call UpdateCurrentSector ; show what we're doing ... movmov LocalBuffer, eax, pBuffer movmov LocalCount, eax, AdapterMaxSectors call ErrorSound .REPEAT ; setup for our series of transfer tests ... varzero Scsi ; clear out the SCSI CDB movmov Scsi, al, byte ptr XferCmd ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov eax, SingleTransferLBA ; the single sector to xfer ; bswap eax ; convert it into BIG endian format ror eax, 8 ; [dabc] swap ah, al ; [dacb] ror eax, 16 ; [cbda] swap ah, al ; [cbad] ror eax, 8 ; [dcba] mov dword ptr Scsi[2], eax ; specify WHICH single LBA to read ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov Scsi[8], 1 ; a single sector ; disable all recovery techniques invoke SetErrorRecovery, FALSE, FALSE, TRUE ; disable Retries & ECC invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, LocalBuffer, BYTES_PER_SECTOR .IF (eax) ; some sort of problem encountered! cmp eax, SS_ERR ; if it's a CONTROLLER ERROR, skip! je Exit ;-------------------------------------------------------------------------- cmp al, 1 ; did we recover? je PostTheError ;-------------------------------------------------------------------------- invoke SetErrorRecovery, TRUE, FALSE, TRUE ; enable retries invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, LocalBuffer, BYTES_PER_SECTOR .IF (eax) ; failed even with retries cmp eax, SS_ERR ; if it's a CONTROLLER ERROR, skip! je Exit ;-------------------------------------------------------------------------- cmp al, 1 ; did we recover? je PostTheError ;-------------------------------------------------------------------------- invoke SetErrorRecovery, TRUE, TRUE, TRUE ; enable retries AND ECC invoke SCSICommand, CurrentAdapter, CurrentDevice, ADDR Scsi, LocalBuffer, BYTES_PER_SECTOR .IF (eax) ; failed with retries and ECC cmp eax, SS_ERR ; if it's a CONTROLLER ERROR, skip! je Exit .ELSE ; succeeded with ECC mov eax, 180101h ; "ECC & Retries" .ENDIF .ELSE ; succeeded using retries mov eax, 170101h ; "Read with Retries" .IF (XferCmd == SCSI_Cmd_WriteMany) mov eax, 0C8001h ; "Wrote with Retries" .ENDIF .ENDIF ;-------------------------------------------------------------------------- PostTheError: invoke BumpErrorCounts, eax ; given eax, count the errors invoke GetSpareSectorCounts, FALSE ; update the Cart's Condition call UpdateRunTimeDisplay .ENDIF inc SingleTransferLBA call UpdateCurrentSector add LocalBuffer, BYTES_PER_SECTOR call ProcessPendingMessages dec LocalCount .UNTIL (zero?) || (UserInterrupt) ; now see whether we *did* found something to complain about ... mov eax, SoftErrors add eax, HardErrors .IF (eax == GlitchCount) ; we missed it ... but SOMETHING happened! So let's report it ... push SoftErrors ; save the existing counts push HardErrors mov eax, GlitchError ; get the error that triggered our search mov ebx, eax and ebx, 00FF00FFh ; strip the ASCQ byte .IF (ebx == 00110003h) ; if we're about to say "unrecovered read" mov eax, 170101h ; change it to: "Read with Retries" .ENDIF invoke BumpErrorCounts, eax ; given eax, count the errors pop HardErrors ; restore the counts pop SoftErrors inc SoftErrors ; and bump the SOFT error count call UpdateRunTimeDisplay .ENDIF reset SingleTransferLBA zero eax ; now let's return happiness to our caller mov ebx, HardErrors ; UNLESS we had some .IF (ebx != InitialHardErrors) ; UNRECOVERABLE errors! dec eax ; in which case return -1 .ENDIF .ENDIF ; restore standard error recovery modes Exit: push eax invoke SetErrorRecovery, TRUE, TRUE, FALSE ; reenable Retries & ECC pop eax ret ;------------------------------------------------------------------------------- PerformRegionTransfer ENDP ;+-----------------------------------------------------------------------------+ ;| TEST THE DISK | ;+-----------------------------------------------------------------------------+ TestTheDisk PROC LOCAL TransferLength:DWORD, DataPattern:DWORD, DriveNumber:DWORD, BlastRecoveryMode:BOOL ;------------------------------------------------------------------------------- ; USB seems to generate insane WM_TIMER messages, so we now stop ; the timer during data read/write processing. call StopApplicationTimer ; determine whether to show "Unknown Error" or the Hex code ... invoke GetAsyncKeyState, VK_SHIFT shl eax, 1 ; move the high bit into CY .IF (carry?) set UnknownErrorMode .ENDIF ; determine which drive to lock call FindDriveLetter ; returns drive letter, or -1 if not found mov DriveNumber, eax .IF (eax == (-1)) mov al, "?" .ELSE invoke ExclusiveAccess, eax, TRUE .IF (!eax) ; could NOT succeed in locking the drive! mov al, byte ptr DriveNumber add al, 'A' mov szExclusiveDrive1, al mov szExclusiveDrive2, al invoke MessageBox, hMainWnd, ADDR szNoExclusiveText, ADDR szNoExclusiveTitle, MB_APPLMODAL or MB_OK ; allow operation on an unlocked drive! invoke GetAsyncKeyState, VK_SHIFT shl eax, 1 ; move the high bit into CY jnc Exit .ENDIF mov al, byte ptr DriveNumber add al, 'A' .ENDIF mov DriveUnderTest, al ;=============================================================================== call PreventProgramExit invoke SetRichEditText, ADDR hTabText, ADDR szRunning mov CartridgeStatus, DISK_TEST_UNDERWAY mov TestingPhase, TESTING_STARTUP ; inhibit stopping now invoke SetWindowText, hTestButton, ADDR szPressToStop invoke InvalidateRect, hTestMonitor, NULL, FALSE call LockCurrentDrive ; prevent media removal ; setup the initial maximum transfer ... invoke HostAdapterInquiry, CurrentAdapter mov eax, dword ptr ASPI_CmdBlock.hai.HA_Unique[4] ; get the max Xfer .IF (!eax) || (eax > MAX_TRANSFER_PER_TEST) ; limit to our max mov eax, MAX_TRANSFER_PER_TEST .ENDIF SizeIt: mov AdapterMaxBytes, eax shr eax, 9 ; divide by Bytes/Sector mov AdapterMaxSectors, eax ;----------------------- Check for "Blast Recovery Mode" ----------------------- call CheckForPartitionTable ; returns NON-ZERO if partition table was NOT found .IF (eax) call PerhapsBlastRecovery ; returns NON-ZERO if BlastRecovery *was* performed ... .ENDIF mov BlastRecoveryMode, eax check eax jnz GetOut IF FORCE_STARTING_SECTOR mov FirstLBASector, FORCE_STARTING_SECTOR ; and the starting point ENDIF ;------------------------- Standard Testing Operation -------------------------- ; log the starting time invoke GetSystemTimeAsFileTime, ADDR StartingInstant .REPEAT ; compute the number of LBS and Last LBA mov eax, LastLBAOnCartridge ; get the maximum LBA count sub eax, FirstLBASector ; and the starting point inc eax ; number of sectors to go .IF (eax > AdapterMaxSectors) mov eax, AdapterMaxSectors ; trim it to the sector count .ENDIF mov NumberOfLBAs, eax ; compute the LastLBA add eax, FirstLBASector ; get just past the end dec eax ; get the last one mov LastLBASector, eax ; compute the percentage complete mov edx, FirstLBASector zero eax div LastLBAOnCartridge mov PercentComplete, eax ; uppdate the elapsed time call GetElapsedTimeInSeconds .IF (eax != (-1)) mov SecondsElapsed, eax .ENDIF ; get a random pattern of data to write call GetRandomNumber mov DataPattern, eax mov ecx, AdapterMaxBytes shr ecx, 2 mov edi, pPatternBuffer rep stosd ; update the cartridge's status invoke GetSpareSectorCounts, FALSE ; update the Cart's Condition mov TestingPhase, READING_DATA call UpdateRunTimeDisplay invoke PerformRegionTransfer, SCSI_Cmd_ReadMany, pUserDataBuffer .IF (!eax) ;-------------------------------------------------------------------------- mov TestingPhase, WRITING_PATT invoke UpdateRunPhaseDisplay invoke PerformRegionTransfer, SCSI_Cmd_WriteMany, pPatternBuffer ;-------------------------------------------------------------------------- mov TestingPhase, READING_PATT invoke UpdateRunPhaseDisplay invoke PerformRegionTransfer, SCSI_Cmd_Verify, pPatternBuffer ;-------------------------------------------------------------------------- mov TestingPhase, WRITING_DATA invoke UpdateRunPhaseDisplay invoke PerformRegionTransfer, SCSI_Cmd_WriteMany, pUserDataBuffer ;-------------------------------------------------------------------------- .ELSEIF (eax == BUFFER_TOO_BIG) ; we need to make it smaller mov eax, AdapterMaxBytes ; get the current size halve eax jmp SizeIt .ENDIF ; if we hit the end of the disk ... exit gracefully! cmp eax, LBA_TOO_LARGE je GetOut ; see if everything's still okay ... .BREAK .IF (CartridgeStatus != DISK_TEST_UNDERWAY) ; bump the FirstLBASector up for the next transfer mov eax, FirstLBASector add eax, NumberOfLBAs mov FirstLBASector, eax .UNTIL (eax > LastLBAOnCartridge) || (UserInterrupt) ; show that we're post-test GetOut: reset TestingPhase call UnlockAllMedia invoke SetErrorRecovery, TRUE, TRUE, FALSE ; reenable Retries & ECC invoke SetWindowText, hTestButton, ADDR szPressToStart mov CartridgeStatus, DISK_AT_SPEED call AllowProgramExit ; compute the number of serious troubles mov eax, FirmErrors add eax, HardErrors .IF (eax >= BADNESS_THRESHOLD) mov eax, OFFSET szBadResult .ELSEIF (UserInterrupt) mov eax, OFFSET szInterrupted .ELSE ; it wasn't interrupted, nor seriously bad, was it perfect? add eax, SoftErrors .IF (eax) mov eax, OFFSET szExplainResult .ELSE mov eax, OFFSET szPerfectResult .ENDIF .ENDIF invoke SetRichEditText, ADDR hTabText, eax invoke InvalidateRect, hTestMonitor, NULL, FALSE ; release any exclusive filesystem lock we may have on the drive Exit: invoke ExclusiveAccess, DriveNumber, FALSE ; if we've just un-zapped the disk ... let's eject it! .IF (BlastRecoveryMode) invoke EjectIomegaCartridge, CurrentAdapter, CurrentDevice .ENDIF ; we're done (with USB'ness) so it's safe to restart the timer call StartApplicationTimer ret ;------------------------------------------------------------------------------- TestTheDisk ENDP ;+-----------------------------------------------------------------------------+ ;| CHECK FOR PARTITION TABLE | ;| Return NON-ZERO if partition table was NOT found! | ;+-----------------------------------------------------------------------------+ CheckForPartitionTable PROC USES ebx LOCAL Return:DWORD, ShiftCount:DWORD ;------------------------------------------------------------------------------- reset Return ; determine how many shift keys we're holding down ... reset ShiftCount invoke GetAsyncKeyState, VK_SHIFT shl eax, 1 ; move the high bit into CY .IF (carry?) inc ShiftCount .ENDIF invoke GetAsyncKeyState, VK_CONTROL shl eax, 1 ; move the high bit into CY .IF (carry?) inc ShiftCount .ENDIF invoke GetAsyncKeyState, VK_MENU shl eax, 1 ; move the high bit into CY .IF (carry?) inc ShiftCount .ENDIF ; allocate a page-aligned read buffer for disk read invoke GlobalAlloc, GPTR, BYTES_PER_SECTOR mov ebx, eax ; read the cartridge's partition table ... invoke ReadSectors, ebx, 0, 1 ; only if we got a good table read *AND* it's not a partition table .IF (ShiftCount == 3) || ((!eax) && (word ptr [ebx+01FEh] != 0AA55h)) set Return .ENDIF invoke GlobalFree, ebx mov eax, Return ret ;------------------------------------------------------------------------------- CheckForPartitionTable ENDP ;+-----------------------------------------------------------------------------+ ;| PERHAPS BLAST RECOVERY | ;|-----------------------------------------------------------------------------| ;| No partition table was found ... so give them the option of performing | ;| a "blast recovery". Return TRUE if they took the option. | ;+-----------------------------------------------------------------------------+ PerhapsBlastRecovery PROC USES ebx esi LOCAL StartOfFAT1:DWORD, StartOfFAT2:DWORD, SectorsToCopy:DWORD ;------------------------------------------------------------------------------- ; update the LBA count invoke GetSpareSectorCounts, FALSE ; update the Cart's Condition ; get the drive index {0-3} mov eax, LastLBAOnCartridge zero ebx .WHILE (LBAMidPoints[ebx*4] < eax) && (ebx < 3) inc ebx .ENDW ; patch the cartridge size into the mesage box text ; get the starting address of the name strings lea esi, IomegaDeviceNames[ebx*8] movmov dword ptr CartSize1[0], eax, [esi] movmov dword ptr CartSize2[0], eax, [esi] movmov dword ptr CartSize3[0], eax, [esi] movmov dword ptr CartSize4[0], eax, [esi] movmov dword ptr CartSize5[0], eax, [esi] movmov dword ptr CartSize1[4], eax, [esi+4] movmov dword ptr CartSize2[4], eax, [esi+4] movmov dword ptr CartSize3[4], eax, [esi+4] movmov dword ptr CartSize4[4], eax, [esi+4] movmov dword ptr CartSize5[4], eax, [esi+4] invoke MessageBox, hMainWnd, ADDR szPerhapsBlastText, ADDR szPerhapsBlastTitle, MB_APPLMODAL or MB_OKCANCEL sub eax, IDCANCEL jz Exit ;=================== Restore the cartridge's Partition Table =================== ; make the offset to the drive's partition sector data shl ebx, 10 ; multiply the drive index by 1024 add ebx, pSectorData invoke WriteSectors, ebx, 0, 1 check eax jnz Trouble ; get the number of sectors per track for the LBA offset of the ; partition boot sector ... mov eax, [ebx+01F6h] ; eax == 32 or 63 mov StartOfFAT1, eax inc StartOfFAT1 ; save the LBA sector for FAT1 ;===================== Restore the Cartridge's Boot Sector ===================== add ebx, BYTES_PER_SECTOR ; skip up to the Boot Sector data invoke WriteSectors, ebx, eax, 1 ; and send the boot sector out check eax jnz Trouble ;======================= Calc the FAT recovery variables ======================= mov eax, MAX_SECTORS_PER_TEST sub eax, StartOfFAT1 mov SectorsToCopy, eax movzx eax, word ptr [ebx + BPB_OFFSET + SECTORS_PER_FAT] add eax, StartOfFAT1 invoke ReadSectors, pUserDataBuffer, eax, SectorsToCopy check eax jnz Trouble invoke WriteSectors, pUserDataBuffer, StartOfFAT1, SectorsToCopy .IF (eax) Trouble: invoke MessageBox, hMainWnd, ADDR szBlastTroubleText, ADDR szBlastTroubleTitle, MB_APPLMODAL or MB_OK .ELSE invoke MessageBox, hMainWnd, ADDR szBlastSuccessText, ADDR szBlastSuccessTitle, MB_APPLMODAL or MB_OK .ENDIF zero eax dec eax Exit: ret ;------------------------------------------------------------------------------- PerhapsBlastRecovery ENDP