mirror of https://github.com/lianthony/NT4.0
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.
2966 lines
76 KiB
2966 lines
76 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
scsitape.c
|
|
|
|
Abstract:
|
|
|
|
This is the tape class driver.
|
|
|
|
Authors:
|
|
|
|
Mike Glass
|
|
Hunter Small
|
|
Norbert Kusters
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "ntddk.h"
|
|
#include "newtape.h"
|
|
#include "class.h"
|
|
|
|
//
|
|
// Define the maximum inquiry data length.
|
|
//
|
|
|
|
#define MAXIMUM_TAPE_INQUIRY_DATA 252
|
|
#define UNDEFINED_BLOCK_SIZE ((ULONG) -1)
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
BOOLEAN
|
|
FindScsiTapes(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PTAPE_INIT_DATA TapeInitData,
|
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|
IN ULONG PortNumber
|
|
);
|
|
|
|
NTSTATUS
|
|
CreateTapeDeviceObject(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PTAPE_INIT_DATA TapeInitData,
|
|
IN PULONG DeviceCount,
|
|
IN PIO_SCSI_CAPABILITIES PortCapabilities,
|
|
IN PSCSI_INQUIRY_DATA LunInfo,
|
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|
IN ULONG PortNumber
|
|
);
|
|
|
|
VOID
|
|
UpdateTapeInformationInRegistry(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSCSI_INQUIRY_DATA ScsiInquiryData,
|
|
IN ULONG PortNumber,
|
|
IN ULONG TapeNumber
|
|
);
|
|
|
|
VOID
|
|
ScsiTapeError(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN OUT PNTSTATUS Status,
|
|
IN OUT PBOOLEAN Retry
|
|
);
|
|
|
|
NTSTATUS
|
|
ScsiTapeReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
ScsiTapeDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
ScsiTapeCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, DriverEntry)
|
|
#pragma alloc_text(PAGE, TapeClassInitialize)
|
|
#pragma alloc_text(PAGE, FindScsiTapes)
|
|
#pragma alloc_text(PAGE, CreateTapeDeviceObject)
|
|
#pragma alloc_text(PAGE, UpdateTapeInformationInRegistry)
|
|
#pragma alloc_text(PAGE, ScsiTapeDeviceControl)
|
|
#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 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;
|
|
ULONG portNumber = 0;
|
|
PDEVICE_OBJECT portDeviceObject;
|
|
NTSTATUS status;
|
|
STRING deviceNameString;
|
|
UNICODE_STRING unicodeDeviceName;
|
|
PFILE_OBJECT fileObject;
|
|
CCHAR deviceNameBuffer[256];
|
|
BOOLEAN tapeDeviceFound = FALSE;
|
|
|
|
DebugPrint((1,"\n\nSCSI Tape Class Driver\n"));
|
|
|
|
//
|
|
// Update driver object with entry points.
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = ScsiTapeReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = ScsiTapeReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiTapeDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiTapeCreate;
|
|
|
|
//
|
|
// Open port driver controller device objects by name.
|
|
//
|
|
|
|
do {
|
|
|
|
sprintf(deviceNameBuffer, "\\Device\\ScsiPort%d", portNumber);
|
|
|
|
DebugPrint((2, "TapeClassInitialize: Open Port %s\n", deviceNameBuffer));
|
|
|
|
RtlInitString(&deviceNameString, deviceNameBuffer);
|
|
|
|
status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
|
|
&deviceNameString,
|
|
TRUE
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
|
|
status = IoGetDeviceObjectPointer(&unicodeDeviceName,
|
|
FILE_READ_ATTRIBUTES,
|
|
&fileObject,
|
|
&portDeviceObject);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (FindScsiTapes(DriverObject, TapeInitData,
|
|
portDeviceObject, portNumber)) {
|
|
|
|
tapeDeviceFound = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check next SCSI adapter.
|
|
//
|
|
|
|
portNumber++;
|
|
|
|
} while(NT_SUCCESS(status));
|
|
|
|
return(tapeDeviceFound ? STATUS_SUCCESS : STATUS_NO_SUCH_DEVICE);
|
|
}
|
|
|
|
BOOLEAN
|
|
FindScsiTapes(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PTAPE_INIT_DATA TapeInitData,
|
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|
IN ULONG PortNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call into port driver to get configuration information to find
|
|
tape devices.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies the driver object.
|
|
|
|
TapeInitData - Supplies the tape initialization data.
|
|
|
|
PortDeviceObject - Supplies the port driver device object.
|
|
|
|
PortNumber - Supplies the SCSI port number.
|
|
|
|
Return Value:
|
|
|
|
TRUE if tape device(s) found.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_SCSI_CAPABILITIES portCapabilities;
|
|
PULONG tapeCount;
|
|
PCHAR buffer;
|
|
PSCSI_INQUIRY_DATA lunInfo;
|
|
PSCSI_ADAPTER_BUS_INFO adapterInfo;
|
|
PINQUIRYDATA inquiryData;
|
|
PCONFIGURATION_INFORMATION configInfo;
|
|
NTSTATUS status;
|
|
ULONG scsiBus;
|
|
BOOLEAN tapeDeviceFound = FALSE;
|
|
|
|
//
|
|
// Call port driver to get adapter capabilities.
|
|
//
|
|
|
|
status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1, "FindScsiTapes: ScsiClassGetCabilities failed\n"));
|
|
|
|
}
|
|
|
|
//
|
|
// Call port driver to get inquiry information to find tapes.
|
|
//
|
|
|
|
status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
configInfo = IoGetConfigurationInformation();
|
|
|
|
tapeCount = &configInfo->TapeCount;
|
|
|
|
adapterInfo = (PVOID) buffer;
|
|
|
|
//
|
|
// For each SCSI bus this adapter supports ...
|
|
//
|
|
|
|
for (scsiBus=0; scsiBus < (ULONG)adapterInfo->NumberOfBuses; scsiBus++) {
|
|
|
|
//
|
|
// Get the SCSI bus scan data for this bus.
|
|
//
|
|
|
|
lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
|
|
|
|
//
|
|
// Search list for unclaimed tape devices.
|
|
//
|
|
|
|
while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {
|
|
|
|
inquiryData = (PVOID)lunInfo->InquiryData;
|
|
|
|
DebugPrint((3,
|
|
"FindScsiTapes: Inquiry data at %lx\n",
|
|
inquiryData));
|
|
|
|
if ((inquiryData->DeviceType == SEQUENTIAL_ACCESS_DEVICE) &&
|
|
(!lunInfo->DeviceClaimed)) {
|
|
|
|
status = CreateTapeDeviceObject(DriverObject,
|
|
TapeInitData,
|
|
tapeCount,
|
|
portCapabilities,
|
|
lunInfo,
|
|
PortDeviceObject,
|
|
PortNumber);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
tapeDeviceFound = TRUE;
|
|
|
|
//
|
|
// Increment tape count
|
|
//
|
|
|
|
(*tapeCount)++;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get next LunInfo.
|
|
//
|
|
|
|
if (lunInfo->NextInquiryDataOffset == 0) {
|
|
break;
|
|
}
|
|
|
|
lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset);
|
|
|
|
}
|
|
}
|
|
|
|
ExFreePool(buffer);
|
|
return tapeDeviceFound;
|
|
|
|
} // end FindScsiTapes()
|
|
|
|
NTSTATUS
|
|
CreateTapeDeviceObject(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PTAPE_INIT_DATA TapeInitData,
|
|
IN PULONG DeviceCount,
|
|
IN PIO_SCSI_CAPABILITIES PortCapabilities,
|
|
IN PSCSI_INQUIRY_DATA LunInfo,
|
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|
IN ULONG PortNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates an object for the device and then searches
|
|
the device for partitions and creates an object for each partition.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
TapeInitData - Supplies the tape initialization data.
|
|
DeviceCount - Pointer to number of previously installed tapes.
|
|
PortCapabilities - Pointer to port capabilities structure
|
|
LunInfo - Pointer to Logical Unit Information structure.
|
|
PortDeviceObject - Pointer to device object of SCSI adapter.
|
|
PortNumber - Number of the SCSI port.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
CCHAR deviceNameBuffer[64];
|
|
CCHAR dosNameBuffer[64];
|
|
STRING deviceNameString;
|
|
UNICODE_STRING unicodeString;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
ULONG requiredStackSize;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PTAPE_INIT_DATA tapeInitData;
|
|
UCHAR pathId = LunInfo->PathId;
|
|
UCHAR targetId = LunInfo->TargetId;
|
|
UCHAR lun = LunInfo->Lun;
|
|
PVOID senseData;
|
|
STRING dosString;
|
|
UNICODE_STRING dosUnicodeString;
|
|
PVOID minitapeExtension;
|
|
PMODE_CAP_PAGE capPage = NULL ;
|
|
PMODE_CAPABILITIES_PAGE capabilitiesPage;
|
|
ULONG pageLength;
|
|
|
|
DebugPrint((3,"CreateDeviceObject: Enter routine\n"));
|
|
|
|
//
|
|
// Create device object for this device.
|
|
//
|
|
|
|
sprintf(deviceNameBuffer,
|
|
"\\Device\\Tape%d",
|
|
*DeviceCount);
|
|
|
|
RtlInitString(&deviceNameString,
|
|
deviceNameBuffer);
|
|
|
|
DebugPrint((2,"CreateDeviceObjects: Create device object %s\n",
|
|
deviceNameBuffer));
|
|
|
|
status = RtlAnsiStringToUnicodeString(&unicodeString,
|
|
&deviceNameString,
|
|
TRUE);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(DEVICE_EXTENSION) +
|
|
sizeof(TAPE_INIT_DATA) +
|
|
TapeInitData->MinitapeExtensionSize,
|
|
&unicodeString,
|
|
FILE_DEVICE_TAPE,
|
|
FILE_REMOVABLE_MEDIA,
|
|
FALSE,
|
|
&deviceObject);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1,"CreateDeviceObjects: Can not create device %s\n",
|
|
deviceNameBuffer));
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Claim the device.
|
|
//
|
|
|
|
status = ScsiClassClaimDevice(
|
|
PortDeviceObject,
|
|
LunInfo,
|
|
FALSE,
|
|
&PortDeviceObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IoDeleteDevice(deviceObject);
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Indicate that IRPs should include MDLs.
|
|
//
|
|
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
//
|
|
// Set up required stack size in device object.
|
|
//
|
|
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
|
|
requiredStackSize = PortDeviceObject->StackSize + 1;
|
|
|
|
deviceExtension->PortDeviceObject = PortDeviceObject;
|
|
|
|
//
|
|
// Allocate spinlock for split request completion.
|
|
//
|
|
|
|
KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
|
|
|
|
deviceObject->StackSize = (CCHAR)requiredStackSize;
|
|
|
|
//
|
|
// Save address of port driver capabilities.
|
|
//
|
|
|
|
deviceExtension->PortCapabilities = PortCapabilities;
|
|
|
|
//
|
|
// Save the tape initialization data.
|
|
//
|
|
|
|
tapeInitData = (PTAPE_INIT_DATA) (deviceExtension + 1);
|
|
*tapeInitData = *TapeInitData;
|
|
|
|
//
|
|
// Disable synchronous transfer for tape requests.
|
|
//
|
|
|
|
deviceExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
|
|
//
|
|
// Allocate request sense buffer.
|
|
//
|
|
|
|
senseData = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
SENSE_BUFFER_SIZE);
|
|
|
|
if (senseData == NULL) {
|
|
|
|
//
|
|
// The buffer could not be allocated.
|
|
//
|
|
|
|
if (unicodeString.Buffer != NULL ) {
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
}
|
|
|
|
//
|
|
// Release device.
|
|
//
|
|
|
|
ScsiClassClaimDevice(PortDeviceObject,
|
|
LunInfo,
|
|
TRUE,
|
|
&PortDeviceObject);
|
|
|
|
IoDeleteDevice(deviceObject);
|
|
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Set the sense data pointer in the device extension.
|
|
//
|
|
|
|
deviceExtension->SenseData = senseData;
|
|
|
|
//
|
|
// TargetId/LUN describes a device location on the SCSI bus.
|
|
// This information comes from the inquiry buffer.
|
|
//
|
|
|
|
deviceExtension->PortNumber = (UCHAR) PortNumber;
|
|
deviceExtension->PathId = pathId;
|
|
deviceExtension->TargetId = targetId;
|
|
deviceExtension->Lun = lun;
|
|
|
|
//
|
|
// Set timeout value in seconds.
|
|
//
|
|
|
|
if (TapeInitData->DefaultTimeOutValue) {
|
|
deviceExtension->TimeOutValue = TapeInitData->DefaultTimeOutValue;
|
|
} else {
|
|
deviceExtension->TimeOutValue = 180;
|
|
}
|
|
|
|
//
|
|
// Back pointer to device object.
|
|
//
|
|
|
|
deviceExtension->DeviceObject = deviceObject;
|
|
|
|
//
|
|
// Allocate buffer for drive geometry.
|
|
// NOTE: the block size of the tape device is saved in Bytes/Sector
|
|
//
|
|
|
|
deviceExtension->DiskGeometry =
|
|
ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY));
|
|
|
|
if (!deviceExtension->DiskGeometry) {
|
|
|
|
if (unicodeString.Buffer != NULL ) {
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
}
|
|
|
|
ExFreePool(senseData);
|
|
|
|
//
|
|
// Release device.
|
|
//
|
|
|
|
ScsiClassClaimDevice(PortDeviceObject,
|
|
LunInfo,
|
|
TRUE,
|
|
&PortDeviceObject);
|
|
|
|
|
|
IoDeleteDevice(deviceObject);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RtlZeroMemory(deviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY));
|
|
deviceExtension->DiskGeometry->BytesPerSector = UNDEFINED_BLOCK_SIZE;
|
|
|
|
//
|
|
// Set tape error handler.
|
|
//
|
|
|
|
deviceExtension->ClassError = ScsiTapeError;
|
|
|
|
//
|
|
// Verify that we really want this device.
|
|
//
|
|
|
|
if(TapeInitData->QueryModeCapabilitiesPage ) {
|
|
|
|
capPage = ExAllocatePool(NonPagedPoolCacheAligned,
|
|
sizeof(MODE_CAP_PAGE));
|
|
}
|
|
if (capPage) {
|
|
|
|
pageLength = ScsiClassModeSense(deviceObject,
|
|
(PCHAR) capPage,
|
|
sizeof(MODE_CAP_PAGE),
|
|
MODE_PAGE_CAPABILITIES);
|
|
|
|
if (pageLength == 0) {
|
|
pageLength = ScsiClassModeSense(deviceObject,
|
|
(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;
|
|
}
|
|
|
|
if (!TapeInitData->VerifyInquiry((PINQUIRYDATA) LunInfo->InquiryData,
|
|
capabilitiesPage)) {
|
|
|
|
//
|
|
// The device is not supported by this driver.
|
|
//
|
|
|
|
ExFreePool(senseData);
|
|
if (capPage) {
|
|
ExFreePool(capPage);
|
|
}
|
|
|
|
if (unicodeString.Buffer != NULL ) {
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
}
|
|
|
|
//
|
|
// Release device.
|
|
//
|
|
|
|
ScsiClassClaimDevice(PortDeviceObject,
|
|
LunInfo,
|
|
TRUE,
|
|
&PortDeviceObject);
|
|
|
|
|
|
IoDeleteDevice(deviceObject);
|
|
return(STATUS_NO_SUCH_DEVICE);
|
|
}
|
|
|
|
//
|
|
// Initialize the minitape extension.
|
|
//
|
|
if (TapeInitData->ExtensionInit) {
|
|
minitapeExtension = tapeInitData + 1;
|
|
TapeInitData->ExtensionInit(minitapeExtension,
|
|
(PINQUIRYDATA) LunInfo->InquiryData,
|
|
capabilitiesPage);
|
|
}
|
|
|
|
if (capPage) {
|
|
ExFreePool(capPage);
|
|
}
|
|
|
|
|
|
//
|
|
// Create the dos port driver name.
|
|
//
|
|
|
|
sprintf(dosNameBuffer,
|
|
"\\DosDevices\\TAPE%d",
|
|
*DeviceCount);
|
|
|
|
RtlInitString(&dosString, dosNameBuffer);
|
|
|
|
status = RtlAnsiStringToUnicodeString(&dosUnicodeString,
|
|
&dosString,
|
|
TRUE);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
dosUnicodeString.Buffer = NULL;
|
|
}
|
|
|
|
if (dosUnicodeString.Buffer != NULL && unicodeString.Buffer != NULL) {
|
|
IoAssignArcName(&dosUnicodeString, &unicodeString);
|
|
}
|
|
|
|
if (dosUnicodeString.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&dosUnicodeString);
|
|
}
|
|
|
|
if (unicodeString.Buffer != NULL ) {
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
}
|
|
|
|
//
|
|
// Add tape device number to registry
|
|
//
|
|
|
|
UpdateTapeInformationInRegistry(deviceObject,
|
|
LunInfo,
|
|
PortNumber,
|
|
*DeviceCount);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // end CreateTapeDeviceObject()
|
|
|
|
VOID
|
|
UpdateTapeInformationInRegistry(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSCSI_INQUIRY_DATA ScsiInquiryData,
|
|
IN ULONG PortNumber,
|
|
IN ULONG TapeNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine has knowledge about the layout of the device map information
|
|
in the registry. It will update this information to include a value
|
|
entry specifying the dos device name that is assumed to get assigned
|
|
to this NT device name. For more information on this assigning of the
|
|
dos device name look in the drive support routine in the hal that assigns
|
|
all dos names. Since most version of tape firmware do not work and most
|
|
vendor did not bother to follow the specification the entire inquiry
|
|
information must also be stored in the registry so than someone can
|
|
figure out the firmware version.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - A pointer to the device object for the tape device.
|
|
|
|
ScsiInquiryData - a pointer to the scsi inquiry data structure defined in
|
|
ntddscsi.h
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
PUCHAR buffer;
|
|
STRING string;
|
|
UNICODE_STRING unicodeName;
|
|
UNICODE_STRING unicodeData;
|
|
HANDLE targetKey;
|
|
PINQUIRYDATA dataBuffer;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
ULONG length;
|
|
PCDB cdb;
|
|
|
|
buffer = ExAllocatePool(NonPagedPool, 1024);
|
|
dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, MAXIMUM_TAPE_INQUIRY_DATA);
|
|
if (buffer == NULL || dataBuffer == NULL) {
|
|
|
|
if (buffer != NULL) {
|
|
ExFreePool(buffer);
|
|
}
|
|
|
|
//
|
|
// There is not return value for this. Since this is done at
|
|
// claim device time (currently only system initialization) getting
|
|
// the registry information correct will be the least of the worries.
|
|
//
|
|
|
|
return;
|
|
}
|
|
sprintf(buffer,
|
|
"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
|
|
PortNumber,
|
|
ScsiInquiryData->PathId,
|
|
ScsiInquiryData->TargetId,
|
|
ScsiInquiryData->Lun);
|
|
|
|
RtlInitString(&string, buffer);
|
|
status = RtlAnsiStringToUnicodeString(&unicodeName,
|
|
&string,
|
|
TRUE);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Open the registry key for the scsi information for this
|
|
// scsibus, target, lun.
|
|
//
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
status = ZwOpenKey(&targetKey,
|
|
KEY_READ | KEY_WRITE,
|
|
&objectAttributes);
|
|
RtlFreeUnicodeString(&unicodeName);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Now construct and attempt to create the registry value
|
|
// specifying the device name in the appropriate place in the
|
|
// device map.
|
|
//
|
|
|
|
RtlInitUnicodeString(&unicodeName, L"DeviceName");
|
|
|
|
sprintf(buffer, "Tape%d", TapeNumber);
|
|
RtlInitString(&string, buffer);
|
|
RtlAnsiStringToUnicodeString(&unicodeData,
|
|
&string,
|
|
TRUE);
|
|
if (NT_SUCCESS(status)) {
|
|
status = ZwSetValueKey(targetKey,
|
|
&unicodeName,
|
|
0,
|
|
REG_SZ,
|
|
unicodeData.Buffer,
|
|
unicodeData.Length);
|
|
RtlFreeUnicodeString(&unicodeData);
|
|
}
|
|
|
|
//
|
|
// Now get the full inquiry information for the device.
|
|
//
|
|
|
|
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 CDB LUN.
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.LogicalUnitNumber = deviceExtension->Lun;
|
|
|
|
//
|
|
// Set allocation length to inquiry data buffer size.
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.AllocationLength = MAXIMUM_TAPE_INQUIRY_DATA;
|
|
|
|
status = ScsiClassSendSrbSynchronous(DeviceObject,
|
|
&srb,
|
|
dataBuffer,
|
|
MAXIMUM_TAPE_INQUIRY_DATA,
|
|
FALSE);
|
|
|
|
if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_SUCCESS ||
|
|
SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
//
|
|
// Updated the length actually transfered.
|
|
//
|
|
|
|
length = dataBuffer->AdditionalLength +
|
|
FIELD_OFFSET(INQUIRYDATA, Reserved);
|
|
|
|
if (length > srb.DataTransferLength) {
|
|
length = srb.DataTransferLength;
|
|
}
|
|
|
|
RtlInitUnicodeString(&unicodeName, L"InquiryData");
|
|
|
|
status = ZwSetValueKey(targetKey,
|
|
&unicodeName,
|
|
0,
|
|
REG_BINARY,
|
|
dataBuffer,
|
|
length);
|
|
|
|
}
|
|
|
|
ZwClose(targetKey);
|
|
}
|
|
}
|
|
|
|
ExFreePool(buffer);
|
|
ExFreePool(dataBuffer);
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
default:
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
ScsiTapeError(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PTAPE_INIT_DATA tapeInitData = (PTAPE_INIT_DATA) (deviceExtension + 1);
|
|
PVOID minitapeExtension = (tapeInitData + 1);
|
|
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
|
PIRP irp = Srb->OriginalRequest;
|
|
LONG residualBlocks;
|
|
LONG length;
|
|
TAPE_STATUS tapeStatus, oldTapeStatus;
|
|
|
|
//
|
|
// Never retry tape requests.
|
|
//
|
|
|
|
*Retry = FALSE;
|
|
|
|
//
|
|
// Check that request sense buffer is valid.
|
|
//
|
|
|
|
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
|
|
|
|
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 (deviceExtension->DiskGeometry->BytesPerSector == 0 &&
|
|
Srb->Cdb[0] == SCSIOP_READ6) {
|
|
|
|
REVERSE_BYTES((PFOUR_BYTE)&residualBlocks,
|
|
(PFOUR_BYTE)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)) {
|
|
|
|
//
|
|
// Not all bytes were transfered. Update information field with
|
|
// number of bytes transfered from sense buffer.
|
|
//
|
|
|
|
if (senseBuffer->Valid) {
|
|
REVERSE_BYTES((PFOUR_BYTE)&residualBlocks,
|
|
(PFOUR_BYTE)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;
|
|
|
|
length -= residualBlocks;
|
|
|
|
if (length < 0) {
|
|
|
|
length = 0;
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
|
|
|
|
if (deviceExtension->DiskGeometry->BytesPerSector) {
|
|
length *= deviceExtension->DiskGeometry->BytesPerSector;
|
|
}
|
|
|
|
//
|
|
// If the miniport indicates fewer bytes were transfered then
|
|
// use that value.
|
|
//
|
|
|
|
if ((ULONG) length > Srb->DataTransferLength) {
|
|
length = (LONG) Srb->DataTransferLength;
|
|
DebugPrint((1,"ScsiTapeError: Calculated length wronge using miniport length. \n"));
|
|
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // end ScsiTapeError()
|
|
|
|
VOID
|
|
TapeReadWrite(
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PTAPE_INIT_DATA tapeInitData = (PTAPE_INIT_DATA) (deviceExtension + 1);
|
|
PVOID tapeData = tapeInitData + 1;
|
|
PIO_STACK_LOCATION irpSp, nextSp;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb;
|
|
ULONG transferBlocks;
|
|
|
|
//
|
|
// Allocate an Srb.
|
|
//
|
|
|
|
if (deviceExtension->SrbZone != NULL &&
|
|
(srb = ExInterlockedAllocateFromZone(
|
|
deviceExtension->SrbZone,
|
|
deviceExtension->SrbZoneSpinLock)) != NULL) {
|
|
|
|
srb->SrbFlags = SRB_FLAGS_ALLOCATED_FROM_ZONE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Allocate Srb from non-paged pool.
|
|
// This call must succeed.
|
|
//
|
|
|
|
srb = ExAllocatePool(NonPagedPoolMustSucceed,
|
|
SCSI_REQUEST_BLOCK_SIZE);
|
|
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->PathId = deviceExtension->PathId;
|
|
srb->TargetId = deviceExtension->TargetId;
|
|
srb->Lun = deviceExtension->Lun;
|
|
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
|
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
srb->SrbFlags |= deviceExtension->SrbFlags;
|
|
srb->DataTransferLength = irpSp->Parameters.Read.Length;
|
|
srb->TimeOutValue = deviceExtension->TimeOutValue;
|
|
srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|
srb->SenseInfoBuffer = deviceExtension->SenseData;
|
|
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 (deviceExtension->DiskGeometry->BytesPerSector) {
|
|
|
|
//
|
|
// Since we are writing fixed block mode, normalize transfer count
|
|
// to number of blocks.
|
|
//
|
|
|
|
transferBlocks = irpSp->Parameters.Read.Length/
|
|
deviceExtension->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, "TapeRequest: Read Command\n"));
|
|
|
|
cdb->CDB6READWRITETAPE.OperationCode = SCSIOP_READ6;
|
|
|
|
} else {
|
|
|
|
DebugPrint((3, "TapeRequest: 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, ScsiClassIoComplete, srb, TRUE, TRUE, FALSE);
|
|
|
|
if (tapeInitData->PreProcessReadWrite) {
|
|
|
|
//
|
|
// If the routine exists, call it. The miniclass driver will
|
|
// do whatever it needs to.
|
|
//
|
|
|
|
tapeInitData->PreProcessReadWrite(tapeData,
|
|
NULL,
|
|
NULL,
|
|
srb,
|
|
0,
|
|
0,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
ScsiTapeIoCompleteAssociated(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
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;
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
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,"ScsiTapeIoCompleteAssociated: IRP %lx, SRB %lx", Irp, srb));
|
|
|
|
//
|
|
// Release the queue if it is frozen.
|
|
//
|
|
|
|
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
|
ScsiClassReleaseQueue(DeviceObject);
|
|
}
|
|
|
|
ScsiClassInterpretSenseInfo(
|
|
DeviceObject,
|
|
srb,
|
|
irpStack->MajorFunction,
|
|
irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0,
|
|
MAXIMUM_RETRIES - ((ULONG)irpStack->Parameters.Others.Argument4),
|
|
&status);
|
|
|
|
//
|
|
// 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) ...
|
|
|
|
//
|
|
// Return SRB to nonpaged pool.
|
|
//
|
|
|
|
if (srb->SrbFlags & SRB_FLAGS_ALLOCATED_FROM_ZONE) {
|
|
|
|
ExInterlockedFreeToZone( deviceExtension->SrbZone,
|
|
srb,
|
|
deviceExtension->SrbZoneSpinLock);
|
|
|
|
} else {
|
|
|
|
ExFreePool(srb);
|
|
|
|
}
|
|
|
|
DebugPrint((2, "ScsiTapeIoCompleteAssociated: Partial xfer IRP %lx\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);
|
|
|
|
//
|
|
// Increment the status information with number of bytes transfered.
|
|
//
|
|
|
|
ExInterlockedAddUlong(&originalIrp->IoStatus.Information,
|
|
Irp->IoStatus.Information,
|
|
&deviceExtension->SplitRequestSpinLock );
|
|
|
|
//
|
|
//
|
|
// 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, DeviceObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Decrement and get the count of remaining IRPs.
|
|
//
|
|
|
|
irpCount = InterlockedDecrement(
|
|
(PLONG)&irpStack->Parameters.Others.Argument1
|
|
);
|
|
|
|
DebugPrint((2, "ScsiTapeIoCompleteAssociated: Partial IRPs left %d\n",
|
|
irpCount));
|
|
|
|
if (irpCount == 0) {
|
|
|
|
#if DBG
|
|
irpStack = IoGetCurrentIrpStackLocation(originalIrp);
|
|
|
|
if (originalIrp->IoStatus.Information != irpStack->Parameters.Read.Length) {
|
|
DebugPrint((1, "ScsiTapeIoCompleteAssociated: 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,
|
|
"ScsiTapeIoCompleteAssociated: All partial IRPs complete %lx\n",
|
|
originalIrp));
|
|
|
|
IoCompleteRequest(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 ScsiTapeIoCompleteAssociated()
|
|
|
|
VOID
|
|
SplitTapeRequest(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->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;
|
|
|
|
//
|
|
// 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 %lx\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.
|
|
// Setting bytes transferred to 0 if an IRP
|
|
// fails allows asynchronous partial transfers. This is an
|
|
// optimization for the successful case. As the irps complete
|
|
// with partital or full transfers they will update the bytes
|
|
// transfered. This is handle as a special case since a read or
|
|
// write can succeed but on part of the data is transfered.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Save number of IRPs to complete count on current stack
|
|
// of original IRP.
|
|
//
|
|
|
|
nextIrpStack->Parameters.Others.Argument1 = (PVOID)irpCount;
|
|
|
|
for (i = 0; i < irpCount; i++) {
|
|
|
|
PIRP newIrp;
|
|
PIO_STACK_LOCATION newIrpStack;
|
|
|
|
//
|
|
// Allocate new IRP.
|
|
//
|
|
|
|
newIrp = IoAllocateIrp(DeviceObject->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 %lx\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 = DeviceObject;
|
|
|
|
//
|
|
// Build SRB and CDB.
|
|
//
|
|
|
|
TapeReadWrite(DeviceObject, 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 ScsiTapeIoCompleteAssociated.
|
|
//
|
|
|
|
IoSetCompletionRoutine(newIrp,
|
|
ScsiTapeIoCompleteAssociated,
|
|
srb,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
//
|
|
// Call port driver with new request.
|
|
//
|
|
|
|
status = IoCallDriver(deviceExtension->PortDeviceObject, 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) {
|
|
|
|
IoCompleteRequest(Irp, 0);
|
|
}
|
|
|
|
return;
|
|
|
|
} // end SplitTapeRequest()
|
|
|
|
NTSTATUS
|
|
ScsiTapeReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the tape class driver IO handler routine. It is the system entry
|
|
point for read and write requests. The number of bytes in the request are
|
|
checked against the maximum byte counts that the adapter supports and
|
|
requests are broken up into smaller sizes if necessary. Then the
|
|
device-specific handler is called.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp - IO request
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
ULONG transferPages;
|
|
ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
|
|
LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
|
|
ULONG maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength;
|
|
ULONG bytesPerSector = deviceExtension->DiskGeometry->BytesPerSector;
|
|
|
|
if (DeviceObject->Flags & DO_VERIFY_VOLUME &&
|
|
!(currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) {
|
|
|
|
//
|
|
// if DO_VERIFY_VOLUME bit is set
|
|
// in device object flags, fail request.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
|
|
|
|
IoCompleteRequest(Irp, 0);
|
|
|
|
return STATUS_VERIFY_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Check that blocksize has been established.
|
|
//
|
|
|
|
if (bytesPerSector == UNDEFINED_BLOCK_SIZE) {
|
|
|
|
DebugPrint((1,"ScsiTapeReadWrite: Invalid block size - UNDEFINED\n"));
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, 0);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (bytesPerSector) {
|
|
if (transferByteCount % bytesPerSector) {
|
|
DebugPrint((1,"ScsiTapeReadWrite: Invalid block size\n"));
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, 0);
|
|
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.
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.Read.Length > maximumTransferLength ||
|
|
transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages) {
|
|
|
|
DebugPrint((2,"ScsiTapeRead: Request greater than maximum\n"));
|
|
DebugPrint((2,"ScsiTapeRead: Maximum is %lx\n",
|
|
maximumTransferLength));
|
|
DebugPrint((2,"ScsiTapeRead: Byte count is %lx\n",
|
|
currentIrpStack->Parameters.Read.Length));
|
|
|
|
transferPages =
|
|
deviceExtension->PortCapabilities->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);
|
|
|
|
return IoCallDriver(deviceExtension->PortDeviceObject, Irp);
|
|
|
|
} // end ScsiTapeReadWrite()
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (Srb->DataBuffer) {
|
|
ExFreePool(Srb->DataBuffer);
|
|
Srb->DataBuffer = NULL;
|
|
}
|
|
Srb->DataTransferLength = 0;
|
|
}
|
|
|
|
NTSTATUS
|
|
ScsiTapeDeviceControl(
|
|
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);
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PTAPE_INIT_DATA tapeInitData = (PTAPE_INIT_DATA) (deviceExtension + 1);
|
|
PVOID minitapeExtension = tapeInitData + 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;
|
|
|
|
DebugPrint((3,"ScsiTapeDeviceControl: Enter routine\n"));
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
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;
|
|
|
|
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 * (deviceExtension->PortCapabilities->MaximumPhysicalPages - 1);
|
|
maxBytes2 = deviceExtension->PortCapabilities->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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
break;
|
|
|
|
case IOCTL_TAPE_GET_STATUS:
|
|
|
|
commandRoutine = tapeInitData->GetStatus;
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Pass the request to the common device control routine.
|
|
//
|
|
|
|
return(ScsiClassDeviceControl(DeviceObject, Irp));
|
|
|
|
} // end switch()
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
if (tapeInitData->CommandExtensionSize) {
|
|
commandExtension = ExAllocatePool(NonPagedPoolMustSucceed,
|
|
tapeInitData->CommandExtensionSize);
|
|
} else {
|
|
commandExtension = NULL;
|
|
}
|
|
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
LastError = TAPE_STATUS_SUCCESS ;
|
|
|
|
for (i = 0; ; i++) {
|
|
|
|
srb.TimeOutValue = deviceExtension->TimeOutValue;
|
|
srb.SrbFlags = 0;
|
|
|
|
retryFlags = 0;
|
|
|
|
tStatus = commandRoutine(minitapeExtension, commandExtension,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
&srb, i, LastError, &retryFlags);
|
|
|
|
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;
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
status = ScsiClassSendSrbSynchronous(DeviceObject, &srb,
|
|
srb.DataBuffer,
|
|
srb.DataTransferLength,
|
|
writeToDevice);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
if (numRetries == 0) {
|
|
|
|
if (retryFlags&RETURN_ERRORS) {
|
|
ScsiTapeNtStatusToTapeStatus(status, &LastError) ;
|
|
break ;
|
|
}
|
|
|
|
if (retryFlags&IGNORE_ERRORS) {
|
|
break;
|
|
}
|
|
|
|
if (commandExtension) {
|
|
ExFreePool(commandExtension);
|
|
}
|
|
|
|
ScsiTapeFreeSrbBuffer(&srb);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(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;
|
|
ULONG maxBytes1,maxBytes2,maxSize;
|
|
|
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_TAPE_GET_MEDIA_PARAMS:
|
|
tapeGetMediaParams = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Check if block size has been initialized.
|
|
//
|
|
|
|
if (deviceExtension->DiskGeometry->BytesPerSector ==
|
|
UNDEFINED_BLOCK_SIZE) {
|
|
|
|
//
|
|
// Set the block size in the device object.
|
|
//
|
|
|
|
deviceExtension->DiskGeometry->BytesPerSector =
|
|
tapeGetMediaParams->BlockSize;
|
|
}
|
|
break;
|
|
|
|
case IOCTL_TAPE_SET_MEDIA_PARAMS:
|
|
tapeSetMediaParams = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Set the block size in the device object.
|
|
//
|
|
|
|
deviceExtension->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 * (deviceExtension->PortCapabilities->MaximumPhysicalPages - 1);
|
|
maxBytes2 = deviceExtension->PortCapabilities->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));
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
} else {
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
IoCompleteRequest(Irp, 2);
|
|
|
|
return status;
|
|
} // end ScsiScsiTapeDeviceControl()
|
|
|
|
NTSTATUS
|
|
ScsiTapeCreate (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles CREATE/OPEN requests and does
|
|
nothing more than return successful status.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(Irp, 0);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // end ScsiTapeCreate()
|
|
|
|
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;
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
return RtlCompareMemory(Source1, Source2, Length);
|
|
}
|