/***************************************************************************** * * COPYRIGHT 1993 - COLORADO MEMORY SYSTEMS, INC. * COPYRIGHT 1997 - COLORADO SOFTWARE ARCHITECTS, INC. * * ALL RIGHTS RESERVED. * ****************************************************************************** * * FUNCTION: cqd_LocateDevice * * PURPOSE: * *****************************************************************************/ #ifdef NO_INC_LLP64 typedef unsigned int UINT_PTR; typedef int INT_PTR; typedef UINT_PTR ULONG_PTR; typedef INT_PTR LONG_PTR; #endif #include #include #include "q117_dat.h" /*endinclude*/ #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,cqd_LocateDevice) #pragma alloc_text(PAGE,cqd_InitializeContext) #pragma alloc_text(PAGE,cqd_LookForDevice) #pragma alloc_text(PAGE,cqd_SendPhantomSelect) #pragma alloc_text(PAGE,cqd_CmdSelectDevice) #pragma alloc_text(PAGE,cqd_CmdDeselectDevice) #pragma alloc_text(PAGE,cqd_ConnerPreamble) #pragma alloc_text(PAGE,cqd_DeselectDevice) #pragma alloc_text(PAGE,cqd_SendByte) #pragma alloc_text(PAGE,cqd_ReceiveByte) #pragma alloc_text(PAGE,cqd_GetDeviceError) #pragma alloc_text(PAGE,cqd_WaitActive) #pragma alloc_text(PAGE,cqd_GetStatus) #pragma alloc_text(PAGE,cqd_WaitCommandComplete) #pragma alloc_text(PAGE,cqd_Report) #pragma alloc_text(PAGE,cqd_ClearTapeError) #pragma alloc_text(PAGE,cqd_ResetFDC) #pragma alloc_text(PAGE,cqd_ConfigureFDC) #pragma alloc_text(PAGE,cqd_DCROut) #pragma alloc_text(PAGE,cqd_DSROut) #pragma alloc_text(PAGE,cqd_TDROut) #pragma alloc_text(PAGE,cqd_IssueFDCCommand) #pragma alloc_text(PAGE,kdi_Error) #pragma alloc_text(PAGE,kdi_GetErrorType) #pragma alloc_text(PAGE,kdi_ReportAbortStatus) #pragma alloc_text(PAGE,kdi_GetSystemTime) #pragma alloc_text(PAGE,kdi_GetInterfaceType) // #pragma alloc_text(PAGE,kdi_CheckedDump) #pragma alloc_text(PAGE,kdi_Sleep) #pragma alloc_text(PAGE,kdi_FdcDeviceIo) #endif #define FCT_ID 0x11017 NTSTATUS cqd_LocateDevice( IN PVOID context, IN PUCHAR vendor_detected ) /* COMMENTS: ***************************************************************** * * The drive search must be done in a specific order. Drives which use only a * hardware select scheme must be searched first. If they are not, some of * them will simulate another manufacturers drive based on the SW select they * receive. In many cases this simulation is incomplete and must not be used. * Whenever possible, an attempt must be made to select a drive in it's native * mode. * * DEFINITIONS: *************************************************************/ { NTSTATUS status; // NTSTATUS or error condition. USHORT i; // loop variable CqdContextPtr cqd_context; BOOLEAN found = FALSE; USHORT ven; UCHAR drv; USHORT seq; cqd_context = (CqdContextPtr)context; for ( i = 0; i < FIND_RETRIES && !found; i++ ) { seq = 0; do { ++seq; switch(seq) { case 1: ven = VENDOR_UNKNOWN; drv = DRIVEU; break; case 2: ven = VENDOR_MOUNTAIN_SUMMIT; drv = DRIVEU; break; case 3: ven = VENDOR_CMS; drv = DRIVEU; break; case 4: ven = VENDOR_UNKNOWN; drv = DRIVED; break; case 5: ven = VENDOR_MOUNTAIN_SUMMIT; drv = DRIVED; break; case 6: ven = VENDOR_CMS; drv = DRIVED; break; #ifdef B_DRIVE case 7: ven = VENDOR_CMS; drv = DRIVEB; break; case 8: ven = VENDOR_UNKNOWN; drv = DRIVEUB; break; case 9: ven = VENDOR_MOUNTAIN_SUMMIT; drv = DRIVEUB; break; case 10: ven = VENDOR_CMS; drv = DRIVEUB; break; #endif default: //ASSERT(seq != seq); seq = 0; // flag complete break; } if (seq) { cqd_context->device_descriptor.vendor = ven; cqd_ResetFDC( cqd_context ); status = cqd_LookForDevice( cqd_context, drv, vendor_detected, &found); } } while(!found && seq); // If not found, Wait a second, and retry if (!found) kdi_Sleep(cqd_context->kdi_context, kdi_wt001s); } // Sort out the results of the drive address search. A DriveFlt or a // CmdFlt indicate that we could never successfully communicate with // the tape drive at either address so we must assume that there is // no tape drive present. A NECFlt indicates that we had serious // trouble talking to the FDC so we must assume that it is either // broken or not there. The last thing to consider here is a TapeFlt. // If the TapeFlt indicates either a hardware or software reset it is // save to continue and the error can be ignored (since we must be // starting a tape session neither of these errors should bother us). // If the TapeFlt indicates any other error, it probably means some // badness has happened. switch (kdi_GetErrorType(status)) { case ERR_DRIVE_FAULT: case ERR_CMD_FAULT: case ERR_CMD_OVERRUN: status = kdi_Error(ERR_NO_DRIVE, FCT_ID, ERR_SEQ_1); break; case ERR_FDC_FAULT: case ERR_INVALID_FDC_STATUS: status = kdi_Error(ERR_NO_FDC, FCT_ID, ERR_SEQ_1); break; case ERR_INVALID_COMMAND: break; default: status = STATUS_SUCCESS; break; } #if DBG if (status) { kdi_CheckedDump( QIC117INFO, "Q117i: DLocateDrv Failed %08x\n", status); } #endif return status; } #undef FCT_ID #define FCT_ID 0x11009 VOID cqd_InitializeContext( IN PVOID cqd_context, IN PVOID kdi_context ) /***************************************************************************** * * FUNCTION: cqd_InitializeContext * * PURPOSE: Initialize the common driver context. * *****************************************************************************/ { RtlZeroMemory( cqd_context, sizeof(CqdContext) ); ((CqdContextPtr)cqd_context)->kdi_context = kdi_context; ((CqdContextPtr)cqd_context)->pegasus_supported = TRUE; ((CqdContextPtr)cqd_context)->configured = FALSE; ((CqdContextPtr)cqd_context)->cms_mode = FALSE; ((CqdContextPtr)cqd_context)->selected = FALSE; ((CqdContextPtr)cqd_context)->cmd_selected = FALSE; ((CqdContextPtr)cqd_context)->operation_status.no_tape = TRUE; ((CqdContextPtr)cqd_context)->device_cfg.speed_change = TRUE; ((CqdContextPtr)cqd_context)->drive_parms.mode = PRIMARY_MODE; ((CqdContextPtr)cqd_context)->device_descriptor.vendor = VENDOR_UNKNOWN; ((CqdContextPtr)cqd_context)->device_descriptor.fdc_type = FDC_TYPE_UNKNOWN; ((CqdContextPtr)cqd_context)->controller_data.isr_reentered = 0; ((CqdContextPtr)cqd_context)->controller_data.start_format_mode = FALSE; ((CqdContextPtr)cqd_context)->controller_data.end_format_mode = FALSE; ((CqdContextPtr)cqd_context)->controller_data.perpendicular_mode = FALSE; ((CqdContextPtr)cqd_context)->operation_status.xfer_rate = XFER_500Kbps; ((CqdContextPtr)cqd_context)->operation_status.retry_mode = FALSE; ((CqdContextPtr)cqd_context)->xfer_rate.tape = TAPE_500Kbps; ((CqdContextPtr)cqd_context)->xfer_rate.fdc = FDC_500Kbps; ((CqdContextPtr)cqd_context)->xfer_rate.srt = SRT_500Kbps; #if DBG ((CqdContextPtr)cqd_context)->dbg_head = ((CqdContextPtr)cqd_context)->dbg_tail = 0; #endif return; } #undef FCT_ID #define FCT_ID 0x1102c NTSTATUS cqd_LookForDevice( IN CqdContextPtr cqd_context, IN UCHAR drive_selector, IN BOOLEAN *vendor_detected, IN BOOLEAN *found ) /***************************************************************************** * * FUNCTION: cqd_LookForDevice * * PURPOSE: * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ /* Set the drive select parameters according to the desired target drive */ /* selector. */ switch (drive_selector) { case DRIVEB: cqd_context->device_cfg.select_byte = selb; cqd_context->device_cfg.deselect_byte = dselb; cqd_context->device_cfg.drive_select = curb; break; case DRIVED: cqd_context->device_cfg.select_byte = seld; cqd_context->device_cfg.deselect_byte = dseld; cqd_context->device_cfg.drive_select = curd; break; case DRIVEU: cqd_context->device_cfg.select_byte = selu; cqd_context->device_cfg.deselect_byte = dselu; cqd_context->device_cfg.drive_select = curu; break; case DRIVEUB: cqd_context->device_cfg.select_byte = selub; cqd_context->device_cfg.deselect_byte = dselub; cqd_context->device_cfg.drive_select = curub; break; } /* Try to communicate with the tape drive by requesting drive status. */ /* If we can successfully communicate with the drive, wait up to the */ /* approximate maximum autoload time (150 seconds) for the tape drive */ /* to become ready. This should cover a new tape being inserted */ /* immediatley before starting a tape session. */ *vendor_detected = FALSE; *found = FALSE; if ((status = cqd_CmdSelectDevice(cqd_context)) == STATUS_SUCCESS) { status = cqd_GetDeviceError(cqd_context); if (kdi_GetErrorType(status) != ERR_DRIVE_FAULT && kdi_GetErrorType(status) != ERR_CMD_FAULT) { *found = TRUE; if ((status = cqd_SendByte(cqd_context, FW_CMD_REPORT_VENDOR32)) == STATUS_SUCCESS) { USHORT vendor_id; if ((status = cqd_ReceiveByte( cqd_context, READ_WORD, (USHORT *)&vendor_id)) != STATUS_SUCCESS) { cqd_GetDeviceError(cqd_context); } else { // // Identify the drive (just new drives that support the // vendor 32 command. This is needed, as the TEAC drive // can not be de-selected (without it needing to // re-autoload). cqd_context->device_descriptor.vendor = (USHORT)(vendor_id >> 6); cqd_context->device_descriptor.model = (UCHAR)(vendor_id & ~VENDOR_MASK); *vendor_detected = TRUE; kdi_CheckedDump( QIC117INFO, "Q117i: Report Vendor 32 succeded (%x) \n", vendor_id); if (cqd_context->device_descriptor.vendor == VENDOR_TEAC && cqd_context->device_descriptor.model == TEAC_TR1) { // TEAC-800 Does not allow a deselect (except through // a reset) cqd_context->deselect_cmd = 0; } } } } cqd_DeselectDevice(cqd_context); } return status; } #undef FCT_ID #define FCT_ID 0x11040 NTSTATUS cqd_SendPhantomSelect( IN CqdContextPtr cqd_context ) /***************************************************************************** * * FUNCTION: cqd_SendPhantomSelect * * PURPOSE: * *****************************************************************************/ { NTSTATUS status=STATUS_SUCCESS; /* NTSTATUS or error condition.*/ status = cqd_SendByte( cqd_context, FW_CMD_SELECT_DRIVE ); if ( status == STATUS_SUCCESS ) { kdi_Sleep( cqd_context->kdi_context, INTERVAL_CMD ); status = cqd_SendByte( cqd_context, (UCHAR)(cqd_context->device_cfg.drive_id + CMD_OFFSET)); // For teac, use conner's deselect and cms's slelect kdi_Sleep( cqd_context->kdi_context, kdi_wt001s ); cqd_context->deselect_cmd = FW_CMD_DESELECT_DRIVE; } return status; } NTSTATUS cqd_CmdSelectDevice( IN CqdContextPtr cqd_context ) /***************************************************************************** * * FUNCTION: cqd_CmdSelectDevice * * PURPOSE: * *****************************************************************************/ { NTSTATUS status = STATUS_SUCCESS; /* NTSTATUS or error condition.*/ KdiContextPtr kdi_context; FDC_ENABLE_PARMS fdcEnableParms; if (cqd_context->selected == FALSE) { kdi_context = cqd_context->kdi_context; fdcEnableParms.DriveOnValue = cqd_context->device_cfg.select_byte; fdcEnableParms.TimeToWait = 0; status = kdi_FdcDeviceIo( kdi_context->controller_data.fdcDeviceObject, IOCTL_DISK_INTERNAL_ENABLE_FDC_DEVICE, &fdcEnableParms ); if (status == STATUS_SUCCESS && (cqd_context->device_cfg.select_byte == seld || cqd_context->device_cfg.select_byte == selu || cqd_context->device_cfg.select_byte == selub) && cqd_context->device_cfg.drive_select != curb) { switch ( cqd_context->device_descriptor.vendor ) { case VENDOR_UNKNOWN: case VENDOR_UNASSIGNED: case VENDOR_UNSUPPORTED: case VENDOR_PERTEC: cqd_context->deselect_cmd = 0; //Do nothing. break; case VENDOR_MOUNTAIN_SUMMIT: case VENDOR_ARCHIVE_CONNER: case VENDOR_CORE: status = cqd_ConnerPreamble(cqd_context, TRUE); cqd_context->deselect_cmd = FW_CMD_CONNER_DESELECT; break; case VENDOR_TEAC: if (cqd_context->device_descriptor.model == TEAC_TR1) { // For the teac-800 drive, we will never deselect the // device (reset is the only way to do so, not good). // If, however, the drive was deselected (reset) by a // floppy drive access, we must take care of that // condition here // Assume the drive is active and send it a device // error command status = cqd_GetDeviceError(cqd_context); // if one of these error occurs, then the drive was // reset. if (kdi_GetErrorType(status) == ERR_DRIVE_FAULT || kdi_GetErrorType(status) == ERR_CMD_FAULT) { // the drive must have been reset, now send the // drive select command, and wait for the drive to // autoload. status = cqd_SendPhantomSelect(cqd_context); // Force no deselect, We will always leave the // drive selected cqd_context->deselect_cmd = 0; // Wait for the tape to autoload if (status == STATUS_SUCCESS) { status = cqd_ClearTapeError(cqd_context); status = STATUS_SUCCESS; } } else { status = STATUS_SUCCESS; //status = cqd_ClearTapeError(cqd_context); } } else { status = cqd_SendPhantomSelect(cqd_context); } break; case VENDOR_IOMEGA: case VENDOR_CMS: default: status = cqd_SendPhantomSelect(cqd_context); break; } } if (status == STATUS_SUCCESS) { cqd_context->selected = TRUE; } kdi_Sleep(cqd_context->kdi_context, INTERVAL_CMD ); } return status; } #undef FCT_ID #define FCT_ID 0x1100C VOID cqd_CmdDeselectDevice( IN CqdContextPtr cqd_context, IN BOOLEAN drive_selected ) /***************************************************************************** * * FUNCTION: cqd_CmdDeselectDevice * * PURPOSE: Deselect the device and release any locked resources * *****************************************************************************/ { /* reset the FDC to ensure reliable drive communications */ (VOID) cqd_ResetFDC(cqd_context); if (drive_selected) { (VOID) cqd_DeselectDevice(cqd_context); } /* Dont issue a pause after this command */ cqd_context->no_pause = TRUE; cqd_context->operation_status.new_tape = FALSE; return; } #undef FCT_ID #define FCT_ID 0x1102D NTSTATUS cqd_ConnerPreamble( IN CqdContextPtr cqd_context, IN BOOLEAN select ) /***************************************************************************** * * FUNCTION: cqd_ConnerPreamble * * PURPOSE: * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ if (select) { status = cqd_SendByte(cqd_context, FW_CMD_CONNER_SELECT_1); if (status == STATUS_SUCCESS) { kdi_Sleep(cqd_context->kdi_context, INTERVAL_CMD ); status = cqd_SendByte(cqd_context, FW_CMD_CONNER_SELECT_2); kdi_Sleep(cqd_context->kdi_context, INTERVAL_CMD ); } } else { status = cqd_SendByte(cqd_context, cqd_context->deselect_cmd); kdi_Sleep(cqd_context->kdi_context, INTERVAL_CMD ); } return status; } #undef FCT_ID #define FCT_ID 0x1101D NTSTATUS cqd_DeselectDevice( IN CqdContextPtr cqd_context ) /***************************************************************************** * * FUNCTION: cqd_DeselectDevice * * PURPOSE: Deselect the tape drive by making the select line inactive (high). * *****************************************************************************/ { NTSTATUS status=STATUS_SUCCESS; /* NTSTATUS or error condition.*/ KdiContextPtr kdi_context; if (cqd_context->selected == TRUE) { if ((cqd_context->device_cfg.select_byte == seld || cqd_context->device_cfg.select_byte == selu || cqd_context->device_cfg.select_byte == selub) && cqd_context->device_cfg.drive_select != curb) { if (cqd_context->deselect_cmd) { status = cqd_SendByte(cqd_context, cqd_context->deselect_cmd); kdi_Sleep(cqd_context->kdi_context, kdi_wt500ms ); } } kdi_context = cqd_context->kdi_context; status = kdi_FdcDeviceIo( kdi_context->controller_data.fdcDeviceObject, IOCTL_DISK_INTERNAL_DISABLE_FDC_DEVICE, NULL ); cqd_context->selected = FALSE; kdi_Sleep(cqd_context->kdi_context, INTERVAL_CMD ); } return status; } #undef FCT_ID #define FCT_ID 0x11041 NTSTATUS cqd_SendByte( IN CqdContextPtr cqd_context, IN UCHAR command ) /***************************************************************************** * * FUNCTION: cqd_SendByte * * PURPOSE: Transmit a command to the tape drive using step pulses * generated by the FDC. * * Using the Present Cylinder Number (pcn) of the FDC * calculate a New Cylinder Number (ncn) that will make * the FDC generate the number of step pulses corresponding * to the command byte. * * Execute a Seek with the FDC. * * Sense Interrupt NTSTATUS of the FDC and make sure that the * pcn concurs with the ncn, which indicates that the correct * number of step pulses were issued. * *****************************************************************************/ { NTSTATUS status = STATUS_SUCCESS; /* NTSTATUS or error condition.*/ FDC_CMD_SEEK seek; FDC_CMD_SENSE_INTERRUPT_STATUS sense_int; FDC_RESULT result; #if DBG BOOLEAN save; #endif #if DBG /* Lockout commands used to receive the status */ save = cqd_context->dbg_lockout; cqd_context->dbg_lockout = TRUE; #endif if (command >= MAX_FDC_SEEK || command <= 0) { #if DBG cqd_context->dbg_lockout = save; #endif return kdi_Error(ERR_INVALID_COMMAND, FCT_ID, ERR_SEQ_1); } if (cqd_context->controller_data.fdc_pcn < MAX_FDC_SEEK) { seek.NCN = (UCHAR)(cqd_context->controller_data.fdc_pcn + command); } else { seek.NCN = (UCHAR)(cqd_context->controller_data.fdc_pcn - command); } seek.cmd = COMMND_SEEK; seek.drive = (UCHAR)cqd_context->device_cfg.drive_select; status = cqd_IssueFDCCommand( cqd_context, (UCHAR *)&seek, (UCHAR *)&result, NULL, 0, 0, kdi_wt001s); if (status != STATUS_SUCCESS) { return status; } /* If the sense interrupt status is O.K., then proceed as if */ /* nothing happened. If, however, there is an error returned by */ /* status register 0, then return a FDCFLT. */ if (!(result.ST0 & ST0_IC)) { /* If we timed out, then we did the sense interrupt status */ /* without clearing the interrupt from the interrupt controller. */ /* Since the FDC did not indicate an error, we assume that we */ /* missed the interrupt and send the EOI. Only needed for an */ /* 82072. */ if (kdi_GetInterfaceType(cqd_context->kdi_context) != MICRO_CHANNEL) { if (result.ST0 != (UCHAR)(cqd_context->device_cfg.drive_select | ST0_SE)) { #if DBG cqd_context->dbg_lockout = save; #endif return kdi_Error(ERR_FDC_FAULT, FCT_ID, ERR_SEQ_1); } } if (seek.NCN != result.PCN) { #if DBG cqd_context->dbg_lockout = save; #endif return kdi_Error(ERR_CMD_FAULT, FCT_ID, ERR_SEQ_1); } cqd_context->controller_data.fdc_pcn = result.PCN; } else { #if DBG cqd_context->dbg_lockout = save; #endif return kdi_Error(ERR_FDC_FAULT, FCT_ID, ERR_SEQ_2); } #if DBG cqd_context->dbg_lockout = save; DBG_ADD_ENTRY(QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, DBG_SEND_BYTE); DBG_ADD_ENTRY(QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, (UCHAR)command); #endif return status; } #undef FCT_ID #define FCT_ID 0x1103B NTSTATUS cqd_ReceiveByte( IN CqdContextPtr cqd_context, IN USHORT receive_length, OUT PUSHORT receive_data ) /***************************************************************************** * * FUNCTION: cqd_ReceiveByte * * PURPOSE: Read a byte/word of response data from the FDC. Response data * can be drive error/status information or drive configuration * information. * * Wait for Track 0 from the tape drive to go active. This * indicates that the drive is ready to start sending data. * * Alternate sending Report Next Bit commands to the tape drive * and sampling Track 0 (response data) from the tape drive * until the proper number of response data bits have been read. * * Read one final data bit from the tape drive which is the * confirmation bit. This bit must be a 1 to confirm the * transmission. * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ UCHAR i = 0; UCHAR stat3; USHORT fdc_data= 0; #if DBG BOOLEAN save; #endif #if DBG /* Lockout commands used to receive the status */ save = cqd_context->dbg_lockout; cqd_context->dbg_lockout = TRUE; #endif if ((status = cqd_WaitActive(cqd_context)) != STATUS_SUCCESS) { #if DBG cqd_context->dbg_lockout = save; #endif return status; } do { if((status = cqd_SendByte( cqd_context, FW_CMD_RPT_NEXT_BIT)) != STATUS_SUCCESS) { #if DBG cqd_context->dbg_lockout = save; #endif return status; } kdi_Sleep( cqd_context->kdi_context, INTERVAL_WAIT_ACTIVE ); if ((status = cqd_GetStatus(cqd_context, &stat3)) != STATUS_SUCCESS) { #if DBG cqd_context->dbg_lockout = save; #endif return status; } fdc_data >>= 1; if (stat3 & ST3_T0) { fdc_data |= 0x8000; } i++; } while (i < receive_length); /* If the received data is only one byte wide, then shift data to the low */ /* byte of fdc_data. */ if (receive_length == READ_BYTE) { fdc_data >>= READ_BYTE; } /* Return the low byte to the caller. */ ((UCHAR *)receive_data)[LOW_BYTE] = ((UCHAR *)&fdc_data)[LOW_BYTE]; /* If the FDC data is a word, then return it to the caller. */ if (receive_length == READ_WORD) { ((UCHAR *)receive_data)[HI_BYTE] = ((UCHAR *)&fdc_data)[HI_BYTE]; } if ((status = cqd_SendByte( cqd_context, FW_CMD_RPT_NEXT_BIT)) != STATUS_SUCCESS) { #if DBG cqd_context->dbg_lockout = save; #endif return status; } kdi_Sleep(cqd_context->kdi_context, INTERVAL_WAIT_ACTIVE ); if((status = cqd_GetStatus(cqd_context, &stat3)) != STATUS_SUCCESS) { #if DBG cqd_context->dbg_lockout = save; #endif return status; } if (!(stat3 & (UCHAR)ST3_T0)) { #if DBG cqd_context->dbg_lockout = save; #endif return kdi_Error(ERR_CMD_OVERRUN, FCT_ID, ERR_SEQ_1); } #if DBG cqd_context->dbg_lockout = save; DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, DBG_RECEIVE_BYTE); DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, fdc_data); #endif return status; } #undef FCT_ID #define FCT_ID 0x11022 NTSTATUS cqd_GetDeviceError( IN CqdContextPtr cqd_context ) /***************************************************************************** * * FUNCTION: cqd_GetDeviceError * * PURPOSE: Read the tape drive NTSTATUS byte and, if necessary, the * tape drive Error information. * * Read the Drive NTSTATUS byte from the tape drive. * * If the drive status indicates that the tape drive has an * error to report, read the error information which includes * both the error code and the command that was being executed * when the error occurred. * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ UCHAR drv_stat; USHORT drv_err; BOOLEAN repeat_report; BOOLEAN repeat_drv_flt = FALSE; BOOLEAN esd_retry = FALSE; cqd_context->firmware_cmd = FW_NO_COMMAND; cqd_context->firmware_error = FW_NO_ERROR; do { repeat_report = FALSE; if ((status = cqd_Report( cqd_context, FW_CMD_REPORT_STATUS, (USHORT *)&drv_stat, READ_BYTE, &esd_retry)) != STATUS_SUCCESS) { if ((kdi_GetErrorType(status) == ERR_DRIVE_FAULT) && !repeat_drv_flt) { repeat_report = TRUE; repeat_drv_flt = TRUE; } } if (status == STATUS_SUCCESS) { cqd_context->drv_stat = drv_stat; kdi_CheckedDump( QIC117DRVSTAT, "QIC117: Drv status = %02x\n", drv_stat); if ((drv_stat & STATUS_READY) == 0) { status = kdi_Error(ERR_DRV_NOT_READY, FCT_ID, ERR_SEQ_1); } else { if ((drv_stat & STATUS_CART_PRESENT) != 0) { if ( ((drv_stat & STATUS_NEW_CART) != 0) || ((cqd_context->device_descriptor.vendor == VENDOR_IOMEGA) && cqd_context->operation_status.no_tape) ) { cqd_context->persistent_new_cart = TRUE; } if ((drv_stat & STATUS_BOT) != 0) { cqd_context->rd_wr_op.bot = TRUE; } else { cqd_context->rd_wr_op.bot = FALSE; } if ((drv_stat & STATUS_EOT) != 0) { cqd_context->rd_wr_op.eot = TRUE; } else { cqd_context->rd_wr_op.eot = FALSE; } if ((drv_stat & STATUS_CART_REFERENCED) != 0) { cqd_context->operation_status.cart_referenced = TRUE; } else { cqd_context->operation_status.cart_referenced = FALSE; if (!repeat_drv_flt && ((drv_stat & STATUS_ERROR) == 0)) { repeat_report = TRUE; repeat_drv_flt = TRUE; } } if ((drv_stat & STATUS_WRITE_PROTECTED) != 0) { cqd_context->tape_cfg.write_protected = TRUE; } else { cqd_context->tape_cfg.write_protected = FALSE; } cqd_context->operation_status.no_tape = FALSE; } else { cqd_context->operation_status.no_tape = TRUE; cqd_context->persistent_new_cart = FALSE; cqd_context->operation_status.cart_referenced = FALSE; cqd_context->tape_cfg.write_protected = FALSE; cqd_context->rd_wr_op.bot = FALSE; cqd_context->rd_wr_op.eot = FALSE; } if ((drv_stat & (STATUS_NEW_CART | STATUS_ERROR)) != 0) { if ((status = cqd_Report(cqd_context, FW_CMD_REPORT_ERROR, &drv_err, READ_WORD, &esd_retry)) != STATUS_SUCCESS) { if ((kdi_GetErrorType(status) == ERR_DRIVE_FAULT) && !repeat_drv_flt) { repeat_report = TRUE; repeat_drv_flt = TRUE; } } if (status == STATUS_SUCCESS) { kdi_CheckedDump( QIC117DRVSTAT, "QIC117: Drv error = %04x\n", drv_err ); if ((drv_stat & STATUS_ERROR) != 0) { cqd_context->firmware_error = (UCHAR)drv_err; cqd_context->firmware_cmd = (UCHAR)(drv_err >> 8); if (cqd_context->firmware_error == FW_CMD_WHILE_NEW_CART) { cqd_context->firmware_cmd = FW_NO_COMMAND; cqd_context->firmware_error = FW_NO_ERROR; cqd_context->persistent_new_cart = TRUE; } } else { cqd_context->firmware_cmd = FW_NO_COMMAND; cqd_context->firmware_error = FW_NO_ERROR; } if (cqd_context->firmware_error != FW_NO_ERROR) { switch (cqd_context->firmware_error) { case FW_ILLEGAL_CMD: if (esd_retry) { esd_retry = FALSE; repeat_report = TRUE; } break; case FW_NO_DRIVE: cqd_ResetFDC(cqd_context); cqd_context->selected = FALSE; status = cqd_CmdSelectDevice(cqd_context); if (!repeat_drv_flt && (status == STATUS_SUCCESS)) { repeat_report = TRUE; repeat_drv_flt = TRUE; } else { status = kdi_Error( ERR_NO_DRIVE, FCT_ID, ERR_SEQ_1); } break; case FW_CART_NOT_IN: break; case FW_DRIVE_NOT_READY: status = kdi_Error( ERR_DRV_NOT_READY, FCT_ID, ERR_SEQ_2); break; default: status = kdi_Error( (USHORT)(ERR_CQD + cqd_context->firmware_error), (ULONG)cqd_context->firmware_cmd, ERR_SEQ_1); } } } } } } } while (repeat_report); if (status == STATUS_SUCCESS) { cqd_context->operation_status.new_tape = cqd_context->persistent_new_cart; if (cqd_context->cmd_selected) { if (cqd_context->operation_status.no_tape) { status = kdi_Error(ERR_NO_TAPE, FCT_ID, ERR_SEQ_1); } if (cqd_context->operation_status.new_tape) { status = kdi_Error(ERR_NEW_TAPE, FCT_ID, ERR_SEQ_1); } } } return status; } #undef FCT_ID #define FCT_ID 0x11049 NTSTATUS cqd_WaitActive( IN CqdContextPtr cqd_context ) /***************************************************************************** * * FUNCTION: cqd_WaitActive * * PURPOSE: Wait up to 5ms for tape drive's TRK0 line to go active. * 5 ms the specified delay for this parameter. * *****************************************************************************/ { NTSTATUS status; /* Status or error condition.*/ UCHAR stat3; kdi_Sleep(cqd_context->kdi_context, INTERVAL_WAIT_ACTIVE ); if ((status = cqd_GetStatus(cqd_context, &stat3)) != STATUS_SUCCESS) { return status; } if (!(stat3 & ST3_T0)) { status = kdi_Error(ERR_DRIVE_FAULT, FCT_ID, ERR_SEQ_1); } return status; } #undef FCT_ID #define FCT_ID 0x11028 NTSTATUS cqd_GetStatus( IN CqdContextPtr cqd_context, OUT UCHAR *status_register_3 ) /***************************************************************************** * * FUNCTION: cqd_GetStatus * * PURPOSE: Get status byte from the floppy controller chip. * Send the Sense Drive NTSTATUS command to the floppy controller. * Read the response from the floppy controller which should be * status register 3. * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ FDC_CMD_SENSE_DRIVE_STATUS send_st; send_st.command = COMMND_SENSE_DRIVE_STATUS; send_st.drive = (UCHAR)cqd_context->device_cfg.drive_select; status = cqd_IssueFDCCommand( cqd_context, (UCHAR *)&send_st, (UCHAR *)status_register_3, NULL, 0, 0, 0); return status; } #undef FCT_ID #define FCT_ID 0x1104A NTSTATUS cqd_WaitCommandComplete( IN CqdContextPtr cqd_context, IN ULONG wait_time, IN BOOLEAN non_interruptible ) /***************************************************************************** * * FUNCTION: cqd_WaitCommandComplete * * PURPOSE: Wait a specified amount of time for the tape drive to become * ready after executing a command. * * Read the Drive NTSTATUS byte from the tape drive. * * If the drive is not ready then wait 1/2 second and try again * until the specified time has elapsed. * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ ULONG wait_start = 0l; ULONG wait_current = 0l; wait_start = kdi_GetSystemTime(); #if DBG /* Lockout commands used to receive the status */ ++cqd_context->dbg_lockout; #endif do { kdi_Sleep(cqd_context->kdi_context, kdi_wt100ms ); status = cqd_GetDeviceError(cqd_context); if (kdi_GetErrorType(status) != ERR_DRV_NOT_READY) { #if DBG --cqd_context->dbg_lockout; DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, DBG_WAITCC); DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, cqd_context->drv_stat); DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, cqd_context->firmware_error); DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, status); #endif return status; } if (!non_interruptible && (kdi_ReportAbortStatus(cqd_context->kdi_context) != NO_ABORT_PENDING)) { #if DBG --cqd_context->dbg_lockout; #endif return kdi_Error(ERR_ABORT, FCT_ID, ERR_SEQ_1); } wait_current = kdi_GetSystemTime(); } while (wait_time > (wait_current - wait_start)); if (kdi_ReportAbortStatus(cqd_context->kdi_context) != NO_ABORT_PENDING) { status = kdi_Error(ERR_ABORT, FCT_ID, ERR_SEQ_1); } else { status = kdi_Error(ERR_KDI_TO_EXPIRED, FCT_ID, ERR_SEQ_1); } #if DBG --cqd_context->dbg_lockout; DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, DBG_WAITCC); DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, cqd_context->drv_stat); DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, cqd_context->firmware_error); DBG_ADD_ENTRY( QIC117SHOWMCMDS, (CqdContextPtr)cqd_context, status); #endif return status; } #undef FCT_ID #define FCT_ID 0x1103C NTSTATUS cqd_Report( IN CqdContextPtr cqd_context, IN UCHAR command, IN PUSHORT report_data, IN USHORT report_size, IN OUT PBOOLEAN esd_retry ) /***************************************************************************** * * FUNCTION: cqd_Report * * PURPOSE: Send a report command to the tape drive and get the response * data. If a communication failure occurs, then we assume that * it is a result of an ESD hit and retry the communication. * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ USHORT i = 0; do { if (cqd_context->controller_data.end_format_mode) { cqd_context->controller_data.end_format_mode = FALSE; status = STATUS_SUCCESS; } else { status = cqd_SendByte(cqd_context, command); } if (status == STATUS_SUCCESS) { status = cqd_ReceiveByte(cqd_context, report_size, report_data); if (kdi_GetErrorType(status) == ERR_CMD_OVERRUN) { if (esd_retry != NULL) { *esd_retry = TRUE; cqd_ResetFDC(cqd_context); status = cqd_CmdSelectDevice(cqd_context); } } } } while (++i < REPORT_RPT && status != STATUS_SUCCESS); return status; } #undef FCT_ID #define FCT_ID 0x11004 NTSTATUS cqd_ClearTapeError( IN CqdContextPtr cqd_context ) /***************************************************************************** * * FUNCTION: cqd_ClearTapeError * * PURPOSE: To correct errors in the Jumbo B drive and firmware version 63. * * This piece of code added due to the face that the Jumbo B drives * with firmware 63 have a bug where you put a tape in very slowly, * they sense that they have a tape and engage the motor before the * tape is actually in. It may also cause the drive to think that * the tape is write protected when it actually is not. Sending it * the New tape command causes it to go through the tape loading * sequence and fixes these 2 bugs. * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ /* Send the NewTape command, and then clear the error byte. */ if ((status = cqd_SendByte(cqd_context, FW_CMD_NEW_TAPE)) == STATUS_SUCCESS) { status = cqd_WaitCommandComplete( cqd_context, INTERVAL_LOAD_POINT, TRUE); /* This command is specific to CMS drives. Since we don't * know whose drive this is when the function is called, * the invalid command error is cleared. */ if ((kdi_GetErrorType(status) == ERR_FW_ILLEGAL_CMD) || (kdi_GetErrorType(status) == ERR_FW_UNDEFINED_COMMAND)) { status = STATUS_SUCCESS; } } return status; } #undef FCT_ID #define FCT_ID 0x1103d VOID cqd_ResetFDC( IN CqdContextPtr cqd_context ) /***************************************************************************** * * FUNCTION: cqd_ResetFDC * * PURPOSE: To reset the floppy controller chip. * *****************************************************************************/ { NTSTATUS status=STATUS_SUCCESS; /* NTSTATUS or error condition.*/ KdiContextPtr kdi_context; kdi_context = cqd_context->kdi_context; status = kdi_FdcDeviceIo( kdi_context->controller_data.fdcDeviceObject, IOCTL_DISK_INTERNAL_RESET_FDC, NULL ); if ( NT_SUCCESS(status) ) { cqd_ConfigureFDC(cqd_context); cqd_context->controller_data.fdc_pcn = 0; cqd_context->controller_data.perpendicular_mode = FALSE; } return; } #undef FCT_ID #define FCT_ID 0x11006 NTSTATUS cqd_ConfigureFDC( IN CqdContextPtr cqd_context ) /***************************************************************************** * * FUNCTION: cqd_ConfigureFDC * * PURPOSE: To configure the floppy controller chip according * to the current FDC parameters. * *****************************************************************************/ { NTSTATUS status; /* NTSTATUS or error condition.*/ FDC_CMD_SPECIFY specify; FDC_CMD_CONFIGURE config; UCHAR precomp_mask = 0; /* Mask write precomp in DSR register */ FDC_CMD_DRIVE_SPECIFICATION drive_s; status = cqd_DCROut(cqd_context, cqd_context->xfer_rate.fdc); if ( status == STATUS_SUCCESS ) { if ( cqd_context->device_descriptor.fdc_type == FDC_TYPE_82078_64 ) { drive_s.command = COMMND_DRIVE_SPECIFICATION; drive_s.drive_spec = (UCHAR)(DRIVE_SPEC | ((cqd_context->device_cfg.select_byte & DRIVE_ID_MASK) << DRIVE_SELECT_OFFSET)); drive_s.done = DONE_MARKER; status = cqd_IssueFDCCommand( cqd_context, (UCHAR *)&drive_s, NULL, NULL, 0, 0, 0 ); } } if ((status == STATUS_SUCCESS) && (cqd_context->device_descriptor.fdc_type == FDC_TYPE_82077 || cqd_context->device_descriptor.fdc_type == FDC_TYPE_82077AA || cqd_context->device_descriptor.fdc_type == FDC_TYPE_82078_44 || cqd_context->device_descriptor.fdc_type == FDC_TYPE_82078_64 || cqd_context->device_descriptor.fdc_type == FDC_TYPE_NATIONAL)) { /* if this is a 3010 or 3020 CMS drive, turn off write precomp */ switch (cqd_context->device_descriptor.drive_class) { case QIC3010_DRIVE: case QIC3010W_DRIVE: case QIC3020_DRIVE: case QIC3020W_DRIVE: precomp_mask = FDC_PRECOMP_OFF; break; default: precomp_mask = FDC_PRECOMP_ON; } /* Select the fdc data rate corresponding to the current xfer rate * and enable or disable write precomp. */ status = cqd_DSROut( cqd_context, (UCHAR)(cqd_context->xfer_rate.fdc | precomp_mask)); /* Deselect the tape drive PLL */ if (status == STATUS_SUCCESS) { cqd_TDROut( cqd_context, curu ); switch (cqd_context->xfer_rate.fdc) { case FDC_250Kbps: case FDC_500Kbps: /* Enable the tape drive PLL */ status = cqd_TDROut( cqd_context, curb ); break; } if (status == STATUS_SUCCESS) { config.cmd = COMMND_CONFIGURE; config.czero = FDC_CONFIG_NULL_BYTE; config.config = (UCHAR)(FDC_FIFO & FIFO_MASK); config.pretrack = FDC_CONFIG_PRETRACK; /* issue the configure command to the FDC */ status = cqd_IssueFDCCommand( cqd_context, (UCHAR *)&config, NULL, NULL, 0, 0, 0 ); } } if (status != STATUS_SUCCESS) { return status; } } /* Specify the rates for the FDC's three internal timers. */ /* This includes the head unload time (HUT), the head load */ /* time (HLT), and the step rate time (SRT) */ specify.command = COMMND_SPECIFY; specify.SRT_HUT = cqd_context->xfer_rate.srt; specify.HLT_ND = FDC_HLT; status = cqd_IssueFDCCommand( cqd_context, (UCHAR *)&specify, NULL, NULL, 0, 0, 0 ); return status; } #undef FCT_ID #define FCT_ID 0x11008 NTSTATUS cqd_DCROut( IN CqdContextPtr cqd_context, IN UCHAR speed ) /***************************************************************************** * * FUNCTION: cqd_DCROut * * PURPOSE: Set the data rate bits of the configuration control register. * ******************************************************************************/ { NTSTATUS status; KdiContextPtr kdi_context; kdi_context = cqd_context->kdi_context; speed = (UCHAR)(speed & FDC_DCR_MASK); status = kdi_FdcDeviceIo( kdi_context->controller_data.fdcDeviceObject, IOCTL_DISK_INTERNAL_SET_FDC_DATA_RATE, &speed ); return status; } NTSTATUS cqd_DSROut( IN CqdContextPtr cqd_context, IN UCHAR precomp ) { NTSTATUS status; KdiContextPtr kdi_context; kdi_context = cqd_context->kdi_context; status = kdi_FdcDeviceIo( kdi_context->controller_data.fdcDeviceObject, IOCTL_DISK_INTERNAL_SET_FDC_PRECOMP, &precomp ); return status; } NTSTATUS cqd_TDROut( IN CqdContextPtr cqd_context, IN UCHAR tape_mode ) { NTSTATUS status; KdiContextPtr kdi_context; kdi_context = cqd_context->kdi_context; status = kdi_FdcDeviceIo( kdi_context->controller_data.fdcDeviceObject, IOCTL_DISK_INTERNAL_SET_FDC_TAPE_MODE, &tape_mode ); return status; } NTSTATUS cqd_IssueFDCCommand( IN CqdContextPtr cqd_context, IN PUCHAR CommandFifo, IN PUCHAR ResultFifo, IN PVOID IoHandle, IN ULONG IoOffset, IN ULONG TransferBytes, IN ULONG TimeOut ) { NTSTATUS status; ISSUE_FDC_COMMAND_PARMS issueCommandParms; KdiContextPtr kdi_context; kdi_context = cqd_context->kdi_context; // // Set the command parameters // issueCommandParms.FifoInBuffer = CommandFifo; issueCommandParms.FifoOutBuffer = ResultFifo; issueCommandParms.IoHandle = IoHandle; issueCommandParms.IoOffset = IoOffset; issueCommandParms.TransferBytes = TransferBytes; // // Timeouts are requested in terms of milliseconds but fdc.sys needs them // in terms of seconds, so adjust the value. // issueCommandParms.TimeOut = TimeOut / 1000; status = kdi_FdcDeviceIo( kdi_context->controller_data.fdcDeviceObject, IOCTL_DISK_INTERNAL_ISSUE_FDC_COMMAND, &issueCommandParms ); return status; } #undef FCT_ID #define FCT_ID 0x15883 NTSTATUS kdi_Error( IN USHORT group_and_type, IN ULONG grp_fct_id, IN UCHAR sequence ) /***************************************************************************** * * FUNCTION: kdi_Error * * PURPOSE: * *****************************************************************************/ { return ERROR_ENCODE( group_and_type, grp_fct_id, sequence ); } #undef FCT_ID #define FCT_ID 0x15886 USHORT kdi_GetErrorType( IN NTSTATUS status ) /***************************************************************************** * * FUNCTION: kdi_GetErrorType * * PURPOSE: Return the GRP+ERROR to the calling function for easy comparisons * and switch statement access. *****************************************************************************/ { return (USHORT)(status >> 16); } #undef FCT_ID #define FCT_ID 0x15a0f BOOLEAN kdi_ReportAbortStatus( IN PVOID kdi_context ) /***************************************************************************** * * FUNCTION: kdi_ReportAbortStatus * * PURPOSE: * *****************************************************************************/ { if (((KdiContextPtr)kdi_context)->abort_requested) { return ABORT_LEVEL_1; } else { return NO_ABORT_PENDING; } } #undef FCT_ID #define FCT_ID 0x15A10 ULONG kdi_GetSystemTime( ) /***************************************************************************** * * FUNCTION: kdi_GetSystemTime * * PURPOSE: Gets the system time in milliseconds * *****************************************************************************/ { ULONG remainder; ULONG time_increment; ULARGE_INTEGER nanosec_interval; LARGE_INTEGER tick_count; LARGE_INTEGER temp; time_increment = KeQueryTimeIncrement(); KeQueryTickCount(&tick_count); temp = RtlExtendedIntegerMultiply( tick_count, time_increment); nanosec_interval = *(ULARGE_INTEGER *)&temp; return RtlEnlargedUnsignedDivide( nanosec_interval, NANOSEC_PER_MILLISEC, &remainder); } #undef FCT_ID #define FCT_ID 0x15A17 UCHAR kdi_GetInterfaceType( IN KdiContextPtr kdi_context ) /***************************************************************************** * * FUNCTION: kdi_GetInterfaceType * * PURPOSE: * *****************************************************************************/ { return kdi_context->interface_type; } #undef FCT_ID #define FCT_ID 0x15A20 #if DBG unsigned long kdi_debug_level = 0; #endif #if DBG VOID kdi_CheckedDump( IN ULONG debug_level, IN PCHAR format_str, IN ULONG_PTR argument ) /***************************************************************************** * * FUNCTION: kdi_CheckedDump * * PURPOSE: * *****************************************************************************/ { if ((kdi_debug_level & debug_level) != 0) { DbgPrint(format_str, argument); } return; } #endif /***************************************************************************** * * FUNCTION: kdi_Sleep * * PURPOSE: * *****************************************************************************/ #undef FCT_ID #define FCT_ID 0x15A0E NTSTATUS kdi_Sleep( IN KdiContextPtr kdi_context, IN ULONG wait_time ) { LARGE_INTEGER timeout; timeout.QuadPart = -(10 * 1000 * (LONGLONG)wait_time); (VOID) KeDelayExecutionThread( KernelMode, FALSE, &timeout ); return kdi_Error( ERR_KDI_TO_EXPIRED, FCT_ID, ERR_SEQ_2 ); } NTSTATUS kdi_FdcDeviceIo( IN PDEVICE_OBJECT DeviceObject, IN ULONG Ioctl, IN OUT PVOID Data ) /***************************************************************************** * * FUNCTION: kdi_FdcDeviceIo * * PURPOSE: * *****************************************************************************/ { NTSTATUS ntStatus; PIRP irp; PIO_STACK_LOCATION irpStack; KEVENT doneEvent; IO_STATUS_BLOCK ioStatus; KeInitializeEvent( &doneEvent, NotificationEvent, FALSE); // // Create an IRP for enabler // irp = IoBuildDeviceIoControlRequest( Ioctl, DeviceObject, NULL, 0, NULL, 0, TRUE, &doneEvent, &ioStatus ); if (irp == NULL) { // // If an Irp can't be allocated, then this call will // simply return. This will leave the queue frozen for // this device, which means it can no longer be accessed. // return ERROR_ENCODE(ERR_OUT_OF_BUFFERS, FCT_ID, 1); } irpStack = IoGetNextIrpStackLocation(irp); irpStack->Parameters.DeviceIoControl.Type3InputBuffer = Data; // // Call the driver and request the operation // ntStatus = IoCallDriver(DeviceObject, irp); // // Now wait for operation to complete (should already be done, but // maybe not) // if ( ntStatus == STATUS_PENDING ) { KeWaitForSingleObject( &doneEvent, Executive, KernelMode, FALSE, NULL); ntStatus = ioStatus.Status; } if ( ntStatus == STATUS_DEVICE_NOT_READY ) { ntStatus = ERROR_ENCODE(ERR_KDI_TO_EXPIRED, FCT_ID, 1); } return ntStatus; }