/drivers/storage/classpnp/class.c
C | 9183 lines | 6777 code | 933 blank | 1473 comment | 409 complexity | 57fb997264080c066297aa10d46bb03d MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-3.0, CC-BY-SA-3.0, AGPL-3.0, GPL-3.0, CPL-1.0
- /*++
- Copyright (C) Microsoft Corporation, 1991 - 1999
- Module Name:
- class.c
- Abstract:
- SCSI class driver routines
- Environment:
- kernel mode only
- Notes:
- Revision History:
- --*/
- #define CLASS_INIT_GUID 1
- #include "classp.h"
- #include "debug.h"
- #ifdef ALLOC_PRAGMA
- #pragma alloc_text(INIT, DriverEntry)
- #pragma alloc_text(PAGE, ClassAddDevice)
- #pragma alloc_text(PAGE, ClassClaimDevice)
- #pragma alloc_text(PAGE, ClassCreateDeviceObject)
- #pragma alloc_text(PAGE, ClassDispatchPnp)
- #pragma alloc_text(PAGE, ClassGetDescriptor)
- #pragma alloc_text(PAGE, ClassGetPdoId)
- #pragma alloc_text(PAGE, ClassInitialize)
- #pragma alloc_text(PAGE, ClassInitializeEx)
- #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
- #pragma alloc_text(PAGE, ClassMarkChildMissing)
- #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
- #pragma alloc_text(PAGE, ClassModeSense)
- #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
- #pragma alloc_text(PAGE, ClassPnpStartDevice)
- #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
- #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
- #pragma alloc_text(PAGE, ClassRemoveDevice)
- #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
- #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
- #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
- #pragma alloc_text(PAGE, ClassUnload)
- #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
- #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
- #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
- #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
- #pragma alloc_text(PAGE, ClasspScanForClassHacks)
- #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
- #endif
- ULONG ClassPnpAllowUnload = TRUE;
- #define FirstDriveLetter 'C'
- #define LastDriveLetter 'Z'
- /*++////////////////////////////////////////////////////////////////////////////
- DriverEntry()
- Routine Description:
- Temporary entry point needed to initialize the class system dll.
- It doesn't do anything.
- Arguments:
- DriverObject - Pointer to the driver object created by the system.
- Return Value:
- STATUS_SUCCESS
- --*/
- NTSTATUS
- NTAPI
- DriverEntry(
- IN PDRIVER_OBJECT DriverObject,
- IN PUNICODE_STRING RegistryPath
- )
- {
- return STATUS_SUCCESS;
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClassInitialize()
- Routine Description:
- This routine is called by a class driver during its
- DriverEntry routine to initialize the driver.
- Arguments:
- Argument1 - Driver Object.
- Argument2 - Registry Path.
- InitializationData - Device-specific driver's initialization data.
- Return Value:
- A valid return code for a DriverEntry routine.
- --*/
- ULONG
- ClassInitialize(
- IN PVOID Argument1,
- IN PVOID Argument2,
- IN PCLASS_INIT_DATA InitializationData
- )
- {
- PDRIVER_OBJECT DriverObject = Argument1;
- PUNICODE_STRING RegistryPath = Argument2;
- PCLASS_DRIVER_EXTENSION driverExtension;
- NTSTATUS status;
- PAGED_CODE();
-
- DebugPrint((3,"\n\nSCSI Class Driver\n"));
- ClasspInitializeDebugGlobals();
- //
- // Validate the length of this structure. This is effectively a
- // version check.
- //
- if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
- //
- // This DebugPrint is to help third-party driver writers
- //
- DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
- return (ULONG) STATUS_REVISION_MISMATCH;
- }
- //
- // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
- // are not required entry points.
- //
- if ((!InitializationData->FdoData.ClassDeviceControl) ||
- (!((InitializationData->FdoData.ClassReadWriteVerification) ||
- (InitializationData->ClassStartIo))) ||
- (!InitializationData->ClassAddDevice) ||
- (!InitializationData->FdoData.ClassStartDevice)) {
- //
- // This DebugPrint is to help third-party driver writers
- //
- DebugPrint((0,
- "ClassInitialize: Class device-specific driver missing required "
- "FDO entry\n"));
- return (ULONG) STATUS_REVISION_MISMATCH;
- }
- if ((InitializationData->ClassEnumerateDevice) &&
- ((!InitializationData->PdoData.ClassDeviceControl) ||
- (!InitializationData->PdoData.ClassStartDevice) ||
- (!((InitializationData->PdoData.ClassReadWriteVerification) ||
- (InitializationData->ClassStartIo))))) {
- //
- // This DebugPrint is to help third-party driver writers
- //
- DebugPrint((0, "ClassInitialize: Class device-specific missing "
- "required PDO entry\n"));
- return (ULONG) STATUS_REVISION_MISMATCH;
- }
- if((InitializationData->FdoData.ClassStopDevice == NULL) ||
- ((InitializationData->ClassEnumerateDevice != NULL) &&
- (InitializationData->PdoData.ClassStopDevice == NULL))) {
- //
- // This DebugPrint is to help third-party driver writers
- //
- DebugPrint((0, "ClassInitialize: Class device-specific missing "
- "required PDO entry\n"));
- ASSERT(FALSE);
- return (ULONG) STATUS_REVISION_MISMATCH;
- }
- //
- // Setup the default power handlers if the class driver didn't provide
- // any.
- //
- if(InitializationData->FdoData.ClassPowerDevice == NULL) {
- InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
- }
- if((InitializationData->ClassEnumerateDevice != NULL) &&
- (InitializationData->PdoData.ClassPowerDevice == NULL)) {
- InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
- }
- //
- // warn that unload is not supported
- //
- // ISSUE-2000/02/03-peterwie
- // We should think about making this a fatal error.
- //
- if(InitializationData->ClassUnload == NULL) {
- //
- // This DebugPrint is to help third-party driver writers
- //
- DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
- RegistryPath));
- }
- //
- // Create an extension for the driver object
- //
- status = IoAllocateDriverObjectExtension(DriverObject,
- CLASS_DRIVER_EXTENSION_KEY,
- sizeof(CLASS_DRIVER_EXTENSION),
- &driverExtension);
- if(NT_SUCCESS(status)) {
- //
- // Copy the registry path into the driver extension so we can use it later
- //
- driverExtension->RegistryPath.Length = RegistryPath->Length;
- driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
- driverExtension->RegistryPath.Buffer =
- ExAllocatePoolWithTag(PagedPool,
- RegistryPath->MaximumLength,
- '1CcS');
- if(driverExtension->RegistryPath.Buffer == NULL) {
- status = STATUS_INSUFFICIENT_RESOURCES;
- return status;
- }
- RtlCopyUnicodeString(
- &(driverExtension->RegistryPath),
- RegistryPath);
- //
- // Copy the initialization data into the driver extension so we can reuse
- // it during our add device routine
- //
- RtlCopyMemory(
- &(driverExtension->InitData),
- InitializationData,
- sizeof(CLASS_INIT_DATA));
- driverExtension->DeviceCount = 0;
- } else if (status == STATUS_OBJECT_NAME_COLLISION) {
- //
- // The extension already exists - get a pointer to it
- //
- driverExtension = IoGetDriverObjectExtension(DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- ASSERT(driverExtension != NULL);
- } else {
- DebugPrint((1, "ClassInitialize: Class driver extension could not be "
- "allocated %lx\n", status));
- return status;
- }
- //
- // Update driver object with entry points.
- //
- DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreateClose;
- DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassCreateClose;
- DriverObject->MajorFunction[IRP_MJ_READ] = ClassReadWrite;
- DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassReadWrite;
- DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassInternalIoControl;
- DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch;
- DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassShutdownFlush;
- DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush;
- DriverObject->MajorFunction[IRP_MJ_PNP] = ClassDispatchPnp;
- DriverObject->MajorFunction[IRP_MJ_POWER] = ClassDispatchPower;
- DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl;
- if (InitializationData->ClassStartIo) {
- DriverObject->DriverStartIo = ClasspStartIo;
- }
- if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) {
- DriverObject->DriverUnload = ClassUnload;
- } else {
- DriverObject->DriverUnload = NULL;
- }
- DriverObject->DriverExtension->AddDevice = ClassAddDevice;
- DbgPrint("Driver is ready to go\n");
- status = STATUS_SUCCESS;
- return status;
- } // end ClassInitialize()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassInitializeEx()
- Routine Description:
- This routine is allows the caller to do any extra initialization or
- setup that is not done in ClassInitialize. The operation is
- controlled by the GUID that is passed and the contents of the Data
- parameter is dependent upon the GUID.
- This is the list of supported operations:
- Guid - GUID_CLASSPNP_QUERY_REGINFOEX
- Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
- Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
- callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
- former callback allows the driver to specify the name of the
- mof resource.
- Arguments:
- DriverObject
- Guid
- Data
- Return Value:
- Status Code
- --*/
- ULONG
- ClassInitializeEx(
- IN PDRIVER_OBJECT DriverObject,
- IN LPGUID Guid,
- IN PVOID Data
- )
- {
- PCLASS_DRIVER_EXTENSION driverExtension;
- NTSTATUS status;
- PAGED_CODE();
- driverExtension = IoGetDriverObjectExtension( DriverObject,
- CLASS_DRIVER_EXTENSION_KEY
- );
- if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
- {
- PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
- //
- // Indicate the device supports PCLASS_QUERY_REGINFO_EX
- // callback instead of PCLASS_QUERY_REGINFO callback.
- //
- List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
- if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
- {
- driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
- driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
- status = STATUS_SUCCESS;
- } else {
- status = STATUS_INVALID_PARAMETER;
- }
- } else {
- status = STATUS_NOT_SUPPORTED;
- }
- return(status);
- } // end ClassInitializeEx()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassUnload()
- Routine Description:
- called when there are no more references to the driver. this allows
- drivers to be updated without rebooting.
- Arguments:
- DriverObject - a pointer to the driver object that is being unloaded
- Status:
- --*/
- VOID
- ClassUnload(
- IN PDRIVER_OBJECT DriverObject
- )
- {
- PCLASS_DRIVER_EXTENSION driverExtension;
- NTSTATUS status;
- PAGED_CODE();
- ASSERT( DriverObject->DeviceObject == NULL );
- driverExtension = IoGetDriverObjectExtension( DriverObject,
- CLASS_DRIVER_EXTENSION_KEY
- );
- ASSERT(driverExtension != NULL);
- ASSERT(driverExtension->RegistryPath.Buffer != NULL);
- ASSERT(driverExtension->InitData.ClassUnload != NULL);
- DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
- &driverExtension->RegistryPath));
- //
- // attempt to process the driver's unload routine first.
- //
- driverExtension->InitData.ClassUnload(DriverObject);
- //
- // free own allocated resources and return
- //
- ExFreePool( driverExtension->RegistryPath.Buffer );
- driverExtension->RegistryPath.Buffer = NULL;
- driverExtension->RegistryPath.Length = 0;
- driverExtension->RegistryPath.MaximumLength = 0;
- return;
- } // end ClassUnload()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassAddDevice()
- Routine Description:
- SCSI class driver add device routine. This is called by pnp when a new
- physical device come into being.
- This routine will call out to the class driver to verify that it should
- own this device then will create and attach a device object and then hand
- it to the driver to initialize and create symbolic links
- Arguments:
- DriverObject - a pointer to the driver object that this is being created for
- PhysicalDeviceObject - a pointer to the physical device object
- Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
- STATUS_SUCCESS if the creation and attachment was successful
- status of device creation and initialization
- --*/
- NTSTATUS
- ClassAddDevice(
- IN PDRIVER_OBJECT DriverObject,
- IN PDEVICE_OBJECT PhysicalDeviceObject
- )
- {
- PCLASS_DRIVER_EXTENSION driverExtension =
- IoGetDriverObjectExtension(DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- NTSTATUS status;
- PAGED_CODE();
- DbgPrint("got a device\n");
- status = driverExtension->InitData.ClassAddDevice(DriverObject,
- PhysicalDeviceObject);
- return status;
- } // end ClassAddDevice()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassDispatchPnp()
- Routine Description:
- Storage class driver pnp routine. This is called by the io system when
- a PNP request is sent to the device.
- Arguments:
- DeviceObject - pointer to the device object
- Irp - pointer to the io request packet
- Return Value:
- status
- --*/
- NTSTATUS
- ClassDispatchPnp(
- IN PDEVICE_OBJECT DeviceObject,
- IN PIRP Irp
- )
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- BOOLEAN isFdo = commonExtension->IsFdo;
- PCLASS_DRIVER_EXTENSION driverExtension;
- PCLASS_INIT_DATA initData;
- PCLASS_DEV_INFO devInfo;
- PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
- PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
- NTSTATUS status = Irp->IoStatus.Status;
- BOOLEAN completeRequest = TRUE;
- BOOLEAN lockReleased = FALSE;
- ULONG isRemoved;
- PAGED_CODE();
- //
- // Extract all the useful information out of the driver object
- // extension
- //
- driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- if (driverExtension){
- initData = &(driverExtension->InitData);
- if(isFdo) {
- devInfo = &(initData->FdoData);
- } else {
- devInfo = &(initData->PdoData);
- }
- isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
- DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
- DeviceObject, Irp,
- irpStack->MinorFunction,
- isFdo ? "fdo" : "pdo",
- DeviceObject));
- DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
- DeviceObject, Irp,
- commonExtension->PreviousState,
- commonExtension->CurrentState));
- switch(irpStack->MinorFunction) {
- case IRP_MN_START_DEVICE: {
- //
- // if this is sent to the FDO we should forward it down the
- // attachment chain before we start the FDO.
- //
- if (isFdo) {
- status = ClassForwardIrpSynchronous(commonExtension, Irp);
- }
- else {
- status = STATUS_SUCCESS;
- }
- if (NT_SUCCESS(status)){
- status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
- }
- break;
- }
- case IRP_MN_QUERY_DEVICE_RELATIONS: {
- DEVICE_RELATION_TYPE type =
- irpStack->Parameters.QueryDeviceRelations.Type;
- PDEVICE_RELATIONS deviceRelations = NULL;
- if(!isFdo) {
- if(type == TargetDeviceRelation) {
- //
- // Device relations has one entry built in to it's size.
- //
- status = STATUS_INSUFFICIENT_RESOURCES;
- deviceRelations = ExAllocatePoolWithTag(PagedPool,
- sizeof(DEVICE_RELATIONS),
- '2CcS');
- if(deviceRelations != NULL) {
- RtlZeroMemory(deviceRelations,
- sizeof(DEVICE_RELATIONS));
- Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
- deviceRelations->Count = 1;
- deviceRelations->Objects[0] = DeviceObject;
- ObReferenceObject(deviceRelations->Objects[0]);
- status = STATUS_SUCCESS;
- }
- } else {
- //
- // PDO's just complete enumeration requests without altering
- // the status.
- //
- status = Irp->IoStatus.Status;
- }
- break;
- } else if (type == BusRelations) {
- ASSERT(commonExtension->IsInitialized);
- //
- // Make sure we support enumeration
- //
- if(initData->ClassEnumerateDevice == NULL) {
- //
- // Just send the request down to the lower driver. Perhaps
- // It can enumerate children.
- //
- } else {
- //
- // Re-enumerate the device
- //
- status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
- if(!NT_SUCCESS(status)) {
- completeRequest = TRUE;
- break;
- }
- }
- }
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- completeRequest = FALSE;
- break;
- }
- case IRP_MN_QUERY_ID: {
- BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
- UNICODE_STRING unicodeString;
- if(isFdo) {
- //
- // FDO's should just forward the query down to the lower
- // device objects
- //
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- completeRequest = FALSE;
- break;
- }
- //
- // PDO's need to give an answer - this is easy for now
- //
- RtlInitUnicodeString(&unicodeString, NULL);
- status = ClassGetPdoId(DeviceObject,
- idType,
- &unicodeString);
- if(status == STATUS_NOT_IMPLEMENTED) {
- //
- // The driver doesn't implement this ID (whatever it is).
- // Use the status out of the IRP so that we don't mangle a
- // response from someone else.
- //
- status = Irp->IoStatus.Status;
- } else if(NT_SUCCESS(status)) {
- Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
- } else {
- Irp->IoStatus.Information = (ULONG_PTR) NULL;
- }
- break;
- }
- case IRP_MN_QUERY_STOP_DEVICE:
- case IRP_MN_QUERY_REMOVE_DEVICE: {
- DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
- DeviceObject, Irp,
- ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
- "STOP" : "REMOVE")));
- //
- // If this device is in use for some reason (paging, etc...)
- // then we need to fail the request.
- //
- if(commonExtension->PagingPathCount != 0) {
- DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
- "path and cannot be removed\n",
- DeviceObject, Irp));
- status = STATUS_DEVICE_BUSY;
- break;
- }
- //
- // Check with the class driver to see if the query operation
- // can succeed.
- //
- if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
- status = devInfo->ClassStopDevice(DeviceObject,
- irpStack->MinorFunction);
- } else {
- status = devInfo->ClassRemoveDevice(DeviceObject,
- irpStack->MinorFunction);
- }
- if(NT_SUCCESS(status)) {
- //
- // ASSERT that we never get two queries in a row, as
- // this will severly mess up the state machine
- //
- ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
- commonExtension->PreviousState = commonExtension->CurrentState;
- commonExtension->CurrentState = irpStack->MinorFunction;
- if(isFdo) {
- DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
- "%s irp\n", DeviceObject, Irp,
- ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
- "STOP" : "REMOVE")));
- status = ClassForwardIrpSynchronous(commonExtension, Irp);
- }
- }
- DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
- DeviceObject, Irp, status));
- break;
- }
- case IRP_MN_CANCEL_STOP_DEVICE:
- case IRP_MN_CANCEL_REMOVE_DEVICE: {
- //
- // Check with the class driver to see if the query or cancel
- // operation can succeed.
- //
- if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
- status = devInfo->ClassStopDevice(DeviceObject,
- irpStack->MinorFunction);
- ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
- "never be failed\n", NT_SUCCESS(status));
- } else {
- status = devInfo->ClassRemoveDevice(DeviceObject,
- irpStack->MinorFunction);
- ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
- "never be failed\n", NT_SUCCESS(status));
- }
- Irp->IoStatus.Status = status;
- //
- // We got a CANCEL - roll back to the previous state only
- // if the current state is the respective QUERY state.
- //
- if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
- (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
- ) ||
- ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
- (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
- )
- ) {
- commonExtension->CurrentState =
- commonExtension->PreviousState;
- commonExtension->PreviousState = 0xff;
- }
- if(isFdo) {
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- completeRequest = FALSE;
- } else {
- status = STATUS_SUCCESS;
- }
- break;
- }
- case IRP_MN_STOP_DEVICE: {
- //
- // These all mean nothing to the class driver currently. The
- // port driver will handle all queueing when necessary.
- //
- DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
- DeviceObject, Irp,
- (isFdo ? "fdo" : "pdo")
- ));
- ASSERT(commonExtension->PagingPathCount == 0);
- //
- // ISSUE-2000/02/03-peterwie
- // if we stop the timer here then it means no class driver can
- // do i/o in its ClassStopDevice routine. This is because the
- // retry (among other things) is tied into the tick handler
- // and disabling retries could cause the class driver to deadlock.
- // Currently no class driver we're aware of issues i/o in its
- // Stop routine but this is a case we may want to defend ourself
- // against.
- //
- if (DeviceObject->Timer) {
- IoStopTimer(DeviceObject);
- }
- status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
- ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
- "never be failed\n", NT_SUCCESS(status));
- if(isFdo) {
- status = ClassForwardIrpSynchronous(commonExtension, Irp);
- }
- if(NT_SUCCESS(status)) {
- commonExtension->CurrentState = irpStack->MinorFunction;
- commonExtension->PreviousState = 0xff;
- }
- break;
- }
- case IRP_MN_REMOVE_DEVICE:
- case IRP_MN_SURPRISE_REMOVAL: {
- PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
- UCHAR removeType = irpStack->MinorFunction;
- if (commonExtension->PagingPathCount != 0) {
- DBGTRACE(ClassDebugWarning, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
- }
- //
- // Release the lock for this IRP before calling in.
- //
- ClassReleaseRemoveLock(DeviceObject, Irp);
- lockReleased = TRUE;
- /*
- * If a timer was started on the device, stop it.
- */
- if (DeviceObject->Timer) {
- IoStopTimer(DeviceObject);
- }
- /*
- * "Fire-and-forget" the remove irp to the lower stack.
- * Don't touch the irp (or the irp stack!) after this.
- */
- if (isFdo) {
- IoCopyCurrentIrpStackLocationToNext(Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- ASSERT(NT_SUCCESS(status));
- completeRequest = FALSE;
- }
- else {
- status = STATUS_SUCCESS;
- }
- /*
- * Do our own cleanup and call the class driver's remove
- * cleanup routine.
- * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
- * so don't touch the extension after this.
- */
- commonExtension->PreviousState = commonExtension->CurrentState;
- commonExtension->CurrentState = removeType;
- ClassRemoveDevice(DeviceObject, removeType);
- break;
- }
- case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
- switch(irpStack->Parameters.UsageNotification.Type) {
- case DeviceUsageTypePaging: {
- BOOLEAN setPagable;
- if((irpStack->Parameters.UsageNotification.InPath) &&
- (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
- //
- // Device isn't started. Don't allow adding a
- // paging file, but allow a removal of one.
- //
- status = STATUS_DEVICE_NOT_READY;
- break;
- }
- ASSERT(commonExtension->IsInitialized);
- //
- // need to synchronize this now...
- //
- KeEnterCriticalRegion();
- status = KeWaitForSingleObject(&commonExtension->PathCountEvent,
- Executive, KernelMode,
- FALSE, NULL);
- ASSERT(NT_SUCCESS(status));
- status = STATUS_SUCCESS;
- //
- // If the volume is removable we should try to lock it in
- // place or unlock it once per paging path count
- //
- if (commonExtension->IsFdo){
- status = ClasspEjectionControl(
- DeviceObject,
- Irp,
- InternalMediaLock,
- (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
- }
- if (!NT_SUCCESS(status)){
- KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
- KeLeaveCriticalRegion();
- break;
- }
- //
- // 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 &&
- commonExtension->PagingPathCount == 1
- ) {
- //
- // removing last paging file
- // must have DO_POWER_PAGABLE bits set, but only
- // if noone set the DO_POWER_INRUSH bit
- //
- if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
- DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
- "paging file removed, but "
- "DO_POWER_INRUSH was set, so NOT "
- "setting DO_POWER_PAGABLE\n",
- DeviceObject, Irp));
- } else {
- DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
- "paging file removed, "
- "setting DO_POWER_PAGABLE\n",
- DeviceObject, Irp));
- SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
- setPagable = TRUE;
- }
- }
- //
- // forward the irp before finishing handling the
- // special cases
- //
- status = ClassForwardIrpSynchronous(commonExtension, Irp);
- //
- // 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(
- &commonExtension->PagingPathCount,
- irpStack->Parameters.UsageNotification.InPath);
- if (irpStack->Parameters.UsageNotification.InPath) {
- if (commonExtension->PagingPathCount == 1) {
- DebugPrint((2, "ClassDispatchPnp (%p,%p): "
- "Clearing PAGABLE bit\n",
- DeviceObject, Irp));
- CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
- }
- }
- } else {
- //
- // cleanup the changes done above
- //
- if (setPagable == TRUE) {
- DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
- "PAGABLE bit due to irp failure\n",
- DeviceObject, Irp));
- CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
- setPagable = FALSE;
- }
- //
- // relock or unlock the media if needed.
- //
- if (commonExtension->IsFdo) {
- ClasspEjectionControl(
- DeviceObject,
- Irp,
- InternalMediaLock,
- (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
- }
- }
- //
- // set the event so the next one can occur.
- //
- KeSetEvent(&commonExtension->PathCountEvent,
- IO_NO_INCREMENT, FALSE);
- KeLeaveCriticalRegion();
- break;
- }
- case DeviceUsageTypeHibernation: {
- IoAdjustPagingPathCount(
- &commonExtension->HibernationPathCount,
- irpStack->Parameters.UsageNotification.InPath
- );
- status = ClassForwardIrpSynchronous(commonExtension, Irp);
- if (!NT_SUCCESS(status)) {
- IoAdjustPagingPathCount(
- &commonExtension->HibernationPathCount,
- !irpStack->Parameters.UsageNotification.InPath
- );
- }
- break;
- }
- case DeviceUsageTypeDumpFile: {
- IoAdjustPagingPathCount(
- &commonExtension->DumpPathCount,
- irpStack->Parameters.UsageNotification.InPath
- );
- status = ClassForwardIrpSynchronous(commonExtension, Irp);
- if (!NT_SUCCESS(status)) {
- IoAdjustPagingPathCount(
- &commonExtension->DumpPathCount,
- !irpStack->Parameters.UsageNotification.InPath
- );
- }
- break;
- }
- default: {
- status = STATUS_INVALID_PARAMETER;
- break;
- }
- }
- break;
- }
- case IRP_MN_QUERY_CAPABILITIES: {
- DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
- DeviceObject, Irp));
- if(!isFdo) {
- status = ClassQueryPnpCapabilities(
- DeviceObject,
- irpStack->Parameters.DeviceCapabilities.Capabilities
- );
- break;
- } else {
- PDEVICE_CAPABILITIES deviceCapabilities;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
- PCLASS_PRIVATE_FDO_DATA fdoData;
- fdoExtension = DeviceObject->DeviceExtension;
- fdoData = fdoExtension->PrivateFdoData;
- deviceCapabilities =
- irpStack->Parameters.DeviceCapabilities.Capabilities;
- //
- // forward the irp before handling the special cases
- //
- status = ClassForwardIrpSynchronous(commonExtension, Irp);
- if (!NT_SUCCESS(status)) {
- break;
- }
- //
- // we generally want to remove the device from the hotplug
- // applet, which requires the SR-OK bit to be set.
- // only when the user specifies that they are capable of
- // safely removing things do we want to clear this bit
- // (saved in WriteCacheEnableOverride)
- //
- // setting of this bit is done either above, or by the
- // lower driver.
- //
- // note: may not be started, so check we have FDO data first.
- //
- if (fdoData &&
- fdoData->HotplugInfo.WriteCacheEnableOverride) {
- if (deviceCapabilities->SurpriseRemovalOK) {
- DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
- "device capabilities due to hotplug "
- "device or media\n"));
- }
- deviceCapabilities->SurpriseRemovalOK = FALSE;
- }
- break;
- } // end QUERY_CAPABILITIES for FDOs
- ASSERT(FALSE);
- break;
- } // end QUERY_CAPABILITIES
- default: {
- if (isFdo){
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- completeRequest = FALSE;
- }
- break;
- }
- }
- }
- else {
- ASSERT(driverExtension);
- status = STATUS_INTERNAL_ERROR;
- }
- if (completeRequest){
- Irp->IoStatus.Status = status;
- if (!lockReleased){
- ClassReleaseRemoveLock(DeviceObject, Irp);
- }
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
- }
- else {
- /*
- * The irp is already completed so don't touch it.
- * This may be a remove so don't touch the device extension.
- */
- DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
- }
- return status;
- } // end ClassDispatchPnp()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassPnpStartDevice()
- Routine Description:
- Storage class driver routine for IRP_MN_START_DEVICE requests.
- This routine kicks off any device specific initialization
- Arguments:
- DeviceObject - a pointer to the device object
- Irp - a pointer to the io request packet
- Return Value:
- none
- --*/
- NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
- {
- PCLASS_DRIVER_EXTENSION driverExtension;
- PCLASS_INIT_DATA initData;
- PCLASS_DEV_INFO devInfo;
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
- BOOLEAN isFdo = commonExtension->IsFdo;
- BOOLEAN isMountedDevice = TRUE;
- UNICODE_STRING interfaceName;
- BOOLEAN timerStarted;
- NTSTATUS status = STATUS_SUCCESS;
- PAGED_CODE();
- driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- initData = &(driverExtension->InitData);
- if(isFdo) {
- devInfo = &(initData->FdoData);
- } else {
- devInfo = &(initData->PdoData);
- }
- ASSERT(devInfo->ClassInitDevice != NULL);
- ASSERT(devInfo->ClassStartDevice != NULL);
- if (!commonExtension->IsInitialized){
- //
- // perform FDO/PDO specific initialization
- //
- if (isFdo){
- STORAGE_PROPERTY_ID propertyId;
- //
- // allocate a private extension for class data
- //
- if (fdoExtension->PrivateFdoData == NULL) {
- fdoExtension->PrivateFdoData =
- ExAllocatePoolWithTag(NonPagedPool,
- sizeof(CLASS_PRIVATE_FDO_DATA),
- CLASS_TAG_PRIVATE_DATA
- );
- }
- if (fdoExtension->PrivateFdoData == NULL) {
- DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
- "private fdo data\n"));
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- //
- // initialize the struct's various fields.
- //
- RtlZeroMemory(fdoExtension->PrivateFdoData,
- sizeof(CLASS_PRIVATE_FDO_DATA)
- );
- KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
- KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
- ClasspRetryRequestDpc,
- DeviceObject);
- KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
- fdoExtension->PrivateFdoData->Retry.Granularity =
- KeQueryTimeIncrement();
- commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
- //
- // NOTE: the old interface allowed the class driver to allocate
- // this. this was unsafe for low-memory conditions. allocate one
- // unconditionally now, and modify our internal functions to use
- // our own exclusively as it is the only safe way to do this.
- //
- status = ClasspAllocateReleaseQueueIrp(fdoExtension);
- if (!NT_SUCCESS(status)) {
- DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
- "private release queue irp\n"));
- return status;
- }
- //
- // Call port driver to get adapter capabilities.
- //
- propertyId = StorageAdapterProperty;
- status = ClassGetDescriptor(
- commonExtension->LowerDeviceObject,
- &propertyId,
- &fdoExtension->AdapterDescriptor);
- if(!NT_SUCCESS(status)) {
- //
- // This DebugPrint is to help third-party driver writers
- //
- DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
- "[ADAPTER] failed %lx\n", status));
- return status;
- }
- //
- // Call port driver to get device descriptor.
- //
- propertyId = StorageDeviceProperty;
- status = ClassGetDescriptor(
- commonExtension->LowerDeviceObject,
- &propertyId,
- &fdoExtension->DeviceDescriptor);
- if(!NT_SUCCESS(status)) {
- //
- // This DebugPrint is to help third-party driver writers
- //
- DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
- "[DEVICE] failed %lx\n", status));
- return status;
- }
- ClasspScanForSpecialInRegistry(fdoExtension);
- ClassScanForSpecial(fdoExtension,
- ClassBadItems,
- ClasspScanForClassHacks);
- //
- // allow perf to be re-enabled after a given number of failed IOs
- // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
- //
- {
- ULONG t = 0;
- ClassGetDeviceParameter(fdoExtension,
- CLASSP_REG_SUBKEY_NAME,
- CLASSP_REG_PERF_RESTORE_VALUE_NAME,
- &t);
- if (t >= CLASS_PERF_RESTORE_MINIMUM) {
- fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
- }
- }
- //
- // compatibility comes first. writable cd media will not
- // get a SYNCH_CACHE on power down.
- //
- if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
- SET_FLAG(fdoExtension->PrivateFdoData->HackFlags,
- FDO_HACK_NO_SYNC_CACHE);
- }
- //
- // initialize the hotplug information only after the ScanForSpecial
- // routines, as it relies upon the hack flags.
- //
- status = ClasspInitializeHotplugInfo(fdoExtension);
- if (!NT_SUCCESS(status)) {
- DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
- "hotplug information %lx\n", status));
- return status;
- }
- /*
- * Allocate/initialize TRANSFER_PACKETs and related resources.
- */
- status = InitializeTransferPackets(DeviceObject);
- }
- //
- // ISSUE - drivers need to disable write caching on the media
- // if hotplug and !useroverride. perhaps we should
- // allow registration of a callback to enable/disable
- // write cache instead.
- //
- if (NT_SUCCESS(status)){
- status = devInfo->ClassInitDevice(DeviceObject);
- }
- }
- if (!NT_SUCCESS(status)){
- //
- // Just bail out - the remove that comes down will clean up the
- // initialized scraps.
- //
- return status;
- } else {
- commonExtension->IsInitialized = TRUE;
- if (commonExtension->IsFdo) {
- fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
- }
- }
- //
- // If device requests autorun functionality or a once a second callback
- // then enable the once per second timer.
- //
- // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
- // called in the context of the ClassInitDevice callback. If called
- // after then this check will have already been made and the
- // once a second timer will not have been enabled.
- //
- if ((isFdo) &&
- ((initData->ClassTick != NULL) ||
- (fdoExtension->MediaChangeDetectionInfo != NULL) ||
- ((fdoExtension->FailurePredictionInfo != NULL) &&
- (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
- {
- ClasspEnableTimer(DeviceObject);
- timerStarted = TRUE;
- } else {
- timerStarted = FALSE;
- }
- //
- // NOTE: the timer looks at commonExtension->CurrentState now
- // to prevent Media Change Notification code from running
- // until the device is started, but allows the device
- // specific tick handler to run. therefore it is imperative
- // that commonExtension->CurrentState not be updated until
- // the device specific startdevice handler has finished.
- //
- status = devInfo->ClassStartDevice(DeviceObject);
- if(NT_SUCCESS(status)) {
- commonExtension->CurrentState = IRP_MN_START_DEVICE;
- if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
- isMountedDevice = FALSE;
- }
- if((DeviceObject->DeviceType != FILE_DEVICE_DISK) &&
- (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM)) {
- isMountedDevice = FALSE;
- }
- if(isMountedDevice) {
- ClasspRegisterMountedDeviceInterface(DeviceObject);
- }
- if((commonExtension->IsFdo) &&
- (devInfo->ClassWmiInfo.GuidRegInfo != NULL)) {
- IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
- }
- } else {
- if (timerStarted) {
- ClasspDisableTimer(DeviceObject);
- }
- }
- return status;
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClassReadWrite()
- Routine Description:
- This is the system entry point for read and write requests. The
- device-specific handler is invoked to perform any validation necessary.
- If the device object is a PDO (partition object) then the request will
- simply be adjusted for Partition0 and issued to the lower device driver.
- IF the device object is an FDO (paritition 0 object), the number of bytes
- in the request are checked against the maximum byte counts that the adapter
- supports and requests are broken up into
- smaller sizes if necessary.
- Arguments:
- DeviceObject - a pointer to the device object for this request
- Irp - IO request
- Return Value:
- NT Status
- --*/
- NTSTATUS ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
- PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
- LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
- ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
- ULONG isRemoved;
- NTSTATUS status;
- /*
- * Grab the remove lock. If we can't acquire it, bail out.
- */
- isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
- if (isRemoved) {
- Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_DEVICE_DOES_NOT_EXIST;
- }
- else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
- (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
- !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
- /*
- * DO_VERIFY_VOLUME is set for the device object,
- * but this request is not itself a verify request.
- * So fail this request.
- */
- IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
- Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
- Irp->IoStatus.Information = 0;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, 0);
- status = STATUS_VERIFY_REQUIRED;
- }
- else {
- /*
- * Since we've bypassed the verify-required tests we don't need to repeat
- * them with this IRP - in particular we don't want to worry about
- * hitting them at the partition 0 level if the request has come through
- * a non-zero partition.
- */
- currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
- /*
- * Call the miniport driver's pre-pass filter to check if we
- * should continue with this transfer.
- */
- ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
- status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
- if (!NT_SUCCESS(status)){
- ASSERT(Irp->IoStatus.Status == status);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
- }
- else if (status == STATUS_PENDING){
- /*
- * ClassReadWriteVerification queued this request.
- * So don't touch the irp anymore.
- */
- }
- else {
- if (transferByteCount == 0) {
- /*
- * Several parts of the code turn 0 into 0xffffffff,
- * so don't process a zero-length request any further.
- */
- Irp->IoStatus.Status = STATUS_SUCCESS;
- Irp->IoStatus.Information = 0;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_SUCCESS;
- }
- else {
- /*
- * If the driver has its own StartIo routine, call it.
- */
- if (commonExtension->DriverExtension->InitData.ClassStartIo) {
- IoMarkIrpPending(Irp);
- IoStartPacket(DeviceObject, Irp, NULL, NULL);
- status = STATUS_PENDING;
- }
- else {
- /*
- * The driver does not have its own StartIo routine.
- * So process this request ourselves.
- */
- /*
- * Add partition byte offset to make starting byte relative to
- * beginning of disk.
- */
- currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
- commonExtension->StartingOffset.QuadPart;
- if (commonExtension->IsFdo){
- /*
- * Add in any skew for the disk manager software.
- */
- currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
- commonExtension->PartitionZeroExtension->DMByteSkew;
- /*
- * Perform the actual transfer(s) on the hardware
- * to service this request.
- */
- ServiceTransferRequest(DeviceObject, Irp);
- status = STATUS_PENDING;
- }
- else {
- /*
- * This is a child PDO enumerated for our FDO by e.g. disk.sys
- * and owned by e.g. partmgr. Send it down to the next device
- * and the same irp will come back to us for the FDO.
- */
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(lowerDeviceObject, Irp);
- }
- }
- }
- }
- }
- return status;
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClassReadDriveCapacity()
- Routine Description:
- This routine sends a READ CAPACITY to the requested device, updates
- the geometry information in the device object and returns
- when it is complete. This routine is synchronous.
- This routine must be called with the remove lock held or some other
- assurance that the Fdo will not be removed while processing.
- Arguments:
- DeviceObject - Supplies a pointer to the device object that represents
- the device whose capacity is to be read.
- Return Value:
- Status is returned.
- --*/
- NTSTATUS ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo)
- {
- READ_CAPACITY_DATA readCapacityBuffer = {0};
- NTSTATUS status;
- PMDL driveCapMdl;
- driveCapMdl = BuildDeviceInputMdl(&readCapacityBuffer, sizeof(READ_CAPACITY_DATA));
- if (driveCapMdl){
- TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
- if (pkt){
- PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
- KEVENT event;
- NTSTATUS pktStatus;
- IRP pseudoIrp = {0};
- /*
- * Our engine needs an "original irp" to write the status back to
- * and to count down packets (one in this case).
- * Just use a pretend irp for this.
- */
- pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
- pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
- pseudoIrp.IoStatus.Information = 0;
- pseudoIrp.MdlAddress = driveCapMdl;
- /*
- * Set this up as a SYNCHRONOUS transfer, submit it,
- * and wait for the packet to complete. The result
- * status will be written to the original irp.
- */
- KeInitializeEvent(&event, SynchronizationEvent, FALSE);
- SetupDriveCapacityTransferPacket( pkt,
- &readCapacityBuffer,
- sizeof(READ_CAPACITY_DATA),
- &event,
- &pseudoIrp);
- SubmitTransferPacket(pkt);
- KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
- status = pseudoIrp.IoStatus.Status;
- /*
- * If we got an UNDERRUN, retry exactly once.
- * (The transfer_packet engine didn't retry because the result
- * status was success).
- */
- if (NT_SUCCESS(status) &&
- (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA))){
- DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG)pseudoIrp.IoStatus.Information, sizeof(READ_CAPACITY_DATA)));
- pkt = DequeueFreeTransferPacket(Fdo, TRUE);
- if (pkt){
- pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
- pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
- pseudoIrp.IoStatus.Information = 0;
- KeInitializeEvent(&event, SynchronizationEvent, FALSE);
- SetupDriveCapacityTransferPacket( pkt,
- &readCapacityBuffer,
- sizeof(READ_CAPACITY_DATA),
- &event,
- &pseudoIrp);
- SubmitTransferPacket(pkt);
- KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
- status = pseudoIrp.IoStatus.Status;
- if (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA)){
- status = STATUS_DEVICE_BUSY;
- }
- }
- else {
- status = STATUS_INSUFFICIENT_RESOURCES;
- }
- }
- if (NT_SUCCESS(status)){
- /*
- * The request succeeded.
- * Read out and store the drive information.
- */
- ULONG cylinderSize;
- ULONG bytesPerSector;
- ULONG tmp;
- ULONG lastSector;
- /*
- * Read the bytesPerSector value,
- * which is big-endian in the returned buffer.
- * Default to the standard 512 bytes.
- */
- tmp = readCapacityBuffer.BytesPerBlock;
- ((PFOUR_BYTE)&bytesPerSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
- ((PFOUR_BYTE)&bytesPerSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
- ((PFOUR_BYTE)&bytesPerSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
- ((PFOUR_BYTE)&bytesPerSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
- if (bytesPerSector == 0) {
- bytesPerSector = 512;
- }
- else {
- /*
- * Clear all but the highest set bit.
- * That will give us a bytesPerSector value that is a power of 2.
- */
- while (bytesPerSector & (bytesPerSector-1)) {
- bytesPerSector &= bytesPerSector-1;
- }
- }
- fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
- //
- // Copy last sector in reverse byte order.
- //
- tmp = readCapacityBuffer.LogicalBlockAddress;
- ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
- ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
- ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
- ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
- //
- // Calculate sector to byte shift.
- //
- WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
- DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
- fdoExt->DiskGeometry.BytesPerSector));
- DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
- lastSector + 1));
- if (fdoExt->DMActive){
- DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
- fdoExt->DMSkew));
- lastSector -= fdoExt->DMSkew;
- }
- /*
- * Check to see if we have a geometry we should be using already.
- */
- cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
- fdoExt->DiskGeometry.SectorsPerTrack);
- if (cylinderSize == 0){
- DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
- "values from %#x/%#x to %#x/%#x\n",
- fdoExt->DiskGeometry.TracksPerCylinder,
- fdoExt->DiskGeometry.SectorsPerTrack,
- 0xff,
- 0x3f));
- fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
- fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
- cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
- fdoExt->DiskGeometry.SectorsPerTrack);
- }
- //
- // Calculate number of cylinders.
- //
- fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/cylinderSize);
- //
- // if there are zero cylinders, then the device lied AND it's
- // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
- // this can fit into a single LONGLONG, so create another usable
- // geometry, even if it's unusual looking. This allows small,
- // non-standard devices, such as Sony's Memory Stick, to show
- // up as having a partition.
- //
- if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
- fdoExt->DiskGeometry.SectorsPerTrack = 1;
- fdoExt->DiskGeometry.TracksPerCylinder = 1;
- fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector;
- }
- //
- // Calculate media capacity in bytes.
- //
- fdoExt->CommonExtension.PartitionLength.QuadPart =
- ((LONGLONG)(lastSector + 1)) << fdoExt->SectorShift;
- /*
- * Is this removable or fixed media
- */
- if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
- fdoExt->DiskGeometry.MediaType = RemovableMedia;
- }
- else {
- fdoExt->DiskGeometry.MediaType = FixedMedia;
- }
- }
- else {
- /*
- * The request failed.
- */
- //
- // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
- // what happens when the disk's sector size is bigger than
- // 512 bytes and we hit this code path? this is untested.
- //
- // If the read capacity fails, set the geometry to reasonable parameter
- // so things don't fail at unexpected places. Zero the geometry
- // except for the bytes per sector and sector shift.
- //
- /*
- * This request can sometimes fail legitimately
- * (e.g. when a SCSI device is attached but turned off)
- * so this is not necessarily a device/driver bug.
- */
- DBGTRACE(ClassDebugWarning, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo, status));
- /*
- * Write in a default disk geometry which we HOPE is right (??).
- * BUGBUG !!
- */
- RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
- fdoExt->DiskGeometry.BytesPerSector = 512;
- fdoExt->SectorShift = 9;
- fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
- /*
- * Is this removable or fixed media
- */
- if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
- fdoExt->DiskGeometry.MediaType = RemovableMedia;
- }
- else {
- fdoExt->DiskGeometry.MediaType = FixedMedia;
- }
- }
- }
- else {
- status = STATUS_INSUFFICIENT_RESOURCES;
- }
- FreeDeviceInputMdl(driveCapMdl);
- }
- else {
- status = STATUS_INSUFFICIENT_RESOURCES;
- }
- return status;
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClassSendStartUnit()
- Routine Description:
- Send command to SCSI unit to start or power up.
- Because this command is issued asynchronounsly, that is, without
- waiting on it to complete, the IMMEDIATE flag is not set. This
- means that the CDB will not return until the drive has powered up.
- This should keep subsequent requests from being submitted to the
- device before it has completely spun up.
- This routine is called from the InterpretSense routine, when a
- request sense returns data indicating that a drive must be
- powered up.
- This routine may also be called from a class driver's error handler,
- or anytime a non-critical start device should be sent to the device.
- Arguments:
- Fdo - The functional device object for the stopped device.
- Return Value:
- None.
- --*/
- VOID
- ClassSendStartUnit(
- IN PDEVICE_OBJECT Fdo
- )
- {
- PIO_STACK_LOCATION irpStack;
- PIRP irp;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PSCSI_REQUEST_BLOCK srb;
- PCOMPLETION_CONTEXT context;
- PCDB cdb;
- //
- // Allocate Srb from nonpaged pool.
- //
- context = ExAllocatePoolWithTag(NonPagedPool,
- sizeof(COMPLETION_CONTEXT),
- '6CcS');
- if(context == NULL) {
- //
- // ISSUE-2000/02/03-peterwie
- // This code path was inheritted from the NT 4.0 class2.sys driver.
- // It needs to be changed to survive low-memory conditions.
- //
- KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
- }
- //
- // Save the device object in the context for use by the completion
- // routine.
- //
- context->DeviceObject = Fdo;
- srb = &context->Srb;
- //
- // Zero out srb.
- //
- RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
- //
- // Write length to SRB.
- //
- srb->Length = sizeof(SCSI_REQUEST_BLOCK);
- srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
- //
- // Set timeout value large enough for drive to spin up.
- //
- srb->TimeOutValue = START_UNIT_TIMEOUT;
- //
- // Set the transfer length.
- //
- srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
- SRB_FLAGS_DISABLE_AUTOSENSE |
- SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
- //
- // Build the start unit CDB.
- //
- srb->CdbLength = 6;
- cdb = (PCDB)srb->Cdb;
- cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
- cdb->START_STOP.Start = 1;
- cdb->START_STOP.Immediate = 0;
- cdb->START_STOP.LogicalUnitNumber = srb->Lun;
- //
- // Build the asynchronous request to be sent to the port driver.
- // Since this routine is called from a DPC the IRP should always be
- // available.
- //
- irp = IoAllocateIrp(Fdo->StackSize, FALSE);
- if(irp == NULL) {
- //
- // ISSUE-2000/02/03-peterwie
- // This code path was inheritted from the NT 4.0 class2.sys driver.
- // It needs to be changed to survive low-memory conditions.
- //
- KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
- }
- ClassAcquireRemoveLock(Fdo, irp);
- IoSetCompletionRoutine(irp,
- (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
- context,
- TRUE,
- TRUE,
- TRUE);
- irpStack = IoGetNextIrpStackLocation(irp);
- irpStack->MajorFunction = IRP_MJ_SCSI;
- srb->OriginalRequest = irp;
- //
- // Store the SRB address in next stack for port driver.
- //
- irpStack->Parameters.Scsi.Srb = srb;
- //
- // Call the port driver with the IRP.
- //
- IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
- return;
- } // end StartUnit()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
- Routine Description:
- This routine is called when an asynchronous I/O request
- which was issused by the class driver completes. Examples of such requests
- are release queue or START UNIT. This routine releases the queue if
- necessary. It then frees the context and the IRP.
- Arguments:
- DeviceObject - The device object for the logical unit; however since this
- is the top stack location the value is NULL.
- Irp - Supplies a pointer to the Irp to be processed.
- Context - Supplies the context to be used to process this request.
- Return Value:
- None.
- --*/
- NTSTATUS
- ClassAsynchronousCompletion(
- PDEVICE_OBJECT DeviceObject,
- PIRP Irp,
- PVOID Context
- )
- {
- PCOMPLETION_CONTEXT context = Context;
- PSCSI_REQUEST_BLOCK srb;
- if(DeviceObject == NULL) {
- DeviceObject = context->DeviceObject;
- }
- srb = &context->Srb;
- //
- // If this is an execute srb, then check the return status and make sure.
- // the queue is not frozen.
- //
- if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
- //
- // Check for a frozen queue.
- //
- if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
- //
- // Unfreeze the queue getting the device object from the context.
- //
- ClassReleaseQueue(context->DeviceObject);
- }
- }
- { // free port-allocated sense buffer if we can detect
- if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
- if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
- FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
- }
- } else {
- ASSERT(!TEST_FLAG(srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
- }
- }
- //
- // Free the context and the Irp.
- //
- if (Irp->MdlAddress != NULL) {
- MmUnlockPages(Irp->MdlAddress);
- IoFreeMdl(Irp->MdlAddress);
- Irp->MdlAddress = NULL;
- }
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ExFreePool(context);
- IoFreeIrp(Irp);
- //
- // Indicate the I/O system should stop processing the Irp completion.
- //
- return STATUS_MORE_PROCESSING_REQUIRED;
- } // end ClassAsynchronousCompletion()
- VOID ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp)
- {
- PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
- PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
- PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
- PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
- ULONG entireXferLen = currentSp->Parameters.Read.Length;
- PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
- LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
- PTRANSFER_PACKET pkt;
- SINGLE_LIST_ENTRY pktList;
- PSINGLE_LIST_ENTRY slistEntry;
- ULONG numPackets;
- KIRQL oldIrql;
- ULONG i;
- /*
- * Compute the number of hw xfers we'll have to do.
- * Calculate this without allowing for an overflow condition.
- */
- ASSERT(fdoData->HwMaxXferLen >= PAGE_SIZE);
- numPackets = entireXferLen/fdoData->HwMaxXferLen;
- if (entireXferLen % fdoData->HwMaxXferLen){
- numPackets++;
- }
- /*
- * First get all the TRANSFER_PACKETs that we'll need at once.
- * Use our 'simple' slist functions since we don't need interlocked.
- */
- SimpleInitSlistHdr(&pktList);
- for (i = 0; i < numPackets; i++){
- pkt = DequeueFreeTransferPacket(Fdo, TRUE);
- if (pkt){
- SimplePushSlist(&pktList, &pkt->SlistEntry);
- }
- else {
- break;
- }
- }
- if (i == numPackets){
- /*
- * Initialize the original IRP's status to success.
- * If any of the packets fail, they will set it to an error status.
- * The IoStatus.Information field will be incremented to the
- * transfer length as the pieces complete.
- */
- Irp->IoStatus.Status = STATUS_SUCCESS;
- Irp->IoStatus.Information = 0;
- /*
- * Store the number of transfer pieces inside the original IRP.
- * It will be used to count down the pieces as they complete.
- */
- Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
- /*
- * We are proceeding with the transfer.
- * Mark the client IRP pending since it may complete on a different thread.
- */
- IoMarkIrpPending(Irp);
- /*
- * Transmit the pieces of the transfer.
- */
- while (entireXferLen > 0){
- ULONG thisPieceLen = MIN(fdoData->HwMaxXferLen, entireXferLen);
- /*
- * Set up a TRANSFER_PACKET for this piece and send it.
- */
- slistEntry = SimplePopSlist(&pktList);
- ASSERT(slistEntry);
- pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
- SetupReadWriteTransferPacket( pkt,
- bufPtr,
- thisPieceLen,
- targetLocation,
- Irp);
- SubmitTransferPacket(pkt);
- entireXferLen -= thisPieceLen;
- bufPtr += thisPieceLen;
- targetLocation.QuadPart += thisPieceLen;
- }
- ASSERT(SimpleIsSlistEmpty(&pktList));
- }
- else if (i >= 1){
- /*
- * We were unable to get all the TRANSFER_PACKETs we need,
- * but we did get at least one.
- * That means that we are in extreme low-memory stress.
- * We'll try doing this transfer using a single packet.
- * The port driver is certainly also in stress, so use one-page
- * transfers.
- */
- /*
- * Free all but one of the TRANSFER_PACKETs.
- */
- while (i-- > 1){
- slistEntry = SimplePopSlist(&pktList);
- ASSERT(slistEntry);
- pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
- EnqueueFreeTransferPacket(Fdo, pkt);
- }
- /*
- * Get the single TRANSFER_PACKET that we'll be using.
- */
- slistEntry = SimplePopSlist(&pktList);
- ASSERT(slistEntry);
- ASSERT(SimpleIsSlistEmpty(&pktList));
- pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
- DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt));
- /*
- * Set default status and the number of transfer packets (one)
- * inside the original irp.
- */
- Irp->IoStatus.Status = STATUS_SUCCESS;
- Irp->IoStatus.Information = 0;
- Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
- /*
- * Mark the client irp pending since it may complete on
- * another thread.
- */
- IoMarkIrpPending(Irp);
- /*
- * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
- */
- SetupReadWriteTransferPacket( pkt,
- bufPtr,
- entireXferLen,
- targetLocation,
- Irp);
- InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
- StepLowMemRetry(pkt);
- }
- else {
- /*
- * We were unable to get ANY TRANSFER_PACKETs.
- * Defer this client irp until some TRANSFER_PACKETs free up.
- */
- DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp));
- IoMarkIrpPending(Irp);
- EnqueueDeferredClientIrp(fdoData, Irp);
- }
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClassIoComplete()
- Routine Description:
- This routine executes when the port driver has completed a request.
- It looks at the SRB status in the completing SRB and if not success
- it checks for valid request sense buffer information. If valid, the
- info is used to update status with more precise message of type of
- error. This routine deallocates the SRB.
- This routine should only be placed on the stack location for a class
- driver FDO.
- Arguments:
- Fdo - Supplies the device object which represents the logical
- unit.
- Irp - Supplies the Irp which has completed.
- Context - Supplies a pointer to the SRB.
- Return Value:
- NT status
- --*/
- NTSTATUS
- ClassIoComplete(
- IN PDEVICE_OBJECT Fdo,
- IN PIRP Irp,
- IN PVOID Context
- )
- {
- PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
- PSCSI_REQUEST_BLOCK srb = Context;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
- NTSTATUS status;
- BOOLEAN retry;
- BOOLEAN callStartNextPacket;
- ASSERT(fdoExtension->CommonExtension.IsFdo);
- //
- // Check SRB status for success of completing request.
- //
- if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
- ULONG retryInterval;
- DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
- //
- // Release the queue if it is frozen.
- //
- if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
- ClassReleaseQueue(Fdo);
- }
- retry = ClassInterpretSenseInfo(
- Fdo,
- srb,
- irpStack->MajorFunction,
- irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
- irpStack->Parameters.DeviceIoControl.IoControlCode :
- 0,
- MAXIMUM_RETRIES -
- ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
- &status,
- &retryInterval);
- //
- // If the status is verified required and the this request
- // should bypass verify required then retry the request.
- //
- if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
- status == STATUS_VERIFY_REQUIRED) {
- status = STATUS_IO_DEVICE_ERROR;
- retry = TRUE;
- }
- if (retry && ((*(PCHAR*)&irpStack->Parameters.Others.Argument4)--)) {
- //
- // Retry request.
- //
- DebugPrint((1, "Retry request %p\n", Irp));
- if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
- FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
- }
- RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
- return STATUS_MORE_PROCESSING_REQUIRED;
- }
- } else {
- //
- // Set status for successful request
- //
- fdoData->LoggedTURFailureSinceLastIO = FALSE;
- ClasspPerfIncrementSuccessfulIo(fdoExtension);
- status = STATUS_SUCCESS;
- } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
- //
- // ensure we have returned some info, and it matches what the
- // original request wanted for PAGING operations only
- //
- if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
- ASSERT(Irp->IoStatus.Information != 0);
- ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
- }
- //
- // remember if the caller wanted to skip calling IoStartNextPacket.
- // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
- // calls. this setting only affects device objects with StartIo routines.
- //
- callStartNextPacket = !TEST_FLAG(srb->SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
- if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
- callStartNextPacket = FALSE;
- }
- //
- // Free the srb
- //
- if(!TEST_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
- if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
- FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
- }
- if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
- ClassFreeOrReuseSrb(fdoExtension, srb);
- }
- else {
- DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
- ExFreePool(srb);
- }
- } else {
- DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
- "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
- if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
- DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
- " because SRB_CLASS_FLAGS_PERSISTANT set\n",
- srb->SenseInfoBuffer));
- }
- }
- //
- // Set status in completing IRP.
- //
- Irp->IoStatus.Status = status;
- //
- // Set the hard error if necessary.
- //
- if (!NT_SUCCESS(status) &&
- IoIsErrorUserInduced(status) &&
- (Irp->Tail.Overlay.Thread != NULL)
- ) {
- //
- // Store DeviceObject for filesystem, and clear
- // in IoStatus.Information field.
- //
- IoSetHardErrorOrVerifyDevice(Irp, Fdo);
- Irp->IoStatus.Information = 0;
- }
- //
- // If pending has be returned for this irp then mark the current stack as
- // pending.
- //
- if (Irp->PendingReturned) {
- IoMarkIrpPending(Irp);
- }
- if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
- if (callStartNextPacket) {
- KIRQL oldIrql;
- KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
- IoStartNextPacket(Fdo, FALSE);
- KeLowerIrql(oldIrql);
- }
- }
- ClassReleaseRemoveLock(Fdo, Irp);
- return status;
- } // end ClassIoComplete()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassSendSrbSynchronous()
- Routine Description:
- This routine is called by SCSI device controls to complete an
- SRB and send it to the port driver synchronously (ie wait for
- completion). The CDB is already completed along with the SRB CDB
- size and request timeout value.
- Arguments:
- Fdo - Supplies the functional device object which represents the target.
- Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
- BufferAddress - Supplies the address of the buffer.
- BufferLength - Supplies the length in bytes of the buffer.
- WriteToDevice - Indicates the data should be transfer to the device.
- Return Value:
- NTSTATUS indicating the final results of the operation.
- If NT_SUCCESS(), then the amount of usable data is contained in the field
- Srb->DataTransferLength
- --*/
- NTSTATUS
- ClassSendSrbSynchronous(
- PDEVICE_OBJECT Fdo,
- PSCSI_REQUEST_BLOCK Srb,
- PVOID BufferAddress,
- ULONG BufferLength,
- BOOLEAN WriteToDevice
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
- IO_STATUS_BLOCK ioStatus;
- ULONG controlType;
- PIRP irp;
- PIO_STACK_LOCATION irpStack;
- KEVENT event;
- PUCHAR senseInfoBuffer;
- ULONG retryCount = MAXIMUM_RETRIES;
- NTSTATUS status;
- BOOLEAN retry;
- //
- // NOTE: This code is only pagable because we are not freezing
- // the queue. Allowing the queue to be frozen from a pagable
- // routine could leave the queue frozen as we try to page in
- // the code to unfreeze the queue. The result would be a nice
- // case of deadlock. Therefore, since we are unfreezing the
- // queue regardless of the result, just set the NO_FREEZE_QUEUE
- // flag in the SRB.
- //
- ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
- ASSERT(fdoExtension->CommonExtension.IsFdo);
- //
- // Write length to SRB.
- //
- Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
- //
- // Set SCSI bus address.
- //
- Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
- //
- // Enable auto request sense.
- //
- Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
- //
- // Sense buffer is in aligned nonpaged pool.
- //
- //
- senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
- SENSE_BUFFER_SIZE,
- '7CcS');
- if (senseInfoBuffer == NULL) {
- DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
- "buffer\n"));
- return(STATUS_INSUFFICIENT_RESOURCES);
- }
- Srb->SenseInfoBuffer = senseInfoBuffer;
- Srb->DataBuffer = BufferAddress;
- //
- // Start retries here.
- //
- retry:
- //
- // use fdoextension's flags by default.
- // do not move out of loop, as the flag may change due to errors
- // sending this command.
- //
- Srb->SrbFlags = fdoExtension->SrbFlags;
- if(BufferAddress != NULL) {
- if(WriteToDevice) {
- SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
- } else {
- SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
- }
- }
- //
- // Initialize the QueueAction field.
- //
- Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
- //
- // Disable synchronous transfer for these requests.
- // Disable freezing the queue, since all we do is unfreeze it anyways.
- //
- SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
- SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
- //
- // Set the event object to the unsignaled state.
- // It will be used to signal request completion.
- //
- KeInitializeEvent(&event, NotificationEvent, FALSE);
- //
- // Build device I/O control request with METHOD_NEITHER data transfer.
- // We'll queue a completion routine to cleanup the MDL's and such ourself.
- //
- irp = IoAllocateIrp(
- (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
- FALSE);
- if(irp == NULL) {
- ExFreePool(senseInfoBuffer);
- DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
- return(STATUS_INSUFFICIENT_RESOURCES);
- }
- //
- // Get next stack location.
- //
- irpStack = IoGetNextIrpStackLocation(irp);
- //
- // Set up SRB for execute scsi request. Save SRB address in next stack
- // for the port driver.
- //
- irpStack->MajorFunction = IRP_MJ_SCSI;
- irpStack->Parameters.Scsi.Srb = Srb;
- IoSetCompletionRoutine(irp,
- ClasspSendSynchronousCompletion,
- Srb,
- TRUE,
- TRUE,
- TRUE);
- irp->UserIosb = &ioStatus;
- irp->UserEvent = &event;
- if(BufferAddress) {
- //
- // Build an MDL for the data buffer and stick it into the irp. The
- // completion routine will unlock the pages and free the MDL.
- //
- irp->MdlAddress = IoAllocateMdl( BufferAddress,
- BufferLength,
- FALSE,
- FALSE,
- irp );
- if (irp->MdlAddress == NULL) {
- ExFreePool(senseInfoBuffer);
- Srb->SenseInfoBuffer = NULL;
- IoFreeIrp( irp );
- DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- _SEH2_TRY {
- //
- // the io manager unlocks these pages upon completion
- //
- MmProbeAndLockPages( irp->MdlAddress,
- KernelMode,
- (WriteToDevice ? IoReadAccess :
- IoWriteAccess));
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- status = _SEH2_GetExceptionCode();
- ExFreePool(senseInfoBuffer);
- Srb->SenseInfoBuffer = NULL;
- IoFreeMdl(irp->MdlAddress);
- IoFreeIrp(irp);
- DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
- "locking buffer\n", status));
- return status;
- } _SEH2_END;
- }
- //
- // Set the transfer length.
- //
- Srb->DataTransferLength = BufferLength;
- //
- // Zero out status.
- //
- Srb->ScsiStatus = Srb->SrbStatus = 0;
- Srb->NextSrb = 0;
- //
- // Set up IRP Address.
- //
- Srb->OriginalRequest = irp;
- //
- // Call the port driver with the request and wait for it to complete.
- //
- status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
- if (status == STATUS_PENDING) {
- KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
- status = ioStatus.Status;
- }
- //
- // Check that request completed without error.
- //
- if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
- ULONG retryInterval;
- DBGTRACE(ClassDebugWarning, ("ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb, DBGGETSCSIOPSTR(Srb), DBGGETSRBSTATUSSTR(Srb), (ULONG)Srb->SrbStatus, status, DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));
- //
- // assert that the queue is not frozen
- //
- ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
- //
- // Update status and determine if request should be retried.
- //
- retry = ClassInterpretSenseInfo(Fdo,
- Srb,
- IRP_MJ_SCSI,
- 0,
- MAXIMUM_RETRIES - retryCount,
- &status,
- &retryInterval);
- if (retry) {
- if ((status == STATUS_DEVICE_NOT_READY &&
- ((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode ==
- SCSI_ADSENSE_LUN_NOT_READY) ||
- (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
- LARGE_INTEGER delay;
- //
- // Delay for at least 2 seconds.
- //
- if(retryInterval < 2) {
- retryInterval = 2;
- }
- delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval);
- //
- // Stall for a while to let the device become ready
- //
- KeDelayExecutionThread(KernelMode, FALSE, &delay);
- }
- //
- // If retries are not exhausted then retry this operation.
- //
- if (retryCount--) {
- if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
- FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
- }
- goto retry;
- }
- }
- } else {
- fdoData->LoggedTURFailureSinceLastIO = FALSE;
- status = STATUS_SUCCESS;
- }
- //
- // required even though we allocated our own, since the port driver may
- // have allocated one also
- //
- if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
- FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
- }
- Srb->SenseInfoBuffer = NULL;
- ExFreePool(senseInfoBuffer);
- return status;
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClassInterpretSenseInfo()
- Routine Description:
- This routine interprets the data returned from the SCSI
- request sense. It determines the status to return in the
- IRP and whether this request can be retried.
- Arguments:
- DeviceObject - Supplies the device object associated with this request.
- Srb - Supplies the scsi request block which failed.
- MajorFunctionCode - Supplies the function code to be used for logging.
- IoDeviceCode - Supplies the device code to be used for logging.
- Status - Returns the status for the request.
- Return Value:
- BOOLEAN TRUE: Drivers should retry this request.
- FALSE: Drivers should not retry this request.
- --*/
- BOOLEAN
- ClassInterpretSenseInfo(
- IN PDEVICE_OBJECT Fdo,
- IN PSCSI_REQUEST_BLOCK Srb,
- IN UCHAR MajorFunctionCode,
- IN ULONG IoDeviceCode,
- IN ULONG RetryCount,
- OUT NTSTATUS *Status,
- OUT OPTIONAL ULONG *RetryInterval
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
- PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
- PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
- BOOLEAN retry = TRUE;
- BOOLEAN logError = FALSE;
- BOOLEAN unhandledError = FALSE;
- BOOLEAN incrementErrorCount = FALSE;
- ULONG badSector = 0;
- ULONG uniqueId = 0;
- NTSTATUS logStatus;
- ULONG readSector;
- ULONG index;
- ULONG retryInterval = 0;
- KIRQL oldIrql;
- logStatus = -1;
- if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
- //
- // Log anything remotely incorrect about paging i/o
- //
- logError = TRUE;
- uniqueId = 301;
- logStatus = IO_WARNING_PAGING_FAILURE;
- }
- //
- // Check that request sense buffer is valid.
- //
- ASSERT(fdoExtension->CommonExtension.IsFdo);
- //
- // must handle the SRB_STATUS_INTERNAL_ERROR case first,
- // as it has all the flags set.
- //
- if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: Internal Error code is %x\n",
- Srb->InternalStatus));
- retry = FALSE;
- *Status = Srb->InternalStatus;
- } else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
- (Srb->SenseInfoBufferLength >=
- offsetof(SENSE_DATA, CommandSpecificInformation))) {
- //
- // Zero the additional sense code and additional sense code qualifier
- // if they were not returned by the device.
- //
- readSector = senseBuffer->AdditionalSenseLength +
- offsetof(SENSE_DATA, AdditionalSenseLength);
- if (readSector > Srb->SenseInfoBufferLength) {
- readSector = Srb->SenseInfoBufferLength;
- }
- if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCode)) {
- senseBuffer->AdditionalSenseCode = 0;
- }
- if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCodeQualifier)) {
- senseBuffer->AdditionalSenseCodeQualifier = 0;
- }
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: Error code is %x\n",
- senseBuffer->ErrorCode));
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: Sense key is %x\n",
- senseBuffer->SenseKey));
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: Additional sense code is %x\n",
- senseBuffer->AdditionalSenseCode));
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: Additional sense code qualifier "
- "is %x\n",
- senseBuffer->AdditionalSenseCodeQualifier));
- switch (senseBuffer->SenseKey & 0xf) {
- case SCSI_SENSE_NOT_READY: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Device not ready\n"));
- *Status = STATUS_DEVICE_NOT_READY;
- switch (senseBuffer->AdditionalSenseCode) {
- case SCSI_ADSENSE_LUN_NOT_READY: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Lun not ready\n"));
- switch (senseBuffer->AdditionalSenseCodeQualifier) {
- case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
- DEVICE_EVENT_BECOMING_READY notReady;
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Operation In Progress\n"));
- retryInterval = NOT_READY_RETRY_INTERVAL;
- RtlZeroMemory(¬Ready, sizeof(DEVICE_EVENT_BECOMING_READY));
- notReady.Version = 1;
- notReady.Reason = 2;
- notReady.Estimated100msToReady = retryInterval * 10;
- ClasspSendNotification(fdoExtension,
- &GUID_IO_DEVICE_BECOMING_READY,
- sizeof(DEVICE_EVENT_BECOMING_READY),
- ¬Ready);
- break;
- }
- case SCSI_SENSEQ_BECOMING_READY: {
- DEVICE_EVENT_BECOMING_READY notReady;
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "In process of becoming ready\n"));
- retryInterval = NOT_READY_RETRY_INTERVAL;
- RtlZeroMemory(¬Ready, sizeof(DEVICE_EVENT_BECOMING_READY));
- notReady.Version = 1;
- notReady.Reason = 1;
- notReady.Estimated100msToReady = retryInterval * 10;
- ClasspSendNotification(fdoExtension,
- &GUID_IO_DEVICE_BECOMING_READY,
- sizeof(DEVICE_EVENT_BECOMING_READY),
- ¬Ready);
- break;
- }
- case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Long write in progress\n"));
- retry = FALSE;
- break;
- }
- case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Manual intervention required\n"));
- *Status = STATUS_NO_MEDIA_IN_DEVICE;
- retry = FALSE;
- break;
- }
- case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Format in progress\n"));
- retry = FALSE;
- break;
- }
- case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
- if(!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
- CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: "
- "not ready, cause unknown\n"));
- /*
- Many non-WHQL certified drives (mostly CD-RW) return
- this when they have no media instead of the obvious
- choice of:
- SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
- These drives should not pass WHQL certification due
- to this discrepency.
- */
- retry = FALSE;
- break;
- } else {
- //
- // Treat this as init command required and fall through.
- //
- }
- }
- case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
- default: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Initializing command required\n"));
- //
- // This sense code/additional sense code
- // combination may indicate that the device
- // needs to be started. Send an start unit if this
- // is a disk device.
- //
- if(TEST_FLAG(fdoExtension->DeviceFlags,
- DEV_SAFE_START_UNIT) &&
- !TEST_FLAG(Srb->SrbFlags,
- SRB_CLASS_FLAGS_LOW_PRIORITY)) {
- ClassSendStartUnit(Fdo);
- }
- break;
- }
- } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
- break;
- }
- case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "No Media in device.\n"));
- *Status = STATUS_NO_MEDIA_IN_DEVICE;
- retry = FALSE;
- //
- // signal MCN that there isn't any media in the device
- //
- if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
- DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
- "No Media in a non-removable device %p\n",
- Fdo));
- }
- ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
- break;
- }
- } // end switch (senseBuffer->AdditionalSenseCode)
- break;
- } // end SCSI_SENSE_NOT_READY
- case SCSI_SENSE_DATA_PROTECT: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Media write protected\n"));
- *Status = STATUS_MEDIA_WRITE_PROTECTED;
- retry = FALSE;
- break;
- } // end SCSI_SENSE_DATA_PROTECT
- case SCSI_SENSE_MEDIUM_ERROR: {
- DebugPrint((ClassDebugSenseInfo,"ClassInterpretSenseInfo: "
- "Medium Error (bad block)\n"));
- *Status = STATUS_DEVICE_DATA_ERROR;
- retry = FALSE;
- logError = TRUE;
- uniqueId = 256;
- logStatus = IO_ERR_BAD_BLOCK;
- //
- // Check if this error is due to unknown format
- //
- if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA){
- switch (senseBuffer->AdditionalSenseCodeQualifier) {
- case SCSI_SENSEQ_UNKNOWN_FORMAT: {
- *Status = STATUS_UNRECOGNIZED_MEDIA;
- //
- // Log error only if this is a paging request
- //
- if(!TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
- logError = FALSE;
- }
- break;
- }
- case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
- *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
- logError = FALSE;
- break;
- }
- default: {
- break;
- }
- } // end switch AdditionalSenseCodeQualifier
- } // end SCSI_ADSENSE_INVALID_MEDIA
- break;
- } // end SCSI_SENSE_MEDIUM_ERROR
- case SCSI_SENSE_HARDWARE_ERROR: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Hardware error\n"));
- *Status = STATUS_IO_DEVICE_ERROR;
- logError = TRUE;
- uniqueId = 257;
- logStatus = IO_ERR_CONTROLLER_ERROR;
- break;
- } // end SCSI_SENSE_HARDWARE_ERROR
- case SCSI_SENSE_ILLEGAL_REQUEST: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Illegal SCSI request\n"));
- *Status = STATUS_INVALID_DEVICE_REQUEST;
- retry = FALSE;
- switch (senseBuffer->AdditionalSenseCode) {
- case SCSI_ADSENSE_ILLEGAL_COMMAND: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Illegal command\n"));
- break;
- }
- case SCSI_ADSENSE_ILLEGAL_BLOCK: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Illegal block address\n"));
- *Status = STATUS_NONEXISTENT_SECTOR;
- break;
- }
- case SCSI_ADSENSE_INVALID_LUN: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Invalid LUN\n"));
- *Status = STATUS_NO_SUCH_DEVICE;
- break;
- }
- case SCSI_ADSENSE_MUSIC_AREA: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Music area\n"));
- break;
- }
- case SCSI_ADSENSE_DATA_AREA: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Data area\n"));
- break;
- }
- case SCSI_ADSENSE_VOLUME_OVERFLOW: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Volume overflow\n"));
- break;
- }
- case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Copy protection failure\n"));
- *Status = STATUS_COPY_PROTECTION_FAILURE;
- switch (senseBuffer->AdditionalSenseCodeQualifier) {
- case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: "
- "Authentication failure\n"));
- *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
- break;
- case SCSI_SENSEQ_KEY_NOT_PRESENT:
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: "
- "Key not present\n"));
- *Status = STATUS_CSS_KEY_NOT_PRESENT;
- break;
- case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: "
- "Key not established\n"));
- *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
- break;
- case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: "
- "Read of scrambled sector w/o "
- "authentication\n"));
- *Status = STATUS_CSS_SCRAMBLED_SECTOR;
- break;
- case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: "
- "Media region does not logical unit "
- "region\n"));
- *Status = STATUS_CSS_REGION_MISMATCH;
- break;
- case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
- DebugPrint((ClassDebugSenseInfo,
- "ClassInterpretSenseInfo: "
- "Region set error -- region may "
- "be permanent\n"));
- *Status = STATUS_CSS_RESETS_EXHAUSTED;
- break;
- } // end switch of ASCQ for COPY_PROTECTION_FAILURE
- break;
- }
- case SCSI_ADSENSE_INVALID_CDB: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Invalid CDB\n"));
- //
- // Note: the retry interval is not typically used.
- // it is set here only because a ClassErrorHandler
- // cannot set the retryInterval, and the error may
- // require a few commands to be sent to clear whatever
- // caused this condition (i.e. disk clears the write
- // cache, requiring at least two commands)
- //
- // hopefully, this shortcoming can be changed for
- // blackcomb.
- //
- retryInterval = 3;
- break;
- }
- } // end switch (senseBuffer->AdditionalSenseCode)
- break;
- } // end SCSI_SENSE_ILLEGAL_REQUEST
- case SCSI_SENSE_UNIT_ATTENTION: {
- PVPB vpb;
- ULONG count;
- //
- // A media change may have occured so increment the change
- // count for the physical device
- //
- count = InterlockedIncrement(&fdoExtension->MediaChangeCount);
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Media change count for device %d incremented to %#lx\n",
- fdoExtension->DeviceNumber, count));
- switch (senseBuffer->AdditionalSenseCode) {
- case SCSI_ADSENSE_MEDIUM_CHANGED: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Media changed\n"));
- if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
- DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
- "Media Changed on non-removable device %p\n",
- Fdo));
- }
- ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
- break;
- }
- case SCSI_ADSENSE_BUS_RESET: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Bus reset\n"));
- break;
- }
- case SCSI_ADSENSE_OPERATOR_REQUEST: {
- switch (senseBuffer->AdditionalSenseCodeQualifier) {
- case SCSI_SENSEQ_MEDIUM_REMOVAL: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Ejection request received!\n"));
- ClassSendEjectionNotification(fdoExtension);
- break;
- }
- case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Operator selected write permit?! "
- "(unsupported!)\n"));
- break;
- }
- case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Operator selected write protect?! "
- "(unsupported!)\n"));
- break;
- }
- }
- break;
- }
- default: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Unit attention\n"));
- break;
- }
- } // end switch (senseBuffer->AdditionalSenseCode)
- if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
- {
- //
- // TODO : Is the media lockable?
- //
- if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
- {
- //
- // Set bit to indicate that media may have changed
- // and volume needs verification.
- //
- SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
- *Status = STATUS_VERIFY_REQUIRED;
- retry = FALSE;
- }
- }
- else
- {
- *Status = STATUS_IO_DEVICE_ERROR;
- }
- break;
- } // end SCSI_SENSE_UNIT_ATTENTION
- case SCSI_SENSE_ABORTED_COMMAND: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Command aborted\n"));
- *Status = STATUS_IO_DEVICE_ERROR;
- retryInterval = 1;
- break;
- } // end SCSI_SENSE_ABORTED_COMMAND
- case SCSI_SENSE_BLANK_CHECK: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Media blank check\n"));
- retry = FALSE;
- *Status = STATUS_NO_DATA_DETECTED;
- break;
- } // end SCSI_SENSE_BLANK_CHECK
- case SCSI_SENSE_RECOVERED_ERROR: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Recovered error\n"));
- *Status = STATUS_SUCCESS;
- retry = FALSE;
- logError = TRUE;
- uniqueId = 258;
- switch(senseBuffer->AdditionalSenseCode) {
- case SCSI_ADSENSE_SEEK_ERROR:
- case SCSI_ADSENSE_TRACK_ERROR: {
- logStatus = IO_ERR_SEEK_ERROR;
- break;
- }
- case SCSI_ADSENSE_REC_DATA_NOECC:
- case SCSI_ADSENSE_REC_DATA_ECC: {
- logStatus = IO_RECOVERED_VIA_ECC;
- break;
- }
- case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
- UCHAR wmiEventData[5];
- *((PULONG)wmiEventData) = sizeof(UCHAR);
- wmiEventData[sizeof(ULONG)] = senseBuffer->AdditionalSenseCodeQualifier;
- //
- // Don't log another eventlog if we have already logged once
- // NOTE: this should have been interlocked, but the structure
- // was publicly defined to use a BOOLEAN (char). Since
- // media only reports these errors once per X minutes,
- // the potential race condition is nearly non-existant.
- // the worst case is duplicate log entries, so ignore.
- //
- if (fdoExtension->FailurePredicted == 0) {
- logError = TRUE;
- }
- fdoExtension->FailurePredicted = TRUE;
- fdoExtension->FailureReason = senseBuffer->AdditionalSenseCodeQualifier;
- logStatus = IO_WRN_FAILURE_PREDICTED;
- ClassNotifyFailurePredicted(fdoExtension,
- (PUCHAR)&wmiEventData,
- sizeof(wmiEventData),
- 0,
- 4,
- Srb->PathId,
- Srb->TargetId,
- Srb->Lun);
- break;
- }
- default: {
- logStatus = IO_ERR_CONTROLLER_ERROR;
- break;
- }
- } // end switch(senseBuffer->AdditionalSenseCode)
- if (senseBuffer->IncorrectLength) {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Incorrect length detected.\n"));
- *Status = STATUS_INVALID_BLOCK_LENGTH ;
- }
- break;
- } // end SCSI_SENSE_RECOVERED_ERROR
- case SCSI_SENSE_NO_SENSE: {
- //
- // Check other indicators.
- //
- if (senseBuffer->IncorrectLength) {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Incorrect length detected.\n"));
- *Status = STATUS_INVALID_BLOCK_LENGTH ;
- retry = FALSE;
- } else {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "No specific sense key\n"));
- *Status = STATUS_IO_DEVICE_ERROR;
- retry = TRUE;
- }
- break;
- } // end SCSI_SENSE_NO_SENSE
- default: {
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Unrecognized sense code\n"));
- *Status = STATUS_IO_DEVICE_ERROR;
- break;
- }
- } // end switch (senseBuffer->SenseKey & 0xf)
- //
- // Try to determine the bad sector from the inquiry data.
- //
- if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_READ ||
- ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY ||
- ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_WRITE)) {
- for (index = 0; index < 4; index++) {
- badSector = (badSector << 8) | senseBuffer->Information[index];
- }
- readSector = 0;
- for (index = 0; index < 4; index++) {
- readSector = (readSector << 8) | Srb->Cdb[index+2];
- }
- index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
- ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
- //
- // Make sure the bad sector is within the read sectors.
- //
- if (!(badSector >= readSector && badSector < readSector + index)) {
- badSector = readSector;
- }
- }
- } else {
- //
- // Request sense buffer not valid. No sense information
- // to pinpoint the error. Return general request fail.
- //
- DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
- "Request sense info not valid. SrbStatus %2x\n",
- SRB_STATUS(Srb->SrbStatus)));
- retry = TRUE;
- switch (SRB_STATUS(Srb->SrbStatus)) {
- case SRB_STATUS_INVALID_LUN:
- case SRB_STATUS_INVALID_TARGET_ID:
- case SRB_STATUS_NO_DEVICE:
- case SRB_STATUS_NO_HBA:
- case SRB_STATUS_INVALID_PATH_ID: {
- *Status = STATUS_NO_SUCH_DEVICE;
- retry = FALSE;
- break;
- }
- case SRB_STATUS_COMMAND_TIMEOUT:
- case SRB_STATUS_TIMEOUT: {
- //
- // Update the error count for the device.
- //
- incrementErrorCount = TRUE;
- *Status = STATUS_IO_TIMEOUT;
- break;
- }
- case SRB_STATUS_ABORTED: {
- //
- // Update the error count for the device.
- //
- incrementErrorCount = TRUE;
- *Status = STATUS_IO_TIMEOUT;
- retryInterval = 1;
- break;
- }
- case SRB_STATUS_SELECTION_TIMEOUT: {
- logError = TRUE;
- logStatus = IO_ERR_NOT_READY;
- uniqueId = 260;
- *Status = STATUS_DEVICE_NOT_CONNECTED;
- retry = FALSE;
- break;
- }
- case SRB_STATUS_DATA_OVERRUN: {
- *Status = STATUS_DATA_OVERRUN;
- retry = FALSE;
- break;
- }
- case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
- //
- // Update the error count for the device.
- //
- incrementErrorCount = TRUE;
- *Status = STATUS_IO_DEVICE_ERROR;
- //
- // If there was phase sequence error then limit the number of
- // retries.
- //
- if (RetryCount > 1 ) {
- retry = FALSE;
- }
- break;
- }
- case SRB_STATUS_REQUEST_FLUSHED: {
- //
- // If the status needs verification bit is set. Then set
- // the status to need verification and no retry; otherwise,
- // just retry the request.
- //
- if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
- *Status = STATUS_VERIFY_REQUIRED;
- retry = FALSE;
- } else {
- *Status = STATUS_IO_DEVICE_ERROR;
- }
- break;
- }
- case SRB_STATUS_INVALID_REQUEST: {
- *Status = STATUS_INVALID_DEVICE_REQUEST;
- retry = FALSE;
- break;
- }
- case SRB_STATUS_UNEXPECTED_BUS_FREE:
- case SRB_STATUS_PARITY_ERROR:
- //
- // Update the error count for the device
- // and fall through to below
- //
- incrementErrorCount = TRUE;
- case SRB_STATUS_BUS_RESET: {
- *Status = STATUS_IO_DEVICE_ERROR;
- break;
- }
- case SRB_STATUS_ERROR: {
- *Status = STATUS_IO_DEVICE_ERROR;
- if (Srb->ScsiStatus == 0) {
- //
- // This is some strange return code. Update the error
- // count for the device.
- //
- incrementErrorCount = TRUE;
- } if (Srb->ScsiStatus == SCSISTAT_BUSY) {
- *Status = STATUS_DEVICE_NOT_READY;
- } if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) {
- *Status = STATUS_DEVICE_BUSY;
- retry = FALSE;
- logError = FALSE;
- }
- break;
- }
- default: {
- logError = TRUE;
- logStatus = IO_ERR_CONTROLLER_ERROR;
- uniqueId = 259;
- *Status = STATUS_IO_DEVICE_ERROR;
- unhandledError = TRUE;
- break;
- }
- }
- //
- // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
- // we know from a previous poll when the device will be ready (ETA)
- // we should delay the retry more appropriately than just guessing.
- //
- /*
- if (fdoExtension->MediaChangeDetectionInfo &&
- fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
- TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
- NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
- ) {
- // check if Gesn.ReadyTime if greater than current tick count
- // if so, delay that long (from 1 to 30 seconds max?)
- // else, leave the guess of time alone.
- }
- */
- }
- if (incrementErrorCount) {
- //
- // if any error count occurred, delay the retry of this io by
- // at least one second, if caller supports it.
- //
- if (retryInterval == 0) {
- retryInterval = 1;
- }
- ClasspPerfIncrementErrorCount(fdoExtension);
- }
- //
- // If there is a class specific error handler call it.
- //
- if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
- fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
- Srb,
- Status,
- &retry);
- }
- //
- // If the caller wants to know the suggested retry interval tell them.
- //
- if(ARGUMENT_PRESENT(RetryInterval)) {
- *RetryInterval = retryInterval;
- }
- /*
- * LOG the error:
- * Always log the error in our internal log.
- * If logError is set, also log the error in the system log.
- */
- {
- ULONG totalSize;
- ULONG senseBufferSize = 0;
- IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
- CLASS_ERROR_LOG_DATA staticErrLogData = {0};
- //
- // Calculate the total size of the error log entry.
- // add to totalSize in the order that they are used.
- // the advantage to calculating all the sizes here is
- // that we don't have to do a bunch of extraneous checks
- // later on in this code path.
- //
- totalSize = sizeof(IO_ERROR_LOG_PACKET) // required
- - sizeof(ULONG) // struct includes one ULONG
- + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
- //
- // also save any available extra sense data, up to the maximum errlog
- // packet size . WMI should be used for real-time analysis.
- // the event log should only be used for post-mortem debugging.
- //
- if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
- ULONG validSenseBytes;
- BOOLEAN validSense;
- //
- // make sure we can at least access the AdditionalSenseLength field
- //
- validSense = RTL_CONTAINS_FIELD(senseBuffer,
- Srb->SenseInfoBufferLength,
- AdditionalSenseLength);
- if (validSense) {
- //
- // if extra info exists, copy the maximum amount of available
- // sense data that is safe into the the errlog.
- //
- validSenseBytes = senseBuffer->AdditionalSenseLength
- + offsetof(SENSE_DATA, AdditionalSenseLength);
- //
- // this is invalid because it causes overflow!
- // whoever sent this type of request would cause
- // a system crash.
- //
- ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
- //
- // set to save the most sense buffer possible
- //
- senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
- senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
- } else {
- //
- // it's smaller than required to read the total number of
- // valid bytes, so just use the SenseInfoBufferLength field.
- //
- senseBufferSize = Srb->SenseInfoBufferLength;
- }
- /*
- * Bump totalSize by the number of extra senseBuffer bytes
- * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
- * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
- */
- if (senseBufferSize > sizeof(SENSE_DATA)){
- totalSize += senseBufferSize-sizeof(SENSE_DATA);
- if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
- senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
- totalSize = ERROR_LOG_MAXIMUM_SIZE;
- }
- }
- }
- //
- // If we've used up all of our retry attempts, set the final status to
- // reflect the appropriate result.
- //
- if (retry && RetryCount < MAXIMUM_RETRIES) {
- staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
- staticErrLogData.ErrorRetried = TRUE;
- } else {
- staticErrLogEntry.FinalStatus = *Status;
- }
- if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
- staticErrLogData.ErrorPaging = TRUE;
- }
- if (unhandledError) {
- staticErrLogData.ErrorUnhandled = TRUE;
- }
- //
- // Calculate the device offset if there is a geometry.
- //
- staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
- staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
- if (logStatus == -1){
- staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
- } else {
- staticErrLogEntry.ErrorCode = logStatus;
- }
- /*
- * The dump data follows the IO_ERROR_LOG_PACKET,
- * with the first ULONG of dump data inside the packet.
- */
- staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG);
- staticErrLogEntry.SequenceNumber = 0;
- staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
- staticErrLogEntry.IoControlCode = IoDeviceCode;
- staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
- staticErrLogEntry.UniqueErrorValue = uniqueId;
- KeQueryTickCount(&staticErrLogData.TickCount);
- staticErrLogData.PortNumber = (ULONG)-1;
- /*
- * Save the entire contents of the SRB.
- */
- staticErrLogData.Srb = *Srb;
- /*
- * For our private log, save just the default length of the SENSE_DATA.
- */
- if (senseBufferSize != 0){
- RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
- }
- /*
- * Save the error log in our context.
- * We only save the default sense buffer length.
- */
- KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
- fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
- fdoData->ErrorLogNextIndex++;
- fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
- KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
- /*
- * If logError is set, also save this log in the system's error log.
- * But make sure we don't log TUR failures over and over
- * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
- */
- if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_TEST_UNIT_READY) && logError){
- if (fdoData->LoggedTURFailureSinceLastIO){
- logError = FALSE;
- }
- else {
- fdoData->LoggedTURFailureSinceLastIO = TRUE;
- }
- }
- if (logError){
- PIO_ERROR_LOG_PACKET errorLogEntry;
- PCLASS_ERROR_LOG_DATA errlogData;
- errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
- if (errorLogEntry){
- errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
- *errorLogEntry = staticErrLogEntry;
- *errlogData = staticErrLogData;
- /*
- * For the system log, copy as much of the sense buffer as possible.
- */
- if (senseBufferSize != 0) {
- RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
- }
- /*
- * Write the error log packet to the system error logging thread.
- */
- IoWriteErrorLogEntry(errorLogEntry);
- }
- }
- }
- return retry;
- } // end ClassInterpretSenseInfo()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassModeSense()
- Routine Description:
- This routine sends a mode sense command to a target ID and returns
- when it is complete.
- Arguments:
- Fdo - Supplies the functional device object associated with this request.
- ModeSenseBuffer - Supplies a buffer to store the sense data.
- Length - Supplies the length in bytes of the mode sense buffer.
- PageMode - Supplies the page or pages of mode sense data to be retrived.
- Return Value:
- Length of the transferred data is returned.
- --*/
- ULONG ClassModeSense( IN PDEVICE_OBJECT Fdo,
- IN PCHAR ModeSenseBuffer,
- IN ULONG Length,
- IN UCHAR PageMode)
- {
- ULONG lengthTransferred = 0;
- PMDL senseBufferMdl;
- PAGED_CODE();
- senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
- if (senseBufferMdl){
- TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
- if (pkt){
- KEVENT event;
- NTSTATUS pktStatus;
- IRP pseudoIrp = {0};
- /*
- * Store the number of packets servicing the irp (one)
- * inside the original IRP. It will be used to counted down
- * to zero when the packet completes.
- * Initialize the original IRP's status to success.
- * If the packet fails, we will set it to the error status.
- */
- pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
- pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
- pseudoIrp.IoStatus.Information = 0;
- pseudoIrp.MdlAddress = senseBufferMdl;
- /*
- * Set this up as a SYNCHRONOUS transfer, submit it,
- * and wait for the packet to complete. The result
- * status will be written to the original irp.
- */
- ASSERT(Length <= 0x0ff);
- KeInitializeEvent(&event, SynchronizationEvent, FALSE);
- SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, &pseudoIrp);
- SubmitTransferPacket(pkt);
- KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
- if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
- lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
- }
- else {
- /*
- * This request can sometimes fail legitimately
- * (e.g. when a SCSI device is attached but turned off)
- * so this is not necessarily a device/driver bug.
- */
- DBGTRACE(ClassDebugWarning, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
- }
- }
- FreeDeviceInputMdl(senseBufferMdl);
- }
- return lengthTransferred;
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClassFindModePage()
- Routine Description:
- This routine scans through the mode sense data and finds the requested
- mode sense page code.
- Arguments:
- ModeSenseBuffer - Supplies a pointer to the mode sense data.
- Length - Indicates the length of valid data.
- PageMode - Supplies the page mode to be searched for.
- Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
- Return Value:
- A pointer to the the requested mode page. If the mode page was not found
- then NULL is return.
- --*/
- PVOID
- ClassFindModePage(
- IN PCHAR ModeSenseBuffer,
- IN ULONG Length,
- IN UCHAR PageMode,
- IN BOOLEAN Use6Byte
- )
- {
- PUCHAR limit;
- ULONG parameterHeaderLength;
- PVOID result = NULL;
- limit = ModeSenseBuffer + Length;
- parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
- if (Length >= parameterHeaderLength) {
- PMODE_PARAMETER_HEADER10 modeParam10;
- ULONG blockDescriptorLength;
- /*
- * Skip the mode select header and block descriptors.
- */
- if (Use6Byte){
- blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
- }
- else {
- modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
- blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
- }
- ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
- //
- // ModeSenseBuffer now points at pages. Walk the pages looking for the
- // requested page until the limit is reached.
- //
- while (ModeSenseBuffer +
- RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit) {
- if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
- /*
- * found the mode page. make sure it's safe to touch it all
- * before returning the pointer to caller
- */
- if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit) {
- /*
- * Return NULL since the page is not safe to access in full
- */
- result = NULL;
- }
- else {
- result = ModeSenseBuffer;
- }
- break;
- }
- //
- // Advance to the next page which is 4-byte-aligned offset after this page.
- //
- ModeSenseBuffer +=
- ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
- RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
- }
- }
- return result;
- } // end ClassFindModePage()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassSendSrbAsynchronous()
- Routine Description:
- This routine takes a partially built Srb and an Irp and sends it down to
- the port driver.
- This routine must be called with the remove lock held for the specified
- Irp.
- Arguments:
- Fdo - Supplies the functional device object for the orginal request.
- Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
- CDB and the SRB timeout value must be filled in. The SRB must not be
- allocated from zone.
- Irp - Supplies the requesting Irp.
- BufferAddress - Supplies a pointer to the buffer to be transfered.
- BufferLength - Supplies the length of data transfer.
- WriteToDevice - Indicates the data transfer will be from system memory to
- device.
- Return Value:
- Returns STATUS_PENDING if the request is dispatched (since the
- completion routine may change the irp's status value we cannot simply
- return the value of the dispatch)
- or returns a status value to indicate why it failed.
- --*/
- NTSTATUS
- ClassSendSrbAsynchronous(
- PDEVICE_OBJECT Fdo,
- PSCSI_REQUEST_BLOCK Srb,
- PIRP Irp,
- PVOID BufferAddress,
- ULONG BufferLength,
- BOOLEAN WriteToDevice
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PIO_STACK_LOCATION irpStack;
- ULONG savedFlags;
- //
- // Write length to SRB.
- //
- Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
- //
- // Set SCSI bus address.
- //
- Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
- //
- // This is a violation of the SCSI spec but it is required for
- // some targets.
- //
- // Srb->Cdb[1] |= deviceExtension->Lun << 5;
- //
- // Indicate auto request sense by specifying buffer and size.
- //
- Srb->SenseInfoBuffer = fdoExtension->SenseData;
- Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
- Srb->DataBuffer = BufferAddress;
- //
- // Save the class driver specific flags away.
- //
- savedFlags = Srb->SrbFlags & SRB_FLAGS_CLASS_DRIVER_RESERVED;
- //
- // Allow the caller to specify that they do not wish
- // IoStartNextPacket() to be called in the completion routine.
- //
- SET_FLAG(savedFlags, (Srb->SrbFlags & SRB_FLAGS_DONT_START_NEXT_PACKET));
- if (BufferAddress != NULL) {
- //
- // Build Mdl if necessary.
- //
- if (Irp->MdlAddress == NULL) {
- if (IoAllocateMdl(BufferAddress,
- BufferLength,
- FALSE,
- FALSE,
- Irp) == NULL) {
- Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
- //
- // ClassIoComplete() would have free'd the srb
- //
- if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
- FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
- }
- ClassFreeOrReuseSrb(fdoExtension, Srb);
- ClassReleaseRemoveLock(Fdo, Irp);
- ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- MmBuildMdlForNonPagedPool(Irp->MdlAddress);
- } else {
- //
- // Make sure the buffer requested matches the MDL.
- //
- ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
- }
- //
- // Set read flag.
- //
- Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN;
- } else {
- //
- // Clear flags.
- //
- Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
- }
- //
- // Restore saved flags.
- //
- SET_FLAG(Srb->SrbFlags, savedFlags);
- //
- // Disable synchronous transfer for these requests.
- //
- SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
- //
- // Set the transfer length.
- //
- Srb->DataTransferLength = BufferLength;
- //
- // Zero out status.
- //
- Srb->ScsiStatus = Srb->SrbStatus = 0;
- Srb->NextSrb = 0;
- //
- // Save a few parameters in the current stack location.
- //
- irpStack = IoGetCurrentIrpStackLocation(Irp);
- //
- // Save retry count in current Irp stack.
- //
- irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
- //
- // Set up IoCompletion routine address.
- //
- IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
- //
- // Get next stack location and
- // set major function code.
- //
- irpStack = IoGetNextIrpStackLocation(Irp);
- irpStack->MajorFunction = IRP_MJ_SCSI;
- //
- // Save SRB address in next stack for port driver.
- //
- irpStack->Parameters.Scsi.Srb = Srb;
- //
- // Set up Irp Address.
- //
- Srb->OriginalRequest = Irp;
- //
- // Call the port driver to process the request.
- //
- IoMarkIrpPending(Irp);
- IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
- return STATUS_PENDING;
- } // end ClassSendSrbAsynchronous()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassDeviceControlDispatch()
- Routine Description:
- The routine is the common class driver device control dispatch entry point.
- This routine is invokes the device-specific drivers DeviceControl routine,
- (which may call the Class driver's common DeviceControl routine).
- Arguments:
- DeviceObject - Supplies a pointer to the device object for this request.
- Irp - Supplies the Irp making the request.
- Return Value:
- Returns the status returned from the device-specific driver.
- --*/
- NTSTATUS
- ClassDeviceControlDispatch(
- PDEVICE_OBJECT DeviceObject,
- PIRP Irp
- )
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- ULONG isRemoved;
- isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
- if(isRemoved) {
- ClassReleaseRemoveLock(DeviceObject, Irp);
- Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- return STATUS_DEVICE_DOES_NOT_EXIST;
- }
- //
- // Call the class specific driver DeviceControl routine.
- // If it doesn't handle it, it will call back into ClassDeviceControl.
- //
- ASSERT(commonExtension->DevInfo->ClassDeviceControl);
- return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
- } // end ClassDeviceControlDispatch()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassDeviceControl()
- Routine Description:
- The routine is the common class driver device control dispatch function.
- This routine is called by a class driver when it get an unrecognized
- device control request. This routine will perform the correct action for
- common requests such as lock media. If the device request is unknown it
- passed down to the next level.
- This routine must be called with the remove lock held for the specified
- irp.
- Arguments:
- DeviceObject - Supplies a pointer to the device object for this request.
- Irp - Supplies the Irp making the request.
- Return Value:
- Returns back a STATUS_PENDING or a completion status.
- --*/
- NTSTATUS
- ClassDeviceControl(
- PDEVICE_OBJECT DeviceObject,
- PIRP Irp
- )
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
- PIO_STACK_LOCATION nextStack = NULL;
- ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
- PSCSI_REQUEST_BLOCK srb = NULL;
- PCDB cdb = NULL;
- NTSTATUS status;
- ULONG modifiedIoControlCode;
- //
- // If this is a pass through I/O control, set the minor function code
- // and device address and pass it to the port driver.
- //
- if ((controlCode == IOCTL_SCSI_PASS_THROUGH) ||
- (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT)) {
- PSCSI_PASS_THROUGH scsiPass;
- //
- // Validiate the user buffer.
- //
- #if defined (_WIN64)
- if (IoIs32bitProcess(Irp)) {
- if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
- Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_INVALID_PARAMETER;
- goto SetStatusAndReturn;
- }
- }
- else
- #endif
- {
- if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
- sizeof(SCSI_PASS_THROUGH)) {
- Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_INVALID_PARAMETER;
- goto SetStatusAndReturn;
- }
- }
- IoCopyCurrentIrpStackLocationToNext(Irp);
- nextStack = IoGetNextIrpStackLocation(Irp);
- nextStack->MinorFunction = 1;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- goto SetStatusAndReturn;
- }
- Irp->IoStatus.Information = 0;
- switch (controlCode) {
- case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
- PMOUNTDEV_UNIQUE_ID uniqueId;
- if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
- status = STATUS_INVALID_PARAMETER;
- break;
- }
- if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
- sizeof(MOUNTDEV_UNIQUE_ID)) {
- status = STATUS_BUFFER_TOO_SMALL;
- Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
- break;
- }
- uniqueId = Irp->AssociatedIrp.SystemBuffer;
- uniqueId->UniqueIdLength =
- commonExtension->MountedDeviceInterfaceName.Length;
- if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
- sizeof(USHORT) + uniqueId->UniqueIdLength) {
- status = STATUS_BUFFER_OVERFLOW;
- Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
- break;
- }
- RtlCopyMemory(uniqueId->UniqueId,
- commonExtension->MountedDeviceInterfaceName.Buffer,
- uniqueId->UniqueIdLength);
- status = STATUS_SUCCESS;
- Irp->IoStatus.Information = sizeof(USHORT) +
- uniqueId->UniqueIdLength;
- break;
- }
- case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
- PMOUNTDEV_NAME name;
- ASSERT(commonExtension->DeviceName.Buffer);
- if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
- sizeof(MOUNTDEV_NAME)) {
- status = STATUS_BUFFER_TOO_SMALL;
- Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
- break;
- }
- name = Irp->AssociatedIrp.SystemBuffer;
- name->NameLength = commonExtension->DeviceName.Length;
- if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
- sizeof(USHORT) + name->NameLength) {
- status = STATUS_BUFFER_OVERFLOW;
- Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
- break;
- }
- RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
- name->NameLength);
- status = STATUS_SUCCESS;
- Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
- break;
- }
- case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
- PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
- WCHAR driveLetterNameBuffer[10];
- RTL_QUERY_REGISTRY_TABLE queryTable[2];
- PWSTR valueName;
- UNICODE_STRING driveLetterName;
- if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
- sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
- status = STATUS_BUFFER_TOO_SMALL;
- Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
- break;
- }
- valueName = ExAllocatePoolWithTag(
- PagedPool,
- commonExtension->DeviceName.Length + sizeof(WCHAR),
- '8CcS');
- if (!valueName) {
- status = STATUS_INSUFFICIENT_RESOURCES;
- break;
- }
- RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
- commonExtension->DeviceName.Length);
- valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
- driveLetterName.Buffer = driveLetterNameBuffer;
- driveLetterName.MaximumLength = 20;
- driveLetterName.Length = 0;
- RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
- queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
- RTL_QUERY_REGISTRY_DIRECT;
- queryTable[0].Name = valueName;
- queryTable[0].EntryContext = &driveLetterName;
- status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
- L"\\Registry\\Machine\\System\\DISK",
- queryTable, NULL, NULL);
- if (!NT_SUCCESS(status)) {
- ExFreePool(valueName);
- break;
- }
- if (driveLetterName.Length == 4 &&
- driveLetterName.Buffer[0] == '%' &&
- driveLetterName.Buffer[1] == ':') {
- driveLetterName.Buffer[0] = 0xFF;
- } else if (driveLetterName.Length != 4 ||
- driveLetterName.Buffer[0] < FirstDriveLetter ||
- driveLetterName.Buffer[0] > LastDriveLetter ||
- driveLetterName.Buffer[1] != ':') {
- status = STATUS_NOT_FOUND;
- ExFreePool(valueName);
- break;
- }
- suggestedName = Irp->AssociatedIrp.SystemBuffer;
- suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
- suggestedName->NameLength = 28;
- Irp->IoStatus.Information =
- FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
- if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
- Irp->IoStatus.Information) {
- Irp->IoStatus.Information =
- sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
- status = STATUS_BUFFER_OVERFLOW;
- ExFreePool(valueName);
- break;
- }
- RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
- L"\\Registry\\Machine\\System\\DISK",
- valueName);
- ExFreePool(valueName);
- RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
- suggestedName->Name[12] = driveLetterName.Buffer[0];
- suggestedName->Name[13] = ':';
- //
- // NT_SUCCESS(status) based on RtlQueryRegistryValues
- //
- status = STATUS_SUCCESS;
- break;
- }
- default:
- status = STATUS_PENDING;
- break;
- }
- if (status != STATUS_PENDING) {
- ClassReleaseRemoveLock(DeviceObject, Irp);
- Irp->IoStatus.Status = status;
- IoCompleteRequest(Irp, IO_NO_INCREMENT);
- return status;
- }
- if (commonExtension->IsFdo){
- PULONG_PTR function;
- srb = ExAllocatePoolWithTag(NonPagedPool,
- sizeof(SCSI_REQUEST_BLOCK) +
- (sizeof(ULONG_PTR) * 2),
- '9CcS');
- if (srb == NULL) {
- Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_INSUFFICIENT_RESOURCES;
- goto SetStatusAndReturn;
- }
- RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
- cdb = (PCDB)srb->Cdb;
- //
- // Save the function code and the device object in the memory after
- // the SRB.
- //
- function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
- *function = (ULONG_PTR) DeviceObject;
- function++;
- *function = (ULONG_PTR) controlCode;
- } else {
- srb = NULL;
- }
- //
- // Change the device type to storage for the switch statement, but only
- // if from a legacy device type
- //
- if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE << 16)) ||
- ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE << 16)) ||
- ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
- ) {
- modifiedIoControlCode = (controlCode & ~0xffff0000);
- modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
- } else {
- modifiedIoControlCode = controlCode;
- }
- DBGTRACE(ClassDebugTrace, ("> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
- switch (modifiedIoControlCode) {
- case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
- if (srb) {
- ExFreePool(srb);
- srb = NULL;
- }
- if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
- sizeof(STORAGE_HOTPLUG_INFO)) {
- //
- // Indicate unsuccessful status and no data transferred.
- //
- Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
- Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_BUFFER_TOO_SMALL;
- } else if(!commonExtension->IsFdo) {
- //
- // Just forward this down and return
- //
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- } else {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
- PSTORAGE_HOTPLUG_INFO info;
- fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
- info = Irp->AssociatedIrp.SystemBuffer;
- *info = fdoExtension->PrivateFdoData->HotplugInfo;
- Irp->IoStatus.Status = STATUS_SUCCESS;
- Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_SUCCESS;
- }
- break;
- }
- case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
- if (srb)
- {
- ExFreePool(srb);
- srb = NULL;
- }
- if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
- sizeof(STORAGE_HOTPLUG_INFO)) {
- //
- // Indicate unsuccessful status and no data transferred.
- //
- Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_INFO_LENGTH_MISMATCH;
- goto SetStatusAndReturn;
- }
- if(!commonExtension->IsFdo) {
- //
- // Just forward this down and return
- //
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- } else {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
- PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
- status = STATUS_SUCCESS;
- if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
- {
- status = STATUS_INVALID_PARAMETER_1;
- }
- if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
- {
- status = STATUS_INVALID_PARAMETER_2;
- }
- if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
- {
- status = STATUS_INVALID_PARAMETER_3;
- }
- if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
- {
- status = STATUS_INVALID_PARAMETER_5;
- }
- if (NT_SUCCESS(status))
- {
- fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
- //
- // Store the user-defined override in the registry
- //
- ClassSetDeviceParameter(fdoExtension,
- CLASSP_REG_SUBKEY_NAME,
- CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
- (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
- }
- Irp->IoStatus.Status = status;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- }
- break;
- }
- case IOCTL_STORAGE_CHECK_VERIFY:
- case IOCTL_STORAGE_CHECK_VERIFY2: {
- PIRP irp2 = NULL;
- PIO_STACK_LOCATION newStack;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
- DebugPrint((1,"DeviceIoControl: Check verify\n"));
- //
- // If a buffer for a media change count was provided, make sure it's
- // big enough to hold the result
- //
- if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
- //
- // If the buffer is too small to hold the media change count
- // then return an error to the caller
- //
- if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
- sizeof(ULONG)) {
- DebugPrint((3,"DeviceIoControl: media count "
- "buffer too small\n"));
- Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
- Irp->IoStatus.Information = sizeof(ULONG);
- if(srb != NULL) {
- ExFreePool(srb);
- }
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_BUFFER_TOO_SMALL;
- goto SetStatusAndReturn;
- }
- }
- if(!commonExtension->IsFdo) {
- //
- // If this is a PDO then we should just forward the request down
- //
- ASSERT(!srb);
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- goto SetStatusAndReturn;
- } else {
- fdoExtension = DeviceObject->DeviceExtension;
- }
- if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
- //
- // The caller has provided a valid buffer. Allocate an additional
- // irp and stick the CheckVerify completion routine on it. We will
- // then send this down to the port driver instead of the irp the
- // caller sent in
- //
- DebugPrint((2,"DeviceIoControl: Check verify wants "
- "media count\n"));
- //
- // Allocate a new irp to send the TestUnitReady to the port driver
- //
- irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
- if(irp2 == NULL) {
- Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
- Irp->IoStatus.Information = 0;
- ASSERT(srb);
- ExFreePool(srb);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_INSUFFICIENT_RESOURCES;
- goto SetStatusAndReturn;
- break;
- }
- //
- // Make sure to acquire the lock for the new irp.
- //
- ClassAcquireRemoveLock(DeviceObject, irp2);
- irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
- IoSetNextIrpStackLocation(irp2);
- //
- // Set the top stack location and shove the master Irp into the
- // top location
- //
- newStack = IoGetCurrentIrpStackLocation(irp2);
- newStack->Parameters.Others.Argument1 = Irp;
- newStack->DeviceObject = DeviceObject;
- //
- // Stick the check verify completion routine onto the stack
- // and prepare the irp for the port driver
- //
- IoSetCompletionRoutine(irp2,
- ClassCheckVerifyComplete,
- NULL,
- TRUE,
- TRUE,
- TRUE);
- IoSetNextIrpStackLocation(irp2);
- newStack = IoGetCurrentIrpStackLocation(irp2);
- newStack->DeviceObject = DeviceObject;
- newStack->MajorFunction = irpStack->MajorFunction;
- newStack->MinorFunction = irpStack->MinorFunction;
- //
- // Mark the master irp as pending - whether the lower level
- // driver completes it immediately or not this should allow it
- // to go all the way back up.
- //
- IoMarkIrpPending(Irp);
- Irp = irp2;
- }
- //
- // Test Unit Ready
- //
- srb->CdbLength = 6;
- cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
- //
- // Set timeout value.
- //
- srb->TimeOutValue = fdoExtension->TimeOutValue;
- //
- // If this was a CV2 then mark the request as low-priority so we don't
- // spin up the drive just to satisfy it.
- //
- if(controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
- SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
- }
- //
- // Since this routine will always hand the request to the
- // port driver if there isn't a data transfer to be done
- // we don't have to worry about completing the request here
- // on an error
- //
- //
- // This routine uses a completion routine so we don't want to release
- // the remove lock until then.
- //
- status = ClassSendSrbAsynchronous(DeviceObject,
- srb,
- Irp,
- NULL,
- 0,
- FALSE);
- break;
- }
- case IOCTL_STORAGE_MEDIA_REMOVAL:
- case IOCTL_STORAGE_EJECTION_CONTROL: {
- PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
- DebugPrint((3, "DiskIoControl: ejection control\n"));
- if(srb) {
- ExFreePool(srb);
- }
- if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
- sizeof(PREVENT_MEDIA_REMOVAL)) {
- //
- // Indicate unsuccessful status and no data transferred.
- //
- Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_INFO_LENGTH_MISMATCH;
- goto SetStatusAndReturn;
- }
- if(!commonExtension->IsFdo) {
- //
- // Just forward this down and return
- //
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- }
- else {
- // i don't believe this assertion is valid. this is a request
- // from user-mode, so they could request this for any device
- // they want? also, we handle it properly.
- // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
- status = ClasspEjectionControl(
- DeviceObject,
- Irp,
- ((modifiedIoControlCode ==
- IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
- SimpleMediaLock),
- mediaRemoval->PreventMediaRemoval);
- Irp->IoStatus.Status = status;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- }
- break;
- }
- case IOCTL_STORAGE_MCN_CONTROL: {
- DebugPrint((3, "DiskIoControl: MCN control\n"));
- if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
- sizeof(PREVENT_MEDIA_REMOVAL)) {
- //
- // Indicate unsuccessful status and no data transferred.
- //
- Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
- Irp->IoStatus.Information = 0;
- if(srb) {
- ExFreePool(srb);
- }
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- status = STATUS_INFO_LENGTH_MISMATCH;
- goto SetStatusAndReturn;
- }
- if(!commonExtension->IsFdo) {
- //
- // Just forward this down and return
- //
- if(srb) {
- ExFreePool(srb);
- }
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- } else {
- //
- // Call to the FDO - handle the ejection control.
- //
- status = ClasspMcnControl(DeviceObject->DeviceExtension,
- Irp,
- srb);
- }
- goto SetStatusAndReturn;
- }
- case IOCTL_STORAGE_RESERVE:
- case IOCTL_STORAGE_RELEASE: {
- //
- // Reserve logical unit.
- //
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
- if(!commonExtension->IsFdo) {
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- goto SetStatusAndReturn;
- } else {
- fdoExtension = DeviceObject->DeviceExtension;
- }
- srb->CdbLength = 6;
- if(modifiedIoControlCode == IOCTL_STORAGE_RESERVE) {
- cdb->CDB6GENERIC.OperationCode = SCSIOP_RESERVE_UNIT;
- } else {
- cdb->CDB6GENERIC.OperationCode = SCSIOP_RELEASE_UNIT;
- }
- //
- // Set timeout value.
- //
- srb->TimeOutValue = fdoExtension->TimeOutValue;
- status = ClassSendSrbAsynchronous(DeviceObject,
- srb,
- Irp,
- NULL,
- 0,
- FALSE);
- break;
- }
- case IOCTL_STORAGE_EJECT_MEDIA:
- case IOCTL_STORAGE_LOAD_MEDIA:
- case IOCTL_STORAGE_LOAD_MEDIA2:{
- //
- // Eject media.
- //
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
- if(!commonExtension->IsFdo) {
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- goto SetStatusAndReturn;
- } else {
- fdoExtension = DeviceObject->DeviceExtension;
- }
- if(commonExtension->PagingPathCount != 0) {
- DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
- "failure\n"));
- status = STATUS_FILES_OPEN;
- Irp->IoStatus.Status = status;
- Irp->IoStatus.Information = 0;
- if(srb) {
- ExFreePool(srb);
- }
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- goto SetStatusAndReturn;
- }
- //
- // Synchronize with ejection control and ejection cleanup code as
- // well as other eject/load requests.
- //
- KeEnterCriticalRegion();
- KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
- UserRequest,
- UserMode,
- FALSE,
- NULL);
- if(fdoExtension->ProtectedLockCount != 0) {
- DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
- "device - failure\n"));
- status = STATUS_DEVICE_BUSY;
- Irp->IoStatus.Status = status;
- Irp->IoStatus.Information = 0;
- if(srb) {
- ExFreePool(srb);
- }
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
- IO_NO_INCREMENT,
- FALSE);
- KeLeaveCriticalRegion();
- goto SetStatusAndReturn;
- }
- srb->CdbLength = 6;
- cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
- cdb->START_STOP.LoadEject = 1;
- if(modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
- cdb->START_STOP.Start = 0;
- } else {
- cdb->START_STOP.Start = 1;
- }
- //
- // Set timeout value.
- //
- srb->TimeOutValue = fdoExtension->TimeOutValue;
- status = ClassSendSrbAsynchronous(DeviceObject,
- srb,
- Irp,
- NULL,
- 0,
- FALSE);
- KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
- KeLeaveCriticalRegion();
- break;
- }
- case IOCTL_STORAGE_FIND_NEW_DEVICES: {
- if(srb) {
- ExFreePool(srb);
- }
- if(commonExtension->IsFdo) {
- IoInvalidateDeviceRelations(
- ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
- BusRelations);
- status = STATUS_SUCCESS;
- Irp->IoStatus.Status = status;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- }
- else {
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- }
- break;
- }
- case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
- if(srb) {
- ExFreePool(srb);
- }
- if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
- sizeof(STORAGE_DEVICE_NUMBER)) {
- PSTORAGE_DEVICE_NUMBER deviceNumber =
- Irp->AssociatedIrp.SystemBuffer;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
- commonExtension->PartitionZeroExtension;
- deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
- deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
- deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
- status = STATUS_SUCCESS;
- Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
- } else {
- status = STATUS_BUFFER_TOO_SMALL;
- Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
- }
- Irp->IoStatus.Status = status;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- break;
- }
- default: {
- DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
- controlCode, DeviceObject));
- //
- // Pass the device control to the next driver.
- //
- if(srb) {
- ExFreePool(srb);
- }
- //
- // Copy the Irp stack parameters to the next stack location.
- //
- IoCopyCurrentIrpStackLocationToNext(Irp);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- break;
- }
- } // end switch( ...
- SetStatusAndReturn:
- DBGTRACE(ClassDebugTrace, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
- return status;
- } // end ClassDeviceControl()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassShutdownFlush()
- Routine Description:
- This routine is called for a shutdown and flush IRPs. These are sent by the
- system before it actually shuts down or when the file system does a flush.
- If it exists, the device-specific driver's routine will be invoked. If there
- wasn't one specified, the Irp will be completed with an Invalid device request.
- Arguments:
- DriverObject - Pointer to device object to being shutdown by system.
- Irp - IRP involved.
- Return Value:
- NT Status
- --*/
- NTSTATUS
- ClassShutdownFlush(
- IN PDEVICE_OBJECT DeviceObject,
- IN PIRP Irp
- )
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- ULONG isRemoved;
- NTSTATUS status;
- isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
- if(isRemoved) {
- ClassReleaseRemoveLock(DeviceObject, Irp);
- Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- return STATUS_DEVICE_DOES_NOT_EXIST;
- }
- if (commonExtension->DevInfo->ClassShutdownFlush) {
- //
- // Call the device-specific driver's routine.
- //
- return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
- }
- //
- // Device-specific driver doesn't support this.
- //
- Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- return STATUS_INVALID_DEVICE_REQUEST;
- } // end ClassShutdownFlush()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassCreateDeviceObject()
- Routine Description:
- This routine creates an object for the physical device specified and
- sets up the deviceExtension's function pointers for each entry point
- in the device-specific driver.
- Arguments:
- DriverObject - Pointer to driver object created by system.
- ObjectNameBuffer - Dir. name of the object to create.
- LowerDeviceObject - Pointer to the lower device object
- IsFdo - should this be an fdo or a pdo
- DeviceObject - Pointer to the device object pointer we will return.
- Return Value:
- NTSTATUS
- --*/
- NTSTATUS
- ClassCreateDeviceObject(
- IN PDRIVER_OBJECT DriverObject,
- IN PCCHAR ObjectNameBuffer,
- IN PDEVICE_OBJECT LowerDevice,
- IN BOOLEAN IsFdo,
- IN OUT PDEVICE_OBJECT *DeviceObject
- )
- {
- BOOLEAN isPartitionable;
- STRING ntNameString;
- UNICODE_STRING ntUnicodeString;
- NTSTATUS status, status2;
- PDEVICE_OBJECT deviceObject = NULL;
- ULONG characteristics;
- PCLASS_DRIVER_EXTENSION
- driverExtension = IoGetDriverObjectExtension(DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- PCLASS_DEV_INFO devInfo;
- PAGED_CODE();
- *DeviceObject = NULL;
- RtlInitUnicodeString(&ntUnicodeString, NULL);
- DebugPrint((2, "ClassCreateFdo: Create device object\n"));
- ASSERT(LowerDevice);
- //
- // Make sure that if we're making PDO's we have an enumeration routine
- //
- isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
- ASSERT(IsFdo || isPartitionable);
- //
- // Grab the correct dev-info structure out of the init data
- //
- if(IsFdo) {
- devInfo = &(driverExtension->InitData.FdoData);
- } else {
- devInfo = &(driverExtension->InitData.PdoData);
- }
- characteristics = devInfo->DeviceCharacteristics;
- if(ARGUMENT_PRESENT(ObjectNameBuffer)) {
- DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
- RtlInitString(&ntNameString, ObjectNameBuffer);
- status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
- if (!NT_SUCCESS(status)) {
- DebugPrint((1,
- "ClassCreateFdo: Cannot convert string %s\n",
- ObjectNameBuffer));
- ntUnicodeString.Buffer = NULL;
- return status;
- }
- } else {
- DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
- if(IsFdo == FALSE) {
- //
- // PDO's have to have some sort of name.
- //
- SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
- }
- RtlInitUnicodeString(&ntUnicodeString, NULL);
- }
- status = IoCreateDevice(DriverObject,
- devInfo->DeviceExtensionSize,
- &ntUnicodeString,
- devInfo->DeviceType,
- devInfo->DeviceCharacteristics,
- FALSE,
- &deviceObject);
- if (!NT_SUCCESS(status)) {
- DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
- status));
- ASSERT(deviceObject == NULL);
- //
- // buffer is not used any longer here.
- //
- if (ntUnicodeString.Buffer != NULL) {
- DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
- ExFreePool(ntUnicodeString.Buffer);
- RtlInitUnicodeString(&ntUnicodeString, NULL);
- }
- } else {
- PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
- RtlZeroMemory(
- deviceObject->DeviceExtension,
- devInfo->DeviceExtensionSize);
- //
- // Setup version code
- //
- commonExtension->Version = 0x03;
- //
- // Setup the remove lock and event
- //
- commonExtension->IsRemoved = NO_REMOVE;
- commonExtension->RemoveLock = 0;
- KeInitializeEvent(&commonExtension->RemoveEvent,
- SynchronizationEvent,
- FALSE);
- #if DBG
- KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
- commonExtension->RemoveTrackingList = NULL;
- #else
- commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
- commonExtension->RemoveTrackingList = (PVOID) -1;
- #endif
- //
- // Acquire the lock once. This reference will be released when the
- // remove IRP has been received.
- //
- ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
- //
- // Store a pointer to the driver extension so we don't have to do
- // lookups to get it.
- //
- commonExtension->DriverExtension = driverExtension;
- //
- // Fill in entry points
- //
- commonExtension->DevInfo = devInfo;
- //
- // Initialize some of the common values in the structure
- //
- commonExtension->DeviceObject = deviceObject;
- commonExtension->LowerDeviceObject = NULL;
- if(IsFdo) {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
- commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
- //
- // Set the initial device object flags.
- //
- SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
- //
- // Clear the PDO list
- //
- commonExtension->ChildList = NULL;
- commonExtension->DriverData =
- ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
- if(isPartitionable) {
- commonExtension->PartitionNumber = 0;
- } else {
- commonExtension->PartitionNumber = (ULONG) (-1L);
- }
- fdoExtension->DevicePowerState = PowerDeviceD0;
- KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
- SynchronizationEvent,
- TRUE);
- KeInitializeEvent(&fdoExtension->ChildLock,
- SynchronizationEvent,
- TRUE);
- status = ClasspAllocateReleaseRequest(deviceObject);
- if(!NT_SUCCESS(status)) {
- IoDeleteDevice(deviceObject);
- *DeviceObject = NULL;
- if (ntUnicodeString.Buffer != NULL) {
- DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
- ExFreePool(ntUnicodeString.Buffer);
- RtlInitUnicodeString(&ntUnicodeString, NULL);
- }
- return status;
- }
- } else {
- PPHYSICAL_DEVICE_EXTENSION pdoExtension =
- deviceObject->DeviceExtension;
- PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
- LowerDevice->DeviceExtension;
- SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
- commonExtension->PartitionZeroExtension = p0Extension;
- //
- // Stick this onto the PDO list
- //
- ClassAddChild(p0Extension, pdoExtension, TRUE);
- commonExtension->DriverData = (PVOID) (pdoExtension + 1);
- //
- // Get the top of stack for the lower device - this allows
- // filters to get stuck in between the partitions and the
- // physical disk.
- //
- commonExtension->LowerDeviceObject =
- IoGetAttachedDeviceReference(LowerDevice);
- //
- // Pnp will keep a reference to the lower device object long
- // after this partition has been deleted. Dereference now so
- // we don't have to deal with it later.
- //
- ObDereferenceObject(commonExtension->LowerDeviceObject);
- }
- KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
- commonExtension->IsFdo = IsFdo;
- commonExtension->DeviceName = ntUnicodeString;
- commonExtension->PreviousState = 0xff;
- InitializeDictionary(&(commonExtension->FileObjectDictionary));
- commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
- }
- *DeviceObject = deviceObject;
- return status;
- } // end ClassCreateDeviceObject()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassClaimDevice()
- Routine Description:
- This function claims a device in the port driver. The port driver object
- is updated with the correct driver object if the device is successfully
- claimed.
- Arguments:
- LowerDeviceObject - Supplies the base port device object.
- Release - Indicates the logical unit should be released rather than claimed.
- Return Value:
- Returns a status indicating success or failure of the operation.
- --*/
- NTSTATUS
- ClassClaimDevice(
- IN PDEVICE_OBJECT LowerDeviceObject,
- IN BOOLEAN Release
- )
- {
- IO_STATUS_BLOCK ioStatus;
- PIRP irp;
- PIO_STACK_LOCATION irpStack;
- KEVENT event;
- NTSTATUS status;
- SCSI_REQUEST_BLOCK srb;
- PAGED_CODE();
- //
- // Clear the SRB fields.
- //
- RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
- //
- // Write length to SRB.
- //
- srb.Length = sizeof(SCSI_REQUEST_BLOCK);
- srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
- SRB_FUNCTION_CLAIM_DEVICE;
- //
- // Set the event object to the unsignaled state.
- // It will be used to signal request completion
- //
- KeInitializeEvent(&event, SynchronizationEvent, FALSE);
- //
- // Build synchronous request with no transfer.
- //
- irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
- LowerDeviceObject,
- NULL,
- 0,
- NULL,
- 0,
- TRUE,
- &event,
- &ioStatus);
- if (irp == NULL) {
- DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- irpStack = IoGetNextIrpStackLocation(irp);
- //
- // Save SRB address in next stack for port driver.
- //
- irpStack->Parameters.Scsi.Srb = &srb;
- //
- // Set up IRP Address.
- //
- srb.OriginalRequest = irp;
- //
- // Call the port driver with the request and wait for it to complete.
- //
- status = IoCallDriver(LowerDeviceObject, irp);
- if (status == STATUS_PENDING) {
- KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
- status = ioStatus.Status;
- }
- //
- // If this is a release request, then just decrement the reference count
- // and return. The status does not matter.
- //
- if (Release) {
- // ObDereferenceObject(LowerDeviceObject);
- return STATUS_SUCCESS;
- }
- if (!NT_SUCCESS(status)) {
- return status;
- }
- ASSERT(srb.DataBuffer != NULL);
- ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
- return status;
- } // end ClassClaimDevice()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassInternalIoControl()
- Routine Description:
- This routine passes internal device controls to the port driver.
- Internal device controls are used by higher level drivers both for ioctls
- and to pass through scsi requests.
- If the IoControlCode does not match any of the handled ioctls and is
- a valid system address then the request will be treated as an SRB and
- passed down to the lower driver. If the IoControlCode is not a valid
- system address the ioctl will be failed.
- Callers must therefore be extremely cautious to pass correct, initialized
- values to this function.
- Arguments:
- DeviceObject - Supplies a pointer to the device object for this request.
- Irp - Supplies the Irp making the request.
- Return Value:
- Returns back a STATUS_PENDING or a completion status.
- --*/
- NTSTATUS
- ClassInternalIoControl(
- IN PDEVICE_OBJECT DeviceObject,
- IN PIRP Irp
- )
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
- PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
- ULONG isRemoved;
- PSCSI_REQUEST_BLOCK srb;
- isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
- if(isRemoved) {
- Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
- return STATUS_DEVICE_DOES_NOT_EXIST;
- }
- //
- // Get a pointer to the SRB.
- //
- srb = irpStack->Parameters.Scsi.Srb;
- //
- // Set the parameters in the next stack location.
- //
- if(commonExtension->IsFdo) {
- nextStack->Parameters.Scsi.Srb = srb;
- nextStack->MajorFunction = IRP_MJ_SCSI;
- nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
- } else {
- IoCopyCurrentIrpStackLocationToNext(Irp);
- }
- ClassReleaseRemoveLock(DeviceObject, Irp);
- return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
- } // end ClassInternalIoControl()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassQueryTimeOutRegistryValue()
- Routine Description:
- This routine determines whether a reg key for a user-specified timeout
- value exists. This should be called at initialization time.
- Arguments:
- DeviceObject - Pointer to the device object we are retrieving the timeout
- value for
- Return Value:
- None, but it sets a new default timeout for a class of devices.
- --*/
- ULONG
- ClassQueryTimeOutRegistryValue(
- IN PDEVICE_OBJECT DeviceObject
- )
- {
- //
- // Find the appropriate reg. key
- //
- PCLASS_DRIVER_EXTENSION
- driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
- PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
- PWSTR path;
- NTSTATUS status;
- LONG timeOut = 0;
- ULONG zero = 0;
- ULONG size;
- PAGED_CODE();
- if (!registryPath) {
- return 0;
- }
- parameters = ExAllocatePoolWithTag(NonPagedPool,
- sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
- '1BcS');
- if (!parameters) {
- return 0;
- }
- size = registryPath->MaximumLength + sizeof(WCHAR);
- path = ExAllocatePoolWithTag(NonPagedPool, size, '2BcS');
- if (!path) {
- ExFreePool(parameters);
- return 0;
- }
- RtlZeroMemory(path,size);
- RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
- //
- // Check for the Timeout value.
- //
- RtlZeroMemory(parameters,
- (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
- parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
- parameters[0].Name = L"TimeOutValue";
- parameters[0].EntryContext = &timeOut;
- parameters[0].DefaultType = REG_DWORD;
- parameters[0].DefaultData = &zero;
- parameters[0].DefaultLength = sizeof(ULONG);
- status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
- path,
- parameters,
- NULL,
- NULL);
- if (!(NT_SUCCESS(status))) {
- timeOut = 0;
- }
- ExFreePool(parameters);
- ExFreePool(path);
- DebugPrint((2,
- "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
- timeOut));
- return timeOut;
- } // end ClassQueryTimeOutRegistryValue()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
- Routine Description:
- This routine executes when the port driver has completed a check verify
- ioctl. It will set the status of the master Irp, copy the media change
- count and complete the request.
- Arguments:
- Fdo - Supplies the functional device object which represents the logical unit.
- Irp - Supplies the Irp which has completed.
- Context - NULL
- Return Value:
- NT status
- --*/
- NTSTATUS
- ClassCheckVerifyComplete(
- IN PDEVICE_OBJECT Fdo,
- IN PIRP Irp,
- IN PVOID Context
- )
- {
- PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PIRP originalIrp;
- ASSERT_FDO(Fdo);
- originalIrp = irpStack->Parameters.Others.Argument1;
- //
- // Copy the media change count and status
- //
- *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
- fdoExtension->MediaChangeCount;
- DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
- "device %d is %lx - saved as %lx\n",
- fdoExtension->DeviceNumber,
- fdoExtension->MediaChangeCount,
- *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
- originalIrp->IoStatus.Status = Irp->IoStatus.Status;
- originalIrp->IoStatus.Information = sizeof(ULONG);
- ClassReleaseRemoveLock(Fdo, originalIrp);
- ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
- IoFreeIrp(Irp);
- return STATUS_MORE_PROCESSING_REQUIRED;
- } // end ClassCheckVerifyComplete()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassGetDescriptor()
- Routine Description:
- This routine will perform a query for the specified property id and will
- allocate a non-paged buffer to store the data in. It is the responsibility
- of the caller to ensure that this buffer is freed.
- This routine must be run at IRQL_PASSIVE_LEVEL
- Arguments:
- DeviceObject - the device to query
- DeviceInfo - a location to store a pointer to the buffer we allocate
- Return Value:
- status
- if status is unsuccessful *DeviceInfo will be set to NULL, else the
- buffer allocated on behalf of the caller.
- --*/
- NTSTATUS
- ClassGetDescriptor(
- IN PDEVICE_OBJECT DeviceObject,
- IN PSTORAGE_PROPERTY_ID PropertyId,
- OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
- )
- {
- STORAGE_PROPERTY_QUERY query;
- IO_STATUS_BLOCK ioStatus;
- PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
- ULONG length;
- UCHAR pass = 0;
- PAGED_CODE();
- //
- // Set the passed-in descriptor pointer to NULL as default
- //
- *Descriptor = NULL;
- RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
- query.PropertyId = *PropertyId;
- query.QueryType = PropertyStandardQuery;
- //
- // On the first pass we just want to get the first few
- // bytes of the descriptor so we can read it's size
- //
- descriptor = (PVOID)&query;
- ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
- ClassSendDeviceIoControlSynchronous(
- IOCTL_STORAGE_QUERY_PROPERTY,
- DeviceObject,
- &query,
- sizeof(STORAGE_PROPERTY_QUERY),
- sizeof(ULONG) * 2,
- FALSE,
- &ioStatus
- );
- if(!NT_SUCCESS(ioStatus.Status)) {
- DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
- "query properties #1\n", ioStatus.Status));
- return ioStatus.Status;
- }
- if (descriptor->Size == 0) {
- //
- // This DebugPrint is to help third-party driver writers
- //
- DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
- "%x\n", ioStatus.Status));
- return STATUS_UNSUCCESSFUL;
- }
- //
- // This time we know how much data there is so we can
- // allocate a buffer of the correct size
- //
- length = descriptor->Size;
- descriptor = ExAllocatePoolWithTag(NonPagedPool, length, '4BcS');
- if(descriptor == NULL) {
- DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
- "(%d bytes)\n", length));
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- //
- // setup the query again, as it was overwritten above
- //
- RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
- query.PropertyId = *PropertyId;
- query.QueryType = PropertyStandardQuery;
- //
- // copy the input to the new outputbuffer
- //
- RtlCopyMemory(descriptor,
- &query,
- sizeof(STORAGE_PROPERTY_QUERY)
- );
- ClassSendDeviceIoControlSynchronous(
- IOCTL_STORAGE_QUERY_PROPERTY,
- DeviceObject,
- descriptor,
- sizeof(STORAGE_PROPERTY_QUERY),
- length,
- FALSE,
- &ioStatus
- );
- if(!NT_SUCCESS(ioStatus.Status)) {
- DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
- "query properties #1\n", ioStatus.Status));
- ExFreePool(descriptor);
- return ioStatus.Status;
- }
- //
- // return the memory we've allocated to the caller
- //
- *Descriptor = descriptor;
- return ioStatus.Status;
- } // end ClassGetDescriptor()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassSignalCompletion()
- Routine Description:
- This completion routine will signal the event given as context and then
- return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
- the responsibility of the routine waiting on the event to complete the
- request and free the event.
- 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
- --*/
- NTSTATUS
- ClassSignalCompletion(
- IN PDEVICE_OBJECT DeviceObject,
- IN PIRP Irp,
- IN PKEVENT Event
- )
- {
- KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
- return STATUS_MORE_PROCESSING_REQUIRED;
- } // end ClassSignalCompletion()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassPnpQueryFdoRelations()
- Routine Description:
- This routine will call the driver's enumeration routine to update the
- list of PDO's. It will then build a response to the
- IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
- the irp.
- Arguments:
- Fdo - a pointer to the functional device object we are enumerating
- Irp - a pointer to the enumeration request
- Return Value:
- status
- --*/
- NTSTATUS
- ClassPnpQueryFdoRelations(
- IN PDEVICE_OBJECT Fdo,
- IN PIRP Irp
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PCLASS_DRIVER_EXTENSION
- driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- NTSTATUS status;
- PAGED_CODE();
- //
- // If there's already an enumeration in progress then don't start another
- // one.
- //
- if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
- status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
- }
- Irp->IoStatus.Information = (ULONG_PTR) NULL;
- Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
- Fdo,
- BusRelations,
- (PDEVICE_RELATIONS*)&Irp->IoStatus.Information);
- InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
- return Irp->IoStatus.Status;
- } // end ClassPnpQueryFdoRelations()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassMarkChildrenMissing()
- Routine Description:
- This routine will call ClassMarkChildMissing() for all children.
- It acquires the ChildLock before calling ClassMarkChildMissing().
- Arguments:
- Fdo - the "bus's" device object, such as the disk FDO for non-removable
- disks with multiple partitions.
- Return Value:
- None
- --*/
- VOID
- ClassMarkChildrenMissing(
- IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
- )
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
- PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
- PAGED_CODE();
- ClassAcquireChildLock(Fdo);
- while (nextChild){
- PPHYSICAL_DEVICE_EXTENSION tmpChild;
- /*
- * ClassMarkChildMissing will also dequeue the child extension.
- * So get the next pointer before calling ClassMarkChildMissing.
- */
- tmpChild = nextChild;
- nextChild = tmpChild->CommonExtension.ChildList;
- ClassMarkChildMissing(tmpChild, FALSE);
- }
- ClassReleaseChildLock(Fdo);
- return;
- } // end ClassMarkChildrenMissing()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassMarkChildMissing()
- Routine Description:
- This routine will make an active child "missing." If the device has never
- been enumerated then it will be deleted on the spot. If the device has
- not been enumerated then it will be marked as missing so that we can
- not report it in the next device enumeration.
- Arguments:
- Child - the child device to be marked as missing.
- AcquireChildLock - TRUE if the child lock should be acquired before removing
- the missing child. FALSE if the child lock is already
- acquired by this thread.
- Return Value:
- returns whether or not the child device object has previously been reported
- to PNP.
- --*/
- BOOLEAN
- ClassMarkChildMissing(
- IN PPHYSICAL_DEVICE_EXTENSION Child,
- IN BOOLEAN AcquireChildLock
- )
- {
- BOOLEAN returnValue = Child->IsEnumerated;
- PAGED_CODE();
- ASSERT_PDO(Child->DeviceObject);
- Child->IsMissing = TRUE;
- //
- // Make sure this child is not in the active list.
- //
- ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
- Child,
- AcquireChildLock);
- if(Child->IsEnumerated == FALSE) {
- ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
- }
- return returnValue;
- } // end ClassMarkChildMissing()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassRetrieveDeviceRelations()
- Routine Description:
- This routine will allocate a buffer to hold the specified list of
- relations. It will then fill in the list with referenced device pointers
- and will return the request.
- Arguments:
- Fdo - pointer to the FDO being queried
- RelationType - what type of relations are being queried
- DeviceRelations - a location to store a pointer to the response
- Return Value:
- status
- --*/
- NTSTATUS
- ClassRetrieveDeviceRelations(
- IN PDEVICE_OBJECT Fdo,
- IN DEVICE_RELATION_TYPE RelationType,
- OUT PDEVICE_RELATIONS *DeviceRelations
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- ULONG count = 0;
- ULONG i;
- PPHYSICAL_DEVICE_EXTENSION nextChild;
- ULONG relationsSize;
- PDEVICE_RELATIONS deviceRelations = NULL;
- NTSTATUS status;
- PAGED_CODE();
- ClassAcquireChildLock(fdoExtension);
- nextChild = fdoExtension->CommonExtension.ChildList;
- //
- // Count the number of PDO's attached to this disk
- //
- while(nextChild != NULL) {
- PCOMMON_DEVICE_EXTENSION commonExtension;
- commonExtension = &(nextChild->CommonExtension);
- ASSERTMSG("ClassPnp internal error: missing child on active list\n",
- (nextChild->IsMissing == FALSE));
- nextChild = commonExtension->ChildList;
- count++;
- };
- relationsSize = (sizeof(DEVICE_RELATIONS) +
- (count * sizeof(PDEVICE_OBJECT)));
- deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
- if(deviceRelations == NULL) {
- DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
- "%d bytes for device relations\n", relationsSize));
- ClassReleaseChildLock(fdoExtension);
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- RtlZeroMemory(deviceRelations, relationsSize);
- nextChild = fdoExtension->CommonExtension.ChildList;
- i = count - 1;
- while(nextChild != NULL) {
- PCOMMON_DEVICE_EXTENSION commonExtension;
- commonExtension = &(nextChild->CommonExtension);
- ASSERTMSG("ClassPnp internal error: missing child on active list\n",
- (nextChild->IsMissing == FALSE));
- deviceRelations->Objects[i--] = nextChild->DeviceObject;
- status = ObReferenceObjectByPointer(
- nextChild->DeviceObject,
- 0,
- NULL,
- KernelMode);
- ASSERT(NT_SUCCESS(status));
- nextChild->IsEnumerated = TRUE;
- nextChild = commonExtension->ChildList;
- }
- ASSERTMSG("Child list has changed: ", i == -1);
- deviceRelations->Count = count;
- *DeviceRelations = deviceRelations;
- ClassReleaseChildLock(fdoExtension);
- return STATUS_SUCCESS;
- } // end ClassRetrieveDeviceRelations()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassGetPdoId()
- Routine Description:
- This routine will call into the driver to retrieve a copy of one of it's
- id strings.
- Arguments:
- Pdo - a pointer to the pdo being queried
- IdType - which type of id string is being queried
- IdString - an allocated unicode string structure which the driver
- can fill in.
- Return Value:
- status
- --*/
- NTSTATUS
- ClassGetPdoId(
- IN PDEVICE_OBJECT Pdo,
- IN BUS_QUERY_ID_TYPE IdType,
- IN PUNICODE_STRING IdString
- )
- {
- PCLASS_DRIVER_EXTENSION
- driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- ASSERT_PDO(Pdo);
- ASSERT(driverExtension->InitData.ClassQueryId);
- PAGED_CODE();
- return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
- } // end ClassGetPdoId()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassQueryPnpCapabilities()
- Routine Description:
- This routine will call into the class driver to retrieve it's pnp
- capabilities.
- Arguments:
- PhysicalDeviceObject - The physical device object to retrieve properties
- for.
- Return Value:
- status
- --*/
- NTSTATUS
- ClassQueryPnpCapabilities(
- IN PDEVICE_OBJECT DeviceObject,
- IN PDEVICE_CAPABILITIES Capabilities
- )
- {
- PCLASS_DRIVER_EXTENSION driverExtension =
- ClassGetDriverExtension(DeviceObject->DriverObject);
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
- PAGED_CODE();
- ASSERT(DeviceObject);
- ASSERT(Capabilities);
- if(commonExtension->IsFdo) {
- queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
- } else {
- queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
- }
- if(queryRoutine) {
- return queryRoutine(DeviceObject,
- Capabilities);
- } else {
- return STATUS_NOT_IMPLEMENTED;
- }
- } // end ClassQueryPnpCapabilities()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassInvalidateBusRelations()
- Routine Description:
- This routine re-enumerates the devices on the "bus". It will call into
- the driver's ClassEnumerate routine to update the device objects
- immediately. It will then schedule a bus re-enumeration for pnp by calling
- IoInvalidateDeviceRelations.
- Arguments:
- Fdo - a pointer to the functional device object for this bus
- Return Value:
- none
- --*/
- VOID
- ClassInvalidateBusRelations(
- IN PDEVICE_OBJECT Fdo
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PCLASS_DRIVER_EXTENSION
- driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- NTSTATUS status = STATUS_SUCCESS;
- PAGED_CODE();
- ASSERT_FDO(Fdo);
- ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
- if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
- status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
- }
- InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
- if(!NT_SUCCESS(status)) {
- DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
- "returned %lx\n", status));
- }
- IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
- return;
- } // end ClassInvalidateBusRelations()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
- Routine Description:
- This routine is called to handle the "removal" of a device. It will
- forward the request downwards if necesssary, call into the driver
- to release any necessary resources (memory, events, etc) and then
- will delete the device object.
- Arguments:
- DeviceObject - a pointer to the device object being removed
- RemoveType - indicates what type of remove this is (regular or surprise).
- Return Value:
- status
- --*/
- NTSTATUS
- ClassRemoveDevice(
- IN PDEVICE_OBJECT DeviceObject,
- IN UCHAR RemoveType
- )
- {
- PCLASS_DRIVER_EXTENSION
- driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
- CLASS_DRIVER_EXTENSION_KEY);
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
- PCLASS_WMI_INFO classWmiInfo;
- BOOLEAN proceedWithRemove = TRUE;
- NTSTATUS status;
- PAGED_CODE();
- commonExtension->IsRemoved = REMOVE_PENDING;
- /*
- * Deregister from WMI.
- */
- classWmiInfo = commonExtension->IsFdo ?
- &driverExtension->InitData.FdoData.ClassWmiInfo :
- &driverExtension->InitData.PdoData.ClassWmiInfo;
- if (classWmiInfo->GuidRegInfo){
- status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
- DBGTRACE(ClassDebugInfo, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
- }
- /*
- * If we exposed a "shingle" (a named device interface openable by CreateFile)
- * then delete it now.
- */
- if (commonExtension->MountedDeviceInterfaceName.Buffer){
- IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
- RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
- RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
- }
- //
- // If this is a surprise removal we leave the device around - which means
- // we don't have to (or want to) drop the remove lock and wait for pending
- // requests to complete.
- //
- if (RemoveType == IRP_MN_REMOVE_DEVICE){
- //
- // Release the lock we acquired when the device object was created.
- //
- ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
- DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
- commonExtension->RemoveLock));
- KeWaitForSingleObject(&commonExtension->RemoveEvent,
- Executive,
- KernelMode,
- FALSE,
- NULL);
- DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject));
- if(commonExtension->IsFdo) {
- DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
- "remove request.\n", DeviceObject));
- }
- else {
- PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
- if (pdoExtension->IsMissing){
- /*
- * The child partition PDO is missing, so we are going to go ahead
- * and delete it for the remove.
- */
- DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
- }
- else {
- /*
- * We got a remove for a child partition PDO which is not actually missing.
- * So we will NOT actually delete it.
- */
- DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
- //
- // Reacquire the remove lock for the next time this comes around.
- //
- ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
- //
- // the device wasn't missing so it's not really been removed.
- //
- commonExtension->IsRemoved = NO_REMOVE;
- IoInvalidateDeviceRelations(
- commonExtension->PartitionZeroExtension->LowerPdo,
- BusRelations);
- proceedWithRemove = FALSE;
- }
- }
- }
- if (proceedWithRemove){
- /*
- * Call the class driver's remove handler.
- * All this is supposed to do is clean up its data and device interfaces.
- */
- ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
- status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
- ASSERT(NT_SUCCESS(status));
- status = STATUS_SUCCESS;
- if (commonExtension->IsFdo){
- PDEVICE_OBJECT pdo;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
- ClasspDisableTimer(fdoExtension->DeviceObject);
- if (RemoveType == IRP_MN_REMOVE_DEVICE){
- PPHYSICAL_DEVICE_EXTENSION child;
- //
- // Cleanup the media detection resources now that the class driver
- // has stopped it's timer (if any) and we can be sure they won't
- // call us to do detection again.
- //
- ClassCleanupMediaChangeDetection(fdoExtension);
- //
- // Cleanup any Failure Prediction stuff
- //
- if (fdoExtension->FailurePredictionInfo) {
- ExFreePool(fdoExtension->FailurePredictionInfo);
- fdoExtension->FailurePredictionInfo = NULL;
- }
- /*
- * Ordinarily all child PDOs will be removed by the time
- * that the parent gets the REMOVE_DEVICE.
- * However, if a child PDO has been created but has not
- * been announced in a QueryDeviceRelations, then it is
- * just a private data structure unknown to pnp, and we have
- * to delete it ourselves.
- */
- ClassAcquireChildLock(fdoExtension);
- while (child = ClassRemoveChild(fdoExtension, NULL, FALSE)){
- //
- // Yank the pdo. This routine will unlink the device from the
- // pdo list so NextPdo will point to the next one when it's
- // complete.
- //
- child->IsMissing = TRUE;
- ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
- }
- ClassReleaseChildLock(fdoExtension);
- }
- else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
- /*
- * This is a surprise-remove on the parent FDO.
- * We will mark the child PDOs as missing so that they
- * will actually get deleted when they get a REMOVE_DEVICE.
- */
- ClassMarkChildrenMissing(fdoExtension);
- }
- ClasspFreeReleaseRequest(DeviceObject);
- if (RemoveType == IRP_MN_REMOVE_DEVICE){
- //
- // Free FDO-specific data structs
- //
- if (fdoExtension->PrivateFdoData){
- DestroyAllTransferPackets(DeviceObject);
- ExFreePool(fdoExtension->PrivateFdoData);
- fdoExtension->PrivateFdoData = NULL;
- }
- if (commonExtension->DeviceName.Buffer) {
- ExFreePool(commonExtension->DeviceName.Buffer);
- RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
- }
- if (fdoExtension->AdapterDescriptor) {
- ExFreePool(fdoExtension->AdapterDescriptor);
- fdoExtension->AdapterDescriptor = NULL;
- }
- if (fdoExtension->DeviceDescriptor) {
- ExFreePool(fdoExtension->DeviceDescriptor);
- fdoExtension->DeviceDescriptor = NULL;
- }
- //
- // Detach our device object from the stack - there's no reason
- // to hold off our cleanup any longer.
- //
- IoDetachDevice(lowerDeviceObject);
- }
- }
- else {
- /*
- * This is a child partition PDO.
- * We have already determined that it was previously marked
- * as missing. So if this is a REMOVE_DEVICE, we will actually
- * delete it.
- */
- if (RemoveType == IRP_MN_REMOVE_DEVICE){
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
- commonExtension->PartitionZeroExtension;
- PPHYSICAL_DEVICE_EXTENSION pdoExtension =
- (PPHYSICAL_DEVICE_EXTENSION) commonExtension;
- //
- // See if this device is in the child list (if this was a suprise
- // removal it might be) and remove it.
- //
- ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
- }
- }
- commonExtension->PartitionLength.QuadPart = 0;
- if (RemoveType == IRP_MN_REMOVE_DEVICE){
- IoDeleteDevice(DeviceObject);
- }
- }
- return STATUS_SUCCESS;
- } // end ClassRemoveDevice()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassGetDriverExtension()
- Routine Description:
- This routine will return the classpnp's driver extension.
- Arguments:
- DriverObject - the driver object for which to get classpnp's extension
- Return Value:
- Either NULL if none, or a pointer to the driver extension
- --*/
- PCLASS_DRIVER_EXTENSION
- ClassGetDriverExtension(
- IN PDRIVER_OBJECT DriverObject
- )
- {
- return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
- } // end ClassGetDriverExtension()
- /*++////////////////////////////////////////////////////////////////////////////
- ClasspStartIo()
- Routine Description:
- This routine wraps the class driver's start io routine. If the device
- is being removed it will complete any requests with
- STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
- Arguments:
- Return Value:
- none
- --*/
- VOID
- ClasspStartIo(
- IN PDEVICE_OBJECT DeviceObject,
- IN PIRP Irp
- )
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- //
- // We're already holding the remove lock so just check the variable and
- // see what's going on.
- //
- if(commonExtension->IsRemoved) {
- Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
- ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
- ClassReleaseRemoveLock(DeviceObject, Irp);
- ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
- IoStartNextPacket(DeviceObject, FALSE);
- ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
- return;
- }
- commonExtension->DriverExtension->InitData.ClassStartIo(
- DeviceObject,
- Irp);
- return;
- } // ClasspStartIo()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassUpdateInformationInRegistry()
- Routine Description:
- This routine has knowledge about the layout of the device map information
- in the registry. It will update this information to include a value
- entry specifying the dos device name that is assumed to get assigned
- to this NT device name. For more information on this assigning of the
- dos device name look in the drive support routine in the hal that assigns
- all dos names.
- Since some versions of some device's firmware did not work and some
- vendors did not bother to follow the specification, the entire inquiry
- information must also be stored in the registry so than someone can
- figure out the firmware version.
- Arguments:
- DeviceObject - A pointer to the device object for the tape device.
- Return Value:
- None
- --*/
- VOID
- ClassUpdateInformationInRegistry(
- IN PDEVICE_OBJECT Fdo,
- IN PCHAR DeviceName,
- IN ULONG DeviceNumber,
- IN PINQUIRYDATA InquiryData,
- IN ULONG InquiryDataLength
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
- NTSTATUS status;
- SCSI_ADDRESS scsiAddress;
- OBJECT_ATTRIBUTES objectAttributes;
- PUCHAR buffer;
- STRING string;
- UNICODE_STRING unicodeName;
- UNICODE_STRING unicodeRegistryPath;
- UNICODE_STRING unicodeData;
- HANDLE targetKey;
- IO_STATUS_BLOCK ioStatus;
- PAGED_CODE();
- ASSERT(DeviceName);
- fdoExtension = Fdo->DeviceExtension;
- buffer = NULL;
- targetKey = NULL;
- RtlZeroMemory(&unicodeName, sizeof(UNICODE_STRING));
- RtlZeroMemory(&unicodeData, sizeof(UNICODE_STRING));
- RtlZeroMemory(&unicodeRegistryPath, sizeof(UNICODE_STRING));
- TRY {
- //
- // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
- //
- ClassSendDeviceIoControlSynchronous(
- IOCTL_SCSI_GET_ADDRESS,
- Fdo,
- &scsiAddress,
- 0,
- sizeof(SCSI_ADDRESS),
- FALSE,
- &ioStatus
- );
- if (!NT_SUCCESS(ioStatus.Status)) {
- status = ioStatus.Status;
- DebugPrint((1,
- "UpdateInformationInRegistry: Get Address failed %lx\n",
- status));
- LEAVE;
- } else {
- DebugPrint((1,
- "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
- scsiAddress.PortNumber,
- scsiAddress.PathId,
- scsiAddress.TargetId,
- scsiAddress.Lun));
- }
- //
- // Allocate a buffer for the reg. spooge.
- //
- buffer = ExAllocatePoolWithTag(PagedPool, 1024, '6BcS');
- if (buffer == NULL) {
- //
- // There is not return value for this. Since this is done at
- // claim device time (currently only system initialization) getting
- // the registry information correct will be the least of the worries.
- //
- LEAVE;
- }
- sprintf(buffer,
- "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
- scsiAddress.PortNumber,
- scsiAddress.PathId,
- scsiAddress.TargetId,
- scsiAddress.Lun);
- RtlInitString(&string, buffer);
- status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
- &string,
- TRUE);
- if (!NT_SUCCESS(status)) {
- LEAVE;
- }
- //
- // Open the registry key for the scsi information for this
- // scsibus, target, lun.
- //
- InitializeObjectAttributes(&objectAttributes,
- &unicodeRegistryPath,
- OBJ_CASE_INSENSITIVE,
- NULL,
- NULL);
- status = ZwOpenKey(&targetKey,
- KEY_READ | KEY_WRITE,
- &objectAttributes);
- if (!NT_SUCCESS(status)) {
- LEAVE;
- }
- //
- // Now construct and attempt to create the registry value
- // specifying the device name in the appropriate place in the
- // device map.
- //
- RtlInitUnicodeString(&unicodeName, L"DeviceName");
- sprintf(buffer, "%s%d", DeviceName, DeviceNumber);
- RtlInitString(&string, buffer);
- status = RtlAnsiStringToUnicodeString(&unicodeData,
- &string,
- TRUE);
- if (NT_SUCCESS(status)) {
- status = ZwSetValueKey(targetKey,
- &unicodeName,
- 0,
- REG_SZ,
- unicodeData.Buffer,
- unicodeData.Length);
- }
- //
- // if they sent in data, update the registry
- //
- if (InquiryDataLength) {
- ASSERT(InquiryData);
- RtlInitUnicodeString(&unicodeName, L"InquiryData");
- status = ZwSetValueKey(targetKey,
- &unicodeName,
- 0,
- REG_BINARY,
- InquiryData,
- InquiryDataLength);
- }
- // that's all, except to clean up.
- } FINALLY {
- if (unicodeData.Buffer) {
- RtlFreeUnicodeString(&unicodeData);
- }
- if (unicodeRegistryPath.Buffer) {
- RtlFreeUnicodeString(&unicodeRegistryPath);
- }
- if (targetKey) {
- ZwClose(targetKey);
- }
- if (buffer) {
- ExFreePool(buffer);
- }
- }
- } // end ClassUpdateInformationInRegistry()
- /*++////////////////////////////////////////////////////////////////////////////
- ClasspSendSynchronousCompletion()
- Routine Description:
- This completion routine will set the user event in the irp after
- freeing the irp and the associated MDL (if any).
- Arguments:
- DeviceObject - the device object which requested the completion routine
- Irp - the irp being completed
- Context - unused
- Return Value:
- STATUS_MORE_PROCESSING_REQUIRED
- --*/
- NTSTATUS
- ClasspSendSynchronousCompletion(
- IN PDEVICE_OBJECT DeviceObject,
- IN PIRP Irp,
- IN PVOID Context
- )
- {
- DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
- DeviceObject, Irp, Context));
- //
- // First set the status and information fields in the io status block
- // provided by the caller.
- //
- *(Irp->UserIosb) = Irp->IoStatus;
- //
- // Unlock the pages for the data buffer.
- //
- if(Irp->MdlAddress) {
- MmUnlockPages(Irp->MdlAddress);
- IoFreeMdl(Irp->MdlAddress);
- }
- //
- // Signal the caller's event.
- //
- KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
- //
- // Free the MDL and the IRP.
- //
- IoFreeIrp(Irp);
- return STATUS_MORE_PROCESSING_REQUIRED;
- } // end ClasspSendSynchronousCompletion()
- /*++
- ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
- --*/
- VOID
- ClasspRegisterMountedDeviceInterface(
- IN PDEVICE_OBJECT DeviceObject
- )
- {
- PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
- BOOLEAN isFdo = commonExtension->IsFdo;
- PDEVICE_OBJECT pdo;
- UNICODE_STRING interfaceName;
- NTSTATUS status;
- if(isFdo) {
- PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
- functionalExtension =
- (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
- pdo = functionalExtension->LowerPdo;
- } else {
- pdo = DeviceObject;
- }
- status = IoRegisterDeviceInterface(
- pdo,
- &MOUNTDEV_MOUNTED_DEVICE_GUID,
- NULL,
- &interfaceName
- );
- if(NT_SUCCESS(status)) {
- //
- // Copy the interface name before setting the interface state - the
- // name is needed by the components we notify.
- //
- commonExtension->MountedDeviceInterfaceName = interfaceName;
- status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
- if(!NT_SUCCESS(status)) {
- RtlFreeUnicodeString(&interfaceName);
- }
- }
- if(!NT_SUCCESS(status)) {
- RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
- NULL);
- }
- return;
- } // end ClasspRegisterMountedDeviceInterface()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassSendDeviceIoControlSynchronous()
- Routine Description:
- This routine is based upon IoBuildDeviceIoControlRequest(). It has been
- modified to reduce code and memory by not double-buffering the io, using
- the same buffer for both input and output, allocating and deallocating
- the mdl on behalf of the caller, and waiting for the io to complete.
- This routine also works around the rare cases in which APC's are disabled.
- Since IoBuildDeviceIoControl() used APC's to signal completion, this had
- led to a number of difficult-to-detect hangs, where the irp was completed,
- but the event passed to IoBuild..() was still being waited upon by the
- caller.
- Arguments:
- IoControlCode - the IOCTL to send
- TargetDeviceObject - the device object that should handle the ioctl
- Buffer - the input and output buffer, or NULL if no input/output
- InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
- OutputBufferLength - the number of bytes to be filled in upon success
- InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
- IoStatus - the status block that contains the results of the operation
- Return Value:
- --*/
- VOID
- ClassSendDeviceIoControlSynchronous(
- IN ULONG IoControlCode,
- IN PDEVICE_OBJECT TargetDeviceObject,
- IN OUT PVOID Buffer OPTIONAL,
- IN ULONG InputBufferLength,
- IN ULONG OutputBufferLength,
- IN BOOLEAN InternalDeviceIoControl,
- OUT PIO_STATUS_BLOCK IoStatus
- )
- {
- PIRP irp;
- PIO_STACK_LOCATION irpSp;
- ULONG method;
- PAGED_CODE();
- irp = NULL;
- method = IoControlCode & 3;
- #if DBG // Begin Argument Checking (nop in fre version)
- ASSERT(ARGUMENT_PRESENT(IoStatus));
- if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
- ASSERT(ARGUMENT_PRESENT(Buffer));
- }
- else {
- ASSERT(!ARGUMENT_PRESENT(Buffer));
- }
- #endif
- //
- // Begin by allocating the IRP for this request. Do not charge quota to
- // the current process for this IRP.
- //
- irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
- if (!irp) {
- (*IoStatus).Information = 0;
- (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
- return;
- }
- //
- // Get a pointer to the stack location of the first driver which will be
- // invoked. This is where the function codes and the parameters are set.
- //
- irpSp = IoGetNextIrpStackLocation(irp);
- //
- // Set the major function code based on the type of device I/O control
- // function the caller has specified.
- //
- if (InternalDeviceIoControl) {
- irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
- } else {
- irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
- }
- //
- // Copy the caller's parameters to the service-specific portion of the
- // IRP for those parameters that are the same for all four methods.
- //
- irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
- irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
- irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
- //
- // Get the method bits from the I/O control code to determine how the
- // buffers are to be passed to the driver.
- //
- switch (method) {
- // case 0
- case METHOD_BUFFERED: {
- if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
- irp->AssociatedIrp.SystemBuffer =
- ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
- max(InputBufferLength, OutputBufferLength),
- CLASS_TAG_DEVICE_CONTROL
- );
- if (irp->AssociatedIrp.SystemBuffer == NULL) {
- IoFreeIrp(irp);
- (*IoStatus).Information = 0;
- (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
- return;
- }
- if (InputBufferLength != 0) {
- RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,
- Buffer,
- InputBufferLength);
- }
- } // end of buffering
- irp->UserBuffer = Buffer;
- break;
- }
- // case 1, case 2
- case METHOD_IN_DIRECT:
- case METHOD_OUT_DIRECT: {
- if (InputBufferLength != 0) {
- irp->AssociatedIrp.SystemBuffer = Buffer;
- }
- if (OutputBufferLength != 0) {
- irp->MdlAddress = IoAllocateMdl(Buffer,
- OutputBufferLength,
- FALSE, FALSE,
- (PIRP) NULL);
- if (irp->MdlAddress == NULL) {
- IoFreeIrp(irp);
- (*IoStatus).Information = 0;
- (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
- return;
- }
- if (method == METHOD_IN_DIRECT) {
- MmProbeAndLockPages(irp->MdlAddress,
- KernelMode,
- IoReadAccess);
- } else if (method == METHOD_OUT_DIRECT) {
- MmProbeAndLockPages(irp->MdlAddress,
- KernelMode,
- IoWriteAccess);
- } else {
- ASSERT(!"If other methods reach here, code is out of date");
- }
- }
- break;
- }
- // case 3
- case METHOD_NEITHER: {
- ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
- IoStatus->Information = 0;
- IoStatus->Status = STATUS_NOT_SUPPORTED;
- return;
- break;
- }
- } // end of switch(method)
- irp->Tail.Overlay.Thread = PsGetCurrentThread();
- //
- // send the irp synchronously
- //
- ClassSendIrpSynchronous(TargetDeviceObject, irp);
- //
- // copy the iostatus block for the caller
- //
- *IoStatus = irp->IoStatus;
- //
- // free any allocated resources
- //
- switch (method) {
- case METHOD_BUFFERED: {
- ASSERT(irp->UserBuffer == Buffer);
- //
- // first copy the buffered result, if any
- // Note that there are no security implications in
- // not checking for success since only drivers can
- // call into this routine anyways...
- //
- if (OutputBufferLength != 0) {
- RtlCopyMemory(Buffer, // irp->UserBuffer
- irp->AssociatedIrp.SystemBuffer,
- OutputBufferLength
- );
- }
- //
- // then free the memory allocated to buffer the io
- //
- if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
- ExFreePool(irp->AssociatedIrp.SystemBuffer);
- irp->AssociatedIrp.SystemBuffer = NULL;
- }
- break;
- }
- case METHOD_IN_DIRECT:
- case METHOD_OUT_DIRECT: {
- //
- // we alloc a mdl if there is an output buffer specified
- // free it here after unlocking the pages
- //
- if (OutputBufferLength != 0) {
- ASSERT(irp->MdlAddress != NULL);
- MmUnlockPages(irp->MdlAddress);
- IoFreeMdl(irp->MdlAddress);
- irp->MdlAddress = (PMDL) NULL;
- }
- break;
- }
- case METHOD_NEITHER: {
- ASSERT(!"Code is out of date");
- break;
- }
- }
- //
- // we always have allocated an irp. free it here.
- //
- IoFreeIrp(irp);
- irp = (PIRP) NULL;
- //
- // return the io status block's status to the caller
- //
- return;
- } // end ClassSendDeviceIoControlSynchronous()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassForwardIrpSynchronous()
- Routine Description:
- Forwards a given irp to the next lower device object.
- Arguments:
- CommonExtension - the common class extension
- Irp - the request to forward down the stack
- Return Value:
- --*/
- NTSTATUS
- ClassForwardIrpSynchronous(
- IN PCOMMON_DEVICE_EXTENSION CommonExtension,
- IN PIRP Irp
- )
- {
- IoCopyCurrentIrpStackLocationToNext(Irp);
- return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
- } // end ClassForwardIrpSynchronous()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassSendIrpSynchronous()
- Routine Description:
- This routine sends the given irp to the given device object, and waits for
- it to complete. On debug versions, will print out a debug message and
- optionally assert for "lost" irps based upon classpnp's globals
- Arguments:
- TargetDeviceObject - the device object to handle this irp
- Irp - the request to be sent
- Return Value:
- --*/
- NTSTATUS
- ClassSendIrpSynchronous(
- IN PDEVICE_OBJECT TargetDeviceObject,
- IN PIRP Irp
- )
- {
- KEVENT event;
- NTSTATUS status;
- ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
- ASSERT(TargetDeviceObject != NULL);
- ASSERT(Irp != NULL);
- ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
- //
- // ISSUE-2000/02/20-henrygab What if APCs are disabled?
- // May need to enter critical section before IoCallDriver()
- // until the event is hit?
- //
- KeInitializeEvent(&event, SynchronizationEvent, FALSE);
- IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
- TRUE, TRUE, TRUE);
- status = IoCallDriver(TargetDeviceObject, Irp);
- if (status == STATUS_PENDING) {
- #if DBG
- LARGE_INTEGER timeout;
- timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
- ClasspnpGlobals.SecondsToWaitForIrps);
- do {
- status = KeWaitForSingleObject(&event,
- Executive,
- KernelMode,
- FALSE,
- &timeout);
- if (status == STATUS_TIMEOUT) {
- //
- // This DebugPrint should almost always be investigated by the
- // party who sent the irp and/or the current owner of the irp.
- // Synchronous Irps should not take this long (currently 30
- // seconds) without good reason. This points to a potentially
- // serious problem in the underlying device stack.
- //
- DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
- "complete within %x seconds\n",
- TargetDeviceObject, Irp,
- ClasspnpGlobals.SecondsToWaitForIrps
- ));
- if (ClasspnpGlobals.BreakOnLostIrps != 0) {
- ASSERT(!" - Irp failed to complete within 30 seconds - ");
- }
- }
- } while (status==STATUS_TIMEOUT);
- #else
- KeWaitForSingleObject(&event,
- Executive,
- KernelMode,
- FALSE,
- NULL);
- #endif
- status = Irp->IoStatus.Status;
- }
- return status;
- } // end ClassSendIrpSynchronous()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassGetVpb()
- Routine Description:
- This routine returns the current VPB (Volume Parameter Block) for the
- given device object.
- The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
- of DEVICE_OBJECT; hence this exported function.
- Arguments:
- DeviceObject - the device to get the VPB for
- Return Value:
- the VPB, or NULL if none.
- --*/
- PVPB
- ClassGetVpb(
- IN PDEVICE_OBJECT DeviceObject
- )
- {
- return DeviceObject->Vpb;
- } // end ClassGetVpb()
- /*++
- ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
- --*/
- NTSTATUS
- ClasspAllocateReleaseRequest(
- IN PDEVICE_OBJECT Fdo
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PIO_STACK_LOCATION irpStack;
- KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
- fdoExtension->ReleaseQueueNeeded = FALSE;
- fdoExtension->ReleaseQueueInProgress = FALSE;
- fdoExtension->ReleaseQueueIrpFromPool = FALSE;
- //
- // The class driver is responsible for allocating a properly sized irp,
- // or ClassReleaseQueue will attempt to do it on the first error.
- //
- fdoExtension->ReleaseQueueIrp = NULL;
- //
- // Write length to SRB.
- //
- fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
- return STATUS_SUCCESS;
- } // end ClasspAllocateReleaseRequest()
- /*++
- ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
- --*/
- VOID
- ClasspFreeReleaseRequest(
- IN PDEVICE_OBJECT Fdo
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- //KIRQL oldIrql;
- ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
- //
- // free anything the driver allocated
- //
- if (fdoExtension->ReleaseQueueIrp) {
- if (fdoExtension->ReleaseQueueIrpFromPool) {
- ExFreePool(fdoExtension->ReleaseQueueIrp);
- } else {
- IoFreeIrp(fdoExtension->ReleaseQueueIrp);
- }
- fdoExtension->ReleaseQueueIrp = NULL;
- }
- //
- // free anything that we allocated
- //
- if ((fdoExtension->PrivateFdoData) &&
- (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
- ExFreePool(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
- fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
- fdoExtension->PrivateFdoData->ReleaseQueueIrp = NULL;
- }
- return;
- } // end ClasspFreeReleaseRequest()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassReleaseQueue()
- Routine Description:
- This routine issues an internal device control command
- to the port driver to release a frozen queue. The call
- is issued asynchronously as ClassReleaseQueue will be invoked
- from the IO completion DPC (and will have no context to
- wait for a synchronous call to complete).
- This routine must be called with the remove lock held.
- Arguments:
- Fdo - The functional device object for the device with the frozen queue.
- Return Value:
- None.
- --*/
- VOID
- ClassReleaseQueue(
- IN PDEVICE_OBJECT Fdo
- )
- {
- ClasspReleaseQueue(Fdo, NULL);
- return;
- } // end ClassReleaseQueue()
- /*++////////////////////////////////////////////////////////////////////////////
- ClasspAllocateReleaseQueueIrp()
- Routine Description:
- This routine allocates the release queue irp held in classpnp's private
- extension. This was added to allow no-memory conditions to be more
- survivable.
- Return Value:
- NT_SUCCESS value.
- Notes:
- Does not grab the spinlock. Should only be called from StartDevice()
- routine. May be called elsewhere for poorly-behaved drivers that cause
- the queue to lockup before the device is started. This should *never*
- occur, since it's illegal to send a request to a non-started PDO. This
- condition is checked for in ClasspReleaseQueue().
- --*/
- NTSTATUS
- ClasspAllocateReleaseQueueIrp(
- PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
- )
- {
- KIRQL oldIrql;
- UCHAR lowerStackSize;
- //
- // do an initial check w/o the spinlock
- //
- if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
- return STATUS_SUCCESS;
- }
- lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
- //
- // don't allocate one if one is in progress! this means whoever called
- // this routine didn't check if one was in progress.
- //
- ASSERT(!(FdoExtension->ReleaseQueueInProgress));
- FdoExtension->PrivateFdoData->ReleaseQueueIrp =
- ExAllocatePoolWithTag(NonPagedPool,
- IoSizeOfIrp(lowerStackSize),
- CLASS_TAG_RELEASE_QUEUE
- );
- if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
- DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
- "release queue irp\n"));
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
- IoSizeOfIrp(lowerStackSize),
- lowerStackSize);
- FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
- return STATUS_SUCCESS;
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClasspReleaseQueue()
- Routine Description:
- This routine issues an internal device control command
- to the port driver to release a frozen queue. The call
- is issued asynchronously as ClassReleaseQueue will be invoked
- from the IO completion DPC (and will have no context to
- wait for a synchronous call to complete).
- This routine must be called with the remove lock held.
- Arguments:
- Fdo - The functional device object for the device with the frozen queue.
- ReleaseQueueIrp - If this irp is supplied then the test to determine whether
- a release queue request is in progress will be ignored.
- The irp provided must be the IRP originally allocated
- for release queue requests (so this parameter can only
- really be provided by the release queue completion
- routine.)
- Return Value:
- None.
- --*/
- VOID
- ClasspReleaseQueue(
- IN PDEVICE_OBJECT Fdo,
- IN PIRP ReleaseQueueIrp OPTIONAL
- )
- {
- PIO_STACK_LOCATION irpStack;
- PIRP irp;
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
- PDEVICE_OBJECT lowerDevice;
- PSCSI_REQUEST_BLOCK srb;
- KIRQL currentIrql;
- lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
- //
- // we raise irql seperately so we're not swapped out or suspended
- // while holding the release queue irp in this routine. this lets
- // us release the spin lock before lowering irql.
- //
- KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql);
- KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
- //
- // make sure that if they passed us an irp, it matches our allocated irp.
- //
- ASSERT((ReleaseQueueIrp == NULL) ||
- (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
- //
- // ASSERT that we've already allocated this. (should not occur)
- // try to allocate it anyways, then finally bugcheck if
- // there's still no memory...
- //
- ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
- if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
- ClasspAllocateReleaseQueueIrp(fdoExtension);
- }
- if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
- KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
- }
- if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
- //
- // Someone is already using the irp - just set the flag to indicate that
- // we need to release the queue again.
- //
- fdoExtension->ReleaseQueueNeeded = TRUE;
- KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
- KeLowerIrql(currentIrql);
- return;
- }
- //
- // Mark that there is a release queue in progress and drop the spinlock.
- //
- fdoExtension->ReleaseQueueInProgress = TRUE;
- if (ReleaseQueueIrp) {
- irp = ReleaseQueueIrp;
- } else {
- irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
- }
- srb = &(fdoExtension->ReleaseQueueSrb);
- KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
- ASSERT(irp != NULL);
- irpStack = IoGetNextIrpStackLocation(irp);
- irpStack->MajorFunction = IRP_MJ_SCSI;
- srb->OriginalRequest = irp;
- //
- // Store the SRB address in next stack for port driver.
- //
- irpStack->Parameters.Scsi.Srb = srb;
- //
- // If this device is removable then flush the queue. This will also
- // release it.
- //
- if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
- srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
- }
- else {
- srb->Function = SRB_FUNCTION_RELEASE_QUEUE;
- }
- ClassAcquireRemoveLock(Fdo, irp);
- IoSetCompletionRoutine(irp,
- ClassReleaseQueueCompletion,
- Fdo,
- TRUE,
- TRUE,
- TRUE);
- IoCallDriver(lowerDevice, irp);
- KeLowerIrql(currentIrql);
- return;
- } // end ClassReleaseQueue()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassReleaseQueueCompletion()
- Routine Description:
- This routine is called when an asynchronous I/O request
- which was issused by the class driver completes. Examples of such requests
- are release queue or START UNIT. This routine releases the queue if
- necessary. It then frees the context and the IRP.
- Arguments:
- DeviceObject - The device object for the logical unit; however since this
- is the top stack location the value is NULL.
- Irp - Supplies a pointer to the Irp to be processed.
- Context - Supplies the context to be used to process this request.
- Return Value:
- None.
- --*/
- NTSTATUS
- ClassReleaseQueueCompletion(
- PDEVICE_OBJECT DeviceObject,
- PIRP Irp,
- PVOID Context
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
- KIRQL oldIrql;
- BOOLEAN releaseQueueNeeded;
- DeviceObject = Context;
- fdoExtension = DeviceObject->DeviceExtension;
- ClassReleaseRemoveLock(DeviceObject, Irp);
- //
- // Grab the spinlock and clear the release queue in progress flag so others
- // can run. Save (and clear) the state of the release queue needed flag
- // so that we can issue a new release queue outside the spinlock.
- //
- KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
- releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
- fdoExtension->ReleaseQueueNeeded = FALSE;
- fdoExtension->ReleaseQueueInProgress = FALSE;
- KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
- //
- // If we need a release queue then issue one now. Another processor may
- // have already started one in which case we'll try to issue this one after
- // it is done - but we should never recurse more than one deep.
- //
- if(releaseQueueNeeded) {
- ClasspReleaseQueue(DeviceObject, Irp);
- }
- //
- // Indicate the I/O system should stop processing the Irp completion.
- //
- return STATUS_MORE_PROCESSING_REQUIRED;
- } // ClassAsynchronousCompletion()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassAcquireChildLock()
- Routine Description:
- This routine acquires the lock protecting children PDOs. It may be
- acquired recursively by the same thread, but must be release by the
- thread once for each acquisition.
- Arguments:
- FdoExtension - the device whose child list is protected.
- Return Value:
- None
- --*/
- VOID
- ClassAcquireChildLock(
- IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
- )
- {
- PAGED_CODE();
- if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
- KeWaitForSingleObject(&FdoExtension->ChildLock,
- Executive, KernelMode,
- FALSE, NULL);
- ASSERT(FdoExtension->ChildLockOwner == NULL);
- ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
- FdoExtension->ChildLockOwner = KeGetCurrentThread();
- } else {
- ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
- }
- FdoExtension->ChildLockAcquisitionCount++;
- return;
- }
- /*++////////////////////////////////////////////////////////////////////////////
- ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
- Routine Description:
- This routine releases the lock protecting children PDOs. It must be
- called once for each time ClassAcquireChildLock was called.
- Arguments:
- FdoExtension - the device whose child list is protected
- Return Value:
- None.
- --*/
- VOID
- ClassReleaseChildLock(
- IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
- )
- {
- ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
- ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
- FdoExtension->ChildLockAcquisitionCount -= 1;
- if(FdoExtension->ChildLockAcquisitionCount == 0) {
- FdoExtension->ChildLockOwner = NULL;
- KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
- }
- return;
- } // end ClassReleaseChildLock(
- /*++////////////////////////////////////////////////////////////////////////////
- ClassAddChild()
- Routine Description:
- This routine will insert a new child into the head of the child list.
- Arguments:
- Parent - the child's parent (contains the head of the list)
- Child - the child to be inserted.
- AcquireLock - whether the child lock should be acquired (TRUE) or whether
- it's already been acquired by or on behalf of the caller
- (FALSE).
- Return Value:
- None.
- --*/
- VOID
- ClassAddChild(
- IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
- IN PPHYSICAL_DEVICE_EXTENSION Child,
- IN BOOLEAN AcquireLock
- )
- {
- if(AcquireLock) {
- ClassAcquireChildLock(Parent);
- }
- #if DBG
- //
- // Make sure this child's not already in the list.
- //
- {
- PPHYSICAL_DEVICE_EXTENSION testChild;
- for (testChild = Parent->CommonExtension.ChildList;
- testChild != NULL;
- testChild = testChild->CommonExtension.ChildList) {
- ASSERT(testChild != Child);
- }
- }
- #endif
- Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
- Parent->CommonExtension.ChildList = Child;
- if(AcquireLock) {
- ClassReleaseChildLock(Parent);
- }
- return;
- } // end ClassAddChild()
- /*++////////////////////////////////////////////////////////////////////////////
- ClassRemoveChild()
- Routine Description:
- This routine will remove a child from the child list.
- Arguments:
- Parent - the parent to be removed from.
- Child - the child to be removed or NULL if the first child should be
- removed.
- AcquireLock - whether the child lock should be acquired (TRUE) or whether
- it's already been acquired by or on behalf of the caller
- (FALSE).
- Return Value:
- A pointer to the child which was removed or NULL if no such child could
- be found in the list (or if Child was NULL but the list is empty).
- --*/
- PPHYSICAL_DEVICE_EXTENSION
- ClassRemoveChild(
- IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
- IN PPHYSICAL_DEVICE_EXTENSION Child,
- IN BOOLEAN AcquireLock
- )
- {
- if(AcquireLock) {
- ClassAcquireChildLock(Parent);
- }
- TRY {
- PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
- //
- // If the list is empty then bail out now.
- //
- if(Parent->CommonExtension.ChildList == NULL) {
- Child = NULL;
- LEAVE;
- }
- //
- // If the caller specified a child then find the child object before
- // it. If none was specified then the FDO is the child object before
- // the one we want to remove.
- //
- if(Child != NULL) {
- //
- // Scan through the child list to find the entry which points to
- // this one.
- //
- do {
- ASSERT(previousChild != &Child->CommonExtension);
- if(previousChild->ChildList == Child) {
- break;
- }
- previousChild = &previousChild->ChildList->CommonExtension;
- } while(previousChild != NULL);
- if(previousChild == NULL) {
- Child = NULL;
- LEAVE;
- }
- }
- //
- // Save the next child away then unlink it from the list.
- //
- Child = previousChild->ChildList;
- previousChild->ChildList = Child->CommonExtension.ChildList;
- Child->CommonExtension.ChildList = NULL;
- } FINALLY {
- if(AcquireLock) {
- ClassReleaseChildLock(Parent);
- }
- }
- return Child;
- } // end ClassRemoveChild()
- /*++
- ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
- --*/
- VOID
- ClasspRetryRequestDpc(
- IN PKDPC Dpc,
- IN PDEVICE_OBJECT DeviceObject,
- IN PVOID Arg1,
- IN PVOID Arg2
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
- PCOMMON_DEVICE_EXTENSION commonExtension;
- PCLASS_PRIVATE_FDO_DATA fdoData;
- PCLASS_RETRY_INFO retryList;
- KIRQL irql;
- commonExtension = DeviceObject->DeviceExtension;
- ASSERT(commonExtension->IsFdo);
- fdoExtension = DeviceObject->DeviceExtension;
- fdoData = fdoExtension->PrivateFdoData;
- KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
- {
- LARGE_INTEGER now;
- KeQueryTickCount(&now);
- //
- // if CurrentTick is less than now
- // fire another DPC
- // else
- // retry entire list
- // endif
- //
- if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
- ClasspRetryDpcTimer(fdoData);
- retryList = NULL;
- } else {
- retryList = fdoData->Retry.ListHead;
- fdoData->Retry.ListHead = NULL;
- fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
- fdoData->Retry.Tick.QuadPart = (LONGLONG)0;
- }
- }
- KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
- while (retryList != NULL) {
- PIRP irp;
- irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
- DebugPrint((ClassDebugDelayedRetry, "ClassRetry: -- %p\n", irp));
- retryList = retryList->Next;
- #if DBG
- irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
- irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
- irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
- irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
- #endif
- IoCallDriver(commonExtension->LowerDeviceObject, irp);
- }
- return;
- } // end ClasspRetryRequestDpc()
- /*++
- ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
- --*/
- VOID
- ClassRetryRequest(
- IN PDEVICE_OBJECT SelfDeviceObject,
- IN PIRP Irp,
- IN LARGE_INTEGER TimeDelta100ns // in 100ns units
- )
- {
- PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
- PCLASS_PRIVATE_FDO_DATA fdoData;
- PCLASS_RETRY_INFO retryInfo;
- PCLASS_RETRY_INFO *previousNext;
- LARGE_INTEGER delta;
- KIRQL irql;
- //
- // this checks we aren't destroying irps
- //
- ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
- fdoExtension = SelfDeviceObject->DeviceExtension;
- fdoData = fdoExtension->PrivateFdoData;
- if (!fdoExtension->CommonExtension.IsFdo) {
- //
- // this debug print/assertion should ALWAYS be investigated.
- // ClassRetryRequest can currently only be used by FDO's
- //
- DebugPrint((ClassDebugError, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
- ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
- return;
- }
- if (TimeDelta100ns.QuadPart < 0) {
- ASSERT(!"ClassRetryRequest - must use positive delay");
- TimeDelta100ns.QuadPart *= -1;
- }
- //
- // prepare what we can out of the loop
- //
- retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
- RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
- delta.QuadPart = (TimeDelta100ns.QuadPart / fdoData->Retry.Granularity);
- if (TimeDelta100ns.QuadPart % fdoData->Retry.Granularity) {
- delta.QuadPart ++; // round up to next tick
- }
- if (delta.QuadPart == (LONGLONG)0) {
- delta.QuadPart = MINIMUM_RETRY_UNITS;
- }
- //
- // now determine if we should fire another DPC or not
- //
- KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
- //
- // always add request to the list
- //
- retryInfo->Next = fdoData->Retry.ListHead;
- fdoData->Retry.ListHead = retryInfo;
- if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
- DebugPrint((ClassDebugDelayedRetry, "ClassRetry: +++ %p\n", Irp));
- //
- // must be exactly one item on list
- //
- ASSERT(fdoData->Retry.ListHead != NULL);
- ASSERT(fdoData->Retry.ListHead->Next == NULL);
- //
- // if currentDelta is zero, always fire a DPC
- //
- KeQueryTickCount(&fdoData->Retry.Tick);
- fdoData->Retry.Tick.QuadPart += delta.QuadPart;
- fdoData->Retry.Delta.QuadPart = delta.QuadPart;
- ClasspRetryDpcTimer(fdoData);
- } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
- //
- // if delta is greater than the list's current delta,
- // increase the DPC handling time by difference
- // and update the delta to new larger value
- // allow the DPC to re-fire itself if needed
- //
- DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp));
- //
- // must be at least two items on list
- //
- ASSERT(fdoData->Retry.ListHead != NULL);
- ASSERT(fdoData->Retry.ListHead->Next != NULL);
- fdoData->Retry.Tick.QuadPart -= fdoData->Retry.Delta.QuadPart;
- fdoData->Retry.Tick.QuadPart += delta.QuadPart;
- fdoData->Retry.Delta.QuadPart = delta.QuadPart;
- } else {
- //
- // just inserting it on the list was enough
- //
- DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp));
- }
- KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
- } // end ClassRetryRequest()
- /*++
- ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
- --*/
- VOID
- ClasspRetryDpcTimer(
- IN PCLASS_PRIVATE_FDO_DATA FdoData
- )
- {
- LARGE_INTEGER fire;
- ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
- ASSERT(FdoData->Retry.ListHead != NULL); // never fire an empty list
- //
- // fire == (CurrentTick - now) * (100ns per tick)
- //
- // NOTE: Overflow is nearly impossible and is ignored here
- //
- KeQueryTickCount(&fire);
- fire.QuadPart = FdoData->Retry.Tick.QuadPart - fire.QuadPart;
- fire.QuadPart *= FdoData->Retry.Granularity;
- //
- // fire is now multiples of 100ns until should fire the timer.
- // if timer should already have expired, or would fire too quickly,
- // fire it in some arbitrary number of ticks to prevent infinitely
- // recursing.
- //
- if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
- fire.QuadPart = MINIMUM_RETRY_UNITS;
- }
- DebugPrint((ClassDebugDelayedRetry,
- "ClassRetry: ======= %I64x ticks\n",
- fire.QuadPart));
- //
- // must use negative to specify relative time to fire
- //
- fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
- //
- // set the timer, since this is the first addition
- //
- KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
- return;
- } // end ClasspRetryDpcTimer()
- NTSTATUS
- ClasspInitializeHotplugInfo(
- IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
- )
- {
- PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
- DEVICE_REMOVAL_POLICY deviceRemovalPolicy;
- NTSTATUS status;
- ULONG resultLength = 0;
- ULONG writeCacheOverride;
- PAGED_CODE();
- //
- // start with some default settings
- //
- RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
- //
- // set the size (aka version)
- //
- fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
- //
- // set if the device has removable media
- //
- if (FdoExtension->DeviceDescriptor->RemovableMedia) {
- fdoData->HotplugInfo.MediaRemovable = TRUE;
- } else {
- fdoData->HotplugInfo.MediaRemovable = FALSE;
- }
- //
- // this refers to devices which, for reasons not yet understood,
- // do not fail PREVENT_MEDIA_REMOVAL requests even though they
- // have no way to lock the media into the drive. this allows
- // the filesystems to turn off delayed-write caching for these
- // devices as well.
- //
- if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
- FDO_HACK_CANNOT_LOCK_MEDIA)) {
- fdoData->HotplugInfo.MediaHotplug = TRUE;
- } else {
- fdoData->HotplugInfo.MediaHotplug = FALSE;
- }
- //
- // Look into the registry to see if the user has chosen
- // to override the default setting for the removal policy
- //
- RtlZeroMemory(&deviceRemovalPolicy, sizeof(DEVICE_REMOVAL_POLICY));
- ClassGetDeviceParameter(FdoExtension,
- CLASSP_REG_SUBKEY_NAME,
- CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
- (PULONG)&deviceRemovalPolicy);
- if (deviceRemovalPolicy == 0)
- {
- //
- // Query the default removal policy from the kernel
- //
- status = IoGetDeviceProperty(FdoExtension->LowerPdo,
- DevicePropertyRemovalPolicy,
- sizeof(DEVICE_REMOVAL_POLICY),
- (PVOID)&deviceRemovalPolicy,
- &resultLength);
- if (!NT_SUCCESS(status))
- {
- return status;
- }
- if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
- {
- return STATUS_UNSUCCESSFUL;
- }
- }
- //
- // use this info to set the DeviceHotplug setting
- // don't rely on DeviceCapabilities, since it can't properly
- // determine device relations, etc. let the kernel figure this
- // stuff out instead.
- //
- if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
- fdoData->HotplugInfo.DeviceHotplug = TRUE;
- } else {
- fdoData->HotplugInfo.DeviceHotplug = FALSE;
- }
- //
- // this refers to the *filesystem* caching, but has to be included
- // here since it's a per-device setting. this may change to be
- // stored by the system in the future.
- //
- writeCacheOverride = FALSE;
- ClassGetDeviceParameter(FdoExtension,
- CLASSP_REG_SUBKEY_NAME,
- CLASSP_REG_WRITE_CACHE_VALUE_NAME,
- &writeCacheOverride);
- if (writeCacheOverride) {
- fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
- } else {
- fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
- }
- return STATUS_SUCCESS;
- }
- VOID
- ClasspScanForClassHacks(
- IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
- IN ULONG_PTR Data
- )
- {
- PAGED_CODE();
- //
- // remove invalid flags and save
- //
- CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
- SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
- return;
- }
- VOID
- ClasspScanForSpecialInRegistry(
- IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
- )
- {
- HANDLE deviceParameterHandle; // device instance key
- HANDLE classParameterHandle; // classpnp subkey
- OBJECT_ATTRIBUTES objectAttributes;
- UNICODE_STRING subkeyName;
- NTSTATUS status;
- //
- // seeded in the ENUM tree by ClassInstaller
- //
- ULONG deviceHacks;
- RTL_QUERY_REGISTRY_TABLE queryTable[2]; // null terminated array
- PAGED_CODE();
- deviceParameterHandle = NULL;
- classParameterHandle = NULL;
- deviceHacks = 0;
- status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
- PLUGPLAY_REGKEY_DEVICE,
- KEY_WRITE,
- &deviceParameterHandle
- );
- if (!NT_SUCCESS(status)) {
- goto cleanupScanForSpecial;
- }
- RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
- InitializeObjectAttributes(&objectAttributes,
- &subkeyName,
- OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
- deviceParameterHandle,
- NULL
- );
- status = ZwOpenKey( &classParameterHandle,
- KEY_READ,
- &objectAttributes
- );
- if (!NT_SUCCESS(status)) {
- goto cleanupScanForSpecial;
- }
- //
- // Zero out the memory
- //
- RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
- //
- // Setup the structure to read
- //
- queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
- queryTable[0].Name = CLASSP_REG_HACK_VALUE_NAME;
- queryTable[0].EntryContext = &deviceHacks;
- queryTable[0].DefaultType = REG_DWORD;
- queryTable[0].DefaultData = &deviceHacks;
- queryTable[0].DefaultLength = 0;
- //
- // read values
- //
- status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
- (PWSTR)classParameterHandle,
- &queryTable[0],
- NULL,
- NULL
- );
- if (!NT_SUCCESS(status)) {
- goto cleanupScanForSpecial;
- }
- //
- // remove unknown values and save...
- //
- KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
- "Classpnp => ScanForSpecial: HackFlags %#08x\n",
- deviceHacks));
- CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
- SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
- cleanupScanForSpecial:
- if (deviceParameterHandle) {
- ZwClose(deviceParameterHandle);
- }
- if (classParameterHandle) {
- ZwClose(classParameterHandle);
- }
- //
- // we should modify the system hive to include another key for us to grab
- // settings from. in this case: Classpnp\HackFlags
- //
- // the use of a DWORD value for the HackFlags allows 32 hacks w/o
- // significant use of the registry, and also reduces OEM exposure.
- //
- // definition of bit flags:
- // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
- // cannot actually prevent removal.
- // 0x00000002 -- Device hard-hangs or times out for GESN requests.
- // 0xfffffffc -- Currently reserved, may be used later.
- //
- return;
- }