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

1613 lines
43 KiB
C
Raw Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (C) Microsoft Corporation, 1997 - 1999
Module Name:
changer.c
Abstract:
Authors:
Chuck Park (chuckp)
Environment:
kernel mode only
Notes:
--*/
#include "cdchgr.h"
#include "ntddcdrm.h"
#include "initguid.h"
#include "ntddstor.h"
//
// Function declarations
//
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
ChangerAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
);
NTSTATUS
ChangerPnp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
ChangerPower(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
ChangerStartDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
ChangerSendToNextDriver(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
ChangerCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
ChangerPassThrough(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
ChangerDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
ChangerUnload(
IN PDRIVER_OBJECT DriverObject
);
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
Installable driver initialization entry point.
Arguments:
DriverObject - Supplies the driver object.
RegistryPath - pointer to a unicode string representing the path,
to driver-specific key in the registry.
Return Value:
STATUS_SUCCESS if successful
--*/
{
ULONG i;
DebugPrint((2,
"Changer: DriverEntry\n"));
//
// Set up the device driver entry points.
//
DriverObject->MajorFunction[IRP_MJ_CREATE] = ChangerPassThrough;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ChangerPassThrough;
DriverObject->MajorFunction[IRP_MJ_READ] = ChangerPassThrough;
DriverObject->MajorFunction[IRP_MJ_WRITE] = ChangerPassThrough;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ChangerDeviceControl;
DriverObject->MajorFunction[IRP_MJ_PNP] = ChangerPnp;
DriverObject->MajorFunction[IRP_MJ_POWER] = ChangerPower;
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ChangerPassThrough;
DriverObject->DriverExtension->AddDevice = ChangerAddDevice;
DriverObject->DriverUnload = ChangerUnload;
return STATUS_SUCCESS;
} // end DriverEntry()
NTSTATUS
ChangerCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine serves create commands. It does no more than
establish the drivers existence by returning status success.
Arguments:
DeviceObject
IRP
Return Value:
NT Status
--*/
{
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, 0);
return STATUS_SUCCESS;
}
NTSTATUS
ChangerAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
Creates and initializes a new filter device object FDO for the
corresponding PDO. Then it attaches the device object to the device
stack of the drivers for the device.
Arguments:
DriverObject - Changer DriverObject.
PhysicalDeviceObject - Physical Device Object from the underlying driver
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatus;
PDEVICE_OBJECT filterDeviceObject;
PDEVICE_EXTENSION deviceExtension;
UNICODE_STRING additionalString;
DebugPrint((2,
"ChangerAddDevice\n"));
//
// Create a filter device object for the underlying cdrom device.
//
status = IoCreateDevice(DriverObject,
DEVICE_EXTENSION_SIZE,
NULL,
FILE_DEVICE_CD_ROM,
0,
FALSE,
&filterDeviceObject);
if (!NT_SUCCESS(status)) {
DebugPrint((2,
"ChangerAddDevice: IoCreateDevice failed %lx\n",
status));
return status;
}
filterDeviceObject->Flags |= DO_DIRECT_IO;
if (filterDeviceObject->Flags & DO_POWER_INRUSH) {
DebugPrint((1,
"ChangerAddDevice: Someone set DO_POWER_INRUSH?\n",
status
));
} else {
filterDeviceObject->Flags |= DO_POWER_PAGABLE;
}
deviceExtension = (PDEVICE_EXTENSION) filterDeviceObject->DeviceExtension;
RtlZeroMemory(deviceExtension, DEVICE_EXTENSION_SIZE);
//
// Attaches the device object to the highest device object in the chain and
// return the previously highest device object, which is passed to IoCallDriver
// when pass IRPs down the device stack
//
deviceExtension->CdromTargetDeviceObject =
IoAttachDeviceToDeviceStack(filterDeviceObject, PhysicalDeviceObject);
if (deviceExtension->CdromTargetDeviceObject == NULL) {
DebugPrint((2,
"ChangerAddDevice: IoAttachDevice failed %lx\n",
STATUS_NO_SUCH_DEVICE));
IoDeleteDevice(filterDeviceObject);
return STATUS_NO_SUCH_DEVICE;
}
//
// Save the filter device object in the device extension
//
deviceExtension->DeviceObject = filterDeviceObject;
//
// Initialize the event for PagingPathNotifications
//
KeInitializeEvent(&deviceExtension->PagingPathCountEvent,
SynchronizationEvent, TRUE);
//
// Register interfaces for this device.
//
RtlInitUnicodeString(&(deviceExtension->InterfaceName), NULL);
RtlInitUnicodeString(&(additionalString), L"CdChanger");
status = IoRegisterDeviceInterface(PhysicalDeviceObject,
(LPGUID) &CdChangerClassGuid,
&additionalString,
&(deviceExtension->InterfaceName));
DebugPrint((1,
"Changer: IoRegisterDeviceInterface - status %lx",
status));
filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
} // end ChangerAddDevice()
NTSTATUS
ChgrCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
/*++
Routine Description:
This completion routine sets the event waited on by the start device.
Arguments:
DeviceObject - a pointer to the device object
Irp - a pointer to the irp
Event - a pointer to the event to signal
Return Value:
STATUS_MORE_PROCESSING_REQUIRED
--*/
{
KeSetEvent(Event,
IO_NO_INCREMENT,
FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
ChangerStartDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
CCHAR dosNameBuffer[64];
CCHAR deviceNameBuffer[64];
STRING deviceNameString;
STRING dosString;
UNICODE_STRING dosUnicodeString;
UNICODE_STRING unicodeString;
PIRP irp2;
IO_STATUS_BLOCK ioStatus;
STORAGE_DEVICE_NUMBER deviceNumber;
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
KEVENT event;
PPASS_THROUGH_REQUEST passThrough = NULL;
PSCSI_PASS_THROUGH srb;
PCDB cdb;
//
// Get the current changer count.
//
//devicesFound = &IoGetConfigurationInformation()->MediumChangerCount;
//
// Recreate the deviceName of the underlying cdrom.
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp2 = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
deviceExtension->CdromTargetDeviceObject,
NULL,
0,
&deviceNumber,
sizeof(STORAGE_DEVICE_NUMBER),
FALSE,
&event,
&ioStatus);
if (!irp2) {
DebugPrint((1,
"ChangerStartDevice: Insufficient resources for GET_DEVICE_NUMBER request\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto StartDeviceExit;
}
status = IoCallDriver(deviceExtension->CdromTargetDeviceObject,irp2);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS(status)) {
DebugPrint((1,
"ChangerStartDevice: GetDeviceNumber failed %lx\n",
status));
goto StartDeviceExit;
}
deviceExtension->CdRomDeviceNumber = deviceNumber.DeviceNumber;
//
// Create the the arcname with the same ordinal as the underlying cdrom device.
//
sprintf(dosNameBuffer,
"\\DosDevices\\CdChanger%d",
deviceExtension->CdRomDeviceNumber);
RtlInitString(&dosString, dosNameBuffer);
status = RtlAnsiStringToUnicodeString(&dosUnicodeString,
&dosString,
TRUE);
if(!NT_SUCCESS(status)) {
status = STATUS_INSUFFICIENT_RESOURCES;
dosUnicodeString.Buffer = NULL;
}
sprintf(deviceNameBuffer,
"\\Device\\CdRom%d",
deviceExtension->CdRomDeviceNumber);
RtlInitString(&deviceNameString,
deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(&unicodeString,
&deviceNameString,
TRUE);
if (!NT_SUCCESS(status)) {
status = STATUS_INSUFFICIENT_RESOURCES;
unicodeString.Buffer = NULL;
}
if (dosUnicodeString.Buffer != NULL && unicodeString.Buffer != NULL) {
//
// Link the ChangerName to the Underlying cdrom name.
//
IoCreateSymbolicLink(&dosUnicodeString, &unicodeString);
}
if (dosUnicodeString.Buffer != NULL) {
RtlFreeUnicodeString(&dosUnicodeString);
}
if (unicodeString.Buffer != NULL ) {
RtlFreeUnicodeString(&unicodeString);
}
if (NT_SUCCESS(status)) {
ULONG length;
ULONG slotCount;
//
// Get the inquiry data for the device.
// The passThrough packet will be re-used throughout.
// Ensure that the buffer is never larger than MAX_INQUIRY_DATA.
//
passThrough = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(PASS_THROUGH_REQUEST) + MAX_INQUIRY_DATA);
if (!passThrough) {
DebugPrint((1,
"ChangerStartDevice: Insufficient resources for Inquiry request\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto StartDeviceExit;
}
srb = &passThrough->Srb;
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST) + MAX_INQUIRY_DATA);
cdb = (PCDB)srb->Cdb;
srb->TimeOutValue = 20;
srb->CdbLength = CDB6GENERIC_LENGTH;
srb->DataTransferLength = MAX_INQUIRY_DATA;
//
// Set CDB operation code.
//
cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
//
// Set allocation length to inquiry data buffer size.
//
cdb->CDB6INQUIRY.AllocationLength = MAX_INQUIRY_DATA;
status = SendPassThrough(DeviceObject,
passThrough);
if (status == STATUS_DATA_OVERRUN) {
status = STATUS_SUCCESS;
}
if (NT_SUCCESS(status)) {
PINQUIRYDATA inquiryData;
ULONG inquiryLength;
//
// Determine the actual inquiry data length.
//
inquiryData = (PINQUIRYDATA)passThrough->DataBuffer;
inquiryLength = inquiryData->AdditionalLength + FIELD_OFFSET(INQUIRYDATA, Reserved);
if (inquiryLength > srb->DataTransferLength) {
inquiryLength = srb->DataTransferLength;
}
//
// Copy to deviceExtension buffer.
//
RtlMoveMemory(&deviceExtension->InquiryData,
inquiryData,
inquiryLength);
//
// Assume atapi 2.5, unless it's one of the special drives.
//
deviceExtension->DeviceType = ATAPI_25;
if (RtlCompareMemory(inquiryData->VendorId,"ALPS", 4) == 4) {
//
// Nominally supporting the spec. the discChanged bits are ALWAYS set
// and DiscPresent is set if the cartridge has a tray, not necessarily
// an actual disc in the tray.
//
deviceExtension->DeviceType = ALPS_25;
} else if ((RtlCompareMemory(inquiryData->VendorId, "TORiSAN CD-ROM CDR-C", 20) == 20) ||
(RtlCompareMemory(inquiryData->VendorId, "TORiSAN CD-ROM CDR_C", 20) == 20)) {
deviceExtension->DeviceType = TORISAN;
deviceExtension->NumberOfSlots = 3;
status = STATUS_SUCCESS;
}
}
if (deviceExtension->DeviceType != TORISAN) {
//
// Send an unload to ensure that the drive is empty.
// The spec. specifically states that after HW initialization
// slot0 is loaded. Good for unaware drivers, but the mech. status
// will return that slot 0 has media, and a TUR will return that
// the drive also has media.
//
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST));
/*
cdb = (PCDB)srb->Cdb;
srb->CdbLength = CDB12GENERIC_LENGTH;
srb->TimeOutValue = CDCHGR_TIMEOUT;
srb->DataTransferLength = 0;
cdb->LOAD_UNLOAD.OperationCode = SCSIOP_LOAD_UNLOAD_SLOT;
cdb->LOAD_UNLOAD.Start = 0;
cdb->LOAD_UNLOAD.LoadEject = 1;
//
// Send SCSI command (CDB) to device
//
status = SendPassThrough(DeviceObject,
passThrough);
if (!NT_SUCCESS(status)) {
//
// Ignore this error.
//
DebugPrint((1,
"ChangerPnP - StartDevive: Unload slot0 failed. %lx\n",
status));
status = STATUS_SUCCESS;
}
*/
//
// Now send and build a mech. status request to determine the
// number of slots that the devices supports.
//
length = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER);
length += (10 * sizeof(SLOT_TABLE_INFORMATION));
//
// Build srb and cdb.
//
srb = &passThrough->Srb;
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST) + length);
cdb = (PCDB)srb->Cdb;
srb->CdbLength = CDB12GENERIC_LENGTH;
srb->DataTransferLength = length;
srb->TimeOutValue = 200;
cdb->MECH_STATUS.OperationCode = SCSIOP_MECHANISM_STATUS;
cdb->MECH_STATUS.AllocationLength[0] = (UCHAR)(length >> 8);
cdb->MECH_STATUS.AllocationLength[1] = (UCHAR)(length & 0xFF);
status = SendPassThrough(DeviceObject,
passThrough);
if (status == STATUS_DATA_OVERRUN) {
status = STATUS_SUCCESS;
}
if (NT_SUCCESS(status)) {
PMECHANICAL_STATUS_INFORMATION_HEADER statusHeader;
PSLOT_TABLE_INFORMATION slotInfo;
ULONG currentSlot;
statusHeader = (PMECHANICAL_STATUS_INFORMATION_HEADER)
passThrough->DataBuffer;
slotCount = statusHeader->NumberAvailableSlots;
DebugPrint((1,
"ChangerPnP - StartDevice: Device has %x slots\n",
slotCount));
deviceExtension->NumberOfSlots = slotCount;
}
}
if (NT_SUCCESS(status)) {
KeInitializeEvent(&event,NotificationEvent,FALSE);
//
// Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
//
irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS,
deviceExtension->CdromTargetDeviceObject,
NULL,
0,
&deviceExtension->ScsiAddress,
sizeof(SCSI_ADDRESS),
FALSE,
&event,
&ioStatus);
if (irp2 != NULL) {
status = IoCallDriver(deviceExtension->CdromTargetDeviceObject, irp2);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
if (NT_SUCCESS(status)) {
DebugPrint((1,
"GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
deviceExtension->ScsiAddress.PortNumber,
deviceExtension->ScsiAddress.PathId,
deviceExtension->ScsiAddress.TargetId,
deviceExtension->ScsiAddress.Lun));
if (deviceExtension->DeviceType != TORISAN) {
//
// Finally send a mode sense capabilities page to find out magazine size, etc.
//
length = sizeof(MODE_PARAMETER_HEADER10) + sizeof(CDVD_CAPABILITIES_PAGE);
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST) + length);
srb = &passThrough->Srb;
cdb = (PCDB)srb->Cdb;
srb->CdbLength = CDB10GENERIC_LENGTH;
srb->DataTransferLength = length;
srb->TimeOutValue = 20;
cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
cdb->MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES;
cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(length >> 8);
cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(length & 0xFF);
status = SendPassThrough(DeviceObject,
passThrough);
if (status == STATUS_DATA_OVERRUN) {
status = STATUS_SUCCESS;
}
if (NT_SUCCESS(status)) {
PMODE_PARAMETER_HEADER10 modeHeader;
PCDVD_CAPABILITIES_PAGE modePage;
(ULONG_PTR)modeHeader = (ULONG_PTR)passThrough->DataBuffer;
(ULONG_PTR)modePage = (ULONG_PTR)modeHeader;
(ULONG_PTR)modePage += sizeof(MODE_PARAMETER_HEADER10);
//
// Determine whether this device uses a cartridge.
//
if ( modePage->LoadingMechanismType ==
CDVD_LMT_CHANGER_CARTRIDGE ) {
//
// Mode data indicates a cartridge.
//
deviceExtension->MechType = 1;
}
DebugPrint((1,
"ChangerStartDevice: Cartridge? %x\n",
deviceExtension->MechType));
goto StartDeviceExit;
} else {
goto StartDeviceExit;
}
} else {
//
// Torisans have a cartridge, not ind. slots.
//
deviceExtension->MechType = 1;
goto StartDeviceExit;
}
} else {
DebugPrint((1,
"ChangerStartDevice: GetAddress of Cdrom%x failed. Status %lx\n",
deviceExtension->CdRomDeviceNumber,
status));
goto StartDeviceExit;
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
DebugPrint((1,
"ChangerPnP - StartDevice: Mechanism status failed %lx.\n",
status));
//
// Fall through.
//
}
}
StartDeviceExit:
if (passThrough) {
ExFreePool(passThrough);
}
if (NT_SUCCESS(status)) {
if (!deviceExtension->InterfaceStateSet) {
status = IoSetDeviceInterfaceState(&(deviceExtension->InterfaceName),
TRUE);
deviceExtension->InterfaceStateSet = TRUE;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
return STATUS_SUCCESS;
} else {
Irp->IoStatus.Status = status;
return status;
}
}
NTSTATUS
ChangerPnp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Dispatch for PNP
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
CCHAR dosNameBuffer[64];
STRING dosString;
UNICODE_STRING dosUnicodeString;
NTSTATUS status;
KEVENT event;
DebugPrint((2,
"ChangerPnP\n"));
switch (irpStack->MinorFunction) {
case IRP_MN_START_DEVICE: {
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine( Irp,
ChgrCompletion,
&event,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(deviceExtension->CdromTargetDeviceObject, Irp);
KeWaitForSingleObject(&event,
Executive,
KernelMode,
FALSE,
NULL);
if(!NT_SUCCESS(Irp->IoStatus.Status)) {
//
// Cdrom failed to start. Bail now.
//
status = Irp->IoStatus.Status;
} else {
status = ChangerStartDevice(DeviceObject,
Irp);
}
break;
}
case IRP_MN_REMOVE_DEVICE: {
//
// IoDelete fake dev. obj
//
status = IoSetDeviceInterfaceState(&(deviceExtension->InterfaceName),
FALSE);
deviceExtension->InterfaceStateSet = FALSE;
RtlFreeUnicodeString(&(deviceExtension->InterfaceName));
//
// Poison it.
//
RtlInitUnicodeString(&(deviceExtension->InterfaceName), NULL);
//
// Delete the symbolic link "CdChangerN".
//
sprintf(dosNameBuffer,
"\\DosDevices\\CdChanger%d",
deviceExtension->CdRomDeviceNumber);
RtlInitString(&dosString, dosNameBuffer);
status = RtlAnsiStringToUnicodeString(&dosUnicodeString,
&dosString,
TRUE);
ASSERT(NT_SUCCESS(status));
if (dosUnicodeString.Buffer != NULL) {
status = IoDeleteSymbolicLink(&dosUnicodeString);
RtlFreeUnicodeString(&dosUnicodeString);
}
IoDetachDevice(deviceExtension->CdromTargetDeviceObject);
return ChangerSendToNextDriver(DeviceObject, Irp);
break;
}
case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
ULONG count;
BOOLEAN setPagable;
if (irpStack->Parameters.UsageNotification.Type != DeviceUsageTypePaging) {
status = ChangerSendToNextDriver(DeviceObject, Irp);
break; // out of case statement
}
//
// wait on the paging path event
//
status = KeWaitForSingleObject(&deviceExtension->PagingPathCountEvent,
Executive, KernelMode,
FALSE, NULL);
//
// if removing last paging device, need to set DO_POWER_PAGABLE
// bit here, and possible re-set it below on failure.
//
setPagable = FALSE;
if (!irpStack->Parameters.UsageNotification.InPath &&
deviceExtension->PagingPathCount == 1 ) {
//
// removing the last paging file.
// must have DO_POWER_PAGABLE bits set
//
if (DeviceObject->Flags & DO_POWER_INRUSH) {
DebugPrint((2, "ChangerPnp: last paging file removed "
"bug DO_POWER_INRUSH set, so not setting "
"DO_POWER_PAGABLE bit for DO %p\n",
DeviceObject));
} else {
DebugPrint((2, "ChangerPnp: Setting PAGABLE "
"bit for DO %p\n", DeviceObject));
DeviceObject->Flags |= DO_POWER_PAGABLE;
setPagable = TRUE;
}
}
//
// send the irp synchronously
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine( Irp, ChgrCompletion,
&event, TRUE, TRUE, TRUE);
status = IoCallDriver(deviceExtension->CdromTargetDeviceObject, Irp);
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
//
// now deal with the failure and success cases.
// note that we are not allowed to fail the irp
// once it is sent to the lower drivers.
//
if (NT_SUCCESS(status)) {
IoAdjustPagingPathCount(
&deviceExtension->PagingPathCount,
irpStack->Parameters.UsageNotification.InPath);
if (irpStack->Parameters.UsageNotification.InPath) {
if (deviceExtension->PagingPathCount == 1) {
DebugPrint((2, "ChangerPnp: Clearing PAGABLE bit "
"for DO %p\n", DeviceObject));
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
}
}
} else {
if (setPagable == TRUE) {
DeviceObject->Flags &= ~DO_POWER_PAGABLE;
setPagable = FALSE;
}
}
//
// set the event so the next one can occur.
//
KeSetEvent(&deviceExtension->PagingPathCountEvent,
IO_NO_INCREMENT, FALSE);
break;
}
default:
return ChangerSendToNextDriver(DeviceObject, Irp);
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
} // end ChangerPnp()
NTSTATUS
ChangerSendToNextDriver(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine sends the Irp to the next driver in line
when the Irp is not processed by this driver.
Arguments:
DeviceObject
Irp
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
DebugPrint((2,
"ChangerSendToNextDriver\n"));
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(deviceExtension->CdromTargetDeviceObject, Irp);
} // end ChangerSendToNextDriver()
NTSTATUS
ChangerPower(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension;
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
return PoCallDriver(deviceExtension->CdromTargetDeviceObject, Irp);
}
NTSTATUS
ChangerDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This routine handles the medium changer ioctls, and
passes down most cdrom ioctls to the target device.
Arguments:
DeviceObject
Irp
Return Value:
Status is returned.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
DebugPrint((2,
"ChangerDeviceControl\n"));
if (ChgrIoctl(irpStack->Parameters.DeviceIoControl.IoControlCode)) {
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_CHANGER_GET_STATUS:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_GET_STATUS\n"));
status = ChgrGetStatus(DeviceObject, Irp);
break;
case IOCTL_CHANGER_GET_PARAMETERS:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_GET_PARAMETERS\n"));
//
// Validate buffer length.
//
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(GET_CHANGER_PARAMETERS)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
status = ChgrGetParameters(DeviceObject, Irp);
}
break;
case IOCTL_CHANGER_GET_PRODUCT_DATA:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_GET_PRODUCT_DATA\n"));
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(CHANGER_PRODUCT_DATA)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
status = ChgrGetProductData(DeviceObject, Irp);
}
break;
case IOCTL_CHANGER_SET_ACCESS:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_SET_ACCESS\n"));
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(CHANGER_SET_ACCESS)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CHANGER_SET_ACCESS)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
status = ChgrSetAccess(DeviceObject, Irp);
}
break;
case IOCTL_CHANGER_GET_ELEMENT_STATUS:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_GET_ELEMENT_STATUS\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CHANGER_READ_ELEMENT_STATUS)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
status = ChgrGetElementStatus(DeviceObject, Irp);
}
break;
case IOCTL_CHANGER_INITIALIZE_ELEMENT_STATUS:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_INITIALIZE_ELEMENT_STATUS\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CHANGER_INITIALIZE_ELEMENT_STATUS)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
status = ChgrInitializeElementStatus(DeviceObject, Irp);
}
break;
case IOCTL_CHANGER_SET_POSITION:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_SET_POSITION\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CHANGER_SET_POSITION)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
status = ChgrSetPosition(DeviceObject, Irp);
}
break;
case IOCTL_CHANGER_EXCHANGE_MEDIUM:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_EXCHANGE_MEDIUM\n"));
status = ChgrExchangeMedium(DeviceObject, Irp);
break;
case IOCTL_CHANGER_MOVE_MEDIUM:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_MOVE_MEDIUM\n"));
//if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
// sizeof(CHANGER_MOVE_MEDIUM)) {
// status = STATUS_INFO_LENGTH_MISMATCH;
//} else
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CHANGER_MOVE_MEDIUM)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
status = ChgrMoveMedium(DeviceObject, Irp);
}
break;
case IOCTL_CHANGER_REINITIALIZE_TRANSPORT:
DebugPrint((2,
"CdChgrDeviceControl: IOCTL_CHANGER_REINITIALIZE_TRANSPORT\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CHANGER_ELEMENT)) {
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
status = ChgrReinitializeUnit(DeviceObject, Irp);
}
break;
case IOCTL_CHANGER_QUERY_VOLUME_TAGS:
DebugPrint((2,
"CdChgrDeviceControl: 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 = ChgrQueryVolumeTags(DeviceObject, Irp);
}
break;
default:
DebugPrint((1,
"CdChgrDeviceControl: Unhandled IOCTL\n"));
//
// Set current stack back one.
//
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++;
//
// Pass unrecognized device control requests
// down to next driver layer.
//
return IoCallDriver(deviceExtension->CdromTargetDeviceObject, Irp);
}
} else {
if (deviceExtension->DeviceType == TORISAN) {
ULONG ioctlCode;
ULONG baseCode;
ULONG functionCode;
ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
baseCode = ioctlCode >> 16;
functionCode = (ioctlCode & (~0xffffc003)) >> 2;
if((functionCode >= 0x200) && (functionCode <= 0x300)) {
ioctlCode = (ioctlCode & 0x0000ffff) | CTL_CODE(IOCTL_CDROM_BASE, 0, 0, 0);
}
if ((ioctlCode == IOCTL_CDROM_CHECK_VERIFY) || (ioctlCode == IOCTL_STORAGE_GET_MEDIA_TYPES_EX)) {
if (ioctlCode == IOCTL_CDROM_CHECK_VERIFY) {
//
// The fine torisan drives overload TUR as a method to switch platters. Have to send this down via passthrough with the
// appropriate bits set.
//
status = SendTorisanCheckVerify(DeviceObject, Irp);
} else if (ioctlCode == IOCTL_STORAGE_GET_MEDIA_TYPES_EX) {
PGET_MEDIA_TYPES mediaTypes = Irp->AssociatedIrp.SystemBuffer;
PDEVICE_MEDIA_INFO mediaInfo = &mediaTypes->MediaInfo[0];
DebugPrint((1,
"ChangerDeviceControl: GET_MEDIA_TYPES\n"));
//
// Yet another case of having to workaround this design. Media types requires knowing if
// media is present. As the cdrom driver will send a TUR, this will always switch to the first
// platter. So fake it here.
//
//
// Ensure that buffer is large enough.
//
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(GET_MEDIA_TYPES)) {
//
// Buffer too small.
//
Irp->IoStatus.Information = 0;
status = STATUS_INFO_LENGTH_MISMATCH;
} else {
//
// Set the type.
//
mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = CD_ROM;
mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_ONLY;
mediaTypes->DeviceType = FILE_DEVICE_CD_ROM;
mediaTypes->MediaInfoCount = 1;
status = SendTorisanCheckVerify(DeviceObject, Irp);
if (NT_SUCCESS(status)) {
mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics |= MEDIA_CURRENTLY_MOUNTED;
}
//todo issue IOCTL_CDROM_GET_DRIVE_GEOMETRY to fill in the geom. information.
mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = 2048;
Irp->IoStatus.Information = sizeof(GET_MEDIA_TYPES);
status = STATUS_SUCCESS;
}
}
} else {
DebugPrint((1,
"CdChgrDeviceControl: Unhandled IOCTL\n"));
//
// Set current stack back one.
//
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++;
//
// Pass unrecognized device control requests
// down to next driver layer.
//
return IoCallDriver(deviceExtension->CdromTargetDeviceObject, Irp);
}
} else {
status = STATUS_SUCCESS;
if (deviceExtension->CdromTargetDeviceObject->Flags & DO_VERIFY_VOLUME) {
DebugPrint((1,
"ChangerDeviceControl: Volume needs to be verified\n"));
if (!(irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) {
status = STATUS_VERIFY_REQUIRED;
}
}
if (NT_SUCCESS(status)) {
//
// Set current stack back one.
//
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++;
//
// Pass unrecognized device control requests
// down to next driver layer.
//
return IoCallDriver(deviceExtension->CdromTargetDeviceObject, Irp);
}
}
}
Irp->IoStatus.Status = status;
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
DebugPrint((1,
"Mcd.ChangerDeviceControl: IOCTL %x, status %lx\n",
irpStack->Parameters.DeviceIoControl.IoControlCode,
status));
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
}
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
} // end ChangerDeviceControl()
NTSTATUS
ChangerPassThrough(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the IO request packet.
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
DebugPrint((2,
"ChangerPassThrough\n"));
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(deviceExtension->CdromTargetDeviceObject, Irp);
}
VOID
ChangerUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
Free all the allocated resources, etc.
Arguments:
DriverObject - pointer to a driver object.
Return Value:
VOID.
--*/
{
DebugPrint((1,
"ChangerUnload\n"));
return;
}
#if DBG
ULONG ChgrDebugLevel = 0;
UCHAR DebugBuffer[128];
#endif
#if DBG
VOID
ChgrDebugPrint(
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 <= ChgrDebugLevel) {
vsprintf(DebugBuffer, DebugMessage, ap);
DbgPrint(DebugBuffer);
}
va_end(ap);
} // end ChgrDebugPrint()
#else
//
// DebugPrint stub
//
VOID
ChgrDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
{
}
#endif