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