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.
1607 lines
41 KiB
1607 lines
41 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
mcd.c
|
|
|
|
Abstract:
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History :
|
|
|
|
--*/
|
|
#include "mchgr.h"
|
|
|
|
#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;
|
|
PMCD_INIT_DATA mcdInitData;
|
|
ULONG miniclassExtSize;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
mcdClassData = (PMCD_CLASS_DATA)(fdoExtension->CommonExtension.DriverData);
|
|
|
|
mcdInitData = IoGetDriverObjectExtension(DeviceObject->DriverObject,
|
|
ChangerClassInitialize);
|
|
|
|
if (mcdInitData == NULL) {
|
|
|
|
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject,Irp, IO_NO_INCREMENT);
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
miniclassExtSize = mcdInitData->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;
|
|
PMCD_INIT_DATA mcdInitData;
|
|
NTSTATUS status;
|
|
ULONG ioControlCode;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
mcdInitData = IoGetDriverObjectExtension(DeviceObject->DriverObject,
|
|
ChangerClassInitialize);
|
|
|
|
if (mcdInitData == NULL) {
|
|
|
|
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
|
ClassReleaseRemoveLock(DeviceObject, Irp);
|
|
ClassCompleteRequest(DeviceObject,Irp, IO_NO_INCREMENT);
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Disable media change detection before processing current IOCTL
|
|
//
|
|
ClassDisableMediaChangeDetection(fdoExtension);
|
|
|
|
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
|
switch (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
|
|
{
|
|
PGET_CHANGER_PARAMETERS changerParameters = Irp->AssociatedIrp.SystemBuffer;
|
|
RtlZeroMemory(changerParameters, sizeof(GET_CHANGER_PARAMETERS));
|
|
|
|
status = mcdInitData->ChangerGetParameters(DeviceObject, Irp);
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_CHANGER_GET_STATUS:
|
|
|
|
DebugPrint((3,
|
|
"Mcd.ChangerDeviceControl: IOCTL_CHANGER_GET_STATUS\n"));
|
|
|
|
status = mcdInitData->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
|
|
{
|
|
PCHANGER_PRODUCT_DATA changerProductData = Irp->AssociatedIrp.SystemBuffer;
|
|
RtlZeroMemory(changerProductData, sizeof(CHANGER_PRODUCT_DATA));
|
|
|
|
status = mcdInitData->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 = mcdInitData->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);
|
|
ULONG lengthEx = readElementStatus->ElementList.NumberOfElements * sizeof(CHANGER_ELEMENT_STATUS_EX);
|
|
ULONG outputBuffLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// Further validate parameters.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
if ((outputBuffLen < lengthEx) && (outputBuffLen < length)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
} else if ((length == 0) || (lengthEx == 0)) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = mcdInitData->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 = mcdInitData->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 = mcdInitData->ChangerSetPosition(DeviceObject, Irp);
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_CHANGER_EXCHANGE_MEDIUM:
|
|
|
|
DebugPrint((3, "Mcd.ChangerDeviceControl: IOCTL_CHANGER_EXCHANGE_MEDIUM\n"));
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CHANGER_EXCHANGE_MEDIUM)) {
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
} else {
|
|
status = mcdInitData->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 = mcdInitData->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 = mcdInitData->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 = mcdInitData->ChangerQueryVolumeTags(DeviceObject, Irp);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
DebugPrint((1,
|
|
"Mcd.ChangerDeviceControl: Unhandled IOCTL\n"));
|
|
|
|
|
|
//
|
|
// Pass the request to the common device control routine.
|
|
//
|
|
|
|
status = ClassDeviceControl(DeviceObject, Irp);
|
|
|
|
//
|
|
// Re-enable media change detection
|
|
//
|
|
ClassEnableMediaChangeDetection(fdoExtension);
|
|
|
|
return status;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
//
|
|
// Re-enable media change detection
|
|
//
|
|
ClassEnableMediaChangeDetection(fdoExtension);
|
|
|
|
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;
|
|
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
|
PMCD_INIT_DATA mcdInitData;
|
|
|
|
mcdInitData = IoGetDriverObjectExtension(DeviceObject->DriverObject,
|
|
ChangerClassInitialize);
|
|
|
|
if (mcdInitData == NULL) {
|
|
|
|
return;
|
|
}
|
|
|
|
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
|
|
switch (senseBuffer->SenseKey & 0xf) {
|
|
|
|
case SCSI_SENSE_MEDIUM_ERROR: {
|
|
*Retry = FALSE;
|
|
|
|
if (((senseBuffer->AdditionalSenseCode) == SCSI_ADSENSE_INVALID_MEDIA) &&
|
|
((senseBuffer->AdditionalSenseCodeQualifier) == SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED)) {
|
|
|
|
//
|
|
// This indicates a cleaner cartridge is present in the changer
|
|
//
|
|
*Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
|
|
}
|
|
break;
|
|
}
|
|
|
|
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;
|
|
} // switch (senseBuffer->AdditionalSenseCode)
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION: {
|
|
|
|
if ((senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_MEDIUM_CHANGED) ||
|
|
(senseBuffer->AdditionalSenseCode == 0x3f && senseBuffer->AdditionalSenseCodeQualifier == 0x81)) {
|
|
|
|
//
|
|
// Changers like the Compaq StorageWorks LIB-81 return
|
|
// 0x06/3f/81 when media is injected/ejected into the
|
|
// changer instead of 0x06/28/00
|
|
//
|
|
|
|
//
|
|
// Need to notify applications of possible media change in
|
|
// the library. First, set the current media state to
|
|
// NotPresent and then set the state to present. We need to
|
|
// do this because, changer devices do not report MediaNotPresent
|
|
// state. They only convey MediumChanged condition. In order for
|
|
// classpnp to notify applications of media change, we need to
|
|
// simulate media notpresent to present state transition
|
|
//
|
|
ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
|
|
ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
|
|
} // end switch (senseBuffer->SenseKey & 0xf)
|
|
} // if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)
|
|
|
|
//
|
|
// Call Changer MiniDriver error routine only if we
|
|
// are running at or below APC_LEVEL
|
|
//
|
|
if (KeGetCurrentIrql() > APC_LEVEL) {
|
|
return;
|
|
}
|
|
|
|
if (mcdInitData->ChangerError) {
|
|
//
|
|
// Allow device-specific module to update this.
|
|
//
|
|
mcdInitData->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;
|
|
PMCD_INIT_DATA mcdInitData;
|
|
ULONG miniClassExtSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
mcdInitData = IoGetDriverObjectExtension(Fdo->DriverObject,
|
|
ChangerClassInitialize);
|
|
|
|
if (mcdInitData == NULL) {
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
miniClassExtSize = mcdInitData->ChangerAdditionalExtensionSize();
|
|
|
|
//
|
|
// 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;
|
|
PMCD_INIT_DATA mcdInitData;
|
|
STORAGE_PROPERTY_ID propertyId;
|
|
UNICODE_STRING interfaceName;
|
|
PMCD_CLASS_DATA mcdClassData;
|
|
|
|
PAGED_CODE();
|
|
|
|
mcdInitData = IoGetDriverObjectExtension(Fdo->DriverObject,
|
|
ChangerClassInitialize);
|
|
|
|
if (mcdInitData == NULL) {
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// 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,
|
|
"ChangerInitDevice: Unable to get adapter descriptor. Status %x\n",
|
|
status));
|
|
goto ChangerInitDeviceExit;
|
|
}
|
|
|
|
//
|
|
// Invoke the device-specific initialization function.
|
|
//
|
|
|
|
status = mcdInitData->ChangerInitialize(Fdo);
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((1,
|
|
"ChangerInitDevice: Minidriver Init failed. Status %x\n",
|
|
status));
|
|
goto ChangerInitDeviceExit;
|
|
}
|
|
|
|
//
|
|
// Register for media change notification
|
|
//
|
|
ClassInitializeMediaChangeDetection(fdoExtension, "Changer");
|
|
|
|
//
|
|
// Register interfaces for this device.
|
|
//
|
|
|
|
RtlInitUnicodeString(&interfaceName, NULL);
|
|
|
|
status = IoRegisterDeviceInterface(fdoExtension->LowerPdo,
|
|
(LPGUID) &MediumChangerClassGuid,
|
|
NULL,
|
|
&interfaceName);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
mcdClassData = (PMCD_CLASS_DATA)(fdoExtension->CommonExtension.DriverData);
|
|
|
|
//
|
|
// The class library's private data is after the miniclass's.
|
|
//
|
|
|
|
(ULONG_PTR)mcdClassData += mcdInitData->ChangerAdditionalExtensionSize();
|
|
|
|
((MCD_CLASS_DATA UNALIGNED *)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);
|
|
fdoExtension->SenseData = NULL;
|
|
}
|
|
|
|
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;
|
|
PMCD_INIT_DATA mcdInitData;
|
|
ULONG miniClassExtSize;
|
|
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;
|
|
}
|
|
|
|
|
|
mcdInitData = IoGetDriverObjectExtension(DeviceObject->DriverObject,
|
|
ChangerClassInitialize);
|
|
|
|
if (mcdInitData == NULL) {
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
miniClassExtSize = mcdInitData->ChangerAdditionalExtensionSize();
|
|
|
|
//
|
|
// Free all allocated memory.
|
|
//
|
|
|
|
if (Type == IRP_MN_REMOVE_DEVICE){
|
|
if (fdoExtension->DeviceDescriptor) {
|
|
ExFreePool(fdoExtension->DeviceDescriptor);
|
|
fdoExtension->DeviceDescriptor = NULL;
|
|
}
|
|
if (fdoExtension->AdapterDescriptor) {
|
|
ExFreePool(fdoExtension->AdapterDescriptor);
|
|
fdoExtension->AdapterDescriptor = NULL;
|
|
}
|
|
if (fdoExtension->SenseData) {
|
|
ExFreePool(fdoExtension->SenseData);
|
|
fdoExtension->SenseData = NULL;
|
|
}
|
|
ClassDeleteSrbLookasideList(&fdoExtension->CommonExtension);
|
|
}
|
|
|
|
(ULONG_PTR)mcdClassData += miniClassExtSize;
|
|
|
|
if((((MCD_CLASS_DATA UNALIGNED *)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 is the entry point for this EXPORT DRIVER. It does nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ChangerClassInitialize(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath,
|
|
IN PMCD_INIT_DATA MCDInitData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by a changer mini-class driver during its
|
|
DriverEntry routine to initialize the driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies the driver object.
|
|
|
|
RegistryPath - Supplies the registry path for this driver.
|
|
|
|
MCDInitData - Changer Minidriver Init Data
|
|
|
|
Return Value:
|
|
|
|
Status value returned by ClassInitialize
|
|
--*/
|
|
|
|
{
|
|
PMCD_INIT_DATA driverExtension;
|
|
CLASS_INIT_DATA InitializationData;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Get the driver extension
|
|
//
|
|
status = IoAllocateDriverObjectExtension(
|
|
DriverObject,
|
|
ChangerClassInitialize,
|
|
sizeof(MCD_INIT_DATA),
|
|
&driverExtension);
|
|
if (!NT_SUCCESS(status)) {
|
|
if(status == STATUS_OBJECT_NAME_COLLISION) {
|
|
|
|
//
|
|
// An extension already exists for this key. Get a pointer to it
|
|
//
|
|
|
|
driverExtension = IoGetDriverObjectExtension(DriverObject,
|
|
ChangerClassInitialize);
|
|
if (driverExtension == NULL) {
|
|
DebugPrint((1,
|
|
"ChangerClassInitialize : driverExtension NULL\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// As this failed, the changer init data won't be able to be stored.
|
|
//
|
|
|
|
DebugPrint((1,
|
|
"ChangerClassInitialize: Error %x allocating driver extension\n",
|
|
status));
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
RtlCopyMemory(driverExtension, MCDInitData, sizeof(MCD_INIT_DATA));
|
|
|
|
//
|
|
// Zero InitData
|
|
//
|
|
|
|
RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
|
|
|
|
//
|
|
// Set sizes
|
|
//
|
|
|
|
InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
|
|
|
|
InitializationData.FdoData.DeviceExtensionSize =
|
|
sizeof(FUNCTIONAL_DEVICE_EXTENSION) +
|
|
MCDInitData->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;
|
|
|
|
|
|
//
|
|
// Routines for WMI support
|
|
//
|
|
InitializationData.FdoData.ClassWmiInfo.GuidCount = 3;
|
|
InitializationData.FdoData.ClassWmiInfo.GuidRegInfo = ChangerWmiFdoGuidList;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiRegInfo = ChangerFdoQueryWmiRegInfo;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiDataBlock = ChangerFdoQueryWmiDataBlock;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataBlock = ChangerFdoSetWmiDataBlock;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataItem = ChangerFdoSetWmiDataItem;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassExecuteWmiMethod = ChangerFdoExecuteWmiMethod;
|
|
InitializationData.FdoData.ClassWmiInfo.ClassWmiFunctionControl = ChangerWmiFunctionControl;
|
|
|
|
//
|
|
// 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;
|
|
PMCD_INIT_DATA mcdInitData;
|
|
ULONG mcdCount;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((3,"CreateChangerDeviceObject: Enter routine\n"));
|
|
|
|
//
|
|
// Get the saved MCD Init Data
|
|
//
|
|
mcdInitData = IoGetDriverObjectExtension(DriverObject,
|
|
ChangerClassInitialize);
|
|
|
|
if (mcdInitData == NULL) {
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
ASSERT(mcdInitData);
|
|
|
|
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 += mcdInitData->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 DeviceObject,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
IN BOOLEAN WriteToDevice
|
|
)
|
|
{
|
|
return ClassSendSrbSynchronous(DeviceObject, Srb,
|
|
Buffer, BufferSize,
|
|
WriteToDevice);
|
|
}
|
|
|
|
|
|
PVOID
|
|
ChangerClassAllocatePool(
|
|
IN POOL_TYPE PoolType,
|
|
IN ULONG NumberOfBytes
|
|
)
|
|
|
|
{
|
|
return ExAllocatePoolWithTag(PoolType, NumberOfBytes, 'CMcS');
|
|
}
|
|
|
|
|
|
VOID
|
|
ChangerClassFreePool(
|
|
IN PVOID PoolToFree
|
|
)
|
|
{
|
|
ExFreePool(PoolToFree);
|
|
}
|
|
|
|
#if DBG
|
|
#define MCHGR_DEBUG_PRINT_BUFF_LEN 127
|
|
ULONG MCDebug = 0;
|
|
UCHAR DebugBuffer[MCHGR_DEBUG_PRINT_BUFF_LEN + 1];
|
|
#endif
|
|
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
ChangerClassDebugPrint(
|
|
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) {
|
|
|
|
_vsnprintf(DebugBuffer, MCHGR_DEBUG_PRINT_BUFF_LEN,
|
|
DebugMessage, ap);
|
|
DebugBuffer[MCHGR_DEBUG_PRINT_BUFF_LEN] = '\0';
|
|
|
|
DbgPrintEx(DPFLTR_MCHGR_ID, DPFLTR_INFO_LEVEL, DebugBuffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
} // end MCDebugPrint()
|
|
|
|
#else
|
|
|
|
//
|
|
// DebugPrint stub
|
|
//
|
|
|
|
VOID
|
|
ChangerClassDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
)
|
|
{
|
|
}
|
|
|
|
#endif
|