You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3594 lines
97 KiB
3594 lines
97 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1994 - 1999
|
|
|
|
Module Name:
|
|
|
|
scsitape.c
|
|
|
|
Abstract:
|
|
|
|
This is the tape class driver.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "tape.h"
|
|
|
|
//
|
|
// Define the maximum inquiry data length.
|
|
//
|
|
|
|
#define MAXIMUM_TAPE_INQUIRY_DATA 252
|
|
#define UNDEFINED_BLOCK_SIZE ((ULONG) -1)
|
|
#define TAPE_SRB_LIST_SIZE 4
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
VOID
|
|
TapeUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
NTSTATUS
|
|
TapeAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT Pdo
|
|
);
|
|
|
|
NTSTATUS
|
|
TapeStartDevice(
|
|
IN PDEVICE_OBJECT Fdo
|
|
);
|
|
|
|
NTSTATUS
|
|
CreateTapeDeviceObject(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN PTAPE_INIT_DATA_EX TapeInitData
|
|
);
|
|
|
|
VOID
|
|
TapeError(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN OUT PNTSTATUS Status,
|
|
IN OUT PBOOLEAN Retry
|
|
);
|
|
|
|
NTSTATUS
|
|
TapeReadWriteVerification(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
TapeReadWrite(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
SplitTapeRequest(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp,
|
|
IN ULONG MaximumBytes
|
|
);
|
|
|
|
NTSTATUS
|
|
TapeIoCompleteAssociated(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
TapeDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
TapeInitDevice(
|
|
IN PDEVICE_OBJECT Fdo
|
|
);
|
|
|
|
NTSTATUS
|
|
TapeRemoveDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR Type
|
|
);
|
|
|
|
NTSTATUS
|
|
TapeStopDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR Type
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(PAGE, TapeUnload)
|
|
#pragma alloc_text(PAGE, TapeClassInitialize)
|
|
#pragma alloc_text(PAGE, TapeAddDevice)
|
|
#pragma alloc_text(PAGE, CreateTapeDeviceObject)
|
|
#pragma alloc_text(PAGE, TapeStartDevice)
|
|
#pragma alloc_text(PAGE, TapeInitDevice)
|
|
#pragma alloc_text(PAGE, TapeRemoveDevice)
|
|
#pragma alloc_text(PAGE, TapeStopDevice)
|
|
#pragma alloc_text(PAGE, TapeDeviceControl)
|
|
#pragma alloc_text(PAGE, TapeReadWriteVerification)
|
|
#pragma alloc_text(PAGE, TapeReadWrite)
|
|
#pragma alloc_text(PAGE, SplitTapeRequest)
|
|
#pragma alloc_text(PAGE, ScsiTapeFreeSrbBuffer)
|
|
#pragma alloc_text(PAGE, TapeClassZeroMemory)
|
|
#pragma alloc_text(PAGE, TapeClassCompareMemory)
|
|
#pragma alloc_text(PAGE, TapeClassLiDiv)
|
|
#pragma alloc_text(PAGE, GetTimeoutDeltaFromRegistry)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the entry point for this EXPORT DRIVER. It does nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
TapeClassInitialize(
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2,
|
|
IN PTAPE_INIT_DATA_EX TapeInitData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by a tape mini-class driver during its
|
|
DriverEntry routine to initialize the driver.
|
|
|
|
Arguments:
|
|
|
|
Argument1 - Supplies the first argument to DriverEntry.
|
|
|
|
Argument2 - Supplies the second argument to DriverEntry.
|
|
|
|
TapeInitData - Supplies the tape initialization data.
|
|
|
|
Return Value:
|
|
|
|
A valid return code for a DriverEntry routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDRIVER_OBJECT driverObject = Argument1;
|
|
PUNICODE_STRING registryPath = Argument2;
|
|
PTAPE_INIT_DATA_EX driverExtension;
|
|
NTSTATUS status;
|
|
CLASS_INIT_DATA initializationData;
|
|
TAPE_INIT_DATA_EX tmpInitData;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((1,"\n\nSCSI Tape Class Driver\n"));
|
|
|
|
//
|
|
// Zero InitData
|
|
//
|
|
|
|
RtlZeroMemory (&tmpInitData, sizeof(TAPE_INIT_DATA_EX));
|
|
|
|
//
|
|
// Save the tape init data passed in from the miniclass driver. When AddDevice gets called, it will be used.
|
|
// First a check for 4.0 vs. later miniclass drivers.
|
|
//
|
|
|
|
if (TapeInitData->InitDataSize != sizeof(TAPE_INIT_DATA_EX)) {
|
|
|
|
//
|
|
// Earlier rev. Copy the bits around so that the EX structure is correct.
|
|
//
|
|
|
|
RtlCopyMemory(&tmpInitData.VerifyInquiry, TapeInitData, sizeof(TAPE_INIT_DATA));
|
|
//
|
|
// Mark it as an earlier rev.
|
|
//
|
|
|
|
tmpInitData.InitDataSize = sizeof(TAPE_INIT_DATA);
|
|
} else {
|
|
RtlCopyMemory(&tmpInitData, TapeInitData, sizeof(TAPE_INIT_DATA_EX));
|
|
}
|
|
|
|
//
|
|
// Get the driverExtension
|
|
|
|
status = IoAllocateDriverObjectExtension(driverObject,
|
|
TapeClassInitialize,
|
|
sizeof(TAPE_INIT_DATA_EX),
|
|
&driverExtension);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
if(status == STATUS_OBJECT_NAME_COLLISION) {
|
|
|
|
//
|
|
// An extension already exists for this key. Get a pointer to it
|
|
//
|
|
|
|
driverExtension = IoGetDriverObjectExtension(driverObject,
|
|
TapeClassInitialize);
|
|
if (driverExtension == NULL) {
|
|
DebugPrint((1, "TapeClassInitialize : driverExtension NULL\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// As this failed, the tape init data won't be able to be stored.
|
|
//
|
|
|
|
DebugPrint((1, "TapeClassInitialize: Error %x allocating driver extension.\n",
|
|
status));
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
RtlCopyMemory(driverExtension, &tmpInitData, sizeof(TAPE_INIT_DATA_EX));
|
|
|
|
RtlZeroMemory (&initializationData, sizeof(CLASS_INIT_DATA));
|
|
|
|
//
|
|
// Set sizes
|
|
//
|
|
|
|
initializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
|
|
|
|
|
|
initializationData.FdoData.DeviceExtensionSize = sizeof(FUNCTIONAL_DEVICE_EXTENSION) +
|
|
sizeof(TAPE_DATA) + tmpInitData.MinitapeExtensionSize;
|
|
|
|
initializationData.FdoData.DeviceType = FILE_DEVICE_TAPE;
|
|
initializationData.FdoData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA |
|
|
FILE_DEVICE_SECURE_OPEN;
|
|
|
|
//
|
|
// Set entry points
|
|
//
|
|
|
|
initializationData.FdoData.ClassStartDevice = TapeStartDevice;
|
|
initializationData.FdoData.ClassStopDevice = TapeStopDevice;
|
|
initializationData.FdoData.ClassInitDevice = TapeInitDevice;
|
|
initializationData.FdoData.ClassRemoveDevice = TapeRemoveDevice;
|
|
initializationData.ClassAddDevice = TapeAddDevice;
|
|
|
|
initializationData.FdoData.ClassError = TapeError;
|
|
initializationData.FdoData.ClassReadWriteVerification = TapeReadWriteVerification;
|
|
initializationData.FdoData.ClassDeviceControl = TapeDeviceControl;
|
|
|
|
|
|
initializationData.FdoData.ClassShutdownFlush = NULL;
|
|
initializationData.FdoData.ClassCreateClose = NULL;
|
|
|
|
//
|
|
// Routines for WMI support
|
|
//
|
|
initializationData.FdoData.ClassWmiInfo.GuidCount = 6;
|
|
initializationData.FdoData.ClassWmiInfo.GuidRegInfo = TapeWmiGuidList;
|
|
initializationData.FdoData.ClassWmiInfo.ClassQueryWmiRegInfo = TapeQueryWmiRegInfo;
|
|
initializationData.FdoData.ClassWmiInfo.ClassQueryWmiDataBlock = TapeQueryWmiDataBlock;
|
|
initializationData.FdoData.ClassWmiInfo.ClassSetWmiDataBlock = TapeSetWmiDataBlock;
|
|
initializationData.FdoData.ClassWmiInfo.ClassSetWmiDataItem = TapeSetWmiDataItem;
|
|
initializationData.FdoData.ClassWmiInfo.ClassExecuteWmiMethod = TapeExecuteWmiMethod;
|
|
initializationData.FdoData.ClassWmiInfo.ClassWmiFunctionControl = TapeWmiFunctionControl;
|
|
|
|
initializationData.ClassUnload = TapeUnload;
|
|
|
|
//
|
|
// Call the class init routine last, so can cleanup if it fails
|
|
//
|
|
|
|
status = ClassInitialize( driverObject, registryPath, &initializationData);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1, "TapeClassInitialize: Error %x from classinit\n", status));
|
|
TapeUnload(driverObject);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
TapeUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
UNREFERENCED_PARAMETER(DriverObject);
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
TapeAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates and initializes a new FDO for the corresponding
|
|
PDO. It may perform property queries on the FDO but cannot do any
|
|
media access operations.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Tape class driver object.
|
|
|
|
Pdo - the physical device object we are being added to
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
PTAPE_INIT_DATA_EX tapeInitData;
|
|
NTSTATUS status;
|
|
PULONG tapeCount;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get the saved-off tape init data.
|
|
//
|
|
|
|
tapeInitData = IoGetDriverObjectExtension(DriverObject, TapeClassInitialize);
|
|
|
|
ASSERT(tapeInitData);
|
|
|
|
//
|
|
// Get the address of the count of the number of tape devices already initialized.
|
|
//
|
|
|
|
tapeCount = &IoGetConfigurationInformation()->TapeCount;
|
|
|
|
status = CreateTapeDeviceObject(DriverObject,
|
|
PhysicalDeviceObject,
|
|
tapeInitData);
|
|
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
(*tapeCount)++;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CreateTapeDeviceObject(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN PTAPE_INIT_DATA_EX TapeInitData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates an object for the device.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
PhysicalDeviceObject - DeviceObject of the attached to device.
|
|
TapeInitData - Supplies the tape initialization data.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR deviceNameBuffer[64];
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PTAPE_INIT_DATA_EX tapeInitData;
|
|
PDEVICE_OBJECT lowerDevice;
|
|
PTAPE_DATA tapeData;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
|
|
WCHAR dosNameBuffer[64];
|
|
WCHAR wideNameBuffer[64];
|
|
UNICODE_STRING dosUnicodeString;
|
|
UNICODE_STRING deviceUnicodeString;
|
|
ULONG tapeCount;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((3,"CreateDeviceObject: Enter routine\n"));
|
|
|
|
lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
|
|
|
|
//
|
|
// Claim the device. Note that any errors after this
|
|
// will goto the generic handler, where the device will
|
|
// be released.
|
|
//
|
|
|
|
status = ClassClaimDevice(lowerDevice, FALSE);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Someone already had this device - we're in trouble
|
|
//
|
|
|
|
ObDereferenceObject(lowerDevice);
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Create device object for this device.
|
|
//
|
|
|
|
tapeCount = 0;
|
|
do {
|
|
sprintf(deviceNameBuffer,
|
|
"\\Device\\Tape%d",
|
|
tapeCount);
|
|
|
|
status = ClassCreateDeviceObject(DriverObject,
|
|
deviceNameBuffer,
|
|
PhysicalDeviceObject,
|
|
TRUE,
|
|
&deviceObject);
|
|
tapeCount++;
|
|
} while (status == STATUS_OBJECT_NAME_COLLISION);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1,"CreateTapeDeviceObjects: Can not create device %s\n",
|
|
deviceNameBuffer));
|
|
|
|
goto CreateTapeDeviceObjectExit;
|
|
}
|
|
|
|
//
|
|
// Indicate that IRPs should include MDLs.
|
|
//
|
|
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
fdoExtension = deviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Back pointer to device object.
|
|
//
|
|
|
|
fdoExtension->CommonExtension.DeviceObject = deviceObject;
|
|
|
|
//
|
|
// This is the physical device.
|
|
//
|
|
|
|
fdoExtension->CommonExtension.PartitionZeroExtension = fdoExtension;
|
|
|
|
//
|
|
// Initialize lock count to zero. The lock count is used to
|
|
// disable the ejection mechanism when media is mounted.
|
|
//
|
|
|
|
fdoExtension->LockCount = 0;
|
|
|
|
//
|
|
// Save system tape number
|
|
//
|
|
|
|
fdoExtension->DeviceNumber = tapeCount - 1;
|
|
|
|
//
|
|
// Set the alignment requirements for the device based on the
|
|
// host adapter requirements
|
|
//
|
|
|
|
if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) {
|
|
deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement;
|
|
}
|
|
|
|
//
|
|
// Save the device descriptors
|
|
//
|
|
|
|
fdoExtension->AdapterDescriptor = NULL;
|
|
fdoExtension->DeviceDescriptor = NULL;
|
|
|
|
//
|
|
// Attach to the PDO
|
|
//
|
|
|
|
fdoExtension->LowerPdo = PhysicalDeviceObject;
|
|
|
|
fdoExtension->CommonExtension.LowerDeviceObject =
|
|
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
|
|
|
|
if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) {
|
|
|
|
//
|
|
// The attach failed. Cleanup and return.
|
|
//
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto CreateTapeDeviceObjectExit;
|
|
}
|
|
|
|
//
|
|
// Save the tape initialization data.
|
|
//
|
|
|
|
RtlCopyMemory(fdoExtension->CommonExtension.DriverData, TapeInitData,sizeof(TAPE_INIT_DATA_EX));
|
|
|
|
//
|
|
// Initialize the splitrequest spinlock.
|
|
//
|
|
|
|
tapeData = (PTAPE_DATA)fdoExtension->CommonExtension.DriverData;
|
|
KeInitializeSpinLock(&tapeData->SplitRequestSpinLock);
|
|
|
|
//
|
|
// Create the dos port driver name.
|
|
//
|
|
|
|
swprintf(dosNameBuffer,
|
|
L"\\DosDevices\\TAPE%d",
|
|
fdoExtension->DeviceNumber);
|
|
|
|
RtlInitUnicodeString(&dosUnicodeString, dosNameBuffer);
|
|
|
|
//
|
|
// Recreate the deviceName
|
|
//
|
|
|
|
swprintf(wideNameBuffer,
|
|
L"\\Device\\Tape%d",
|
|
fdoExtension->DeviceNumber);
|
|
|
|
RtlInitUnicodeString(&deviceUnicodeString,
|
|
wideNameBuffer);
|
|
|
|
status = IoAssignArcName(&dosUnicodeString,
|
|
&deviceUnicodeString);
|
|
if (NT_SUCCESS(status)) {
|
|
tapeData->DosNameCreated = TRUE;
|
|
} else {
|
|
tapeData->DosNameCreated = FALSE;
|
|
}
|
|
|
|
//
|
|
// The device is initialized properly - mark it as such.
|
|
//
|
|
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
ObDereferenceObject(lowerDevice);
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
CreateTapeDeviceObjectExit:
|
|
|
|
//
|
|
// Release the device since an error occured.
|
|
//
|
|
|
|
// ClassClaimDevice(PortDeviceObject,
|
|
// LunInfo,
|
|
// TRUE,
|
|
// NULL);
|
|
|
|
ObDereferenceObject(lowerDevice);
|
|
|
|
if (deviceObject != NULL) {
|
|
IoDeleteDevice(deviceObject);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end CreateTapeDeviceObject()
|
|
|
|
|
|
NTSTATUS
|
|
TapeStartDevice(
|
|
IN PDEVICE_OBJECT Fdo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called after InitDevice, and creates the symbolic link,
|
|
and sets up information in the registry.
|
|
The routine could be called multiple times, in the event of a StopDevice.
|
|
|
|
|
|
Arguments:
|
|
|
|
Fdo - a pointer to the functional device object for this device
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PTAPE_DATA tapeData;
|
|
PTAPE_INIT_DATA_EX tapeInitData;
|
|
PINQUIRYDATA inquiryData;
|
|
ULONG inquiryLength;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb;
|
|
NTSTATUS status;
|
|
PVOID minitapeExtension;
|
|
PMODE_CAP_PAGE capPage = NULL ;
|
|
PMODE_CAPABILITIES_PAGE capabilitiesPage;
|
|
ULONG pageLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Build and send request to get inquiry data.
|
|
//
|
|
|
|
inquiryData = ExAllocatePool(NonPagedPoolCacheAligned, MAXIMUM_TAPE_INQUIRY_DATA);
|
|
if (!inquiryData) {
|
|
//
|
|
// The buffer cannot be allocated.
|
|
//
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Get the tape init data again.
|
|
//
|
|
|
|
tapeData = (PTAPE_DATA)(fdoExtension->CommonExtension.DriverData);
|
|
tapeInitData = &tapeData->TapeInitData;
|
|
|
|
RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
srb.TimeOutValue = 2;
|
|
|
|
srb.CdbLength = 6;
|
|
|
|
cdb = (PCDB)srb.Cdb;
|
|
|
|
//
|
|
// Set CDB operation code.
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
|
|
|
|
//
|
|
// Set allocation length to inquiry data buffer size.
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.AllocationLength = MAXIMUM_TAPE_INQUIRY_DATA;
|
|
|
|
status = ClassSendSrbSynchronous(Fdo,
|
|
&srb,
|
|
inquiryData,
|
|
MAXIMUM_TAPE_INQUIRY_DATA,
|
|
FALSE);
|
|
|
|
|
|
if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_SUCCESS ||
|
|
SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
srb.SrbStatus = SRB_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (srb.SrbStatus == SRB_STATUS_SUCCESS) {
|
|
inquiryLength = inquiryData->AdditionalLength + FIELD_OFFSET(INQUIRYDATA, Reserved);
|
|
|
|
if (inquiryLength > srb.DataTransferLength) {
|
|
inquiryLength = srb.DataTransferLength;
|
|
}
|
|
|
|
//
|
|
// Verify that we really want this device.
|
|
//
|
|
|
|
if (tapeInitData->QueryModeCapabilitiesPage ) {
|
|
|
|
capPage = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
sizeof(MODE_CAP_PAGE));
|
|
}
|
|
if (capPage) {
|
|
|
|
pageLength = ClassModeSense(Fdo,
|
|
(PCHAR) capPage,
|
|
sizeof(MODE_CAP_PAGE),
|
|
MODE_PAGE_CAPABILITIES);
|
|
|
|
if (pageLength == 0) {
|
|
pageLength = ClassModeSense(Fdo,
|
|
(PCHAR) capPage,
|
|
sizeof(MODE_CAP_PAGE),
|
|
MODE_PAGE_CAPABILITIES);
|
|
}
|
|
|
|
if (pageLength < (sizeof(MODE_CAP_PAGE) - 1)) {
|
|
ExFreePool(capPage);
|
|
capPage = NULL;
|
|
}
|
|
}
|
|
|
|
if (capPage) {
|
|
capabilitiesPage = &(capPage->CapabilitiesPage);
|
|
} else {
|
|
capabilitiesPage = NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the minitape extension.
|
|
//
|
|
|
|
if (tapeInitData->ExtensionInit) {
|
|
minitapeExtension = tapeData + 1;
|
|
tapeInitData->ExtensionInit(minitapeExtension,
|
|
inquiryData,
|
|
capabilitiesPage);
|
|
}
|
|
|
|
if (capPage) {
|
|
ExFreePool(capPage);
|
|
}
|
|
} else {
|
|
inquiryLength = 0;
|
|
}
|
|
|
|
//
|
|
// Add tape device number to registry
|
|
//
|
|
|
|
ClassUpdateInformationInRegistry(Fdo,
|
|
"Tape",
|
|
fdoExtension->DeviceNumber,
|
|
inquiryData,
|
|
inquiryLength);
|
|
|
|
ExFreePool(inquiryData);
|
|
|
|
status = IoSetDeviceInterfaceState(&(tapeData->TapeInterfaceString),
|
|
TRUE);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((1,
|
|
"TapeStartDevice: Unable to register Tape%x interface name - %x.\n",
|
|
fdoExtension->DeviceNumber,
|
|
status));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
TapeInitDevice(
|
|
IN PDEVICE_OBJECT Fdo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will complete the tape miniclass initialization. This includes
|
|
allocating sense info buffers and srb s-lists.
|
|
|
|
This routine will not clean up allocate resources if it fails - that
|
|
is left for device stop/removal
|
|
|
|
Arguments:
|
|
|
|
Fdo - a pointer to the functional device object for this device
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PVOID senseData = NULL;
|
|
PTAPE_DATA tapeData;
|
|
PTAPE_INIT_DATA_EX tapeInitData;
|
|
NTSTATUS status;
|
|
PVOID minitapeExtension;
|
|
STORAGE_PROPERTY_ID propertyId;
|
|
UNICODE_STRING interfaceName;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allocate request sense buffer.
|
|
//
|
|
|
|
senseData = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
SENSE_BUFFER_SIZE);
|
|
|
|
if (senseData == NULL) {
|
|
|
|
|
|
//
|
|
// The buffer cannot be allocated.
|
|
//
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
//
|
|
// Build the lookaside list for srb's for the physical disk. Should only
|
|
// need a couple.
|
|
//
|
|
|
|
ClassInitializeSrbLookasideList(&(fdoExtension->CommonExtension),
|
|
TAPE_SRB_LIST_SIZE);
|
|
|
|
//
|
|
// Set the sense data pointer in the device extension.
|
|
//
|
|
|
|
fdoExtension->SenseData = senseData;
|
|
|
|
fdoExtension->DiskGeometry.BytesPerSector = UNDEFINED_BLOCK_SIZE;
|
|
|
|
//
|
|
// Get the tape init data again.
|
|
//
|
|
|
|
tapeData = (PTAPE_DATA)(fdoExtension->CommonExtension.DriverData);
|
|
tapeInitData = &tapeData->TapeInitData;
|
|
|
|
//
|
|
// Set timeout value in seconds.
|
|
//
|
|
|
|
if (tapeInitData->DefaultTimeOutValue) {
|
|
fdoExtension->TimeOutValue = tapeInitData->DefaultTimeOutValue;
|
|
} else {
|
|
fdoExtension->TimeOutValue = 180;
|
|
}
|
|
|
|
//
|
|
// Used to keep track of the last time a drive clean
|
|
// notification was sent by the driver
|
|
//
|
|
tapeData->LastDriveCleanRequestTime.QuadPart = 0;
|
|
|
|
//
|
|
// SRB Timeout delta is used to increase the timeout for certain
|
|
// commands - typically, commands such as SET_POSITION, ERASE, etc.
|
|
//
|
|
tapeData->SrbTimeoutDelta = GetTimeoutDeltaFromRegistry(fdoExtension->LowerPdo);
|
|
if ((tapeData->SrbTimeoutDelta) == 0) {
|
|
tapeData->SrbTimeoutDelta = fdoExtension->TimeOutValue;
|
|
}
|
|
|
|
//
|
|
// Call port driver to get adapter capabilities.
|
|
//
|
|
|
|
propertyId = StorageAdapterProperty;
|
|
|
|
status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
|
|
&propertyId,
|
|
&(fdoExtension->AdapterDescriptor));
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((1,
|
|
"TapeStartDevice: Unable to get adapter descriptor. Status %x\n",
|
|
status));
|
|
ExFreePool(senseData);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Register for media change notification
|
|
//
|
|
ClassInitializeMediaChangeDetection(fdoExtension,
|
|
"Tape");
|
|
|
|
//
|
|
// Register interfaces for this device.
|
|
//
|
|
|
|
RtlInitUnicodeString(&tapeData->TapeInterfaceString, NULL);
|
|
|
|
status = IoRegisterDeviceInterface(fdoExtension->LowerPdo,
|
|
(LPGUID) &TapeClassGuid,
|
|
NULL,
|
|
&(tapeData->TapeInterfaceString));
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((1,
|
|
"TapeInitDevice: Unable to register Tape%x interface name - %x.\n",
|
|
fdoExtension->DeviceNumber,
|
|
status));
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
} // End TapeStartDevice
|
|
|
|
|
|
NTSTATUS
|
|
TapeRemoveDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR Type
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is responsible for releasing any resources in use by the
|
|
tape driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object being removed
|
|
|
|
Return Value:
|
|
|
|
none - this routine may not fail
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
|
PTAPE_DATA tapeData = (PTAPE_DATA)fdoExtension->CommonExtension.DriverData;
|
|
WCHAR dosNameBuffer[64];
|
|
UNICODE_STRING dosUnicodeString;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if((Type == IRP_MN_QUERY_REMOVE_DEVICE) ||
|
|
(Type == IRP_MN_CANCEL_REMOVE_DEVICE)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Free all allocated memory.
|
|
//
|
|
if (Type == IRP_MN_REMOVE_DEVICE){
|
|
if (fdoExtension->DeviceDescriptor) {
|
|
ExFreePool(fdoExtension->DeviceDescriptor);
|
|
fdoExtension->DeviceDescriptor = NULL;
|
|
}
|
|
if (fdoExtension->AdapterDescriptor) {
|
|
ExFreePool(fdoExtension->AdapterDescriptor);
|
|
fdoExtension->AdapterDescriptor = NULL;
|
|
}
|
|
if (fdoExtension->SenseData) {
|
|
ExFreePool(fdoExtension->SenseData);
|
|
fdoExtension->SenseData = NULL;
|
|
}
|
|
ClassDeleteSrbLookasideList(&fdoExtension->CommonExtension);
|
|
}
|
|
|
|
if(tapeData->TapeInterfaceString.Buffer != NULL) {
|
|
IoSetDeviceInterfaceState(&(tapeData->TapeInterfaceString),
|
|
FALSE);
|
|
|
|
RtlFreeUnicodeString(&(tapeData->TapeInterfaceString));
|
|
|
|
//
|
|
// Clear it.
|
|
//
|
|
|
|
RtlInitUnicodeString(&(tapeData->TapeInterfaceString), NULL);
|
|
}
|
|
|
|
if(tapeData->DosNameCreated) {
|
|
//
|
|
// Delete the symbolic link "tapeN".
|
|
//
|
|
|
|
swprintf(dosNameBuffer,
|
|
L"\\DosDevices\\TAPE%d",
|
|
fdoExtension->DeviceNumber);
|
|
|
|
RtlInitUnicodeString(&dosUnicodeString, dosNameBuffer);
|
|
|
|
IoDeleteSymbolicLink(&dosUnicodeString);
|
|
|
|
tapeData->DosNameCreated = FALSE;
|
|
}
|
|
|
|
IoGetConfigurationInformation()->TapeCount--;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
TapeStopDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR Type
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ScsiTapeNtStatusToTapeStatus(
|
|
IN NTSTATUS NtStatus,
|
|
OUT PTAPE_STATUS TapeStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine translates an NT status code to a TAPE status code.
|
|
|
|
Arguments:
|
|
|
|
NtStatus - Supplies the NT status code.
|
|
|
|
TapeStatus - Returns the tape status code.
|
|
|
|
Return Value:
|
|
|
|
FALSE - No tranlation was possible.
|
|
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (NtStatus) {
|
|
|
|
case STATUS_SUCCESS:
|
|
*TapeStatus = TAPE_STATUS_SUCCESS;
|
|
break;
|
|
|
|
case STATUS_INSUFFICIENT_RESOURCES:
|
|
*TapeStatus = TAPE_STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
case STATUS_NOT_IMPLEMENTED:
|
|
*TapeStatus = TAPE_STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case STATUS_INVALID_DEVICE_REQUEST:
|
|
*TapeStatus = TAPE_STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case STATUS_INVALID_PARAMETER:
|
|
*TapeStatus = TAPE_STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
case STATUS_VERIFY_REQUIRED:
|
|
case STATUS_MEDIA_CHANGED:
|
|
*TapeStatus = TAPE_STATUS_MEDIA_CHANGED;
|
|
break;
|
|
|
|
case STATUS_BUS_RESET:
|
|
*TapeStatus = TAPE_STATUS_BUS_RESET;
|
|
break;
|
|
|
|
case STATUS_SETMARK_DETECTED:
|
|
*TapeStatus = TAPE_STATUS_SETMARK_DETECTED;
|
|
break;
|
|
|
|
case STATUS_FILEMARK_DETECTED:
|
|
*TapeStatus = TAPE_STATUS_FILEMARK_DETECTED;
|
|
break;
|
|
|
|
case STATUS_BEGINNING_OF_MEDIA:
|
|
*TapeStatus = TAPE_STATUS_BEGINNING_OF_MEDIA;
|
|
break;
|
|
|
|
case STATUS_END_OF_MEDIA:
|
|
*TapeStatus = TAPE_STATUS_END_OF_MEDIA;
|
|
break;
|
|
|
|
case STATUS_BUFFER_OVERFLOW:
|
|
*TapeStatus = TAPE_STATUS_BUFFER_OVERFLOW;
|
|
break;
|
|
|
|
case STATUS_NO_DATA_DETECTED:
|
|
*TapeStatus = TAPE_STATUS_NO_DATA_DETECTED;
|
|
break;
|
|
|
|
case STATUS_EOM_OVERFLOW:
|
|
*TapeStatus = TAPE_STATUS_EOM_OVERFLOW;
|
|
break;
|
|
|
|
case STATUS_NO_MEDIA:
|
|
case STATUS_NO_MEDIA_IN_DEVICE:
|
|
*TapeStatus = TAPE_STATUS_NO_MEDIA;
|
|
break;
|
|
|
|
case STATUS_IO_DEVICE_ERROR:
|
|
case STATUS_NONEXISTENT_SECTOR:
|
|
*TapeStatus = TAPE_STATUS_IO_DEVICE_ERROR;
|
|
break;
|
|
|
|
case STATUS_UNRECOGNIZED_MEDIA:
|
|
*TapeStatus = TAPE_STATUS_UNRECOGNIZED_MEDIA;
|
|
break;
|
|
|
|
case STATUS_DEVICE_NOT_READY:
|
|
*TapeStatus = TAPE_STATUS_DEVICE_NOT_READY;
|
|
break;
|
|
|
|
case STATUS_MEDIA_WRITE_PROTECTED:
|
|
*TapeStatus = TAPE_STATUS_MEDIA_WRITE_PROTECTED;
|
|
break;
|
|
|
|
case STATUS_DEVICE_DATA_ERROR:
|
|
*TapeStatus = TAPE_STATUS_DEVICE_DATA_ERROR;
|
|
break;
|
|
|
|
case STATUS_NO_SUCH_DEVICE:
|
|
*TapeStatus = TAPE_STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
|
|
case STATUS_INVALID_BLOCK_LENGTH:
|
|
*TapeStatus = TAPE_STATUS_INVALID_BLOCK_LENGTH;
|
|
break;
|
|
|
|
case STATUS_IO_TIMEOUT:
|
|
*TapeStatus = TAPE_STATUS_IO_TIMEOUT;
|
|
break;
|
|
|
|
case STATUS_DEVICE_NOT_CONNECTED:
|
|
*TapeStatus = TAPE_STATUS_DEVICE_NOT_CONNECTED;
|
|
break;
|
|
|
|
case STATUS_DATA_OVERRUN:
|
|
*TapeStatus = TAPE_STATUS_DATA_OVERRUN;
|
|
break;
|
|
|
|
case STATUS_DEVICE_BUSY:
|
|
*TapeStatus = TAPE_STATUS_DEVICE_BUSY;
|
|
break;
|
|
|
|
case STATUS_CLEANER_CARTRIDGE_INSTALLED:
|
|
*TapeStatus = TAPE_STATUS_CLEANER_CARTRIDGE_INSTALLED;
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ScsiTapeTapeStatusToNtStatus(
|
|
IN TAPE_STATUS TapeStatus,
|
|
OUT PNTSTATUS NtStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine translates a TAPE status code to an NT status code.
|
|
|
|
Arguments:
|
|
|
|
TapeStatus - Supplies the tape status code.
|
|
|
|
NtStatus - Returns the NT status code.
|
|
|
|
|
|
Return Value:
|
|
|
|
FALSE - No tranlation was possible.
|
|
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (TapeStatus) {
|
|
|
|
case TAPE_STATUS_SUCCESS:
|
|
*NtStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case TAPE_STATUS_INSUFFICIENT_RESOURCES:
|
|
*NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
case TAPE_STATUS_NOT_IMPLEMENTED:
|
|
*NtStatus = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case TAPE_STATUS_INVALID_DEVICE_REQUEST:
|
|
*NtStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case TAPE_STATUS_INVALID_PARAMETER:
|
|
*NtStatus = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
case TAPE_STATUS_MEDIA_CHANGED:
|
|
*NtStatus = STATUS_VERIFY_REQUIRED;
|
|
break;
|
|
|
|
case TAPE_STATUS_BUS_RESET:
|
|
*NtStatus = STATUS_BUS_RESET;
|
|
break;
|
|
|
|
case TAPE_STATUS_SETMARK_DETECTED:
|
|
*NtStatus = STATUS_SETMARK_DETECTED;
|
|
break;
|
|
|
|
case TAPE_STATUS_FILEMARK_DETECTED:
|
|
*NtStatus = STATUS_FILEMARK_DETECTED;
|
|
break;
|
|
|
|
case TAPE_STATUS_BEGINNING_OF_MEDIA:
|
|
*NtStatus = STATUS_BEGINNING_OF_MEDIA;
|
|
break;
|
|
|
|
case TAPE_STATUS_END_OF_MEDIA:
|
|
*NtStatus = STATUS_END_OF_MEDIA;
|
|
break;
|
|
|
|
case TAPE_STATUS_BUFFER_OVERFLOW:
|
|
*NtStatus = STATUS_BUFFER_OVERFLOW;
|
|
break;
|
|
|
|
case TAPE_STATUS_NO_DATA_DETECTED:
|
|
*NtStatus = STATUS_NO_DATA_DETECTED;
|
|
break;
|
|
|
|
case TAPE_STATUS_EOM_OVERFLOW:
|
|
*NtStatus = STATUS_EOM_OVERFLOW;
|
|
break;
|
|
|
|
case TAPE_STATUS_NO_MEDIA:
|
|
*NtStatus = STATUS_NO_MEDIA;
|
|
break;
|
|
|
|
case TAPE_STATUS_IO_DEVICE_ERROR:
|
|
*NtStatus = STATUS_IO_DEVICE_ERROR;
|
|
break;
|
|
|
|
case TAPE_STATUS_UNRECOGNIZED_MEDIA:
|
|
*NtStatus = STATUS_UNRECOGNIZED_MEDIA;
|
|
break;
|
|
|
|
case TAPE_STATUS_DEVICE_NOT_READY:
|
|
*NtStatus = STATUS_DEVICE_NOT_READY;
|
|
break;
|
|
|
|
case TAPE_STATUS_MEDIA_WRITE_PROTECTED:
|
|
*NtStatus = STATUS_MEDIA_WRITE_PROTECTED;
|
|
break;
|
|
|
|
case TAPE_STATUS_DEVICE_DATA_ERROR:
|
|
*NtStatus = STATUS_DEVICE_DATA_ERROR;
|
|
break;
|
|
|
|
case TAPE_STATUS_NO_SUCH_DEVICE:
|
|
*NtStatus = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
|
|
case TAPE_STATUS_INVALID_BLOCK_LENGTH:
|
|
*NtStatus = STATUS_INVALID_BLOCK_LENGTH;
|
|
break;
|
|
|
|
case TAPE_STATUS_IO_TIMEOUT:
|
|
*NtStatus = STATUS_IO_TIMEOUT;
|
|
break;
|
|
|
|
case TAPE_STATUS_DEVICE_NOT_CONNECTED:
|
|
*NtStatus = STATUS_DEVICE_NOT_CONNECTED;
|
|
break;
|
|
|
|
case TAPE_STATUS_DATA_OVERRUN:
|
|
*NtStatus = STATUS_DATA_OVERRUN;
|
|
break;
|
|
|
|
case TAPE_STATUS_DEVICE_BUSY:
|
|
*NtStatus = STATUS_DEVICE_BUSY;
|
|
break;
|
|
|
|
case TAPE_STATUS_REQUIRES_CLEANING:
|
|
*NtStatus = STATUS_DEVICE_REQUIRES_CLEANING;
|
|
break;
|
|
|
|
case TAPE_STATUS_CLEANER_CARTRIDGE_INSTALLED:
|
|
*NtStatus = STATUS_CLEANER_CARTRIDGE_INSTALLED;
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
TapeError(
|
|
IN PDEVICE_OBJECT FDO,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN OUT PNTSTATUS Status,
|
|
IN OUT PBOOLEAN Retry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When a request completes with error, the routine ScsiClassInterpretSenseInfo is
|
|
called to determine from the sense data whether the request should be
|
|
retried and what NT status to set in the IRP. Then this routine is called
|
|
for tape requests to handle tape-specific errors and update the nt status
|
|
and retry boolean.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies a pointer to the device object.
|
|
|
|
Srb - Supplies a pointer to the failing Srb.
|
|
|
|
Status - NT Status used to set the IRP's completion status.
|
|
|
|
Retry - Indicates that this request should be retried.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = FDO->DeviceExtension;
|
|
PTAPE_DATA tapeData = (PTAPE_DATA)(fdoExtension->CommonExtension.DriverData);
|
|
PTAPE_INIT_DATA_EX tapeInitData = &tapeData->TapeInitData;
|
|
PVOID minitapeExtension = (tapeData + 1);
|
|
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
|
PIRP irp = Srb->OriginalRequest;
|
|
LONG residualBlocks;
|
|
LONG length;
|
|
TAPE_STATUS tapeStatus, oldTapeStatus;
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure[2];
|
|
|
|
//
|
|
// Never retry tape requests.
|
|
//
|
|
|
|
*Retry = FALSE;
|
|
|
|
//
|
|
// Check that request sense buffer is valid.
|
|
//
|
|
|
|
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
|
|
|
|
DebugPrint((1,
|
|
"Sense Code : %x, Ad Sense : %x, Ad Sense Qual : %x\n",
|
|
((senseBuffer->SenseKey) & 0xf),
|
|
(senseBuffer->AdditionalSenseCode),
|
|
(senseBuffer->AdditionalSenseCodeQualifier)));
|
|
|
|
switch (senseBuffer->SenseKey & 0xf) {
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|
|
|
switch (senseBuffer->AdditionalSenseCode) {
|
|
|
|
case SCSI_ADSENSE_MEDIUM_CHANGED:
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Media changed\n"));
|
|
|
|
*Status = STATUS_MEDIA_CHANGED;
|
|
|
|
break;
|
|
|
|
default:
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Bus reset\n"));
|
|
|
|
*Status = STATUS_BUS_RESET;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_RECOVERED_ERROR:
|
|
|
|
//
|
|
// Check other indicators
|
|
//
|
|
|
|
if (senseBuffer->FileMark) {
|
|
|
|
switch (senseBuffer->AdditionalSenseCodeQualifier) {
|
|
|
|
case SCSI_SENSEQ_SETMARK_DETECTED :
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Setmark detected\n"));
|
|
|
|
*Status = STATUS_SETMARK_DETECTED;
|
|
break ;
|
|
|
|
case SCSI_SENSEQ_FILEMARK_DETECTED :
|
|
default:
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Filemark detected\n"));
|
|
|
|
*Status = STATUS_FILEMARK_DETECTED;
|
|
break ;
|
|
|
|
}
|
|
|
|
} else if ( senseBuffer->EndOfMedia ) {
|
|
|
|
switch( senseBuffer->AdditionalSenseCodeQualifier ) {
|
|
|
|
case SCSI_SENSEQ_BEGINNING_OF_MEDIA_DETECTED :
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Beginning of media detected\n"));
|
|
|
|
*Status = STATUS_BEGINNING_OF_MEDIA;
|
|
break ;
|
|
|
|
case SCSI_SENSEQ_END_OF_MEDIA_DETECTED :
|
|
default:
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: End of media detected\n"));
|
|
|
|
*Status = STATUS_END_OF_MEDIA;
|
|
break ;
|
|
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_NO_SENSE:
|
|
|
|
//
|
|
// Check other indicators
|
|
//
|
|
|
|
if (senseBuffer->FileMark) {
|
|
|
|
switch( senseBuffer->AdditionalSenseCodeQualifier ) {
|
|
|
|
case SCSI_SENSEQ_SETMARK_DETECTED :
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Setmark detected\n"));
|
|
|
|
*Status = STATUS_SETMARK_DETECTED;
|
|
break ;
|
|
|
|
case SCSI_SENSEQ_FILEMARK_DETECTED :
|
|
default:
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Filemark detected\n"));
|
|
|
|
*Status = STATUS_FILEMARK_DETECTED;
|
|
break ;
|
|
}
|
|
|
|
} else if (senseBuffer->EndOfMedia) {
|
|
|
|
switch(senseBuffer->AdditionalSenseCodeQualifier) {
|
|
|
|
case SCSI_SENSEQ_BEGINNING_OF_MEDIA_DETECTED :
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Beginning of media detected\n"));
|
|
|
|
*Status = STATUS_BEGINNING_OF_MEDIA;
|
|
break ;
|
|
|
|
case SCSI_SENSEQ_END_OF_MEDIA_DETECTED :
|
|
default:
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: End of media detected\n"));
|
|
|
|
*Status = STATUS_END_OF_MEDIA;
|
|
break;
|
|
|
|
}
|
|
} else if (senseBuffer->IncorrectLength) {
|
|
|
|
//
|
|
// If we're in variable block mode then ignore
|
|
// incorrect length.
|
|
//
|
|
|
|
if (fdoExtension->DiskGeometry.BytesPerSector == 0 &&
|
|
Srb->Cdb[0] == SCSIOP_READ6) {
|
|
|
|
REVERSE_BYTES((FOUR_BYTE UNALIGNED *)&residualBlocks,
|
|
(FOUR_BYTE UNALIGNED *)(senseBuffer->Information));
|
|
|
|
if (residualBlocks >= 0) {
|
|
DebugPrint((1,"InterpretSenseInfo: In variable block mode :We read less than specified\n"));
|
|
*Status = STATUS_SUCCESS;
|
|
} else {
|
|
DebugPrint((1,"InterpretSenseInfo: In variable block mode :Data left in block\n"));
|
|
*Status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SCSI_SENSE_BLANK_CHECK:
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: Media blank check\n"));
|
|
|
|
*Status = STATUS_NO_DATA_DETECTED;
|
|
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_VOL_OVERFLOW:
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo: End of Media Overflow\n"));
|
|
|
|
*Status = STATUS_EOM_OVERFLOW;
|
|
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_NOT_READY:
|
|
|
|
switch (senseBuffer->AdditionalSenseCode) {
|
|
|
|
case SCSI_ADSENSE_LUN_NOT_READY:
|
|
|
|
switch (senseBuffer->AdditionalSenseCodeQualifier) {
|
|
|
|
case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
|
|
|
|
*Status = STATUS_NO_MEDIA;
|
|
break;
|
|
|
|
case SCSI_SENSEQ_FORMAT_IN_PROGRESS:
|
|
break;
|
|
|
|
case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
|
|
default:
|
|
|
|
//
|
|
// Allow retries, if the drive isn't ready.
|
|
//
|
|
|
|
*Retry = TRUE;
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE:
|
|
|
|
DebugPrint((1,
|
|
"InterpretSenseInfo:"
|
|
" No Media in device.\n"));
|
|
*Status = STATUS_NO_MEDIA;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
} // end switch
|
|
|
|
//
|
|
// Check if a filemark or setmark was encountered,
|
|
// or an end-of-media or no-data condition exists.
|
|
//
|
|
|
|
if ((NT_WARNING(*Status) || NT_SUCCESS( *Status)) &&
|
|
(Srb->Cdb[0] == SCSIOP_WRITE6 || Srb->Cdb[0] == SCSIOP_READ6)) {
|
|
|
|
LONG actualLength;
|
|
|
|
//
|
|
// Not all bytes were transfered. Update information field with
|
|
// number of bytes transfered from sense buffer.
|
|
//
|
|
|
|
if (senseBuffer->Valid) {
|
|
REVERSE_BYTES((FOUR_BYTE UNALIGNED *)&residualBlocks,
|
|
(FOUR_BYTE UNALIGNED *)(senseBuffer->Information));
|
|
} else {
|
|
residualBlocks = 0;
|
|
}
|
|
|
|
length = ((PCDB) Srb->Cdb)->CDB6READWRITETAPE.TransferLenLSB;
|
|
length |= ((PCDB) Srb->Cdb)->CDB6READWRITETAPE.TransferLen << 8;
|
|
length |= ((PCDB) Srb->Cdb)->CDB6READWRITETAPE.TransferLenMSB << 16;
|
|
|
|
actualLength = length;
|
|
|
|
length -= residualBlocks;
|
|
|
|
if (length < 0) {
|
|
|
|
length = 0;
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
|
|
|
|
if (fdoExtension->DiskGeometry.BytesPerSector) {
|
|
actualLength *= fdoExtension->DiskGeometry.BytesPerSector;
|
|
length *= fdoExtension->DiskGeometry.BytesPerSector;
|
|
}
|
|
|
|
if (length > actualLength) {
|
|
length = actualLength;
|
|
}
|
|
|
|
irp->IoStatus.Information = length;
|
|
|
|
DebugPrint((1,"ScsiTapeError: Transfer Count: %lx\n", Srb->DataTransferLength));
|
|
DebugPrint((1," Residual Blocks: %lx\n", residualBlocks));
|
|
DebugPrint((1," Irp IoStatus Information = %lx\n", irp->IoStatus.Information));
|
|
}
|
|
|
|
} else {
|
|
DebugPrint((1, "SRB Status : %x, SCSI Status : %x\n",
|
|
SRB_STATUS(Srb->SrbStatus),
|
|
(Srb->ScsiStatus)));
|
|
|
|
}
|
|
|
|
//
|
|
// Call tape device specific error handler.
|
|
//
|
|
|
|
if (tapeInitData->TapeError &&
|
|
ScsiTapeNtStatusToTapeStatus(*Status, &tapeStatus)) {
|
|
|
|
oldTapeStatus = tapeStatus;
|
|
tapeInitData->TapeError(minitapeExtension, Srb, &tapeStatus);
|
|
if (tapeStatus != oldTapeStatus) {
|
|
ScsiTapeTapeStatusToNtStatus(tapeStatus, Status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Notify the system that this tape drive requires cleaning
|
|
//
|
|
if ((*Status) == STATUS_DEVICE_REQUIRES_CLEANING) {
|
|
LARGE_INTEGER currentTime;
|
|
LARGE_INTEGER driveCleanInterval;
|
|
|
|
KeQuerySystemTime(¤tTime);
|
|
driveCleanInterval.QuadPart = ONE_SECOND;
|
|
driveCleanInterval.QuadPart *= TAPE_DRIVE_CLEAN_NOTIFICATION_INTERVAL;
|
|
if ((currentTime.QuadPart) >
|
|
((tapeData->LastDriveCleanRequestTime.QuadPart) +
|
|
(driveCleanInterval.QuadPart))) {
|
|
NotificationStructure[0].Event = GUID_IO_DRIVE_REQUIRES_CLEANING;
|
|
NotificationStructure[0].Version = 1;
|
|
NotificationStructure[0].Size = sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) +
|
|
sizeof(ULONG) - sizeof(UCHAR);
|
|
NotificationStructure[0].FileObject = NULL;
|
|
NotificationStructure[0].NameBufferOffset = -1;
|
|
|
|
//
|
|
// Increasing Index for this event
|
|
//
|
|
|
|
*((PULONG) (&(NotificationStructure[0].CustomDataBuffer[0]))) = 0;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&NotificationStructure[0],
|
|
NULL,
|
|
NULL);
|
|
tapeData->LastDriveCleanRequestTime.QuadPart = currentTime.QuadPart;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // end ScsiTapeError()
|
|
|
|
|
|
NTSTATUS
|
|
TapeReadWriteVerification(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds up the given irp for a read or write request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
|
|
PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor = fdoExtension->CommonExtension.PartitionZeroExtension->AdapterDescriptor;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
ULONG transferPages;
|
|
ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
|
|
LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
|
|
ULONG maximumTransferLength = adapterDescriptor->MaximumTransferLength;
|
|
ULONG bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Since most tape devices don't support 10-byte read/write, the entire request must be dealt with here.
|
|
// STATUS_PENDING will be returned to the classpnp driver, so that it does nothing.
|
|
//
|
|
|
|
//
|
|
// Ensure that the request is for something valid - ie. not 0.
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.Read.Length == 0) {
|
|
|
|
//
|
|
// Class code will handle this.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Check that blocksize has been established.
|
|
//
|
|
|
|
if (bytesPerSector == UNDEFINED_BLOCK_SIZE) {
|
|
|
|
DebugPrint((1,
|
|
"TapeReadWriteVerification: Invalid block size - UNDEFINED\n"));
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// ClassPnp will handle completing the request.
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (bytesPerSector) {
|
|
if (transferByteCount % bytesPerSector) {
|
|
|
|
DebugPrint((1,
|
|
"TapeReadWriteVerification: Invalid block size\n"));
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// ClassPnp will handle completing the request.
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Calculate number of pages in this transfer.
|
|
//
|
|
|
|
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
|
|
currentIrpStack->Parameters.Read.Length);
|
|
|
|
//
|
|
// Check if request length is greater than the maximum number of
|
|
// bytes that the hardware can transfer.
|
|
//
|
|
|
|
|
|
|
|
//
|
|
// Calculate number of pages in this transfer.
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.Read.Length > maximumTransferLength ||
|
|
transferPages > adapterDescriptor->MaximumPhysicalPages) {
|
|
|
|
DebugPrint((2,
|
|
"TapeReadWriteVerification: Request greater than maximum\n"));
|
|
DebugPrint((2,
|
|
"TapeReadWriteVerification: Maximum is %lx\n",
|
|
maximumTransferLength));
|
|
DebugPrint((2,
|
|
"TapeReadWriteVerification: Byte count is %lx\n",
|
|
currentIrpStack->Parameters.Read.Length));
|
|
|
|
transferPages = adapterDescriptor->MaximumPhysicalPages - 1;
|
|
|
|
if (maximumTransferLength > transferPages << PAGE_SHIFT ) {
|
|
maximumTransferLength = transferPages << PAGE_SHIFT;
|
|
}
|
|
|
|
//
|
|
// Check that maximum transfer size is not zero.
|
|
//
|
|
|
|
if (maximumTransferLength == 0) {
|
|
maximumTransferLength = PAGE_SIZE;
|
|
}
|
|
|
|
//
|
|
// Ensure that this is reasonable, according to the current block size.
|
|
//
|
|
|
|
if (bytesPerSector) {
|
|
if (maximumTransferLength % bytesPerSector) {
|
|
ULONG tmpLength;
|
|
|
|
tmpLength = maximumTransferLength % bytesPerSector;
|
|
maximumTransferLength = maximumTransferLength - tmpLength;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark IRP with status pending.
|
|
//
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
//
|
|
// Request greater than port driver maximum.
|
|
// Break up into smaller routines.
|
|
//
|
|
SplitTapeRequest(DeviceObject, Irp, maximumTransferLength);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
//
|
|
// Build SRB and CDB for this IRP.
|
|
//
|
|
|
|
TapeReadWrite(DeviceObject, Irp);
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
IoCallDriver(commonExtension->LowerDeviceObject, Irp);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
SplitTapeRequest(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp,
|
|
IN ULONG MaximumBytes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Break request into smaller requests.
|
|
Each new request will be the maximum transfer
|
|
size that the port driver can handle or if it
|
|
is the final request, it may be the residual
|
|
size.
|
|
|
|
The number of IRPs required to process this
|
|
request is written in the current stack of
|
|
the original IRP. Then as each new IRP completes
|
|
the count in the original IRP is decremented.
|
|
When the count goes to zero, the original IRP
|
|
is completed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object
|
|
Irp - Pointer to Irp
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|
ULONG irpCount;
|
|
ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
|
|
ULONG dataLength = MaximumBytes;
|
|
PVOID dataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|
LONG remainingIrps;
|
|
BOOLEAN completeOriginalIrp = FALSE;
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Caluculate number of requests to break this IRP into.
|
|
//
|
|
|
|
irpCount = (transferByteCount + MaximumBytes - 1) / MaximumBytes;
|
|
|
|
DebugPrint((2,
|
|
"SplitTapeRequest: Requires %d IRPs\n", irpCount));
|
|
DebugPrint((2,
|
|
"SplitTapeRequest: Original IRP %p\n", Irp));
|
|
|
|
//
|
|
// If all partial transfers complete successfully then
|
|
// the status is already set up.
|
|
// Failing partial transfer IRP will set status to
|
|
// error and bytes transferred to 0 during IoCompletion.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
//CEP Irp->IoStatus.Information = transferByteCount;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Save number of IRPs to complete count on current stack
|
|
// of original IRP.
|
|
//
|
|
|
|
nextIrpStack->Parameters.Others.Argument1 = ULongToPtr( irpCount );
|
|
|
|
for (i = 0; i < irpCount; i++) {
|
|
|
|
PIRP newIrp;
|
|
PIO_STACK_LOCATION newIrpStack;
|
|
|
|
//
|
|
// Allocate new IRP.
|
|
//
|
|
|
|
newIrp = IoAllocateIrp(Fdo->StackSize, FALSE);
|
|
|
|
if (newIrp == NULL) {
|
|
|
|
DebugPrint((1,
|
|
"SplitTapeRequest: Can't allocate Irp\n"));
|
|
|
|
//
|
|
// Decrement count of outstanding partial requests.
|
|
//
|
|
|
|
remainingIrps = InterlockedDecrement((PLONG)&nextIrpStack->Parameters.Others.Argument1);
|
|
|
|
//
|
|
// Check if any outstanding IRPs.
|
|
//
|
|
|
|
if (remainingIrps == 0) {
|
|
completeOriginalIrp = TRUE;
|
|
}
|
|
|
|
//
|
|
// Update original IRP with failing status.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Keep going with this request as outstanding partials
|
|
// may be in progress.
|
|
//
|
|
|
|
goto KeepGoing;
|
|
}
|
|
|
|
DebugPrint((2,
|
|
"SplitTapeRequest: New IRP %p\n", newIrp));
|
|
|
|
//
|
|
// Write MDL address to new IRP.
|
|
// In the port driver the SRB data length
|
|
// field is used as an offset into the MDL,
|
|
// so the same MDL can be used for each partial
|
|
// transfer. This saves having to build a new
|
|
// MDL for each partial transfer.
|
|
//
|
|
|
|
newIrp->MdlAddress = Irp->MdlAddress;
|
|
|
|
//
|
|
// At this point there is no current stack.
|
|
// IoSetNextIrpStackLocation will make the
|
|
// first stack location the current stack
|
|
// so that the SRB address can be written
|
|
// there.
|
|
//
|
|
|
|
IoSetNextIrpStackLocation(newIrp);
|
|
|
|
newIrpStack = IoGetCurrentIrpStackLocation(newIrp);
|
|
|
|
newIrpStack->MajorFunction = currentIrpStack->MajorFunction;
|
|
|
|
newIrpStack->Parameters.Read.Length = dataLength;
|
|
newIrpStack->Parameters.Read.ByteOffset = startingOffset;
|
|
|
|
newIrpStack->DeviceObject = Fdo;
|
|
|
|
//
|
|
// Build SRB and CDB.
|
|
//
|
|
|
|
TapeReadWrite(Fdo, newIrp);
|
|
|
|
//
|
|
// Adjust SRB for this partial transfer.
|
|
//
|
|
|
|
newIrpStack = IoGetNextIrpStackLocation(newIrp);
|
|
|
|
srb = newIrpStack->Parameters.Others.Argument1;
|
|
|
|
srb->DataBuffer = dataBuffer;
|
|
|
|
//
|
|
// Write original IRP address to new IRP.
|
|
//
|
|
|
|
newIrp->AssociatedIrp.MasterIrp = Irp;
|
|
|
|
//
|
|
// Set the completion routine to TapeIoCompleteAssociated.
|
|
//
|
|
|
|
IoSetCompletionRoutine(newIrp,
|
|
TapeIoCompleteAssociated,
|
|
srb,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
//
|
|
// Call port driver with new request.
|
|
//
|
|
|
|
status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, newIrp);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((1,
|
|
"SplitTapeRequest: IoCallDriver returned error\n"));
|
|
|
|
//
|
|
// Decrement count of outstanding partial requests.
|
|
//
|
|
|
|
remainingIrps = InterlockedDecrement((PLONG)&nextIrpStack->Parameters.Others.Argument1);
|
|
|
|
//
|
|
// Check if any outstanding IRPs.
|
|
//
|
|
|
|
if (remainingIrps == 0) {
|
|
completeOriginalIrp = TRUE;
|
|
}
|
|
|
|
//
|
|
// Update original IRP with failing status.
|
|
//
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Deallocate this partial IRP.
|
|
//
|
|
|
|
IoFreeIrp(newIrp);
|
|
}
|
|
|
|
KeepGoing:
|
|
|
|
//
|
|
// Set up for next request.
|
|
//
|
|
|
|
dataBuffer = (PCHAR)dataBuffer + MaximumBytes;
|
|
|
|
transferByteCount -= MaximumBytes;
|
|
|
|
if (transferByteCount > MaximumBytes) {
|
|
|
|
dataLength = MaximumBytes;
|
|
|
|
} else {
|
|
|
|
dataLength = transferByteCount;
|
|
}
|
|
|
|
//
|
|
// Adjust disk byte offset.
|
|
//
|
|
|
|
startingOffset.QuadPart += MaximumBytes;
|
|
}
|
|
|
|
//
|
|
// Check if original IRP should be completed.
|
|
//
|
|
|
|
if (completeOriginalIrp) {
|
|
|
|
ClassReleaseRemoveLock(Fdo, Irp);
|
|
ClassCompleteRequest(Fdo, Irp, 0);
|
|
}
|
|
|
|
return;
|
|
|
|
} // end SplitTapeRequest()
|
|
|
|
|
|
|
|
VOID
|
|
TapeReadWrite(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds up the given irp for a read or write request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PTAPE_DATA tapeData = (PTAPE_DATA)(fdoExtension->CommonExtension.DriverData);
|
|
PTAPE_INIT_DATA_EX tapeInitData = &tapeData->TapeInitData;
|
|
PVOID minitapeExtension = (tapeData + 1);
|
|
PIO_STACK_LOCATION irpSp, nextSp;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb;
|
|
ULONG transferBlocks;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allocate an Srb.
|
|
//
|
|
|
|
srb = ExAllocateFromNPagedLookasideList(&(fdoExtension->CommonExtension.SrbLookasideList));
|
|
|
|
srb->SrbFlags = 0;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (irpSp->MajorFunction == IRP_MJ_READ) {
|
|
srb->SrbFlags |= SRB_FLAGS_DATA_IN;
|
|
} else {
|
|
srb->SrbFlags |= SRB_FLAGS_DATA_OUT;
|
|
}
|
|
|
|
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb->SrbStatus = 0;
|
|
srb->ScsiStatus = 0;
|
|
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
|
srb->SrbFlags |= fdoExtension->SrbFlags;
|
|
srb->DataTransferLength = irpSp->Parameters.Read.Length;
|
|
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
|
srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|
srb->SenseInfoBuffer = fdoExtension->SenseData;
|
|
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
srb->NextSrb = NULL;
|
|
srb->OriginalRequest = Irp;
|
|
srb->SrbExtension = NULL;
|
|
srb->QueueSortKey = 0;
|
|
|
|
//
|
|
// Indicate that 6-byte CDB's will be used.
|
|
//
|
|
|
|
srb->CdbLength = CDB6GENERIC_LENGTH;
|
|
|
|
//
|
|
// Fill in CDB fields.
|
|
//
|
|
|
|
cdb = (PCDB)srb->Cdb;
|
|
|
|
//
|
|
// Zero CDB in SRB.
|
|
//
|
|
|
|
RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);
|
|
|
|
if (fdoExtension->DiskGeometry.BytesPerSector) {
|
|
|
|
//
|
|
// Since we are writing fixed block mode, normalize transfer count
|
|
// to number of blocks.
|
|
//
|
|
|
|
transferBlocks = irpSp->Parameters.Read.Length / fdoExtension->DiskGeometry.BytesPerSector;
|
|
|
|
//
|
|
// Tell the device that we are in fixed block mode.
|
|
//
|
|
|
|
cdb->CDB6READWRITETAPE.VendorSpecific = 1;
|
|
} else {
|
|
|
|
//
|
|
// Variable block mode transfer.
|
|
//
|
|
|
|
transferBlocks = irpSp->Parameters.Read.Length;
|
|
cdb->CDB6READWRITETAPE.VendorSpecific = 0;
|
|
}
|
|
|
|
//
|
|
// Set up transfer length
|
|
//
|
|
|
|
cdb->CDB6READWRITETAPE.TransferLenMSB = (UCHAR)((transferBlocks >> 16) & 0xff);
|
|
cdb->CDB6READWRITETAPE.TransferLen = (UCHAR)((transferBlocks >> 8) & 0xff);
|
|
cdb->CDB6READWRITETAPE.TransferLenLSB = (UCHAR)(transferBlocks & 0xff);
|
|
|
|
//
|
|
// Set transfer direction.
|
|
//
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_DATA_IN) {
|
|
|
|
DebugPrint((3,
|
|
"TapeReadWrite: Read Command\n"));
|
|
|
|
cdb->CDB6READWRITETAPE.OperationCode = SCSIOP_READ6;
|
|
|
|
} else {
|
|
|
|
DebugPrint((3,
|
|
"TapeReadWrite: Write Command\n"));
|
|
|
|
cdb->CDB6READWRITETAPE.OperationCode = SCSIOP_WRITE6;
|
|
}
|
|
|
|
nextSp = IoGetNextIrpStackLocation(Irp);
|
|
|
|
nextSp->MajorFunction = IRP_MJ_SCSI;
|
|
nextSp->Parameters.Scsi.Srb = srb;
|
|
irpSp->Parameters.Others.Argument4 = (PVOID) MAXIMUM_RETRIES;
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
ClassIoComplete,
|
|
srb,
|
|
TRUE,
|
|
TRUE,
|
|
FALSE);
|
|
|
|
if (tapeInitData->PreProcessReadWrite) {
|
|
|
|
//
|
|
// If the routine exists, call it. The miniclass driver will
|
|
// do whatever it needs to.
|
|
//
|
|
|
|
tapeInitData->PreProcessReadWrite(minitapeExtension,
|
|
NULL,
|
|
NULL,
|
|
srb,
|
|
0,
|
|
0,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
TapeIoCompleteAssociated(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine executes when the port driver has completed a request.
|
|
It looks at the SRB status in the completing SRB and if not success
|
|
it checks for valid request sense buffer information. If valid, the
|
|
info is used to update status with more precise message of type of
|
|
error. This routine deallocates the SRB. This routine is used for
|
|
requests which were build by split request. After it has processed
|
|
the request it decrements the Irp count in the master Irp. If the
|
|
count goes to zero then the master Irp is completed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object which represents the logical
|
|
unit.
|
|
|
|
Irp - Supplies the Irp which has completed.
|
|
|
|
Context - Supplies a pointer to the SRB.
|
|
|
|
Return Value:
|
|
|
|
NT status
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK srb = Context;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
|
PTAPE_DATA tapeData = (PTAPE_DATA)(fdoExtension->CommonExtension.DriverData);
|
|
LONG irpCount;
|
|
PIRP originalIrp = Irp->AssociatedIrp.MasterIrp;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Check SRB status for success of completing request.
|
|
//
|
|
|
|
if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
|
|
|
|
DebugPrint((2,
|
|
"TapeIoCompleteAssociated: IRP %p, SRB %p", Irp, srb));
|
|
|
|
//
|
|
// Release the queue if it is frozen.
|
|
//
|
|
|
|
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
|
ClassReleaseQueue(Fdo);
|
|
}
|
|
|
|
ClassInterpretSenseInfo(Fdo,
|
|
srb,
|
|
irpStack->MajorFunction,
|
|
irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
|
|
irpStack->Parameters.DeviceIoControl.IoControlCode : 0,
|
|
MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
|
|
&status,
|
|
NULL);
|
|
|
|
//
|
|
// Return the highest error that occurs. This way warning take precedence
|
|
// over success and errors take precedence over warnings.
|
|
//
|
|
|
|
if ((ULONG) status > (ULONG) originalIrp->IoStatus.Status) {
|
|
|
|
//
|
|
// Ignore any requests which were flushed.
|
|
//
|
|
|
|
if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_REQUEST_FLUSHED) {
|
|
|
|
originalIrp->IoStatus.Status = status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
} // end if (SRB_STATUS(srb->SrbStatus) ...
|
|
|
|
ExInterlockedAddUlong((PULONG)&originalIrp->IoStatus.Information,
|
|
(ULONG)Irp->IoStatus.Information,
|
|
&tapeData->SplitRequestSpinLock );
|
|
|
|
//
|
|
// Return SRB to the slist
|
|
//
|
|
|
|
ExFreeToNPagedLookasideList((&fdoExtension->CommonExtension.SrbLookasideList), srb);
|
|
|
|
DebugPrint((2,
|
|
"TapeIoCompleteAssociated: Partial xfer IRP %p\n", Irp));
|
|
|
|
//
|
|
// Get next stack location. This original request is unused
|
|
// except to keep track of the completing partial IRPs so the
|
|
// stack location is valid.
|
|
//
|
|
|
|
irpStack = IoGetNextIrpStackLocation(originalIrp);
|
|
|
|
//
|
|
//
|
|
// If any of the asynchronous partial transfer IRPs fail with an error
|
|
// with an error then the original IRP will return 0 bytes transfered.
|
|
// This is an optimization for successful transfers.
|
|
//
|
|
|
|
if (NT_ERROR(originalIrp->IoStatus.Status)) {
|
|
|
|
originalIrp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Set the hard error if necessary.
|
|
//
|
|
|
|
if (IoIsErrorUserInduced(originalIrp->IoStatus.Status)) {
|
|
|
|
//
|
|
// Store DeviceObject for filesystem.
|
|
//
|
|
|
|
IoSetHardErrorOrVerifyDevice(originalIrp, Fdo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Decrement and get the count of remaining IRPs.
|
|
//
|
|
|
|
irpCount = InterlockedDecrement((PLONG)&irpStack->Parameters.Others.Argument1);
|
|
|
|
DebugPrint((2,
|
|
"TapeIoCompleteAssociated: Partial IRPs left %d\n",
|
|
irpCount));
|
|
|
|
if (irpCount == 0) {
|
|
|
|
#if DBG
|
|
irpStack = IoGetCurrentIrpStackLocation(originalIrp);
|
|
|
|
if (originalIrp->IoStatus.Information != irpStack->Parameters.Read.Length) {
|
|
DebugPrint((1,
|
|
"TapeIoCompleteAssociated: Short transfer. Request length: %lx, Return length: %lx, Status: %lx\n",
|
|
irpStack->Parameters.Read.Length,
|
|
originalIrp->IoStatus.Information,
|
|
originalIrp->IoStatus.Status));
|
|
}
|
|
#endif
|
|
//
|
|
// All partial IRPs have completed.
|
|
//
|
|
|
|
DebugPrint((2,
|
|
"TapeIoCompleteAssociated: All partial IRPs complete %p\n",
|
|
originalIrp));
|
|
|
|
|
|
//
|
|
// Release the lock and complete the original request.
|
|
//
|
|
|
|
ClassReleaseRemoveLock(Fdo, originalIrp);
|
|
ClassCompleteRequest(Fdo,originalIrp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
//
|
|
// Deallocate IRP and indicate the I/O system should not attempt any more
|
|
// processing.
|
|
//
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // end TapeIoCompleteAssociated()
|
|
|
|
|
|
VOID
|
|
ScsiTapeFreeSrbBuffer(
|
|
IN OUT PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees an SRB buffer that was previously allocated with
|
|
'TapeClassAllocateSrbBuffer'.
|
|
|
|
Arguments:
|
|
|
|
Srb - Supplies the SCSI request block.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (Srb->DataBuffer) {
|
|
ExFreePool(Srb->DataBuffer);
|
|
Srb->DataBuffer = NULL;
|
|
}
|
|
Srb->DataTransferLength = 0;
|
|
}
|
|
|
|
#define IOCTL_TAPE_OLD_SET_MEDIA_PARAMS CTL_CODE(IOCTL_TAPE_BASE, 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
|
|
|
|
|
|
|
NTSTATUS
|
|
TapeDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the dispatcher for device control requests. It
|
|
looks at the IOCTL code and calls the appropriate tape device
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp - Request packet
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
|
PTAPE_DATA tapeData= (PTAPE_DATA) (fdoExtension->CommonExtension.DriverData);
|
|
PTAPE_INIT_DATA_EX tapeInitData = &tapeData->TapeInitData;
|
|
PVOID minitapeExtension = tapeData + 1;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
TAPE_PROCESS_COMMAND_ROUTINE commandRoutine;
|
|
ULONG i;
|
|
PVOID commandExtension;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
BOOLEAN writeToDevice;
|
|
TAPE_STATUS tStatus;
|
|
TAPE_STATUS LastError ;
|
|
ULONG retryFlags, numRetries;
|
|
TAPE_WMI_OPERATIONS WMIOperations;
|
|
TAPE_DRIVE_PROBLEM_TYPE DriveProblemType;
|
|
PVOID commandParameters;
|
|
ULONG ioControlCode;
|
|
PWMI_TAPE_PROBLEM_WARNING TapeDriveProblem = NULL;
|
|
ULONG timeoutDelta = 0;
|
|
ULONG dataTransferLength = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((3,"ScsiTapeDeviceControl: Enter routine\n"));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
|
switch (ioControlCode) {
|
|
|
|
case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: {
|
|
|
|
ULONG tmpSize;
|
|
|
|
//
|
|
// Validate version. Don't send this to a 4.0 miniclass driver.
|
|
//
|
|
|
|
if (tapeInitData->InitDataSize == sizeof(TAPE_INIT_DATA_EX)) {
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
tmpSize = (tapeInitData->MediaTypesSupported - 1) * sizeof(DEVICE_MEDIA_INFO);
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(GET_MEDIA_TYPES) + tmpSize) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Validate that the buffer is large enough for all media types.
|
|
//
|
|
|
|
commandRoutine = tapeInitData->TapeGetMediaTypes;
|
|
|
|
} else {
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
case IOCTL_TAPE_GET_DRIVE_PARAMS:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(TAPE_GET_DRIVE_PARAMETERS)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->GetDriveParameters;
|
|
Irp->IoStatus.Information = sizeof(TAPE_GET_DRIVE_PARAMETERS);
|
|
break;
|
|
|
|
case IOCTL_TAPE_SET_DRIVE_PARAMS:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(TAPE_SET_DRIVE_PARAMETERS)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->SetDriveParameters;
|
|
break;
|
|
|
|
case IOCTL_TAPE_GET_MEDIA_PARAMS:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(TAPE_GET_MEDIA_PARAMETERS)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->GetMediaParameters;
|
|
Irp->IoStatus.Information = sizeof(TAPE_GET_MEDIA_PARAMETERS);
|
|
break;
|
|
|
|
//
|
|
// OLD_SET_XXX is here for legacy apps (defined READ/WRITE)
|
|
//
|
|
|
|
case IOCTL_TAPE_OLD_SET_MEDIA_PARAMS:
|
|
case IOCTL_TAPE_SET_MEDIA_PARAMS: {
|
|
|
|
PTAPE_SET_MEDIA_PARAMETERS tapeSetMediaParams = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG maxBytes1,maxBytes2,maxSize;
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(TAPE_SET_MEDIA_PARAMETERS)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Ensure that Max. block size is less than the miniports
|
|
// reported MaximumTransferLength.
|
|
//
|
|
|
|
maxBytes1 = PAGE_SIZE * (fdoExtension->AdapterDescriptor->MaximumPhysicalPages - 1);
|
|
maxBytes2 = fdoExtension->AdapterDescriptor->MaximumTransferLength;
|
|
maxSize = (maxBytes1 > maxBytes2) ? maxBytes2 : maxBytes1;
|
|
|
|
if (tapeSetMediaParams->BlockSize > maxSize) {
|
|
|
|
DebugPrint((1,
|
|
"ScsiTapeDeviceControl: Attempted to set blocksize greater than miniport capabilities\n"));
|
|
DebugPrint((1,"BlockSize %x, Miniport Maximum %x\n",
|
|
tapeSetMediaParams->BlockSize,
|
|
maxSize));
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
}
|
|
|
|
commandRoutine = tapeInitData->SetMediaParameters;
|
|
break;
|
|
}
|
|
|
|
case IOCTL_TAPE_CREATE_PARTITION:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(TAPE_CREATE_PARTITION)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->CreatePartition;
|
|
timeoutDelta = tapeData->SrbTimeoutDelta;
|
|
break;
|
|
|
|
case IOCTL_TAPE_ERASE:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(TAPE_ERASE)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->Erase;
|
|
timeoutDelta = tapeData->SrbTimeoutDelta;
|
|
break;
|
|
|
|
case IOCTL_TAPE_PREPARE:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(TAPE_PREPARE)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->Prepare;
|
|
timeoutDelta = tapeData->SrbTimeoutDelta;
|
|
break;
|
|
|
|
case IOCTL_TAPE_WRITE_MARKS:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(TAPE_WRITE_MARKS)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->WriteMarks;
|
|
timeoutDelta = tapeData->SrbTimeoutDelta;
|
|
break;
|
|
|
|
case IOCTL_TAPE_GET_POSITION:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(TAPE_GET_POSITION)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->GetPosition;
|
|
Irp->IoStatus.Information = sizeof(TAPE_GET_POSITION);
|
|
break;
|
|
|
|
case IOCTL_TAPE_SET_POSITION:
|
|
|
|
//
|
|
// Validate buffer length.
|
|
//
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(TAPE_SET_POSITION)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
commandRoutine = tapeInitData->SetPosition;
|
|
timeoutDelta = tapeData->SrbTimeoutDelta;
|
|
break;
|
|
|
|
case IOCTL_TAPE_GET_STATUS:
|
|
|
|
commandRoutine = tapeInitData->GetStatus;
|
|
break;
|
|
|
|
case IOCTL_STORAGE_PREDICT_FAILURE : {
|
|
//
|
|
// This IOCTL is for checking the tape drive
|
|
// to see if the device is having any problem.
|
|
//
|
|
PSTORAGE_PREDICT_FAILURE checkFailure;
|
|
|
|
checkFailure = (PSTORAGE_PREDICT_FAILURE)Irp->AssociatedIrp.SystemBuffer;
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(STORAGE_PREDICT_FAILURE)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// WMI routine to check for drive problems.
|
|
//
|
|
commandRoutine = tapeInitData->TapeWMIOperations;
|
|
if (commandRoutine == NULL) {
|
|
//
|
|
// WMI not supported by minidriver.
|
|
//
|
|
status = STATUS_WMI_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
TapeDriveProblem = ExAllocatePool(NonPagedPool,
|
|
sizeof(WMI_TAPE_PROBLEM_WARNING));
|
|
if (TapeDriveProblem == NULL) {
|
|
status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Call the WMI method to check for drive problem.
|
|
//
|
|
RtlZeroMemory(TapeDriveProblem, sizeof(WMI_TAPE_PROBLEM_WARNING));
|
|
TapeDriveProblem->DriveProblemType = TapeDriveProblemNone;
|
|
WMIOperations.Method = TAPE_CHECK_FOR_DRIVE_PROBLEM;
|
|
WMIOperations.DataBufferSize = sizeof(WMI_TAPE_PROBLEM_WARNING);
|
|
WMIOperations.DataBuffer = (PVOID)TapeDriveProblem;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
|
|
//
|
|
// Pass the request to the common device control routine.
|
|
//
|
|
|
|
return ClassDeviceControl(DeviceObject, Irp);
|
|
|
|
} // end switch()
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject,Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
if (tapeInitData->CommandExtensionSize) {
|
|
commandExtension = ExAllocatePool(NonPagedPool,
|
|
tapeInitData->CommandExtensionSize);
|
|
if (commandExtension == NULL) {
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject,Irp, IO_NO_INCREMENT);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else {
|
|
commandExtension = NULL;
|
|
}
|
|
|
|
if (ioControlCode == IOCTL_STORAGE_PREDICT_FAILURE) {
|
|
commandParameters = (PVOID)&WMIOperations;
|
|
} else {
|
|
commandParameters = Irp->AssociatedIrp.SystemBuffer;
|
|
}
|
|
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
LastError = TAPE_STATUS_SUCCESS ;
|
|
|
|
for (i = 0; ; i++) {
|
|
|
|
srb.TimeOutValue = fdoExtension->TimeOutValue;
|
|
srb.SrbFlags = 0;
|
|
|
|
retryFlags = 0;
|
|
|
|
tStatus = commandRoutine(minitapeExtension, commandExtension,
|
|
commandParameters, &srb, i,
|
|
LastError, &retryFlags);
|
|
|
|
if (srb.TimeOutValue == 0) {
|
|
srb.TimeOutValue = fdoExtension->TimeOutValue;
|
|
}
|
|
|
|
//
|
|
// Add Srb Timeout delta to the current timeout value
|
|
// set in the SRB.
|
|
//
|
|
srb.TimeOutValue += timeoutDelta;
|
|
|
|
LastError = TAPE_STATUS_SUCCESS ;
|
|
|
|
numRetries = retryFlags&TAPE_RETRY_MASK;
|
|
|
|
if (tStatus == TAPE_STATUS_CHECK_TEST_UNIT_READY) {
|
|
PCDB cdb = (PCDB)srb.Cdb;
|
|
|
|
//
|
|
// Prepare SCSI command (CDB)
|
|
//
|
|
|
|
TapeClassZeroMemory(srb.Cdb, MAXIMUM_CDB_SIZE);
|
|
srb.CdbLength = CDB6GENERIC_LENGTH;
|
|
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
|
srb.DataTransferLength = 0 ;
|
|
|
|
DebugPrint((3,"Test Unit Ready\n"));
|
|
|
|
} else if (tStatus == TAPE_STATUS_CALLBACK) {
|
|
LastError = TAPE_STATUS_CALLBACK ;
|
|
continue;
|
|
|
|
} else if (tStatus != TAPE_STATUS_SEND_SRB_AND_CALLBACK) {
|
|
break;
|
|
}
|
|
|
|
if (srb.DataBuffer && !srb.DataTransferLength) {
|
|
ScsiTapeFreeSrbBuffer(&srb);
|
|
}
|
|
|
|
if (srb.DataBuffer && (srb.SrbFlags&SRB_FLAGS_DATA_OUT)) {
|
|
writeToDevice = TRUE;
|
|
} else {
|
|
writeToDevice = FALSE;
|
|
}
|
|
|
|
dataTransferLength = srb.DataTransferLength;
|
|
for (;;) {
|
|
|
|
status = ClassSendSrbSynchronous(DeviceObject, &srb,
|
|
srb.DataBuffer,
|
|
srb.DataTransferLength,
|
|
writeToDevice);
|
|
|
|
if (NT_SUCCESS(status) ||
|
|
(status == STATUS_DATA_OVERRUN)) {
|
|
|
|
if (status == STATUS_DATA_OVERRUN) {
|
|
if ((srb.DataTransferLength) <= dataTransferLength) {
|
|
DebugPrint((1, "DataUnderRun reported as overrun\n"));
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((status == STATUS_BUS_RESET) ||
|
|
(status == STATUS_IO_TIMEOUT)) {
|
|
//
|
|
// Timeout value for the command probably wasn't sufficient.
|
|
// Update timeout delta from the registry
|
|
//
|
|
tapeData->SrbTimeoutDelta = GetTimeoutDeltaFromRegistry(fdoExtension->LowerPdo);
|
|
if ((tapeData->SrbTimeoutDelta) == 0) {
|
|
tapeData->SrbTimeoutDelta = fdoExtension->TimeOutValue;
|
|
timeoutDelta = tapeData->SrbTimeoutDelta;
|
|
srb.TimeOutValue += timeoutDelta;
|
|
}
|
|
}
|
|
|
|
if (numRetries == 0) {
|
|
|
|
if (retryFlags&RETURN_ERRORS) {
|
|
ScsiTapeNtStatusToTapeStatus(status, &LastError) ;
|
|
break ;
|
|
}
|
|
|
|
if (retryFlags&IGNORE_ERRORS) {
|
|
break;
|
|
}
|
|
|
|
if (commandExtension) {
|
|
ExFreePool(commandExtension);
|
|
}
|
|
|
|
ScsiTapeFreeSrbBuffer(&srb);
|
|
|
|
if (TapeDriveProblem) {
|
|
ExFreePool(TapeDriveProblem);
|
|
}
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
numRetries--;
|
|
}
|
|
}
|
|
|
|
ScsiTapeFreeSrbBuffer(&srb);
|
|
|
|
if (commandExtension) {
|
|
ExFreePool(commandExtension);
|
|
}
|
|
|
|
if (!ScsiTapeTapeStatusToNtStatus(tStatus, &status)) {
|
|
status = STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PTAPE_GET_MEDIA_PARAMETERS tapeGetMediaParams;
|
|
PTAPE_SET_MEDIA_PARAMETERS tapeSetMediaParams;
|
|
PTAPE_GET_DRIVE_PARAMETERS tapeGetDriveParams;
|
|
PGET_MEDIA_TYPES tapeGetMediaTypes;
|
|
ULONG maxBytes1,maxBytes2,maxSize;
|
|
|
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_STORAGE_GET_MEDIA_TYPES_EX:
|
|
|
|
tapeGetMediaTypes = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Set information field based on the returned number of mediaTypes
|
|
//
|
|
|
|
Irp->IoStatus.Information = sizeof(GET_MEDIA_TYPES);
|
|
Irp->IoStatus.Information += ((tapeGetMediaTypes->MediaInfoCount - 1) * sizeof(DEVICE_MEDIA_INFO));
|
|
|
|
DebugPrint((1,"Tape: GET_MEDIA_TYPES - Information %x\n", Irp->IoStatus.Information));
|
|
break;
|
|
|
|
case IOCTL_TAPE_GET_MEDIA_PARAMS:
|
|
tapeGetMediaParams = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Check if block size has been initialized.
|
|
//
|
|
|
|
if (fdoExtension->DiskGeometry.BytesPerSector ==
|
|
UNDEFINED_BLOCK_SIZE) {
|
|
|
|
//
|
|
// Set the block size in the device object.
|
|
//
|
|
|
|
fdoExtension->DiskGeometry.BytesPerSector =
|
|
tapeGetMediaParams->BlockSize;
|
|
}
|
|
break;
|
|
|
|
case IOCTL_TAPE_OLD_SET_MEDIA_PARAMS:
|
|
case IOCTL_TAPE_SET_MEDIA_PARAMS:
|
|
tapeSetMediaParams = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Set the block size in the device object.
|
|
//
|
|
|
|
fdoExtension->DiskGeometry.BytesPerSector =
|
|
tapeSetMediaParams->BlockSize;
|
|
|
|
break;
|
|
|
|
case IOCTL_TAPE_GET_DRIVE_PARAMS: {
|
|
ULONG oldMinBlockSize;
|
|
ULONG oldMaxBlockSize;
|
|
ULONG oldDefBlockSize;
|
|
|
|
tapeGetDriveParams = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Ensure that Max. block size is less than the miniports
|
|
// reported MaximumTransferLength.
|
|
//
|
|
|
|
|
|
maxBytes1 = PAGE_SIZE * (fdoExtension->AdapterDescriptor->MaximumPhysicalPages - 1);
|
|
maxBytes2 = fdoExtension->AdapterDescriptor->MaximumTransferLength;
|
|
maxSize = (maxBytes1 > maxBytes2) ? maxBytes2 : maxBytes1;
|
|
|
|
if (tapeGetDriveParams->MaximumBlockSize > maxSize) {
|
|
tapeGetDriveParams->MaximumBlockSize = maxSize;
|
|
|
|
DebugPrint((1,
|
|
"ScsiTapeDeviceControl: Resetting max. tape block size to %x\n",
|
|
tapeGetDriveParams->MaximumBlockSize));
|
|
}
|
|
|
|
//
|
|
// Ensure that the default block size is less than or equal
|
|
// to maximum block size.
|
|
//
|
|
if ((tapeGetDriveParams->DefaultBlockSize) >
|
|
(tapeGetDriveParams->MaximumBlockSize)) {
|
|
tapeGetDriveParams->DefaultBlockSize =
|
|
tapeGetDriveParams->MaximumBlockSize;
|
|
}
|
|
|
|
oldMinBlockSize = tapeGetDriveParams->MinimumBlockSize;
|
|
oldMaxBlockSize = tapeGetDriveParams->MaximumBlockSize;
|
|
oldDefBlockSize = tapeGetDriveParams->DefaultBlockSize;
|
|
|
|
//
|
|
// Ensure the blocksize we return are power of 2
|
|
//
|
|
|
|
UPDATE_BLOCK_SIZE(tapeGetDriveParams->DefaultBlockSize, FALSE);
|
|
|
|
UPDATE_BLOCK_SIZE(tapeGetDriveParams->MaximumBlockSize, FALSE);
|
|
|
|
UPDATE_BLOCK_SIZE(tapeGetDriveParams->MinimumBlockSize, TRUE);
|
|
|
|
if (tapeGetDriveParams->MinimumBlockSize >
|
|
tapeGetDriveParams->MaximumBlockSize ) {
|
|
|
|
//
|
|
// After converting the blocksizes to power of 2
|
|
// Min blocksize is bigger than max blocksize.
|
|
// Revert everything to the value returned by the device
|
|
//
|
|
tapeGetDriveParams->MinimumBlockSize = oldMinBlockSize;
|
|
tapeGetDriveParams->MaximumBlockSize = oldMaxBlockSize;
|
|
tapeGetDriveParams->DefaultBlockSize = oldDefBlockSize;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_STORAGE_PREDICT_FAILURE: {
|
|
|
|
PSTORAGE_PREDICT_FAILURE checkFailure;
|
|
WMI_TAPE_PROBLEM_WARNING TapeProblemWarning;
|
|
GUID TapeProblemWarningGuid = WMI_TAPE_PROBLEM_WARNING_GUID;
|
|
|
|
checkFailure = (PSTORAGE_PREDICT_FAILURE)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// We don't want classpnp to notify WMI if the drive is having
|
|
// problems or not. We'll handle that here. So, set
|
|
// PredictFailure to 0. Then, classpnp will not process
|
|
// it further.
|
|
//
|
|
checkFailure->PredictFailure = 0;
|
|
|
|
//
|
|
// If the drive is reporting problem, we'll notify WMI
|
|
//
|
|
if (TapeDriveProblem->DriveProblemType !=
|
|
TapeDriveProblemNone) {
|
|
DebugPrint((1,
|
|
"IOCTL_STORAGE_PREDICT_FAILURE : Tape drive %p",
|
|
" is experiencing problem %d\n",
|
|
DeviceObject,
|
|
TapeDriveProblem->DriveProblemType));
|
|
ClassWmiFireEvent(DeviceObject,
|
|
&TapeProblemWarningGuid,
|
|
0,
|
|
sizeof(WMI_TAPE_PROBLEM_WARNING),
|
|
(PUCHAR)TapeDriveProblem);
|
|
//
|
|
// ISSUE 02/28/2000 - nramas : We should decide whether
|
|
// or not we need to log an event in addition to
|
|
// firing a WMI event.
|
|
//
|
|
}
|
|
|
|
Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE);
|
|
|
|
//
|
|
// Free the buffer allocated for tape problem
|
|
// warning data
|
|
//
|
|
ExFreePool(TapeDriveProblem);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_TAPE_ERASE: {
|
|
|
|
//
|
|
// Notify that the media has been successfully erased
|
|
//
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure[2];
|
|
|
|
NotificationStructure[0].Event = GUID_IO_TAPE_ERASE;
|
|
NotificationStructure[0].Version = 1;
|
|
NotificationStructure[0].Size = sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) +
|
|
sizeof(ULONG) - sizeof(UCHAR);
|
|
NotificationStructure[0].FileObject = NULL;
|
|
NotificationStructure[0].NameBufferOffset = -1;
|
|
|
|
//
|
|
// Increasing Index for this event
|
|
//
|
|
|
|
*((PULONG) (&(NotificationStructure[0].CustomDataBuffer[0]))) = 0;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
|
|
&NotificationStructure[0],
|
|
NULL,
|
|
NULL);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
if (TapeDriveProblem) {
|
|
ExFreePool(TapeDriveProblem);
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject,Irp, 2);
|
|
|
|
return status;
|
|
} // end ScsiScsiTapeDeviceControl()
|
|
|
|
|
|
|
|
BOOLEAN
|
|
TapeClassAllocateSrbBuffer(
|
|
IN OUT PSCSI_REQUEST_BLOCK Srb,
|
|
IN ULONG SrbBufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a 'DataBuffer' for the given SRB of the given
|
|
size.
|
|
|
|
Arguments:
|
|
|
|
Srb - Supplies the SCSI request block.
|
|
|
|
SrbBufferSize - Supplies the desired 'DataBuffer' size.
|
|
|
|
Return Value:
|
|
|
|
FALSE - The allocation failed.
|
|
|
|
TRUE - The allocation succeeded.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID p;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Srb->DataBuffer) {
|
|
ExFreePool(Srb->DataBuffer);
|
|
}
|
|
|
|
p = ExAllocatePool(NonPagedPoolCacheAligned, SrbBufferSize);
|
|
if (!p) {
|
|
Srb->DataBuffer = NULL;
|
|
Srb->DataTransferLength = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
Srb->DataBuffer = p;
|
|
Srb->DataTransferLength = SrbBufferSize;
|
|
RtlZeroMemory(p, SrbBufferSize);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
TapeClassZeroMemory(
|
|
IN OUT PVOID Buffer,
|
|
IN ULONG BufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine zeroes the given memory.
|
|
|
|
Arguments:
|
|
|
|
Buffer - Supplies the buffer.
|
|
|
|
BufferSize - Supplies the buffer size.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory(Buffer, BufferSize);
|
|
}
|
|
|
|
|
|
ULONG
|
|
TapeClassCompareMemory(
|
|
IN OUT PVOID Source1,
|
|
IN OUT PVOID Source2,
|
|
IN ULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine compares the two memory buffers and returns the number
|
|
of bytes that are equivalent.
|
|
|
|
Arguments:
|
|
|
|
Source1 - Supplies the first memory buffer.
|
|
|
|
Source2 - Supplies the second memory buffer.
|
|
|
|
Length - Supplies the number of bytes to be compared.
|
|
|
|
Return Value:
|
|
|
|
The number of bytes that compared as equal.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return (ULONG)RtlCompareMemory(Source1, Source2, Length);
|
|
}
|
|
|
|
|
|
LARGE_INTEGER
|
|
TapeClassLiDiv(
|
|
IN LARGE_INTEGER Dividend,
|
|
IN LARGE_INTEGER Divisor
|
|
)
|
|
{
|
|
LARGE_INTEGER li;
|
|
|
|
PAGED_CODE();
|
|
|
|
li.QuadPart = Dividend.QuadPart / Divisor.QuadPart;
|
|
return li;
|
|
}
|
|
|
|
|
|
ULONG
|
|
GetTimeoutDeltaFromRegistry(
|
|
IN PDEVICE_OBJECT LowerPdo
|
|
)
|
|
{
|
|
ULONG srbTimeoutDelta = 0;
|
|
HANDLE deviceKey;
|
|
NTSTATUS status;
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
PAGED_CODE();
|
|
|
|
#define SRB_TIMEOUT_DELTA (L"SrbTimeoutDelta")
|
|
|
|
ASSERT(LowerPdo != NULL);
|
|
|
|
//
|
|
// Open a handle to the device node
|
|
//
|
|
status = IoOpenDeviceRegistryKey(LowerPdo,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_QUERY_VALUE,
|
|
&deviceKey);
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1,
|
|
"IoOpenDeviceRegistryKey Failed in GetTimeoutDeltaFromRegistry : %x\n",
|
|
status));
|
|
return 0;
|
|
}
|
|
|
|
RtlZeroMemory(&queryTable[0], sizeof(queryTable));
|
|
|
|
queryTable[0].Name = SRB_TIMEOUT_DELTA;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[0].EntryContext = &srbTimeoutDelta;
|
|
queryTable[0].DefaultType = REG_DWORD;
|
|
queryTable[0].DefaultData = NULL;
|
|
queryTable[0].DefaultLength = 0;
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)deviceKey,
|
|
queryTable,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1,
|
|
"RtlQueryRegistryValue failed for SrbTimeoutDelta : %x\n",
|
|
status));
|
|
srbTimeoutDelta = 0;
|
|
}
|
|
|
|
ZwClose(deviceKey);
|
|
|
|
DebugPrint((3, "SrbTimeoutDelta read from registry %x\n",
|
|
srbTimeoutDelta));
|
|
return srbTimeoutDelta;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
#define TAPE_DEBUG_PRINT_BUFF_LEN 127
|
|
ULONG TapeClassDebug = 0;
|
|
UCHAR TapeClassBuffer[TAPE_DEBUG_PRINT_BUFF_LEN + 1];
|
|
|
|
VOID
|
|
TapeDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug print for all Tape minidrivers
|
|
|
|
Arguments:
|
|
|
|
Debug print level between 0 and 3, with 3 being the most verbose.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
va_list ap;
|
|
va_start(ap, DebugMessage);
|
|
|
|
if ((DebugPrintLevel <= (TapeClassDebug & 0x0000ffff)) ||
|
|
((1 << (DebugPrintLevel + 15)) & TapeClassDebug)) {
|
|
|
|
_vsnprintf(TapeClassBuffer, TAPE_DEBUG_PRINT_BUFF_LEN,
|
|
DebugMessage, ap);
|
|
TapeClassBuffer[TAPE_DEBUG_PRINT_BUFF_LEN] = '\0';
|
|
|
|
DbgPrintEx(DPFLTR_TAPE_ID, DPFLTR_INFO_LEVEL, TapeClassBuffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
} // end TapeDebugPrint()
|
|
|
|
#else
|
|
|
|
//
|
|
// TapeDebugPrint stub
|
|
//
|
|
|
|
VOID
|
|
TapeDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|