1778 lines
40 KiB
C
1778 lines
40 KiB
C
/*++
|
||
|
||
Copyright (C) Microsoft Corporation, 1996 - 1999
|
||
|
||
Module Name:
|
||
|
||
mcd.c
|
||
|
||
Abstract:
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History :
|
||
|
||
--*/
|
||
|
||
#include "stdarg.h"
|
||
#include "ntddk.h"
|
||
#include "mcd.h"
|
||
|
||
#include "initguid.h"
|
||
#include "ntddstor.h"
|
||
|
||
typedef struct _MCD_CLASS_DATA {
|
||
LONG DeviceOpen;
|
||
UNICODE_STRING MediumChangerInterfaceString;
|
||
BOOLEAN DosNameCreated;
|
||
} MCD_CLASS_DATA, *PMCD_CLASS_DATA;
|
||
|
||
|
||
NTSTATUS
|
||
ChangerClassCreateClose (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
ChangerClassDeviceControl (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
ChangerClassError(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
NTSTATUS *Status,
|
||
BOOLEAN *Retry
|
||
);
|
||
|
||
NTSTATUS
|
||
ChangerAddDevice(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject
|
||
);
|
||
|
||
NTSTATUS
|
||
ChangerStartDevice(
|
||
IN PDEVICE_OBJECT Fdo
|
||
);
|
||
|
||
NTSTATUS
|
||
ChangerStopDevice(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN UCHAR Type
|
||
);
|
||
|
||
NTSTATUS
|
||
ChangerInitDevice(
|
||
IN PDEVICE_OBJECT Fdo
|
||
);
|
||
|
||
NTSTATUS
|
||
ChangerRemoveDevice(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN UCHAR Type
|
||
);
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
VOID
|
||
ChangerUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
NTSTATUS
|
||
CreateChangerDeviceObject(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject
|
||
);
|
||
|
||
NTSTATUS
|
||
ClasspSendSynchronousCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT, DriverEntry)
|
||
|
||
#pragma alloc_text(PAGE, ChangerUnload)
|
||
#pragma alloc_text(PAGE, CreateChangerDeviceObject)
|
||
#pragma alloc_text(PAGE, ChangerClassCreateClose)
|
||
#pragma alloc_text(PAGE, ChangerClassDeviceControl)
|
||
#pragma alloc_text(PAGE, ChangerAddDevice)
|
||
#pragma alloc_text(PAGE, ChangerStartDevice)
|
||
#pragma alloc_text(PAGE, ChangerInitDevice)
|
||
#pragma alloc_text(PAGE, ChangerRemoveDevice)
|
||
#pragma alloc_text(PAGE, ChangerStopDevice)
|
||
#pragma alloc_text(PAGE, ChangerReadWriteVerification)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
ChangerClassCreateClose (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles CREATE/CLOSE requests.
|
||
As these are exclusive devices, don't allow multiple opens.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject
|
||
Irp
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
PMCD_CLASS_DATA mcdClassData;
|
||
ULONG miniclassExtSize;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
mcdClassData = (PMCD_CLASS_DATA)(fdoExtension->CommonExtension.DriverData);
|
||
miniclassExtSize = ChangerAdditionalExtensionSize();
|
||
|
||
//
|
||
// The class library's private data is after the miniclass's.
|
||
//
|
||
|
||
(ULONG_PTR)mcdClassData += miniclassExtSize;
|
||
|
||
if (irpStack->MajorFunction == IRP_MJ_CLOSE) {
|
||
DebugPrint((3,
|
||
"ChangerClassCreateClose - IRP_MJ_CLOSE\n"));
|
||
|
||
//
|
||
// Indicate that the device is available for others.
|
||
//
|
||
|
||
mcdClassData->DeviceOpen = 0;
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else if (irpStack->MajorFunction == IRP_MJ_CREATE) {
|
||
|
||
DebugPrint((3,
|
||
"ChangerClassCreateClose - IRP_MJ_CREATE\n"));
|
||
|
||
//
|
||
// If already opened, return busy.
|
||
//
|
||
|
||
if (mcdClassData->DeviceOpen) {
|
||
|
||
DebugPrint((1,
|
||
"ChangerClassCreateClose - returning DEVICE_BUSY. DeviceOpen - %x\n",
|
||
mcdClassData->DeviceOpen));
|
||
|
||
status = STATUS_DEVICE_BUSY;
|
||
} else {
|
||
|
||
//
|
||
// Indicate that the device is busy.
|
||
//
|
||
|
||
InterlockedIncrement(&mcdClassData->DeviceOpen);
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
Irp->IoStatus.Status = status;
|
||
ClassReleaseRemoveLock(DeviceObject, Irp);
|
||
ClassCompleteRequest(DeviceObject,Irp, IO_NO_INCREMENT);
|
||
|
||
return status;
|
||
|
||
} // end ChangerCreate()
|
||
|
||
|
||
NTSTATUS
|
||
ChangerClassDeviceControl (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
||
|
||
case IOCTL_CHANGER_GET_PARAMETERS:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_PARAMETERS\n"));
|
||
|
||
//
|
||
// Validate buffer length.
|
||
//
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(GET_CHANGER_PARAMETERS)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
} else {
|
||
|
||
status = ChangerGetParameters(DeviceObject, Irp);
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_GET_STATUS:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_STATUS\n"));
|
||
|
||
status = ChangerGetStatus(DeviceObject, Irp);
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_GET_PRODUCT_DATA:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_PRODUCT_DATA\n"));
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(CHANGER_PRODUCT_DATA)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
|
||
} else {
|
||
|
||
status = ChangerGetProductData(DeviceObject, Irp);
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_SET_ACCESS:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_SET_ACCESS\n"));
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(CHANGER_SET_ACCESS)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
} else {
|
||
|
||
status = ChangerSetAccess(DeviceObject, Irp);
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_GET_ELEMENT_STATUS:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_ELEMENT_STATUS\n"));
|
||
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(CHANGER_READ_ELEMENT_STATUS)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
|
||
} else {
|
||
|
||
PCHANGER_READ_ELEMENT_STATUS readElementStatus = Irp->AssociatedIrp.SystemBuffer;
|
||
ULONG length = readElementStatus->ElementList.NumberOfElements * sizeof(CHANGER_ELEMENT_STATUS);
|
||
|
||
//
|
||
// Further validate parameters.
|
||
//
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < length) {
|
||
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
|
||
} else if (length == 0) {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
} else {
|
||
|
||
status = ChangerGetElementStatus(DeviceObject, Irp);
|
||
}
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_INITIALIZE_ELEMENT_STATUS:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_INITIALIZE_ELEMENT_STATUS\n"));
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(CHANGER_INITIALIZE_ELEMENT_STATUS)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
} else {
|
||
|
||
status = ChangerInitializeElementStatus(DeviceObject, Irp);
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_SET_POSITION:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_SET_POSITION\n"));
|
||
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(CHANGER_SET_POSITION)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
} else {
|
||
|
||
status = ChangerSetPosition(DeviceObject, Irp);
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_EXCHANGE_MEDIUM:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_EXCHANGE_MEDIUM\n"));
|
||
|
||
status = ChangerExchangeMedium(DeviceObject, Irp);
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_MOVE_MEDIUM:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_MOVE_MEDIUM\n"));
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(CHANGER_MOVE_MEDIUM)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
|
||
} else {
|
||
|
||
status = ChangerMoveMedium(DeviceObject, Irp);
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_REINITIALIZE_TRANSPORT:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_REINITIALIZE_TRANSPORT\n"));
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(CHANGER_ELEMENT)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
|
||
} else {
|
||
|
||
status = ChangerReinitializeUnit(DeviceObject, Irp);
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_CHANGER_QUERY_VOLUME_TAGS:
|
||
|
||
DebugPrint((3,
|
||
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_QUERY_VOLUME_TAGS\n"));
|
||
|
||
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(CHANGER_SEND_VOLUME_TAG_INFORMATION)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
|
||
} else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
||
sizeof(READ_ELEMENT_ADDRESS_INFO)) {
|
||
|
||
status = STATUS_INFO_LENGTH_MISMATCH;
|
||
|
||
} else {
|
||
status = ChangerQueryVolumeTags(DeviceObject, Irp);
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
DebugPrint((1,
|
||
"Mcd.ChangerDeviceControl: Unhandled IOCTL\n"));
|
||
|
||
|
||
//
|
||
// Pass the request to the common device control routine.
|
||
//
|
||
|
||
return ClassDeviceControl(DeviceObject, Irp);
|
||
break;
|
||
}
|
||
|
||
Irp->IoStatus.Status = status;
|
||
|
||
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
|
||
|
||
DebugPrint((1,
|
||
"Mcd.ChangerDeviceControl: IOCTL %x, status %x\n",
|
||
irpStack->Parameters.DeviceIoControl.IoControlCode,
|
||
status));
|
||
|
||
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
|
||
}
|
||
|
||
ClassReleaseRemoveLock(DeviceObject, Irp);
|
||
ClassCompleteRequest(DeviceObject,Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
ChangerClassError(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
NTSTATUS *Status,
|
||
BOOLEAN *Retry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
DeviceObject
|
||
Irp
|
||
|
||
Return Value:
|
||
|
||
Final Nt status indicating the results of the operation.
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
||
PIRP irp = Srb->OriginalRequest;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
|
||
|
||
switch (senseBuffer->SenseKey & 0xf) {
|
||
|
||
|
||
case SCSI_SENSE_ILLEGAL_REQUEST:
|
||
switch (senseBuffer->AdditionalSenseCode) {
|
||
case SCSI_ADSENSE_ILLEGAL_BLOCK:
|
||
if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_ILLEGAL_ELEMENT_ADDR ) {
|
||
|
||
DebugPrint((1,
|
||
"MediumChanger: An operation was attempted on an invalid element\n"));
|
||
|
||
//
|
||
// Attemped operation to an invalid element.
|
||
//
|
||
|
||
*Retry = FALSE;
|
||
*Status = STATUS_ILLEGAL_ELEMENT_ADDRESS;
|
||
}
|
||
break;
|
||
|
||
case SCSI_ADSENSE_POSITION_ERROR:
|
||
|
||
if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_SOURCE_EMPTY) {
|
||
|
||
DebugPrint((1,
|
||
"MediumChanger: The specified source element has no media\n"));
|
||
|
||
//
|
||
// The indicated source address has no media.
|
||
//
|
||
|
||
*Retry = FALSE;
|
||
*Status = STATUS_SOURCE_ELEMENT_EMPTY;
|
||
|
||
} else if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_DESTINATION_FULL) {
|
||
|
||
DebugPrint((1,
|
||
"MediumChanger: The specified destination element already has media.\n"));
|
||
//
|
||
// The indicated destination already contains media.
|
||
//
|
||
|
||
*Retry = FALSE;
|
||
*Status = STATUS_DESTINATION_ELEMENT_FULL;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
|
||
} // end switch
|
||
}
|
||
|
||
//
|
||
// Allow device-specific module to update this.
|
||
//
|
||
|
||
ChangerError(DeviceObject, Srb, Status, Retry);
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ChangerAddDevice(
|
||
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 - MC class driver object.
|
||
|
||
Pdo - the physical device object we are being added to
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
PULONG devicesFound = NULL;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the address of the count of the number of tape devices already initialized.
|
||
//
|
||
|
||
devicesFound = &IoGetConfigurationInformation()->MediumChangerCount;
|
||
|
||
status = CreateChangerDeviceObject(DriverObject,
|
||
PhysicalDeviceObject);
|
||
|
||
|
||
if(NT_SUCCESS(status)) {
|
||
|
||
(*devicesFound)++;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ChangerStartDevice(
|
||
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;
|
||
PINQUIRYDATA inquiryData = NULL;
|
||
ULONG pageLength;
|
||
ULONG inquiryLength;
|
||
SCSI_REQUEST_BLOCK srb;
|
||
PCDB cdb;
|
||
NTSTATUS status;
|
||
PMCD_CLASS_DATA mcdClassData = (PMCD_CLASS_DATA)fdoExtension->CommonExtension.DriverData;
|
||
ULONG miniClassExtSize = ChangerAdditionalExtensionSize();
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Build and send request to get inquiry data.
|
||
//
|
||
|
||
inquiryData = ExAllocatePool(NonPagedPoolCacheAligned, MAXIMUM_CHANGER_INQUIRY_DATA);
|
||
if (!inquiryData) {
|
||
//
|
||
// The buffer cannot be allocated.
|
||
//
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
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_CHANGER_INQUIRY_DATA;
|
||
|
||
status = ClassSendSrbSynchronous(Fdo,
|
||
&srb,
|
||
inquiryData,
|
||
MAXIMUM_CHANGER_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;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// The class function will only write inquiryLength of inquiryData
|
||
// to the reg. key.
|
||
//
|
||
|
||
inquiryLength = 0;
|
||
}
|
||
|
||
//
|
||
// Add changer device info to registry
|
||
//
|
||
|
||
ClassUpdateInformationInRegistry(Fdo,
|
||
"Changer",
|
||
fdoExtension->DeviceNumber,
|
||
inquiryData,
|
||
inquiryLength);
|
||
|
||
ExFreePool(inquiryData);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ChangerStopDevice(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN UCHAR Type
|
||
)
|
||
{
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
|
||
#define CHANGER_SRB_LIST_SIZE 2
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ChangerInitDevice(
|
||
IN PDEVICE_OBJECT Fdo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will complete the changer initialization. This includes
|
||
allocating sense info buffers and srb s-lists. Additionally, the miniclass
|
||
driver's init entry points are called.
|
||
|
||
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:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
PVOID senseData = NULL;
|
||
NTSTATUS status;
|
||
PVOID minitapeExtension;
|
||
STORAGE_PROPERTY_ID propertyId;
|
||
UNICODE_STRING interfaceName;
|
||
PMCD_CLASS_DATA mcdClassData;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Allocate request sense buffer.
|
||
//
|
||
|
||
senseData = ExAllocatePool(NonPagedPoolCacheAligned,
|
||
SENSE_BUFFER_SIZE);
|
||
|
||
if (senseData == NULL) {
|
||
|
||
//
|
||
// The buffer cannot be allocated.
|
||
//
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ChangerInitDeviceExit;
|
||
}
|
||
|
||
//
|
||
// Build the lookaside list for srb's for the device. Should only
|
||
// need a couple.
|
||
//
|
||
|
||
ClassInitializeSrbLookasideList(&(fdoExtension->CommonExtension), CHANGER_SRB_LIST_SIZE);
|
||
|
||
//
|
||
// Set the sense data pointer in the device extension.
|
||
//
|
||
|
||
fdoExtension->SenseData = senseData;
|
||
|
||
fdoExtension->TimeOutValue = 600;
|
||
|
||
//
|
||
// Call port driver to get adapter capabilities.
|
||
//
|
||
|
||
propertyId = StorageAdapterProperty;
|
||
|
||
status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
|
||
&propertyId,
|
||
&(fdoExtension->AdapterDescriptor));
|
||
|
||
if(!NT_SUCCESS(status)) {
|
||
DebugPrint((1,
|
||
"ChangerStartDevice: Unable to get adapter descriptor. Status %x\n",
|
||
status));
|
||
goto ChangerInitDeviceExit;
|
||
}
|
||
|
||
//
|
||
// Invoke the device-specific initialization function.
|
||
//
|
||
|
||
status = ChangerInitialize(Fdo);
|
||
|
||
//
|
||
// Register interfaces for this device.
|
||
//
|
||
|
||
RtlInitUnicodeString(&interfaceName, NULL);
|
||
|
||
status = IoRegisterDeviceInterface(fdoExtension->LowerPdo,
|
||
(LPGUID) &MediumChangerClassGuid,
|
||
NULL,
|
||
&interfaceName);
|
||
|
||
if(NT_SUCCESS(status)) {
|
||
ULONG miniclassExtSize;
|
||
|
||
mcdClassData = (PMCD_CLASS_DATA)(fdoExtension->CommonExtension.DriverData);
|
||
miniclassExtSize = ChangerAdditionalExtensionSize();
|
||
|
||
//
|
||
// The class library's private data is after the miniclass's.
|
||
//
|
||
|
||
(ULONG_PTR)mcdClassData += miniclassExtSize;
|
||
|
||
mcdClassData->MediumChangerInterfaceString = interfaceName;
|
||
|
||
status = IoSetDeviceInterfaceState(
|
||
&interfaceName,
|
||
TRUE);
|
||
|
||
if(!NT_SUCCESS(status)) {
|
||
|
||
DebugPrint((1,
|
||
"ChangerInitDevice: Unable to register Changer%x interface name - %x.\n",
|
||
fdoExtension->DeviceNumber,
|
||
status));
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
|
||
//
|
||
// Fall through and return whatever status the miniclass driver returned.
|
||
//
|
||
|
||
ChangerInitDeviceExit:
|
||
|
||
if (senseData) {
|
||
ExFreePool(senseData);
|
||
}
|
||
|
||
return status;
|
||
|
||
} // End ChangerStartDevice
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ChangerRemoveDevice(
|
||
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;
|
||
PMCD_CLASS_DATA mcdClassData = (PMCD_CLASS_DATA)fdoExtension->CommonExtension.DriverData;
|
||
ULONG miniClassExtSize = ChangerAdditionalExtensionSize();
|
||
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);
|
||
|
||
(ULONG_PTR)mcdClassData += miniClassExtSize;
|
||
|
||
if(mcdClassData->MediumChangerInterfaceString.Buffer != NULL) {
|
||
IoSetDeviceInterfaceState(&(mcdClassData->MediumChangerInterfaceString),
|
||
FALSE);
|
||
|
||
RtlFreeUnicodeString(&(mcdClassData->MediumChangerInterfaceString));
|
||
|
||
//
|
||
// Clear it.
|
||
//
|
||
|
||
RtlInitUnicodeString(&(mcdClassData->MediumChangerInterfaceString), NULL);
|
||
}
|
||
|
||
//
|
||
// Delete the symbolic link "changerN".
|
||
//
|
||
|
||
if(mcdClassData->DosNameCreated) {
|
||
|
||
swprintf(dosNameBuffer,
|
||
L"\\DosDevices\\Changer%d",
|
||
fdoExtension->DeviceNumber);
|
||
RtlInitUnicodeString(&dosUnicodeString, dosNameBuffer);
|
||
IoDeleteSymbolicLink(&dosUnicodeString);
|
||
mcdClassData->DosNameCreated = FALSE;
|
||
}
|
||
|
||
//
|
||
// Remove registry bits.
|
||
//
|
||
|
||
if(Type == IRP_MN_REMOVE_DEVICE) {
|
||
IoGetConfigurationInformation()->MediumChangerCount--;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ChangerReadWriteVerification(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a stub that returns invalid device request.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies the device object.
|
||
|
||
Irp - Supplies the I/O request packet.
|
||
|
||
Return Value:
|
||
|
||
STATUS_INVALID_DEVICE_REQUEST
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called at system initialization time to initialize
|
||
this driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Supplies the driver object.
|
||
|
||
RegistryPath - Supplies the registry path for this driver.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - We could initialize at least one device.
|
||
STATUS_NO_SUCH_DEVICE - We could not initialize even one device.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
|
||
CLASS_INIT_DATA InitializationData;
|
||
|
||
//
|
||
// Zero InitData
|
||
//
|
||
|
||
RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
|
||
|
||
//
|
||
// Set sizes
|
||
//
|
||
|
||
InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
|
||
|
||
InitializationData.FdoData.DeviceExtensionSize = sizeof(FUNCTIONAL_DEVICE_EXTENSION) + ChangerAdditionalExtensionSize() + sizeof(MCD_CLASS_DATA);
|
||
InitializationData.FdoData.DeviceType = FILE_DEVICE_CHANGER;
|
||
InitializationData.FdoData.DeviceCharacteristics = 0;
|
||
|
||
//
|
||
// Set entry points
|
||
//
|
||
|
||
InitializationData.FdoData.ClassStartDevice = ChangerStartDevice;
|
||
InitializationData.FdoData.ClassInitDevice = ChangerInitDevice;
|
||
InitializationData.FdoData.ClassStopDevice = ChangerStopDevice;
|
||
InitializationData.FdoData.ClassRemoveDevice = ChangerRemoveDevice;
|
||
InitializationData.ClassAddDevice = ChangerAddDevice;
|
||
|
||
InitializationData.FdoData.ClassReadWriteVerification = NULL;
|
||
InitializationData.FdoData.ClassDeviceControl = ChangerClassDeviceControl;
|
||
InitializationData.FdoData.ClassError = ChangerClassError;
|
||
InitializationData.FdoData.ClassShutdownFlush = NULL;
|
||
|
||
InitializationData.FdoData.ClassCreateClose = ChangerClassCreateClose;
|
||
|
||
//
|
||
// Stub routine to make the class driver happy.
|
||
//
|
||
|
||
InitializationData.FdoData.ClassReadWriteVerification = ChangerReadWriteVerification;
|
||
|
||
InitializationData.ClassUnload = ChangerUnload;
|
||
|
||
|
||
//
|
||
// Call the class init routine
|
||
//
|
||
|
||
return ClassInitialize( DriverObject, RegistryPath, &InitializationData);
|
||
|
||
}
|
||
|
||
VOID
|
||
ChangerUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
UNREFERENCED_PARAMETER(DriverObject);
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
CreateChangerDeviceObject(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
PhysicalDeviceObject - DeviceObject of the attached to device.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PDEVICE_OBJECT lowerDevice;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
|
||
CCHAR deviceNameBuffer[64];
|
||
NTSTATUS status;
|
||
PDEVICE_OBJECT deviceObject;
|
||
ULONG requiredStackSize;
|
||
PVOID senseData;
|
||
WCHAR dosNameBuffer[64];
|
||
WCHAR wideNameBuffer[64];
|
||
UNICODE_STRING dosUnicodeString;
|
||
UNICODE_STRING deviceUnicodeString;
|
||
PMCD_CLASS_DATA mcdClassData;
|
||
ULONG mcdCount;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugPrint((3,"CreateChangerDeviceObject: 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.
|
||
//
|
||
|
||
ObDereferenceObject(lowerDevice);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Create device object for this device.
|
||
//
|
||
mcdCount = 0;
|
||
do {
|
||
sprintf(deviceNameBuffer,
|
||
"\\Device\\Changer%d",
|
||
mcdCount);
|
||
|
||
status = ClassCreateDeviceObject(DriverObject,
|
||
deviceNameBuffer,
|
||
PhysicalDeviceObject,
|
||
TRUE,
|
||
&deviceObject);
|
||
mcdCount++;
|
||
} while (status == STATUS_OBJECT_NAME_COLLISION);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DebugPrint((1,"CreateChangerDeviceObjects: Can not create device %s\n",
|
||
deviceNameBuffer));
|
||
|
||
goto CreateChangerDeviceObjectExit;
|
||
}
|
||
|
||
//
|
||
// 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 = mcdCount - 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;
|
||
|
||
//
|
||
// Clear the SrbFlags and disable synchronous transfers
|
||
//
|
||
|
||
fdoExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
||
|
||
//
|
||
// 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 CreateChangerDeviceObjectExit;
|
||
}
|
||
|
||
|
||
//
|
||
// Create the dos port driver name.
|
||
//
|
||
|
||
swprintf(dosNameBuffer,
|
||
L"\\DosDevices\\Changer%d",
|
||
fdoExtension->DeviceNumber);
|
||
|
||
RtlInitUnicodeString(&dosUnicodeString, dosNameBuffer);
|
||
|
||
//
|
||
// Recreate the deviceName
|
||
//
|
||
|
||
swprintf(wideNameBuffer,
|
||
L"\\Device\\Changer%d",
|
||
fdoExtension->DeviceNumber);
|
||
|
||
RtlInitUnicodeString(&deviceUnicodeString,
|
||
wideNameBuffer);
|
||
|
||
|
||
mcdClassData = (PMCD_CLASS_DATA)(fdoExtension->CommonExtension.DriverData);
|
||
(ULONG_PTR)mcdClassData += ChangerAdditionalExtensionSize();
|
||
if (NT_SUCCESS(IoAssignArcName(&dosUnicodeString, &deviceUnicodeString))) {
|
||
mcdClassData->DosNameCreated = TRUE;
|
||
} else {
|
||
mcdClassData->DosNameCreated = FALSE;
|
||
}
|
||
|
||
//
|
||
// The device is initialized properly - mark it as such.
|
||
//
|
||
|
||
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
||
ObDereferenceObject(lowerDevice);
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
CreateChangerDeviceObjectExit:
|
||
|
||
//
|
||
// Release the device since an error occured.
|
||
//
|
||
|
||
// ClassClaimDevice(PortDeviceObject,
|
||
// LunInfo,
|
||
// TRUE,
|
||
// NULL);
|
||
|
||
ObDereferenceObject(lowerDevice);
|
||
|
||
if (deviceObject != NULL) {
|
||
IoDeleteDevice(deviceObject);
|
||
}
|
||
|
||
return status;
|
||
|
||
|
||
} // end CreateChangerDeviceObject()
|
||
|
||
|
||
NTSTATUS
|
||
ChangerClassSendSrbSynchronous(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PSCSI_REQUEST_BLOCK Srb,
|
||
IN PVOID BufferAddress,
|
||
IN ULONG BufferLength,
|
||
IN BOOLEAN WriteToDevice
|
||
)
|
||
{
|
||
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
ULONG controlType;
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpStack;
|
||
KEVENT event;
|
||
PUCHAR senseInfoBuffer;
|
||
ULONG retryCount = MAXIMUM_RETRIES;
|
||
NTSTATUS status;
|
||
BOOLEAN retry;
|
||
|
||
//
|
||
// NOTE: While this code may look as though it could be pagable,
|
||
// making it pagable creates the possibility of a page
|
||
// boundary between IoCallDriver() and ClassReleaseQueue(),
|
||
// which could leave the queue frozen as we try to page in
|
||
// this code, which is required to unfreeze the queue.
|
||
// The result would be a nice case of deadlock.
|
||
//
|
||
|
||
ASSERT(fdoExtension->CommonExtension.IsFdo);
|
||
|
||
//
|
||
// Write length to SRB.
|
||
//
|
||
|
||
Srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
||
|
||
//
|
||
// Set SCSI bus address.
|
||
//
|
||
|
||
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
|
||
//
|
||
// NOTICE: The SCSI-II specification indicates that this field should be
|
||
// zero; however, some target controllers ignore the logical unit number
|
||
// in the INDENTIFY message and only look at the logical unit number field
|
||
// in the CDB.
|
||
//
|
||
|
||
// Srb->Cdb[1] |= deviceExtension->Lun << 5;
|
||
|
||
//
|
||
// Enable auto request sense.
|
||
//
|
||
|
||
Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
||
|
||
//
|
||
// Sense buffer is in aligned nonpaged pool.
|
||
//
|
||
|
||
senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
SENSE_BUFFER_SIZE,
|
||
'7CcS');
|
||
|
||
if (senseInfoBuffer == NULL) {
|
||
|
||
DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
|
||
"buffer\n"));
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
Srb->SenseInfoBuffer = senseInfoBuffer;
|
||
Srb->DataBuffer = BufferAddress;
|
||
|
||
if(BufferAddress != NULL) {
|
||
if(WriteToDevice) {
|
||
Srb->SrbFlags = SRB_FLAGS_DATA_OUT;
|
||
} else {
|
||
Srb->SrbFlags = SRB_FLAGS_DATA_IN;
|
||
}
|
||
} else {
|
||
Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
|
||
}
|
||
|
||
//
|
||
// Start retries here.
|
||
//
|
||
|
||
retry:
|
||
|
||
//
|
||
// Set the event object to the unsignaled state.
|
||
// It will be used to signal request completion.
|
||
//
|
||
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
||
//
|
||
// Build device I/O control request with METHOD_NEITHER data transfer.
|
||
// We'll queue a completion routine to cleanup the MDL's and such ourself.
|
||
//
|
||
|
||
irp = IoAllocateIrp(
|
||
(CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
|
||
FALSE);
|
||
|
||
if(irp == NULL) {
|
||
ExFreePool(senseInfoBuffer);
|
||
DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
//
|
||
// Get next stack location.
|
||
//
|
||
|
||
irpStack = IoGetNextIrpStackLocation(irp);
|
||
|
||
//
|
||
// Set up SRB for execute scsi request. Save SRB address in next stack
|
||
// for the port driver.
|
||
//
|
||
|
||
irpStack->MajorFunction = IRP_MJ_SCSI;
|
||
irpStack->Parameters.Scsi.Srb = Srb;
|
||
|
||
IoSetCompletionRoutine(irp,
|
||
ClasspSendSynchronousCompletion,
|
||
Srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
irp->UserIosb = &ioStatus;
|
||
irp->UserEvent = &event;
|
||
|
||
if(BufferAddress) {
|
||
//
|
||
// Build an MDL for the data buffer and stick it into the irp. The
|
||
// completion routine will unlock the pages and free the MDL.
|
||
//
|
||
|
||
irp->MdlAddress = IoAllocateMdl( BufferAddress,
|
||
BufferLength,
|
||
FALSE,
|
||
FALSE,
|
||
irp );
|
||
if (irp->MdlAddress == NULL) {
|
||
ExFreePool(senseInfoBuffer);
|
||
Srb->SenseInfoBuffer = NULL;
|
||
IoFreeIrp( irp );
|
||
DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
try {
|
||
MmProbeAndLockPages( irp->MdlAddress,
|
||
KernelMode,
|
||
(WriteToDevice ? IoReadAccess :
|
||
IoWriteAccess));
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
status = GetExceptionCode();
|
||
|
||
ExFreePool(senseInfoBuffer);
|
||
Srb->SenseInfoBuffer = NULL;
|
||
IoFreeMdl(irp->MdlAddress);
|
||
IoFreeIrp(irp);
|
||
|
||
DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
|
||
"locking buffer\n", status));
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Disable synchronous transfer for these requests.
|
||
//
|
||
|
||
SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
|
||
//
|
||
// Set the transfer length.
|
||
//
|
||
|
||
Srb->DataTransferLength = BufferLength;
|
||
|
||
//
|
||
// Zero out status.
|
||
//
|
||
|
||
Srb->ScsiStatus = Srb->SrbStatus = 0;
|
||
Srb->NextSrb = 0;
|
||
|
||
//
|
||
// Set up IRP Address.
|
||
//
|
||
|
||
Srb->OriginalRequest = irp;
|
||
|
||
//
|
||
// Call the port driver with the request and wait for it to complete.
|
||
//
|
||
|
||
status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
|
||
status = ioStatus.Status;
|
||
}
|
||
|
||
//
|
||
// Check that request completed without error.
|
||
//
|
||
|
||
if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
|
||
|
||
ULONG retryInterval;
|
||
|
||
//
|
||
// Release the queue if it is frozen.
|
||
//
|
||
|
||
if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
||
ClassReleaseQueue(Fdo);
|
||
}
|
||
|
||
//
|
||
// Update status and determine if request should be retried.
|
||
//
|
||
|
||
retry = ClassInterpretSenseInfo(Fdo,
|
||
Srb,
|
||
IRP_MJ_SCSI,
|
||
0,
|
||
MAXIMUM_RETRIES - retryCount,
|
||
&status,
|
||
&retryInterval);
|
||
|
||
if (retry) {
|
||
|
||
if ((status == STATUS_DEVICE_NOT_READY &&
|
||
((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode ==
|
||
SCSI_ADSENSE_LUN_NOT_READY) ||
|
||
(SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
|
||
|
||
LARGE_INTEGER delay;
|
||
|
||
//
|
||
// Delay for at least 2 seconds.
|
||
//
|
||
|
||
if(retryInterval < 2) {
|
||
retryInterval = 2;
|
||
}
|
||
|
||
delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval);
|
||
|
||
//
|
||
// Stall for a while to let the controller spinup.
|
||
//
|
||
|
||
KeDelayExecutionThread(KernelMode, FALSE, &delay);
|
||
|
||
}
|
||
|
||
//
|
||
// If retries are not exhausted then retry this operation.
|
||
//
|
||
|
||
if (retryCount--) {
|
||
goto retry;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
Srb->SenseInfoBuffer = NULL;
|
||
ExFreePool(senseInfoBuffer);
|
||
return status;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ClasspSendSynchronousCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This completion routine will set the user event in the irp after
|
||
freeing the irp and the associated MDL (if any).
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the device object which requested the completion routine
|
||
|
||
Irp - the irp being completed
|
||
|
||
Context - unused
|
||
|
||
Return Value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED
|
||
|
||
--*/
|
||
|
||
{
|
||
DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
|
||
DeviceObject, Irp, Context));
|
||
//
|
||
// First set the status and information fields in the io status block
|
||
// provided by the caller.
|
||
//
|
||
|
||
*(Irp->UserIosb) = Irp->IoStatus;
|
||
|
||
//
|
||
// Unlock the pages for the data buffer.
|
||
//
|
||
|
||
if(Irp->MdlAddress) {
|
||
MmUnlockPages(Irp->MdlAddress);
|
||
IoFreeMdl(Irp->MdlAddress);
|
||
}
|
||
|
||
//
|
||
// Signal the caller's event.
|
||
//
|
||
|
||
KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
|
||
|
||
//
|
||
// Free the MDL and the IRP.
|
||
//
|
||
|
||
IoFreeIrp(Irp);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
PVOID
|
||
ChangerClassAllocatePool(
|
||
IN POOL_TYPE PoolType,
|
||
IN ULONG NumberOfBytes
|
||
)
|
||
|
||
{
|
||
return ExAllocatePoolWithTag(PoolType, NumberOfBytes, 'CMcS');
|
||
}
|
||
|
||
|
||
VOID
|
||
ChangerClassFreePool(
|
||
IN PVOID PoolToFree
|
||
)
|
||
{
|
||
ExFreePool(PoolToFree);
|
||
}
|
||
|
||
#if DBG
|
||
ULONG MCDebug = 0;
|
||
UCHAR DebugBuffer[128];
|
||
#endif
|
||
|
||
|
||
#if DBG
|
||
|
||
VOID
|
||
MCDebugPrint(
|
||
ULONG DebugPrintLevel,
|
||
PCCHAR DebugMessage,
|
||
...
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Debug print for all medium changer drivers
|
||
|
||
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 <= MCDebug) {
|
||
|
||
vsprintf(DebugBuffer, DebugMessage, ap);
|
||
|
||
DbgPrint(DebugBuffer);
|
||
}
|
||
|
||
va_end(ap);
|
||
|
||
} // end MCDebugPrint()
|
||
|
||
#else
|
||
|
||
//
|
||
// DebugPrint stub
|
||
//
|
||
|
||
VOID
|
||
MCDebugPrint(
|
||
ULONG DebugPrintLevel,
|
||
PCCHAR DebugMessage,
|
||
...
|
||
)
|
||
{
|
||
}
|
||
|
||
#endif
|