1102 lines
27 KiB
C
1102 lines
27 KiB
C
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Microsoft Windows
|
|||
|
//
|
|||
|
// Copyright (C) Microsoft Corporation, 1997 - 1999
|
|||
|
//
|
|||
|
// File: ioctl.c
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
#include "cdchgr.h"
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
InvalidElement(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN CHANGER_ELEMENT Element
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ChgrIoctl(
|
|||
|
IN ULONG Code
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
ULONG baseCode;
|
|||
|
|
|||
|
baseCode = Code >> 16;
|
|||
|
if (baseCode == IOCTL_CHANGER_BASE) {
|
|||
|
DebugPrint((3,
|
|||
|
"ChngrIoctl returning TRUE for Base %x, Code %x\n",
|
|||
|
baseCode,
|
|||
|
Code));
|
|||
|
|
|||
|
return TRUE;
|
|||
|
} else {
|
|||
|
DebugPrint((3,
|
|||
|
"ChngrIoctl returning FALSE for Base %x, Code %x\n",
|
|||
|
baseCode,
|
|||
|
Code));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrGetStatus(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PPASS_THROUGH_REQUEST passThrough;
|
|||
|
PSCSI_PASS_THROUGH srb;
|
|||
|
NTSTATUS status;
|
|||
|
ULONG length;
|
|||
|
PCDB cdb;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a request block.
|
|||
|
//
|
|||
|
|
|||
|
passThrough = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
|
|||
|
if (!passThrough) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
srb = &passThrough->Srb;
|
|||
|
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
cdb = (PCDB)srb->Cdb;
|
|||
|
|
|||
|
srb->CdbLength = CDB6GENERIC_LENGTH;
|
|||
|
|
|||
|
//
|
|||
|
// Build TUR.
|
|||
|
//
|
|||
|
|
|||
|
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
|||
|
srb->TimeOutValue = 20;
|
|||
|
|
|||
|
srb->DataTransferLength = 0;
|
|||
|
|
|||
|
if (deviceExtension->DeviceType == TORISAN) {
|
|||
|
DebugPrint((1,
|
|||
|
"GetStatus: Using CurrentPlatter %x\n",
|
|||
|
deviceExtension->CurrentPlatter));
|
|||
|
srb->Cdb[7] = (UCHAR)deviceExtension->CurrentPlatter;
|
|||
|
srb->CdbLength = 10;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send the request.
|
|||
|
//
|
|||
|
|
|||
|
status = SendPassThrough(DeviceObject,
|
|||
|
passThrough);
|
|||
|
|
|||
|
//
|
|||
|
// Check out the status. As this is fake (taking to the cdrom drive, not to a robotic target),
|
|||
|
// will probably have to make up some stuff.
|
|||
|
//
|
|||
|
|
|||
|
if (status == STATUS_NO_MEDIA_IN_DEVICE) {
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
if (deviceExtension->DeviceType == ATAPI_25) {
|
|||
|
|
|||
|
//
|
|||
|
// Issue mech. status to see if any changed bits are set for those
|
|||
|
// drives that actually support this.
|
|||
|
//
|
|||
|
|
|||
|
length = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER);
|
|||
|
length += (deviceExtension->NumberOfSlots) * sizeof(SLOT_TABLE_INFORMATION);
|
|||
|
|
|||
|
passThrough = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(PASS_THROUGH_REQUEST) + length);
|
|||
|
|
|||
|
if (!passThrough) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
//
|
|||
|
// Send SCSI command (CDB) to device
|
|||
|
//
|
|||
|
|
|||
|
status = SendPassThrough(DeviceObject,
|
|||
|
passThrough);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Run through slot info, looking for a set changed bit.
|
|||
|
//
|
|||
|
|
|||
|
PSLOT_TABLE_INFORMATION slotInfo;
|
|||
|
PMECHANICAL_STATUS_INFORMATION_HEADER statusHeader;
|
|||
|
ULONG slotCount;
|
|||
|
ULONG currentSlot;
|
|||
|
|
|||
|
(ULONG_PTR)statusHeader = (ULONG_PTR)passThrough->DataBuffer;
|
|||
|
(ULONG_PTR)slotInfo = (ULONG_PTR)statusHeader;
|
|||
|
(ULONG_PTR)slotInfo += sizeof(MECHANICAL_STATUS_INFORMATION_HEADER);
|
|||
|
|
|||
|
slotCount = statusHeader->SlotTableLength[1];
|
|||
|
slotCount |= (statusHeader->SlotTableLength[0] << 8);
|
|||
|
|
|||
|
//
|
|||
|
// Total slot information entries.
|
|||
|
//
|
|||
|
|
|||
|
slotCount /= sizeof(SLOT_TABLE_INFORMATION);
|
|||
|
|
|||
|
//
|
|||
|
// Move the slotInfo pointer to the correct entry.
|
|||
|
//
|
|||
|
|
|||
|
for (currentSlot = 0; currentSlot < slotCount; currentSlot++) {
|
|||
|
|
|||
|
if (slotInfo->DiscChanged) {
|
|||
|
status = STATUS_MEDIA_CHANGED;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Advance to next slot.
|
|||
|
//
|
|||
|
|
|||
|
slotInfo += 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrGetParameters(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PGET_CHANGER_PARAMETERS changerParameters;
|
|||
|
|
|||
|
changerParameters = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
RtlZeroMemory(changerParameters, sizeof(GET_CHANGER_PARAMETERS));
|
|||
|
|
|||
|
changerParameters->Size = sizeof(GET_CHANGER_PARAMETERS);
|
|||
|
|
|||
|
changerParameters->NumberTransportElements = 1;
|
|||
|
changerParameters->NumberStorageElements = (USHORT)deviceExtension->NumberOfSlots;
|
|||
|
changerParameters->NumberIEElements = 0;
|
|||
|
changerParameters->NumberDataTransferElements = 1;
|
|||
|
changerParameters->NumberOfDoors = 0;
|
|||
|
changerParameters->NumberCleanerSlots = 0;
|
|||
|
|
|||
|
changerParameters->FirstSlotNumber = 1;
|
|||
|
changerParameters->FirstDriveNumber = 0;
|
|||
|
changerParameters->FirstTransportNumber = 0;
|
|||
|
changerParameters->FirstIEPortNumber = 0;
|
|||
|
|
|||
|
if (deviceExtension->MechType == 1) {
|
|||
|
|
|||
|
//
|
|||
|
// For example, ALPS, Panasonic, Torisan.
|
|||
|
//
|
|||
|
|
|||
|
changerParameters->MagazineSize = (USHORT)deviceExtension->NumberOfSlots;
|
|||
|
|
|||
|
changerParameters->Features0 = (CHANGER_CARTRIDGE_MAGAZINE |
|
|||
|
CHANGER_STORAGE_SLOT |
|
|||
|
CHANGER_LOCK_UNLOCK);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// For the NEC.
|
|||
|
//
|
|||
|
|
|||
|
changerParameters->MagazineSize = 0;
|
|||
|
|
|||
|
changerParameters->Features0 = (CHANGER_STORAGE_SLOT |
|
|||
|
CHANGER_LOCK_UNLOCK);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
changerParameters->DriveCleanTimeout = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Features based on manual, nothing programatic.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
changerParameters->MoveFromSlot = CHANGER_TO_DRIVE | CHANGER_TO_TRANSPORT;
|
|||
|
|
|||
|
Irp->IoStatus.Information = sizeof(GET_CHANGER_PARAMETERS);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrGetProductData(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PCHANGER_PRODUCT_DATA productData = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
RtlZeroMemory(productData, sizeof(CHANGER_PRODUCT_DATA));
|
|||
|
|
|||
|
//
|
|||
|
// Copy cached inquiry data fields into the system buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlMoveMemory(productData->VendorId, deviceExtension->InquiryData.VendorId, VENDOR_ID_LENGTH);
|
|||
|
RtlMoveMemory(productData->ProductId, deviceExtension->InquiryData.ProductId, PRODUCT_ID_LENGTH);
|
|||
|
RtlMoveMemory(productData->Revision, deviceExtension->InquiryData.ProductRevisionLevel, REVISION_LENGTH);
|
|||
|
RtlMoveMemory(productData->SerialNumber, deviceExtension->InquiryData.VendorSpecific, SERIAL_NUMBER_LENGTH);
|
|||
|
productData->DeviceType = MEDIUM_CHANGER;
|
|||
|
|
|||
|
Irp->IoStatus.Information = sizeof(CHANGER_PRODUCT_DATA);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrSetAccess(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PCHANGER_SET_ACCESS setAccess = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
ULONG controlOperation = setAccess->Control;
|
|||
|
PPASS_THROUGH_REQUEST passThrough;
|
|||
|
PSCSI_PASS_THROUGH srb;
|
|||
|
NTSTATUS status;
|
|||
|
PCDB cdb;
|
|||
|
|
|||
|
|
|||
|
if (setAccess->Element.ElementType != ChangerDoor) {
|
|||
|
|
|||
|
//
|
|||
|
// No IEPORTs on these devices.
|
|||
|
//
|
|||
|
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a request block.
|
|||
|
//
|
|||
|
|
|||
|
passThrough = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
|
|||
|
if (!passThrough) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
srb = &passThrough->Srb;
|
|||
|
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
cdb = (PCDB)srb->Cdb;
|
|||
|
|
|||
|
srb->CdbLength = CDB6GENERIC_LENGTH;
|
|||
|
cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
|
|||
|
|
|||
|
srb->DataTransferLength = 0;
|
|||
|
srb->TimeOutValue = 10;
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
if (controlOperation == LOCK_ELEMENT) {
|
|||
|
|
|||
|
//
|
|||
|
// Issue prevent media removal command to lock the magazine.
|
|||
|
//
|
|||
|
|
|||
|
cdb->MEDIA_REMOVAL.Prevent = 1;
|
|||
|
|
|||
|
} else if (controlOperation == UNLOCK_ELEMENT) {
|
|||
|
|
|||
|
//
|
|||
|
// Issue allow media removal.
|
|||
|
//
|
|||
|
|
|||
|
cdb->MEDIA_REMOVAL.Prevent = 0;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Send the request.
|
|||
|
//
|
|||
|
|
|||
|
status = SendPassThrough(DeviceObject,
|
|||
|
passThrough);
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
Irp->IoStatus.Information = sizeof(CHANGER_SET_ACCESS);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrGetElementStatus(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrInitializeElementStatus(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrSetPosition(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
//
|
|||
|
// These device don't support this.
|
|||
|
//
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrExchangeMedium(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// These device don't support this.
|
|||
|
//
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrReinitializeUnit(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// These device don't support this.
|
|||
|
//
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrQueryVolumeTags(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// These device don't support this.
|
|||
|
//
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ChgrMoveMedium(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PCHANGER_MOVE_MEDIUM moveMedium = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
USHORT transport;
|
|||
|
USHORT source;
|
|||
|
USHORT destination;
|
|||
|
PPASS_THROUGH_REQUEST passThrough;
|
|||
|
PSCSI_PASS_THROUGH srb;
|
|||
|
PCDB cdb;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// Verify transport, source, and dest. are within range.
|
|||
|
//
|
|||
|
|
|||
|
if (InvalidElement(deviceExtension,moveMedium->Transport)) {
|
|||
|
DebugPrint((1,
|
|||
|
"ChangerMoveMedium: Transport element out of range.\n"));
|
|||
|
|
|||
|
return STATUS_ILLEGAL_ELEMENT_ADDRESS;
|
|||
|
}
|
|||
|
|
|||
|
if (InvalidElement(deviceExtension, moveMedium->Source)) {
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"ChangerMoveMedium: Source element out of range.\n"));
|
|||
|
|
|||
|
return STATUS_ILLEGAL_ELEMENT_ADDRESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (InvalidElement(deviceExtension,moveMedium->Destination)) {
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"ChangerMoveMedium: Destination element out of range.\n"));
|
|||
|
|
|||
|
return STATUS_ILLEGAL_ELEMENT_ADDRESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Build srb and cdb.
|
|||
|
//
|
|||
|
|
|||
|
passThrough = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
|
|||
|
if (!passThrough) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The torisan units don't really move medium, rather the active disc is changed.
|
|||
|
// To change slots, they've overloaded TUR.
|
|||
|
//
|
|||
|
|
|||
|
if (deviceExtension->DeviceType == TORISAN) {
|
|||
|
|
|||
|
if (moveMedium->Destination.ElementType == ChangerDrive) {
|
|||
|
|
|||
|
srb = &passThrough->Srb;
|
|||
|
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
cdb = (PCDB)srb->Cdb;
|
|||
|
|
|||
|
srb->CdbLength = CDB10GENERIC_LENGTH;
|
|||
|
|
|||
|
//
|
|||
|
// Build TUR.
|
|||
|
//
|
|||
|
|
|||
|
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
|||
|
|
|||
|
srb->Cdb[7] = (UCHAR)moveMedium->Source.ElementAddress;
|
|||
|
srb->TimeOutValue = 20;
|
|||
|
|
|||
|
srb->DataTransferLength = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Send the request.
|
|||
|
//
|
|||
|
|
|||
|
status = SendPassThrough(DeviceObject,
|
|||
|
passThrough);
|
|||
|
|
|||
|
if (status == STATUS_DEVICE_NOT_READY) {
|
|||
|
|
|||
|
// TODO send a TUR to verify this.
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"MoveMedium - Claiming success\n"));
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
} else if (status == STATUS_NO_MEDIA_IN_DEVICE) {
|
|||
|
status = STATUS_SOURCE_ELEMENT_EMPTY;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Update the current disc indicator.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->CurrentPlatter = moveMedium->Source.ElementAddress;
|
|||
|
DebugPrint((1,
|
|||
|
"MoveMedium: Set currentPlatter to %x\n",
|
|||
|
deviceExtension->CurrentPlatter));
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
DebugPrint((1,
|
|||
|
"MoveMedium - Status on move %lx\n",
|
|||
|
status));
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Claim that is happened.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If destination is the drive, determine if media is already present.
|
|||
|
// The alps always claims media is there, so don't check.
|
|||
|
//
|
|||
|
|
|||
|
#if 0
|
|||
|
if (((moveMedium->Destination.ElementType) == ChangerDrive) &&
|
|||
|
(deviceExtension->DeviceType != ALPS_25)) {
|
|||
|
|
|||
|
srb = &passThrough->Srb;
|
|||
|
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
cdb = (PCDB)srb->Cdb;
|
|||
|
|
|||
|
srb->CdbLength = CDB6GENERIC_LENGTH;
|
|||
|
|
|||
|
//
|
|||
|
// Build TUR.
|
|||
|
//
|
|||
|
|
|||
|
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
|||
|
srb->TimeOutValue = 20;
|
|||
|
|
|||
|
srb->DataTransferLength = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Send the request.
|
|||
|
//
|
|||
|
|
|||
|
status = SendPassThrough(DeviceObject,
|
|||
|
passThrough);
|
|||
|
|
|||
|
if (status != STATUS_NO_MEDIA_IN_DEVICE) {
|
|||
|
|
|||
|
//
|
|||
|
// Drive has media. Though the device will allow this,
|
|||
|
// error it, as the expected medium changer behaviour is
|
|||
|
// to return element full in this case.
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"ChgrMoveMedium: Drive already has media. TUR Status %lx\n",
|
|||
|
status));
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
return STATUS_DESTINATION_ELEMENT_FULL;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
srb = &passThrough->Srb;
|
|||
|
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
cdb = (PCDB)srb->Cdb;
|
|||
|
|
|||
|
srb->CdbLength = CDB12GENERIC_LENGTH;
|
|||
|
srb->TimeOutValue = CDCHGR_TIMEOUT;
|
|||
|
srb->DataTransferLength = 0;
|
|||
|
|
|||
|
//
|
|||
|
// LOAD_UNLOAD will move a disc from slot to drive,
|
|||
|
// or from drive to slot.
|
|||
|
//
|
|||
|
|
|||
|
cdb->LOAD_UNLOAD.OperationCode = SCSIOP_LOAD_UNLOAD_SLOT;
|
|||
|
if (moveMedium->Source.ElementType == ChangerDrive) {
|
|||
|
|
|||
|
cdb->LOAD_UNLOAD.Slot = (UCHAR)moveMedium->Destination.ElementAddress;
|
|||
|
cdb->LOAD_UNLOAD.Start = 0;
|
|||
|
cdb->LOAD_UNLOAD.LoadEject = 1;
|
|||
|
|
|||
|
|
|||
|
} else if (moveMedium->Source.ElementType == ChangerSlot) {
|
|||
|
|
|||
|
cdb->LOAD_UNLOAD.Slot = (UCHAR)moveMedium->Source.ElementAddress;
|
|||
|
cdb->LOAD_UNLOAD.Start = 1;
|
|||
|
cdb->LOAD_UNLOAD.LoadEject = 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send SCSI command (CDB) to device
|
|||
|
//
|
|||
|
|
|||
|
status = SendPassThrough(DeviceObject,
|
|||
|
passThrough);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// These devices don't seem to ever generate
|
|||
|
// a unit attention, for media changed, so fake it.
|
|||
|
//
|
|||
|
|
|||
|
if (deviceExtension->CdromTargetDeviceObject->Vpb->Flags & VPB_MOUNTED) {
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"Faking DO_VERIFY_VOLUME\n"));
|
|||
|
|
|||
|
deviceExtension->CdromTargetDeviceObject->Flags |= DO_VERIFY_VOLUME;
|
|||
|
}
|
|||
|
|
|||
|
} else if (status == STATUS_NO_MEDIA_IN_DEVICE) {
|
|||
|
status = STATUS_SOURCE_ELEMENT_EMPTY;
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
InvalidElement(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN CHANGER_ELEMENT Element
|
|||
|
)
|
|||
|
{
|
|||
|
if (Element.ElementType == ChangerSlot) {
|
|||
|
if (Element.ElementAddress >= DeviceExtension->NumberOfSlots) {
|
|||
|
DebugPrint((1,
|
|||
|
"Cdchgr: InvalidElement - type %x, address %x\n",
|
|||
|
Element.ElementType,
|
|||
|
Element.ElementAddress));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
} else if (Element.ElementType == ChangerDrive) {
|
|||
|
if (Element.ElementAddress != 0) {
|
|||
|
DebugPrint((1,
|
|||
|
"Cdchgr: InvalidElement - type %x, address %x\n",
|
|||
|
Element.ElementType,
|
|||
|
Element.ElementAddress));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
} else if (Element.ElementType == ChangerTransport) {
|
|||
|
if (Element.ElementAddress != 0) {
|
|||
|
DebugPrint((1,
|
|||
|
"Cdchgr: InvalidElement - type %x, address %x\n",
|
|||
|
Element.ElementType,
|
|||
|
Element.ElementAddress));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"Cdchgr: InvalidElement - type %x, address %x\n",
|
|||
|
Element.ElementType,
|
|||
|
Element.ElementAddress));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Acceptable element/address.
|
|||
|
//
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MapSenseInfo(
|
|||
|
IN PSENSE_DATA SenseBuffer
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
UCHAR senseCode = SenseBuffer->SenseKey;
|
|||
|
UCHAR additionalSenseCode = SenseBuffer->AdditionalSenseCode;
|
|||
|
UCHAR additionalSenseCodeQualifier = SenseBuffer->AdditionalSenseCodeQualifier;
|
|||
|
|
|||
|
switch (senseCode) {
|
|||
|
case SCSI_SENSE_NO_SENSE:
|
|||
|
|
|||
|
if (SenseBuffer->IncorrectLength) {
|
|||
|
|
|||
|
status = STATUS_INVALID_BLOCK_LENGTH;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
status = STATUS_IO_DEVICE_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_SENSE_RECOVERED_ERROR:
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_SENSE_NOT_READY:
|
|||
|
|
|||
|
status = STATUS_DEVICE_NOT_READY;
|
|||
|
|
|||
|
switch (additionalSenseCode) {
|
|||
|
case SCSI_ADSENSE_LUN_NOT_READY:
|
|||
|
|
|||
|
switch (additionalSenseCodeQualifier) {
|
|||
|
|
|||
|
case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
|
|||
|
|
|||
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|||
|
break;
|
|||
|
case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
|
|||
|
case SCSI_SENSEQ_BECOMING_READY:
|
|||
|
|
|||
|
//
|
|||
|
// Fall through.
|
|||
|
//
|
|||
|
default:
|
|||
|
|
|||
|
status = STATUS_DEVICE_NOT_READY;
|
|||
|
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE:
|
|||
|
|
|||
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|||
|
break;
|
|||
|
default:
|
|||
|
status = STATUS_DEVICE_NOT_READY;
|
|||
|
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_SENSE_MEDIUM_ERROR:
|
|||
|
|
|||
|
status = STATUS_DEVICE_DATA_ERROR;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_SENSE_ILLEGAL_REQUEST:
|
|||
|
|
|||
|
switch (additionalSenseCode) {
|
|||
|
|
|||
|
case SCSI_ADSENSE_ILLEGAL_BLOCK:
|
|||
|
status = STATUS_NONEXISTENT_SECTOR;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_ADSENSE_INVALID_LUN:
|
|||
|
status = STATUS_NO_SUCH_DEVICE;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_ADSENSE_MUSIC_AREA:
|
|||
|
case SCSI_ADSENSE_DATA_AREA:
|
|||
|
case SCSI_ADSENSE_VOLUME_OVERFLOW:
|
|||
|
case SCSI_ADSENSE_ILLEGAL_COMMAND:
|
|||
|
case SCSI_ADSENSE_INVALID_CDB:
|
|||
|
default:
|
|||
|
|
|||
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|||
|
|
|||
|
// TODO - check on this.
|
|||
|
DebugPrint((1,
|
|||
|
"MapSenseInfo: UnitAttention \n"));
|
|||
|
|
|||
|
status = STATUS_VERIFY_REQUIRED;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_SENSE_DATA_PROTECT:
|
|||
|
|
|||
|
status = STATUS_MEDIA_WRITE_PROTECTED;
|
|||
|
break;
|
|||
|
|
|||
|
case SCSI_SENSE_HARDWARE_ERROR:
|
|||
|
case SCSI_SENSE_ABORTED_COMMAND:
|
|||
|
|
|||
|
//
|
|||
|
// Fall through.
|
|||
|
//
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
status = STATUS_IO_DEVICE_ERROR;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"CdChgr: MapSenseInfo - SK %x, ASC %x, ASCQ %x, Status %lx\n",
|
|||
|
senseCode,
|
|||
|
additionalSenseCode,
|
|||
|
additionalSenseCodeQualifier,
|
|||
|
status));
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SendTorisanCheckVerify(
|
|||
|
PDEVICE_OBJECT DeviceObject,
|
|||
|
PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine handles only the check verify commands for the Sanyo changers.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject
|
|||
|
Irp
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PPASS_THROUGH_REQUEST passThrough;
|
|||
|
PSCSI_PASS_THROUGH srb;
|
|||
|
NTSTATUS status;
|
|||
|
ULONG length;
|
|||
|
PCDB cdb;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a request block.
|
|||
|
//
|
|||
|
|
|||
|
passThrough = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
|
|||
|
if (!passThrough) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
srb = &passThrough->Srb;
|
|||
|
RtlZeroMemory(passThrough, sizeof(PASS_THROUGH_REQUEST));
|
|||
|
cdb = (PCDB)srb->Cdb;
|
|||
|
|
|||
|
srb->CdbLength = CDB10GENERIC_LENGTH;
|
|||
|
|
|||
|
//
|
|||
|
// Build TUR.
|
|||
|
//
|
|||
|
|
|||
|
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
|||
|
srb->TimeOutValue = 20;
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"SendTorisanCheckVerify: Using CurrentPlatter of %x\n",
|
|||
|
deviceExtension->CurrentPlatter));
|
|||
|
|
|||
|
srb->Cdb[7] = (UCHAR)deviceExtension->CurrentPlatter;
|
|||
|
srb->DataTransferLength = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Send the request.
|
|||
|
//
|
|||
|
|
|||
|
status = SendPassThrough(DeviceObject,
|
|||
|
passThrough);
|
|||
|
|
|||
|
|
|||
|
ExFreePool(passThrough);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SendPassThrough(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PPASS_THROUGH_REQUEST ScsiPassThrough
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine fills in most SPT fields, then sends the given SRB synchronously
|
|||
|
to the CDROM class driver.
|
|||
|
DataTransferLength, TimeoutValue are the responsibility of the caller.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Srb - Supplies the SRB.
|
|||
|
|
|||
|
Buffer - Supplies the return buffer.
|
|||
|
|
|||
|
BufferLength - Supplies the buffer length.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
//typedef struct _PASS_THROUGH_REQUEST {
|
|||
|
// SCSI_PASS_THROUGH Srb;
|
|||
|
// SENSE_DATA SenseInfoBuffer;
|
|||
|
// CHAR DataBuffer[0];
|
|||
|
//} PASS_THROUGH_REQUEST, *PPASS_THROUGH_REQUEST;
|
|||
|
|
|||
|
|
|||
|
//typedef struct _SCSI_PASS_THROUGH {
|
|||
|
// USHORT Length;
|
|||
|
// UCHAR ScsiStatus;
|
|||
|
// UCHAR PathId;
|
|||
|
// UCHAR TargetId;
|
|||
|
// UCHAR Lun;
|
|||
|
// UCHAR CdbLength;
|
|||
|
// UCHAR SenseInfoLength;
|
|||
|
// UCHAR DataIn;
|
|||
|
// ULONG DataTransferLength;
|
|||
|
// ULONG TimeOutValue;
|
|||
|
// ULONG DataBufferOffset;
|
|||
|
// ULONG SenseInfoOffset;
|
|||
|
// UCHAR Cdb[16];
|
|||
|
//}SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PSCSI_PASS_THROUGH srb = &ScsiPassThrough->Srb;
|
|||
|
KEVENT event;
|
|||
|
PIRP irp;
|
|||
|
IO_STATUS_BLOCK ioStatus;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
srb->Length = sizeof(SCSI_PASS_THROUGH);
|
|||
|
srb->SenseInfoLength = sizeof(SENSE_DATA);
|
|||
|
srb->SenseInfoOffset = FIELD_OFFSET(PASS_THROUGH_REQUEST, SenseInfoBuffer);
|
|||
|
|
|||
|
if (srb->DataTransferLength) {
|
|||
|
|
|||
|
srb->DataBufferOffset = FIELD_OFFSET(PASS_THROUGH_REQUEST, DataBuffer);
|
|||
|
srb->DataIn = SCSI_IOCTL_DATA_IN;
|
|||
|
} else {
|
|||
|
|
|||
|
srb->DataIn = SCSI_IOCTL_DATA_OUT;
|
|||
|
srb->DataBufferOffset = 0;
|
|||
|
}
|
|||
|
|
|||
|
KeInitializeEvent(&event,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
|
|||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_PASS_THROUGH,
|
|||
|
deviceExtension->CdromTargetDeviceObject,
|
|||
|
ScsiPassThrough,
|
|||
|
sizeof(PASS_THROUGH_REQUEST) + srb->DataTransferLength,
|
|||
|
ScsiPassThrough,
|
|||
|
sizeof(PASS_THROUGH_REQUEST) + srb->DataTransferLength,
|
|||
|
FALSE,
|
|||
|
&event,
|
|||
|
&ioStatus);
|
|||
|
if (!irp) {
|
|||
|
DebugPrint((1,
|
|||
|
"Cdchgr: SendPassThrough NULL irp\n"));
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
status = IoCallDriver(deviceExtension->CdromTargetDeviceObject,
|
|||
|
irp);
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|||
|
status = ioStatus.Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check status and map appropriately.
|
|||
|
//
|
|||
|
|
|||
|
if (srb->ScsiStatus != SCSISTAT_GOOD) {
|
|||
|
|
|||
|
if (srb->ScsiStatus == SCSISTAT_CHECK_CONDITION) {
|
|||
|
|
|||
|
status = MapSenseInfo(&ScsiPassThrough->SenseInfoBuffer);
|
|||
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|||
|
|
|||
|
if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
|
|||
|
|
|||
|
DeviceObject->Flags |= DO_VERIFY_VOLUME;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"Cdchgr: Unhandled scsi status %lx\n",
|
|||
|
srb->ScsiStatus));
|
|||
|
status = STATUS_IO_DEVICE_ERROR;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"Cdchgr: SendSrbPassThrough Status %lx\n",
|
|||
|
status));
|
|||
|
|
|||
|
return status;
|
|||
|
}
|