1953 lines
57 KiB
C
1953 lines
57 KiB
C
/*****************************************************************************
|
||
*
|
||
* 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 <ntddk.h>
|
||
#include <ntddfdc.h>
|
||
#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;
|
||
}
|
||
|