3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 SCSI class driver routines
24 #define CLASS_INIT_GUID 1
29 #pragma alloc_text(INIT, DriverEntry)
30 #pragma alloc_text(PAGE, ClassAddDevice)
31 #pragma alloc_text(PAGE, ClassClaimDevice)
32 #pragma alloc_text(PAGE, ClassCreateDeviceObject)
33 #pragma alloc_text(PAGE, ClassDispatchPnp)
34 #pragma alloc_text(PAGE, ClassGetDescriptor)
35 #pragma alloc_text(PAGE, ClassGetPdoId)
36 #pragma alloc_text(PAGE, ClassInitialize)
37 #pragma alloc_text(PAGE, ClassInitializeEx)
38 #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
39 #pragma alloc_text(PAGE, ClassMarkChildMissing)
40 #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
41 #pragma alloc_text(PAGE, ClassModeSense)
42 #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
43 #pragma alloc_text(PAGE, ClassPnpStartDevice)
44 #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
45 #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
46 #pragma alloc_text(PAGE, ClassRemoveDevice)
47 #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
48 #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
49 #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
50 #pragma alloc_text(PAGE, ClassUnload)
51 #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
52 #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
53 #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
54 #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
55 #pragma alloc_text(PAGE, ClasspScanForClassHacks)
56 #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
59 ULONG ClassPnpAllowUnload
= TRUE
;
62 #define FirstDriveLetter 'C'
63 #define LastDriveLetter 'Z'
67 /*++////////////////////////////////////////////////////////////////////////////
73 Temporary entry point needed to initialize the class system dll.
74 It doesn't do anything.
78 DriverObject - Pointer to the driver object created by the system.
88 IN PDRIVER_OBJECT DriverObject
,
89 IN PUNICODE_STRING RegistryPath
92 return STATUS_SUCCESS
;
98 /*++////////////////////////////////////////////////////////////////////////////
104 This routine is called by a class driver during its
105 DriverEntry routine to initialize the driver.
109 Argument1 - Driver Object.
110 Argument2 - Registry Path.
111 InitializationData - Device-specific driver's initialization data.
115 A valid return code for a DriverEntry routine.
122 IN PCLASS_INIT_DATA InitializationData
125 PDRIVER_OBJECT DriverObject
= Argument1
;
126 PUNICODE_STRING RegistryPath
= Argument2
;
128 PCLASS_DRIVER_EXTENSION driverExtension
;
134 DebugPrint((3,"\n\nSCSI Class Driver\n"));
136 ClasspInitializeDebugGlobals();
139 // Validate the length of this structure. This is effectively a
143 if (InitializationData
->InitializationDataSize
!= sizeof(CLASS_INIT_DATA
)) {
146 // This DebugPrint is to help third-party driver writers
149 DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
150 return (ULONG
) STATUS_REVISION_MISMATCH
;
154 // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
155 // are not required entry points.
158 if ((!InitializationData
->FdoData
.ClassDeviceControl
) ||
159 (!((InitializationData
->FdoData
.ClassReadWriteVerification
) ||
160 (InitializationData
->ClassStartIo
))) ||
161 (!InitializationData
->ClassAddDevice
) ||
162 (!InitializationData
->FdoData
.ClassStartDevice
)) {
165 // This DebugPrint is to help third-party driver writers
169 "ClassInitialize: Class device-specific driver missing required "
172 return (ULONG
) STATUS_REVISION_MISMATCH
;
175 if ((InitializationData
->ClassEnumerateDevice
) &&
176 ((!InitializationData
->PdoData
.ClassDeviceControl
) ||
177 (!InitializationData
->PdoData
.ClassStartDevice
) ||
178 (!((InitializationData
->PdoData
.ClassReadWriteVerification
) ||
179 (InitializationData
->ClassStartIo
))))) {
182 // This DebugPrint is to help third-party driver writers
185 DebugPrint((0, "ClassInitialize: Class device-specific missing "
186 "required PDO entry\n"));
188 return (ULONG
) STATUS_REVISION_MISMATCH
;
191 if((InitializationData
->FdoData
.ClassStopDevice
== NULL
) ||
192 ((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
193 (InitializationData
->PdoData
.ClassStopDevice
== NULL
))) {
196 // This DebugPrint is to help third-party driver writers
199 DebugPrint((0, "ClassInitialize: Class device-specific missing "
200 "required PDO entry\n"));
202 return (ULONG
) STATUS_REVISION_MISMATCH
;
206 // Setup the default power handlers if the class driver didn't provide
210 if(InitializationData
->FdoData
.ClassPowerDevice
== NULL
) {
211 InitializationData
->FdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
214 if((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
215 (InitializationData
->PdoData
.ClassPowerDevice
== NULL
)) {
216 InitializationData
->PdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
220 // warn that unload is not supported
222 // ISSUE-2000/02/03-peterwie
223 // We should think about making this a fatal error.
226 if(InitializationData
->ClassUnload
== NULL
) {
229 // This DebugPrint is to help third-party driver writers
232 DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
237 // Create an extension for the driver object
240 status
= IoAllocateDriverObjectExtension(DriverObject
,
241 CLASS_DRIVER_EXTENSION_KEY
,
242 sizeof(CLASS_DRIVER_EXTENSION
),
245 if(NT_SUCCESS(status
)) {
248 // Copy the registry path into the driver extension so we can use it later
251 driverExtension
->RegistryPath
.Length
= RegistryPath
->Length
;
252 driverExtension
->RegistryPath
.MaximumLength
= RegistryPath
->MaximumLength
;
254 driverExtension
->RegistryPath
.Buffer
=
255 ExAllocatePoolWithTag(PagedPool
,
256 RegistryPath
->MaximumLength
,
259 if(driverExtension
->RegistryPath
.Buffer
== NULL
) {
261 status
= STATUS_INSUFFICIENT_RESOURCES
;
265 RtlCopyUnicodeString(
266 &(driverExtension
->RegistryPath
),
270 // Copy the initialization data into the driver extension so we can reuse
271 // it during our add device routine
275 &(driverExtension
->InitData
),
277 sizeof(CLASS_INIT_DATA
));
279 driverExtension
->DeviceCount
= 0;
281 } else if (status
== STATUS_OBJECT_NAME_COLLISION
) {
284 // The extension already exists - get a pointer to it
287 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
288 CLASS_DRIVER_EXTENSION_KEY
);
290 ASSERT(driverExtension
!= NULL
);
294 DebugPrint((1, "ClassInitialize: Class driver extension could not be "
295 "allocated %lx\n", status
));
300 // Update driver object with entry points.
303 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = ClassCreateClose
;
304 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = ClassCreateClose
;
305 DriverObject
->MajorFunction
[IRP_MJ_READ
] = ClassReadWrite
;
306 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = ClassReadWrite
;
307 DriverObject
->MajorFunction
[IRP_MJ_SCSI
] = ClassInternalIoControl
;
308 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = ClassDeviceControlDispatch
;
309 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = ClassShutdownFlush
;
310 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = ClassShutdownFlush
;
311 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = ClassDispatchPnp
;
312 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = ClassDispatchPower
;
313 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = ClassSystemControl
;
315 if (InitializationData
->ClassStartIo
) {
316 DriverObject
->DriverStartIo
= ClasspStartIo
;
319 if ((InitializationData
->ClassUnload
) && (ClassPnpAllowUnload
== TRUE
)) {
320 DriverObject
->DriverUnload
= ClassUnload
;
322 DriverObject
->DriverUnload
= NULL
;
325 DriverObject
->DriverExtension
->AddDevice
= ClassAddDevice
;
327 DbgPrint("Driver is ready to go\n");
328 status
= STATUS_SUCCESS
;
330 } // end ClassInitialize()
332 /*++////////////////////////////////////////////////////////////////////////////
338 This routine is allows the caller to do any extra initialization or
339 setup that is not done in ClassInitialize. The operation is
340 controlled by the GUID that is passed and the contents of the Data
341 parameter is dependent upon the GUID.
343 This is the list of supported operations:
345 Guid - GUID_CLASSPNP_QUERY_REGINFOEX
346 Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
348 Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
349 callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
350 former callback allows the driver to specify the name of the
366 IN PDRIVER_OBJECT DriverObject
,
371 PCLASS_DRIVER_EXTENSION driverExtension
;
377 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
378 CLASS_DRIVER_EXTENSION_KEY
380 if (IsEqualGUID(Guid
, &ClassGuidQueryRegInfoEx
))
382 PCLASS_QUERY_WMI_REGINFO_EX_LIST List
;
385 // Indicate the device supports PCLASS_QUERY_REGINFO_EX
386 // callback instead of PCLASS_QUERY_REGINFO callback.
388 List
= (PCLASS_QUERY_WMI_REGINFO_EX_LIST
)Data
;
390 if (List
->Size
== sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST
))
392 driverExtension
->ClassFdoQueryWmiRegInfoEx
= List
->ClassFdoQueryWmiRegInfoEx
;
393 driverExtension
->ClassPdoQueryWmiRegInfoEx
= List
->ClassPdoQueryWmiRegInfoEx
;
394 status
= STATUS_SUCCESS
;
396 status
= STATUS_INVALID_PARAMETER
;
399 status
= STATUS_NOT_SUPPORTED
;
404 } // end ClassInitializeEx()
406 /*++////////////////////////////////////////////////////////////////////////////
412 called when there are no more references to the driver. this allows
413 drivers to be updated without rebooting.
417 DriverObject - a pointer to the driver object that is being unloaded
424 IN PDRIVER_OBJECT DriverObject
427 PCLASS_DRIVER_EXTENSION driverExtension
;
432 ASSERT( DriverObject
->DeviceObject
== NULL
);
434 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
435 CLASS_DRIVER_EXTENSION_KEY
438 ASSERT(driverExtension
!= NULL
);
439 ASSERT(driverExtension
->RegistryPath
.Buffer
!= NULL
);
440 ASSERT(driverExtension
->InitData
.ClassUnload
!= NULL
);
442 DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
443 &driverExtension
->RegistryPath
));
446 // attempt to process the driver's unload routine first.
449 driverExtension
->InitData
.ClassUnload(DriverObject
);
452 // free own allocated resources and return
455 ExFreePool( driverExtension
->RegistryPath
.Buffer
);
456 driverExtension
->RegistryPath
.Buffer
= NULL
;
457 driverExtension
->RegistryPath
.Length
= 0;
458 driverExtension
->RegistryPath
.MaximumLength
= 0;
461 } // end ClassUnload()
463 /*++////////////////////////////////////////////////////////////////////////////
469 SCSI class driver add device routine. This is called by pnp when a new
470 physical device come into being.
472 This routine will call out to the class driver to verify that it should
473 own this device then will create and attach a device object and then hand
474 it to the driver to initialize and create symbolic links
478 DriverObject - a pointer to the driver object that this is being created for
479 PhysicalDeviceObject - a pointer to the physical device object
481 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
482 STATUS_SUCCESS if the creation and attachment was successful
483 status of device creation and initialization
488 IN PDRIVER_OBJECT DriverObject
,
489 IN PDEVICE_OBJECT PhysicalDeviceObject
492 PCLASS_DRIVER_EXTENSION driverExtension
=
493 IoGetDriverObjectExtension(DriverObject
,
494 CLASS_DRIVER_EXTENSION_KEY
);
500 DbgPrint("got a device\n");
501 status
= driverExtension
->InitData
.ClassAddDevice(DriverObject
,
502 PhysicalDeviceObject
);
504 } // end ClassAddDevice()
506 /*++////////////////////////////////////////////////////////////////////////////
512 Storage class driver pnp routine. This is called by the io system when
513 a PNP request is sent to the device.
517 DeviceObject - pointer to the device object
519 Irp - pointer to the io request packet
528 IN PDEVICE_OBJECT DeviceObject
,
532 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
533 BOOLEAN isFdo
= commonExtension
->IsFdo
;
535 PCLASS_DRIVER_EXTENSION driverExtension
;
536 PCLASS_INIT_DATA initData
;
537 PCLASS_DEV_INFO devInfo
;
539 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
540 PIO_STACK_LOCATION nextIrpStack
= IoGetNextIrpStackLocation(Irp
);
542 NTSTATUS status
= Irp
->IoStatus
.Status
;
543 BOOLEAN completeRequest
= TRUE
;
544 BOOLEAN lockReleased
= FALSE
;
551 // Extract all the useful information out of the driver object
555 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
556 CLASS_DRIVER_EXTENSION_KEY
);
557 if (driverExtension
){
559 initData
= &(driverExtension
->InitData
);
562 devInfo
= &(initData
->FdoData
);
564 devInfo
= &(initData
->PdoData
);
567 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
569 DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
571 irpStack
->MinorFunction
,
572 isFdo
? "fdo" : "pdo",
574 DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
576 commonExtension
->PreviousState
,
577 commonExtension
->CurrentState
));
579 switch(irpStack
->MinorFunction
) {
581 case IRP_MN_START_DEVICE
: {
584 // if this is sent to the FDO we should forward it down the
585 // attachment chain before we start the FDO.
589 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
592 status
= STATUS_SUCCESS
;
595 if (NT_SUCCESS(status
)){
596 status
= Irp
->IoStatus
.Status
= ClassPnpStartDevice(DeviceObject
);
603 case IRP_MN_QUERY_DEVICE_RELATIONS
: {
605 DEVICE_RELATION_TYPE type
=
606 irpStack
->Parameters
.QueryDeviceRelations
.Type
;
608 PDEVICE_RELATIONS deviceRelations
= NULL
;
612 if(type
== TargetDeviceRelation
) {
615 // Device relations has one entry built in to it's size.
618 status
= STATUS_INSUFFICIENT_RESOURCES
;
620 deviceRelations
= ExAllocatePoolWithTag(PagedPool
,
621 sizeof(DEVICE_RELATIONS
),
624 if(deviceRelations
!= NULL
) {
626 RtlZeroMemory(deviceRelations
,
627 sizeof(DEVICE_RELATIONS
));
629 Irp
->IoStatus
.Information
= (ULONG_PTR
) deviceRelations
;
631 deviceRelations
->Count
= 1;
632 deviceRelations
->Objects
[0] = DeviceObject
;
633 ObReferenceObject(deviceRelations
->Objects
[0]);
635 status
= STATUS_SUCCESS
;
640 // PDO's just complete enumeration requests without altering
644 status
= Irp
->IoStatus
.Status
;
649 } else if (type
== BusRelations
) {
651 ASSERT(commonExtension
->IsInitialized
);
654 // Make sure we support enumeration
657 if(initData
->ClassEnumerateDevice
== NULL
) {
660 // Just send the request down to the lower driver. Perhaps
661 // It can enumerate children.
667 // Re-enumerate the device
670 status
= ClassPnpQueryFdoRelations(DeviceObject
, Irp
);
672 if(!NT_SUCCESS(status
)) {
673 completeRequest
= TRUE
;
679 IoCopyCurrentIrpStackLocationToNext(Irp
);
680 ClassReleaseRemoveLock(DeviceObject
, Irp
);
681 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
682 completeRequest
= FALSE
;
687 case IRP_MN_QUERY_ID
: {
689 BUS_QUERY_ID_TYPE idType
= irpStack
->Parameters
.QueryId
.IdType
;
690 UNICODE_STRING unicodeString
;
695 // FDO's should just forward the query down to the lower
699 IoCopyCurrentIrpStackLocationToNext(Irp
);
700 ClassReleaseRemoveLock(DeviceObject
, Irp
);
702 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
703 completeRequest
= FALSE
;
708 // PDO's need to give an answer - this is easy for now
711 RtlInitUnicodeString(&unicodeString
, NULL
);
713 status
= ClassGetPdoId(DeviceObject
,
717 if(status
== STATUS_NOT_IMPLEMENTED
) {
719 // The driver doesn't implement this ID (whatever it is).
720 // Use the status out of the IRP so that we don't mangle a
721 // response from someone else.
724 status
= Irp
->IoStatus
.Status
;
725 } else if(NT_SUCCESS(status
)) {
726 Irp
->IoStatus
.Information
= (ULONG_PTR
) unicodeString
.Buffer
;
728 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
734 case IRP_MN_QUERY_STOP_DEVICE
:
735 case IRP_MN_QUERY_REMOVE_DEVICE
: {
737 DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
739 ((irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) ?
740 "STOP" : "REMOVE")));
743 // If this device is in use for some reason (paging, etc...)
744 // then we need to fail the request.
747 if(commonExtension
->PagingPathCount
!= 0) {
749 DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
750 "path and cannot be removed\n",
752 status
= STATUS_DEVICE_BUSY
;
757 // Check with the class driver to see if the query operation
761 if(irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) {
762 status
= devInfo
->ClassStopDevice(DeviceObject
,
763 irpStack
->MinorFunction
);
765 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
766 irpStack
->MinorFunction
);
769 if(NT_SUCCESS(status
)) {
772 // ASSERT that we never get two queries in a row, as
773 // this will severly mess up the state machine
775 ASSERT(commonExtension
->CurrentState
!= irpStack
->MinorFunction
);
776 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
777 commonExtension
->CurrentState
= irpStack
->MinorFunction
;
780 DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
781 "%s irp\n", DeviceObject
, Irp
,
782 ((irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) ?
783 "STOP" : "REMOVE")));
784 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
787 DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
788 DeviceObject
, Irp
, status
));
793 case IRP_MN_CANCEL_STOP_DEVICE
:
794 case IRP_MN_CANCEL_REMOVE_DEVICE
: {
797 // Check with the class driver to see if the query or cancel
798 // operation can succeed.
801 if(irpStack
->MinorFunction
== IRP_MN_CANCEL_STOP_DEVICE
) {
802 status
= devInfo
->ClassStopDevice(DeviceObject
,
803 irpStack
->MinorFunction
);
804 ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
805 "never be failed\n", NT_SUCCESS(status
));
807 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
808 irpStack
->MinorFunction
);
809 ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
810 "never be failed\n", NT_SUCCESS(status
));
813 Irp
->IoStatus
.Status
= status
;
816 // We got a CANCEL - roll back to the previous state only
817 // if the current state is the respective QUERY state.
820 if(((irpStack
->MinorFunction
== IRP_MN_CANCEL_STOP_DEVICE
) &&
821 (commonExtension
->CurrentState
== IRP_MN_QUERY_STOP_DEVICE
)
823 ((irpStack
->MinorFunction
== IRP_MN_CANCEL_REMOVE_DEVICE
) &&
824 (commonExtension
->CurrentState
== IRP_MN_QUERY_REMOVE_DEVICE
)
828 commonExtension
->CurrentState
=
829 commonExtension
->PreviousState
;
830 commonExtension
->PreviousState
= 0xff;
835 IoCopyCurrentIrpStackLocationToNext(Irp
);
836 ClassReleaseRemoveLock(DeviceObject
, Irp
);
837 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
838 completeRequest
= FALSE
;
840 status
= STATUS_SUCCESS
;
846 case IRP_MN_STOP_DEVICE
: {
849 // These all mean nothing to the class driver currently. The
850 // port driver will handle all queueing when necessary.
853 DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
855 (isFdo
? "fdo" : "pdo")
858 ASSERT(commonExtension
->PagingPathCount
== 0);
861 // ISSUE-2000/02/03-peterwie
862 // if we stop the timer here then it means no class driver can
863 // do i/o in its ClassStopDevice routine. This is because the
864 // retry (among other things) is tied into the tick handler
865 // and disabling retries could cause the class driver to deadlock.
866 // Currently no class driver we're aware of issues i/o in its
867 // Stop routine but this is a case we may want to defend ourself
871 if (DeviceObject
->Timer
) {
872 IoStopTimer(DeviceObject
);
875 status
= devInfo
->ClassStopDevice(DeviceObject
, IRP_MN_STOP_DEVICE
);
877 ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
878 "never be failed\n", NT_SUCCESS(status
));
881 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
884 if(NT_SUCCESS(status
)) {
885 commonExtension
->CurrentState
= irpStack
->MinorFunction
;
886 commonExtension
->PreviousState
= 0xff;
892 case IRP_MN_REMOVE_DEVICE
:
893 case IRP_MN_SURPRISE_REMOVAL
: {
895 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
896 UCHAR removeType
= irpStack
->MinorFunction
;
898 if (commonExtension
->PagingPathCount
!= 0) {
899 DBGTRACE(ClassDebugWarning
, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject
, Irp
));
903 // Release the lock for this IRP before calling in.
905 ClassReleaseRemoveLock(DeviceObject
, Irp
);
909 * If a timer was started on the device, stop it.
911 if (DeviceObject
->Timer
) {
912 IoStopTimer(DeviceObject
);
916 * "Fire-and-forget" the remove irp to the lower stack.
917 * Don't touch the irp (or the irp stack!) after this.
920 IoCopyCurrentIrpStackLocationToNext(Irp
);
921 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
922 ASSERT(NT_SUCCESS(status
));
923 completeRequest
= FALSE
;
926 status
= STATUS_SUCCESS
;
930 * Do our own cleanup and call the class driver's remove
932 * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
933 * so don't touch the extension after this.
935 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
936 commonExtension
->CurrentState
= removeType
;
937 ClassRemoveDevice(DeviceObject
, removeType
);
942 case IRP_MN_DEVICE_USAGE_NOTIFICATION
: {
944 switch(irpStack
->Parameters
.UsageNotification
.Type
) {
946 case DeviceUsageTypePaging
: {
950 if((irpStack
->Parameters
.UsageNotification
.InPath
) &&
951 (commonExtension
->CurrentState
!= IRP_MN_START_DEVICE
)) {
954 // Device isn't started. Don't allow adding a
955 // paging file, but allow a removal of one.
958 status
= STATUS_DEVICE_NOT_READY
;
962 ASSERT(commonExtension
->IsInitialized
);
965 // need to synchronize this now...
968 KeEnterCriticalRegion();
969 status
= KeWaitForSingleObject(&commonExtension
->PathCountEvent
,
970 Executive
, KernelMode
,
972 ASSERT(NT_SUCCESS(status
));
973 status
= STATUS_SUCCESS
;
976 // If the volume is removable we should try to lock it in
977 // place or unlock it once per paging path count
980 if (commonExtension
->IsFdo
){
981 status
= ClasspEjectionControl(
985 (BOOLEAN
)irpStack
->Parameters
.UsageNotification
.InPath
);
988 if (!NT_SUCCESS(status
)){
989 KeSetEvent(&commonExtension
->PathCountEvent
, IO_NO_INCREMENT
, FALSE
);
990 KeLeaveCriticalRegion();
995 // if removing last paging device, need to set DO_POWER_PAGABLE
996 // bit here, and possible re-set it below on failure.
1001 if (!irpStack
->Parameters
.UsageNotification
.InPath
&&
1002 commonExtension
->PagingPathCount
== 1
1006 // removing last paging file
1007 // must have DO_POWER_PAGABLE bits set, but only
1008 // if noone set the DO_POWER_INRUSH bit
1012 if (TEST_FLAG(DeviceObject
->Flags
, DO_POWER_INRUSH
)) {
1013 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1014 "paging file removed, but "
1015 "DO_POWER_INRUSH was set, so NOT "
1016 "setting DO_POWER_PAGABLE\n",
1017 DeviceObject
, Irp
));
1019 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1020 "paging file removed, "
1021 "setting DO_POWER_PAGABLE\n",
1022 DeviceObject
, Irp
));
1023 SET_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1030 // forward the irp before finishing handling the
1034 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1037 // now deal with the failure and success cases.
1038 // note that we are not allowed to fail the irp
1039 // once it is sent to the lower drivers.
1042 if (NT_SUCCESS(status
)) {
1044 IoAdjustPagingPathCount(
1045 &commonExtension
->PagingPathCount
,
1046 irpStack
->Parameters
.UsageNotification
.InPath
);
1048 if (irpStack
->Parameters
.UsageNotification
.InPath
) {
1049 if (commonExtension
->PagingPathCount
== 1) {
1050 DebugPrint((2, "ClassDispatchPnp (%p,%p): "
1051 "Clearing PAGABLE bit\n",
1052 DeviceObject
, Irp
));
1053 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1060 // cleanup the changes done above
1063 if (setPagable
== TRUE
) {
1064 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
1065 "PAGABLE bit due to irp failure\n",
1066 DeviceObject
, Irp
));
1067 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1072 // relock or unlock the media if needed.
1075 if (commonExtension
->IsFdo
) {
1077 ClasspEjectionControl(
1081 (BOOLEAN
)!irpStack
->Parameters
.UsageNotification
.InPath
);
1086 // set the event so the next one can occur.
1089 KeSetEvent(&commonExtension
->PathCountEvent
,
1090 IO_NO_INCREMENT
, FALSE
);
1091 KeLeaveCriticalRegion();
1095 case DeviceUsageTypeHibernation
: {
1097 IoAdjustPagingPathCount(
1098 &commonExtension
->HibernationPathCount
,
1099 irpStack
->Parameters
.UsageNotification
.InPath
1101 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1102 if (!NT_SUCCESS(status
)) {
1103 IoAdjustPagingPathCount(
1104 &commonExtension
->HibernationPathCount
,
1105 !irpStack
->Parameters
.UsageNotification
.InPath
1112 case DeviceUsageTypeDumpFile
: {
1113 IoAdjustPagingPathCount(
1114 &commonExtension
->DumpPathCount
,
1115 irpStack
->Parameters
.UsageNotification
.InPath
1117 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1118 if (!NT_SUCCESS(status
)) {
1119 IoAdjustPagingPathCount(
1120 &commonExtension
->DumpPathCount
,
1121 !irpStack
->Parameters
.UsageNotification
.InPath
1129 status
= STATUS_INVALID_PARAMETER
;
1136 case IRP_MN_QUERY_CAPABILITIES
: {
1138 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1139 DeviceObject
, Irp
));
1143 status
= ClassQueryPnpCapabilities(
1145 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
1152 PDEVICE_CAPABILITIES deviceCapabilities
;
1153 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
1154 PCLASS_PRIVATE_FDO_DATA fdoData
;
1156 fdoExtension
= DeviceObject
->DeviceExtension
;
1157 fdoData
= fdoExtension
->PrivateFdoData
;
1158 deviceCapabilities
=
1159 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
;
1162 // forward the irp before handling the special cases
1165 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1166 if (!NT_SUCCESS(status
)) {
1171 // we generally want to remove the device from the hotplug
1172 // applet, which requires the SR-OK bit to be set.
1173 // only when the user specifies that they are capable of
1174 // safely removing things do we want to clear this bit
1175 // (saved in WriteCacheEnableOverride)
1177 // setting of this bit is done either above, or by the
1180 // note: may not be started, so check we have FDO data first.
1184 fdoData
->HotplugInfo
.WriteCacheEnableOverride
) {
1185 if (deviceCapabilities
->SurpriseRemovalOK
) {
1186 DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
1187 "device capabilities due to hotplug "
1188 "device or media\n"));
1190 deviceCapabilities
->SurpriseRemovalOK
= FALSE
;
1194 } // end QUERY_CAPABILITIES for FDOs
1200 } // end QUERY_CAPABILITIES
1205 IoCopyCurrentIrpStackLocationToNext(Irp
);
1207 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1208 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
1210 completeRequest
= FALSE
;
1218 ASSERT(driverExtension
);
1219 status
= STATUS_INTERNAL_ERROR
;
1222 if (completeRequest
){
1223 Irp
->IoStatus
.Status
= status
;
1226 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1229 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1231 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject
, Irp
, commonExtension
->PreviousState
, commonExtension
->CurrentState
));
1235 * The irp is already completed so don't touch it.
1236 * This may be a remove so don't touch the device extension.
1238 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject
, Irp
));
1242 } // end ClassDispatchPnp()
1244 /*++////////////////////////////////////////////////////////////////////////////
1246 ClassPnpStartDevice()
1248 Routine Description:
1250 Storage class driver routine for IRP_MN_START_DEVICE requests.
1251 This routine kicks off any device specific initialization
1255 DeviceObject - a pointer to the device object
1257 Irp - a pointer to the io request packet
1264 NTSTATUS
ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject
)
1266 PCLASS_DRIVER_EXTENSION driverExtension
;
1267 PCLASS_INIT_DATA initData
;
1269 PCLASS_DEV_INFO devInfo
;
1271 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1272 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
1273 BOOLEAN isFdo
= commonExtension
->IsFdo
;
1275 BOOLEAN isMountedDevice
= TRUE
;
1276 UNICODE_STRING interfaceName
;
1278 BOOLEAN timerStarted
;
1280 NTSTATUS status
= STATUS_SUCCESS
;
1284 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
1285 CLASS_DRIVER_EXTENSION_KEY
);
1287 initData
= &(driverExtension
->InitData
);
1289 devInfo
= &(initData
->FdoData
);
1291 devInfo
= &(initData
->PdoData
);
1294 ASSERT(devInfo
->ClassInitDevice
!= NULL
);
1295 ASSERT(devInfo
->ClassStartDevice
!= NULL
);
1297 if (!commonExtension
->IsInitialized
){
1300 // perform FDO/PDO specific initialization
1304 STORAGE_PROPERTY_ID propertyId
;
1307 // allocate a private extension for class data
1310 if (fdoExtension
->PrivateFdoData
== NULL
) {
1311 fdoExtension
->PrivateFdoData
=
1312 ExAllocatePoolWithTag(NonPagedPool
,
1313 sizeof(CLASS_PRIVATE_FDO_DATA
),
1314 CLASS_TAG_PRIVATE_DATA
1318 if (fdoExtension
->PrivateFdoData
== NULL
) {
1319 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
1320 "private fdo data\n"));
1321 return STATUS_INSUFFICIENT_RESOURCES
;
1325 // initialize the struct's various fields.
1328 RtlZeroMemory(fdoExtension
->PrivateFdoData
,
1329 sizeof(CLASS_PRIVATE_FDO_DATA
)
1331 KeInitializeTimer(&fdoExtension
->PrivateFdoData
->Retry
.Timer
);
1332 KeInitializeDpc(&fdoExtension
->PrivateFdoData
->Retry
.Dpc
,
1333 ClasspRetryRequestDpc
,
1335 KeInitializeSpinLock(&fdoExtension
->PrivateFdoData
->Retry
.Lock
);
1336 fdoExtension
->PrivateFdoData
->Retry
.Granularity
=
1337 KeQueryTimeIncrement();
1338 commonExtension
->Reserved4
= (ULONG_PTR
)(' GPH'); // debug aid
1341 // NOTE: the old interface allowed the class driver to allocate
1342 // this. this was unsafe for low-memory conditions. allocate one
1343 // unconditionally now, and modify our internal functions to use
1344 // our own exclusively as it is the only safe way to do this.
1347 status
= ClasspAllocateReleaseQueueIrp(fdoExtension
);
1348 if (!NT_SUCCESS(status
)) {
1349 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
1350 "private release queue irp\n"));
1355 // Call port driver to get adapter capabilities.
1358 propertyId
= StorageAdapterProperty
;
1360 status
= ClassGetDescriptor(
1361 commonExtension
->LowerDeviceObject
,
1363 &fdoExtension
->AdapterDescriptor
);
1365 if(!NT_SUCCESS(status
)) {
1368 // This DebugPrint is to help third-party driver writers
1371 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1372 "[ADAPTER] failed %lx\n", status
));
1377 // Call port driver to get device descriptor.
1380 propertyId
= StorageDeviceProperty
;
1382 status
= ClassGetDescriptor(
1383 commonExtension
->LowerDeviceObject
,
1385 &fdoExtension
->DeviceDescriptor
);
1387 if(!NT_SUCCESS(status
)) {
1390 // This DebugPrint is to help third-party driver writers
1393 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1394 "[DEVICE] failed %lx\n", status
));
1398 ClasspScanForSpecialInRegistry(fdoExtension
);
1399 ClassScanForSpecial(fdoExtension
,
1401 ClasspScanForClassHacks
);
1404 // allow perf to be re-enabled after a given number of failed IOs
1405 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
1410 ClassGetDeviceParameter(fdoExtension
,
1411 CLASSP_REG_SUBKEY_NAME
,
1412 CLASSP_REG_PERF_RESTORE_VALUE_NAME
,
1414 if (t
>= CLASS_PERF_RESTORE_MINIMUM
) {
1415 fdoExtension
->PrivateFdoData
->Perf
.ReEnableThreshhold
= t
;
1421 // compatibility comes first. writable cd media will not
1422 // get a SYNCH_CACHE on power down.
1425 if (fdoExtension
->DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) {
1426 SET_FLAG(fdoExtension
->PrivateFdoData
->HackFlags
,
1427 FDO_HACK_NO_SYNC_CACHE
);
1431 // initialize the hotplug information only after the ScanForSpecial
1432 // routines, as it relies upon the hack flags.
1435 status
= ClasspInitializeHotplugInfo(fdoExtension
);
1437 if (!NT_SUCCESS(status
)) {
1438 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
1439 "hotplug information %lx\n", status
));
1444 * Allocate/initialize TRANSFER_PACKETs and related resources.
1446 status
= InitializeTransferPackets(DeviceObject
);
1450 // ISSUE - drivers need to disable write caching on the media
1451 // if hotplug and !useroverride. perhaps we should
1452 // allow registration of a callback to enable/disable
1453 // write cache instead.
1456 if (NT_SUCCESS(status
)){
1457 status
= devInfo
->ClassInitDevice(DeviceObject
);
1462 if (!NT_SUCCESS(status
)){
1465 // Just bail out - the remove that comes down will clean up the
1466 // initialized scraps.
1471 commonExtension
->IsInitialized
= TRUE
;
1473 if (commonExtension
->IsFdo
) {
1474 fdoExtension
->PrivateFdoData
->Perf
.OriginalSrbFlags
= fdoExtension
->SrbFlags
;
1480 // If device requests autorun functionality or a once a second callback
1481 // then enable the once per second timer.
1483 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
1484 // called in the context of the ClassInitDevice callback. If called
1485 // after then this check will have already been made and the
1486 // once a second timer will not have been enabled.
1489 ((initData
->ClassTick
!= NULL
) ||
1490 (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) ||
1491 ((fdoExtension
->FailurePredictionInfo
!= NULL
) &&
1492 (fdoExtension
->FailurePredictionInfo
->Method
!= FailurePredictionNone
))))
1494 ClasspEnableTimer(DeviceObject
);
1495 timerStarted
= TRUE
;
1497 timerStarted
= FALSE
;
1501 // NOTE: the timer looks at commonExtension->CurrentState now
1502 // to prevent Media Change Notification code from running
1503 // until the device is started, but allows the device
1504 // specific tick handler to run. therefore it is imperative
1505 // that commonExtension->CurrentState not be updated until
1506 // the device specific startdevice handler has finished.
1509 status
= devInfo
->ClassStartDevice(DeviceObject
);
1511 if(NT_SUCCESS(status
)) {
1512 commonExtension
->CurrentState
= IRP_MN_START_DEVICE
;
1514 if((isFdo
) && (initData
->ClassEnumerateDevice
!= NULL
)) {
1515 isMountedDevice
= FALSE
;
1518 if((DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) &&
1519 (DeviceObject
->DeviceType
!= FILE_DEVICE_CD_ROM
)) {
1521 isMountedDevice
= FALSE
;
1525 if(isMountedDevice
) {
1526 ClasspRegisterMountedDeviceInterface(DeviceObject
);
1529 if((commonExtension
->IsFdo
) &&
1530 (devInfo
->ClassWmiInfo
.GuidRegInfo
!= NULL
)) {
1532 IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_REGISTER
);
1537 ClasspDisableTimer(DeviceObject
);
1545 /*++////////////////////////////////////////////////////////////////////////////
1549 Routine Description:
1551 This is the system entry point for read and write requests. The
1552 device-specific handler is invoked to perform any validation necessary.
1554 If the device object is a PDO (partition object) then the request will
1555 simply be adjusted for Partition0 and issued to the lower device driver.
1557 IF the device object is an FDO (paritition 0 object), the number of bytes
1558 in the request are checked against the maximum byte counts that the adapter
1559 supports and requests are broken up into
1560 smaller sizes if necessary.
1564 DeviceObject - a pointer to the device object for this request
1573 NTSTATUS
ClassReadWrite(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
1575 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1576 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
1577 PIO_STACK_LOCATION currentIrpStack
= IoGetCurrentIrpStackLocation(Irp
);
1578 LARGE_INTEGER startingOffset
= currentIrpStack
->Parameters
.Read
.ByteOffset
;
1579 ULONG transferByteCount
= currentIrpStack
->Parameters
.Read
.Length
;
1584 * Grab the remove lock. If we can't acquire it, bail out.
1586 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
1588 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1589 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1590 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1591 status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1593 else if (TEST_FLAG(DeviceObject
->Flags
, DO_VERIFY_VOLUME
) &&
1594 (currentIrpStack
->MinorFunction
!= CLASSP_VOLUME_VERIFY_CHECKED
) &&
1595 !TEST_FLAG(currentIrpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
)){
1598 * DO_VERIFY_VOLUME is set for the device object,
1599 * but this request is not itself a verify request.
1600 * So fail this request.
1602 IoSetHardErrorOrVerifyDevice(Irp
, DeviceObject
);
1603 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
1604 Irp
->IoStatus
.Information
= 0;
1605 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1606 ClassCompleteRequest(DeviceObject
, Irp
, 0);
1607 status
= STATUS_VERIFY_REQUIRED
;
1612 * Since we've bypassed the verify-required tests we don't need to repeat
1613 * them with this IRP - in particular we don't want to worry about
1614 * hitting them at the partition 0 level if the request has come through
1615 * a non-zero partition.
1617 currentIrpStack
->MinorFunction
= CLASSP_VOLUME_VERIFY_CHECKED
;
1620 * Call the miniport driver's pre-pass filter to check if we
1621 * should continue with this transfer.
1623 ASSERT(commonExtension
->DevInfo
->ClassReadWriteVerification
);
1624 status
= commonExtension
->DevInfo
->ClassReadWriteVerification(DeviceObject
, Irp
);
1625 if (!NT_SUCCESS(status
)){
1626 ASSERT(Irp
->IoStatus
.Status
== status
);
1627 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1628 ClassCompleteRequest (DeviceObject
, Irp
, IO_NO_INCREMENT
);
1630 else if (status
== STATUS_PENDING
){
1632 * ClassReadWriteVerification queued this request.
1633 * So don't touch the irp anymore.
1638 if (transferByteCount
== 0) {
1640 * Several parts of the code turn 0 into 0xffffffff,
1641 * so don't process a zero-length request any further.
1643 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1644 Irp
->IoStatus
.Information
= 0;
1645 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1646 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1647 status
= STATUS_SUCCESS
;
1651 * If the driver has its own StartIo routine, call it.
1653 if (commonExtension
->DriverExtension
->InitData
.ClassStartIo
) {
1654 IoMarkIrpPending(Irp
);
1655 IoStartPacket(DeviceObject
, Irp
, NULL
, NULL
);
1656 status
= STATUS_PENDING
;
1660 * The driver does not have its own StartIo routine.
1661 * So process this request ourselves.
1665 * Add partition byte offset to make starting byte relative to
1666 * beginning of disk.
1668 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1669 commonExtension
->StartingOffset
.QuadPart
;
1671 if (commonExtension
->IsFdo
){
1674 * Add in any skew for the disk manager software.
1676 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1677 commonExtension
->PartitionZeroExtension
->DMByteSkew
;
1680 * Perform the actual transfer(s) on the hardware
1681 * to service this request.
1683 ServiceTransferRequest(DeviceObject
, Irp
);
1684 status
= STATUS_PENDING
;
1688 * This is a child PDO enumerated for our FDO by e.g. disk.sys
1689 * and owned by e.g. partmgr. Send it down to the next device
1690 * and the same irp will come back to us for the FDO.
1692 IoCopyCurrentIrpStackLocationToNext(Irp
);
1693 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1694 status
= IoCallDriver(lowerDeviceObject
, Irp
);
1705 /*++////////////////////////////////////////////////////////////////////////////
1707 ClassReadDriveCapacity()
1709 Routine Description:
1711 This routine sends a READ CAPACITY to the requested device, updates
1712 the geometry information in the device object and returns
1713 when it is complete. This routine is synchronous.
1715 This routine must be called with the remove lock held or some other
1716 assurance that the Fdo will not be removed while processing.
1720 DeviceObject - Supplies a pointer to the device object that represents
1721 the device whose capacity is to be read.
1728 NTSTATUS
ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo
)
1730 READ_CAPACITY_DATA readCapacityBuffer
= {0};
1734 driveCapMdl
= BuildDeviceInputMdl(&readCapacityBuffer
, sizeof(READ_CAPACITY_DATA
));
1737 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1739 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
1742 IRP pseudoIrp
= {0};
1745 * Our engine needs an "original irp" to write the status back to
1746 * and to count down packets (one in this case).
1747 * Just use a pretend irp for this.
1749 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1750 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1751 pseudoIrp
.IoStatus
.Information
= 0;
1752 pseudoIrp
.MdlAddress
= driveCapMdl
;
1755 * Set this up as a SYNCHRONOUS transfer, submit it,
1756 * and wait for the packet to complete. The result
1757 * status will be written to the original irp.
1759 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1760 SetupDriveCapacityTransferPacket( pkt
,
1761 &readCapacityBuffer
,
1762 sizeof(READ_CAPACITY_DATA
),
1765 SubmitTransferPacket(pkt
);
1766 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1768 status
= pseudoIrp
.IoStatus
.Status
;
1771 * If we got an UNDERRUN, retry exactly once.
1772 * (The transfer_packet engine didn't retry because the result
1773 * status was success).
1775 if (NT_SUCCESS(status
) &&
1776 (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
))){
1777 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG
)pseudoIrp
.IoStatus
.Information
, sizeof(READ_CAPACITY_DATA
)));
1779 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1781 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1782 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1783 pseudoIrp
.IoStatus
.Information
= 0;
1784 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1785 SetupDriveCapacityTransferPacket( pkt
,
1786 &readCapacityBuffer
,
1787 sizeof(READ_CAPACITY_DATA
),
1790 SubmitTransferPacket(pkt
);
1791 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1792 status
= pseudoIrp
.IoStatus
.Status
;
1793 if (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
)){
1794 status
= STATUS_DEVICE_BUSY
;
1798 status
= STATUS_INSUFFICIENT_RESOURCES
;
1803 if (NT_SUCCESS(status
)){
1805 * The request succeeded.
1806 * Read out and store the drive information.
1809 ULONG bytesPerSector
;
1814 * Read the bytesPerSector value,
1815 * which is big-endian in the returned buffer.
1816 * Default to the standard 512 bytes.
1818 tmp
= readCapacityBuffer
.BytesPerBlock
;
1819 ((PFOUR_BYTE
)&bytesPerSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1820 ((PFOUR_BYTE
)&bytesPerSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1821 ((PFOUR_BYTE
)&bytesPerSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1822 ((PFOUR_BYTE
)&bytesPerSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1823 if (bytesPerSector
== 0) {
1824 bytesPerSector
= 512;
1828 * Clear all but the highest set bit.
1829 * That will give us a bytesPerSector value that is a power of 2.
1831 while (bytesPerSector
& (bytesPerSector
-1)) {
1832 bytesPerSector
&= bytesPerSector
-1;
1835 fdoExt
->DiskGeometry
.BytesPerSector
= bytesPerSector
;
1838 // Copy last sector in reverse byte order.
1841 tmp
= readCapacityBuffer
.LogicalBlockAddress
;
1842 ((PFOUR_BYTE
)&lastSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1843 ((PFOUR_BYTE
)&lastSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1844 ((PFOUR_BYTE
)&lastSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1845 ((PFOUR_BYTE
)&lastSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1848 // Calculate sector to byte shift.
1851 WHICH_BIT(fdoExt
->DiskGeometry
.BytesPerSector
, fdoExt
->SectorShift
);
1853 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
1854 fdoExt
->DiskGeometry
.BytesPerSector
));
1856 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
1859 if (fdoExt
->DMActive
){
1860 DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
1862 lastSector
-= fdoExt
->DMSkew
;
1866 * Check to see if we have a geometry we should be using already.
1868 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1869 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1870 if (cylinderSize
== 0){
1871 DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
1872 "values from %#x/%#x to %#x/%#x\n",
1873 fdoExt
->DiskGeometry
.TracksPerCylinder
,
1874 fdoExt
->DiskGeometry
.SectorsPerTrack
,
1878 fdoExt
->DiskGeometry
.TracksPerCylinder
= 0xff;
1879 fdoExt
->DiskGeometry
.SectorsPerTrack
= 0x3f;
1882 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1883 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1887 // Calculate number of cylinders.
1890 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= (LONGLONG
)((lastSector
+ 1)/cylinderSize
);
1893 // if there are zero cylinders, then the device lied AND it's
1894 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
1895 // this can fit into a single LONGLONG, so create another usable
1896 // geometry, even if it's unusual looking. This allows small,
1897 // non-standard devices, such as Sony's Memory Stick, to show
1898 // up as having a partition.
1901 if (fdoExt
->DiskGeometry
.Cylinders
.QuadPart
== (LONGLONG
)0) {
1902 fdoExt
->DiskGeometry
.SectorsPerTrack
= 1;
1903 fdoExt
->DiskGeometry
.TracksPerCylinder
= 1;
1904 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= lastSector
;
1909 // Calculate media capacity in bytes.
1912 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
=
1913 ((LONGLONG
)(lastSector
+ 1)) << fdoExt
->SectorShift
;
1916 * Is this removable or fixed media
1918 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1919 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1922 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1927 * The request failed.
1931 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
1932 // what happens when the disk's sector size is bigger than
1933 // 512 bytes and we hit this code path? this is untested.
1935 // If the read capacity fails, set the geometry to reasonable parameter
1936 // so things don't fail at unexpected places. Zero the geometry
1937 // except for the bytes per sector and sector shift.
1941 * This request can sometimes fail legitimately
1942 * (e.g. when a SCSI device is attached but turned off)
1943 * so this is not necessarily a device/driver bug.
1945 DBGTRACE(ClassDebugWarning
, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo
, status
));
1948 * Write in a default disk geometry which we HOPE is right (??).
1951 RtlZeroMemory(&fdoExt
->DiskGeometry
, sizeof(DISK_GEOMETRY
));
1952 fdoExt
->DiskGeometry
.BytesPerSector
= 512;
1953 fdoExt
->SectorShift
= 9;
1954 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
= (LONGLONG
) 0;
1957 * Is this removable or fixed media
1959 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1960 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1963 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1969 status
= STATUS_INSUFFICIENT_RESOURCES
;
1972 FreeDeviceInputMdl(driveCapMdl
);
1975 status
= STATUS_INSUFFICIENT_RESOURCES
;
1982 /*++////////////////////////////////////////////////////////////////////////////
1984 ClassSendStartUnit()
1986 Routine Description:
1988 Send command to SCSI unit to start or power up.
1989 Because this command is issued asynchronounsly, that is, without
1990 waiting on it to complete, the IMMEDIATE flag is not set. This
1991 means that the CDB will not return until the drive has powered up.
1992 This should keep subsequent requests from being submitted to the
1993 device before it has completely spun up.
1995 This routine is called from the InterpretSense routine, when a
1996 request sense returns data indicating that a drive must be
1999 This routine may also be called from a class driver's error handler,
2000 or anytime a non-critical start device should be sent to the device.
2004 Fdo - The functional device object for the stopped device.
2013 IN PDEVICE_OBJECT Fdo
2016 PIO_STACK_LOCATION irpStack
;
2018 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2019 PSCSI_REQUEST_BLOCK srb
;
2020 PCOMPLETION_CONTEXT context
;
2024 // Allocate Srb from nonpaged pool.
2027 context
= ExAllocatePoolWithTag(NonPagedPool
,
2028 sizeof(COMPLETION_CONTEXT
),
2031 if(context
== NULL
) {
2034 // ISSUE-2000/02/03-peterwie
2035 // This code path was inheritted from the NT 4.0 class2.sys driver.
2036 // It needs to be changed to survive low-memory conditions.
2039 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2043 // Save the device object in the context for use by the completion
2047 context
->DeviceObject
= Fdo
;
2048 srb
= &context
->Srb
;
2054 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
2057 // Write length to SRB.
2060 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2062 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2065 // Set timeout value large enough for drive to spin up.
2068 srb
->TimeOutValue
= START_UNIT_TIMEOUT
;
2071 // Set the transfer length.
2074 srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
|
2075 SRB_FLAGS_DISABLE_AUTOSENSE
|
2076 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
;
2079 // Build the start unit CDB.
2083 cdb
= (PCDB
)srb
->Cdb
;
2085 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
2086 cdb
->START_STOP
.Start
= 1;
2087 cdb
->START_STOP
.Immediate
= 0;
2088 cdb
->START_STOP
.LogicalUnitNumber
= srb
->Lun
;
2091 // Build the asynchronous request to be sent to the port driver.
2092 // Since this routine is called from a DPC the IRP should always be
2096 irp
= IoAllocateIrp(Fdo
->StackSize
, FALSE
);
2101 // ISSUE-2000/02/03-peterwie
2102 // This code path was inheritted from the NT 4.0 class2.sys driver.
2103 // It needs to be changed to survive low-memory conditions.
2106 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2110 ClassAcquireRemoveLock(Fdo
, irp
);
2112 IoSetCompletionRoutine(irp
,
2113 (PIO_COMPLETION_ROUTINE
)ClassAsynchronousCompletion
,
2119 irpStack
= IoGetNextIrpStackLocation(irp
);
2120 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2121 srb
->OriginalRequest
= irp
;
2124 // Store the SRB address in next stack for port driver.
2127 irpStack
->Parameters
.Scsi
.Srb
= srb
;
2130 // Call the port driver with the IRP.
2133 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2137 } // end StartUnit()
2139 /*++////////////////////////////////////////////////////////////////////////////
2141 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
2143 Routine Description:
2145 This routine is called when an asynchronous I/O request
2146 which was issused by the class driver completes. Examples of such requests
2147 are release queue or START UNIT. This routine releases the queue if
2148 necessary. It then frees the context and the IRP.
2152 DeviceObject - The device object for the logical unit; however since this
2153 is the top stack location the value is NULL.
2155 Irp - Supplies a pointer to the Irp to be processed.
2157 Context - Supplies the context to be used to process this request.
2165 ClassAsynchronousCompletion(
2166 PDEVICE_OBJECT DeviceObject
,
2171 PCOMPLETION_CONTEXT context
= Context
;
2172 PSCSI_REQUEST_BLOCK srb
;
2174 if(DeviceObject
== NULL
) {
2176 DeviceObject
= context
->DeviceObject
;
2179 srb
= &context
->Srb
;
2182 // If this is an execute srb, then check the return status and make sure.
2183 // the queue is not frozen.
2186 if (srb
->Function
== SRB_FUNCTION_EXECUTE_SCSI
) {
2189 // Check for a frozen queue.
2192 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2195 // Unfreeze the queue getting the device object from the context.
2198 ClassReleaseQueue(context
->DeviceObject
);
2202 { // free port-allocated sense buffer if we can detect
2204 if (((PCOMMON_DEVICE_EXTENSION
)(DeviceObject
->DeviceExtension
))->IsFdo
) {
2206 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
2207 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2208 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2213 ASSERT(!TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
2220 // Free the context and the Irp.
2223 if (Irp
->MdlAddress
!= NULL
) {
2224 MmUnlockPages(Irp
->MdlAddress
);
2225 IoFreeMdl(Irp
->MdlAddress
);
2227 Irp
->MdlAddress
= NULL
;
2230 ClassReleaseRemoveLock(DeviceObject
, Irp
);
2232 ExFreePool(context
);
2236 // Indicate the I/O system should stop processing the Irp completion.
2239 return STATUS_MORE_PROCESSING_REQUIRED
;
2241 } // end ClassAsynchronousCompletion()
2245 VOID
ServiceTransferRequest(PDEVICE_OBJECT Fdo
, PIRP Irp
)
2247 PCOMMON_DEVICE_EXTENSION commonExt
= Fdo
->DeviceExtension
;
2248 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
2249 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
2250 PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc
= commonExt
->PartitionZeroExtension
->AdapterDescriptor
;
2251 PIO_STACK_LOCATION currentSp
= IoGetCurrentIrpStackLocation(Irp
);
2252 ULONG entireXferLen
= currentSp
->Parameters
.Read
.Length
;
2253 PUCHAR bufPtr
= MmGetMdlVirtualAddress(Irp
->MdlAddress
);
2254 LARGE_INTEGER targetLocation
= currentSp
->Parameters
.Read
.ByteOffset
;
2255 PTRANSFER_PACKET pkt
;
2256 SINGLE_LIST_ENTRY pktList
;
2257 PSINGLE_LIST_ENTRY slistEntry
;
2263 * Compute the number of hw xfers we'll have to do.
2264 * Calculate this without allowing for an overflow condition.
2266 ASSERT(fdoData
->HwMaxXferLen
>= PAGE_SIZE
);
2267 numPackets
= entireXferLen
/fdoData
->HwMaxXferLen
;
2268 if (entireXferLen
% fdoData
->HwMaxXferLen
){
2273 * First get all the TRANSFER_PACKETs that we'll need at once.
2274 * Use our 'simple' slist functions since we don't need interlocked.
2276 SimpleInitSlistHdr(&pktList
);
2277 for (i
= 0; i
< numPackets
; i
++){
2278 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
2280 SimplePushSlist(&pktList
, &pkt
->SlistEntry
);
2287 if (i
== numPackets
){
2289 * Initialize the original IRP's status to success.
2290 * If any of the packets fail, they will set it to an error status.
2291 * The IoStatus.Information field will be incremented to the
2292 * transfer length as the pieces complete.
2294 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2295 Irp
->IoStatus
.Information
= 0;
2298 * Store the number of transfer pieces inside the original IRP.
2299 * It will be used to count down the pieces as they complete.
2301 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(numPackets
);
2304 * We are proceeding with the transfer.
2305 * Mark the client IRP pending since it may complete on a different thread.
2307 IoMarkIrpPending(Irp
);
2310 * Transmit the pieces of the transfer.
2312 while (entireXferLen
> 0){
2313 ULONG thisPieceLen
= MIN(fdoData
->HwMaxXferLen
, entireXferLen
);
2316 * Set up a TRANSFER_PACKET for this piece and send it.
2318 slistEntry
= SimplePopSlist(&pktList
);
2320 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2321 SetupReadWriteTransferPacket( pkt
,
2326 SubmitTransferPacket(pkt
);
2328 entireXferLen
-= thisPieceLen
;
2329 bufPtr
+= thisPieceLen
;
2330 targetLocation
.QuadPart
+= thisPieceLen
;
2332 ASSERT(SimpleIsSlistEmpty(&pktList
));
2336 * We were unable to get all the TRANSFER_PACKETs we need,
2337 * but we did get at least one.
2338 * That means that we are in extreme low-memory stress.
2339 * We'll try doing this transfer using a single packet.
2340 * The port driver is certainly also in stress, so use one-page
2345 * Free all but one of the TRANSFER_PACKETs.
2348 slistEntry
= SimplePopSlist(&pktList
);
2350 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2351 EnqueueFreeTransferPacket(Fdo
, pkt
);
2355 * Get the single TRANSFER_PACKET that we'll be using.
2357 slistEntry
= SimplePopSlist(&pktList
);
2359 ASSERT(SimpleIsSlistEmpty(&pktList
));
2360 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2361 DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt
));
2364 * Set default status and the number of transfer packets (one)
2365 * inside the original irp.
2367 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2368 Irp
->IoStatus
.Information
= 0;
2369 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
2372 * Mark the client irp pending since it may complete on
2375 IoMarkIrpPending(Irp
);
2378 * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
2380 SetupReadWriteTransferPacket( pkt
,
2385 InitLowMemRetry(pkt
, bufPtr
, entireXferLen
, targetLocation
);
2386 StepLowMemRetry(pkt
);
2390 * We were unable to get ANY TRANSFER_PACKETs.
2391 * Defer this client irp until some TRANSFER_PACKETs free up.
2393 DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp
));
2394 IoMarkIrpPending(Irp
);
2395 EnqueueDeferredClientIrp(fdoData
, Irp
);
2401 /*++////////////////////////////////////////////////////////////////////////////
2405 Routine Description:
2407 This routine executes when the port driver has completed a request.
2408 It looks at the SRB status in the completing SRB and if not success
2409 it checks for valid request sense buffer information. If valid, the
2410 info is used to update status with more precise message of type of
2411 error. This routine deallocates the SRB.
2413 This routine should only be placed on the stack location for a class
2418 Fdo - Supplies the device object which represents the logical
2421 Irp - Supplies the Irp which has completed.
2423 Context - Supplies a pointer to the SRB.
2432 IN PDEVICE_OBJECT Fdo
,
2437 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2438 PSCSI_REQUEST_BLOCK srb
= Context
;
2439 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2440 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2443 BOOLEAN callStartNextPacket
;
2445 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2448 // Check SRB status for success of completing request.
2451 if (SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2452 ULONG retryInterval
;
2454 DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp
, srb
));
2457 // Release the queue if it is frozen.
2460 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2461 ClassReleaseQueue(Fdo
);
2464 retry
= ClassInterpretSenseInfo(
2467 irpStack
->MajorFunction
,
2468 irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
?
2469 irpStack
->Parameters
.DeviceIoControl
.IoControlCode
:
2472 ((ULONG
)(ULONG_PTR
)irpStack
->Parameters
.Others
.Argument4
),
2477 // If the status is verified required and the this request
2478 // should bypass verify required then retry the request.
2481 if (TEST_FLAG(irpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
) &&
2482 status
== STATUS_VERIFY_REQUIRED
) {
2484 status
= STATUS_IO_DEVICE_ERROR
;
2488 if (retry
&& (irpStack
->Parameters
.Others
.Argument4
--)) {
2494 DebugPrint((1, "Retry request %p\n", Irp
));
2496 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2497 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2500 RetryRequest(Fdo
, Irp
, srb
, FALSE
, retryInterval
);
2501 return STATUS_MORE_PROCESSING_REQUIRED
;
2507 // Set status for successful request
2509 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2510 ClasspPerfIncrementSuccessfulIo(fdoExtension
);
2511 status
= STATUS_SUCCESS
;
2512 } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
2516 // ensure we have returned some info, and it matches what the
2517 // original request wanted for PAGING operations only
2520 if ((NT_SUCCESS(status
)) && TEST_FLAG(Irp
->Flags
, IRP_PAGING_IO
)) {
2521 ASSERT(Irp
->IoStatus
.Information
!= 0);
2522 ASSERT(irpStack
->Parameters
.Read
.Length
== Irp
->IoStatus
.Information
);
2526 // remember if the caller wanted to skip calling IoStartNextPacket.
2527 // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
2528 // calls. this setting only affects device objects with StartIo routines.
2531 callStartNextPacket
= !TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_DONT_START_NEXT_PACKET
);
2532 if (irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
) {
2533 callStartNextPacket
= FALSE
;
2540 if(!TEST_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_PERSISTANT
)) {
2542 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2543 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2546 if (fdoExtension
->CommonExtension
.IsSrbLookasideListInitialized
){
2547 ClassFreeOrReuseSrb(fdoExtension
, srb
);
2550 DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
2556 DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
2557 "SRB_CLASS_FLAGS_PERSISTANT set\n", srb
));
2558 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2559 DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
2560 " because SRB_CLASS_FLAGS_PERSISTANT set\n",
2561 srb
->SenseInfoBuffer
));
2567 // Set status in completing IRP.
2570 Irp
->IoStatus
.Status
= status
;
2573 // Set the hard error if necessary.
2576 if (!NT_SUCCESS(status
) &&
2577 IoIsErrorUserInduced(status
) &&
2578 (Irp
->Tail
.Overlay
.Thread
!= NULL
)
2582 // Store DeviceObject for filesystem, and clear
2583 // in IoStatus.Information field.
2586 IoSetHardErrorOrVerifyDevice(Irp
, Fdo
);
2587 Irp
->IoStatus
.Information
= 0;
2591 // If pending has be returned for this irp then mark the current stack as
2595 if (Irp
->PendingReturned
) {
2596 IoMarkIrpPending(Irp
);
2599 if (fdoExtension
->CommonExtension
.DriverExtension
->InitData
.ClassStartIo
) {
2600 if (callStartNextPacket
) {
2602 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
2603 IoStartNextPacket(Fdo
, FALSE
);
2604 KeLowerIrql(oldIrql
);
2608 ClassReleaseRemoveLock(Fdo
, Irp
);
2612 } // end ClassIoComplete()
2615 /*++////////////////////////////////////////////////////////////////////////////
2617 ClassSendSrbSynchronous()
2619 Routine Description:
2621 This routine is called by SCSI device controls to complete an
2622 SRB and send it to the port driver synchronously (ie wait for
2623 completion). The CDB is already completed along with the SRB CDB
2624 size and request timeout value.
2628 Fdo - Supplies the functional device object which represents the target.
2630 Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
2632 BufferAddress - Supplies the address of the buffer.
2634 BufferLength - Supplies the length in bytes of the buffer.
2636 WriteToDevice - Indicates the data should be transfer to the device.
2640 NTSTATUS indicating the final results of the operation.
2642 If NT_SUCCESS(), then the amount of usable data is contained in the field
2643 Srb->DataTransferLength
2647 ClassSendSrbSynchronous(
2649 PSCSI_REQUEST_BLOCK Srb
,
2650 PVOID BufferAddress
,
2652 BOOLEAN WriteToDevice
2656 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2657 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2658 IO_STATUS_BLOCK ioStatus
;
2661 PIO_STACK_LOCATION irpStack
;
2663 PUCHAR senseInfoBuffer
;
2664 ULONG retryCount
= MAXIMUM_RETRIES
;
2669 // NOTE: This code is only pagable because we are not freezing
2670 // the queue. Allowing the queue to be frozen from a pagable
2671 // routine could leave the queue frozen as we try to page in
2672 // the code to unfreeze the queue. The result would be a nice
2673 // case of deadlock. Therefore, since we are unfreezing the
2674 // queue regardless of the result, just set the NO_FREEZE_QUEUE
2678 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2679 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2682 // Write length to SRB.
2685 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2688 // Set SCSI bus address.
2691 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2694 // Enable auto request sense.
2697 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
2700 // Sense buffer is in aligned nonpaged pool.
2703 senseInfoBuffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
2707 if (senseInfoBuffer
== NULL
) {
2709 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
2711 return(STATUS_INSUFFICIENT_RESOURCES
);
2714 Srb
->SenseInfoBuffer
= senseInfoBuffer
;
2715 Srb
->DataBuffer
= BufferAddress
;
2718 // Start retries here.
2724 // use fdoextension's flags by default.
2725 // do not move out of loop, as the flag may change due to errors
2726 // sending this command.
2729 Srb
->SrbFlags
= fdoExtension
->SrbFlags
;
2731 if(BufferAddress
!= NULL
) {
2733 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_OUT
);
2735 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
2740 // Initialize the QueueAction field.
2743 Srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
2746 // Disable synchronous transfer for these requests.
2747 // Disable freezing the queue, since all we do is unfreeze it anyways.
2750 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
2751 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
2754 // Set the event object to the unsignaled state.
2755 // It will be used to signal request completion.
2758 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
2761 // Build device I/O control request with METHOD_NEITHER data transfer.
2762 // We'll queue a completion routine to cleanup the MDL's and such ourself.
2765 irp
= IoAllocateIrp(
2766 (CCHAR
) (fdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
+ 1),
2770 ExFreePool(senseInfoBuffer
);
2771 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
2772 return(STATUS_INSUFFICIENT_RESOURCES
);
2776 // Get next stack location.
2779 irpStack
= IoGetNextIrpStackLocation(irp
);
2782 // Set up SRB for execute scsi request. Save SRB address in next stack
2783 // for the port driver.
2786 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2787 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
2789 IoSetCompletionRoutine(irp
,
2790 ClasspSendSynchronousCompletion
,
2796 irp
->UserIosb
= &ioStatus
;
2797 irp
->UserEvent
= &event
;
2801 // Build an MDL for the data buffer and stick it into the irp. The
2802 // completion routine will unlock the pages and free the MDL.
2805 irp
->MdlAddress
= IoAllocateMdl( BufferAddress
,
2810 if (irp
->MdlAddress
== NULL
) {
2811 ExFreePool(senseInfoBuffer
);
2812 Srb
->SenseInfoBuffer
= NULL
;
2814 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
2815 return STATUS_INSUFFICIENT_RESOURCES
;
2821 // the io manager unlocks these pages upon completion
2824 MmProbeAndLockPages( irp
->MdlAddress
,
2826 (WriteToDevice
? IoReadAccess
:
2829 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
2830 status
= _SEH2_GetExceptionCode();
2832 ExFreePool(senseInfoBuffer
);
2833 Srb
->SenseInfoBuffer
= NULL
;
2834 IoFreeMdl(irp
->MdlAddress
);
2837 DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
2838 "locking buffer\n", status
));
2844 // Set the transfer length.
2847 Srb
->DataTransferLength
= BufferLength
;
2853 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
2857 // Set up IRP Address.
2860 Srb
->OriginalRequest
= irp
;
2863 // Call the port driver with the request and wait for it to complete.
2866 status
= IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2868 if (status
== STATUS_PENDING
) {
2869 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
2870 status
= ioStatus
.Status
;
2874 // Check that request completed without error.
2877 if (SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2879 ULONG retryInterval
;
2881 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
)));
2884 // assert that the queue is not frozen
2887 ASSERT(!TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
2890 // Update status and determine if request should be retried.
2893 retry
= ClassInterpretSenseInfo(Fdo
,
2897 MAXIMUM_RETRIES
- retryCount
,
2904 if ((status
== STATUS_DEVICE_NOT_READY
&&
2905 ((PSENSE_DATA
) senseInfoBuffer
)->AdditionalSenseCode
==
2906 SCSI_ADSENSE_LUN_NOT_READY
) ||
2907 (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)) {
2909 LARGE_INTEGER delay
;
2912 // Delay for at least 2 seconds.
2915 if(retryInterval
< 2) {
2919 delay
.QuadPart
= (LONGLONG
)( - 10 * 1000 * (LONGLONG
)1000 * retryInterval
);
2922 // Stall for a while to let the device become ready
2925 KeDelayExecutionThread(KernelMode
, FALSE
, &delay
);
2930 // If retries are not exhausted then retry this operation.
2935 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2936 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2944 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2945 status
= STATUS_SUCCESS
;
2949 // required even though we allocated our own, since the port driver may
2950 // have allocated one also
2953 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2954 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2957 Srb
->SenseInfoBuffer
= NULL
;
2958 ExFreePool(senseInfoBuffer
);
2964 /*++////////////////////////////////////////////////////////////////////////////
2966 ClassInterpretSenseInfo()
2968 Routine Description:
2970 This routine interprets the data returned from the SCSI
2971 request sense. It determines the status to return in the
2972 IRP and whether this request can be retried.
2976 DeviceObject - Supplies the device object associated with this request.
2978 Srb - Supplies the scsi request block which failed.
2980 MajorFunctionCode - Supplies the function code to be used for logging.
2982 IoDeviceCode - Supplies the device code to be used for logging.
2984 Status - Returns the status for the request.
2988 BOOLEAN TRUE: Drivers should retry this request.
2989 FALSE: Drivers should not retry this request.
2993 ClassInterpretSenseInfo(
2994 IN PDEVICE_OBJECT Fdo
,
2995 IN PSCSI_REQUEST_BLOCK Srb
,
2996 IN UCHAR MajorFunctionCode
,
2997 IN ULONG IoDeviceCode
,
2998 IN ULONG RetryCount
,
2999 OUT NTSTATUS
*Status
,
3000 OUT OPTIONAL ULONG
*RetryInterval
3003 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
3004 PCOMMON_DEVICE_EXTENSION commonExtension
= Fdo
->DeviceExtension
;
3005 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
3007 PSENSE_DATA senseBuffer
= Srb
->SenseInfoBuffer
;
3009 BOOLEAN retry
= TRUE
;
3010 BOOLEAN logError
= FALSE
;
3011 BOOLEAN unhandledError
= FALSE
;
3012 BOOLEAN incrementErrorCount
= FALSE
;
3014 ULONG badSector
= 0;
3022 ULONG retryInterval
= 0;
3028 if(TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3031 // Log anything remotely incorrect about paging i/o
3036 logStatus
= IO_WARNING_PAGING_FAILURE
;
3040 // Check that request sense buffer is valid.
3043 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
3047 // must handle the SRB_STATUS_INTERNAL_ERROR case first,
3048 // as it has all the flags set.
3051 if (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_INTERNAL_ERROR
) {
3053 DebugPrint((ClassDebugSenseInfo
,
3054 "ClassInterpretSenseInfo: Internal Error code is %x\n",
3055 Srb
->InternalStatus
));
3058 *Status
= Srb
->InternalStatus
;
3060 } else if ((Srb
->SrbStatus
& SRB_STATUS_AUTOSENSE_VALID
) &&
3061 (Srb
->SenseInfoBufferLength
>=
3062 offsetof(SENSE_DATA
, CommandSpecificInformation
))) {
3065 // Zero the additional sense code and additional sense code qualifier
3066 // if they were not returned by the device.
3069 readSector
= senseBuffer
->AdditionalSenseLength
+
3070 offsetof(SENSE_DATA
, AdditionalSenseLength
);
3072 if (readSector
> Srb
->SenseInfoBufferLength
) {
3073 readSector
= Srb
->SenseInfoBufferLength
;
3076 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCode
)) {
3077 senseBuffer
->AdditionalSenseCode
= 0;
3080 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCodeQualifier
)) {
3081 senseBuffer
->AdditionalSenseCodeQualifier
= 0;
3084 DebugPrint((ClassDebugSenseInfo
,
3085 "ClassInterpretSenseInfo: Error code is %x\n",
3086 senseBuffer
->ErrorCode
));
3087 DebugPrint((ClassDebugSenseInfo
,
3088 "ClassInterpretSenseInfo: Sense key is %x\n",
3089 senseBuffer
->SenseKey
));
3090 DebugPrint((ClassDebugSenseInfo
,
3091 "ClassInterpretSenseInfo: Additional sense code is %x\n",
3092 senseBuffer
->AdditionalSenseCode
));
3093 DebugPrint((ClassDebugSenseInfo
,
3094 "ClassInterpretSenseInfo: Additional sense code qualifier "
3096 senseBuffer
->AdditionalSenseCodeQualifier
));
3099 switch (senseBuffer
->SenseKey
& 0xf) {
3101 case SCSI_SENSE_NOT_READY
: {
3103 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3104 "Device not ready\n"));
3105 *Status
= STATUS_DEVICE_NOT_READY
;
3107 switch (senseBuffer
->AdditionalSenseCode
) {
3109 case SCSI_ADSENSE_LUN_NOT_READY
: {
3111 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3112 "Lun not ready\n"));
3114 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3116 case SCSI_SENSEQ_OPERATION_IN_PROGRESS
: {
3117 DEVICE_EVENT_BECOMING_READY notReady
;
3119 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3120 "Operation In Progress\n"));
3121 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3123 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3124 notReady
.Version
= 1;
3125 notReady
.Reason
= 2;
3126 notReady
.Estimated100msToReady
= retryInterval
* 10;
3127 ClasspSendNotification(fdoExtension
,
3128 &GUID_IO_DEVICE_BECOMING_READY
,
3129 sizeof(DEVICE_EVENT_BECOMING_READY
),
3135 case SCSI_SENSEQ_BECOMING_READY
: {
3136 DEVICE_EVENT_BECOMING_READY notReady
;
3138 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3139 "In process of becoming ready\n"));
3140 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3142 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3143 notReady
.Version
= 1;
3144 notReady
.Reason
= 1;
3145 notReady
.Estimated100msToReady
= retryInterval
* 10;
3146 ClasspSendNotification(fdoExtension
,
3147 &GUID_IO_DEVICE_BECOMING_READY
,
3148 sizeof(DEVICE_EVENT_BECOMING_READY
),
3153 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS
: {
3154 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3155 "Long write in progress\n"));
3160 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED
: {
3161 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3162 "Manual intervention required\n"));
3163 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3168 case SCSI_SENSEQ_FORMAT_IN_PROGRESS
: {
3169 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3170 "Format in progress\n"));
3175 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE
: {
3177 if(!TEST_FLAG(fdoExtension
->ScanForSpecialFlags
,
3178 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
)) {
3180 DebugPrint((ClassDebugSenseInfo
,
3181 "ClassInterpretSenseInfo: "
3182 "not ready, cause unknown\n"));
3184 Many non-WHQL certified drives (mostly CD-RW) return
3185 this when they have no media instead of the obvious
3188 SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
3190 These drives should not pass WHQL certification due
3191 to this discrepency.
3200 // Treat this as init command required and fall through.
3205 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED
:
3207 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3208 "Initializing command required\n"));
3211 // This sense code/additional sense code
3212 // combination may indicate that the device
3213 // needs to be started. Send an start unit if this
3214 // is a disk device.
3217 if(TEST_FLAG(fdoExtension
->DeviceFlags
,
3218 DEV_SAFE_START_UNIT
) &&
3219 !TEST_FLAG(Srb
->SrbFlags
,
3220 SRB_CLASS_FLAGS_LOW_PRIORITY
)) {
3221 ClassSendStartUnit(Fdo
);
3227 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
3231 case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
: {
3232 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3233 "No Media in device.\n"));
3234 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3238 // signal MCN that there isn't any media in the device
3240 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3241 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3242 "No Media in a non-removable device %p\n",
3245 ClassSetMediaChangeState(fdoExtension
, MediaNotPresent
, FALSE
);
3249 } // end switch (senseBuffer->AdditionalSenseCode)
3252 } // end SCSI_SENSE_NOT_READY
3254 case SCSI_SENSE_DATA_PROTECT
: {
3255 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3256 "Media write protected\n"));
3257 *Status
= STATUS_MEDIA_WRITE_PROTECTED
;
3260 } // end SCSI_SENSE_DATA_PROTECT
3262 case SCSI_SENSE_MEDIUM_ERROR
: {
3263 DebugPrint((ClassDebugSenseInfo
,"ClassInterpretSenseInfo: "
3264 "Medium Error (bad block)\n"));
3265 *Status
= STATUS_DEVICE_DATA_ERROR
;
3270 logStatus
= IO_ERR_BAD_BLOCK
;
3273 // Check if this error is due to unknown format
3275 if (senseBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_INVALID_MEDIA
){
3277 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3279 case SCSI_SENSEQ_UNKNOWN_FORMAT
: {
3281 *Status
= STATUS_UNRECOGNIZED_MEDIA
;
3284 // Log error only if this is a paging request
3286 if(!TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3292 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED
: {
3294 *Status
= STATUS_CLEANER_CARTRIDGE_INSTALLED
;
3302 } // end switch AdditionalSenseCodeQualifier
3304 } // end SCSI_ADSENSE_INVALID_MEDIA
3308 } // end SCSI_SENSE_MEDIUM_ERROR
3310 case SCSI_SENSE_HARDWARE_ERROR
: {
3311 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3312 "Hardware error\n"));
3313 *Status
= STATUS_IO_DEVICE_ERROR
;
3316 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3318 } // end SCSI_SENSE_HARDWARE_ERROR
3320 case SCSI_SENSE_ILLEGAL_REQUEST
: {
3322 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3323 "Illegal SCSI request\n"));
3324 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3327 switch (senseBuffer
->AdditionalSenseCode
) {
3329 case SCSI_ADSENSE_ILLEGAL_COMMAND
: {
3330 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3331 "Illegal command\n"));
3335 case SCSI_ADSENSE_ILLEGAL_BLOCK
: {
3336 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3337 "Illegal block address\n"));
3338 *Status
= STATUS_NONEXISTENT_SECTOR
;
3342 case SCSI_ADSENSE_INVALID_LUN
: {
3343 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3345 *Status
= STATUS_NO_SUCH_DEVICE
;
3349 case SCSI_ADSENSE_MUSIC_AREA
: {
3350 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3355 case SCSI_ADSENSE_DATA_AREA
: {
3356 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3361 case SCSI_ADSENSE_VOLUME_OVERFLOW
: {
3362 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3363 "Volume overflow\n"));
3367 case SCSI_ADSENSE_COPY_PROTECTION_FAILURE
: {
3368 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3369 "Copy protection failure\n"));
3371 *Status
= STATUS_COPY_PROTECTION_FAILURE
;
3373 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3374 case SCSI_SENSEQ_AUTHENTICATION_FAILURE
:
3375 DebugPrint((ClassDebugSenseInfo
,
3376 "ClassInterpretSenseInfo: "
3377 "Authentication failure\n"));
3378 *Status
= STATUS_CSS_AUTHENTICATION_FAILURE
;
3380 case SCSI_SENSEQ_KEY_NOT_PRESENT
:
3381 DebugPrint((ClassDebugSenseInfo
,
3382 "ClassInterpretSenseInfo: "
3383 "Key not present\n"));
3384 *Status
= STATUS_CSS_KEY_NOT_PRESENT
;
3386 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED
:
3387 DebugPrint((ClassDebugSenseInfo
,
3388 "ClassInterpretSenseInfo: "
3389 "Key not established\n"));
3390 *Status
= STATUS_CSS_KEY_NOT_ESTABLISHED
;
3392 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION
:
3393 DebugPrint((ClassDebugSenseInfo
,
3394 "ClassInterpretSenseInfo: "
3395 "Read of scrambled sector w/o "
3396 "authentication\n"));
3397 *Status
= STATUS_CSS_SCRAMBLED_SECTOR
;
3399 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT
:
3400 DebugPrint((ClassDebugSenseInfo
,
3401 "ClassInterpretSenseInfo: "
3402 "Media region does not logical unit "
3404 *Status
= STATUS_CSS_REGION_MISMATCH
;
3406 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR
:
3407 DebugPrint((ClassDebugSenseInfo
,
3408 "ClassInterpretSenseInfo: "
3409 "Region set error -- region may "
3411 *Status
= STATUS_CSS_RESETS_EXHAUSTED
;
3413 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
3419 case SCSI_ADSENSE_INVALID_CDB
: {
3420 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3424 // Note: the retry interval is not typically used.
3425 // it is set here only because a ClassErrorHandler
3426 // cannot set the retryInterval, and the error may
3427 // require a few commands to be sent to clear whatever
3428 // caused this condition (i.e. disk clears the write
3429 // cache, requiring at least two commands)
3431 // hopefully, this shortcoming can be changed for
3439 } // end switch (senseBuffer->AdditionalSenseCode)
3442 } // end SCSI_SENSE_ILLEGAL_REQUEST
3444 case SCSI_SENSE_UNIT_ATTENTION
: {
3450 // A media change may have occured so increment the change
3451 // count for the physical device
3454 count
= InterlockedIncrement(&fdoExtension
->MediaChangeCount
);
3455 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3456 "Media change count for device %d incremented to %#lx\n",
3457 fdoExtension
->DeviceNumber
, count
));
3460 switch (senseBuffer
->AdditionalSenseCode
) {
3461 case SCSI_ADSENSE_MEDIUM_CHANGED
: {
3462 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3463 "Media changed\n"));
3465 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3466 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3467 "Media Changed on non-removable device %p\n",
3470 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
3474 case SCSI_ADSENSE_BUS_RESET
: {
3475 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3480 case SCSI_ADSENSE_OPERATOR_REQUEST
: {
3481 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3483 case SCSI_SENSEQ_MEDIUM_REMOVAL
: {
3484 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3485 "Ejection request received!\n"));
3486 ClassSendEjectionNotification(fdoExtension
);
3490 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE
: {
3491 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3492 "Operator selected write permit?! "
3493 "(unsupported!)\n"));
3497 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE
: {
3498 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3499 "Operator selected write protect?! "
3500 "(unsupported!)\n"));
3508 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3509 "Unit attention\n"));
3513 } // end switch (senseBuffer->AdditionalSenseCode)
3515 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
))
3518 // TODO : Is the media lockable?
3521 if ((ClassGetVpb(Fdo
) != NULL
) && (ClassGetVpb(Fdo
)->Flags
& VPB_MOUNTED
))
3524 // Set bit to indicate that media may have changed
3525 // and volume needs verification.
3528 SET_FLAG(Fdo
->Flags
, DO_VERIFY_VOLUME
);
3530 *Status
= STATUS_VERIFY_REQUIRED
;
3536 *Status
= STATUS_IO_DEVICE_ERROR
;
3541 } // end SCSI_SENSE_UNIT_ATTENTION
3543 case SCSI_SENSE_ABORTED_COMMAND
: {
3544 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3545 "Command aborted\n"));
3546 *Status
= STATUS_IO_DEVICE_ERROR
;
3549 } // end SCSI_SENSE_ABORTED_COMMAND
3551 case SCSI_SENSE_BLANK_CHECK
: {
3552 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3553 "Media blank check\n"));
3555 *Status
= STATUS_NO_DATA_DETECTED
;
3557 } // end SCSI_SENSE_BLANK_CHECK
3559 case SCSI_SENSE_RECOVERED_ERROR
: {
3561 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3562 "Recovered error\n"));
3563 *Status
= STATUS_SUCCESS
;
3568 switch(senseBuffer
->AdditionalSenseCode
) {
3569 case SCSI_ADSENSE_SEEK_ERROR
:
3570 case SCSI_ADSENSE_TRACK_ERROR
: {
3571 logStatus
= IO_ERR_SEEK_ERROR
;
3575 case SCSI_ADSENSE_REC_DATA_NOECC
:
3576 case SCSI_ADSENSE_REC_DATA_ECC
: {
3577 logStatus
= IO_RECOVERED_VIA_ECC
;
3581 case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED
: {
3582 UCHAR wmiEventData
[5];
3584 *((PULONG
)wmiEventData
) = sizeof(UCHAR
);
3585 wmiEventData
[sizeof(ULONG
)] = senseBuffer
->AdditionalSenseCodeQualifier
;
3588 // Don't log another eventlog if we have already logged once
3589 // NOTE: this should have been interlocked, but the structure
3590 // was publicly defined to use a BOOLEAN (char). Since
3591 // media only reports these errors once per X minutes,
3592 // the potential race condition is nearly non-existant.
3593 // the worst case is duplicate log entries, so ignore.
3596 if (fdoExtension
->FailurePredicted
== 0) {
3599 fdoExtension
->FailurePredicted
= TRUE
;
3600 fdoExtension
->FailureReason
= senseBuffer
->AdditionalSenseCodeQualifier
;
3601 logStatus
= IO_WRN_FAILURE_PREDICTED
;
3603 ClassNotifyFailurePredicted(fdoExtension
,
3604 (PUCHAR
)&wmiEventData
,
3605 sizeof(wmiEventData
),
3615 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3619 } // end switch(senseBuffer->AdditionalSenseCode)
3621 if (senseBuffer
->IncorrectLength
) {
3623 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3624 "Incorrect length detected.\n"));
3625 *Status
= STATUS_INVALID_BLOCK_LENGTH
;
3629 } // end SCSI_SENSE_RECOVERED_ERROR
3631 case SCSI_SENSE_NO_SENSE
: {
3634 // Check other indicators.
3637 if (senseBuffer
->IncorrectLength
) {
3639 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3640 "Incorrect length detected.\n"));
3641 *Status
= STATUS_INVALID_BLOCK_LENGTH
;
3646 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3647 "No specific sense key\n"));
3648 *Status
= STATUS_IO_DEVICE_ERROR
;
3653 } // end SCSI_SENSE_NO_SENSE
3656 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3657 "Unrecognized sense code\n"));
3658 *Status
= STATUS_IO_DEVICE_ERROR
;
3662 } // end switch (senseBuffer->SenseKey & 0xf)
3665 // Try to determine the bad sector from the inquiry data.
3668 if ((((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_READ
||
3669 ((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_VERIFY
||
3670 ((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_WRITE
)) {
3672 for (index
= 0; index
< 4; index
++) {
3673 badSector
= (badSector
<< 8) | senseBuffer
->Information
[index
];
3677 for (index
= 0; index
< 4; index
++) {
3678 readSector
= (readSector
<< 8) | Srb
->Cdb
[index
+2];
3681 index
= (((PCDB
)Srb
->Cdb
)->CDB10
.TransferBlocksMsb
<< 8) |
3682 ((PCDB
)Srb
->Cdb
)->CDB10
.TransferBlocksLsb
;
3685 // Make sure the bad sector is within the read sectors.
3688 if (!(badSector
>= readSector
&& badSector
< readSector
+ index
)) {
3689 badSector
= readSector
;
3696 // Request sense buffer not valid. No sense information
3697 // to pinpoint the error. Return general request fail.
3700 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3701 "Request sense info not valid. SrbStatus %2x\n",
3702 SRB_STATUS(Srb
->SrbStatus
)));
3705 switch (SRB_STATUS(Srb
->SrbStatus
)) {
3706 case SRB_STATUS_INVALID_LUN
:
3707 case SRB_STATUS_INVALID_TARGET_ID
:
3708 case SRB_STATUS_NO_DEVICE
:
3709 case SRB_STATUS_NO_HBA
:
3710 case SRB_STATUS_INVALID_PATH_ID
: {
3711 *Status
= STATUS_NO_SUCH_DEVICE
;
3716 case SRB_STATUS_COMMAND_TIMEOUT
:
3717 case SRB_STATUS_TIMEOUT
: {
3720 // Update the error count for the device.
3723 incrementErrorCount
= TRUE
;
3724 *Status
= STATUS_IO_TIMEOUT
;
3728 case SRB_STATUS_ABORTED
: {
3731 // Update the error count for the device.
3734 incrementErrorCount
= TRUE
;
3735 *Status
= STATUS_IO_TIMEOUT
;
3741 case SRB_STATUS_SELECTION_TIMEOUT
: {
3743 logStatus
= IO_ERR_NOT_READY
;
3745 *Status
= STATUS_DEVICE_NOT_CONNECTED
;
3750 case SRB_STATUS_DATA_OVERRUN
: {
3751 *Status
= STATUS_DATA_OVERRUN
;
3756 case SRB_STATUS_PHASE_SEQUENCE_FAILURE
: {
3759 // Update the error count for the device.
3762 incrementErrorCount
= TRUE
;
3763 *Status
= STATUS_IO_DEVICE_ERROR
;
3766 // If there was phase sequence error then limit the number of
3770 if (RetryCount
> 1 ) {
3777 case SRB_STATUS_REQUEST_FLUSHED
: {
3780 // If the status needs verification bit is set. Then set
3781 // the status to need verification and no retry; otherwise,
3782 // just retry the request.
3785 if (TEST_FLAG(Fdo
->Flags
, DO_VERIFY_VOLUME
)) {
3787 *Status
= STATUS_VERIFY_REQUIRED
;
3791 *Status
= STATUS_IO_DEVICE_ERROR
;
3797 case SRB_STATUS_INVALID_REQUEST
: {
3798 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3803 case SRB_STATUS_UNEXPECTED_BUS_FREE
:
3804 case SRB_STATUS_PARITY_ERROR
:
3807 // Update the error count for the device
3808 // and fall through to below
3811 incrementErrorCount
= TRUE
;
3813 case SRB_STATUS_BUS_RESET
: {
3814 *Status
= STATUS_IO_DEVICE_ERROR
;
3818 case SRB_STATUS_ERROR
: {
3820 *Status
= STATUS_IO_DEVICE_ERROR
;
3821 if (Srb
->ScsiStatus
== 0) {
3824 // This is some strange return code. Update the error
3825 // count for the device.
3828 incrementErrorCount
= TRUE
;
3830 } if (Srb
->ScsiStatus
== SCSISTAT_BUSY
) {
3832 *Status
= STATUS_DEVICE_NOT_READY
;
3834 } if (Srb
->ScsiStatus
== SCSISTAT_RESERVATION_CONFLICT
) {
3836 *Status
= STATUS_DEVICE_BUSY
;
3847 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3849 *Status
= STATUS_IO_DEVICE_ERROR
;
3850 unhandledError
= TRUE
;
3857 // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
3858 // we know from a previous poll when the device will be ready (ETA)
3859 // we should delay the retry more appropriately than just guessing.
3862 if (fdoExtension->MediaChangeDetectionInfo &&
3863 fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
3864 TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
3865 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
3867 // check if Gesn.ReadyTime if greater than current tick count
3868 // if so, delay that long (from 1 to 30 seconds max?)
3869 // else, leave the guess of time alone.
3875 if (incrementErrorCount
) {
3878 // if any error count occurred, delay the retry of this io by
3879 // at least one second, if caller supports it.
3882 if (retryInterval
== 0) {
3885 ClasspPerfIncrementErrorCount(fdoExtension
);
3889 // If there is a class specific error handler call it.
3892 if (fdoExtension
->CommonExtension
.DevInfo
->ClassError
!= NULL
) {
3894 fdoExtension
->CommonExtension
.DevInfo
->ClassError(Fdo
,
3901 // If the caller wants to know the suggested retry interval tell them.
3904 if(ARGUMENT_PRESENT(RetryInterval
)) {
3905 *RetryInterval
= retryInterval
;
3911 * Always log the error in our internal log.
3912 * If logError is set, also log the error in the system log.
3916 ULONG senseBufferSize
= 0;
3917 IO_ERROR_LOG_PACKET staticErrLogEntry
= {0};
3918 CLASS_ERROR_LOG_DATA staticErrLogData
= {0};
3921 // Calculate the total size of the error log entry.
3922 // add to totalSize in the order that they are used.
3923 // the advantage to calculating all the sizes here is
3924 // that we don't have to do a bunch of extraneous checks
3925 // later on in this code path.
3927 totalSize
= sizeof(IO_ERROR_LOG_PACKET
) // required
3928 - sizeof(ULONG
) // struct includes one ULONG
3929 + sizeof(CLASS_ERROR_LOG_DATA
);// struct for ease
3932 // also save any available extra sense data, up to the maximum errlog
3933 // packet size . WMI should be used for real-time analysis.
3934 // the event log should only be used for post-mortem debugging.
3936 if (TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_AUTOSENSE_VALID
)) {
3937 ULONG validSenseBytes
;
3941 // make sure we can at least access the AdditionalSenseLength field
3943 validSense
= RTL_CONTAINS_FIELD(senseBuffer
,
3944 Srb
->SenseInfoBufferLength
,
3945 AdditionalSenseLength
);
3949 // if extra info exists, copy the maximum amount of available
3950 // sense data that is safe into the the errlog.
3952 validSenseBytes
= senseBuffer
->AdditionalSenseLength
3953 + offsetof(SENSE_DATA
, AdditionalSenseLength
);
3956 // this is invalid because it causes overflow!
3957 // whoever sent this type of request would cause
3960 ASSERT(validSenseBytes
< MAX_ADDITIONAL_SENSE_BYTES
);
3963 // set to save the most sense buffer possible
3965 senseBufferSize
= max(validSenseBytes
, sizeof(SENSE_DATA
));
3966 senseBufferSize
= min(senseBufferSize
, Srb
->SenseInfoBufferLength
);
3969 // it's smaller than required to read the total number of
3970 // valid bytes, so just use the SenseInfoBufferLength field.
3972 senseBufferSize
= Srb
->SenseInfoBufferLength
;
3976 * Bump totalSize by the number of extra senseBuffer bytes
3977 * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
3978 * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
3980 if (senseBufferSize
> sizeof(SENSE_DATA
)){
3981 totalSize
+= senseBufferSize
-sizeof(SENSE_DATA
);
3982 if (totalSize
> ERROR_LOG_MAXIMUM_SIZE
){
3983 senseBufferSize
-= totalSize
-ERROR_LOG_MAXIMUM_SIZE
;
3984 totalSize
= ERROR_LOG_MAXIMUM_SIZE
;
3990 // If we've used up all of our retry attempts, set the final status to
3991 // reflect the appropriate result.
3993 if (retry
&& RetryCount
< MAXIMUM_RETRIES
) {
3994 staticErrLogEntry
.FinalStatus
= STATUS_SUCCESS
;
3995 staticErrLogData
.ErrorRetried
= TRUE
;
3997 staticErrLogEntry
.FinalStatus
= *Status
;
3999 if (TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
4000 staticErrLogData
.ErrorPaging
= TRUE
;
4002 if (unhandledError
) {
4003 staticErrLogData
.ErrorUnhandled
= TRUE
;
4007 // Calculate the device offset if there is a geometry.
4009 staticErrLogEntry
.DeviceOffset
.QuadPart
= (LONGLONG
)badSector
;
4010 staticErrLogEntry
.DeviceOffset
.QuadPart
*= (LONGLONG
)fdoExtension
->DiskGeometry
.BytesPerSector
;
4011 if (logStatus
== -1){
4012 staticErrLogEntry
.ErrorCode
= STATUS_IO_DEVICE_ERROR
;
4014 staticErrLogEntry
.ErrorCode
= logStatus
;
4018 * The dump data follows the IO_ERROR_LOG_PACKET,
4019 * with the first ULONG of dump data inside the packet.
4021 staticErrLogEntry
.DumpDataSize
= (USHORT
)totalSize
- sizeof(IO_ERROR_LOG_PACKET
) + sizeof(ULONG
);
4023 staticErrLogEntry
.SequenceNumber
= 0;
4024 staticErrLogEntry
.MajorFunctionCode
= MajorFunctionCode
;
4025 staticErrLogEntry
.IoControlCode
= IoDeviceCode
;
4026 staticErrLogEntry
.RetryCount
= (UCHAR
) RetryCount
;
4027 staticErrLogEntry
.UniqueErrorValue
= uniqueId
;
4029 KeQueryTickCount(&staticErrLogData
.TickCount
);
4030 staticErrLogData
.PortNumber
= (ULONG
)-1;
4033 * Save the entire contents of the SRB.
4035 staticErrLogData
.Srb
= *Srb
;
4038 * For our private log, save just the default length of the SENSE_DATA.
4040 if (senseBufferSize
!= 0){
4041 RtlCopyMemory(&staticErrLogData
.SenseData
, senseBuffer
, min(senseBufferSize
, sizeof(SENSE_DATA
)));
4045 * Save the error log in our context.
4046 * We only save the default sense buffer length.
4048 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
4049 fdoData
->ErrorLogs
[fdoData
->ErrorLogNextIndex
] = staticErrLogData
;
4050 fdoData
->ErrorLogNextIndex
++;
4051 fdoData
->ErrorLogNextIndex
%= NUM_ERROR_LOG_ENTRIES
;
4052 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
4055 * If logError is set, also save this log in the system's error log.
4056 * But make sure we don't log TUR failures over and over
4057 * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
4059 if ((((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_TEST_UNIT_READY
) && logError
){
4060 if (fdoData
->LoggedTURFailureSinceLastIO
){
4064 fdoData
->LoggedTURFailureSinceLastIO
= TRUE
;
4068 PIO_ERROR_LOG_PACKET errorLogEntry
;
4069 PCLASS_ERROR_LOG_DATA errlogData
;
4071 errorLogEntry
= (PIO_ERROR_LOG_PACKET
)IoAllocateErrorLogEntry(Fdo
, (UCHAR
)totalSize
);
4073 errlogData
= (PCLASS_ERROR_LOG_DATA
)errorLogEntry
->DumpData
;
4075 *errorLogEntry
= staticErrLogEntry
;
4076 *errlogData
= staticErrLogData
;
4079 * For the system log, copy as much of the sense buffer as possible.
4081 if (senseBufferSize
!= 0) {
4082 RtlCopyMemory(&errlogData
->SenseData
, senseBuffer
, senseBufferSize
);
4086 * Write the error log packet to the system error logging thread.
4088 IoWriteErrorLogEntry(errorLogEntry
);
4095 } // end ClassInterpretSenseInfo()
4099 /*++////////////////////////////////////////////////////////////////////////////
4103 Routine Description:
4105 This routine sends a mode sense command to a target ID and returns
4106 when it is complete.
4110 Fdo - Supplies the functional device object associated with this request.
4112 ModeSenseBuffer - Supplies a buffer to store the sense data.
4114 Length - Supplies the length in bytes of the mode sense buffer.
4116 PageMode - Supplies the page or pages of mode sense data to be retrived.
4120 Length of the transferred data is returned.
4123 ULONG
ClassModeSense( IN PDEVICE_OBJECT Fdo
,
4124 IN PCHAR ModeSenseBuffer
,
4128 ULONG lengthTransferred
= 0;
4129 PMDL senseBufferMdl
;
4133 senseBufferMdl
= BuildDeviceInputMdl(ModeSenseBuffer
, Length
);
4134 if (senseBufferMdl
){
4136 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
4140 IRP pseudoIrp
= {0};
4143 * Store the number of packets servicing the irp (one)
4144 * inside the original IRP. It will be used to counted down
4145 * to zero when the packet completes.
4146 * Initialize the original IRP's status to success.
4147 * If the packet fails, we will set it to the error status.
4149 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
4150 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
4151 pseudoIrp
.IoStatus
.Information
= 0;
4152 pseudoIrp
.MdlAddress
= senseBufferMdl
;
4155 * Set this up as a SYNCHRONOUS transfer, submit it,
4156 * and wait for the packet to complete. The result
4157 * status will be written to the original irp.
4159 ASSERT(Length
<= 0x0ff);
4160 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
4161 SetupModeSenseTransferPacket(pkt
, &event
, ModeSenseBuffer
, (UCHAR
)Length
, PageMode
, &pseudoIrp
);
4162 SubmitTransferPacket(pkt
);
4163 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
4165 if (NT_SUCCESS(pseudoIrp
.IoStatus
.Status
)){
4166 lengthTransferred
= (ULONG
)pseudoIrp
.IoStatus
.Information
;
4170 * This request can sometimes fail legitimately
4171 * (e.g. when a SCSI device is attached but turned off)
4172 * so this is not necessarily a device/driver bug.
4174 DBGTRACE(ClassDebugWarning
, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo
, pseudoIrp
.IoStatus
.Status
));
4178 FreeDeviceInputMdl(senseBufferMdl
);
4181 return lengthTransferred
;
4185 /*++////////////////////////////////////////////////////////////////////////////
4189 Routine Description:
4191 This routine scans through the mode sense data and finds the requested
4192 mode sense page code.
4195 ModeSenseBuffer - Supplies a pointer to the mode sense data.
4197 Length - Indicates the length of valid data.
4199 PageMode - Supplies the page mode to be searched for.
4201 Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
4205 A pointer to the the requested mode page. If the mode page was not found
4206 then NULL is return.
4211 IN PCHAR ModeSenseBuffer
,
4218 ULONG parameterHeaderLength
;
4219 PVOID result
= NULL
;
4221 limit
= ModeSenseBuffer
+ Length
;
4222 parameterHeaderLength
= (Use6Byte
) ? sizeof(MODE_PARAMETER_HEADER
) : sizeof(MODE_PARAMETER_HEADER10
);
4224 if (Length
>= parameterHeaderLength
) {
4226 PMODE_PARAMETER_HEADER10 modeParam10
;
4227 ULONG blockDescriptorLength
;
4230 * Skip the mode select header and block descriptors.
4233 blockDescriptorLength
= ((PMODE_PARAMETER_HEADER
) ModeSenseBuffer
)->BlockDescriptorLength
;
4236 modeParam10
= (PMODE_PARAMETER_HEADER10
) ModeSenseBuffer
;
4237 blockDescriptorLength
= modeParam10
->BlockDescriptorLength
[1];
4240 ModeSenseBuffer
+= parameterHeaderLength
+ blockDescriptorLength
;
4243 // ModeSenseBuffer now points at pages. Walk the pages looking for the
4244 // requested page until the limit is reached.
4247 while (ModeSenseBuffer
+
4248 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
) < limit
) {
4250 if (((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageCode
== PageMode
) {
4253 * found the mode page. make sure it's safe to touch it all
4254 * before returning the pointer to caller
4257 if (ModeSenseBuffer
+ ((PMODE_DISCONNECT_PAGE
)ModeSenseBuffer
)->PageLength
> limit
) {
4259 * Return NULL since the page is not safe to access in full
4264 result
= ModeSenseBuffer
;
4270 // Advance to the next page which is 4-byte-aligned offset after this page.
4273 ((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageLength
+
4274 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
);
4280 } // end ClassFindModePage()
4282 /*++////////////////////////////////////////////////////////////////////////////
4284 ClassSendSrbAsynchronous()
4286 Routine Description:
4288 This routine takes a partially built Srb and an Irp and sends it down to
4291 This routine must be called with the remove lock held for the specified
4296 Fdo - Supplies the functional device object for the orginal request.
4298 Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
4299 CDB and the SRB timeout value must be filled in. The SRB must not be
4300 allocated from zone.
4302 Irp - Supplies the requesting Irp.
4304 BufferAddress - Supplies a pointer to the buffer to be transfered.
4306 BufferLength - Supplies the length of data transfer.
4308 WriteToDevice - Indicates the data transfer will be from system memory to
4313 Returns STATUS_PENDING if the request is dispatched (since the
4314 completion routine may change the irp's status value we cannot simply
4315 return the value of the dispatch)
4317 or returns a status value to indicate why it failed.
4321 ClassSendSrbAsynchronous(
4323 PSCSI_REQUEST_BLOCK Srb
,
4325 PVOID BufferAddress
,
4327 BOOLEAN WriteToDevice
4331 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
4332 PIO_STACK_LOCATION irpStack
;
4337 // Write length to SRB.
4340 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
4343 // Set SCSI bus address.
4346 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
4349 // This is a violation of the SCSI spec but it is required for
4353 // Srb->Cdb[1] |= deviceExtension->Lun << 5;
4356 // Indicate auto request sense by specifying buffer and size.
4359 Srb
->SenseInfoBuffer
= fdoExtension
->SenseData
;
4360 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
4361 Srb
->DataBuffer
= BufferAddress
;
4364 // Save the class driver specific flags away.
4367 savedFlags
= Srb
->SrbFlags
& SRB_FLAGS_CLASS_DRIVER_RESERVED
;
4370 // Allow the caller to specify that they do not wish
4371 // IoStartNextPacket() to be called in the completion routine.
4374 SET_FLAG(savedFlags
, (Srb
->SrbFlags
& SRB_FLAGS_DONT_START_NEXT_PACKET
));
4376 if (BufferAddress
!= NULL
) {
4379 // Build Mdl if necessary.
4382 if (Irp
->MdlAddress
== NULL
) {
4384 if (IoAllocateMdl(BufferAddress
,
4390 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4393 // ClassIoComplete() would have free'd the srb
4396 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
4397 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
4399 ClassFreeOrReuseSrb(fdoExtension
, Srb
);
4400 ClassReleaseRemoveLock(Fdo
, Irp
);
4401 ClassCompleteRequest(Fdo
, Irp
, IO_NO_INCREMENT
);
4403 return STATUS_INSUFFICIENT_RESOURCES
;
4406 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
4411 // Make sure the buffer requested matches the MDL.
4414 ASSERT(BufferAddress
== MmGetMdlVirtualAddress(Irp
->MdlAddress
));
4421 Srb
->SrbFlags
= WriteToDevice
? SRB_FLAGS_DATA_OUT
: SRB_FLAGS_DATA_IN
;
4429 Srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
;
4433 // Restore saved flags.
4436 SET_FLAG(Srb
->SrbFlags
, savedFlags
);
4439 // Disable synchronous transfer for these requests.
4442 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
4445 // Set the transfer length.
4448 Srb
->DataTransferLength
= BufferLength
;
4454 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
4459 // Save a few parameters in the current stack location.
4462 irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4465 // Save retry count in current Irp stack.
4468 irpStack
->Parameters
.Others
.Argument4
= (PVOID
)MAXIMUM_RETRIES
;
4471 // Set up IoCompletion routine address.
4474 IoSetCompletionRoutine(Irp
, ClassIoComplete
, Srb
, TRUE
, TRUE
, TRUE
);
4477 // Get next stack location and
4478 // set major function code.
4481 irpStack
= IoGetNextIrpStackLocation(Irp
);
4483 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
4486 // Save SRB address in next stack for port driver.
4489 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
4492 // Set up Irp Address.
4495 Srb
->OriginalRequest
= Irp
;
4498 // Call the port driver to process the request.
4501 IoMarkIrpPending(Irp
);
4503 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, Irp
);
4505 return STATUS_PENDING
;
4507 } // end ClassSendSrbAsynchronous()
4509 /*++////////////////////////////////////////////////////////////////////////////
4511 ClassDeviceControlDispatch()
4513 Routine Description:
4515 The routine is the common class driver device control dispatch entry point.
4516 This routine is invokes the device-specific drivers DeviceControl routine,
4517 (which may call the Class driver's common DeviceControl routine).
4521 DeviceObject - Supplies a pointer to the device object for this request.
4523 Irp - Supplies the Irp making the request.
4527 Returns the status returned from the device-specific driver.
4531 ClassDeviceControlDispatch(
4532 PDEVICE_OBJECT DeviceObject
,
4537 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
4540 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
4544 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4546 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
4547 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4548 return STATUS_DEVICE_DOES_NOT_EXIST
;
4552 // Call the class specific driver DeviceControl routine.
4553 // If it doesn't handle it, it will call back into ClassDeviceControl.
4556 ASSERT(commonExtension
->DevInfo
->ClassDeviceControl
);
4558 return commonExtension
->DevInfo
->ClassDeviceControl(DeviceObject
,Irp
);
4559 } // end ClassDeviceControlDispatch()
4562 /*++////////////////////////////////////////////////////////////////////////////
4564 ClassDeviceControl()
4566 Routine Description:
4568 The routine is the common class driver device control dispatch function.
4569 This routine is called by a class driver when it get an unrecognized
4570 device control request. This routine will perform the correct action for
4571 common requests such as lock media. If the device request is unknown it
4572 passed down to the next level.
4574 This routine must be called with the remove lock held for the specified
4579 DeviceObject - Supplies a pointer to the device object for this request.
4581 Irp - Supplies the Irp making the request.
4585 Returns back a STATUS_PENDING or a completion status.
4590 PDEVICE_OBJECT DeviceObject
,
4594 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
4596 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4597 PIO_STACK_LOCATION nextStack
= NULL
;
4599 ULONG controlCode
= irpStack
->Parameters
.DeviceIoControl
.IoControlCode
;
4601 PSCSI_REQUEST_BLOCK srb
= NULL
;
4605 ULONG modifiedIoControlCode
;
4608 // If this is a pass through I/O control, set the minor function code
4609 // and device address and pass it to the port driver.
4612 if ((controlCode
== IOCTL_SCSI_PASS_THROUGH
) ||
4613 (controlCode
== IOCTL_SCSI_PASS_THROUGH_DIRECT
)) {
4615 PSCSI_PASS_THROUGH scsiPass
;
4618 // Validiate the user buffer.
4620 #if defined (_WIN64)
4622 if (IoIs32bitProcess(Irp
)) {
4624 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(SCSI_PASS_THROUGH32
)){
4626 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
4628 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4629 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4631 status
= STATUS_INVALID_PARAMETER
;
4632 goto SetStatusAndReturn
;
4638 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
4639 sizeof(SCSI_PASS_THROUGH
)) {
4641 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
4643 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4644 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4646 status
= STATUS_INVALID_PARAMETER
;
4647 goto SetStatusAndReturn
;
4651 IoCopyCurrentIrpStackLocationToNext(Irp
);
4653 nextStack
= IoGetNextIrpStackLocation(Irp
);
4654 nextStack
->MinorFunction
= 1;
4656 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4658 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4659 goto SetStatusAndReturn
;
4662 Irp
->IoStatus
.Information
= 0;
4664 switch (controlCode
) {
4666 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
: {
4668 PMOUNTDEV_UNIQUE_ID uniqueId
;
4670 if (!commonExtension
->MountedDeviceInterfaceName
.Buffer
) {
4671 status
= STATUS_INVALID_PARAMETER
;
4675 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4676 sizeof(MOUNTDEV_UNIQUE_ID
)) {
4678 status
= STATUS_BUFFER_TOO_SMALL
;
4679 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4683 uniqueId
= Irp
->AssociatedIrp
.SystemBuffer
;
4684 uniqueId
->UniqueIdLength
=
4685 commonExtension
->MountedDeviceInterfaceName
.Length
;
4687 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4688 sizeof(USHORT
) + uniqueId
->UniqueIdLength
) {
4690 status
= STATUS_BUFFER_OVERFLOW
;
4691 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4695 RtlCopyMemory(uniqueId
->UniqueId
,
4696 commonExtension
->MountedDeviceInterfaceName
.Buffer
,
4697 uniqueId
->UniqueIdLength
);
4699 status
= STATUS_SUCCESS
;
4700 Irp
->IoStatus
.Information
= sizeof(USHORT
) +
4701 uniqueId
->UniqueIdLength
;
4705 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
: {
4707 PMOUNTDEV_NAME name
;
4709 ASSERT(commonExtension
->DeviceName
.Buffer
);
4711 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4712 sizeof(MOUNTDEV_NAME
)) {
4714 status
= STATUS_BUFFER_TOO_SMALL
;
4715 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4719 name
= Irp
->AssociatedIrp
.SystemBuffer
;
4720 name
->NameLength
= commonExtension
->DeviceName
.Length
;
4722 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4723 sizeof(USHORT
) + name
->NameLength
) {
4725 status
= STATUS_BUFFER_OVERFLOW
;
4726 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4730 RtlCopyMemory(name
->Name
, commonExtension
->DeviceName
.Buffer
,
4733 status
= STATUS_SUCCESS
;
4734 Irp
->IoStatus
.Information
= sizeof(USHORT
) + name
->NameLength
;
4738 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
: {
4740 PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName
;
4741 WCHAR driveLetterNameBuffer
[10];
4742 RTL_QUERY_REGISTRY_TABLE queryTable
[2];
4744 UNICODE_STRING driveLetterName
;
4746 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4747 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
)) {
4749 status
= STATUS_BUFFER_TOO_SMALL
;
4750 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
4754 valueName
= ExAllocatePoolWithTag(
4756 commonExtension
->DeviceName
.Length
+ sizeof(WCHAR
),
4760 status
= STATUS_INSUFFICIENT_RESOURCES
;
4764 RtlCopyMemory(valueName
, commonExtension
->DeviceName
.Buffer
,
4765 commonExtension
->DeviceName
.Length
);
4766 valueName
[commonExtension
->DeviceName
.Length
/sizeof(WCHAR
)] = 0;
4768 driveLetterName
.Buffer
= driveLetterNameBuffer
;
4769 driveLetterName
.MaximumLength
= 20;
4770 driveLetterName
.Length
= 0;
4772 RtlZeroMemory(queryTable
, 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
4773 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_REQUIRED
|
4774 RTL_QUERY_REGISTRY_DIRECT
;
4775 queryTable
[0].Name
= valueName
;
4776 queryTable
[0].EntryContext
= &driveLetterName
;
4778 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
4779 L
"\\Registry\\Machine\\System\\DISK",
4780 queryTable
, NULL
, NULL
);
4782 if (!NT_SUCCESS(status
)) {
4783 ExFreePool(valueName
);
4787 if (driveLetterName
.Length
== 4 &&
4788 driveLetterName
.Buffer
[0] == '%' &&
4789 driveLetterName
.Buffer
[1] == ':') {
4791 driveLetterName
.Buffer
[0] = 0xFF;
4793 } else if (driveLetterName
.Length
!= 4 ||
4794 driveLetterName
.Buffer
[0] < FirstDriveLetter
||
4795 driveLetterName
.Buffer
[0] > LastDriveLetter
||
4796 driveLetterName
.Buffer
[1] != ':') {
4798 status
= STATUS_NOT_FOUND
;
4799 ExFreePool(valueName
);
4803 suggestedName
= Irp
->AssociatedIrp
.SystemBuffer
;
4804 suggestedName
->UseOnlyIfThereAreNoOtherLinks
= TRUE
;
4805 suggestedName
->NameLength
= 28;
4807 Irp
->IoStatus
.Information
=
4808 FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME
, Name
) + 28;
4810 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4811 Irp
->IoStatus
.Information
) {
4813 Irp
->IoStatus
.Information
=
4814 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
4815 status
= STATUS_BUFFER_OVERFLOW
;
4816 ExFreePool(valueName
);
4820 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
4821 L
"\\Registry\\Machine\\System\\DISK",
4824 ExFreePool(valueName
);
4826 RtlCopyMemory(suggestedName
->Name
, L
"\\DosDevices\\", 24);
4827 suggestedName
->Name
[12] = driveLetterName
.Buffer
[0];
4828 suggestedName
->Name
[13] = ':';
4831 // NT_SUCCESS(status) based on RtlQueryRegistryValues
4833 status
= STATUS_SUCCESS
;
4839 status
= STATUS_PENDING
;
4843 if (status
!= STATUS_PENDING
) {
4844 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4845 Irp
->IoStatus
.Status
= status
;
4846 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4850 if (commonExtension
->IsFdo
){
4852 PULONG_PTR function
;
4854 srb
= ExAllocatePoolWithTag(NonPagedPool
,
4855 sizeof(SCSI_REQUEST_BLOCK
) +
4856 (sizeof(ULONG_PTR
) * 2),
4861 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4862 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4863 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4864 status
= STATUS_INSUFFICIENT_RESOURCES
;
4865 goto SetStatusAndReturn
;
4868 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
4870 cdb
= (PCDB
)srb
->Cdb
;
4873 // Save the function code and the device object in the memory after
4877 function
= (PULONG_PTR
) ((PSCSI_REQUEST_BLOCK
) (srb
+ 1));
4878 *function
= (ULONG_PTR
) DeviceObject
;
4880 *function
= (ULONG_PTR
) controlCode
;
4887 // Change the device type to storage for the switch statement, but only
4888 // if from a legacy device type
4891 if (((controlCode
& 0xffff0000) == (IOCTL_DISK_BASE
<< 16)) ||
4892 ((controlCode
& 0xffff0000) == (IOCTL_TAPE_BASE
<< 16)) ||
4893 ((controlCode
& 0xffff0000) == (IOCTL_CDROM_BASE
<< 16))
4896 modifiedIoControlCode
= (controlCode
& ~0xffff0000);
4897 modifiedIoControlCode
|= (IOCTL_STORAGE_BASE
<< 16);
4901 modifiedIoControlCode
= controlCode
;
4905 DBGTRACE(ClassDebugTrace
, ("> ioctl %xh (%s)", modifiedIoControlCode
, DBGGETIOCTLSTR(modifiedIoControlCode
)));
4907 switch (modifiedIoControlCode
) {
4909 case IOCTL_STORAGE_GET_HOTPLUG_INFO
: {
4916 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4917 sizeof(STORAGE_HOTPLUG_INFO
)) {
4920 // Indicate unsuccessful status and no data transferred.
4923 Irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
4924 Irp
->IoStatus
.Information
= sizeof(STORAGE_HOTPLUG_INFO
);
4926 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4927 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4928 status
= STATUS_BUFFER_TOO_SMALL
;
4930 } else if(!commonExtension
->IsFdo
) {
4933 // Just forward this down and return
4936 IoCopyCurrentIrpStackLocationToNext(Irp
);
4938 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4939 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4943 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
4944 PSTORAGE_HOTPLUG_INFO info
;
4946 fdoExtension
= (PFUNCTIONAL_DEVICE_EXTENSION
)commonExtension
;
4947 info
= Irp
->AssociatedIrp
.SystemBuffer
;
4949 *info
= fdoExtension
->PrivateFdoData
->HotplugInfo
;
4950 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
4951 Irp
->IoStatus
.Information
= sizeof(STORAGE_HOTPLUG_INFO
);
4952 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4953 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4954 status
= STATUS_SUCCESS
;
4960 case IOCTL_STORAGE_SET_HOTPLUG_INFO
: {
4968 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
4969 sizeof(STORAGE_HOTPLUG_INFO
)) {
4972 // Indicate unsuccessful status and no data transferred.
4975 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4977 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4978 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4979 status
= STATUS_INFO_LENGTH_MISMATCH
;
4980 goto SetStatusAndReturn
;
4984 if(!commonExtension
->IsFdo
) {
4987 // Just forward this down and return
4990 IoCopyCurrentIrpStackLocationToNext(Irp
);
4992 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4993 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4997 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PFUNCTIONAL_DEVICE_EXTENSION
)commonExtension
;
4998 PSTORAGE_HOTPLUG_INFO info
= Irp
->AssociatedIrp
.SystemBuffer
;
5000 status
= STATUS_SUCCESS
;
5002 if (info
->Size
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.Size
)
5004 status
= STATUS_INVALID_PARAMETER_1
;
5007 if (info
->MediaRemovable
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.MediaRemovable
)
5009 status
= STATUS_INVALID_PARAMETER_2
;
5012 if (info
->MediaHotplug
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.MediaHotplug
)
5014 status
= STATUS_INVALID_PARAMETER_3
;
5017 if (info
->WriteCacheEnableOverride
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.WriteCacheEnableOverride
)
5019 status
= STATUS_INVALID_PARAMETER_5
;
5022 if (NT_SUCCESS(status
))
5024 fdoExtension
->PrivateFdoData
->HotplugInfo
.DeviceHotplug
= info
->DeviceHotplug
;
5027 // Store the user-defined override in the registry
5030 ClassSetDeviceParameter(fdoExtension
,
5031 CLASSP_REG_SUBKEY_NAME
,
5032 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
5033 (info
->DeviceHotplug
) ? RemovalPolicyExpectSurpriseRemoval
: RemovalPolicyExpectOrderlyRemoval
);
5036 Irp
->IoStatus
.Status
= status
;
5038 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5039 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5045 case IOCTL_STORAGE_CHECK_VERIFY
:
5046 case IOCTL_STORAGE_CHECK_VERIFY2
: {
5049 PIO_STACK_LOCATION newStack
;
5051 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5053 DebugPrint((1,"DeviceIoControl: Check verify\n"));
5056 // If a buffer for a media change count was provided, make sure it's
5057 // big enough to hold the result
5060 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
) {
5063 // If the buffer is too small to hold the media change count
5064 // then return an error to the caller
5067 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
5070 DebugPrint((3,"DeviceIoControl: media count "
5071 "buffer too small\n"));
5073 Irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
5074 Irp
->IoStatus
.Information
= sizeof(ULONG
);
5080 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5081 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5083 status
= STATUS_BUFFER_TOO_SMALL
;
5084 goto SetStatusAndReturn
;
5089 if(!commonExtension
->IsFdo
) {
5092 // If this is a PDO then we should just forward the request down
5096 IoCopyCurrentIrpStackLocationToNext(Irp
);
5098 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5100 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5102 goto SetStatusAndReturn
;
5106 fdoExtension
= DeviceObject
->DeviceExtension
;
5110 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
) {
5113 // The caller has provided a valid buffer. Allocate an additional
5114 // irp and stick the CheckVerify completion routine on it. We will
5115 // then send this down to the port driver instead of the irp the
5119 DebugPrint((2,"DeviceIoControl: Check verify wants "
5123 // Allocate a new irp to send the TestUnitReady to the port driver
5126 irp2
= IoAllocateIrp((CCHAR
) (DeviceObject
->StackSize
+ 3), FALSE
);
5129 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
5130 Irp
->IoStatus
.Information
= 0;
5133 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5134 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5135 status
= STATUS_INSUFFICIENT_RESOURCES
;
5136 goto SetStatusAndReturn
;
5142 // Make sure to acquire the lock for the new irp.
5145 ClassAcquireRemoveLock(DeviceObject
, irp2
);
5147 irp2
->Tail
.Overlay
.Thread
= Irp
->Tail
.Overlay
.Thread
;
5148 IoSetNextIrpStackLocation(irp2
);
5151 // Set the top stack location and shove the master Irp into the
5155 newStack
= IoGetCurrentIrpStackLocation(irp2
);
5156 newStack
->Parameters
.Others
.Argument1
= Irp
;
5157 newStack
->DeviceObject
= DeviceObject
;
5160 // Stick the check verify completion routine onto the stack
5161 // and prepare the irp for the port driver
5164 IoSetCompletionRoutine(irp2
,
5165 ClassCheckVerifyComplete
,
5171 IoSetNextIrpStackLocation(irp2
);
5172 newStack
= IoGetCurrentIrpStackLocation(irp2
);
5173 newStack
->DeviceObject
= DeviceObject
;
5174 newStack
->MajorFunction
= irpStack
->MajorFunction
;
5175 newStack
->MinorFunction
= irpStack
->MinorFunction
;
5178 // Mark the master irp as pending - whether the lower level
5179 // driver completes it immediately or not this should allow it
5180 // to go all the way back up.
5183 IoMarkIrpPending(Irp
);
5194 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_TEST_UNIT_READY
;
5197 // Set timeout value.
5200 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5203 // If this was a CV2 then mark the request as low-priority so we don't
5204 // spin up the drive just to satisfy it.
5207 if(controlCode
== IOCTL_STORAGE_CHECK_VERIFY2
) {
5208 SET_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_LOW_PRIORITY
);
5212 // Since this routine will always hand the request to the
5213 // port driver if there isn't a data transfer to be done
5214 // we don't have to worry about completing the request here
5219 // This routine uses a completion routine so we don't want to release
5220 // the remove lock until then.
5223 status
= ClassSendSrbAsynchronous(DeviceObject
,
5233 case IOCTL_STORAGE_MEDIA_REMOVAL
:
5234 case IOCTL_STORAGE_EJECTION_CONTROL
: {
5236 PPREVENT_MEDIA_REMOVAL mediaRemoval
= Irp
->AssociatedIrp
.SystemBuffer
;
5238 DebugPrint((3, "DiskIoControl: ejection control\n"));
5244 if(irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
5245 sizeof(PREVENT_MEDIA_REMOVAL
)) {
5248 // Indicate unsuccessful status and no data transferred.
5251 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5253 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5254 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5255 status
= STATUS_INFO_LENGTH_MISMATCH
;
5256 goto SetStatusAndReturn
;
5259 if(!commonExtension
->IsFdo
) {
5262 // Just forward this down and return
5265 IoCopyCurrentIrpStackLocationToNext(Irp
);
5267 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5268 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5272 // i don't believe this assertion is valid. this is a request
5273 // from user-mode, so they could request this for any device
5274 // they want? also, we handle it properly.
5275 // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
5276 status
= ClasspEjectionControl(
5279 ((modifiedIoControlCode
==
5280 IOCTL_STORAGE_EJECTION_CONTROL
) ? SecureMediaLock
:
5282 mediaRemoval
->PreventMediaRemoval
);
5284 Irp
->IoStatus
.Status
= status
;
5285 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5286 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5292 case IOCTL_STORAGE_MCN_CONTROL
: {
5294 DebugPrint((3, "DiskIoControl: MCN control\n"));
5296 if(irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
5297 sizeof(PREVENT_MEDIA_REMOVAL
)) {
5300 // Indicate unsuccessful status and no data transferred.
5303 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5304 Irp
->IoStatus
.Information
= 0;
5310 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5311 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5312 status
= STATUS_INFO_LENGTH_MISMATCH
;
5313 goto SetStatusAndReturn
;
5316 if(!commonExtension
->IsFdo
) {
5319 // Just forward this down and return
5326 IoCopyCurrentIrpStackLocationToNext(Irp
);
5328 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5329 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5334 // Call to the FDO - handle the ejection control.
5337 status
= ClasspMcnControl(DeviceObject
->DeviceExtension
,
5341 goto SetStatusAndReturn
;
5344 case IOCTL_STORAGE_RESERVE
:
5345 case IOCTL_STORAGE_RELEASE
: {
5348 // Reserve logical unit.
5351 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5353 if(!commonExtension
->IsFdo
) {
5355 IoCopyCurrentIrpStackLocationToNext(Irp
);
5357 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5358 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5359 goto SetStatusAndReturn
;
5361 fdoExtension
= DeviceObject
->DeviceExtension
;
5366 if(modifiedIoControlCode
== IOCTL_STORAGE_RESERVE
) {
5367 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_RESERVE_UNIT
;
5369 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_RELEASE_UNIT
;
5373 // Set timeout value.
5376 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5378 status
= ClassSendSrbAsynchronous(DeviceObject
,
5388 case IOCTL_STORAGE_EJECT_MEDIA
:
5389 case IOCTL_STORAGE_LOAD_MEDIA
:
5390 case IOCTL_STORAGE_LOAD_MEDIA2
:{
5396 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5398 if(!commonExtension
->IsFdo
) {
5400 IoCopyCurrentIrpStackLocationToNext(Irp
);
5402 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5404 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5405 goto SetStatusAndReturn
;
5407 fdoExtension
= DeviceObject
->DeviceExtension
;
5410 if(commonExtension
->PagingPathCount
!= 0) {
5412 DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
5415 status
= STATUS_FILES_OPEN
;
5416 Irp
->IoStatus
.Status
= status
;
5418 Irp
->IoStatus
.Information
= 0;
5424 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5425 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5426 goto SetStatusAndReturn
;
5430 // Synchronize with ejection control and ejection cleanup code as
5431 // well as other eject/load requests.
5434 KeEnterCriticalRegion();
5435 KeWaitForSingleObject(&(fdoExtension
->EjectSynchronizationEvent
),
5441 if(fdoExtension
->ProtectedLockCount
!= 0) {
5443 DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
5444 "device - failure\n"));
5446 status
= STATUS_DEVICE_BUSY
;
5447 Irp
->IoStatus
.Status
= status
;
5448 Irp
->IoStatus
.Information
= 0;
5454 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5455 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5457 KeSetEvent(&fdoExtension
->EjectSynchronizationEvent
,
5460 KeLeaveCriticalRegion();
5462 goto SetStatusAndReturn
;
5467 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
5468 cdb
->START_STOP
.LoadEject
= 1;
5470 if(modifiedIoControlCode
== IOCTL_STORAGE_EJECT_MEDIA
) {
5471 cdb
->START_STOP
.Start
= 0;
5473 cdb
->START_STOP
.Start
= 1;
5477 // Set timeout value.
5480 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5481 status
= ClassSendSrbAsynchronous(DeviceObject
,
5488 KeSetEvent(&fdoExtension
->EjectSynchronizationEvent
, IO_NO_INCREMENT
, FALSE
);
5489 KeLeaveCriticalRegion();
5494 case IOCTL_STORAGE_FIND_NEW_DEVICES
: {
5500 if(commonExtension
->IsFdo
) {
5502 IoInvalidateDeviceRelations(
5503 ((PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
)->LowerPdo
,
5506 status
= STATUS_SUCCESS
;
5507 Irp
->IoStatus
.Status
= status
;
5509 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5510 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5514 IoCopyCurrentIrpStackLocationToNext(Irp
);
5516 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5517 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5522 case IOCTL_STORAGE_GET_DEVICE_NUMBER
: {
5528 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
>=
5529 sizeof(STORAGE_DEVICE_NUMBER
)) {
5531 PSTORAGE_DEVICE_NUMBER deviceNumber
=
5532 Irp
->AssociatedIrp
.SystemBuffer
;
5533 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
5534 commonExtension
->PartitionZeroExtension
;
5536 deviceNumber
->DeviceType
= fdoExtension
->CommonExtension
.DeviceObject
->DeviceType
;
5537 deviceNumber
->DeviceNumber
= fdoExtension
->DeviceNumber
;
5538 deviceNumber
->PartitionNumber
= commonExtension
->PartitionNumber
;
5540 status
= STATUS_SUCCESS
;
5541 Irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_NUMBER
);
5544 status
= STATUS_BUFFER_TOO_SMALL
;
5545 Irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_NUMBER
);
5548 Irp
->IoStatus
.Status
= status
;
5549 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5550 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5557 DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
5558 controlCode
, DeviceObject
));
5561 // Pass the device control to the next driver.
5569 // Copy the Irp stack parameters to the next stack location.
5572 IoCopyCurrentIrpStackLocationToNext(Irp
);
5574 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5575 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5579 } // end switch( ...
5583 DBGTRACE(ClassDebugTrace
, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode
, DBGGETIOCTLSTR(modifiedIoControlCode
), status
));
5586 } // end ClassDeviceControl()
5588 /*++////////////////////////////////////////////////////////////////////////////
5590 ClassShutdownFlush()
5592 Routine Description:
5594 This routine is called for a shutdown and flush IRPs. These are sent by the
5595 system before it actually shuts down or when the file system does a flush.
5596 If it exists, the device-specific driver's routine will be invoked. If there
5597 wasn't one specified, the Irp will be completed with an Invalid device request.
5601 DriverObject - Pointer to device object to being shutdown by system.
5612 IN PDEVICE_OBJECT DeviceObject
,
5616 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
5622 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
5626 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5628 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
5630 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5632 return STATUS_DEVICE_DOES_NOT_EXIST
;
5635 if (commonExtension
->DevInfo
->ClassShutdownFlush
) {
5638 // Call the device-specific driver's routine.
5641 return commonExtension
->DevInfo
->ClassShutdownFlush(DeviceObject
, Irp
);
5645 // Device-specific driver doesn't support this.
5648 Irp
->IoStatus
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
5650 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5651 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5653 return STATUS_INVALID_DEVICE_REQUEST
;
5654 } // end ClassShutdownFlush()
5656 /*++////////////////////////////////////////////////////////////////////////////
5658 ClassCreateDeviceObject()
5660 Routine Description:
5662 This routine creates an object for the physical device specified and
5663 sets up the deviceExtension's function pointers for each entry point
5664 in the device-specific driver.
5668 DriverObject - Pointer to driver object created by system.
5670 ObjectNameBuffer - Dir. name of the object to create.
5672 LowerDeviceObject - Pointer to the lower device object
5674 IsFdo - should this be an fdo or a pdo
5676 DeviceObject - Pointer to the device object pointer we will return.
5684 ClassCreateDeviceObject(
5685 IN PDRIVER_OBJECT DriverObject
,
5686 IN PCCHAR ObjectNameBuffer
,
5687 IN PDEVICE_OBJECT LowerDevice
,
5689 IN OUT PDEVICE_OBJECT
*DeviceObject
5692 BOOLEAN isPartitionable
;
5693 STRING ntNameString
;
5694 UNICODE_STRING ntUnicodeString
;
5695 NTSTATUS status
, status2
;
5696 PDEVICE_OBJECT deviceObject
= NULL
;
5698 ULONG characteristics
;
5700 PCLASS_DRIVER_EXTENSION
5701 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
5702 CLASS_DRIVER_EXTENSION_KEY
);
5704 PCLASS_DEV_INFO devInfo
;
5708 *DeviceObject
= NULL
;
5709 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5711 DebugPrint((2, "ClassCreateFdo: Create device object\n"));
5713 ASSERT(LowerDevice
);
5716 // Make sure that if we're making PDO's we have an enumeration routine
5719 isPartitionable
= (driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
5721 ASSERT(IsFdo
|| isPartitionable
);
5724 // Grab the correct dev-info structure out of the init data
5728 devInfo
= &(driverExtension
->InitData
.FdoData
);
5730 devInfo
= &(driverExtension
->InitData
.PdoData
);
5733 characteristics
= devInfo
->DeviceCharacteristics
;
5735 if(ARGUMENT_PRESENT(ObjectNameBuffer
)) {
5736 DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer
));
5738 RtlInitString(&ntNameString
, ObjectNameBuffer
);
5740 status
= RtlAnsiStringToUnicodeString(&ntUnicodeString
, &ntNameString
, TRUE
);
5742 if (!NT_SUCCESS(status
)) {
5745 "ClassCreateFdo: Cannot convert string %s\n",
5748 ntUnicodeString
.Buffer
= NULL
;
5752 DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
5754 if(IsFdo
== FALSE
) {
5757 // PDO's have to have some sort of name.
5760 SET_FLAG(characteristics
, FILE_AUTOGENERATED_DEVICE_NAME
);
5763 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5766 status
= IoCreateDevice(DriverObject
,
5767 devInfo
->DeviceExtensionSize
,
5769 devInfo
->DeviceType
,
5770 devInfo
->DeviceCharacteristics
,
5774 if (!NT_SUCCESS(status
)) {
5776 DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
5778 ASSERT(deviceObject
== NULL
);
5781 // buffer is not used any longer here.
5784 if (ntUnicodeString
.Buffer
!= NULL
) {
5785 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5786 ExFreePool(ntUnicodeString
.Buffer
);
5787 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5792 PCOMMON_DEVICE_EXTENSION commonExtension
= deviceObject
->DeviceExtension
;
5795 deviceObject
->DeviceExtension
,
5796 devInfo
->DeviceExtensionSize
);
5799 // Setup version code
5802 commonExtension
->Version
= 0x03;
5805 // Setup the remove lock and event
5808 commonExtension
->IsRemoved
= NO_REMOVE
;
5809 commonExtension
->RemoveLock
= 0;
5810 KeInitializeEvent(&commonExtension
->RemoveEvent
,
5811 SynchronizationEvent
,
5815 KeInitializeSpinLock(&commonExtension
->RemoveTrackingSpinlock
);
5816 commonExtension
->RemoveTrackingList
= NULL
;
5818 commonExtension
->RemoveTrackingSpinlock
= (ULONG_PTR
) -1;
5819 commonExtension
->RemoveTrackingList
= (PVOID
) -1;
5823 // Acquire the lock once. This reference will be released when the
5824 // remove IRP has been received.
5827 ClassAcquireRemoveLock(deviceObject
, (PIRP
) deviceObject
);
5830 // Store a pointer to the driver extension so we don't have to do
5831 // lookups to get it.
5834 commonExtension
->DriverExtension
= driverExtension
;
5837 // Fill in entry points
5840 commonExtension
->DevInfo
= devInfo
;
5843 // Initialize some of the common values in the structure
5846 commonExtension
->DeviceObject
= deviceObject
;
5848 commonExtension
->LowerDeviceObject
= NULL
;
5852 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PVOID
) commonExtension
;
5854 commonExtension
->PartitionZeroExtension
= deviceObject
->DeviceExtension
;
5857 // Set the initial device object flags.
5860 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5863 // Clear the PDO list
5866 commonExtension
->ChildList
= NULL
;
5868 commonExtension
->DriverData
=
5869 ((PFUNCTIONAL_DEVICE_EXTENSION
) deviceObject
->DeviceExtension
+ 1);
5871 if(isPartitionable
) {
5873 commonExtension
->PartitionNumber
= 0;
5875 commonExtension
->PartitionNumber
= (ULONG
) (-1L);
5878 fdoExtension
->DevicePowerState
= PowerDeviceD0
;
5880 KeInitializeEvent(&fdoExtension
->EjectSynchronizationEvent
,
5881 SynchronizationEvent
,
5884 KeInitializeEvent(&fdoExtension
->ChildLock
,
5885 SynchronizationEvent
,
5888 status
= ClasspAllocateReleaseRequest(deviceObject
);
5890 if(!NT_SUCCESS(status
)) {
5891 IoDeleteDevice(deviceObject
);
5892 *DeviceObject
= NULL
;
5894 if (ntUnicodeString
.Buffer
!= NULL
) {
5895 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5896 ExFreePool(ntUnicodeString
.Buffer
);
5897 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5905 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
5906 deviceObject
->DeviceExtension
;
5908 PFUNCTIONAL_DEVICE_EXTENSION p0Extension
=
5909 LowerDevice
->DeviceExtension
;
5911 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5913 commonExtension
->PartitionZeroExtension
= p0Extension
;
5916 // Stick this onto the PDO list
5919 ClassAddChild(p0Extension
, pdoExtension
, TRUE
);
5921 commonExtension
->DriverData
= (PVOID
) (pdoExtension
+ 1);
5924 // Get the top of stack for the lower device - this allows
5925 // filters to get stuck in between the partitions and the
5929 commonExtension
->LowerDeviceObject
=
5930 IoGetAttachedDeviceReference(LowerDevice
);
5933 // Pnp will keep a reference to the lower device object long
5934 // after this partition has been deleted. Dereference now so
5935 // we don't have to deal with it later.
5938 ObDereferenceObject(commonExtension
->LowerDeviceObject
);
5941 KeInitializeEvent(&commonExtension
->PathCountEvent
, SynchronizationEvent
, TRUE
);
5943 commonExtension
->IsFdo
= IsFdo
;
5945 commonExtension
->DeviceName
= ntUnicodeString
;
5947 commonExtension
->PreviousState
= 0xff;
5949 InitializeDictionary(&(commonExtension
->FileObjectDictionary
));
5951 commonExtension
->CurrentState
= IRP_MN_STOP_DEVICE
;
5954 *DeviceObject
= deviceObject
;
5957 } // end ClassCreateDeviceObject()
5959 /*++////////////////////////////////////////////////////////////////////////////
5963 Routine Description:
5965 This function claims a device in the port driver. The port driver object
5966 is updated with the correct driver object if the device is successfully
5971 LowerDeviceObject - Supplies the base port device object.
5973 Release - Indicates the logical unit should be released rather than claimed.
5977 Returns a status indicating success or failure of the operation.
5982 IN PDEVICE_OBJECT LowerDeviceObject
,
5986 IO_STATUS_BLOCK ioStatus
;
5988 PIO_STACK_LOCATION irpStack
;
5991 SCSI_REQUEST_BLOCK srb
;
5996 // Clear the SRB fields.
5999 RtlZeroMemory(&srb
, sizeof(SCSI_REQUEST_BLOCK
));
6002 // Write length to SRB.
6005 srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
6007 srb
.Function
= Release
? SRB_FUNCTION_RELEASE_DEVICE
:
6008 SRB_FUNCTION_CLAIM_DEVICE
;
6011 // Set the event object to the unsignaled state.
6012 // It will be used to signal request completion
6015 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
6018 // Build synchronous request with no transfer.
6021 irp
= IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE
,
6032 DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
6033 return STATUS_INSUFFICIENT_RESOURCES
;
6036 irpStack
= IoGetNextIrpStackLocation(irp
);
6039 // Save SRB address in next stack for port driver.
6042 irpStack
->Parameters
.Scsi
.Srb
= &srb
;
6045 // Set up IRP Address.
6048 srb
.OriginalRequest
= irp
;
6051 // Call the port driver with the request and wait for it to complete.
6054 status
= IoCallDriver(LowerDeviceObject
, irp
);
6055 if (status
== STATUS_PENDING
) {
6057 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
6058 status
= ioStatus
.Status
;
6062 // If this is a release request, then just decrement the reference count
6063 // and return. The status does not matter.
6068 // ObDereferenceObject(LowerDeviceObject);
6069 return STATUS_SUCCESS
;
6072 if (!NT_SUCCESS(status
)) {
6076 ASSERT(srb
.DataBuffer
!= NULL
);
6077 ASSERT(!TEST_FLAG(srb
.SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
6080 } // end ClassClaimDevice()
6082 /*++////////////////////////////////////////////////////////////////////////////
6084 ClassInternalIoControl()
6086 Routine Description:
6088 This routine passes internal device controls to the port driver.
6089 Internal device controls are used by higher level drivers both for ioctls
6090 and to pass through scsi requests.
6092 If the IoControlCode does not match any of the handled ioctls and is
6093 a valid system address then the request will be treated as an SRB and
6094 passed down to the lower driver. If the IoControlCode is not a valid
6095 system address the ioctl will be failed.
6097 Callers must therefore be extremely cautious to pass correct, initialized
6098 values to this function.
6102 DeviceObject - Supplies a pointer to the device object for this request.
6104 Irp - Supplies the Irp making the request.
6108 Returns back a STATUS_PENDING or a completion status.
6112 ClassInternalIoControl(
6113 IN PDEVICE_OBJECT DeviceObject
,
6117 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6119 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6120 PIO_STACK_LOCATION nextStack
= IoGetNextIrpStackLocation(Irp
);
6124 PSCSI_REQUEST_BLOCK srb
;
6126 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
6130 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
6132 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6134 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
6136 return STATUS_DEVICE_DOES_NOT_EXIST
;
6140 // Get a pointer to the SRB.
6143 srb
= irpStack
->Parameters
.Scsi
.Srb
;
6146 // Set the parameters in the next stack location.
6149 if(commonExtension
->IsFdo
) {
6150 nextStack
->Parameters
.Scsi
.Srb
= srb
;
6151 nextStack
->MajorFunction
= IRP_MJ_SCSI
;
6152 nextStack
->MinorFunction
= IRP_MN_SCSI_CLASS
;
6156 IoCopyCurrentIrpStackLocationToNext(Irp
);
6159 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6161 return IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
6162 } // end ClassInternalIoControl()
6164 /*++////////////////////////////////////////////////////////////////////////////
6166 ClassQueryTimeOutRegistryValue()
6168 Routine Description:
6170 This routine determines whether a reg key for a user-specified timeout
6171 value exists. This should be called at initialization time.
6175 DeviceObject - Pointer to the device object we are retrieving the timeout
6180 None, but it sets a new default timeout for a class of devices.
6184 ClassQueryTimeOutRegistryValue(
6185 IN PDEVICE_OBJECT DeviceObject
6189 // Find the appropriate reg. key
6192 PCLASS_DRIVER_EXTENSION
6193 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6194 CLASS_DRIVER_EXTENSION_KEY
);
6196 PUNICODE_STRING registryPath
= &(driverExtension
->RegistryPath
);
6198 PRTL_QUERY_REGISTRY_TABLE parameters
= NULL
;
6207 if (!registryPath
) {
6211 parameters
= ExAllocatePoolWithTag(NonPagedPool
,
6212 sizeof(RTL_QUERY_REGISTRY_TABLE
)*2,
6219 size
= registryPath
->MaximumLength
+ sizeof(WCHAR
);
6220 path
= ExAllocatePoolWithTag(NonPagedPool
, size
, '2BcS');
6223 ExFreePool(parameters
);
6227 RtlZeroMemory(path
,size
);
6228 RtlCopyMemory(path
, registryPath
->Buffer
, size
- sizeof(WCHAR
));
6232 // Check for the Timeout value.
6235 RtlZeroMemory(parameters
,
6236 (sizeof(RTL_QUERY_REGISTRY_TABLE
)*2));
6238 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
6239 parameters
[0].Name
= L
"TimeOutValue";
6240 parameters
[0].EntryContext
= &timeOut
;
6241 parameters
[0].DefaultType
= REG_DWORD
;
6242 parameters
[0].DefaultData
= &zero
;
6243 parameters
[0].DefaultLength
= sizeof(ULONG
);
6245 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
| RTL_REGISTRY_OPTIONAL
,
6251 if (!(NT_SUCCESS(status
))) {
6255 ExFreePool(parameters
);
6259 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
6265 } // end ClassQueryTimeOutRegistryValue()
6267 /*++////////////////////////////////////////////////////////////////////////////
6269 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
6271 Routine Description:
6273 This routine executes when the port driver has completed a check verify
6274 ioctl. It will set the status of the master Irp, copy the media change
6275 count and complete the request.
6279 Fdo - Supplies the functional device object which represents the logical unit.
6281 Irp - Supplies the Irp which has completed.
6291 ClassCheckVerifyComplete(
6292 IN PDEVICE_OBJECT Fdo
,
6297 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6298 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6304 originalIrp
= irpStack
->Parameters
.Others
.Argument1
;
6307 // Copy the media change count and status
6310 *((PULONG
) (originalIrp
->AssociatedIrp
.SystemBuffer
)) =
6311 fdoExtension
->MediaChangeCount
;
6313 DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
6314 "device %d is %lx - saved as %lx\n",
6315 fdoExtension
->DeviceNumber
,
6316 fdoExtension
->MediaChangeCount
,
6317 *((PULONG
) originalIrp
->AssociatedIrp
.SystemBuffer
)));
6319 originalIrp
->IoStatus
.Status
= Irp
->IoStatus
.Status
;
6320 originalIrp
->IoStatus
.Information
= sizeof(ULONG
);
6322 ClassReleaseRemoveLock(Fdo
, originalIrp
);
6323 ClassCompleteRequest(Fdo
, originalIrp
, IO_DISK_INCREMENT
);
6327 return STATUS_MORE_PROCESSING_REQUIRED
;
6329 } // end ClassCheckVerifyComplete()
6331 /*++////////////////////////////////////////////////////////////////////////////
6333 ClassGetDescriptor()
6335 Routine Description:
6337 This routine will perform a query for the specified property id and will
6338 allocate a non-paged buffer to store the data in. It is the responsibility
6339 of the caller to ensure that this buffer is freed.
6341 This routine must be run at IRQL_PASSIVE_LEVEL
6345 DeviceObject - the device to query
6346 DeviceInfo - a location to store a pointer to the buffer we allocate
6351 if status is unsuccessful *DeviceInfo will be set to NULL, else the
6352 buffer allocated on behalf of the caller.
6357 IN PDEVICE_OBJECT DeviceObject
,
6358 IN PSTORAGE_PROPERTY_ID PropertyId
,
6359 OUT PSTORAGE_DESCRIPTOR_HEADER
*Descriptor
6362 STORAGE_PROPERTY_QUERY query
;
6363 IO_STATUS_BLOCK ioStatus
;
6365 PSTORAGE_DESCRIPTOR_HEADER descriptor
= NULL
;
6373 // Set the passed-in descriptor pointer to NULL as default
6379 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6380 query
.PropertyId
= *PropertyId
;
6381 query
.QueryType
= PropertyStandardQuery
;
6384 // On the first pass we just want to get the first few
6385 // bytes of the descriptor so we can read it's size
6388 descriptor
= (PVOID
)&query
;
6390 ASSERT(sizeof(STORAGE_PROPERTY_QUERY
) >= (sizeof(ULONG
)*2));
6392 ClassSendDeviceIoControlSynchronous(
6393 IOCTL_STORAGE_QUERY_PROPERTY
,
6396 sizeof(STORAGE_PROPERTY_QUERY
),
6402 if(!NT_SUCCESS(ioStatus
.Status
)) {
6404 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6405 "query properties #1\n", ioStatus
.Status
));
6406 return ioStatus
.Status
;
6409 if (descriptor
->Size
== 0) {
6412 // This DebugPrint is to help third-party driver writers
6415 DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
6416 "%x\n", ioStatus
.Status
));
6417 return STATUS_UNSUCCESSFUL
;
6422 // This time we know how much data there is so we can
6423 // allocate a buffer of the correct size
6426 length
= descriptor
->Size
;
6428 descriptor
= ExAllocatePoolWithTag(NonPagedPool
, length
, '4BcS');
6430 if(descriptor
== NULL
) {
6432 DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
6433 "(%d bytes)\n", length
));
6434 return STATUS_INSUFFICIENT_RESOURCES
;
6438 // setup the query again, as it was overwritten above
6441 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6442 query
.PropertyId
= *PropertyId
;
6443 query
.QueryType
= PropertyStandardQuery
;
6446 // copy the input to the new outputbuffer
6449 RtlCopyMemory(descriptor
,
6451 sizeof(STORAGE_PROPERTY_QUERY
)
6454 ClassSendDeviceIoControlSynchronous(
6455 IOCTL_STORAGE_QUERY_PROPERTY
,
6458 sizeof(STORAGE_PROPERTY_QUERY
),
6464 if(!NT_SUCCESS(ioStatus
.Status
)) {
6466 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6467 "query properties #1\n", ioStatus
.Status
));
6468 ExFreePool(descriptor
);
6469 return ioStatus
.Status
;
6473 // return the memory we've allocated to the caller
6476 *Descriptor
= descriptor
;
6477 return ioStatus
.Status
;
6478 } // end ClassGetDescriptor()
6480 /*++////////////////////////////////////////////////////////////////////////////
6482 ClassSignalCompletion()
6484 Routine Description:
6486 This completion routine will signal the event given as context and then
6487 return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
6488 the responsibility of the routine waiting on the event to complete the
6489 request and free the event.
6493 DeviceObject - a pointer to the device object
6495 Irp - a pointer to the irp
6497 Event - a pointer to the event to signal
6501 STATUS_MORE_PROCESSING_REQUIRED
6505 ClassSignalCompletion(
6506 IN PDEVICE_OBJECT DeviceObject
,
6511 KeSetEvent(Event
, IO_NO_INCREMENT
, FALSE
);
6513 return STATUS_MORE_PROCESSING_REQUIRED
;
6514 } // end ClassSignalCompletion()
6516 /*++////////////////////////////////////////////////////////////////////////////
6518 ClassPnpQueryFdoRelations()
6520 Routine Description:
6522 This routine will call the driver's enumeration routine to update the
6523 list of PDO's. It will then build a response to the
6524 IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
6529 Fdo - a pointer to the functional device object we are enumerating
6531 Irp - a pointer to the enumeration request
6539 ClassPnpQueryFdoRelations(
6540 IN PDEVICE_OBJECT Fdo
,
6544 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6545 PCLASS_DRIVER_EXTENSION
6546 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6547 CLASS_DRIVER_EXTENSION_KEY
);
6553 // If there's already an enumeration in progress then don't start another
6557 if(InterlockedIncrement(&(fdoExtension
->EnumerationInterlock
)) == 1) {
6558 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6561 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
6563 Irp
->IoStatus
.Status
= ClassRetrieveDeviceRelations(
6566 (PDEVICE_RELATIONS
)&Irp
->IoStatus
.Information
);
6567 InterlockedDecrement(&(fdoExtension
->EnumerationInterlock
));
6569 return Irp
->IoStatus
.Status
;
6570 } // end ClassPnpQueryFdoRelations()
6572 /*++////////////////////////////////////////////////////////////////////////////
6574 ClassMarkChildrenMissing()
6576 Routine Description:
6578 This routine will call ClassMarkChildMissing() for all children.
6579 It acquires the ChildLock before calling ClassMarkChildMissing().
6583 Fdo - the "bus's" device object, such as the disk FDO for non-removable
6584 disks with multiple partitions.
6592 ClassMarkChildrenMissing(
6593 IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
6596 PCOMMON_DEVICE_EXTENSION commonExtension
= &(Fdo
->CommonExtension
);
6597 PPHYSICAL_DEVICE_EXTENSION nextChild
= commonExtension
->ChildList
;
6601 ClassAcquireChildLock(Fdo
);
6604 PPHYSICAL_DEVICE_EXTENSION tmpChild
;
6607 * ClassMarkChildMissing will also dequeue the child extension.
6608 * So get the next pointer before calling ClassMarkChildMissing.
6610 tmpChild
= nextChild
;
6611 nextChild
= tmpChild
->CommonExtension
.ChildList
;
6612 ClassMarkChildMissing(tmpChild
, FALSE
);
6614 ClassReleaseChildLock(Fdo
);
6616 } // end ClassMarkChildrenMissing()
6618 /*++////////////////////////////////////////////////////////////////////////////
6620 ClassMarkChildMissing()
6622 Routine Description:
6624 This routine will make an active child "missing." If the device has never
6625 been enumerated then it will be deleted on the spot. If the device has
6626 not been enumerated then it will be marked as missing so that we can
6627 not report it in the next device enumeration.
6631 Child - the child device to be marked as missing.
6633 AcquireChildLock - TRUE if the child lock should be acquired before removing
6634 the missing child. FALSE if the child lock is already
6635 acquired by this thread.
6639 returns whether or not the child device object has previously been reported
6644 ClassMarkChildMissing(
6645 IN PPHYSICAL_DEVICE_EXTENSION Child
,
6646 IN BOOLEAN AcquireChildLock
6649 BOOLEAN returnValue
= Child
->IsEnumerated
;
6652 ASSERT_PDO(Child
->DeviceObject
);
6654 Child
->IsMissing
= TRUE
;
6657 // Make sure this child is not in the active list.
6660 ClassRemoveChild(Child
->CommonExtension
.PartitionZeroExtension
,
6664 if(Child
->IsEnumerated
== FALSE
) {
6665 ClassRemoveDevice(Child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
6669 } // end ClassMarkChildMissing()
6671 /*++////////////////////////////////////////////////////////////////////////////
6673 ClassRetrieveDeviceRelations()
6675 Routine Description:
6677 This routine will allocate a buffer to hold the specified list of
6678 relations. It will then fill in the list with referenced device pointers
6679 and will return the request.
6683 Fdo - pointer to the FDO being queried
6685 RelationType - what type of relations are being queried
6687 DeviceRelations - a location to store a pointer to the response
6695 ClassRetrieveDeviceRelations(
6696 IN PDEVICE_OBJECT Fdo
,
6697 IN DEVICE_RELATION_TYPE RelationType
,
6698 OUT PDEVICE_RELATIONS
*DeviceRelations
6701 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6706 PPHYSICAL_DEVICE_EXTENSION nextChild
;
6708 ULONG relationsSize
;
6709 PDEVICE_RELATIONS deviceRelations
= NULL
;
6715 ClassAcquireChildLock(fdoExtension
);
6717 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6720 // Count the number of PDO's attached to this disk
6723 while(nextChild
!= NULL
) {
6724 PCOMMON_DEVICE_EXTENSION commonExtension
;
6726 commonExtension
= &(nextChild
->CommonExtension
);
6728 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6729 (nextChild
->IsMissing
== FALSE
));
6731 nextChild
= commonExtension
->ChildList
;
6736 relationsSize
= (sizeof(DEVICE_RELATIONS
) +
6737 (count
* sizeof(PDEVICE_OBJECT
)));
6739 deviceRelations
= ExAllocatePoolWithTag(PagedPool
, relationsSize
, '5BcS');
6741 if(deviceRelations
== NULL
) {
6743 DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
6744 "%d bytes for device relations\n", relationsSize
));
6746 ClassReleaseChildLock(fdoExtension
);
6748 return STATUS_INSUFFICIENT_RESOURCES
;
6751 RtlZeroMemory(deviceRelations
, relationsSize
);
6753 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6756 while(nextChild
!= NULL
) {
6757 PCOMMON_DEVICE_EXTENSION commonExtension
;
6759 commonExtension
= &(nextChild
->CommonExtension
);
6761 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6762 (nextChild
->IsMissing
== FALSE
));
6764 deviceRelations
->Objects
[i
--] = nextChild
->DeviceObject
;
6766 status
= ObReferenceObjectByPointer(
6767 nextChild
->DeviceObject
,
6771 ASSERT(NT_SUCCESS(status
));
6773 nextChild
->IsEnumerated
= TRUE
;
6774 nextChild
= commonExtension
->ChildList
;
6777 ASSERTMSG("Child list has changed: ", i
== -1);
6779 deviceRelations
->Count
= count
;
6780 *DeviceRelations
= deviceRelations
;
6781 ClassReleaseChildLock(fdoExtension
);
6782 return STATUS_SUCCESS
;
6783 } // end ClassRetrieveDeviceRelations()
6785 /*++////////////////////////////////////////////////////////////////////////////
6789 Routine Description:
6791 This routine will call into the driver to retrieve a copy of one of it's
6796 Pdo - a pointer to the pdo being queried
6798 IdType - which type of id string is being queried
6800 IdString - an allocated unicode string structure which the driver
6810 IN PDEVICE_OBJECT Pdo
,
6811 IN BUS_QUERY_ID_TYPE IdType
,
6812 IN PUNICODE_STRING IdString
6815 PCLASS_DRIVER_EXTENSION
6816 driverExtension
= IoGetDriverObjectExtension(Pdo
->DriverObject
,
6817 CLASS_DRIVER_EXTENSION_KEY
);
6820 ASSERT(driverExtension
->InitData
.ClassQueryId
);
6824 return driverExtension
->InitData
.ClassQueryId( Pdo
, IdType
, IdString
);
6825 } // end ClassGetPdoId()
6827 /*++////////////////////////////////////////////////////////////////////////////
6829 ClassQueryPnpCapabilities()
6831 Routine Description:
6833 This routine will call into the class driver to retrieve it's pnp
6838 PhysicalDeviceObject - The physical device object to retrieve properties
6847 ClassQueryPnpCapabilities(
6848 IN PDEVICE_OBJECT DeviceObject
,
6849 IN PDEVICE_CAPABILITIES Capabilities
6852 PCLASS_DRIVER_EXTENSION driverExtension
=
6853 ClassGetDriverExtension(DeviceObject
->DriverObject
);
6854 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6856 PCLASS_QUERY_PNP_CAPABILITIES queryRoutine
= NULL
;
6860 ASSERT(DeviceObject
);
6861 ASSERT(Capabilities
);
6863 if(commonExtension
->IsFdo
) {
6864 queryRoutine
= driverExtension
->InitData
.FdoData
.ClassQueryPnpCapabilities
;
6866 queryRoutine
= driverExtension
->InitData
.PdoData
.ClassQueryPnpCapabilities
;
6870 return queryRoutine(DeviceObject
,
6873 return STATUS_NOT_IMPLEMENTED
;
6875 } // end ClassQueryPnpCapabilities()
6877 /*++////////////////////////////////////////////////////////////////////////////
6879 ClassInvalidateBusRelations()
6881 Routine Description:
6883 This routine re-enumerates the devices on the "bus". It will call into
6884 the driver's ClassEnumerate routine to update the device objects
6885 immediately. It will then schedule a bus re-enumeration for pnp by calling
6886 IoInvalidateDeviceRelations.
6890 Fdo - a pointer to the functional device object for this bus
6898 ClassInvalidateBusRelations(
6899 IN PDEVICE_OBJECT Fdo
6902 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6903 PCLASS_DRIVER_EXTENSION
6904 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6905 CLASS_DRIVER_EXTENSION_KEY
);
6907 NTSTATUS status
= STATUS_SUCCESS
;
6912 ASSERT(driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
6914 if(InterlockedIncrement(&(fdoExtension
->EnumerationInterlock
)) == 1) {
6915 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6917 InterlockedDecrement(&(fdoExtension
->EnumerationInterlock
));
6919 if(!NT_SUCCESS(status
)) {
6921 DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
6922 "returned %lx\n", status
));
6925 IoInvalidateDeviceRelations(fdoExtension
->LowerPdo
, BusRelations
);
6928 } // end ClassInvalidateBusRelations()
6930 /*++////////////////////////////////////////////////////////////////////////////
6932 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
6934 Routine Description:
6936 This routine is called to handle the "removal" of a device. It will
6937 forward the request downwards if necesssary, call into the driver
6938 to release any necessary resources (memory, events, etc) and then
6939 will delete the device object.
6943 DeviceObject - a pointer to the device object being removed
6945 RemoveType - indicates what type of remove this is (regular or surprise).
6954 IN PDEVICE_OBJECT DeviceObject
,
6958 PCLASS_DRIVER_EXTENSION
6959 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6960 CLASS_DRIVER_EXTENSION_KEY
);
6961 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6962 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
6963 PCLASS_WMI_INFO classWmiInfo
;
6964 BOOLEAN proceedWithRemove
= TRUE
;
6969 commonExtension
->IsRemoved
= REMOVE_PENDING
;
6972 * Deregister from WMI.
6974 classWmiInfo
= commonExtension
->IsFdo
?
6975 &driverExtension
->InitData
.FdoData
.ClassWmiInfo
:
6976 &driverExtension
->InitData
.PdoData
.ClassWmiInfo
;
6977 if (classWmiInfo
->GuidRegInfo
){
6978 status
= IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_DEREGISTER
);
6979 DBGTRACE(ClassDebugInfo
, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject
, status
));
6983 * If we exposed a "shingle" (a named device interface openable by CreateFile)
6984 * then delete it now.
6986 if (commonExtension
->MountedDeviceInterfaceName
.Buffer
){
6987 IoSetDeviceInterfaceState(&commonExtension
->MountedDeviceInterfaceName
, FALSE
);
6988 RtlFreeUnicodeString(&commonExtension
->MountedDeviceInterfaceName
);
6989 RtlInitUnicodeString(&commonExtension
->MountedDeviceInterfaceName
, NULL
);
6993 // If this is a surprise removal we leave the device around - which means
6994 // we don't have to (or want to) drop the remove lock and wait for pending
6995 // requests to complete.
6998 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7001 // Release the lock we acquired when the device object was created.
7004 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7006 DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
7007 commonExtension
->RemoveLock
));
7009 KeWaitForSingleObject(&commonExtension
->RemoveEvent
,
7015 DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject
));
7017 if(commonExtension
->IsFdo
) {
7019 DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
7020 "remove request.\n", DeviceObject
));
7024 PPHYSICAL_DEVICE_EXTENSION pdoExtension
= DeviceObject
->DeviceExtension
;
7026 if (pdoExtension
->IsMissing
){
7028 * The child partition PDO is missing, so we are going to go ahead
7029 * and delete it for the remove.
7031 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject
));
7035 * We got a remove for a child partition PDO which is not actually missing.
7036 * So we will NOT actually delete it.
7038 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject
));
7041 // Reacquire the remove lock for the next time this comes around.
7044 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7047 // the device wasn't missing so it's not really been removed.
7050 commonExtension
->IsRemoved
= NO_REMOVE
;
7052 IoInvalidateDeviceRelations(
7053 commonExtension
->PartitionZeroExtension
->LowerPdo
,
7056 proceedWithRemove
= FALSE
;
7062 if (proceedWithRemove
){
7065 * Call the class driver's remove handler.
7066 * All this is supposed to do is clean up its data and device interfaces.
7068 ASSERT(commonExtension
->DevInfo
->ClassRemoveDevice
);
7069 status
= commonExtension
->DevInfo
->ClassRemoveDevice(DeviceObject
, RemoveType
);
7070 ASSERT(NT_SUCCESS(status
));
7071 status
= STATUS_SUCCESS
;
7073 if (commonExtension
->IsFdo
){
7075 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
7077 ClasspDisableTimer(fdoExtension
->DeviceObject
);
7079 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7081 PPHYSICAL_DEVICE_EXTENSION child
;
7084 // Cleanup the media detection resources now that the class driver
7085 // has stopped it's timer (if any) and we can be sure they won't
7086 // call us to do detection again.
7089 ClassCleanupMediaChangeDetection(fdoExtension
);
7092 // Cleanup any Failure Prediction stuff
7094 if (fdoExtension
->FailurePredictionInfo
) {
7095 ExFreePool(fdoExtension
->FailurePredictionInfo
);
7096 fdoExtension
->FailurePredictionInfo
= NULL
;
7100 * Ordinarily all child PDOs will be removed by the time
7101 * that the parent gets the REMOVE_DEVICE.
7102 * However, if a child PDO has been created but has not
7103 * been announced in a QueryDeviceRelations, then it is
7104 * just a private data structure unknown to pnp, and we have
7105 * to delete it ourselves.
7107 ClassAcquireChildLock(fdoExtension
);
7108 while (child
= ClassRemoveChild(fdoExtension
, NULL
, FALSE
)){
7111 // Yank the pdo. This routine will unlink the device from the
7112 // pdo list so NextPdo will point to the next one when it's
7115 child
->IsMissing
= TRUE
;
7116 ClassRemoveDevice(child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
7118 ClassReleaseChildLock(fdoExtension
);
7120 else if (RemoveType
== IRP_MN_SURPRISE_REMOVAL
){
7122 * This is a surprise-remove on the parent FDO.
7123 * We will mark the child PDOs as missing so that they
7124 * will actually get deleted when they get a REMOVE_DEVICE.
7126 ClassMarkChildrenMissing(fdoExtension
);
7129 ClasspFreeReleaseRequest(DeviceObject
);
7131 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7134 // Free FDO-specific data structs
7136 if (fdoExtension
->PrivateFdoData
){
7138 DestroyAllTransferPackets(DeviceObject
);
7140 ExFreePool(fdoExtension
->PrivateFdoData
);
7141 fdoExtension
->PrivateFdoData
= NULL
;
7144 if (commonExtension
->DeviceName
.Buffer
) {
7145 ExFreePool(commonExtension
->DeviceName
.Buffer
);
7146 RtlInitUnicodeString(&commonExtension
->DeviceName
, NULL
);
7149 if (fdoExtension
->AdapterDescriptor
) {
7150 ExFreePool(fdoExtension
->AdapterDescriptor
);
7151 fdoExtension
->AdapterDescriptor
= NULL
;
7154 if (fdoExtension
->DeviceDescriptor
) {
7155 ExFreePool(fdoExtension
->DeviceDescriptor
);
7156 fdoExtension
->DeviceDescriptor
= NULL
;
7160 // Detach our device object from the stack - there's no reason
7161 // to hold off our cleanup any longer.
7164 IoDetachDevice(lowerDeviceObject
);
7169 * This is a child partition PDO.
7170 * We have already determined that it was previously marked
7171 * as missing. So if this is a REMOVE_DEVICE, we will actually
7174 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7175 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
7176 commonExtension
->PartitionZeroExtension
;
7177 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
7178 (PPHYSICAL_DEVICE_EXTENSION
) commonExtension
;
7181 // See if this device is in the child list (if this was a suprise
7182 // removal it might be) and remove it.
7185 ClassRemoveChild(fdoExtension
, pdoExtension
, TRUE
);
7189 commonExtension
->PartitionLength
.QuadPart
= 0;
7191 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7192 IoDeleteDevice(DeviceObject
);
7196 return STATUS_SUCCESS
;
7197 } // end ClassRemoveDevice()
7199 /*++////////////////////////////////////////////////////////////////////////////
7201 ClassGetDriverExtension()
7203 Routine Description:
7205 This routine will return the classpnp's driver extension.
7209 DriverObject - the driver object for which to get classpnp's extension
7213 Either NULL if none, or a pointer to the driver extension
7216 PCLASS_DRIVER_EXTENSION
7217 ClassGetDriverExtension(
7218 IN PDRIVER_OBJECT DriverObject
7221 return IoGetDriverObjectExtension(DriverObject
, CLASS_DRIVER_EXTENSION_KEY
);
7222 } // end ClassGetDriverExtension()
7224 /*++////////////////////////////////////////////////////////////////////////////
7228 Routine Description:
7230 This routine wraps the class driver's start io routine. If the device
7231 is being removed it will complete any requests with
7232 STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
7243 IN PDEVICE_OBJECT DeviceObject
,
7247 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7250 // We're already holding the remove lock so just check the variable and
7251 // see what's going on.
7254 if(commonExtension
->IsRemoved
) {
7256 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
7258 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7259 ClassReleaseRemoveLock(DeviceObject
, Irp
);
7260 ClassCompleteRequest(DeviceObject
, Irp
, IO_DISK_INCREMENT
);
7261 IoStartNextPacket(DeviceObject
, FALSE
);
7262 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7266 commonExtension
->DriverExtension
->InitData
.ClassStartIo(
7271 } // ClasspStartIo()
7273 /*++////////////////////////////////////////////////////////////////////////////
7275 ClassUpdateInformationInRegistry()
7277 Routine Description:
7279 This routine has knowledge about the layout of the device map information
7280 in the registry. It will update this information to include a value
7281 entry specifying the dos device name that is assumed to get assigned
7282 to this NT device name. For more information on this assigning of the
7283 dos device name look in the drive support routine in the hal that assigns
7286 Since some versions of some device's firmware did not work and some
7287 vendors did not bother to follow the specification, the entire inquiry
7288 information must also be stored in the registry so than someone can
7289 figure out the firmware version.
7293 DeviceObject - A pointer to the device object for the tape device.
7301 ClassUpdateInformationInRegistry(
7302 IN PDEVICE_OBJECT Fdo
,
7303 IN PCHAR DeviceName
,
7304 IN ULONG DeviceNumber
,
7305 IN PINQUIRYDATA InquiryData
,
7306 IN ULONG InquiryDataLength
7309 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
7311 SCSI_ADDRESS scsiAddress
;
7312 OBJECT_ATTRIBUTES objectAttributes
;
7315 UNICODE_STRING unicodeName
;
7316 UNICODE_STRING unicodeRegistryPath
;
7317 UNICODE_STRING unicodeData
;
7319 IO_STATUS_BLOCK ioStatus
;
7325 fdoExtension
= Fdo
->DeviceExtension
;
7328 RtlZeroMemory(&unicodeName
, sizeof(UNICODE_STRING
));
7329 RtlZeroMemory(&unicodeData
, sizeof(UNICODE_STRING
));
7330 RtlZeroMemory(&unicodeRegistryPath
, sizeof(UNICODE_STRING
));
7335 // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
7338 ClassSendDeviceIoControlSynchronous(
7339 IOCTL_SCSI_GET_ADDRESS
,
7343 sizeof(SCSI_ADDRESS
),
7348 if (!NT_SUCCESS(ioStatus
.Status
)) {
7350 status
= ioStatus
.Status
;
7352 "UpdateInformationInRegistry: Get Address failed %lx\n",
7359 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
7360 scsiAddress
.PortNumber
,
7362 scsiAddress
.TargetId
,
7368 // Allocate a buffer for the reg. spooge.
7371 buffer
= ExAllocatePoolWithTag(PagedPool
, 1024, '6BcS');
7373 if (buffer
== NULL
) {
7376 // There is not return value for this. Since this is done at
7377 // claim device time (currently only system initialization) getting
7378 // the registry information correct will be the least of the worries.
7385 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
7386 scsiAddress
.PortNumber
,
7388 scsiAddress
.TargetId
,
7391 RtlInitString(&string
, buffer
);
7393 status
= RtlAnsiStringToUnicodeString(&unicodeRegistryPath
,
7397 if (!NT_SUCCESS(status
)) {
7402 // Open the registry key for the scsi information for this
7403 // scsibus, target, lun.
7406 InitializeObjectAttributes(&objectAttributes
,
7407 &unicodeRegistryPath
,
7408 OBJ_CASE_INSENSITIVE
,
7412 status
= ZwOpenKey(&targetKey
,
7413 KEY_READ
| KEY_WRITE
,
7416 if (!NT_SUCCESS(status
)) {
7421 // Now construct and attempt to create the registry value
7422 // specifying the device name in the appropriate place in the
7426 RtlInitUnicodeString(&unicodeName
, L
"DeviceName");
7428 sprintf(buffer
, "%s%d", DeviceName
, DeviceNumber
);
7429 RtlInitString(&string
, buffer
);
7430 status
= RtlAnsiStringToUnicodeString(&unicodeData
,
7433 if (NT_SUCCESS(status
)) {
7434 status
= ZwSetValueKey(targetKey
,
7439 unicodeData
.Length
);
7443 // if they sent in data, update the registry
7446 if (InquiryDataLength
) {
7448 ASSERT(InquiryData
);
7450 RtlInitUnicodeString(&unicodeName
, L
"InquiryData");
7451 status
= ZwSetValueKey(targetKey
,
7459 // that's all, except to clean up.
7463 if (unicodeData
.Buffer
) {
7464 RtlFreeUnicodeString(&unicodeData
);
7466 if (unicodeRegistryPath
.Buffer
) {
7467 RtlFreeUnicodeString(&unicodeRegistryPath
);
7478 } // end ClassUpdateInformationInRegistry()
7480 /*++////////////////////////////////////////////////////////////////////////////
7482 ClasspSendSynchronousCompletion()
7484 Routine Description:
7486 This completion routine will set the user event in the irp after
7487 freeing the irp and the associated MDL (if any).
7491 DeviceObject - the device object which requested the completion routine
7493 Irp - the irp being completed
7499 STATUS_MORE_PROCESSING_REQUIRED
7503 ClasspSendSynchronousCompletion(
7504 IN PDEVICE_OBJECT DeviceObject
,
7509 DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
7510 DeviceObject
, Irp
, Context
));
7512 // First set the status and information fields in the io status block
7513 // provided by the caller.
7516 *(Irp
->UserIosb
) = Irp
->IoStatus
;
7519 // Unlock the pages for the data buffer.
7522 if(Irp
->MdlAddress
) {
7523 MmUnlockPages(Irp
->MdlAddress
);
7524 IoFreeMdl(Irp
->MdlAddress
);
7528 // Signal the caller's event.
7531 KeSetEvent(Irp
->UserEvent
, IO_NO_INCREMENT
, FALSE
);
7534 // Free the MDL and the IRP.
7539 return STATUS_MORE_PROCESSING_REQUIRED
;
7540 } // end ClasspSendSynchronousCompletion()
7544 ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
7548 ClasspRegisterMountedDeviceInterface(
7549 IN PDEVICE_OBJECT DeviceObject
7553 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7554 BOOLEAN isFdo
= commonExtension
->IsFdo
;
7557 UNICODE_STRING interfaceName
;
7563 PFUNCTIONAL_DEVICE_EXTENSION functionalExtension
;
7565 functionalExtension
=
7566 (PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
;
7567 pdo
= functionalExtension
->LowerPdo
;
7572 status
= IoRegisterDeviceInterface(
7574 &MOUNTDEV_MOUNTED_DEVICE_GUID
,
7579 if(NT_SUCCESS(status
)) {
7582 // Copy the interface name before setting the interface state - the
7583 // name is needed by the components we notify.
7586 commonExtension
->MountedDeviceInterfaceName
= interfaceName
;
7587 status
= IoSetDeviceInterfaceState(&interfaceName
, TRUE
);
7589 if(!NT_SUCCESS(status
)) {
7590 RtlFreeUnicodeString(&interfaceName
);
7594 if(!NT_SUCCESS(status
)) {
7595 RtlInitUnicodeString(&(commonExtension
->MountedDeviceInterfaceName
),
7599 } // end ClasspRegisterMountedDeviceInterface()
7601 /*++////////////////////////////////////////////////////////////////////////////
7603 ClassSendDeviceIoControlSynchronous()
7605 Routine Description:
7607 This routine is based upon IoBuildDeviceIoControlRequest(). It has been
7608 modified to reduce code and memory by not double-buffering the io, using
7609 the same buffer for both input and output, allocating and deallocating
7610 the mdl on behalf of the caller, and waiting for the io to complete.
7612 This routine also works around the rare cases in which APC's are disabled.
7613 Since IoBuildDeviceIoControl() used APC's to signal completion, this had
7614 led to a number of difficult-to-detect hangs, where the irp was completed,
7615 but the event passed to IoBuild..() was still being waited upon by the
7620 IoControlCode - the IOCTL to send
7622 TargetDeviceObject - the device object that should handle the ioctl
7624 Buffer - the input and output buffer, or NULL if no input/output
7626 InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
7628 OutputBufferLength - the number of bytes to be filled in upon success
7630 InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
7632 IoStatus - the status block that contains the results of the operation
7638 ClassSendDeviceIoControlSynchronous(
7639 IN ULONG IoControlCode
,
7640 IN PDEVICE_OBJECT TargetDeviceObject
,
7641 IN OUT PVOID Buffer OPTIONAL
,
7642 IN ULONG InputBufferLength
,
7643 IN ULONG OutputBufferLength
,
7644 IN BOOLEAN InternalDeviceIoControl
,
7645 OUT PIO_STATUS_BLOCK IoStatus
7649 PIO_STACK_LOCATION irpSp
;
7655 method
= IoControlCode
& 3;
7658 #if DBG // Begin Argument Checking (nop in fre version)
7660 ASSERT(ARGUMENT_PRESENT(IoStatus
));
7662 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7663 ASSERT(ARGUMENT_PRESENT(Buffer
));
7666 ASSERT(!ARGUMENT_PRESENT(Buffer
));
7671 // Begin by allocating the IRP for this request. Do not charge quota to
7672 // the current process for this IRP.
7675 irp
= IoAllocateIrp(TargetDeviceObject
->StackSize
, FALSE
);
7677 (*IoStatus
).Information
= 0;
7678 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7683 // Get a pointer to the stack location of the first driver which will be
7684 // invoked. This is where the function codes and the parameters are set.
7687 irpSp
= IoGetNextIrpStackLocation(irp
);
7690 // Set the major function code based on the type of device I/O control
7691 // function the caller has specified.
7694 if (InternalDeviceIoControl
) {
7695 irpSp
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
7697 irpSp
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
7701 // Copy the caller's parameters to the service-specific portion of the
7702 // IRP for those parameters that are the same for all four methods.
7705 irpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
= OutputBufferLength
;
7706 irpSp
->Parameters
.DeviceIoControl
.InputBufferLength
= InputBufferLength
;
7707 irpSp
->Parameters
.DeviceIoControl
.IoControlCode
= IoControlCode
;
7710 // Get the method bits from the I/O control code to determine how the
7711 // buffers are to be passed to the driver.
7716 case METHOD_BUFFERED
: {
7717 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7719 irp
->AssociatedIrp
.SystemBuffer
=
7720 ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
7721 max(InputBufferLength
, OutputBufferLength
),
7722 CLASS_TAG_DEVICE_CONTROL
7725 if (irp
->AssociatedIrp
.SystemBuffer
== NULL
) {
7727 (*IoStatus
).Information
= 0;
7728 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7732 if (InputBufferLength
!= 0) {
7733 RtlCopyMemory(irp
->AssociatedIrp
.SystemBuffer
,
7737 } // end of buffering
7739 irp
->UserBuffer
= Buffer
;
7744 case METHOD_IN_DIRECT
:
7745 case METHOD_OUT_DIRECT
: {
7748 if (InputBufferLength
!= 0) {
7749 irp
->AssociatedIrp
.SystemBuffer
= Buffer
;
7752 if (OutputBufferLength
!= 0) {
7754 irp
->MdlAddress
= IoAllocateMdl(Buffer
,
7759 if (irp
->MdlAddress
== NULL
) {
7761 (*IoStatus
).Information
= 0;
7762 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7766 if (method
== METHOD_IN_DIRECT
) {
7767 MmProbeAndLockPages(irp
->MdlAddress
,
7770 } else if (method
== METHOD_OUT_DIRECT
) {
7771 MmProbeAndLockPages(irp
->MdlAddress
,
7775 ASSERT(!"If other methods reach here, code is out of date");
7782 case METHOD_NEITHER
: {
7784 ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
7785 IoStatus
->Information
= 0;
7786 IoStatus
->Status
= STATUS_NOT_SUPPORTED
;
7790 } // end of switch(method)
7792 irp
->Tail
.Overlay
.Thread
= PsGetCurrentThread();
7795 // send the irp synchronously
7798 ClassSendIrpSynchronous(TargetDeviceObject
, irp
);
7801 // copy the iostatus block for the caller
7804 *IoStatus
= irp
->IoStatus
;
7807 // free any allocated resources
7811 case METHOD_BUFFERED
: {
7813 ASSERT(irp
->UserBuffer
== Buffer
);
7816 // first copy the buffered result, if any
7817 // Note that there are no security implications in
7818 // not checking for success since only drivers can
7819 // call into this routine anyways...
7822 if (OutputBufferLength
!= 0) {
7823 RtlCopyMemory(Buffer
, // irp->UserBuffer
7824 irp
->AssociatedIrp
.SystemBuffer
,
7830 // then free the memory allocated to buffer the io
7833 if ((InputBufferLength
!=0) || (OutputBufferLength
!= 0)) {
7834 ExFreePool(irp
->AssociatedIrp
.SystemBuffer
);
7835 irp
->AssociatedIrp
.SystemBuffer
= NULL
;
7840 case METHOD_IN_DIRECT
:
7841 case METHOD_OUT_DIRECT
: {
7844 // we alloc a mdl if there is an output buffer specified
7845 // free it here after unlocking the pages
7848 if (OutputBufferLength
!= 0) {
7849 ASSERT(irp
->MdlAddress
!= NULL
);
7850 MmUnlockPages(irp
->MdlAddress
);
7851 IoFreeMdl(irp
->MdlAddress
);
7852 irp
->MdlAddress
= (PMDL
) NULL
;
7857 case METHOD_NEITHER
: {
7858 ASSERT(!"Code is out of date");
7864 // we always have allocated an irp. free it here.
7871 // return the io status block's status to the caller
7875 } // end ClassSendDeviceIoControlSynchronous()
7877 /*++////////////////////////////////////////////////////////////////////////////
7879 ClassForwardIrpSynchronous()
7881 Routine Description:
7883 Forwards a given irp to the next lower device object.
7887 CommonExtension - the common class extension
7889 Irp - the request to forward down the stack
7895 ClassForwardIrpSynchronous(
7896 IN PCOMMON_DEVICE_EXTENSION CommonExtension
,
7900 IoCopyCurrentIrpStackLocationToNext(Irp
);
7901 return ClassSendIrpSynchronous(CommonExtension
->LowerDeviceObject
, Irp
);
7902 } // end ClassForwardIrpSynchronous()
7904 /*++////////////////////////////////////////////////////////////////////////////
7906 ClassSendIrpSynchronous()
7908 Routine Description:
7910 This routine sends the given irp to the given device object, and waits for
7911 it to complete. On debug versions, will print out a debug message and
7912 optionally assert for "lost" irps based upon classpnp's globals
7916 TargetDeviceObject - the device object to handle this irp
7918 Irp - the request to be sent
7924 ClassSendIrpSynchronous(
7925 IN PDEVICE_OBJECT TargetDeviceObject
,
7932 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL
);
7933 ASSERT(TargetDeviceObject
!= NULL
);
7934 ASSERT(Irp
!= NULL
);
7935 ASSERT(Irp
->StackCount
>= TargetDeviceObject
->StackSize
);
7938 // ISSUE-2000/02/20-henrygab What if APCs are disabled?
7939 // May need to enter critical section before IoCallDriver()
7940 // until the event is hit?
7943 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
7944 IoSetCompletionRoutine(Irp
, ClassSignalCompletion
, &event
,
7947 status
= IoCallDriver(TargetDeviceObject
, Irp
);
7949 if (status
== STATUS_PENDING
) {
7952 LARGE_INTEGER timeout
;
7954 timeout
.QuadPart
= (LONGLONG
)(-1 * 10 * 1000 * (LONGLONG
)1000 *
7955 ClasspnpGlobals
.SecondsToWaitForIrps
);
7958 status
= KeWaitForSingleObject(&event
,
7965 if (status
== STATUS_TIMEOUT
) {
7968 // This DebugPrint should almost always be investigated by the
7969 // party who sent the irp and/or the current owner of the irp.
7970 // Synchronous Irps should not take this long (currently 30
7971 // seconds) without good reason. This points to a potentially
7972 // serious problem in the underlying device stack.
7975 DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
7976 "complete within %x seconds\n",
7977 TargetDeviceObject
, Irp
,
7978 ClasspnpGlobals
.SecondsToWaitForIrps
7981 if (ClasspnpGlobals
.BreakOnLostIrps
!= 0) {
7982 ASSERT(!" - Irp failed to complete within 30 seconds - ");
7987 } while (status
==STATUS_TIMEOUT
);
7989 KeWaitForSingleObject(&event
,
7996 status
= Irp
->IoStatus
.Status
;
8000 } // end ClassSendIrpSynchronous()
8002 /*++////////////////////////////////////////////////////////////////////////////
8006 Routine Description:
8008 This routine returns the current VPB (Volume Parameter Block) for the
8009 given device object.
8010 The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
8011 of DEVICE_OBJECT; hence this exported function.
8015 DeviceObject - the device to get the VPB for
8019 the VPB, or NULL if none.
8024 IN PDEVICE_OBJECT DeviceObject
8027 return DeviceObject
->Vpb
;
8028 } // end ClassGetVpb()
8032 ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
8036 ClasspAllocateReleaseRequest(
8037 IN PDEVICE_OBJECT Fdo
8040 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8041 PIO_STACK_LOCATION irpStack
;
8043 KeInitializeSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
));
8045 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8046 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8047 fdoExtension
->ReleaseQueueIrpFromPool
= FALSE
;
8050 // The class driver is responsible for allocating a properly sized irp,
8051 // or ClassReleaseQueue will attempt to do it on the first error.
8054 fdoExtension
->ReleaseQueueIrp
= NULL
;
8057 // Write length to SRB.
8060 fdoExtension
->ReleaseQueueSrb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
8062 return STATUS_SUCCESS
;
8063 } // end ClasspAllocateReleaseRequest()
8067 ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
8071 ClasspFreeReleaseRequest(
8072 IN PDEVICE_OBJECT Fdo
8075 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8078 ASSERT(fdoExtension
->CommonExtension
.IsRemoved
!= NO_REMOVE
);
8081 // free anything the driver allocated
8084 if (fdoExtension
->ReleaseQueueIrp
) {
8085 if (fdoExtension
->ReleaseQueueIrpFromPool
) {
8086 ExFreePool(fdoExtension
->ReleaseQueueIrp
);
8088 IoFreeIrp(fdoExtension
->ReleaseQueueIrp
);
8090 fdoExtension
->ReleaseQueueIrp
= NULL
;
8094 // free anything that we allocated
8097 if ((fdoExtension
->PrivateFdoData
) &&
8098 (fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
)) {
8100 ExFreePool(fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
);
8101 fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= FALSE
;
8102 fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
= NULL
;
8106 } // end ClasspFreeReleaseRequest()
8108 /*++////////////////////////////////////////////////////////////////////////////
8112 Routine Description:
8114 This routine issues an internal device control command
8115 to the port driver to release a frozen queue. The call
8116 is issued asynchronously as ClassReleaseQueue will be invoked
8117 from the IO completion DPC (and will have no context to
8118 wait for a synchronous call to complete).
8120 This routine must be called with the remove lock held.
8124 Fdo - The functional device object for the device with the frozen queue.
8133 IN PDEVICE_OBJECT Fdo
8136 ClasspReleaseQueue(Fdo
, NULL
);
8138 } // end ClassReleaseQueue()
8140 /*++////////////////////////////////////////////////////////////////////////////
8142 ClasspAllocateReleaseQueueIrp()
8144 Routine Description:
8146 This routine allocates the release queue irp held in classpnp's private
8147 extension. This was added to allow no-memory conditions to be more
8156 Does not grab the spinlock. Should only be called from StartDevice()
8157 routine. May be called elsewhere for poorly-behaved drivers that cause
8158 the queue to lockup before the device is started. This should *never*
8159 occur, since it's illegal to send a request to a non-started PDO. This
8160 condition is checked for in ClasspReleaseQueue().
8164 ClasspAllocateReleaseQueueIrp(
8165 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8169 UCHAR lowerStackSize
;
8172 // do an initial check w/o the spinlock
8175 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8176 return STATUS_SUCCESS
;
8180 lowerStackSize
= FdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
;
8183 // don't allocate one if one is in progress! this means whoever called
8184 // this routine didn't check if one was in progress.
8187 ASSERT(!(FdoExtension
->ReleaseQueueInProgress
));
8189 FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
=
8190 ExAllocatePoolWithTag(NonPagedPool
,
8191 IoSizeOfIrp(lowerStackSize
),
8192 CLASS_TAG_RELEASE_QUEUE
8195 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
== NULL
) {
8196 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
8197 "release queue irp\n"));
8198 return STATUS_INSUFFICIENT_RESOURCES
;
8200 IoInitializeIrp(FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
,
8201 IoSizeOfIrp(lowerStackSize
),
8203 FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= TRUE
;
8205 return STATUS_SUCCESS
;
8209 /*++////////////////////////////////////////////////////////////////////////////
8211 ClasspReleaseQueue()
8213 Routine Description:
8215 This routine issues an internal device control command
8216 to the port driver to release a frozen queue. The call
8217 is issued asynchronously as ClassReleaseQueue will be invoked
8218 from the IO completion DPC (and will have no context to
8219 wait for a synchronous call to complete).
8221 This routine must be called with the remove lock held.
8225 Fdo - The functional device object for the device with the frozen queue.
8227 ReleaseQueueIrp - If this irp is supplied then the test to determine whether
8228 a release queue request is in progress will be ignored.
8229 The irp provided must be the IRP originally allocated
8230 for release queue requests (so this parameter can only
8231 really be provided by the release queue completion
8241 IN PDEVICE_OBJECT Fdo
,
8242 IN PIRP ReleaseQueueIrp OPTIONAL
8245 PIO_STACK_LOCATION irpStack
;
8247 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8248 PDEVICE_OBJECT lowerDevice
;
8249 PSCSI_REQUEST_BLOCK srb
;
8252 lowerDevice
= fdoExtension
->CommonExtension
.LowerDeviceObject
;
8255 // we raise irql seperately so we're not swapped out or suspended
8256 // while holding the release queue irp in this routine. this lets
8257 // us release the spin lock before lowering irql.
8260 KeRaiseIrql(DISPATCH_LEVEL
, ¤tIrql
);
8262 KeAcquireSpinLockAtDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8265 // make sure that if they passed us an irp, it matches our allocated irp.
8268 ASSERT((ReleaseQueueIrp
== NULL
) ||
8269 (ReleaseQueueIrp
== fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
));
8272 // ASSERT that we've already allocated this. (should not occur)
8273 // try to allocate it anyways, then finally bugcheck if
8274 // there's still no memory...
8277 ASSERT(fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
);
8278 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8279 ClasspAllocateReleaseQueueIrp(fdoExtension
);
8281 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8282 KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL
, 0x12, (ULONG_PTR
)Fdo
, 0x0, 0x0);
8285 if ((fdoExtension
->ReleaseQueueInProgress
) && (ReleaseQueueIrp
== NULL
)) {
8288 // Someone is already using the irp - just set the flag to indicate that
8289 // we need to release the queue again.
8292 fdoExtension
->ReleaseQueueNeeded
= TRUE
;
8293 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8294 KeLowerIrql(currentIrql
);
8300 // Mark that there is a release queue in progress and drop the spinlock.
8303 fdoExtension
->ReleaseQueueInProgress
= TRUE
;
8304 if (ReleaseQueueIrp
) {
8305 irp
= ReleaseQueueIrp
;
8307 irp
= fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
;
8309 srb
= &(fdoExtension
->ReleaseQueueSrb
);
8311 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8313 ASSERT(irp
!= NULL
);
8315 irpStack
= IoGetNextIrpStackLocation(irp
);
8317 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
8319 srb
->OriginalRequest
= irp
;
8322 // Store the SRB address in next stack for port driver.
8325 irpStack
->Parameters
.Scsi
.Srb
= srb
;
8328 // If this device is removable then flush the queue. This will also
8332 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
8333 srb
->Function
= SRB_FUNCTION_FLUSH_QUEUE
;
8336 srb
->Function
= SRB_FUNCTION_RELEASE_QUEUE
;
8339 ClassAcquireRemoveLock(Fdo
, irp
);
8341 IoSetCompletionRoutine(irp
,
8342 ClassReleaseQueueCompletion
,
8348 IoCallDriver(lowerDevice
, irp
);
8350 KeLowerIrql(currentIrql
);
8354 } // end ClassReleaseQueue()
8356 /*++////////////////////////////////////////////////////////////////////////////
8358 ClassReleaseQueueCompletion()
8360 Routine Description:
8362 This routine is called when an asynchronous I/O request
8363 which was issused by the class driver completes. Examples of such requests
8364 are release queue or START UNIT. This routine releases the queue if
8365 necessary. It then frees the context and the IRP.
8369 DeviceObject - The device object for the logical unit; however since this
8370 is the top stack location the value is NULL.
8372 Irp - Supplies a pointer to the Irp to be processed.
8374 Context - Supplies the context to be used to process this request.
8382 ClassReleaseQueueCompletion(
8383 PDEVICE_OBJECT DeviceObject
,
8388 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8391 BOOLEAN releaseQueueNeeded
;
8393 DeviceObject
= Context
;
8395 fdoExtension
= DeviceObject
->DeviceExtension
;
8397 ClassReleaseRemoveLock(DeviceObject
, Irp
);
8400 // Grab the spinlock and clear the release queue in progress flag so others
8401 // can run. Save (and clear) the state of the release queue needed flag
8402 // so that we can issue a new release queue outside the spinlock.
8405 KeAcquireSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), &oldIrql
);
8407 releaseQueueNeeded
= fdoExtension
->ReleaseQueueNeeded
;
8409 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8410 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8412 KeReleaseSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), oldIrql
);
8415 // If we need a release queue then issue one now. Another processor may
8416 // have already started one in which case we'll try to issue this one after
8417 // it is done - but we should never recurse more than one deep.
8420 if(releaseQueueNeeded
) {
8421 ClasspReleaseQueue(DeviceObject
, Irp
);
8425 // Indicate the I/O system should stop processing the Irp completion.
8428 return STATUS_MORE_PROCESSING_REQUIRED
;
8430 } // ClassAsynchronousCompletion()
8432 /*++////////////////////////////////////////////////////////////////////////////
8434 ClassAcquireChildLock()
8436 Routine Description:
8438 This routine acquires the lock protecting children PDOs. It may be
8439 acquired recursively by the same thread, but must be release by the
8440 thread once for each acquisition.
8444 FdoExtension - the device whose child list is protected.
8452 ClassAcquireChildLock(
8453 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8458 if(FdoExtension
->ChildLockOwner
!= KeGetCurrentThread()) {
8459 KeWaitForSingleObject(&FdoExtension
->ChildLock
,
8460 Executive
, KernelMode
,
8463 ASSERT(FdoExtension
->ChildLockOwner
== NULL
);
8464 ASSERT(FdoExtension
->ChildLockAcquisitionCount
== 0);
8466 FdoExtension
->ChildLockOwner
= KeGetCurrentThread();
8468 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8471 FdoExtension
->ChildLockAcquisitionCount
++;
8475 /*++////////////////////////////////////////////////////////////////////////////
8477 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
8479 Routine Description:
8481 This routine releases the lock protecting children PDOs. It must be
8482 called once for each time ClassAcquireChildLock was called.
8486 FdoExtension - the device whose child list is protected
8494 ClassReleaseChildLock(
8495 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8498 ASSERT(FdoExtension
->ChildLockOwner
== KeGetCurrentThread());
8499 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8501 FdoExtension
->ChildLockAcquisitionCount
-= 1;
8503 if(FdoExtension
->ChildLockAcquisitionCount
== 0) {
8504 FdoExtension
->ChildLockOwner
= NULL
;
8505 KeSetEvent(&FdoExtension
->ChildLock
, IO_NO_INCREMENT
, FALSE
);
8509 } // end ClassReleaseChildLock(
8511 /*++////////////////////////////////////////////////////////////////////////////
8515 Routine Description:
8517 This routine will insert a new child into the head of the child list.
8521 Parent - the child's parent (contains the head of the list)
8522 Child - the child to be inserted.
8523 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8524 it's already been acquired by or on behalf of the caller
8534 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8535 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8536 IN BOOLEAN AcquireLock
8540 ClassAcquireChildLock(Parent
);
8545 // Make sure this child's not already in the list.
8548 PPHYSICAL_DEVICE_EXTENSION testChild
;
8550 for (testChild
= Parent
->CommonExtension
.ChildList
;
8552 testChild
= testChild
->CommonExtension
.ChildList
) {
8554 ASSERT(testChild
!= Child
);
8559 Child
->CommonExtension
.ChildList
= Parent
->CommonExtension
.ChildList
;
8560 Parent
->CommonExtension
.ChildList
= Child
;
8563 ClassReleaseChildLock(Parent
);
8566 } // end ClassAddChild()
8568 /*++////////////////////////////////////////////////////////////////////////////
8572 Routine Description:
8574 This routine will remove a child from the child list.
8578 Parent - the parent to be removed from.
8580 Child - the child to be removed or NULL if the first child should be
8583 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8584 it's already been acquired by or on behalf of the caller
8589 A pointer to the child which was removed or NULL if no such child could
8590 be found in the list (or if Child was NULL but the list is empty).
8593 PPHYSICAL_DEVICE_EXTENSION
8595 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8596 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8597 IN BOOLEAN AcquireLock
8601 ClassAcquireChildLock(Parent
);
8605 PCOMMON_DEVICE_EXTENSION previousChild
= &Parent
->CommonExtension
;
8608 // If the list is empty then bail out now.
8611 if(Parent
->CommonExtension
.ChildList
== NULL
) {
8617 // If the caller specified a child then find the child object before
8618 // it. If none was specified then the FDO is the child object before
8619 // the one we want to remove.
8625 // Scan through the child list to find the entry which points to
8630 ASSERT(previousChild
!= &Child
->CommonExtension
);
8632 if(previousChild
->ChildList
== Child
) {
8636 previousChild
= &previousChild
->ChildList
->CommonExtension
;
8637 } while(previousChild
!= NULL
);
8639 if(previousChild
== NULL
) {
8646 // Save the next child away then unlink it from the list.
8649 Child
= previousChild
->ChildList
;
8650 previousChild
->ChildList
= Child
->CommonExtension
.ChildList
;
8651 Child
->CommonExtension
.ChildList
= NULL
;
8655 ClassReleaseChildLock(Parent
);
8659 } // end ClassRemoveChild()
8664 ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
8668 ClasspRetryRequestDpc(
8670 IN PDEVICE_OBJECT DeviceObject
,
8675 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8676 PCOMMON_DEVICE_EXTENSION commonExtension
;
8677 PCLASS_PRIVATE_FDO_DATA fdoData
;
8678 PCLASS_RETRY_INFO retryList
;
8682 commonExtension
= DeviceObject
->DeviceExtension
;
8683 ASSERT(commonExtension
->IsFdo
);
8684 fdoExtension
= DeviceObject
->DeviceExtension
;
8685 fdoData
= fdoExtension
->PrivateFdoData
;
8688 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8691 KeQueryTickCount(&now
);
8694 // if CurrentTick is less than now
8697 // retry entire list
8701 if (now
.QuadPart
< fdoData
->Retry
.Tick
.QuadPart
) {
8703 ClasspRetryDpcTimer(fdoData
);
8708 retryList
= fdoData
->Retry
.ListHead
;
8709 fdoData
->Retry
.ListHead
= NULL
;
8710 fdoData
->Retry
.Delta
.QuadPart
= (LONGLONG
)0;
8711 fdoData
->Retry
.Tick
.QuadPart
= (LONGLONG
)0;
8715 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8717 while (retryList
!= NULL
) {
8721 irp
= CONTAINING_RECORD(retryList
, IRP
, Tail
.Overlay
.DriverContext
[0]);
8722 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: -- %p\n", irp
));
8723 retryList
= retryList
->Next
;
8725 irp
->Tail
.Overlay
.DriverContext
[0] = ULongToPtr(0xdddddddd); // invalidate data
8726 irp
->Tail
.Overlay
.DriverContext
[1] = ULongToPtr(0xdddddddd); // invalidate data
8727 irp
->Tail
.Overlay
.DriverContext
[2] = ULongToPtr(0xdddddddd); // invalidate data
8728 irp
->Tail
.Overlay
.DriverContext
[3] = ULongToPtr(0xdddddddd); // invalidate data
8731 IoCallDriver(commonExtension
->LowerDeviceObject
, irp
);
8736 } // end ClasspRetryRequestDpc()
8740 ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
8745 IN PDEVICE_OBJECT SelfDeviceObject
,
8747 IN LARGE_INTEGER TimeDelta100ns
// in 100ns units
8750 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8751 PCLASS_PRIVATE_FDO_DATA fdoData
;
8752 PCLASS_RETRY_INFO retryInfo
;
8753 PCLASS_RETRY_INFO
*previousNext
;
8754 LARGE_INTEGER delta
;
8758 // this checks we aren't destroying irps
8760 ASSERT(sizeof(CLASS_RETRY_INFO
) <= (4*sizeof(PVOID
)));
8762 fdoExtension
= SelfDeviceObject
->DeviceExtension
;
8763 fdoData
= fdoExtension
->PrivateFdoData
;
8765 if (!fdoExtension
->CommonExtension
.IsFdo
) {
8768 // this debug print/assertion should ALWAYS be investigated.
8769 // ClassRetryRequest can currently only be used by FDO's
8772 DebugPrint((ClassDebugError
, "ClassRetryRequestEx: LOST IRP %p\n", Irp
));
8773 ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
8778 if (TimeDelta100ns
.QuadPart
< 0) {
8779 ASSERT(!"ClassRetryRequest - must use positive delay");
8780 TimeDelta100ns
.QuadPart
*= -1;
8784 // prepare what we can out of the loop
8787 retryInfo
= (PCLASS_RETRY_INFO
)(&Irp
->Tail
.Overlay
.DriverContext
[0]);
8788 RtlZeroMemory(retryInfo
, sizeof(CLASS_RETRY_INFO
));
8790 delta
.QuadPart
= (TimeDelta100ns
.QuadPart
/ fdoData
->Retry
.Granularity
);
8791 if (TimeDelta100ns
.QuadPart
% fdoData
->Retry
.Granularity
) {
8792 delta
.QuadPart
++; // round up to next tick
8794 if (delta
.QuadPart
== (LONGLONG
)0) {
8795 delta
.QuadPart
= MINIMUM_RETRY_UNITS
;
8799 // now determine if we should fire another DPC or not
8802 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8805 // always add request to the list
8808 retryInfo
->Next
= fdoData
->Retry
.ListHead
;
8809 fdoData
->Retry
.ListHead
= retryInfo
;
8811 if (fdoData
->Retry
.Delta
.QuadPart
== (LONGLONG
)0) {
8813 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: +++ %p\n", Irp
));
8816 // must be exactly one item on list
8819 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8820 ASSERT(fdoData
->Retry
.ListHead
->Next
== NULL
);
8823 // if currentDelta is zero, always fire a DPC
8826 KeQueryTickCount(&fdoData
->Retry
.Tick
);
8827 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8828 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8829 ClasspRetryDpcTimer(fdoData
);
8831 } else if (delta
.QuadPart
> fdoData
->Retry
.Delta
.QuadPart
) {
8834 // if delta is greater than the list's current delta,
8835 // increase the DPC handling time by difference
8836 // and update the delta to new larger value
8837 // allow the DPC to re-fire itself if needed
8840 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8843 // must be at least two items on list
8846 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8847 ASSERT(fdoData
->Retry
.ListHead
->Next
!= NULL
);
8849 fdoData
->Retry
.Tick
.QuadPart
-= fdoData
->Retry
.Delta
.QuadPart
;
8850 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8852 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8857 // just inserting it on the list was enough
8860 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8865 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8868 } // end ClassRetryRequest()
8872 ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
8876 ClasspRetryDpcTimer(
8877 IN PCLASS_PRIVATE_FDO_DATA FdoData
8882 ASSERT(FdoData
->Retry
.Tick
.QuadPart
!= (LONGLONG
)0);
8883 ASSERT(FdoData
->Retry
.ListHead
!= NULL
); // never fire an empty list
8886 // fire == (CurrentTick - now) * (100ns per tick)
8888 // NOTE: Overflow is nearly impossible and is ignored here
8891 KeQueryTickCount(&fire
);
8892 fire
.QuadPart
= FdoData
->Retry
.Tick
.QuadPart
- fire
.QuadPart
;
8893 fire
.QuadPart
*= FdoData
->Retry
.Granularity
;
8896 // fire is now multiples of 100ns until should fire the timer.
8897 // if timer should already have expired, or would fire too quickly,
8898 // fire it in some arbitrary number of ticks to prevent infinitely
8902 if (fire
.QuadPart
< MINIMUM_RETRY_UNITS
) {
8903 fire
.QuadPart
= MINIMUM_RETRY_UNITS
;
8906 DebugPrint((ClassDebugDelayedRetry
,
8907 "ClassRetry: ======= %I64x ticks\n",
8911 // must use negative to specify relative time to fire
8914 fire
.QuadPart
= fire
.QuadPart
* ((LONGLONG
)-1);
8917 // set the timer, since this is the first addition
8920 KeSetTimerEx(&FdoData
->Retry
.Timer
, fire
, 0, &FdoData
->Retry
.Dpc
);
8923 } // end ClasspRetryDpcTimer()
8926 ClasspInitializeHotplugInfo(
8927 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8930 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
8931 DEVICE_REMOVAL_POLICY deviceRemovalPolicy
;
8933 ULONG resultLength
= 0;
8934 ULONG writeCacheOverride
;
8939 // start with some default settings
8941 RtlZeroMemory(&(fdoData
->HotplugInfo
), sizeof(STORAGE_HOTPLUG_INFO
));
8944 // set the size (aka version)
8947 fdoData
->HotplugInfo
.Size
= sizeof(STORAGE_HOTPLUG_INFO
);
8950 // set if the device has removable media
8953 if (FdoExtension
->DeviceDescriptor
->RemovableMedia
) {
8954 fdoData
->HotplugInfo
.MediaRemovable
= TRUE
;
8956 fdoData
->HotplugInfo
.MediaRemovable
= FALSE
;
8960 // this refers to devices which, for reasons not yet understood,
8961 // do not fail PREVENT_MEDIA_REMOVAL requests even though they
8962 // have no way to lock the media into the drive. this allows
8963 // the filesystems to turn off delayed-write caching for these
8967 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
8968 FDO_HACK_CANNOT_LOCK_MEDIA
)) {
8969 fdoData
->HotplugInfo
.MediaHotplug
= TRUE
;
8971 fdoData
->HotplugInfo
.MediaHotplug
= FALSE
;
8976 // Look into the registry to see if the user has chosen
8977 // to override the default setting for the removal policy
8980 RtlZeroMemory(&deviceRemovalPolicy
, sizeof(DEVICE_REMOVAL_POLICY
));
8982 ClassGetDeviceParameter(FdoExtension
,
8983 CLASSP_REG_SUBKEY_NAME
,
8984 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
8985 (PULONG
)&deviceRemovalPolicy
);
8987 if (deviceRemovalPolicy
== 0)
8990 // Query the default removal policy from the kernel
8993 status
= IoGetDeviceProperty(FdoExtension
->LowerPdo
,
8994 DevicePropertyRemovalPolicy
,
8995 sizeof(DEVICE_REMOVAL_POLICY
),
8996 (PVOID
)&deviceRemovalPolicy
,
8998 if (!NT_SUCCESS(status
))
9003 if (resultLength
!= sizeof(DEVICE_REMOVAL_POLICY
))
9005 return STATUS_UNSUCCESSFUL
;
9010 // use this info to set the DeviceHotplug setting
9011 // don't rely on DeviceCapabilities, since it can't properly
9012 // determine device relations, etc. let the kernel figure this
9013 // stuff out instead.
9016 if (deviceRemovalPolicy
== RemovalPolicyExpectSurpriseRemoval
) {
9017 fdoData
->HotplugInfo
.DeviceHotplug
= TRUE
;
9019 fdoData
->HotplugInfo
.DeviceHotplug
= FALSE
;
9023 // this refers to the *filesystem* caching, but has to be included
9024 // here since it's a per-device setting. this may change to be
9025 // stored by the system in the future.
9028 writeCacheOverride
= FALSE
;
9029 ClassGetDeviceParameter(FdoExtension
,
9030 CLASSP_REG_SUBKEY_NAME
,
9031 CLASSP_REG_WRITE_CACHE_VALUE_NAME
,
9032 &writeCacheOverride
);
9034 if (writeCacheOverride
) {
9035 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= TRUE
;
9037 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= FALSE
;
9040 return STATUS_SUCCESS
;
9044 ClasspScanForClassHacks(
9045 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
9052 // remove invalid flags and save
9055 CLEAR_FLAG(Data
, FDO_HACK_INVALID_FLAGS
);
9056 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, Data
);
9061 ClasspScanForSpecialInRegistry(
9062 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
9065 HANDLE deviceParameterHandle
; // device instance key
9066 HANDLE classParameterHandle
; // classpnp subkey
9067 OBJECT_ATTRIBUTES objectAttributes
;
9068 UNICODE_STRING subkeyName
;
9072 // seeded in the ENUM tree by ClassInstaller
9075 RTL_QUERY_REGISTRY_TABLE queryTable
[2]; // null terminated array
9079 deviceParameterHandle
= NULL
;
9080 classParameterHandle
= NULL
;
9083 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
9084 PLUGPLAY_REGKEY_DEVICE
,
9086 &deviceParameterHandle
9089 if (!NT_SUCCESS(status
)) {
9090 goto cleanupScanForSpecial
;
9093 RtlInitUnicodeString(&subkeyName
, CLASSP_REG_SUBKEY_NAME
);
9094 InitializeObjectAttributes(&objectAttributes
,
9096 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
9097 deviceParameterHandle
,
9101 status
= ZwOpenKey( &classParameterHandle
,
9106 if (!NT_SUCCESS(status
)) {
9107 goto cleanupScanForSpecial
;
9111 // Zero out the memory
9114 RtlZeroMemory(&queryTable
[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
9117 // Setup the structure to read
9120 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
9121 queryTable
[0].Name
= CLASSP_REG_HACK_VALUE_NAME
;
9122 queryTable
[0].EntryContext
= &deviceHacks
;
9123 queryTable
[0].DefaultType
= REG_DWORD
;
9124 queryTable
[0].DefaultData
= &deviceHacks
;
9125 queryTable
[0].DefaultLength
= 0;
9131 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
9132 (PWSTR
)classParameterHandle
,
9137 if (!NT_SUCCESS(status
)) {
9138 goto cleanupScanForSpecial
;
9142 // remove unknown values and save...
9145 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
9146 "Classpnp => ScanForSpecial: HackFlags %#08x\n",
9149 CLEAR_FLAG(deviceHacks
, FDO_HACK_INVALID_FLAGS
);
9150 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, deviceHacks
);
9153 cleanupScanForSpecial
:
9155 if (deviceParameterHandle
) {
9156 ZwClose(deviceParameterHandle
);
9159 if (classParameterHandle
) {
9160 ZwClose(classParameterHandle
);
9164 // we should modify the system hive to include another key for us to grab
9165 // settings from. in this case: Classpnp\HackFlags
9167 // the use of a DWORD value for the HackFlags allows 32 hacks w/o
9168 // significant use of the registry, and also reduces OEM exposure.
9170 // definition of bit flags:
9171 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
9172 // cannot actually prevent removal.
9173 // 0x00000002 -- Device hard-hangs or times out for GESN requests.
9174 // 0xfffffffc -- Currently reserved, may be used later.