windows-nt/Source/XPSP1/NT/drivers/storage/tape/class/tape.c
2020-09-26 16:20:57 +08:00

3560 lines
92 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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 (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;
}
//
// Remove the lookaside list.
//
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(&currentTime);
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);
} 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);
//
// 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:
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;
}
//
// Ensure the blocksize we return are power of 2
//
UPDATE_BLOCK_SIZE(tapeGetDriveParams->DefaultBlockSize);
UPDATE_BLOCK_SIZE(tapeGetDriveParams->MaximumBlockSize);
UPDATE_BLOCK_SIZE(tapeGetDriveParams->MinimumBlockSize);
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 128
ULONG TapeClassDebug = 0;
UCHAR TapeClassBuffer[TAPE_DEBUG_PRINT_BUFF_LEN];
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);
DbgPrintEx(DPFLTR_TAPE_ID, DPFLTR_INFO_LEVEL, TapeClassBuffer);
}
va_end(ap);
} // end TapeDebugPrint()
#else
//
// TapeDebugPrint stub
//
VOID
TapeDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
{
}
#endif