|
|
/*++
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
|