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.
124 IN PCLASS_INIT_DATA InitializationData
127 PDRIVER_OBJECT DriverObject
= Argument1
;
128 PUNICODE_STRING RegistryPath
= Argument2
;
130 PCLASS_DRIVER_EXTENSION driverExtension
;
136 DebugPrint((3,"\n\nSCSI Class Driver\n"));
138 ClasspInitializeDebugGlobals();
141 // Validate the length of this structure. This is effectively a
145 if (InitializationData
->InitializationDataSize
!= sizeof(CLASS_INIT_DATA
)) {
148 // This DebugPrint is to help third-party driver writers
151 DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
152 return (ULONG
) STATUS_REVISION_MISMATCH
;
156 // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
157 // are not required entry points.
160 if ((!InitializationData
->FdoData
.ClassDeviceControl
) ||
161 (!((InitializationData
->FdoData
.ClassReadWriteVerification
) ||
162 (InitializationData
->ClassStartIo
))) ||
163 (!InitializationData
->ClassAddDevice
) ||
164 (!InitializationData
->FdoData
.ClassStartDevice
)) {
167 // This DebugPrint is to help third-party driver writers
171 "ClassInitialize: Class device-specific driver missing required "
174 return (ULONG
) STATUS_REVISION_MISMATCH
;
177 if ((InitializationData
->ClassEnumerateDevice
) &&
178 ((!InitializationData
->PdoData
.ClassDeviceControl
) ||
179 (!InitializationData
->PdoData
.ClassStartDevice
) ||
180 (!((InitializationData
->PdoData
.ClassReadWriteVerification
) ||
181 (InitializationData
->ClassStartIo
))))) {
184 // This DebugPrint is to help third-party driver writers
187 DebugPrint((0, "ClassInitialize: Class device-specific missing "
188 "required PDO entry\n"));
190 return (ULONG
) STATUS_REVISION_MISMATCH
;
193 if((InitializationData
->FdoData
.ClassStopDevice
== NULL
) ||
194 ((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
195 (InitializationData
->PdoData
.ClassStopDevice
== NULL
))) {
198 // This DebugPrint is to help third-party driver writers
201 DebugPrint((0, "ClassInitialize: Class device-specific missing "
202 "required PDO entry\n"));
204 return (ULONG
) STATUS_REVISION_MISMATCH
;
208 // Setup the default power handlers if the class driver didn't provide
212 if(InitializationData
->FdoData
.ClassPowerDevice
== NULL
) {
213 InitializationData
->FdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
216 if((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
217 (InitializationData
->PdoData
.ClassPowerDevice
== NULL
)) {
218 InitializationData
->PdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
222 // warn that unload is not supported
224 // ISSUE-2000/02/03-peterwie
225 // We should think about making this a fatal error.
228 if(InitializationData
->ClassUnload
== NULL
) {
231 // This DebugPrint is to help third-party driver writers
234 DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
239 // Create an extension for the driver object
242 status
= IoAllocateDriverObjectExtension(DriverObject
,
243 CLASS_DRIVER_EXTENSION_KEY
,
244 sizeof(CLASS_DRIVER_EXTENSION
),
247 if(NT_SUCCESS(status
)) {
250 // Copy the registry path into the driver extension so we can use it later
253 driverExtension
->RegistryPath
.Length
= RegistryPath
->Length
;
254 driverExtension
->RegistryPath
.MaximumLength
= RegistryPath
->MaximumLength
;
256 driverExtension
->RegistryPath
.Buffer
=
257 ExAllocatePoolWithTag(PagedPool
,
258 RegistryPath
->MaximumLength
,
261 if(driverExtension
->RegistryPath
.Buffer
== NULL
) {
263 status
= STATUS_INSUFFICIENT_RESOURCES
;
267 RtlCopyUnicodeString(
268 &(driverExtension
->RegistryPath
),
272 // Copy the initialization data into the driver extension so we can reuse
273 // it during our add device routine
277 &(driverExtension
->InitData
),
279 sizeof(CLASS_INIT_DATA
));
281 driverExtension
->DeviceCount
= 0;
283 } else if (status
== STATUS_OBJECT_NAME_COLLISION
) {
286 // The extension already exists - get a pointer to it
289 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
290 CLASS_DRIVER_EXTENSION_KEY
);
292 ASSERT(driverExtension
!= NULL
);
296 DebugPrint((1, "ClassInitialize: Class driver extension could not be "
297 "allocated %lx\n", status
));
302 // Update driver object with entry points.
305 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = ClassCreateClose
;
306 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = ClassCreateClose
;
307 DriverObject
->MajorFunction
[IRP_MJ_READ
] = ClassReadWrite
;
308 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = ClassReadWrite
;
309 DriverObject
->MajorFunction
[IRP_MJ_SCSI
] = ClassInternalIoControl
;
310 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = ClassDeviceControlDispatch
;
311 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = ClassShutdownFlush
;
312 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = ClassShutdownFlush
;
313 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = ClassDispatchPnp
;
314 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = ClassDispatchPower
;
315 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = ClassSystemControl
;
317 if (InitializationData
->ClassStartIo
) {
318 DriverObject
->DriverStartIo
= ClasspStartIo
;
321 if ((InitializationData
->ClassUnload
) && (ClassPnpAllowUnload
== TRUE
)) {
322 DriverObject
->DriverUnload
= ClassUnload
;
324 DriverObject
->DriverUnload
= NULL
;
327 DriverObject
->DriverExtension
->AddDevice
= ClassAddDevice
;
329 DbgPrint("Driver is ready to go\n");
330 status
= STATUS_SUCCESS
;
332 } // end ClassInitialize()
334 /*++////////////////////////////////////////////////////////////////////////////
340 This routine is allows the caller to do any extra initialization or
341 setup that is not done in ClassInitialize. The operation is
342 controlled by the GUID that is passed and the contents of the Data
343 parameter is dependent upon the GUID.
345 This is the list of supported operations:
347 Guid - GUID_CLASSPNP_QUERY_REGINFOEX
348 Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
350 Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
351 callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
352 former callback allows the driver to specify the name of the
370 IN PDRIVER_OBJECT DriverObject
,
375 PCLASS_DRIVER_EXTENSION driverExtension
;
381 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
382 CLASS_DRIVER_EXTENSION_KEY
384 if (IsEqualGUID(Guid
, &ClassGuidQueryRegInfoEx
))
386 PCLASS_QUERY_WMI_REGINFO_EX_LIST List
;
389 // Indicate the device supports PCLASS_QUERY_REGINFO_EX
390 // callback instead of PCLASS_QUERY_REGINFO callback.
392 List
= (PCLASS_QUERY_WMI_REGINFO_EX_LIST
)Data
;
394 if (List
->Size
== sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST
))
396 driverExtension
->ClassFdoQueryWmiRegInfoEx
= List
->ClassFdoQueryWmiRegInfoEx
;
397 driverExtension
->ClassPdoQueryWmiRegInfoEx
= List
->ClassPdoQueryWmiRegInfoEx
;
398 status
= STATUS_SUCCESS
;
400 status
= STATUS_INVALID_PARAMETER
;
403 status
= STATUS_NOT_SUPPORTED
;
408 } // end ClassInitializeEx()
410 /*++////////////////////////////////////////////////////////////////////////////
416 called when there are no more references to the driver. this allows
417 drivers to be updated without rebooting.
421 DriverObject - a pointer to the driver object that is being unloaded
428 IN PDRIVER_OBJECT DriverObject
431 PCLASS_DRIVER_EXTENSION driverExtension
;
436 ASSERT( DriverObject
->DeviceObject
== NULL
);
438 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
439 CLASS_DRIVER_EXTENSION_KEY
442 ASSERT(driverExtension
!= NULL
);
443 ASSERT(driverExtension
->RegistryPath
.Buffer
!= NULL
);
444 ASSERT(driverExtension
->InitData
.ClassUnload
!= NULL
);
446 DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
447 &driverExtension
->RegistryPath
));
450 // attempt to process the driver's unload routine first.
453 driverExtension
->InitData
.ClassUnload(DriverObject
);
456 // free own allocated resources and return
459 ExFreePool( driverExtension
->RegistryPath
.Buffer
);
460 driverExtension
->RegistryPath
.Buffer
= NULL
;
461 driverExtension
->RegistryPath
.Length
= 0;
462 driverExtension
->RegistryPath
.MaximumLength
= 0;
465 } // end ClassUnload()
467 /*++////////////////////////////////////////////////////////////////////////////
473 SCSI class driver add device routine. This is called by pnp when a new
474 physical device come into being.
476 This routine will call out to the class driver to verify that it should
477 own this device then will create and attach a device object and then hand
478 it to the driver to initialize and create symbolic links
482 DriverObject - a pointer to the driver object that this is being created for
483 PhysicalDeviceObject - a pointer to the physical device object
485 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
486 STATUS_SUCCESS if the creation and attachment was successful
487 status of device creation and initialization
492 IN PDRIVER_OBJECT DriverObject
,
493 IN PDEVICE_OBJECT PhysicalDeviceObject
496 PCLASS_DRIVER_EXTENSION driverExtension
=
497 IoGetDriverObjectExtension(DriverObject
,
498 CLASS_DRIVER_EXTENSION_KEY
);
504 DbgPrint("got a device\n");
505 status
= driverExtension
->InitData
.ClassAddDevice(DriverObject
,
506 PhysicalDeviceObject
);
508 } // end ClassAddDevice()
510 /*++////////////////////////////////////////////////////////////////////////////
516 Storage class driver pnp routine. This is called by the io system when
517 a PNP request is sent to the device.
521 DeviceObject - pointer to the device object
523 Irp - pointer to the io request packet
532 IN PDEVICE_OBJECT DeviceObject
,
536 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
537 BOOLEAN isFdo
= commonExtension
->IsFdo
;
539 PCLASS_DRIVER_EXTENSION driverExtension
;
540 PCLASS_INIT_DATA initData
;
541 PCLASS_DEV_INFO devInfo
;
543 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
544 PIO_STACK_LOCATION nextIrpStack
= IoGetNextIrpStackLocation(Irp
);
546 NTSTATUS status
= Irp
->IoStatus
.Status
;
547 BOOLEAN completeRequest
= TRUE
;
548 BOOLEAN lockReleased
= FALSE
;
555 // Extract all the useful information out of the driver object
559 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
560 CLASS_DRIVER_EXTENSION_KEY
);
561 if (driverExtension
){
563 initData
= &(driverExtension
->InitData
);
566 devInfo
= &(initData
->FdoData
);
568 devInfo
= &(initData
->PdoData
);
571 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
573 DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
575 irpStack
->MinorFunction
,
576 isFdo
? "fdo" : "pdo",
578 DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
580 commonExtension
->PreviousState
,
581 commonExtension
->CurrentState
));
583 switch(irpStack
->MinorFunction
) {
585 case IRP_MN_START_DEVICE
: {
588 // if this is sent to the FDO we should forward it down the
589 // attachment chain before we start the FDO.
593 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
596 status
= STATUS_SUCCESS
;
599 if (NT_SUCCESS(status
)){
600 status
= Irp
->IoStatus
.Status
= ClassPnpStartDevice(DeviceObject
);
607 case IRP_MN_QUERY_DEVICE_RELATIONS
: {
609 DEVICE_RELATION_TYPE type
=
610 irpStack
->Parameters
.QueryDeviceRelations
.Type
;
612 PDEVICE_RELATIONS deviceRelations
= NULL
;
616 if(type
== TargetDeviceRelation
) {
619 // Device relations has one entry built in to it's size.
622 status
= STATUS_INSUFFICIENT_RESOURCES
;
624 deviceRelations
= ExAllocatePoolWithTag(PagedPool
,
625 sizeof(DEVICE_RELATIONS
),
628 if(deviceRelations
!= NULL
) {
630 RtlZeroMemory(deviceRelations
,
631 sizeof(DEVICE_RELATIONS
));
633 Irp
->IoStatus
.Information
= (ULONG_PTR
) deviceRelations
;
635 deviceRelations
->Count
= 1;
636 deviceRelations
->Objects
[0] = DeviceObject
;
637 ObReferenceObject(deviceRelations
->Objects
[0]);
639 status
= STATUS_SUCCESS
;
644 // PDO's just complete enumeration requests without altering
648 status
= Irp
->IoStatus
.Status
;
653 } else if (type
== BusRelations
) {
655 ASSERT(commonExtension
->IsInitialized
);
658 // Make sure we support enumeration
661 if(initData
->ClassEnumerateDevice
== NULL
) {
664 // Just send the request down to the lower driver. Perhaps
665 // It can enumerate children.
671 // Re-enumerate the device
674 status
= ClassPnpQueryFdoRelations(DeviceObject
, Irp
);
676 if(!NT_SUCCESS(status
)) {
677 completeRequest
= TRUE
;
683 IoCopyCurrentIrpStackLocationToNext(Irp
);
684 ClassReleaseRemoveLock(DeviceObject
, Irp
);
685 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
686 completeRequest
= FALSE
;
691 case IRP_MN_QUERY_ID
: {
693 BUS_QUERY_ID_TYPE idType
= irpStack
->Parameters
.QueryId
.IdType
;
694 UNICODE_STRING unicodeString
;
699 // FDO's should just forward the query down to the lower
703 IoCopyCurrentIrpStackLocationToNext(Irp
);
704 ClassReleaseRemoveLock(DeviceObject
, Irp
);
706 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
707 completeRequest
= FALSE
;
712 // PDO's need to give an answer - this is easy for now
715 RtlInitUnicodeString(&unicodeString
, NULL
);
717 status
= ClassGetPdoId(DeviceObject
,
721 if(status
== STATUS_NOT_IMPLEMENTED
) {
723 // The driver doesn't implement this ID (whatever it is).
724 // Use the status out of the IRP so that we don't mangle a
725 // response from someone else.
728 status
= Irp
->IoStatus
.Status
;
729 } else if(NT_SUCCESS(status
)) {
730 Irp
->IoStatus
.Information
= (ULONG_PTR
) unicodeString
.Buffer
;
732 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
738 case IRP_MN_QUERY_STOP_DEVICE
:
739 case IRP_MN_QUERY_REMOVE_DEVICE
: {
741 DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
743 ((irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) ?
744 "STOP" : "REMOVE")));
747 // If this device is in use for some reason (paging, etc...)
748 // then we need to fail the request.
751 if(commonExtension
->PagingPathCount
!= 0) {
753 DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
754 "path and cannot be removed\n",
756 status
= STATUS_DEVICE_BUSY
;
761 // Check with the class driver to see if the query operation
765 if(irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) {
766 status
= devInfo
->ClassStopDevice(DeviceObject
,
767 irpStack
->MinorFunction
);
769 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
770 irpStack
->MinorFunction
);
773 if(NT_SUCCESS(status
)) {
776 // ASSERT that we never get two queries in a row, as
777 // this will severly mess up the state machine
779 ASSERT(commonExtension
->CurrentState
!= irpStack
->MinorFunction
);
780 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
781 commonExtension
->CurrentState
= irpStack
->MinorFunction
;
784 DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
785 "%s irp\n", DeviceObject
, Irp
,
786 ((irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) ?
787 "STOP" : "REMOVE")));
788 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
791 DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
792 DeviceObject
, Irp
, status
));
797 case IRP_MN_CANCEL_STOP_DEVICE
:
798 case IRP_MN_CANCEL_REMOVE_DEVICE
: {
801 // Check with the class driver to see if the query or cancel
802 // operation can succeed.
805 if(irpStack
->MinorFunction
== IRP_MN_CANCEL_STOP_DEVICE
) {
806 status
= devInfo
->ClassStopDevice(DeviceObject
,
807 irpStack
->MinorFunction
);
808 ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
809 "never be failed\n", NT_SUCCESS(status
));
811 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
812 irpStack
->MinorFunction
);
813 ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
814 "never be failed\n", NT_SUCCESS(status
));
817 Irp
->IoStatus
.Status
= status
;
820 // We got a CANCEL - roll back to the previous state only
821 // if the current state is the respective QUERY state.
824 if(((irpStack
->MinorFunction
== IRP_MN_CANCEL_STOP_DEVICE
) &&
825 (commonExtension
->CurrentState
== IRP_MN_QUERY_STOP_DEVICE
)
827 ((irpStack
->MinorFunction
== IRP_MN_CANCEL_REMOVE_DEVICE
) &&
828 (commonExtension
->CurrentState
== IRP_MN_QUERY_REMOVE_DEVICE
)
832 commonExtension
->CurrentState
=
833 commonExtension
->PreviousState
;
834 commonExtension
->PreviousState
= 0xff;
839 IoCopyCurrentIrpStackLocationToNext(Irp
);
840 ClassReleaseRemoveLock(DeviceObject
, Irp
);
841 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
842 completeRequest
= FALSE
;
844 status
= STATUS_SUCCESS
;
850 case IRP_MN_STOP_DEVICE
: {
853 // These all mean nothing to the class driver currently. The
854 // port driver will handle all queueing when necessary.
857 DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
859 (isFdo
? "fdo" : "pdo")
862 ASSERT(commonExtension
->PagingPathCount
== 0);
865 // ISSUE-2000/02/03-peterwie
866 // if we stop the timer here then it means no class driver can
867 // do i/o in its ClassStopDevice routine. This is because the
868 // retry (among other things) is tied into the tick handler
869 // and disabling retries could cause the class driver to deadlock.
870 // Currently no class driver we're aware of issues i/o in its
871 // Stop routine but this is a case we may want to defend ourself
875 if (DeviceObject
->Timer
) {
876 IoStopTimer(DeviceObject
);
879 status
= devInfo
->ClassStopDevice(DeviceObject
, IRP_MN_STOP_DEVICE
);
881 ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
882 "never be failed\n", NT_SUCCESS(status
));
885 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
888 if(NT_SUCCESS(status
)) {
889 commonExtension
->CurrentState
= irpStack
->MinorFunction
;
890 commonExtension
->PreviousState
= 0xff;
896 case IRP_MN_REMOVE_DEVICE
:
897 case IRP_MN_SURPRISE_REMOVAL
: {
899 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
900 UCHAR removeType
= irpStack
->MinorFunction
;
902 if (commonExtension
->PagingPathCount
!= 0) {
903 DBGTRACE(ClassDebugWarning
, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject
, Irp
));
907 // Release the lock for this IRP before calling in.
909 ClassReleaseRemoveLock(DeviceObject
, Irp
);
913 * If a timer was started on the device, stop it.
915 if (DeviceObject
->Timer
) {
916 IoStopTimer(DeviceObject
);
920 * "Fire-and-forget" the remove irp to the lower stack.
921 * Don't touch the irp (or the irp stack!) after this.
924 IoCopyCurrentIrpStackLocationToNext(Irp
);
925 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
926 ASSERT(NT_SUCCESS(status
));
927 completeRequest
= FALSE
;
930 status
= STATUS_SUCCESS
;
934 * Do our own cleanup and call the class driver's remove
936 * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
937 * so don't touch the extension after this.
939 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
940 commonExtension
->CurrentState
= removeType
;
941 ClassRemoveDevice(DeviceObject
, removeType
);
946 case IRP_MN_DEVICE_USAGE_NOTIFICATION
: {
948 switch(irpStack
->Parameters
.UsageNotification
.Type
) {
950 case DeviceUsageTypePaging
: {
954 if((irpStack
->Parameters
.UsageNotification
.InPath
) &&
955 (commonExtension
->CurrentState
!= IRP_MN_START_DEVICE
)) {
958 // Device isn't started. Don't allow adding a
959 // paging file, but allow a removal of one.
962 status
= STATUS_DEVICE_NOT_READY
;
966 ASSERT(commonExtension
->IsInitialized
);
969 // need to synchronize this now...
972 KeEnterCriticalRegion();
973 status
= KeWaitForSingleObject(&commonExtension
->PathCountEvent
,
974 Executive
, KernelMode
,
976 ASSERT(NT_SUCCESS(status
));
977 status
= STATUS_SUCCESS
;
980 // If the volume is removable we should try to lock it in
981 // place or unlock it once per paging path count
984 if (commonExtension
->IsFdo
){
985 status
= ClasspEjectionControl(
989 (BOOLEAN
)irpStack
->Parameters
.UsageNotification
.InPath
);
992 if (!NT_SUCCESS(status
)){
993 KeSetEvent(&commonExtension
->PathCountEvent
, IO_NO_INCREMENT
, FALSE
);
994 KeLeaveCriticalRegion();
999 // if removing last paging device, need to set DO_POWER_PAGABLE
1000 // bit here, and possible re-set it below on failure.
1005 if (!irpStack
->Parameters
.UsageNotification
.InPath
&&
1006 commonExtension
->PagingPathCount
== 1
1010 // removing last paging file
1011 // must have DO_POWER_PAGABLE bits set, but only
1012 // if noone set the DO_POWER_INRUSH bit
1016 if (TEST_FLAG(DeviceObject
->Flags
, DO_POWER_INRUSH
)) {
1017 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1018 "paging file removed, but "
1019 "DO_POWER_INRUSH was set, so NOT "
1020 "setting DO_POWER_PAGABLE\n",
1021 DeviceObject
, Irp
));
1023 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1024 "paging file removed, "
1025 "setting DO_POWER_PAGABLE\n",
1026 DeviceObject
, Irp
));
1027 SET_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1034 // forward the irp before finishing handling the
1038 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1041 // now deal with the failure and success cases.
1042 // note that we are not allowed to fail the irp
1043 // once it is sent to the lower drivers.
1046 if (NT_SUCCESS(status
)) {
1048 IoAdjustPagingPathCount(
1049 &commonExtension
->PagingPathCount
,
1050 irpStack
->Parameters
.UsageNotification
.InPath
);
1052 if (irpStack
->Parameters
.UsageNotification
.InPath
) {
1053 if (commonExtension
->PagingPathCount
== 1) {
1054 DebugPrint((2, "ClassDispatchPnp (%p,%p): "
1055 "Clearing PAGABLE bit\n",
1056 DeviceObject
, Irp
));
1057 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1064 // cleanup the changes done above
1067 if (setPagable
== TRUE
) {
1068 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
1069 "PAGABLE bit due to irp failure\n",
1070 DeviceObject
, Irp
));
1071 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1076 // relock or unlock the media if needed.
1079 if (commonExtension
->IsFdo
) {
1081 ClasspEjectionControl(
1085 (BOOLEAN
)!irpStack
->Parameters
.UsageNotification
.InPath
);
1090 // set the event so the next one can occur.
1093 KeSetEvent(&commonExtension
->PathCountEvent
,
1094 IO_NO_INCREMENT
, FALSE
);
1095 KeLeaveCriticalRegion();
1099 case DeviceUsageTypeHibernation
: {
1101 IoAdjustPagingPathCount(
1102 &commonExtension
->HibernationPathCount
,
1103 irpStack
->Parameters
.UsageNotification
.InPath
1105 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1106 if (!NT_SUCCESS(status
)) {
1107 IoAdjustPagingPathCount(
1108 &commonExtension
->HibernationPathCount
,
1109 !irpStack
->Parameters
.UsageNotification
.InPath
1116 case DeviceUsageTypeDumpFile
: {
1117 IoAdjustPagingPathCount(
1118 &commonExtension
->DumpPathCount
,
1119 irpStack
->Parameters
.UsageNotification
.InPath
1121 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1122 if (!NT_SUCCESS(status
)) {
1123 IoAdjustPagingPathCount(
1124 &commonExtension
->DumpPathCount
,
1125 !irpStack
->Parameters
.UsageNotification
.InPath
1133 status
= STATUS_INVALID_PARAMETER
;
1140 case IRP_MN_QUERY_CAPABILITIES
: {
1142 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1143 DeviceObject
, Irp
));
1147 status
= ClassQueryPnpCapabilities(
1149 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
1156 PDEVICE_CAPABILITIES deviceCapabilities
;
1157 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
1158 PCLASS_PRIVATE_FDO_DATA fdoData
;
1160 fdoExtension
= DeviceObject
->DeviceExtension
;
1161 fdoData
= fdoExtension
->PrivateFdoData
;
1162 deviceCapabilities
=
1163 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
;
1166 // forward the irp before handling the special cases
1169 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1170 if (!NT_SUCCESS(status
)) {
1175 // we generally want to remove the device from the hotplug
1176 // applet, which requires the SR-OK bit to be set.
1177 // only when the user specifies that they are capable of
1178 // safely removing things do we want to clear this bit
1179 // (saved in WriteCacheEnableOverride)
1181 // setting of this bit is done either above, or by the
1184 // note: may not be started, so check we have FDO data first.
1188 fdoData
->HotplugInfo
.WriteCacheEnableOverride
) {
1189 if (deviceCapabilities
->SurpriseRemovalOK
) {
1190 DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
1191 "device capabilities due to hotplug "
1192 "device or media\n"));
1194 deviceCapabilities
->SurpriseRemovalOK
= FALSE
;
1198 } // end QUERY_CAPABILITIES for FDOs
1204 } // end QUERY_CAPABILITIES
1209 IoCopyCurrentIrpStackLocationToNext(Irp
);
1211 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1212 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
1214 completeRequest
= FALSE
;
1222 ASSERT(driverExtension
);
1223 status
= STATUS_INTERNAL_ERROR
;
1226 if (completeRequest
){
1227 Irp
->IoStatus
.Status
= status
;
1230 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1233 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1235 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject
, Irp
, commonExtension
->PreviousState
, commonExtension
->CurrentState
));
1239 * The irp is already completed so don't touch it.
1240 * This may be a remove so don't touch the device extension.
1242 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject
, Irp
));
1246 } // end ClassDispatchPnp()
1248 /*++////////////////////////////////////////////////////////////////////////////
1250 ClassPnpStartDevice()
1252 Routine Description:
1254 Storage class driver routine for IRP_MN_START_DEVICE requests.
1255 This routine kicks off any device specific initialization
1259 DeviceObject - a pointer to the device object
1261 Irp - a pointer to the io request packet
1268 NTSTATUS
ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject
)
1270 PCLASS_DRIVER_EXTENSION driverExtension
;
1271 PCLASS_INIT_DATA initData
;
1273 PCLASS_DEV_INFO devInfo
;
1275 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1276 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
1277 BOOLEAN isFdo
= commonExtension
->IsFdo
;
1279 BOOLEAN isMountedDevice
= TRUE
;
1280 UNICODE_STRING interfaceName
;
1282 BOOLEAN timerStarted
;
1284 NTSTATUS status
= STATUS_SUCCESS
;
1288 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
1289 CLASS_DRIVER_EXTENSION_KEY
);
1291 initData
= &(driverExtension
->InitData
);
1293 devInfo
= &(initData
->FdoData
);
1295 devInfo
= &(initData
->PdoData
);
1298 ASSERT(devInfo
->ClassInitDevice
!= NULL
);
1299 ASSERT(devInfo
->ClassStartDevice
!= NULL
);
1301 if (!commonExtension
->IsInitialized
){
1304 // perform FDO/PDO specific initialization
1308 STORAGE_PROPERTY_ID propertyId
;
1311 // allocate a private extension for class data
1314 if (fdoExtension
->PrivateFdoData
== NULL
) {
1315 fdoExtension
->PrivateFdoData
=
1316 ExAllocatePoolWithTag(NonPagedPool
,
1317 sizeof(CLASS_PRIVATE_FDO_DATA
),
1318 CLASS_TAG_PRIVATE_DATA
1322 if (fdoExtension
->PrivateFdoData
== NULL
) {
1323 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
1324 "private fdo data\n"));
1325 return STATUS_INSUFFICIENT_RESOURCES
;
1329 // initialize the struct's various fields.
1332 RtlZeroMemory(fdoExtension
->PrivateFdoData
,
1333 sizeof(CLASS_PRIVATE_FDO_DATA
)
1335 KeInitializeTimer(&fdoExtension
->PrivateFdoData
->Retry
.Timer
);
1336 KeInitializeDpc(&fdoExtension
->PrivateFdoData
->Retry
.Dpc
,
1337 ClasspRetryRequestDpc
,
1339 KeInitializeSpinLock(&fdoExtension
->PrivateFdoData
->Retry
.Lock
);
1340 fdoExtension
->PrivateFdoData
->Retry
.Granularity
=
1341 KeQueryTimeIncrement();
1342 commonExtension
->Reserved4
= (ULONG_PTR
)(' GPH'); // debug aid
1345 // NOTE: the old interface allowed the class driver to allocate
1346 // this. this was unsafe for low-memory conditions. allocate one
1347 // unconditionally now, and modify our internal functions to use
1348 // our own exclusively as it is the only safe way to do this.
1351 status
= ClasspAllocateReleaseQueueIrp(fdoExtension
);
1352 if (!NT_SUCCESS(status
)) {
1353 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
1354 "private release queue irp\n"));
1359 // Call port driver to get adapter capabilities.
1362 propertyId
= StorageAdapterProperty
;
1364 status
= ClassGetDescriptor(
1365 commonExtension
->LowerDeviceObject
,
1367 &fdoExtension
->AdapterDescriptor
);
1369 if(!NT_SUCCESS(status
)) {
1372 // This DebugPrint is to help third-party driver writers
1375 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1376 "[ADAPTER] failed %lx\n", status
));
1381 // Call port driver to get device descriptor.
1384 propertyId
= StorageDeviceProperty
;
1386 status
= ClassGetDescriptor(
1387 commonExtension
->LowerDeviceObject
,
1389 &fdoExtension
->DeviceDescriptor
);
1391 if(!NT_SUCCESS(status
)) {
1394 // This DebugPrint is to help third-party driver writers
1397 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1398 "[DEVICE] failed %lx\n", status
));
1402 ClasspScanForSpecialInRegistry(fdoExtension
);
1403 ClassScanForSpecial(fdoExtension
,
1405 ClasspScanForClassHacks
);
1408 // allow perf to be re-enabled after a given number of failed IOs
1409 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
1414 ClassGetDeviceParameter(fdoExtension
,
1415 CLASSP_REG_SUBKEY_NAME
,
1416 CLASSP_REG_PERF_RESTORE_VALUE_NAME
,
1418 if (t
>= CLASS_PERF_RESTORE_MINIMUM
) {
1419 fdoExtension
->PrivateFdoData
->Perf
.ReEnableThreshhold
= t
;
1425 // compatibility comes first. writable cd media will not
1426 // get a SYNCH_CACHE on power down.
1429 if (fdoExtension
->DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) {
1430 SET_FLAG(fdoExtension
->PrivateFdoData
->HackFlags
,
1431 FDO_HACK_NO_SYNC_CACHE
);
1435 // initialize the hotplug information only after the ScanForSpecial
1436 // routines, as it relies upon the hack flags.
1439 status
= ClasspInitializeHotplugInfo(fdoExtension
);
1441 if (!NT_SUCCESS(status
)) {
1442 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
1443 "hotplug information %lx\n", status
));
1448 * Allocate/initialize TRANSFER_PACKETs and related resources.
1450 status
= InitializeTransferPackets(DeviceObject
);
1454 // ISSUE - drivers need to disable write caching on the media
1455 // if hotplug and !useroverride. perhaps we should
1456 // allow registration of a callback to enable/disable
1457 // write cache instead.
1460 if (NT_SUCCESS(status
)){
1461 status
= devInfo
->ClassInitDevice(DeviceObject
);
1466 if (!NT_SUCCESS(status
)){
1469 // Just bail out - the remove that comes down will clean up the
1470 // initialized scraps.
1475 commonExtension
->IsInitialized
= TRUE
;
1477 if (commonExtension
->IsFdo
) {
1478 fdoExtension
->PrivateFdoData
->Perf
.OriginalSrbFlags
= fdoExtension
->SrbFlags
;
1484 // If device requests autorun functionality or a once a second callback
1485 // then enable the once per second timer.
1487 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
1488 // called in the context of the ClassInitDevice callback. If called
1489 // after then this check will have already been made and the
1490 // once a second timer will not have been enabled.
1493 ((initData
->ClassTick
!= NULL
) ||
1494 (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) ||
1495 ((fdoExtension
->FailurePredictionInfo
!= NULL
) &&
1496 (fdoExtension
->FailurePredictionInfo
->Method
!= FailurePredictionNone
))))
1498 ClasspEnableTimer(DeviceObject
);
1499 timerStarted
= TRUE
;
1501 timerStarted
= FALSE
;
1505 // NOTE: the timer looks at commonExtension->CurrentState now
1506 // to prevent Media Change Notification code from running
1507 // until the device is started, but allows the device
1508 // specific tick handler to run. therefore it is imperative
1509 // that commonExtension->CurrentState not be updated until
1510 // the device specific startdevice handler has finished.
1513 status
= devInfo
->ClassStartDevice(DeviceObject
);
1515 if(NT_SUCCESS(status
)) {
1516 commonExtension
->CurrentState
= IRP_MN_START_DEVICE
;
1518 if((isFdo
) && (initData
->ClassEnumerateDevice
!= NULL
)) {
1519 isMountedDevice
= FALSE
;
1522 if((DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) &&
1523 (DeviceObject
->DeviceType
!= FILE_DEVICE_CD_ROM
)) {
1525 isMountedDevice
= FALSE
;
1529 if(isMountedDevice
) {
1530 ClasspRegisterMountedDeviceInterface(DeviceObject
);
1533 if((commonExtension
->IsFdo
) &&
1534 (devInfo
->ClassWmiInfo
.GuidRegInfo
!= NULL
)) {
1536 IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_REGISTER
);
1541 ClasspDisableTimer(DeviceObject
);
1549 /*++////////////////////////////////////////////////////////////////////////////
1553 Routine Description:
1555 This is the system entry point for read and write requests. The
1556 device-specific handler is invoked to perform any validation necessary.
1558 If the device object is a PDO (partition object) then the request will
1559 simply be adjusted for Partition0 and issued to the lower device driver.
1561 IF the device object is an FDO (paritition 0 object), the number of bytes
1562 in the request are checked against the maximum byte counts that the adapter
1563 supports and requests are broken up into
1564 smaller sizes if necessary.
1568 DeviceObject - a pointer to the device object for this request
1577 NTSTATUS
ClassReadWrite(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
1579 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1580 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
1581 PIO_STACK_LOCATION currentIrpStack
= IoGetCurrentIrpStackLocation(Irp
);
1582 LARGE_INTEGER startingOffset
= currentIrpStack
->Parameters
.Read
.ByteOffset
;
1583 ULONG transferByteCount
= currentIrpStack
->Parameters
.Read
.Length
;
1588 * Grab the remove lock. If we can't acquire it, bail out.
1590 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
1592 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1593 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1594 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1595 status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1597 else if (TEST_FLAG(DeviceObject
->Flags
, DO_VERIFY_VOLUME
) &&
1598 (currentIrpStack
->MinorFunction
!= CLASSP_VOLUME_VERIFY_CHECKED
) &&
1599 !TEST_FLAG(currentIrpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
)){
1602 * DO_VERIFY_VOLUME is set for the device object,
1603 * but this request is not itself a verify request.
1604 * So fail this request.
1606 IoSetHardErrorOrVerifyDevice(Irp
, DeviceObject
);
1607 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
1608 Irp
->IoStatus
.Information
= 0;
1609 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1610 ClassCompleteRequest(DeviceObject
, Irp
, 0);
1611 status
= STATUS_VERIFY_REQUIRED
;
1616 * Since we've bypassed the verify-required tests we don't need to repeat
1617 * them with this IRP - in particular we don't want to worry about
1618 * hitting them at the partition 0 level if the request has come through
1619 * a non-zero partition.
1621 currentIrpStack
->MinorFunction
= CLASSP_VOLUME_VERIFY_CHECKED
;
1624 * Call the miniport driver's pre-pass filter to check if we
1625 * should continue with this transfer.
1627 ASSERT(commonExtension
->DevInfo
->ClassReadWriteVerification
);
1628 status
= commonExtension
->DevInfo
->ClassReadWriteVerification(DeviceObject
, Irp
);
1629 if (!NT_SUCCESS(status
)){
1630 ASSERT(Irp
->IoStatus
.Status
== status
);
1631 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1632 ClassCompleteRequest (DeviceObject
, Irp
, IO_NO_INCREMENT
);
1634 else if (status
== STATUS_PENDING
){
1636 * ClassReadWriteVerification queued this request.
1637 * So don't touch the irp anymore.
1642 if (transferByteCount
== 0) {
1644 * Several parts of the code turn 0 into 0xffffffff,
1645 * so don't process a zero-length request any further.
1647 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1648 Irp
->IoStatus
.Information
= 0;
1649 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1650 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1651 status
= STATUS_SUCCESS
;
1655 * If the driver has its own StartIo routine, call it.
1657 if (commonExtension
->DriverExtension
->InitData
.ClassStartIo
) {
1658 IoMarkIrpPending(Irp
);
1659 IoStartPacket(DeviceObject
, Irp
, NULL
, NULL
);
1660 status
= STATUS_PENDING
;
1664 * The driver does not have its own StartIo routine.
1665 * So process this request ourselves.
1669 * Add partition byte offset to make starting byte relative to
1670 * beginning of disk.
1672 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1673 commonExtension
->StartingOffset
.QuadPart
;
1675 if (commonExtension
->IsFdo
){
1678 * Add in any skew for the disk manager software.
1680 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1681 commonExtension
->PartitionZeroExtension
->DMByteSkew
;
1684 * Perform the actual transfer(s) on the hardware
1685 * to service this request.
1687 ServiceTransferRequest(DeviceObject
, Irp
);
1688 status
= STATUS_PENDING
;
1692 * This is a child PDO enumerated for our FDO by e.g. disk.sys
1693 * and owned by e.g. partmgr. Send it down to the next device
1694 * and the same irp will come back to us for the FDO.
1696 IoCopyCurrentIrpStackLocationToNext(Irp
);
1697 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1698 status
= IoCallDriver(lowerDeviceObject
, Irp
);
1709 /*++////////////////////////////////////////////////////////////////////////////
1711 ClassReadDriveCapacity()
1713 Routine Description:
1715 This routine sends a READ CAPACITY to the requested device, updates
1716 the geometry information in the device object and returns
1717 when it is complete. This routine is synchronous.
1719 This routine must be called with the remove lock held or some other
1720 assurance that the Fdo will not be removed while processing.
1724 DeviceObject - Supplies a pointer to the device object that represents
1725 the device whose capacity is to be read.
1735 ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo
)
1737 READ_CAPACITY_DATA readCapacityBuffer
= {0};
1741 driveCapMdl
= BuildDeviceInputMdl(&readCapacityBuffer
, sizeof(READ_CAPACITY_DATA
));
1744 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1746 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
1749 IRP pseudoIrp
= {0};
1752 * Our engine needs an "original irp" to write the status back to
1753 * and to count down packets (one in this case).
1754 * Just use a pretend irp for this.
1756 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1757 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1758 pseudoIrp
.IoStatus
.Information
= 0;
1759 pseudoIrp
.MdlAddress
= driveCapMdl
;
1762 * Set this up as a SYNCHRONOUS transfer, submit it,
1763 * and wait for the packet to complete. The result
1764 * status will be written to the original irp.
1766 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1767 SetupDriveCapacityTransferPacket( pkt
,
1768 &readCapacityBuffer
,
1769 sizeof(READ_CAPACITY_DATA
),
1772 SubmitTransferPacket(pkt
);
1773 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1775 status
= pseudoIrp
.IoStatus
.Status
;
1778 * If we got an UNDERRUN, retry exactly once.
1779 * (The transfer_packet engine didn't retry because the result
1780 * status was success).
1782 if (NT_SUCCESS(status
) &&
1783 (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
))){
1784 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG
)pseudoIrp
.IoStatus
.Information
, sizeof(READ_CAPACITY_DATA
)));
1786 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1788 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1789 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1790 pseudoIrp
.IoStatus
.Information
= 0;
1791 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1792 SetupDriveCapacityTransferPacket( pkt
,
1793 &readCapacityBuffer
,
1794 sizeof(READ_CAPACITY_DATA
),
1797 SubmitTransferPacket(pkt
);
1798 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1799 status
= pseudoIrp
.IoStatus
.Status
;
1800 if (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
)){
1801 status
= STATUS_DEVICE_BUSY
;
1805 status
= STATUS_INSUFFICIENT_RESOURCES
;
1810 if (NT_SUCCESS(status
)){
1812 * The request succeeded.
1813 * Read out and store the drive information.
1816 ULONG bytesPerSector
;
1821 * Read the bytesPerSector value,
1822 * which is big-endian in the returned buffer.
1823 * Default to the standard 512 bytes.
1825 tmp
= readCapacityBuffer
.BytesPerBlock
;
1826 ((PFOUR_BYTE
)&bytesPerSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1827 ((PFOUR_BYTE
)&bytesPerSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1828 ((PFOUR_BYTE
)&bytesPerSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1829 ((PFOUR_BYTE
)&bytesPerSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1830 if (bytesPerSector
== 0) {
1831 bytesPerSector
= 512;
1835 * Clear all but the highest set bit.
1836 * That will give us a bytesPerSector value that is a power of 2.
1838 while (bytesPerSector
& (bytesPerSector
-1)) {
1839 bytesPerSector
&= bytesPerSector
-1;
1842 fdoExt
->DiskGeometry
.BytesPerSector
= bytesPerSector
;
1845 // Copy last sector in reverse byte order.
1848 tmp
= readCapacityBuffer
.LogicalBlockAddress
;
1849 ((PFOUR_BYTE
)&lastSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1850 ((PFOUR_BYTE
)&lastSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1851 ((PFOUR_BYTE
)&lastSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1852 ((PFOUR_BYTE
)&lastSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1855 // Calculate sector to byte shift.
1858 WHICH_BIT(fdoExt
->DiskGeometry
.BytesPerSector
, fdoExt
->SectorShift
);
1860 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
1861 fdoExt
->DiskGeometry
.BytesPerSector
));
1863 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
1866 if (fdoExt
->DMActive
){
1867 DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
1869 lastSector
-= fdoExt
->DMSkew
;
1873 * Check to see if we have a geometry we should be using already.
1875 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1876 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1877 if (cylinderSize
== 0){
1878 DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
1879 "values from %#x/%#x to %#x/%#x\n",
1880 fdoExt
->DiskGeometry
.TracksPerCylinder
,
1881 fdoExt
->DiskGeometry
.SectorsPerTrack
,
1885 fdoExt
->DiskGeometry
.TracksPerCylinder
= 0xff;
1886 fdoExt
->DiskGeometry
.SectorsPerTrack
= 0x3f;
1889 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1890 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1894 // Calculate number of cylinders.
1897 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= (LONGLONG
)((lastSector
+ 1)/cylinderSize
);
1900 // if there are zero cylinders, then the device lied AND it's
1901 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
1902 // this can fit into a single LONGLONG, so create another usable
1903 // geometry, even if it's unusual looking. This allows small,
1904 // non-standard devices, such as Sony's Memory Stick, to show
1905 // up as having a partition.
1908 if (fdoExt
->DiskGeometry
.Cylinders
.QuadPart
== (LONGLONG
)0) {
1909 fdoExt
->DiskGeometry
.SectorsPerTrack
= 1;
1910 fdoExt
->DiskGeometry
.TracksPerCylinder
= 1;
1911 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= lastSector
;
1916 // Calculate media capacity in bytes.
1919 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
=
1920 ((LONGLONG
)(lastSector
+ 1)) << fdoExt
->SectorShift
;
1923 * Is this removable or fixed media
1925 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1926 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1929 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1934 * The request failed.
1938 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
1939 // what happens when the disk's sector size is bigger than
1940 // 512 bytes and we hit this code path? this is untested.
1942 // If the read capacity fails, set the geometry to reasonable parameter
1943 // so things don't fail at unexpected places. Zero the geometry
1944 // except for the bytes per sector and sector shift.
1948 * This request can sometimes fail legitimately
1949 * (e.g. when a SCSI device is attached but turned off)
1950 * so this is not necessarily a device/driver bug.
1952 DBGTRACE(ClassDebugWarning
, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo
, status
));
1955 * Write in a default disk geometry which we HOPE is right (??).
1958 RtlZeroMemory(&fdoExt
->DiskGeometry
, sizeof(DISK_GEOMETRY
));
1959 fdoExt
->DiskGeometry
.BytesPerSector
= 512;
1960 fdoExt
->SectorShift
= 9;
1961 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
= (LONGLONG
) 0;
1964 * Is this removable or fixed media
1966 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1967 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1970 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1976 status
= STATUS_INSUFFICIENT_RESOURCES
;
1979 FreeDeviceInputMdl(driveCapMdl
);
1982 status
= STATUS_INSUFFICIENT_RESOURCES
;
1989 /*++////////////////////////////////////////////////////////////////////////////
1991 ClassSendStartUnit()
1993 Routine Description:
1995 Send command to SCSI unit to start or power up.
1996 Because this command is issued asynchronounsly, that is, without
1997 waiting on it to complete, the IMMEDIATE flag is not set. This
1998 means that the CDB will not return until the drive has powered up.
1999 This should keep subsequent requests from being submitted to the
2000 device before it has completely spun up.
2002 This routine is called from the InterpretSense routine, when a
2003 request sense returns data indicating that a drive must be
2006 This routine may also be called from a class driver's error handler,
2007 or anytime a non-critical start device should be sent to the device.
2011 Fdo - The functional device object for the stopped device.
2022 IN PDEVICE_OBJECT Fdo
2025 PIO_STACK_LOCATION irpStack
;
2027 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2028 PSCSI_REQUEST_BLOCK srb
;
2029 PCOMPLETION_CONTEXT context
;
2033 // Allocate Srb from nonpaged pool.
2036 context
= ExAllocatePoolWithTag(NonPagedPool
,
2037 sizeof(COMPLETION_CONTEXT
),
2040 if(context
== NULL
) {
2043 // ISSUE-2000/02/03-peterwie
2044 // This code path was inheritted from the NT 4.0 class2.sys driver.
2045 // It needs to be changed to survive low-memory conditions.
2048 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2052 // Save the device object in the context for use by the completion
2056 context
->DeviceObject
= Fdo
;
2057 srb
= &context
->Srb
;
2063 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
2066 // Write length to SRB.
2069 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2071 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2074 // Set timeout value large enough for drive to spin up.
2077 srb
->TimeOutValue
= START_UNIT_TIMEOUT
;
2080 // Set the transfer length.
2083 srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
|
2084 SRB_FLAGS_DISABLE_AUTOSENSE
|
2085 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
;
2088 // Build the start unit CDB.
2092 cdb
= (PCDB
)srb
->Cdb
;
2094 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
2095 cdb
->START_STOP
.Start
= 1;
2096 cdb
->START_STOP
.Immediate
= 0;
2097 cdb
->START_STOP
.LogicalUnitNumber
= srb
->Lun
;
2100 // Build the asynchronous request to be sent to the port driver.
2101 // Since this routine is called from a DPC the IRP should always be
2105 irp
= IoAllocateIrp(Fdo
->StackSize
, FALSE
);
2110 // ISSUE-2000/02/03-peterwie
2111 // This code path was inheritted from the NT 4.0 class2.sys driver.
2112 // It needs to be changed to survive low-memory conditions.
2115 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2119 ClassAcquireRemoveLock(Fdo
, irp
);
2121 IoSetCompletionRoutine(irp
,
2122 (PIO_COMPLETION_ROUTINE
)ClassAsynchronousCompletion
,
2128 irpStack
= IoGetNextIrpStackLocation(irp
);
2129 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2130 srb
->OriginalRequest
= irp
;
2133 // Store the SRB address in next stack for port driver.
2136 irpStack
->Parameters
.Scsi
.Srb
= srb
;
2139 // Call the port driver with the IRP.
2142 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2146 } // end StartUnit()
2148 /*++////////////////////////////////////////////////////////////////////////////
2150 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
2152 Routine Description:
2154 This routine is called when an asynchronous I/O request
2155 which was issused by the class driver completes. Examples of such requests
2156 are release queue or START UNIT. This routine releases the queue if
2157 necessary. It then frees the context and the IRP.
2161 DeviceObject - The device object for the logical unit; however since this
2162 is the top stack location the value is NULL.
2164 Irp - Supplies a pointer to the Irp to be processed.
2166 Context - Supplies the context to be used to process this request.
2176 ClassAsynchronousCompletion(
2177 PDEVICE_OBJECT DeviceObject
,
2182 PCOMPLETION_CONTEXT context
= Context
;
2183 PSCSI_REQUEST_BLOCK srb
;
2185 if(DeviceObject
== NULL
) {
2187 DeviceObject
= context
->DeviceObject
;
2190 srb
= &context
->Srb
;
2193 // If this is an execute srb, then check the return status and make sure.
2194 // the queue is not frozen.
2197 if (srb
->Function
== SRB_FUNCTION_EXECUTE_SCSI
) {
2200 // Check for a frozen queue.
2203 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2206 // Unfreeze the queue getting the device object from the context.
2209 ClassReleaseQueue(context
->DeviceObject
);
2213 { // free port-allocated sense buffer if we can detect
2215 if (((PCOMMON_DEVICE_EXTENSION
)(DeviceObject
->DeviceExtension
))->IsFdo
) {
2217 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
2218 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2219 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2224 ASSERT(!TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
2231 // Free the context and the Irp.
2234 if (Irp
->MdlAddress
!= NULL
) {
2235 MmUnlockPages(Irp
->MdlAddress
);
2236 IoFreeMdl(Irp
->MdlAddress
);
2238 Irp
->MdlAddress
= NULL
;
2241 ClassReleaseRemoveLock(DeviceObject
, Irp
);
2243 ExFreePool(context
);
2247 // Indicate the I/O system should stop processing the Irp completion.
2250 return STATUS_MORE_PROCESSING_REQUIRED
;
2252 } // end ClassAsynchronousCompletion()
2256 VOID
ServiceTransferRequest(PDEVICE_OBJECT Fdo
, PIRP Irp
)
2258 PCOMMON_DEVICE_EXTENSION commonExt
= Fdo
->DeviceExtension
;
2259 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
2260 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
2261 PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc
= commonExt
->PartitionZeroExtension
->AdapterDescriptor
;
2262 PIO_STACK_LOCATION currentSp
= IoGetCurrentIrpStackLocation(Irp
);
2263 ULONG entireXferLen
= currentSp
->Parameters
.Read
.Length
;
2264 PUCHAR bufPtr
= MmGetMdlVirtualAddress(Irp
->MdlAddress
);
2265 LARGE_INTEGER targetLocation
= currentSp
->Parameters
.Read
.ByteOffset
;
2266 PTRANSFER_PACKET pkt
;
2267 SINGLE_LIST_ENTRY pktList
;
2268 PSINGLE_LIST_ENTRY slistEntry
;
2274 * Compute the number of hw xfers we'll have to do.
2275 * Calculate this without allowing for an overflow condition.
2277 ASSERT(fdoData
->HwMaxXferLen
>= PAGE_SIZE
);
2278 numPackets
= entireXferLen
/fdoData
->HwMaxXferLen
;
2279 if (entireXferLen
% fdoData
->HwMaxXferLen
){
2284 * First get all the TRANSFER_PACKETs that we'll need at once.
2285 * Use our 'simple' slist functions since we don't need interlocked.
2287 SimpleInitSlistHdr(&pktList
);
2288 for (i
= 0; i
< numPackets
; i
++){
2289 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
2291 SimplePushSlist(&pktList
, &pkt
->SlistEntry
);
2298 if (i
== numPackets
){
2300 * Initialize the original IRP's status to success.
2301 * If any of the packets fail, they will set it to an error status.
2302 * The IoStatus.Information field will be incremented to the
2303 * transfer length as the pieces complete.
2305 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2306 Irp
->IoStatus
.Information
= 0;
2309 * Store the number of transfer pieces inside the original IRP.
2310 * It will be used to count down the pieces as they complete.
2312 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(numPackets
);
2315 * We are proceeding with the transfer.
2316 * Mark the client IRP pending since it may complete on a different thread.
2318 IoMarkIrpPending(Irp
);
2321 * Transmit the pieces of the transfer.
2323 while (entireXferLen
> 0){
2324 ULONG thisPieceLen
= MIN(fdoData
->HwMaxXferLen
, entireXferLen
);
2327 * Set up a TRANSFER_PACKET for this piece and send it.
2329 slistEntry
= SimplePopSlist(&pktList
);
2331 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2332 SetupReadWriteTransferPacket( pkt
,
2337 SubmitTransferPacket(pkt
);
2339 entireXferLen
-= thisPieceLen
;
2340 bufPtr
+= thisPieceLen
;
2341 targetLocation
.QuadPart
+= thisPieceLen
;
2343 ASSERT(SimpleIsSlistEmpty(&pktList
));
2347 * We were unable to get all the TRANSFER_PACKETs we need,
2348 * but we did get at least one.
2349 * That means that we are in extreme low-memory stress.
2350 * We'll try doing this transfer using a single packet.
2351 * The port driver is certainly also in stress, so use one-page
2356 * Free all but one of the TRANSFER_PACKETs.
2359 slistEntry
= SimplePopSlist(&pktList
);
2361 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2362 EnqueueFreeTransferPacket(Fdo
, pkt
);
2366 * Get the single TRANSFER_PACKET that we'll be using.
2368 slistEntry
= SimplePopSlist(&pktList
);
2370 ASSERT(SimpleIsSlistEmpty(&pktList
));
2371 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2372 DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt
));
2375 * Set default status and the number of transfer packets (one)
2376 * inside the original irp.
2378 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2379 Irp
->IoStatus
.Information
= 0;
2380 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
2383 * Mark the client irp pending since it may complete on
2386 IoMarkIrpPending(Irp
);
2389 * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
2391 SetupReadWriteTransferPacket( pkt
,
2396 InitLowMemRetry(pkt
, bufPtr
, entireXferLen
, targetLocation
);
2397 StepLowMemRetry(pkt
);
2401 * We were unable to get ANY TRANSFER_PACKETs.
2402 * Defer this client irp until some TRANSFER_PACKETs free up.
2404 DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp
));
2405 IoMarkIrpPending(Irp
);
2406 EnqueueDeferredClientIrp(fdoData
, Irp
);
2412 /*++////////////////////////////////////////////////////////////////////////////
2416 Routine Description:
2418 This routine executes when the port driver has completed a request.
2419 It looks at the SRB status in the completing SRB and if not success
2420 it checks for valid request sense buffer information. If valid, the
2421 info is used to update status with more precise message of type of
2422 error. This routine deallocates the SRB.
2424 This routine should only be placed on the stack location for a class
2429 Fdo - Supplies the device object which represents the logical
2432 Irp - Supplies the Irp which has completed.
2434 Context - Supplies a pointer to the SRB.
2445 IN PDEVICE_OBJECT Fdo
,
2450 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2451 PSCSI_REQUEST_BLOCK srb
= Context
;
2452 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2453 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2456 BOOLEAN callStartNextPacket
;
2458 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2461 // Check SRB status for success of completing request.
2464 if (SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2465 ULONG retryInterval
;
2467 DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp
, srb
));
2470 // Release the queue if it is frozen.
2473 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2474 ClassReleaseQueue(Fdo
);
2477 retry
= ClassInterpretSenseInfo(
2480 irpStack
->MajorFunction
,
2481 irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
?
2482 irpStack
->Parameters
.DeviceIoControl
.IoControlCode
:
2485 ((ULONG
)(ULONG_PTR
)irpStack
->Parameters
.Others
.Argument4
),
2490 // If the status is verified required and the this request
2491 // should bypass verify required then retry the request.
2494 if (TEST_FLAG(irpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
) &&
2495 status
== STATUS_VERIFY_REQUIRED
) {
2497 status
= STATUS_IO_DEVICE_ERROR
;
2501 if (retry
&& (irpStack
->Parameters
.Others
.Argument4
--)) {
2507 DebugPrint((1, "Retry request %p\n", Irp
));
2509 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2510 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2513 RetryRequest(Fdo
, Irp
, srb
, FALSE
, retryInterval
);
2514 return STATUS_MORE_PROCESSING_REQUIRED
;
2520 // Set status for successful request
2522 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2523 ClasspPerfIncrementSuccessfulIo(fdoExtension
);
2524 status
= STATUS_SUCCESS
;
2525 } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
2529 // ensure we have returned some info, and it matches what the
2530 // original request wanted for PAGING operations only
2533 if ((NT_SUCCESS(status
)) && TEST_FLAG(Irp
->Flags
, IRP_PAGING_IO
)) {
2534 ASSERT(Irp
->IoStatus
.Information
!= 0);
2535 ASSERT(irpStack
->Parameters
.Read
.Length
== Irp
->IoStatus
.Information
);
2539 // remember if the caller wanted to skip calling IoStartNextPacket.
2540 // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
2541 // calls. this setting only affects device objects with StartIo routines.
2544 callStartNextPacket
= !TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_DONT_START_NEXT_PACKET
);
2545 if (irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
) {
2546 callStartNextPacket
= FALSE
;
2553 if(!TEST_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_PERSISTANT
)) {
2555 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2556 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2559 if (fdoExtension
->CommonExtension
.IsSrbLookasideListInitialized
){
2560 ClassFreeOrReuseSrb(fdoExtension
, srb
);
2563 DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
2569 DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
2570 "SRB_CLASS_FLAGS_PERSISTANT set\n", srb
));
2571 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2572 DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
2573 " because SRB_CLASS_FLAGS_PERSISTANT set\n",
2574 srb
->SenseInfoBuffer
));
2580 // Set status in completing IRP.
2583 Irp
->IoStatus
.Status
= status
;
2586 // Set the hard error if necessary.
2589 if (!NT_SUCCESS(status
) &&
2590 IoIsErrorUserInduced(status
) &&
2591 (Irp
->Tail
.Overlay
.Thread
!= NULL
)
2595 // Store DeviceObject for filesystem, and clear
2596 // in IoStatus.Information field.
2599 IoSetHardErrorOrVerifyDevice(Irp
, Fdo
);
2600 Irp
->IoStatus
.Information
= 0;
2604 // If pending has be returned for this irp then mark the current stack as
2608 if (Irp
->PendingReturned
) {
2609 IoMarkIrpPending(Irp
);
2612 if (fdoExtension
->CommonExtension
.DriverExtension
->InitData
.ClassStartIo
) {
2613 if (callStartNextPacket
) {
2615 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
2616 IoStartNextPacket(Fdo
, FALSE
);
2617 KeLowerIrql(oldIrql
);
2621 ClassReleaseRemoveLock(Fdo
, Irp
);
2625 } // end ClassIoComplete()
2628 /*++////////////////////////////////////////////////////////////////////////////
2630 ClassSendSrbSynchronous()
2632 Routine Description:
2634 This routine is called by SCSI device controls to complete an
2635 SRB and send it to the port driver synchronously (ie wait for
2636 completion). The CDB is already completed along with the SRB CDB
2637 size and request timeout value.
2641 Fdo - Supplies the functional device object which represents the target.
2643 Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
2645 BufferAddress - Supplies the address of the buffer.
2647 BufferLength - Supplies the length in bytes of the buffer.
2649 WriteToDevice - Indicates the data should be transfer to the device.
2653 NTSTATUS indicating the final results of the operation.
2655 If NT_SUCCESS(), then the amount of usable data is contained in the field
2656 Srb->DataTransferLength
2662 ClassSendSrbSynchronous(
2664 PSCSI_REQUEST_BLOCK Srb
,
2665 PVOID BufferAddress
,
2667 BOOLEAN WriteToDevice
2671 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2672 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2673 IO_STATUS_BLOCK ioStatus
;
2676 PIO_STACK_LOCATION irpStack
;
2678 PUCHAR senseInfoBuffer
;
2679 ULONG retryCount
= MAXIMUM_RETRIES
;
2684 // NOTE: This code is only pagable because we are not freezing
2685 // the queue. Allowing the queue to be frozen from a pagable
2686 // routine could leave the queue frozen as we try to page in
2687 // the code to unfreeze the queue. The result would be a nice
2688 // case of deadlock. Therefore, since we are unfreezing the
2689 // queue regardless of the result, just set the NO_FREEZE_QUEUE
2693 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2694 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2697 // Write length to SRB.
2700 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2703 // Set SCSI bus address.
2706 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2709 // Enable auto request sense.
2712 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
2715 // Sense buffer is in aligned nonpaged pool.
2718 senseInfoBuffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
2722 if (senseInfoBuffer
== NULL
) {
2724 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
2726 return(STATUS_INSUFFICIENT_RESOURCES
);
2729 Srb
->SenseInfoBuffer
= senseInfoBuffer
;
2730 Srb
->DataBuffer
= BufferAddress
;
2733 // Start retries here.
2739 // use fdoextension's flags by default.
2740 // do not move out of loop, as the flag may change due to errors
2741 // sending this command.
2744 Srb
->SrbFlags
= fdoExtension
->SrbFlags
;
2746 if(BufferAddress
!= NULL
) {
2748 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_OUT
);
2750 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
2755 // Initialize the QueueAction field.
2758 Srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
2761 // Disable synchronous transfer for these requests.
2762 // Disable freezing the queue, since all we do is unfreeze it anyways.
2765 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
2766 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
2769 // Set the event object to the unsignaled state.
2770 // It will be used to signal request completion.
2773 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
2776 // Build device I/O control request with METHOD_NEITHER data transfer.
2777 // We'll queue a completion routine to cleanup the MDL's and such ourself.
2780 irp
= IoAllocateIrp(
2781 (CCHAR
) (fdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
+ 1),
2785 ExFreePool(senseInfoBuffer
);
2786 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
2787 return(STATUS_INSUFFICIENT_RESOURCES
);
2791 // Get next stack location.
2794 irpStack
= IoGetNextIrpStackLocation(irp
);
2797 // Set up SRB for execute scsi request. Save SRB address in next stack
2798 // for the port driver.
2801 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2802 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
2804 IoSetCompletionRoutine(irp
,
2805 ClasspSendSynchronousCompletion
,
2811 irp
->UserIosb
= &ioStatus
;
2812 irp
->UserEvent
= &event
;
2816 // Build an MDL for the data buffer and stick it into the irp. The
2817 // completion routine will unlock the pages and free the MDL.
2820 irp
->MdlAddress
= IoAllocateMdl( BufferAddress
,
2825 if (irp
->MdlAddress
== NULL
) {
2826 ExFreePool(senseInfoBuffer
);
2827 Srb
->SenseInfoBuffer
= NULL
;
2829 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
2830 return STATUS_INSUFFICIENT_RESOURCES
;
2836 // the io manager unlocks these pages upon completion
2839 MmProbeAndLockPages( irp
->MdlAddress
,
2841 (WriteToDevice
? IoReadAccess
:
2844 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
2845 status
= _SEH2_GetExceptionCode();
2847 ExFreePool(senseInfoBuffer
);
2848 Srb
->SenseInfoBuffer
= NULL
;
2849 IoFreeMdl(irp
->MdlAddress
);
2852 DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
2853 "locking buffer\n", status
));
2859 // Set the transfer length.
2862 Srb
->DataTransferLength
= BufferLength
;
2868 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
2872 // Set up IRP Address.
2875 Srb
->OriginalRequest
= irp
;
2878 // Call the port driver with the request and wait for it to complete.
2881 status
= IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2883 if (status
== STATUS_PENDING
) {
2884 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
2885 status
= ioStatus
.Status
;
2889 // Check that request completed without error.
2892 if (SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2894 ULONG retryInterval
;
2896 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
)));
2899 // assert that the queue is not frozen
2902 ASSERT(!TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
2905 // Update status and determine if request should be retried.
2908 retry
= ClassInterpretSenseInfo(Fdo
,
2912 MAXIMUM_RETRIES
- retryCount
,
2919 if ((status
== STATUS_DEVICE_NOT_READY
&&
2920 ((PSENSE_DATA
) senseInfoBuffer
)->AdditionalSenseCode
==
2921 SCSI_ADSENSE_LUN_NOT_READY
) ||
2922 (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)) {
2924 LARGE_INTEGER delay
;
2927 // Delay for at least 2 seconds.
2930 if(retryInterval
< 2) {
2934 delay
.QuadPart
= (LONGLONG
)( - 10 * 1000 * (LONGLONG
)1000 * retryInterval
);
2937 // Stall for a while to let the device become ready
2940 KeDelayExecutionThread(KernelMode
, FALSE
, &delay
);
2945 // If retries are not exhausted then retry this operation.
2950 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2951 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2959 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2960 status
= STATUS_SUCCESS
;
2964 // required even though we allocated our own, since the port driver may
2965 // have allocated one also
2968 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2969 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2972 Srb
->SenseInfoBuffer
= NULL
;
2973 ExFreePool(senseInfoBuffer
);
2979 /*++////////////////////////////////////////////////////////////////////////////
2981 ClassInterpretSenseInfo()
2983 Routine Description:
2985 This routine interprets the data returned from the SCSI
2986 request sense. It determines the status to return in the
2987 IRP and whether this request can be retried.
2991 DeviceObject - Supplies the device object associated with this request.
2993 Srb - Supplies the scsi request block which failed.
2995 MajorFunctionCode - Supplies the function code to be used for logging.
2997 IoDeviceCode - Supplies the device code to be used for logging.
2999 Status - Returns the status for the request.
3003 BOOLEAN TRUE: Drivers should retry this request.
3004 FALSE: Drivers should not retry this request.
3010 ClassInterpretSenseInfo(
3011 IN PDEVICE_OBJECT Fdo
,
3012 IN PSCSI_REQUEST_BLOCK Srb
,
3013 IN UCHAR MajorFunctionCode
,
3014 IN ULONG IoDeviceCode
,
3015 IN ULONG RetryCount
,
3016 OUT NTSTATUS
*Status
,
3017 OUT OPTIONAL ULONG
*RetryInterval
3020 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
3021 PCOMMON_DEVICE_EXTENSION commonExtension
= Fdo
->DeviceExtension
;
3022 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
3024 PSENSE_DATA senseBuffer
= Srb
->SenseInfoBuffer
;
3026 BOOLEAN retry
= TRUE
;
3027 BOOLEAN logError
= FALSE
;
3028 BOOLEAN unhandledError
= FALSE
;
3029 BOOLEAN incrementErrorCount
= FALSE
;
3031 ULONG badSector
= 0;
3039 ULONG retryInterval
= 0;
3045 if(TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3048 // Log anything remotely incorrect about paging i/o
3053 logStatus
= IO_WARNING_PAGING_FAILURE
;
3057 // Check that request sense buffer is valid.
3060 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
3064 // must handle the SRB_STATUS_INTERNAL_ERROR case first,
3065 // as it has all the flags set.
3068 if (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_INTERNAL_ERROR
) {
3070 DebugPrint((ClassDebugSenseInfo
,
3071 "ClassInterpretSenseInfo: Internal Error code is %x\n",
3072 Srb
->InternalStatus
));
3075 *Status
= Srb
->InternalStatus
;
3077 } else if ((Srb
->SrbStatus
& SRB_STATUS_AUTOSENSE_VALID
) &&
3078 (Srb
->SenseInfoBufferLength
>=
3079 offsetof(SENSE_DATA
, CommandSpecificInformation
))) {
3082 // Zero the additional sense code and additional sense code qualifier
3083 // if they were not returned by the device.
3086 readSector
= senseBuffer
->AdditionalSenseLength
+
3087 offsetof(SENSE_DATA
, AdditionalSenseLength
);
3089 if (readSector
> Srb
->SenseInfoBufferLength
) {
3090 readSector
= Srb
->SenseInfoBufferLength
;
3093 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCode
)) {
3094 senseBuffer
->AdditionalSenseCode
= 0;
3097 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCodeQualifier
)) {
3098 senseBuffer
->AdditionalSenseCodeQualifier
= 0;
3101 DebugPrint((ClassDebugSenseInfo
,
3102 "ClassInterpretSenseInfo: Error code is %x\n",
3103 senseBuffer
->ErrorCode
));
3104 DebugPrint((ClassDebugSenseInfo
,
3105 "ClassInterpretSenseInfo: Sense key is %x\n",
3106 senseBuffer
->SenseKey
));
3107 DebugPrint((ClassDebugSenseInfo
,
3108 "ClassInterpretSenseInfo: Additional sense code is %x\n",
3109 senseBuffer
->AdditionalSenseCode
));
3110 DebugPrint((ClassDebugSenseInfo
,
3111 "ClassInterpretSenseInfo: Additional sense code qualifier "
3113 senseBuffer
->AdditionalSenseCodeQualifier
));
3116 switch (senseBuffer
->SenseKey
& 0xf) {
3118 case SCSI_SENSE_NOT_READY
: {
3120 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3121 "Device not ready\n"));
3122 *Status
= STATUS_DEVICE_NOT_READY
;
3124 switch (senseBuffer
->AdditionalSenseCode
) {
3126 case SCSI_ADSENSE_LUN_NOT_READY
: {
3128 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3129 "Lun not ready\n"));
3131 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3133 case SCSI_SENSEQ_OPERATION_IN_PROGRESS
: {
3134 DEVICE_EVENT_BECOMING_READY notReady
;
3136 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3137 "Operation In Progress\n"));
3138 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3140 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3141 notReady
.Version
= 1;
3142 notReady
.Reason
= 2;
3143 notReady
.Estimated100msToReady
= retryInterval
* 10;
3144 ClasspSendNotification(fdoExtension
,
3145 &GUID_IO_DEVICE_BECOMING_READY
,
3146 sizeof(DEVICE_EVENT_BECOMING_READY
),
3152 case SCSI_SENSEQ_BECOMING_READY
: {
3153 DEVICE_EVENT_BECOMING_READY notReady
;
3155 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3156 "In process of becoming ready\n"));
3157 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3159 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3160 notReady
.Version
= 1;
3161 notReady
.Reason
= 1;
3162 notReady
.Estimated100msToReady
= retryInterval
* 10;
3163 ClasspSendNotification(fdoExtension
,
3164 &GUID_IO_DEVICE_BECOMING_READY
,
3165 sizeof(DEVICE_EVENT_BECOMING_READY
),
3170 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS
: {
3171 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3172 "Long write in progress\n"));
3177 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED
: {
3178 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3179 "Manual intervention required\n"));
3180 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3185 case SCSI_SENSEQ_FORMAT_IN_PROGRESS
: {
3186 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3187 "Format in progress\n"));
3192 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE
: {
3194 if(!TEST_FLAG(fdoExtension
->ScanForSpecialFlags
,
3195 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
)) {
3197 DebugPrint((ClassDebugSenseInfo
,
3198 "ClassInterpretSenseInfo: "
3199 "not ready, cause unknown\n"));
3201 Many non-WHQL certified drives (mostly CD-RW) return
3202 this when they have no media instead of the obvious
3205 SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
3207 These drives should not pass WHQL certification due
3208 to this discrepency.
3217 // Treat this as init command required and fall through.
3222 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED
:
3224 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3225 "Initializing command required\n"));
3228 // This sense code/additional sense code
3229 // combination may indicate that the device
3230 // needs to be started. Send an start unit if this
3231 // is a disk device.
3234 if(TEST_FLAG(fdoExtension
->DeviceFlags
,
3235 DEV_SAFE_START_UNIT
) &&
3236 !TEST_FLAG(Srb
->SrbFlags
,
3237 SRB_CLASS_FLAGS_LOW_PRIORITY
)) {
3238 ClassSendStartUnit(Fdo
);
3244 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
3248 case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
: {
3249 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3250 "No Media in device.\n"));
3251 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3255 // signal MCN that there isn't any media in the device
3257 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3258 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3259 "No Media in a non-removable device %p\n",
3262 ClassSetMediaChangeState(fdoExtension
, MediaNotPresent
, FALSE
);
3266 } // end switch (senseBuffer->AdditionalSenseCode)
3269 } // end SCSI_SENSE_NOT_READY
3271 case SCSI_SENSE_DATA_PROTECT
: {
3272 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3273 "Media write protected\n"));
3274 *Status
= STATUS_MEDIA_WRITE_PROTECTED
;
3277 } // end SCSI_SENSE_DATA_PROTECT
3279 case SCSI_SENSE_MEDIUM_ERROR
: {
3280 DebugPrint((ClassDebugSenseInfo
,"ClassInterpretSenseInfo: "
3281 "Medium Error (bad block)\n"));
3282 *Status
= STATUS_DEVICE_DATA_ERROR
;
3287 logStatus
= IO_ERR_BAD_BLOCK
;
3290 // Check if this error is due to unknown format
3292 if (senseBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_INVALID_MEDIA
){
3294 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3296 case SCSI_SENSEQ_UNKNOWN_FORMAT
: {
3298 *Status
= STATUS_UNRECOGNIZED_MEDIA
;
3301 // Log error only if this is a paging request
3303 if(!TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3309 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED
: {
3311 *Status
= STATUS_CLEANER_CARTRIDGE_INSTALLED
;
3319 } // end switch AdditionalSenseCodeQualifier
3321 } // end SCSI_ADSENSE_INVALID_MEDIA
3325 } // end SCSI_SENSE_MEDIUM_ERROR
3327 case SCSI_SENSE_HARDWARE_ERROR
: {
3328 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3329 "Hardware error\n"));
3330 *Status
= STATUS_IO_DEVICE_ERROR
;
3333 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3335 } // end SCSI_SENSE_HARDWARE_ERROR
3337 case SCSI_SENSE_ILLEGAL_REQUEST
: {
3339 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3340 "Illegal SCSI request\n"));
3341 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3344 switch (senseBuffer
->AdditionalSenseCode
) {
3346 case SCSI_ADSENSE_ILLEGAL_COMMAND
: {
3347 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3348 "Illegal command\n"));
3352 case SCSI_ADSENSE_ILLEGAL_BLOCK
: {
3353 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3354 "Illegal block address\n"));
3355 *Status
= STATUS_NONEXISTENT_SECTOR
;
3359 case SCSI_ADSENSE_INVALID_LUN
: {
3360 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3362 *Status
= STATUS_NO_SUCH_DEVICE
;
3366 case SCSI_ADSENSE_MUSIC_AREA
: {
3367 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3372 case SCSI_ADSENSE_DATA_AREA
: {
3373 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3378 case SCSI_ADSENSE_VOLUME_OVERFLOW
: {
3379 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3380 "Volume overflow\n"));
3384 case SCSI_ADSENSE_COPY_PROTECTION_FAILURE
: {
3385 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3386 "Copy protection failure\n"));
3388 *Status
= STATUS_COPY_PROTECTION_FAILURE
;
3390 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3391 case SCSI_SENSEQ_AUTHENTICATION_FAILURE
:
3392 DebugPrint((ClassDebugSenseInfo
,
3393 "ClassInterpretSenseInfo: "
3394 "Authentication failure\n"));
3395 *Status
= STATUS_CSS_AUTHENTICATION_FAILURE
;
3397 case SCSI_SENSEQ_KEY_NOT_PRESENT
:
3398 DebugPrint((ClassDebugSenseInfo
,
3399 "ClassInterpretSenseInfo: "
3400 "Key not present\n"));
3401 *Status
= STATUS_CSS_KEY_NOT_PRESENT
;
3403 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED
:
3404 DebugPrint((ClassDebugSenseInfo
,
3405 "ClassInterpretSenseInfo: "
3406 "Key not established\n"));
3407 *Status
= STATUS_CSS_KEY_NOT_ESTABLISHED
;
3409 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION
:
3410 DebugPrint((ClassDebugSenseInfo
,
3411 "ClassInterpretSenseInfo: "
3412 "Read of scrambled sector w/o "
3413 "authentication\n"));
3414 *Status
= STATUS_CSS_SCRAMBLED_SECTOR
;
3416 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT
:
3417 DebugPrint((ClassDebugSenseInfo
,
3418 "ClassInterpretSenseInfo: "
3419 "Media region does not logical unit "
3421 *Status
= STATUS_CSS_REGION_MISMATCH
;
3423 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR
:
3424 DebugPrint((ClassDebugSenseInfo
,
3425 "ClassInterpretSenseInfo: "
3426 "Region set error -- region may "
3428 *Status
= STATUS_CSS_RESETS_EXHAUSTED
;
3430 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
3436 case SCSI_ADSENSE_INVALID_CDB
: {
3437 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3441 // Note: the retry interval is not typically used.
3442 // it is set here only because a ClassErrorHandler
3443 // cannot set the retryInterval, and the error may
3444 // require a few commands to be sent to clear whatever
3445 // caused this condition (i.e. disk clears the write
3446 // cache, requiring at least two commands)
3448 // hopefully, this shortcoming can be changed for
3456 } // end switch (senseBuffer->AdditionalSenseCode)
3459 } // end SCSI_SENSE_ILLEGAL_REQUEST
3461 case SCSI_SENSE_UNIT_ATTENTION
: {
3467 // A media change may have occured so increment the change
3468 // count for the physical device
3471 count
= InterlockedIncrement(&fdoExtension
->MediaChangeCount
);
3472 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3473 "Media change count for device %d incremented to %#lx\n",
3474 fdoExtension
->DeviceNumber
, count
));
3477 switch (senseBuffer
->AdditionalSenseCode
) {
3478 case SCSI_ADSENSE_MEDIUM_CHANGED
: {
3479 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3480 "Media changed\n"));
3482 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3483 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3484 "Media Changed on non-removable device %p\n",
3487 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
3491 case SCSI_ADSENSE_BUS_RESET
: {
3492 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3497 case SCSI_ADSENSE_OPERATOR_REQUEST
: {
3498 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3500 case SCSI_SENSEQ_MEDIUM_REMOVAL
: {
3501 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3502 "Ejection request received!\n"));
3503 ClassSendEjectionNotification(fdoExtension
);
3507 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE
: {
3508 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3509 "Operator selected write permit?! "
3510 "(unsupported!)\n"));
3514 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE
: {
3515 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3516 "Operator selected write protect?! "
3517 "(unsupported!)\n"));
3525 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3526 "Unit attention\n"));
3530 } // end switch (senseBuffer->AdditionalSenseCode)
3532 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
))
3535 // TODO : Is the media lockable?
3538 if ((ClassGetVpb(Fdo
) != NULL
) && (ClassGetVpb(Fdo
)->Flags
& VPB_MOUNTED
))
3541 // Set bit to indicate that media may have changed
3542 // and volume needs verification.
3545 SET_FLAG(Fdo
->Flags
, DO_VERIFY_VOLUME
);
3547 *Status
= STATUS_VERIFY_REQUIRED
;
3553 *Status
= STATUS_IO_DEVICE_ERROR
;
3558 } // end SCSI_SENSE_UNIT_ATTENTION
3560 case SCSI_SENSE_ABORTED_COMMAND
: {
3561 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3562 "Command aborted\n"));
3563 *Status
= STATUS_IO_DEVICE_ERROR
;
3566 } // end SCSI_SENSE_ABORTED_COMMAND
3568 case SCSI_SENSE_BLANK_CHECK
: {
3569 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3570 "Media blank check\n"));
3572 *Status
= STATUS_NO_DATA_DETECTED
;
3574 } // end SCSI_SENSE_BLANK_CHECK
3576 case SCSI_SENSE_RECOVERED_ERROR
: {
3578 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3579 "Recovered error\n"));
3580 *Status
= STATUS_SUCCESS
;
3585 switch(senseBuffer
->AdditionalSenseCode
) {
3586 case SCSI_ADSENSE_SEEK_ERROR
:
3587 case SCSI_ADSENSE_TRACK_ERROR
: {
3588 logStatus
= IO_ERR_SEEK_ERROR
;
3592 case SCSI_ADSENSE_REC_DATA_NOECC
:
3593 case SCSI_ADSENSE_REC_DATA_ECC
: {
3594 logStatus
= IO_RECOVERED_VIA_ECC
;
3598 case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED
: {
3599 UCHAR wmiEventData
[5];
3601 *((PULONG
)wmiEventData
) = sizeof(UCHAR
);
3602 wmiEventData
[sizeof(ULONG
)] = senseBuffer
->AdditionalSenseCodeQualifier
;
3605 // Don't log another eventlog if we have already logged once
3606 // NOTE: this should have been interlocked, but the structure
3607 // was publicly defined to use a BOOLEAN (char). Since
3608 // media only reports these errors once per X minutes,
3609 // the potential race condition is nearly non-existant.
3610 // the worst case is duplicate log entries, so ignore.
3613 if (fdoExtension
->FailurePredicted
== 0) {
3616 fdoExtension
->FailurePredicted
= TRUE
;
3617 fdoExtension
->FailureReason
= senseBuffer
->AdditionalSenseCodeQualifier
;
3618 logStatus
= IO_WRN_FAILURE_PREDICTED
;
3620 ClassNotifyFailurePredicted(fdoExtension
,
3621 (PUCHAR
)&wmiEventData
,
3622 sizeof(wmiEventData
),
3632 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3636 } // end switch(senseBuffer->AdditionalSenseCode)
3638 if (senseBuffer
->IncorrectLength
) {
3640 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3641 "Incorrect length detected.\n"));
3642 *Status
= STATUS_INVALID_BLOCK_LENGTH
;
3646 } // end SCSI_SENSE_RECOVERED_ERROR
3648 case SCSI_SENSE_NO_SENSE
: {
3651 // Check other indicators.
3654 if (senseBuffer
->IncorrectLength
) {
3656 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3657 "Incorrect length detected.\n"));
3658 *Status
= STATUS_INVALID_BLOCK_LENGTH
;
3663 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3664 "No specific sense key\n"));
3665 *Status
= STATUS_IO_DEVICE_ERROR
;
3670 } // end SCSI_SENSE_NO_SENSE
3673 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3674 "Unrecognized sense code\n"));
3675 *Status
= STATUS_IO_DEVICE_ERROR
;
3679 } // end switch (senseBuffer->SenseKey & 0xf)
3682 // Try to determine the bad sector from the inquiry data.
3685 if ((((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_READ
||
3686 ((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_VERIFY
||
3687 ((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_WRITE
)) {
3689 for (index
= 0; index
< 4; index
++) {
3690 badSector
= (badSector
<< 8) | senseBuffer
->Information
[index
];
3694 for (index
= 0; index
< 4; index
++) {
3695 readSector
= (readSector
<< 8) | Srb
->Cdb
[index
+2];
3698 index
= (((PCDB
)Srb
->Cdb
)->CDB10
.TransferBlocksMsb
<< 8) |
3699 ((PCDB
)Srb
->Cdb
)->CDB10
.TransferBlocksLsb
;
3702 // Make sure the bad sector is within the read sectors.
3705 if (!(badSector
>= readSector
&& badSector
< readSector
+ index
)) {
3706 badSector
= readSector
;
3713 // Request sense buffer not valid. No sense information
3714 // to pinpoint the error. Return general request fail.
3717 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3718 "Request sense info not valid. SrbStatus %2x\n",
3719 SRB_STATUS(Srb
->SrbStatus
)));
3722 switch (SRB_STATUS(Srb
->SrbStatus
)) {
3723 case SRB_STATUS_INVALID_LUN
:
3724 case SRB_STATUS_INVALID_TARGET_ID
:
3725 case SRB_STATUS_NO_DEVICE
:
3726 case SRB_STATUS_NO_HBA
:
3727 case SRB_STATUS_INVALID_PATH_ID
: {
3728 *Status
= STATUS_NO_SUCH_DEVICE
;
3733 case SRB_STATUS_COMMAND_TIMEOUT
:
3734 case SRB_STATUS_TIMEOUT
: {
3737 // Update the error count for the device.
3740 incrementErrorCount
= TRUE
;
3741 *Status
= STATUS_IO_TIMEOUT
;
3745 case SRB_STATUS_ABORTED
: {
3748 // Update the error count for the device.
3751 incrementErrorCount
= TRUE
;
3752 *Status
= STATUS_IO_TIMEOUT
;
3758 case SRB_STATUS_SELECTION_TIMEOUT
: {
3760 logStatus
= IO_ERR_NOT_READY
;
3762 *Status
= STATUS_DEVICE_NOT_CONNECTED
;
3767 case SRB_STATUS_DATA_OVERRUN
: {
3768 *Status
= STATUS_DATA_OVERRUN
;
3773 case SRB_STATUS_PHASE_SEQUENCE_FAILURE
: {
3776 // Update the error count for the device.
3779 incrementErrorCount
= TRUE
;
3780 *Status
= STATUS_IO_DEVICE_ERROR
;
3783 // If there was phase sequence error then limit the number of
3787 if (RetryCount
> 1 ) {
3794 case SRB_STATUS_REQUEST_FLUSHED
: {
3797 // If the status needs verification bit is set. Then set
3798 // the status to need verification and no retry; otherwise,
3799 // just retry the request.
3802 if (TEST_FLAG(Fdo
->Flags
, DO_VERIFY_VOLUME
)) {
3804 *Status
= STATUS_VERIFY_REQUIRED
;
3808 *Status
= STATUS_IO_DEVICE_ERROR
;
3814 case SRB_STATUS_INVALID_REQUEST
: {
3815 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3820 case SRB_STATUS_UNEXPECTED_BUS_FREE
:
3821 case SRB_STATUS_PARITY_ERROR
:
3824 // Update the error count for the device
3825 // and fall through to below
3828 incrementErrorCount
= TRUE
;
3830 case SRB_STATUS_BUS_RESET
: {
3831 *Status
= STATUS_IO_DEVICE_ERROR
;
3835 case SRB_STATUS_ERROR
: {
3837 *Status
= STATUS_IO_DEVICE_ERROR
;
3838 if (Srb
->ScsiStatus
== 0) {
3841 // This is some strange return code. Update the error
3842 // count for the device.
3845 incrementErrorCount
= TRUE
;
3847 } if (Srb
->ScsiStatus
== SCSISTAT_BUSY
) {
3849 *Status
= STATUS_DEVICE_NOT_READY
;
3851 } if (Srb
->ScsiStatus
== SCSISTAT_RESERVATION_CONFLICT
) {
3853 *Status
= STATUS_DEVICE_BUSY
;
3864 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3866 *Status
= STATUS_IO_DEVICE_ERROR
;
3867 unhandledError
= TRUE
;
3874 // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
3875 // we know from a previous poll when the device will be ready (ETA)
3876 // we should delay the retry more appropriately than just guessing.
3879 if (fdoExtension->MediaChangeDetectionInfo &&
3880 fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
3881 TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
3882 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
3884 // check if Gesn.ReadyTime if greater than current tick count
3885 // if so, delay that long (from 1 to 30 seconds max?)
3886 // else, leave the guess of time alone.
3892 if (incrementErrorCount
) {
3895 // if any error count occurred, delay the retry of this io by
3896 // at least one second, if caller supports it.
3899 if (retryInterval
== 0) {
3902 ClasspPerfIncrementErrorCount(fdoExtension
);
3906 // If there is a class specific error handler call it.
3909 if (fdoExtension
->CommonExtension
.DevInfo
->ClassError
!= NULL
) {
3911 fdoExtension
->CommonExtension
.DevInfo
->ClassError(Fdo
,
3918 // If the caller wants to know the suggested retry interval tell them.
3921 if(ARGUMENT_PRESENT(RetryInterval
)) {
3922 *RetryInterval
= retryInterval
;
3928 * Always log the error in our internal log.
3929 * If logError is set, also log the error in the system log.
3933 ULONG senseBufferSize
= 0;
3934 IO_ERROR_LOG_PACKET staticErrLogEntry
= {0};
3935 CLASS_ERROR_LOG_DATA staticErrLogData
= {0};
3938 // Calculate the total size of the error log entry.
3939 // add to totalSize in the order that they are used.
3940 // the advantage to calculating all the sizes here is
3941 // that we don't have to do a bunch of extraneous checks
3942 // later on in this code path.
3944 totalSize
= sizeof(IO_ERROR_LOG_PACKET
) // required
3945 - sizeof(ULONG
) // struct includes one ULONG
3946 + sizeof(CLASS_ERROR_LOG_DATA
);// struct for ease
3949 // also save any available extra sense data, up to the maximum errlog
3950 // packet size . WMI should be used for real-time analysis.
3951 // the event log should only be used for post-mortem debugging.
3953 if (TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_AUTOSENSE_VALID
)) {
3954 ULONG validSenseBytes
;
3958 // make sure we can at least access the AdditionalSenseLength field
3960 validSense
= RTL_CONTAINS_FIELD(senseBuffer
,
3961 Srb
->SenseInfoBufferLength
,
3962 AdditionalSenseLength
);
3966 // if extra info exists, copy the maximum amount of available
3967 // sense data that is safe into the the errlog.
3969 validSenseBytes
= senseBuffer
->AdditionalSenseLength
3970 + offsetof(SENSE_DATA
, AdditionalSenseLength
);
3973 // this is invalid because it causes overflow!
3974 // whoever sent this type of request would cause
3977 ASSERT(validSenseBytes
< MAX_ADDITIONAL_SENSE_BYTES
);
3980 // set to save the most sense buffer possible
3982 senseBufferSize
= max(validSenseBytes
, sizeof(SENSE_DATA
));
3983 senseBufferSize
= min(senseBufferSize
, Srb
->SenseInfoBufferLength
);
3986 // it's smaller than required to read the total number of
3987 // valid bytes, so just use the SenseInfoBufferLength field.
3989 senseBufferSize
= Srb
->SenseInfoBufferLength
;
3993 * Bump totalSize by the number of extra senseBuffer bytes
3994 * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
3995 * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
3997 if (senseBufferSize
> sizeof(SENSE_DATA
)){
3998 totalSize
+= senseBufferSize
-sizeof(SENSE_DATA
);
3999 if (totalSize
> ERROR_LOG_MAXIMUM_SIZE
){
4000 senseBufferSize
-= totalSize
-ERROR_LOG_MAXIMUM_SIZE
;
4001 totalSize
= ERROR_LOG_MAXIMUM_SIZE
;
4007 // If we've used up all of our retry attempts, set the final status to
4008 // reflect the appropriate result.
4010 if (retry
&& RetryCount
< MAXIMUM_RETRIES
) {
4011 staticErrLogEntry
.FinalStatus
= STATUS_SUCCESS
;
4012 staticErrLogData
.ErrorRetried
= TRUE
;
4014 staticErrLogEntry
.FinalStatus
= *Status
;
4016 if (TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
4017 staticErrLogData
.ErrorPaging
= TRUE
;
4019 if (unhandledError
) {
4020 staticErrLogData
.ErrorUnhandled
= TRUE
;
4024 // Calculate the device offset if there is a geometry.
4026 staticErrLogEntry
.DeviceOffset
.QuadPart
= (LONGLONG
)badSector
;
4027 staticErrLogEntry
.DeviceOffset
.QuadPart
*= (LONGLONG
)fdoExtension
->DiskGeometry
.BytesPerSector
;
4028 if (logStatus
== -1){
4029 staticErrLogEntry
.ErrorCode
= STATUS_IO_DEVICE_ERROR
;
4031 staticErrLogEntry
.ErrorCode
= logStatus
;
4035 * The dump data follows the IO_ERROR_LOG_PACKET,
4036 * with the first ULONG of dump data inside the packet.
4038 staticErrLogEntry
.DumpDataSize
= (USHORT
)totalSize
- sizeof(IO_ERROR_LOG_PACKET
) + sizeof(ULONG
);
4040 staticErrLogEntry
.SequenceNumber
= 0;
4041 staticErrLogEntry
.MajorFunctionCode
= MajorFunctionCode
;
4042 staticErrLogEntry
.IoControlCode
= IoDeviceCode
;
4043 staticErrLogEntry
.RetryCount
= (UCHAR
) RetryCount
;
4044 staticErrLogEntry
.UniqueErrorValue
= uniqueId
;
4046 KeQueryTickCount(&staticErrLogData
.TickCount
);
4047 staticErrLogData
.PortNumber
= (ULONG
)-1;
4050 * Save the entire contents of the SRB.
4052 staticErrLogData
.Srb
= *Srb
;
4055 * For our private log, save just the default length of the SENSE_DATA.
4057 if (senseBufferSize
!= 0){
4058 RtlCopyMemory(&staticErrLogData
.SenseData
, senseBuffer
, min(senseBufferSize
, sizeof(SENSE_DATA
)));
4062 * Save the error log in our context.
4063 * We only save the default sense buffer length.
4065 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
4066 fdoData
->ErrorLogs
[fdoData
->ErrorLogNextIndex
] = staticErrLogData
;
4067 fdoData
->ErrorLogNextIndex
++;
4068 fdoData
->ErrorLogNextIndex
%= NUM_ERROR_LOG_ENTRIES
;
4069 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
4072 * If logError is set, also save this log in the system's error log.
4073 * But make sure we don't log TUR failures over and over
4074 * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
4076 if ((((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_TEST_UNIT_READY
) && logError
){
4077 if (fdoData
->LoggedTURFailureSinceLastIO
){
4081 fdoData
->LoggedTURFailureSinceLastIO
= TRUE
;
4085 PIO_ERROR_LOG_PACKET errorLogEntry
;
4086 PCLASS_ERROR_LOG_DATA errlogData
;
4088 errorLogEntry
= (PIO_ERROR_LOG_PACKET
)IoAllocateErrorLogEntry(Fdo
, (UCHAR
)totalSize
);
4090 errlogData
= (PCLASS_ERROR_LOG_DATA
)errorLogEntry
->DumpData
;
4092 *errorLogEntry
= staticErrLogEntry
;
4093 *errlogData
= staticErrLogData
;
4096 * For the system log, copy as much of the sense buffer as possible.
4098 if (senseBufferSize
!= 0) {
4099 RtlCopyMemory(&errlogData
->SenseData
, senseBuffer
, senseBufferSize
);
4103 * Write the error log packet to the system error logging thread.
4105 IoWriteErrorLogEntry(errorLogEntry
);
4112 } // end ClassInterpretSenseInfo()
4116 /*++////////////////////////////////////////////////////////////////////////////
4120 Routine Description:
4122 This routine sends a mode sense command to a target ID and returns
4123 when it is complete.
4127 Fdo - Supplies the functional device object associated with this request.
4129 ModeSenseBuffer - Supplies a buffer to store the sense data.
4131 Length - Supplies the length in bytes of the mode sense buffer.
4133 PageMode - Supplies the page or pages of mode sense data to be retrived.
4137 Length of the transferred data is returned.
4143 ClassModeSense( IN PDEVICE_OBJECT Fdo
,
4144 IN PCHAR ModeSenseBuffer
,
4148 ULONG lengthTransferred
= 0;
4149 PMDL senseBufferMdl
;
4153 senseBufferMdl
= BuildDeviceInputMdl(ModeSenseBuffer
, Length
);
4154 if (senseBufferMdl
){
4156 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
4160 IRP pseudoIrp
= {0};
4163 * Store the number of packets servicing the irp (one)
4164 * inside the original IRP. It will be used to counted down
4165 * to zero when the packet completes.
4166 * Initialize the original IRP's status to success.
4167 * If the packet fails, we will set it to the error status.
4169 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
4170 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
4171 pseudoIrp
.IoStatus
.Information
= 0;
4172 pseudoIrp
.MdlAddress
= senseBufferMdl
;
4175 * Set this up as a SYNCHRONOUS transfer, submit it,
4176 * and wait for the packet to complete. The result
4177 * status will be written to the original irp.
4179 ASSERT(Length
<= 0x0ff);
4180 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
4181 SetupModeSenseTransferPacket(pkt
, &event
, ModeSenseBuffer
, (UCHAR
)Length
, PageMode
, &pseudoIrp
);
4182 SubmitTransferPacket(pkt
);
4183 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
4185 if (NT_SUCCESS(pseudoIrp
.IoStatus
.Status
)){
4186 lengthTransferred
= (ULONG
)pseudoIrp
.IoStatus
.Information
;
4190 * This request can sometimes fail legitimately
4191 * (e.g. when a SCSI device is attached but turned off)
4192 * so this is not necessarily a device/driver bug.
4194 DBGTRACE(ClassDebugWarning
, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo
, pseudoIrp
.IoStatus
.Status
));
4198 FreeDeviceInputMdl(senseBufferMdl
);
4201 return lengthTransferred
;
4205 /*++////////////////////////////////////////////////////////////////////////////
4209 Routine Description:
4211 This routine scans through the mode sense data and finds the requested
4212 mode sense page code.
4215 ModeSenseBuffer - Supplies a pointer to the mode sense data.
4217 Length - Indicates the length of valid data.
4219 PageMode - Supplies the page mode to be searched for.
4221 Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
4225 A pointer to the the requested mode page. If the mode page was not found
4226 then NULL is return.
4233 IN PCHAR ModeSenseBuffer
,
4240 ULONG parameterHeaderLength
;
4241 PVOID result
= NULL
;
4243 limit
= ModeSenseBuffer
+ Length
;
4244 parameterHeaderLength
= (Use6Byte
) ? sizeof(MODE_PARAMETER_HEADER
) : sizeof(MODE_PARAMETER_HEADER10
);
4246 if (Length
>= parameterHeaderLength
) {
4248 PMODE_PARAMETER_HEADER10 modeParam10
;
4249 ULONG blockDescriptorLength
;
4252 * Skip the mode select header and block descriptors.
4255 blockDescriptorLength
= ((PMODE_PARAMETER_HEADER
) ModeSenseBuffer
)->BlockDescriptorLength
;
4258 modeParam10
= (PMODE_PARAMETER_HEADER10
) ModeSenseBuffer
;
4259 blockDescriptorLength
= modeParam10
->BlockDescriptorLength
[1];
4262 ModeSenseBuffer
+= parameterHeaderLength
+ blockDescriptorLength
;
4265 // ModeSenseBuffer now points at pages. Walk the pages looking for the
4266 // requested page until the limit is reached.
4269 while (ModeSenseBuffer
+
4270 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
) < limit
) {
4272 if (((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageCode
== PageMode
) {
4275 * found the mode page. make sure it's safe to touch it all
4276 * before returning the pointer to caller
4279 if (ModeSenseBuffer
+ ((PMODE_DISCONNECT_PAGE
)ModeSenseBuffer
)->PageLength
> limit
) {
4281 * Return NULL since the page is not safe to access in full
4286 result
= ModeSenseBuffer
;
4292 // Advance to the next page which is 4-byte-aligned offset after this page.
4295 ((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageLength
+
4296 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
);
4302 } // end ClassFindModePage()
4304 /*++////////////////////////////////////////////////////////////////////////////
4306 ClassSendSrbAsynchronous()
4308 Routine Description:
4310 This routine takes a partially built Srb and an Irp and sends it down to
4313 This routine must be called with the remove lock held for the specified
4318 Fdo - Supplies the functional device object for the orginal request.
4320 Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
4321 CDB and the SRB timeout value must be filled in. The SRB must not be
4322 allocated from zone.
4324 Irp - Supplies the requesting Irp.
4326 BufferAddress - Supplies a pointer to the buffer to be transfered.
4328 BufferLength - Supplies the length of data transfer.
4330 WriteToDevice - Indicates the data transfer will be from system memory to
4335 Returns STATUS_PENDING if the request is dispatched (since the
4336 completion routine may change the irp's status value we cannot simply
4337 return the value of the dispatch)
4339 or returns a status value to indicate why it failed.
4345 ClassSendSrbAsynchronous(
4347 PSCSI_REQUEST_BLOCK Srb
,
4349 PVOID BufferAddress
,
4351 BOOLEAN WriteToDevice
4355 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
4356 PIO_STACK_LOCATION irpStack
;
4361 // Write length to SRB.
4364 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
4367 // Set SCSI bus address.
4370 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
4373 // This is a violation of the SCSI spec but it is required for
4377 // Srb->Cdb[1] |= deviceExtension->Lun << 5;
4380 // Indicate auto request sense by specifying buffer and size.
4383 Srb
->SenseInfoBuffer
= fdoExtension
->SenseData
;
4384 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
4385 Srb
->DataBuffer
= BufferAddress
;
4388 // Save the class driver specific flags away.
4391 savedFlags
= Srb
->SrbFlags
& SRB_FLAGS_CLASS_DRIVER_RESERVED
;
4394 // Allow the caller to specify that they do not wish
4395 // IoStartNextPacket() to be called in the completion routine.
4398 SET_FLAG(savedFlags
, (Srb
->SrbFlags
& SRB_FLAGS_DONT_START_NEXT_PACKET
));
4400 if (BufferAddress
!= NULL
) {
4403 // Build Mdl if necessary.
4406 if (Irp
->MdlAddress
== NULL
) {
4408 if (IoAllocateMdl(BufferAddress
,
4414 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4417 // ClassIoComplete() would have free'd the srb
4420 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
4421 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
4423 ClassFreeOrReuseSrb(fdoExtension
, Srb
);
4424 ClassReleaseRemoveLock(Fdo
, Irp
);
4425 ClassCompleteRequest(Fdo
, Irp
, IO_NO_INCREMENT
);
4427 return STATUS_INSUFFICIENT_RESOURCES
;
4430 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
4435 // Make sure the buffer requested matches the MDL.
4438 ASSERT(BufferAddress
== MmGetMdlVirtualAddress(Irp
->MdlAddress
));
4445 Srb
->SrbFlags
= WriteToDevice
? SRB_FLAGS_DATA_OUT
: SRB_FLAGS_DATA_IN
;
4453 Srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
;
4457 // Restore saved flags.
4460 SET_FLAG(Srb
->SrbFlags
, savedFlags
);
4463 // Disable synchronous transfer for these requests.
4466 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
4469 // Set the transfer length.
4472 Srb
->DataTransferLength
= BufferLength
;
4478 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
4483 // Save a few parameters in the current stack location.
4486 irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4489 // Save retry count in current Irp stack.
4492 irpStack
->Parameters
.Others
.Argument4
= (PVOID
)MAXIMUM_RETRIES
;
4495 // Set up IoCompletion routine address.
4498 IoSetCompletionRoutine(Irp
, ClassIoComplete
, Srb
, TRUE
, TRUE
, TRUE
);
4501 // Get next stack location and
4502 // set major function code.
4505 irpStack
= IoGetNextIrpStackLocation(Irp
);
4507 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
4510 // Save SRB address in next stack for port driver.
4513 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
4516 // Set up Irp Address.
4519 Srb
->OriginalRequest
= Irp
;
4522 // Call the port driver to process the request.
4525 IoMarkIrpPending(Irp
);
4527 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, Irp
);
4529 return STATUS_PENDING
;
4531 } // end ClassSendSrbAsynchronous()
4533 /*++////////////////////////////////////////////////////////////////////////////
4535 ClassDeviceControlDispatch()
4537 Routine Description:
4539 The routine is the common class driver device control dispatch entry point.
4540 This routine is invokes the device-specific drivers DeviceControl routine,
4541 (which may call the Class driver's common DeviceControl routine).
4545 DeviceObject - Supplies a pointer to the device object for this request.
4547 Irp - Supplies the Irp making the request.
4551 Returns the status returned from the device-specific driver.
4555 ClassDeviceControlDispatch(
4556 PDEVICE_OBJECT DeviceObject
,
4561 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
4564 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
4568 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4570 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
4571 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4572 return STATUS_DEVICE_DOES_NOT_EXIST
;
4576 // Call the class specific driver DeviceControl routine.
4577 // If it doesn't handle it, it will call back into ClassDeviceControl.
4580 ASSERT(commonExtension
->DevInfo
->ClassDeviceControl
);
4582 return commonExtension
->DevInfo
->ClassDeviceControl(DeviceObject
,Irp
);
4583 } // end ClassDeviceControlDispatch()
4586 /*++////////////////////////////////////////////////////////////////////////////
4588 ClassDeviceControl()
4590 Routine Description:
4592 The routine is the common class driver device control dispatch function.
4593 This routine is called by a class driver when it get an unrecognized
4594 device control request. This routine will perform the correct action for
4595 common requests such as lock media. If the device request is unknown it
4596 passed down to the next level.
4598 This routine must be called with the remove lock held for the specified
4603 DeviceObject - Supplies a pointer to the device object for this request.
4605 Irp - Supplies the Irp making the request.
4609 Returns back a STATUS_PENDING or a completion status.
4616 PDEVICE_OBJECT DeviceObject
,
4620 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
4622 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4623 PIO_STACK_LOCATION nextStack
= NULL
;
4625 ULONG controlCode
= irpStack
->Parameters
.DeviceIoControl
.IoControlCode
;
4627 PSCSI_REQUEST_BLOCK srb
= NULL
;
4631 ULONG modifiedIoControlCode
;
4634 // If this is a pass through I/O control, set the minor function code
4635 // and device address and pass it to the port driver.
4638 if ((controlCode
== IOCTL_SCSI_PASS_THROUGH
) ||
4639 (controlCode
== IOCTL_SCSI_PASS_THROUGH_DIRECT
)) {
4641 PSCSI_PASS_THROUGH scsiPass
;
4644 // Validiate the user buffer.
4646 #if defined (_WIN64)
4648 if (IoIs32bitProcess(Irp
)) {
4650 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(SCSI_PASS_THROUGH32
)){
4652 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
4654 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4655 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4657 status
= STATUS_INVALID_PARAMETER
;
4658 goto SetStatusAndReturn
;
4664 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
4665 sizeof(SCSI_PASS_THROUGH
)) {
4667 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
4669 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4670 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4672 status
= STATUS_INVALID_PARAMETER
;
4673 goto SetStatusAndReturn
;
4677 IoCopyCurrentIrpStackLocationToNext(Irp
);
4679 nextStack
= IoGetNextIrpStackLocation(Irp
);
4680 nextStack
->MinorFunction
= 1;
4682 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4684 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4685 goto SetStatusAndReturn
;
4688 Irp
->IoStatus
.Information
= 0;
4690 switch (controlCode
) {
4692 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
: {
4694 PMOUNTDEV_UNIQUE_ID uniqueId
;
4696 if (!commonExtension
->MountedDeviceInterfaceName
.Buffer
) {
4697 status
= STATUS_INVALID_PARAMETER
;
4701 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4702 sizeof(MOUNTDEV_UNIQUE_ID
)) {
4704 status
= STATUS_BUFFER_TOO_SMALL
;
4705 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4709 uniqueId
= Irp
->AssociatedIrp
.SystemBuffer
;
4710 uniqueId
->UniqueIdLength
=
4711 commonExtension
->MountedDeviceInterfaceName
.Length
;
4713 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4714 sizeof(USHORT
) + uniqueId
->UniqueIdLength
) {
4716 status
= STATUS_BUFFER_OVERFLOW
;
4717 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4721 RtlCopyMemory(uniqueId
->UniqueId
,
4722 commonExtension
->MountedDeviceInterfaceName
.Buffer
,
4723 uniqueId
->UniqueIdLength
);
4725 status
= STATUS_SUCCESS
;
4726 Irp
->IoStatus
.Information
= sizeof(USHORT
) +
4727 uniqueId
->UniqueIdLength
;
4731 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
: {
4733 PMOUNTDEV_NAME name
;
4735 ASSERT(commonExtension
->DeviceName
.Buffer
);
4737 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4738 sizeof(MOUNTDEV_NAME
)) {
4740 status
= STATUS_BUFFER_TOO_SMALL
;
4741 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4745 name
= Irp
->AssociatedIrp
.SystemBuffer
;
4746 name
->NameLength
= commonExtension
->DeviceName
.Length
;
4748 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4749 sizeof(USHORT
) + name
->NameLength
) {
4751 status
= STATUS_BUFFER_OVERFLOW
;
4752 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4756 RtlCopyMemory(name
->Name
, commonExtension
->DeviceName
.Buffer
,
4759 status
= STATUS_SUCCESS
;
4760 Irp
->IoStatus
.Information
= sizeof(USHORT
) + name
->NameLength
;
4764 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
: {
4766 PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName
;
4767 WCHAR driveLetterNameBuffer
[10];
4768 RTL_QUERY_REGISTRY_TABLE queryTable
[2];
4770 UNICODE_STRING driveLetterName
;
4772 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4773 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
)) {
4775 status
= STATUS_BUFFER_TOO_SMALL
;
4776 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
4780 valueName
= ExAllocatePoolWithTag(
4782 commonExtension
->DeviceName
.Length
+ sizeof(WCHAR
),
4786 status
= STATUS_INSUFFICIENT_RESOURCES
;
4790 RtlCopyMemory(valueName
, commonExtension
->DeviceName
.Buffer
,
4791 commonExtension
->DeviceName
.Length
);
4792 valueName
[commonExtension
->DeviceName
.Length
/sizeof(WCHAR
)] = 0;
4794 driveLetterName
.Buffer
= driveLetterNameBuffer
;
4795 driveLetterName
.MaximumLength
= 20;
4796 driveLetterName
.Length
= 0;
4798 RtlZeroMemory(queryTable
, 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
4799 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_REQUIRED
|
4800 RTL_QUERY_REGISTRY_DIRECT
;
4801 queryTable
[0].Name
= valueName
;
4802 queryTable
[0].EntryContext
= &driveLetterName
;
4804 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
4805 L
"\\Registry\\Machine\\System\\DISK",
4806 queryTable
, NULL
, NULL
);
4808 if (!NT_SUCCESS(status
)) {
4809 ExFreePool(valueName
);
4813 if (driveLetterName
.Length
== 4 &&
4814 driveLetterName
.Buffer
[0] == '%' &&
4815 driveLetterName
.Buffer
[1] == ':') {
4817 driveLetterName
.Buffer
[0] = 0xFF;
4819 } else if (driveLetterName
.Length
!= 4 ||
4820 driveLetterName
.Buffer
[0] < FirstDriveLetter
||
4821 driveLetterName
.Buffer
[0] > LastDriveLetter
||
4822 driveLetterName
.Buffer
[1] != ':') {
4824 status
= STATUS_NOT_FOUND
;
4825 ExFreePool(valueName
);
4829 suggestedName
= Irp
->AssociatedIrp
.SystemBuffer
;
4830 suggestedName
->UseOnlyIfThereAreNoOtherLinks
= TRUE
;
4831 suggestedName
->NameLength
= 28;
4833 Irp
->IoStatus
.Information
=
4834 FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME
, Name
) + 28;
4836 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4837 Irp
->IoStatus
.Information
) {
4839 Irp
->IoStatus
.Information
=
4840 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
4841 status
= STATUS_BUFFER_OVERFLOW
;
4842 ExFreePool(valueName
);
4846 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
4847 L
"\\Registry\\Machine\\System\\DISK",
4850 ExFreePool(valueName
);
4852 RtlCopyMemory(suggestedName
->Name
, L
"\\DosDevices\\", 24);
4853 suggestedName
->Name
[12] = driveLetterName
.Buffer
[0];
4854 suggestedName
->Name
[13] = ':';
4857 // NT_SUCCESS(status) based on RtlQueryRegistryValues
4859 status
= STATUS_SUCCESS
;
4865 status
= STATUS_PENDING
;
4869 if (status
!= STATUS_PENDING
) {
4870 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4871 Irp
->IoStatus
.Status
= status
;
4872 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4876 if (commonExtension
->IsFdo
){
4878 PULONG_PTR function
;
4880 srb
= ExAllocatePoolWithTag(NonPagedPool
,
4881 sizeof(SCSI_REQUEST_BLOCK
) +
4882 (sizeof(ULONG_PTR
) * 2),
4887 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4888 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4889 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4890 status
= STATUS_INSUFFICIENT_RESOURCES
;
4891 goto SetStatusAndReturn
;
4894 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
4896 cdb
= (PCDB
)srb
->Cdb
;
4899 // Save the function code and the device object in the memory after
4903 function
= (PULONG_PTR
) ((PSCSI_REQUEST_BLOCK
) (srb
+ 1));
4904 *function
= (ULONG_PTR
) DeviceObject
;
4906 *function
= (ULONG_PTR
) controlCode
;
4913 // Change the device type to storage for the switch statement, but only
4914 // if from a legacy device type
4917 if (((controlCode
& 0xffff0000) == (IOCTL_DISK_BASE
<< 16)) ||
4918 ((controlCode
& 0xffff0000) == (IOCTL_TAPE_BASE
<< 16)) ||
4919 ((controlCode
& 0xffff0000) == (IOCTL_CDROM_BASE
<< 16))
4922 modifiedIoControlCode
= (controlCode
& ~0xffff0000);
4923 modifiedIoControlCode
|= (IOCTL_STORAGE_BASE
<< 16);
4927 modifiedIoControlCode
= controlCode
;
4931 DBGTRACE(ClassDebugTrace
, ("> ioctl %xh (%s)", modifiedIoControlCode
, DBGGETIOCTLSTR(modifiedIoControlCode
)));
4933 switch (modifiedIoControlCode
) {
4935 case IOCTL_STORAGE_GET_HOTPLUG_INFO
: {
4942 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4943 sizeof(STORAGE_HOTPLUG_INFO
)) {
4946 // Indicate unsuccessful status and no data transferred.
4949 Irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
4950 Irp
->IoStatus
.Information
= sizeof(STORAGE_HOTPLUG_INFO
);
4952 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4953 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4954 status
= STATUS_BUFFER_TOO_SMALL
;
4956 } else if(!commonExtension
->IsFdo
) {
4959 // Just forward this down and return
4962 IoCopyCurrentIrpStackLocationToNext(Irp
);
4964 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4965 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4969 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
4970 PSTORAGE_HOTPLUG_INFO info
;
4972 fdoExtension
= (PFUNCTIONAL_DEVICE_EXTENSION
)commonExtension
;
4973 info
= Irp
->AssociatedIrp
.SystemBuffer
;
4975 *info
= fdoExtension
->PrivateFdoData
->HotplugInfo
;
4976 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
4977 Irp
->IoStatus
.Information
= sizeof(STORAGE_HOTPLUG_INFO
);
4978 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4979 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4980 status
= STATUS_SUCCESS
;
4986 case IOCTL_STORAGE_SET_HOTPLUG_INFO
: {
4994 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
4995 sizeof(STORAGE_HOTPLUG_INFO
)) {
4998 // Indicate unsuccessful status and no data transferred.
5001 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5003 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5004 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5005 status
= STATUS_INFO_LENGTH_MISMATCH
;
5006 goto SetStatusAndReturn
;
5010 if(!commonExtension
->IsFdo
) {
5013 // Just forward this down and return
5016 IoCopyCurrentIrpStackLocationToNext(Irp
);
5018 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5019 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5023 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PFUNCTIONAL_DEVICE_EXTENSION
)commonExtension
;
5024 PSTORAGE_HOTPLUG_INFO info
= Irp
->AssociatedIrp
.SystemBuffer
;
5026 status
= STATUS_SUCCESS
;
5028 if (info
->Size
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.Size
)
5030 status
= STATUS_INVALID_PARAMETER_1
;
5033 if (info
->MediaRemovable
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.MediaRemovable
)
5035 status
= STATUS_INVALID_PARAMETER_2
;
5038 if (info
->MediaHotplug
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.MediaHotplug
)
5040 status
= STATUS_INVALID_PARAMETER_3
;
5043 if (info
->WriteCacheEnableOverride
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.WriteCacheEnableOverride
)
5045 status
= STATUS_INVALID_PARAMETER_5
;
5048 if (NT_SUCCESS(status
))
5050 fdoExtension
->PrivateFdoData
->HotplugInfo
.DeviceHotplug
= info
->DeviceHotplug
;
5053 // Store the user-defined override in the registry
5056 ClassSetDeviceParameter(fdoExtension
,
5057 CLASSP_REG_SUBKEY_NAME
,
5058 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
5059 (info
->DeviceHotplug
) ? RemovalPolicyExpectSurpriseRemoval
: RemovalPolicyExpectOrderlyRemoval
);
5062 Irp
->IoStatus
.Status
= status
;
5064 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5065 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5071 case IOCTL_STORAGE_CHECK_VERIFY
:
5072 case IOCTL_STORAGE_CHECK_VERIFY2
: {
5075 PIO_STACK_LOCATION newStack
;
5077 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5079 DebugPrint((1,"DeviceIoControl: Check verify\n"));
5082 // If a buffer for a media change count was provided, make sure it's
5083 // big enough to hold the result
5086 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
) {
5089 // If the buffer is too small to hold the media change count
5090 // then return an error to the caller
5093 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
5096 DebugPrint((3,"DeviceIoControl: media count "
5097 "buffer too small\n"));
5099 Irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
5100 Irp
->IoStatus
.Information
= sizeof(ULONG
);
5106 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5107 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5109 status
= STATUS_BUFFER_TOO_SMALL
;
5110 goto SetStatusAndReturn
;
5115 if(!commonExtension
->IsFdo
) {
5118 // If this is a PDO then we should just forward the request down
5122 IoCopyCurrentIrpStackLocationToNext(Irp
);
5124 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5126 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5128 goto SetStatusAndReturn
;
5132 fdoExtension
= DeviceObject
->DeviceExtension
;
5136 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
) {
5139 // The caller has provided a valid buffer. Allocate an additional
5140 // irp and stick the CheckVerify completion routine on it. We will
5141 // then send this down to the port driver instead of the irp the
5145 DebugPrint((2,"DeviceIoControl: Check verify wants "
5149 // Allocate a new irp to send the TestUnitReady to the port driver
5152 irp2
= IoAllocateIrp((CCHAR
) (DeviceObject
->StackSize
+ 3), FALSE
);
5155 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
5156 Irp
->IoStatus
.Information
= 0;
5159 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5160 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5161 status
= STATUS_INSUFFICIENT_RESOURCES
;
5162 goto SetStatusAndReturn
;
5168 // Make sure to acquire the lock for the new irp.
5171 ClassAcquireRemoveLock(DeviceObject
, irp2
);
5173 irp2
->Tail
.Overlay
.Thread
= Irp
->Tail
.Overlay
.Thread
;
5174 IoSetNextIrpStackLocation(irp2
);
5177 // Set the top stack location and shove the master Irp into the
5181 newStack
= IoGetCurrentIrpStackLocation(irp2
);
5182 newStack
->Parameters
.Others
.Argument1
= Irp
;
5183 newStack
->DeviceObject
= DeviceObject
;
5186 // Stick the check verify completion routine onto the stack
5187 // and prepare the irp for the port driver
5190 IoSetCompletionRoutine(irp2
,
5191 ClassCheckVerifyComplete
,
5197 IoSetNextIrpStackLocation(irp2
);
5198 newStack
= IoGetCurrentIrpStackLocation(irp2
);
5199 newStack
->DeviceObject
= DeviceObject
;
5200 newStack
->MajorFunction
= irpStack
->MajorFunction
;
5201 newStack
->MinorFunction
= irpStack
->MinorFunction
;
5204 // Mark the master irp as pending - whether the lower level
5205 // driver completes it immediately or not this should allow it
5206 // to go all the way back up.
5209 IoMarkIrpPending(Irp
);
5220 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_TEST_UNIT_READY
;
5223 // Set timeout value.
5226 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5229 // If this was a CV2 then mark the request as low-priority so we don't
5230 // spin up the drive just to satisfy it.
5233 if(controlCode
== IOCTL_STORAGE_CHECK_VERIFY2
) {
5234 SET_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_LOW_PRIORITY
);
5238 // Since this routine will always hand the request to the
5239 // port driver if there isn't a data transfer to be done
5240 // we don't have to worry about completing the request here
5245 // This routine uses a completion routine so we don't want to release
5246 // the remove lock until then.
5249 status
= ClassSendSrbAsynchronous(DeviceObject
,
5259 case IOCTL_STORAGE_MEDIA_REMOVAL
:
5260 case IOCTL_STORAGE_EJECTION_CONTROL
: {
5262 PPREVENT_MEDIA_REMOVAL mediaRemoval
= Irp
->AssociatedIrp
.SystemBuffer
;
5264 DebugPrint((3, "DiskIoControl: ejection control\n"));
5270 if(irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
5271 sizeof(PREVENT_MEDIA_REMOVAL
)) {
5274 // Indicate unsuccessful status and no data transferred.
5277 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5279 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5280 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5281 status
= STATUS_INFO_LENGTH_MISMATCH
;
5282 goto SetStatusAndReturn
;
5285 if(!commonExtension
->IsFdo
) {
5288 // Just forward this down and return
5291 IoCopyCurrentIrpStackLocationToNext(Irp
);
5293 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5294 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5298 // i don't believe this assertion is valid. this is a request
5299 // from user-mode, so they could request this for any device
5300 // they want? also, we handle it properly.
5301 // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
5302 status
= ClasspEjectionControl(
5305 ((modifiedIoControlCode
==
5306 IOCTL_STORAGE_EJECTION_CONTROL
) ? SecureMediaLock
:
5308 mediaRemoval
->PreventMediaRemoval
);
5310 Irp
->IoStatus
.Status
= status
;
5311 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5312 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5318 case IOCTL_STORAGE_MCN_CONTROL
: {
5320 DebugPrint((3, "DiskIoControl: MCN control\n"));
5322 if(irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
5323 sizeof(PREVENT_MEDIA_REMOVAL
)) {
5326 // Indicate unsuccessful status and no data transferred.
5329 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5330 Irp
->IoStatus
.Information
= 0;
5336 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5337 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5338 status
= STATUS_INFO_LENGTH_MISMATCH
;
5339 goto SetStatusAndReturn
;
5342 if(!commonExtension
->IsFdo
) {
5345 // Just forward this down and return
5352 IoCopyCurrentIrpStackLocationToNext(Irp
);
5354 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5355 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5360 // Call to the FDO - handle the ejection control.
5363 status
= ClasspMcnControl(DeviceObject
->DeviceExtension
,
5367 goto SetStatusAndReturn
;
5370 case IOCTL_STORAGE_RESERVE
:
5371 case IOCTL_STORAGE_RELEASE
: {
5374 // Reserve logical unit.
5377 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5379 if(!commonExtension
->IsFdo
) {
5381 IoCopyCurrentIrpStackLocationToNext(Irp
);
5383 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5384 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5385 goto SetStatusAndReturn
;
5387 fdoExtension
= DeviceObject
->DeviceExtension
;
5392 if(modifiedIoControlCode
== IOCTL_STORAGE_RESERVE
) {
5393 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_RESERVE_UNIT
;
5395 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_RELEASE_UNIT
;
5399 // Set timeout value.
5402 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5404 status
= ClassSendSrbAsynchronous(DeviceObject
,
5414 case IOCTL_STORAGE_EJECT_MEDIA
:
5415 case IOCTL_STORAGE_LOAD_MEDIA
:
5416 case IOCTL_STORAGE_LOAD_MEDIA2
:{
5422 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5424 if(!commonExtension
->IsFdo
) {
5426 IoCopyCurrentIrpStackLocationToNext(Irp
);
5428 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5430 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5431 goto SetStatusAndReturn
;
5433 fdoExtension
= DeviceObject
->DeviceExtension
;
5436 if(commonExtension
->PagingPathCount
!= 0) {
5438 DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
5441 status
= STATUS_FILES_OPEN
;
5442 Irp
->IoStatus
.Status
= status
;
5444 Irp
->IoStatus
.Information
= 0;
5450 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5451 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5452 goto SetStatusAndReturn
;
5456 // Synchronize with ejection control and ejection cleanup code as
5457 // well as other eject/load requests.
5460 KeEnterCriticalRegion();
5461 KeWaitForSingleObject(&(fdoExtension
->EjectSynchronizationEvent
),
5467 if(fdoExtension
->ProtectedLockCount
!= 0) {
5469 DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
5470 "device - failure\n"));
5472 status
= STATUS_DEVICE_BUSY
;
5473 Irp
->IoStatus
.Status
= status
;
5474 Irp
->IoStatus
.Information
= 0;
5480 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5481 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5483 KeSetEvent(&fdoExtension
->EjectSynchronizationEvent
,
5486 KeLeaveCriticalRegion();
5488 goto SetStatusAndReturn
;
5493 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
5494 cdb
->START_STOP
.LoadEject
= 1;
5496 if(modifiedIoControlCode
== IOCTL_STORAGE_EJECT_MEDIA
) {
5497 cdb
->START_STOP
.Start
= 0;
5499 cdb
->START_STOP
.Start
= 1;
5503 // Set timeout value.
5506 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5507 status
= ClassSendSrbAsynchronous(DeviceObject
,
5514 KeSetEvent(&fdoExtension
->EjectSynchronizationEvent
, IO_NO_INCREMENT
, FALSE
);
5515 KeLeaveCriticalRegion();
5520 case IOCTL_STORAGE_FIND_NEW_DEVICES
: {
5526 if(commonExtension
->IsFdo
) {
5528 IoInvalidateDeviceRelations(
5529 ((PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
)->LowerPdo
,
5532 status
= STATUS_SUCCESS
;
5533 Irp
->IoStatus
.Status
= status
;
5535 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5536 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5540 IoCopyCurrentIrpStackLocationToNext(Irp
);
5542 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5543 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5548 case IOCTL_STORAGE_GET_DEVICE_NUMBER
: {
5554 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
>=
5555 sizeof(STORAGE_DEVICE_NUMBER
)) {
5557 PSTORAGE_DEVICE_NUMBER deviceNumber
=
5558 Irp
->AssociatedIrp
.SystemBuffer
;
5559 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
5560 commonExtension
->PartitionZeroExtension
;
5562 deviceNumber
->DeviceType
= fdoExtension
->CommonExtension
.DeviceObject
->DeviceType
;
5563 deviceNumber
->DeviceNumber
= fdoExtension
->DeviceNumber
;
5564 deviceNumber
->PartitionNumber
= commonExtension
->PartitionNumber
;
5566 status
= STATUS_SUCCESS
;
5567 Irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_NUMBER
);
5570 status
= STATUS_BUFFER_TOO_SMALL
;
5571 Irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_NUMBER
);
5574 Irp
->IoStatus
.Status
= status
;
5575 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5576 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5583 DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
5584 controlCode
, DeviceObject
));
5587 // Pass the device control to the next driver.
5595 // Copy the Irp stack parameters to the next stack location.
5598 IoCopyCurrentIrpStackLocationToNext(Irp
);
5600 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5601 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5605 } // end switch( ...
5609 DBGTRACE(ClassDebugTrace
, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode
, DBGGETIOCTLSTR(modifiedIoControlCode
), status
));
5612 } // end ClassDeviceControl()
5614 /*++////////////////////////////////////////////////////////////////////////////
5616 ClassShutdownFlush()
5618 Routine Description:
5620 This routine is called for a shutdown and flush IRPs. These are sent by the
5621 system before it actually shuts down or when the file system does a flush.
5622 If it exists, the device-specific driver's routine will be invoked. If there
5623 wasn't one specified, the Irp will be completed with an Invalid device request.
5627 DriverObject - Pointer to device object to being shutdown by system.
5639 IN PDEVICE_OBJECT DeviceObject
,
5643 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
5649 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
5653 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5655 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
5657 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5659 return STATUS_DEVICE_DOES_NOT_EXIST
;
5662 if (commonExtension
->DevInfo
->ClassShutdownFlush
) {
5665 // Call the device-specific driver's routine.
5668 return commonExtension
->DevInfo
->ClassShutdownFlush(DeviceObject
, Irp
);
5672 // Device-specific driver doesn't support this.
5675 Irp
->IoStatus
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
5677 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5678 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5680 return STATUS_INVALID_DEVICE_REQUEST
;
5681 } // end ClassShutdownFlush()
5683 /*++////////////////////////////////////////////////////////////////////////////
5685 ClassCreateDeviceObject()
5687 Routine Description:
5689 This routine creates an object for the physical device specified and
5690 sets up the deviceExtension's function pointers for each entry point
5691 in the device-specific driver.
5695 DriverObject - Pointer to driver object created by system.
5697 ObjectNameBuffer - Dir. name of the object to create.
5699 LowerDeviceObject - Pointer to the lower device object
5701 IsFdo - should this be an fdo or a pdo
5703 DeviceObject - Pointer to the device object pointer we will return.
5713 ClassCreateDeviceObject(
5714 IN PDRIVER_OBJECT DriverObject
,
5715 IN PCCHAR ObjectNameBuffer
,
5716 IN PDEVICE_OBJECT LowerDevice
,
5718 IN OUT PDEVICE_OBJECT
*DeviceObject
5721 BOOLEAN isPartitionable
;
5722 STRING ntNameString
;
5723 UNICODE_STRING ntUnicodeString
;
5724 NTSTATUS status
, status2
;
5725 PDEVICE_OBJECT deviceObject
= NULL
;
5727 ULONG characteristics
;
5729 PCLASS_DRIVER_EXTENSION
5730 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
5731 CLASS_DRIVER_EXTENSION_KEY
);
5733 PCLASS_DEV_INFO devInfo
;
5737 *DeviceObject
= NULL
;
5738 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5740 DebugPrint((2, "ClassCreateFdo: Create device object\n"));
5742 ASSERT(LowerDevice
);
5745 // Make sure that if we're making PDO's we have an enumeration routine
5748 isPartitionable
= (driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
5750 ASSERT(IsFdo
|| isPartitionable
);
5753 // Grab the correct dev-info structure out of the init data
5757 devInfo
= &(driverExtension
->InitData
.FdoData
);
5759 devInfo
= &(driverExtension
->InitData
.PdoData
);
5762 characteristics
= devInfo
->DeviceCharacteristics
;
5764 if(ARGUMENT_PRESENT(ObjectNameBuffer
)) {
5765 DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer
));
5767 RtlInitString(&ntNameString
, ObjectNameBuffer
);
5769 status
= RtlAnsiStringToUnicodeString(&ntUnicodeString
, &ntNameString
, TRUE
);
5771 if (!NT_SUCCESS(status
)) {
5774 "ClassCreateFdo: Cannot convert string %s\n",
5777 ntUnicodeString
.Buffer
= NULL
;
5781 DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
5783 if(IsFdo
== FALSE
) {
5786 // PDO's have to have some sort of name.
5789 SET_FLAG(characteristics
, FILE_AUTOGENERATED_DEVICE_NAME
);
5792 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5795 status
= IoCreateDevice(DriverObject
,
5796 devInfo
->DeviceExtensionSize
,
5798 devInfo
->DeviceType
,
5799 devInfo
->DeviceCharacteristics
,
5803 if (!NT_SUCCESS(status
)) {
5805 DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
5807 ASSERT(deviceObject
== NULL
);
5810 // buffer is not used any longer here.
5813 if (ntUnicodeString
.Buffer
!= NULL
) {
5814 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5815 ExFreePool(ntUnicodeString
.Buffer
);
5816 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5821 PCOMMON_DEVICE_EXTENSION commonExtension
= deviceObject
->DeviceExtension
;
5824 deviceObject
->DeviceExtension
,
5825 devInfo
->DeviceExtensionSize
);
5828 // Setup version code
5831 commonExtension
->Version
= 0x03;
5834 // Setup the remove lock and event
5837 commonExtension
->IsRemoved
= NO_REMOVE
;
5838 commonExtension
->RemoveLock
= 0;
5839 KeInitializeEvent(&commonExtension
->RemoveEvent
,
5840 SynchronizationEvent
,
5844 KeInitializeSpinLock(&commonExtension
->RemoveTrackingSpinlock
);
5845 commonExtension
->RemoveTrackingList
= NULL
;
5847 commonExtension
->RemoveTrackingSpinlock
= (ULONG_PTR
) -1;
5848 commonExtension
->RemoveTrackingList
= (PVOID
) -1;
5852 // Acquire the lock once. This reference will be released when the
5853 // remove IRP has been received.
5856 ClassAcquireRemoveLock(deviceObject
, (PIRP
) deviceObject
);
5859 // Store a pointer to the driver extension so we don't have to do
5860 // lookups to get it.
5863 commonExtension
->DriverExtension
= driverExtension
;
5866 // Fill in entry points
5869 commonExtension
->DevInfo
= devInfo
;
5872 // Initialize some of the common values in the structure
5875 commonExtension
->DeviceObject
= deviceObject
;
5877 commonExtension
->LowerDeviceObject
= NULL
;
5881 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PVOID
) commonExtension
;
5883 commonExtension
->PartitionZeroExtension
= deviceObject
->DeviceExtension
;
5886 // Set the initial device object flags.
5889 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5892 // Clear the PDO list
5895 commonExtension
->ChildList
= NULL
;
5897 commonExtension
->DriverData
=
5898 ((PFUNCTIONAL_DEVICE_EXTENSION
) deviceObject
->DeviceExtension
+ 1);
5900 if(isPartitionable
) {
5902 commonExtension
->PartitionNumber
= 0;
5904 commonExtension
->PartitionNumber
= (ULONG
) (-1L);
5907 fdoExtension
->DevicePowerState
= PowerDeviceD0
;
5909 KeInitializeEvent(&fdoExtension
->EjectSynchronizationEvent
,
5910 SynchronizationEvent
,
5913 KeInitializeEvent(&fdoExtension
->ChildLock
,
5914 SynchronizationEvent
,
5917 status
= ClasspAllocateReleaseRequest(deviceObject
);
5919 if(!NT_SUCCESS(status
)) {
5920 IoDeleteDevice(deviceObject
);
5921 *DeviceObject
= NULL
;
5923 if (ntUnicodeString
.Buffer
!= NULL
) {
5924 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5925 ExFreePool(ntUnicodeString
.Buffer
);
5926 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5934 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
5935 deviceObject
->DeviceExtension
;
5937 PFUNCTIONAL_DEVICE_EXTENSION p0Extension
=
5938 LowerDevice
->DeviceExtension
;
5940 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5942 commonExtension
->PartitionZeroExtension
= p0Extension
;
5945 // Stick this onto the PDO list
5948 ClassAddChild(p0Extension
, pdoExtension
, TRUE
);
5950 commonExtension
->DriverData
= (PVOID
) (pdoExtension
+ 1);
5953 // Get the top of stack for the lower device - this allows
5954 // filters to get stuck in between the partitions and the
5958 commonExtension
->LowerDeviceObject
=
5959 IoGetAttachedDeviceReference(LowerDevice
);
5962 // Pnp will keep a reference to the lower device object long
5963 // after this partition has been deleted. Dereference now so
5964 // we don't have to deal with it later.
5967 ObDereferenceObject(commonExtension
->LowerDeviceObject
);
5970 KeInitializeEvent(&commonExtension
->PathCountEvent
, SynchronizationEvent
, TRUE
);
5972 commonExtension
->IsFdo
= IsFdo
;
5974 commonExtension
->DeviceName
= ntUnicodeString
;
5976 commonExtension
->PreviousState
= 0xff;
5978 InitializeDictionary(&(commonExtension
->FileObjectDictionary
));
5980 commonExtension
->CurrentState
= IRP_MN_STOP_DEVICE
;
5983 *DeviceObject
= deviceObject
;
5986 } // end ClassCreateDeviceObject()
5988 /*++////////////////////////////////////////////////////////////////////////////
5992 Routine Description:
5994 This function claims a device in the port driver. The port driver object
5995 is updated with the correct driver object if the device is successfully
6000 LowerDeviceObject - Supplies the base port device object.
6002 Release - Indicates the logical unit should be released rather than claimed.
6006 Returns a status indicating success or failure of the operation.
6013 IN PDEVICE_OBJECT LowerDeviceObject
,
6017 IO_STATUS_BLOCK ioStatus
;
6019 PIO_STACK_LOCATION irpStack
;
6022 SCSI_REQUEST_BLOCK srb
;
6027 // Clear the SRB fields.
6030 RtlZeroMemory(&srb
, sizeof(SCSI_REQUEST_BLOCK
));
6033 // Write length to SRB.
6036 srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
6038 srb
.Function
= Release
? SRB_FUNCTION_RELEASE_DEVICE
:
6039 SRB_FUNCTION_CLAIM_DEVICE
;
6042 // Set the event object to the unsignaled state.
6043 // It will be used to signal request completion
6046 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
6049 // Build synchronous request with no transfer.
6052 irp
= IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE
,
6063 DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
6064 return STATUS_INSUFFICIENT_RESOURCES
;
6067 irpStack
= IoGetNextIrpStackLocation(irp
);
6070 // Save SRB address in next stack for port driver.
6073 irpStack
->Parameters
.Scsi
.Srb
= &srb
;
6076 // Set up IRP Address.
6079 srb
.OriginalRequest
= irp
;
6082 // Call the port driver with the request and wait for it to complete.
6085 status
= IoCallDriver(LowerDeviceObject
, irp
);
6086 if (status
== STATUS_PENDING
) {
6088 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
6089 status
= ioStatus
.Status
;
6093 // If this is a release request, then just decrement the reference count
6094 // and return. The status does not matter.
6099 // ObDereferenceObject(LowerDeviceObject);
6100 return STATUS_SUCCESS
;
6103 if (!NT_SUCCESS(status
)) {
6107 ASSERT(srb
.DataBuffer
!= NULL
);
6108 ASSERT(!TEST_FLAG(srb
.SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
6111 } // end ClassClaimDevice()
6113 /*++////////////////////////////////////////////////////////////////////////////
6115 ClassInternalIoControl()
6117 Routine Description:
6119 This routine passes internal device controls to the port driver.
6120 Internal device controls are used by higher level drivers both for ioctls
6121 and to pass through scsi requests.
6123 If the IoControlCode does not match any of the handled ioctls and is
6124 a valid system address then the request will be treated as an SRB and
6125 passed down to the lower driver. If the IoControlCode is not a valid
6126 system address the ioctl will be failed.
6128 Callers must therefore be extremely cautious to pass correct, initialized
6129 values to this function.
6133 DeviceObject - Supplies a pointer to the device object for this request.
6135 Irp - Supplies the Irp making the request.
6139 Returns back a STATUS_PENDING or a completion status.
6145 ClassInternalIoControl(
6146 IN PDEVICE_OBJECT DeviceObject
,
6150 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6152 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6153 PIO_STACK_LOCATION nextStack
= IoGetNextIrpStackLocation(Irp
);
6157 PSCSI_REQUEST_BLOCK srb
;
6159 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
6163 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
6165 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6167 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
6169 return STATUS_DEVICE_DOES_NOT_EXIST
;
6173 // Get a pointer to the SRB.
6176 srb
= irpStack
->Parameters
.Scsi
.Srb
;
6179 // Set the parameters in the next stack location.
6182 if(commonExtension
->IsFdo
) {
6183 nextStack
->Parameters
.Scsi
.Srb
= srb
;
6184 nextStack
->MajorFunction
= IRP_MJ_SCSI
;
6185 nextStack
->MinorFunction
= IRP_MN_SCSI_CLASS
;
6189 IoCopyCurrentIrpStackLocationToNext(Irp
);
6192 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6194 return IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
6195 } // end ClassInternalIoControl()
6197 /*++////////////////////////////////////////////////////////////////////////////
6199 ClassQueryTimeOutRegistryValue()
6201 Routine Description:
6203 This routine determines whether a reg key for a user-specified timeout
6204 value exists. This should be called at initialization time.
6208 DeviceObject - Pointer to the device object we are retrieving the timeout
6213 None, but it sets a new default timeout for a class of devices.
6219 ClassQueryTimeOutRegistryValue(
6220 IN PDEVICE_OBJECT DeviceObject
6224 // Find the appropriate reg. key
6227 PCLASS_DRIVER_EXTENSION
6228 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6229 CLASS_DRIVER_EXTENSION_KEY
);
6231 PUNICODE_STRING registryPath
= &(driverExtension
->RegistryPath
);
6233 PRTL_QUERY_REGISTRY_TABLE parameters
= NULL
;
6242 if (!registryPath
) {
6246 parameters
= ExAllocatePoolWithTag(NonPagedPool
,
6247 sizeof(RTL_QUERY_REGISTRY_TABLE
)*2,
6254 size
= registryPath
->MaximumLength
+ sizeof(WCHAR
);
6255 path
= ExAllocatePoolWithTag(NonPagedPool
, size
, '2BcS');
6258 ExFreePool(parameters
);
6262 RtlZeroMemory(path
,size
);
6263 RtlCopyMemory(path
, registryPath
->Buffer
, size
- sizeof(WCHAR
));
6267 // Check for the Timeout value.
6270 RtlZeroMemory(parameters
,
6271 (sizeof(RTL_QUERY_REGISTRY_TABLE
)*2));
6273 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
6274 parameters
[0].Name
= L
"TimeOutValue";
6275 parameters
[0].EntryContext
= &timeOut
;
6276 parameters
[0].DefaultType
= REG_DWORD
;
6277 parameters
[0].DefaultData
= &zero
;
6278 parameters
[0].DefaultLength
= sizeof(ULONG
);
6280 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
| RTL_REGISTRY_OPTIONAL
,
6286 if (!(NT_SUCCESS(status
))) {
6290 ExFreePool(parameters
);
6294 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
6300 } // end ClassQueryTimeOutRegistryValue()
6302 /*++////////////////////////////////////////////////////////////////////////////
6304 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
6306 Routine Description:
6308 This routine executes when the port driver has completed a check verify
6309 ioctl. It will set the status of the master Irp, copy the media change
6310 count and complete the request.
6314 Fdo - Supplies the functional device object which represents the logical unit.
6316 Irp - Supplies the Irp which has completed.
6328 ClassCheckVerifyComplete(
6329 IN PDEVICE_OBJECT Fdo
,
6334 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6335 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6341 originalIrp
= irpStack
->Parameters
.Others
.Argument1
;
6344 // Copy the media change count and status
6347 *((PULONG
) (originalIrp
->AssociatedIrp
.SystemBuffer
)) =
6348 fdoExtension
->MediaChangeCount
;
6350 DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
6351 "device %d is %lx - saved as %lx\n",
6352 fdoExtension
->DeviceNumber
,
6353 fdoExtension
->MediaChangeCount
,
6354 *((PULONG
) originalIrp
->AssociatedIrp
.SystemBuffer
)));
6356 originalIrp
->IoStatus
.Status
= Irp
->IoStatus
.Status
;
6357 originalIrp
->IoStatus
.Information
= sizeof(ULONG
);
6359 ClassReleaseRemoveLock(Fdo
, originalIrp
);
6360 ClassCompleteRequest(Fdo
, originalIrp
, IO_DISK_INCREMENT
);
6364 return STATUS_MORE_PROCESSING_REQUIRED
;
6366 } // end ClassCheckVerifyComplete()
6368 /*++////////////////////////////////////////////////////////////////////////////
6370 ClassGetDescriptor()
6372 Routine Description:
6374 This routine will perform a query for the specified property id and will
6375 allocate a non-paged buffer to store the data in. It is the responsibility
6376 of the caller to ensure that this buffer is freed.
6378 This routine must be run at IRQL_PASSIVE_LEVEL
6382 DeviceObject - the device to query
6383 DeviceInfo - a location to store a pointer to the buffer we allocate
6388 if status is unsuccessful *DeviceInfo will be set to NULL, else the
6389 buffer allocated on behalf of the caller.
6396 IN PDEVICE_OBJECT DeviceObject
,
6397 IN PSTORAGE_PROPERTY_ID PropertyId
,
6398 OUT PSTORAGE_DESCRIPTOR_HEADER
*Descriptor
6401 STORAGE_PROPERTY_QUERY query
;
6402 IO_STATUS_BLOCK ioStatus
;
6404 PSTORAGE_DESCRIPTOR_HEADER descriptor
= NULL
;
6412 // Set the passed-in descriptor pointer to NULL as default
6418 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6419 query
.PropertyId
= *PropertyId
;
6420 query
.QueryType
= PropertyStandardQuery
;
6423 // On the first pass we just want to get the first few
6424 // bytes of the descriptor so we can read it's size
6427 descriptor
= (PVOID
)&query
;
6429 ASSERT(sizeof(STORAGE_PROPERTY_QUERY
) >= (sizeof(ULONG
)*2));
6431 ClassSendDeviceIoControlSynchronous(
6432 IOCTL_STORAGE_QUERY_PROPERTY
,
6435 sizeof(STORAGE_PROPERTY_QUERY
),
6441 if(!NT_SUCCESS(ioStatus
.Status
)) {
6443 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6444 "query properties #1\n", ioStatus
.Status
));
6445 return ioStatus
.Status
;
6448 if (descriptor
->Size
== 0) {
6451 // This DebugPrint is to help third-party driver writers
6454 DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
6455 "%x\n", ioStatus
.Status
));
6456 return STATUS_UNSUCCESSFUL
;
6461 // This time we know how much data there is so we can
6462 // allocate a buffer of the correct size
6465 length
= descriptor
->Size
;
6467 descriptor
= ExAllocatePoolWithTag(NonPagedPool
, length
, '4BcS');
6469 if(descriptor
== NULL
) {
6471 DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
6472 "(%d bytes)\n", length
));
6473 return STATUS_INSUFFICIENT_RESOURCES
;
6477 // setup the query again, as it was overwritten above
6480 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6481 query
.PropertyId
= *PropertyId
;
6482 query
.QueryType
= PropertyStandardQuery
;
6485 // copy the input to the new outputbuffer
6488 RtlCopyMemory(descriptor
,
6490 sizeof(STORAGE_PROPERTY_QUERY
)
6493 ClassSendDeviceIoControlSynchronous(
6494 IOCTL_STORAGE_QUERY_PROPERTY
,
6497 sizeof(STORAGE_PROPERTY_QUERY
),
6503 if(!NT_SUCCESS(ioStatus
.Status
)) {
6505 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6506 "query properties #1\n", ioStatus
.Status
));
6507 ExFreePool(descriptor
);
6508 return ioStatus
.Status
;
6512 // return the memory we've allocated to the caller
6515 *Descriptor
= descriptor
;
6516 return ioStatus
.Status
;
6517 } // end ClassGetDescriptor()
6519 /*++////////////////////////////////////////////////////////////////////////////
6521 ClassSignalCompletion()
6523 Routine Description:
6525 This completion routine will signal the event given as context and then
6526 return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
6527 the responsibility of the routine waiting on the event to complete the
6528 request and free the event.
6532 DeviceObject - a pointer to the device object
6534 Irp - a pointer to the irp
6536 Event - a pointer to the event to signal
6540 STATUS_MORE_PROCESSING_REQUIRED
6546 ClassSignalCompletion(
6547 IN PDEVICE_OBJECT DeviceObject
,
6552 KeSetEvent(Event
, IO_NO_INCREMENT
, FALSE
);
6554 return STATUS_MORE_PROCESSING_REQUIRED
;
6555 } // end ClassSignalCompletion()
6557 /*++////////////////////////////////////////////////////////////////////////////
6559 ClassPnpQueryFdoRelations()
6561 Routine Description:
6563 This routine will call the driver's enumeration routine to update the
6564 list of PDO's. It will then build a response to the
6565 IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
6570 Fdo - a pointer to the functional device object we are enumerating
6572 Irp - a pointer to the enumeration request
6580 ClassPnpQueryFdoRelations(
6581 IN PDEVICE_OBJECT Fdo
,
6585 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6586 PCLASS_DRIVER_EXTENSION
6587 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6588 CLASS_DRIVER_EXTENSION_KEY
);
6594 // If there's already an enumeration in progress then don't start another
6598 if(InterlockedIncrement(&(fdoExtension
->EnumerationInterlock
)) == 1) {
6599 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6602 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
6604 Irp
->IoStatus
.Status
= ClassRetrieveDeviceRelations(
6607 (PDEVICE_RELATIONS
)&Irp
->IoStatus
.Information
);
6608 InterlockedDecrement(&(fdoExtension
->EnumerationInterlock
));
6610 return Irp
->IoStatus
.Status
;
6611 } // end ClassPnpQueryFdoRelations()
6613 /*++////////////////////////////////////////////////////////////////////////////
6615 ClassMarkChildrenMissing()
6617 Routine Description:
6619 This routine will call ClassMarkChildMissing() for all children.
6620 It acquires the ChildLock before calling ClassMarkChildMissing().
6624 Fdo - the "bus's" device object, such as the disk FDO for non-removable
6625 disks with multiple partitions.
6635 ClassMarkChildrenMissing(
6636 IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
6639 PCOMMON_DEVICE_EXTENSION commonExtension
= &(Fdo
->CommonExtension
);
6640 PPHYSICAL_DEVICE_EXTENSION nextChild
= commonExtension
->ChildList
;
6644 ClassAcquireChildLock(Fdo
);
6647 PPHYSICAL_DEVICE_EXTENSION tmpChild
;
6650 * ClassMarkChildMissing will also dequeue the child extension.
6651 * So get the next pointer before calling ClassMarkChildMissing.
6653 tmpChild
= nextChild
;
6654 nextChild
= tmpChild
->CommonExtension
.ChildList
;
6655 ClassMarkChildMissing(tmpChild
, FALSE
);
6657 ClassReleaseChildLock(Fdo
);
6659 } // end ClassMarkChildrenMissing()
6661 /*++////////////////////////////////////////////////////////////////////////////
6663 ClassMarkChildMissing()
6665 Routine Description:
6667 This routine will make an active child "missing." If the device has never
6668 been enumerated then it will be deleted on the spot. If the device has
6669 not been enumerated then it will be marked as missing so that we can
6670 not report it in the next device enumeration.
6674 Child - the child device to be marked as missing.
6676 AcquireChildLock - TRUE if the child lock should be acquired before removing
6677 the missing child. FALSE if the child lock is already
6678 acquired by this thread.
6682 returns whether or not the child device object has previously been reported
6689 ClassMarkChildMissing(
6690 IN PPHYSICAL_DEVICE_EXTENSION Child
,
6691 IN BOOLEAN AcquireChildLock
6694 BOOLEAN returnValue
= Child
->IsEnumerated
;
6697 ASSERT_PDO(Child
->DeviceObject
);
6699 Child
->IsMissing
= TRUE
;
6702 // Make sure this child is not in the active list.
6705 ClassRemoveChild(Child
->CommonExtension
.PartitionZeroExtension
,
6709 if(Child
->IsEnumerated
== FALSE
) {
6710 ClassRemoveDevice(Child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
6714 } // end ClassMarkChildMissing()
6716 /*++////////////////////////////////////////////////////////////////////////////
6718 ClassRetrieveDeviceRelations()
6720 Routine Description:
6722 This routine will allocate a buffer to hold the specified list of
6723 relations. It will then fill in the list with referenced device pointers
6724 and will return the request.
6728 Fdo - pointer to the FDO being queried
6730 RelationType - what type of relations are being queried
6732 DeviceRelations - a location to store a pointer to the response
6740 ClassRetrieveDeviceRelations(
6741 IN PDEVICE_OBJECT Fdo
,
6742 IN DEVICE_RELATION_TYPE RelationType
,
6743 OUT PDEVICE_RELATIONS
*DeviceRelations
6746 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6751 PPHYSICAL_DEVICE_EXTENSION nextChild
;
6753 ULONG relationsSize
;
6754 PDEVICE_RELATIONS deviceRelations
= NULL
;
6760 ClassAcquireChildLock(fdoExtension
);
6762 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6765 // Count the number of PDO's attached to this disk
6768 while(nextChild
!= NULL
) {
6769 PCOMMON_DEVICE_EXTENSION commonExtension
;
6771 commonExtension
= &(nextChild
->CommonExtension
);
6773 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6774 (nextChild
->IsMissing
== FALSE
));
6776 nextChild
= commonExtension
->ChildList
;
6781 relationsSize
= (sizeof(DEVICE_RELATIONS
) +
6782 (count
* sizeof(PDEVICE_OBJECT
)));
6784 deviceRelations
= ExAllocatePoolWithTag(PagedPool
, relationsSize
, '5BcS');
6786 if(deviceRelations
== NULL
) {
6788 DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
6789 "%d bytes for device relations\n", relationsSize
));
6791 ClassReleaseChildLock(fdoExtension
);
6793 return STATUS_INSUFFICIENT_RESOURCES
;
6796 RtlZeroMemory(deviceRelations
, relationsSize
);
6798 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6801 while(nextChild
!= NULL
) {
6802 PCOMMON_DEVICE_EXTENSION commonExtension
;
6804 commonExtension
= &(nextChild
->CommonExtension
);
6806 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6807 (nextChild
->IsMissing
== FALSE
));
6809 deviceRelations
->Objects
[i
--] = nextChild
->DeviceObject
;
6811 status
= ObReferenceObjectByPointer(
6812 nextChild
->DeviceObject
,
6816 ASSERT(NT_SUCCESS(status
));
6818 nextChild
->IsEnumerated
= TRUE
;
6819 nextChild
= commonExtension
->ChildList
;
6822 ASSERTMSG("Child list has changed: ", i
== -1);
6824 deviceRelations
->Count
= count
;
6825 *DeviceRelations
= deviceRelations
;
6826 ClassReleaseChildLock(fdoExtension
);
6827 return STATUS_SUCCESS
;
6828 } // end ClassRetrieveDeviceRelations()
6830 /*++////////////////////////////////////////////////////////////////////////////
6834 Routine Description:
6836 This routine will call into the driver to retrieve a copy of one of it's
6841 Pdo - a pointer to the pdo being queried
6843 IdType - which type of id string is being queried
6845 IdString - an allocated unicode string structure which the driver
6855 IN PDEVICE_OBJECT Pdo
,
6856 IN BUS_QUERY_ID_TYPE IdType
,
6857 IN PUNICODE_STRING IdString
6860 PCLASS_DRIVER_EXTENSION
6861 driverExtension
= IoGetDriverObjectExtension(Pdo
->DriverObject
,
6862 CLASS_DRIVER_EXTENSION_KEY
);
6865 ASSERT(driverExtension
->InitData
.ClassQueryId
);
6869 return driverExtension
->InitData
.ClassQueryId( Pdo
, IdType
, IdString
);
6870 } // end ClassGetPdoId()
6872 /*++////////////////////////////////////////////////////////////////////////////
6874 ClassQueryPnpCapabilities()
6876 Routine Description:
6878 This routine will call into the class driver to retrieve it's pnp
6883 PhysicalDeviceObject - The physical device object to retrieve properties
6892 ClassQueryPnpCapabilities(
6893 IN PDEVICE_OBJECT DeviceObject
,
6894 IN PDEVICE_CAPABILITIES Capabilities
6897 PCLASS_DRIVER_EXTENSION driverExtension
=
6898 ClassGetDriverExtension(DeviceObject
->DriverObject
);
6899 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6901 PCLASS_QUERY_PNP_CAPABILITIES queryRoutine
= NULL
;
6905 ASSERT(DeviceObject
);
6906 ASSERT(Capabilities
);
6908 if(commonExtension
->IsFdo
) {
6909 queryRoutine
= driverExtension
->InitData
.FdoData
.ClassQueryPnpCapabilities
;
6911 queryRoutine
= driverExtension
->InitData
.PdoData
.ClassQueryPnpCapabilities
;
6915 return queryRoutine(DeviceObject
,
6918 return STATUS_NOT_IMPLEMENTED
;
6920 } // end ClassQueryPnpCapabilities()
6922 /*++////////////////////////////////////////////////////////////////////////////
6924 ClassInvalidateBusRelations()
6926 Routine Description:
6928 This routine re-enumerates the devices on the "bus". It will call into
6929 the driver's ClassEnumerate routine to update the device objects
6930 immediately. It will then schedule a bus re-enumeration for pnp by calling
6931 IoInvalidateDeviceRelations.
6935 Fdo - a pointer to the functional device object for this bus
6945 ClassInvalidateBusRelations(
6946 IN PDEVICE_OBJECT Fdo
6949 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6950 PCLASS_DRIVER_EXTENSION
6951 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6952 CLASS_DRIVER_EXTENSION_KEY
);
6954 NTSTATUS status
= STATUS_SUCCESS
;
6959 ASSERT(driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
6961 if(InterlockedIncrement(&(fdoExtension
->EnumerationInterlock
)) == 1) {
6962 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6964 InterlockedDecrement(&(fdoExtension
->EnumerationInterlock
));
6966 if(!NT_SUCCESS(status
)) {
6968 DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
6969 "returned %lx\n", status
));
6972 IoInvalidateDeviceRelations(fdoExtension
->LowerPdo
, BusRelations
);
6975 } // end ClassInvalidateBusRelations()
6977 /*++////////////////////////////////////////////////////////////////////////////
6979 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
6981 Routine Description:
6983 This routine is called to handle the "removal" of a device. It will
6984 forward the request downwards if necesssary, call into the driver
6985 to release any necessary resources (memory, events, etc) and then
6986 will delete the device object.
6990 DeviceObject - a pointer to the device object being removed
6992 RemoveType - indicates what type of remove this is (regular or surprise).
7003 IN PDEVICE_OBJECT DeviceObject
,
7007 PCLASS_DRIVER_EXTENSION
7008 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
7009 CLASS_DRIVER_EXTENSION_KEY
);
7010 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7011 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
7012 PCLASS_WMI_INFO classWmiInfo
;
7013 BOOLEAN proceedWithRemove
= TRUE
;
7018 commonExtension
->IsRemoved
= REMOVE_PENDING
;
7021 * Deregister from WMI.
7023 classWmiInfo
= commonExtension
->IsFdo
?
7024 &driverExtension
->InitData
.FdoData
.ClassWmiInfo
:
7025 &driverExtension
->InitData
.PdoData
.ClassWmiInfo
;
7026 if (classWmiInfo
->GuidRegInfo
){
7027 status
= IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_DEREGISTER
);
7028 DBGTRACE(ClassDebugInfo
, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject
, status
));
7032 * If we exposed a "shingle" (a named device interface openable by CreateFile)
7033 * then delete it now.
7035 if (commonExtension
->MountedDeviceInterfaceName
.Buffer
){
7036 IoSetDeviceInterfaceState(&commonExtension
->MountedDeviceInterfaceName
, FALSE
);
7037 RtlFreeUnicodeString(&commonExtension
->MountedDeviceInterfaceName
);
7038 RtlInitUnicodeString(&commonExtension
->MountedDeviceInterfaceName
, NULL
);
7042 // If this is a surprise removal we leave the device around - which means
7043 // we don't have to (or want to) drop the remove lock and wait for pending
7044 // requests to complete.
7047 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7050 // Release the lock we acquired when the device object was created.
7053 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7055 DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
7056 commonExtension
->RemoveLock
));
7058 KeWaitForSingleObject(&commonExtension
->RemoveEvent
,
7064 DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject
));
7066 if(commonExtension
->IsFdo
) {
7068 DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
7069 "remove request.\n", DeviceObject
));
7073 PPHYSICAL_DEVICE_EXTENSION pdoExtension
= DeviceObject
->DeviceExtension
;
7075 if (pdoExtension
->IsMissing
){
7077 * The child partition PDO is missing, so we are going to go ahead
7078 * and delete it for the remove.
7080 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject
));
7084 * We got a remove for a child partition PDO which is not actually missing.
7085 * So we will NOT actually delete it.
7087 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject
));
7090 // Reacquire the remove lock for the next time this comes around.
7093 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7096 // the device wasn't missing so it's not really been removed.
7099 commonExtension
->IsRemoved
= NO_REMOVE
;
7101 IoInvalidateDeviceRelations(
7102 commonExtension
->PartitionZeroExtension
->LowerPdo
,
7105 proceedWithRemove
= FALSE
;
7111 if (proceedWithRemove
){
7114 * Call the class driver's remove handler.
7115 * All this is supposed to do is clean up its data and device interfaces.
7117 ASSERT(commonExtension
->DevInfo
->ClassRemoveDevice
);
7118 status
= commonExtension
->DevInfo
->ClassRemoveDevice(DeviceObject
, RemoveType
);
7119 ASSERT(NT_SUCCESS(status
));
7120 status
= STATUS_SUCCESS
;
7122 if (commonExtension
->IsFdo
){
7124 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
7126 ClasspDisableTimer(fdoExtension
->DeviceObject
);
7128 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7130 PPHYSICAL_DEVICE_EXTENSION child
;
7133 // Cleanup the media detection resources now that the class driver
7134 // has stopped it's timer (if any) and we can be sure they won't
7135 // call us to do detection again.
7138 ClassCleanupMediaChangeDetection(fdoExtension
);
7141 // Cleanup any Failure Prediction stuff
7143 if (fdoExtension
->FailurePredictionInfo
) {
7144 ExFreePool(fdoExtension
->FailurePredictionInfo
);
7145 fdoExtension
->FailurePredictionInfo
= NULL
;
7149 * Ordinarily all child PDOs will be removed by the time
7150 * that the parent gets the REMOVE_DEVICE.
7151 * However, if a child PDO has been created but has not
7152 * been announced in a QueryDeviceRelations, then it is
7153 * just a private data structure unknown to pnp, and we have
7154 * to delete it ourselves.
7156 ClassAcquireChildLock(fdoExtension
);
7157 while (child
= ClassRemoveChild(fdoExtension
, NULL
, FALSE
)){
7160 // Yank the pdo. This routine will unlink the device from the
7161 // pdo list so NextPdo will point to the next one when it's
7164 child
->IsMissing
= TRUE
;
7165 ClassRemoveDevice(child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
7167 ClassReleaseChildLock(fdoExtension
);
7169 else if (RemoveType
== IRP_MN_SURPRISE_REMOVAL
){
7171 * This is a surprise-remove on the parent FDO.
7172 * We will mark the child PDOs as missing so that they
7173 * will actually get deleted when they get a REMOVE_DEVICE.
7175 ClassMarkChildrenMissing(fdoExtension
);
7178 ClasspFreeReleaseRequest(DeviceObject
);
7180 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7183 // Free FDO-specific data structs
7185 if (fdoExtension
->PrivateFdoData
){
7187 DestroyAllTransferPackets(DeviceObject
);
7189 ExFreePool(fdoExtension
->PrivateFdoData
);
7190 fdoExtension
->PrivateFdoData
= NULL
;
7193 if (commonExtension
->DeviceName
.Buffer
) {
7194 ExFreePool(commonExtension
->DeviceName
.Buffer
);
7195 RtlInitUnicodeString(&commonExtension
->DeviceName
, NULL
);
7198 if (fdoExtension
->AdapterDescriptor
) {
7199 ExFreePool(fdoExtension
->AdapterDescriptor
);
7200 fdoExtension
->AdapterDescriptor
= NULL
;
7203 if (fdoExtension
->DeviceDescriptor
) {
7204 ExFreePool(fdoExtension
->DeviceDescriptor
);
7205 fdoExtension
->DeviceDescriptor
= NULL
;
7209 // Detach our device object from the stack - there's no reason
7210 // to hold off our cleanup any longer.
7213 IoDetachDevice(lowerDeviceObject
);
7218 * This is a child partition PDO.
7219 * We have already determined that it was previously marked
7220 * as missing. So if this is a REMOVE_DEVICE, we will actually
7223 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7224 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
7225 commonExtension
->PartitionZeroExtension
;
7226 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
7227 (PPHYSICAL_DEVICE_EXTENSION
) commonExtension
;
7230 // See if this device is in the child list (if this was a suprise
7231 // removal it might be) and remove it.
7234 ClassRemoveChild(fdoExtension
, pdoExtension
, TRUE
);
7238 commonExtension
->PartitionLength
.QuadPart
= 0;
7240 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7241 IoDeleteDevice(DeviceObject
);
7245 return STATUS_SUCCESS
;
7246 } // end ClassRemoveDevice()
7248 /*++////////////////////////////////////////////////////////////////////////////
7250 ClassGetDriverExtension()
7252 Routine Description:
7254 This routine will return the classpnp's driver extension.
7258 DriverObject - the driver object for which to get classpnp's extension
7262 Either NULL if none, or a pointer to the driver extension
7266 PCLASS_DRIVER_EXTENSION
7268 ClassGetDriverExtension(
7269 IN PDRIVER_OBJECT DriverObject
7272 return IoGetDriverObjectExtension(DriverObject
, CLASS_DRIVER_EXTENSION_KEY
);
7273 } // end ClassGetDriverExtension()
7275 /*++////////////////////////////////////////////////////////////////////////////
7279 Routine Description:
7281 This routine wraps the class driver's start io routine. If the device
7282 is being removed it will complete any requests with
7283 STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
7294 IN PDEVICE_OBJECT DeviceObject
,
7298 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7301 // We're already holding the remove lock so just check the variable and
7302 // see what's going on.
7305 if(commonExtension
->IsRemoved
) {
7307 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
7309 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7310 ClassReleaseRemoveLock(DeviceObject
, Irp
);
7311 ClassCompleteRequest(DeviceObject
, Irp
, IO_DISK_INCREMENT
);
7312 IoStartNextPacket(DeviceObject
, FALSE
);
7313 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7317 commonExtension
->DriverExtension
->InitData
.ClassStartIo(
7322 } // ClasspStartIo()
7324 /*++////////////////////////////////////////////////////////////////////////////
7326 ClassUpdateInformationInRegistry()
7328 Routine Description:
7330 This routine has knowledge about the layout of the device map information
7331 in the registry. It will update this information to include a value
7332 entry specifying the dos device name that is assumed to get assigned
7333 to this NT device name. For more information on this assigning of the
7334 dos device name look in the drive support routine in the hal that assigns
7337 Since some versions of some device's firmware did not work and some
7338 vendors did not bother to follow the specification, the entire inquiry
7339 information must also be stored in the registry so than someone can
7340 figure out the firmware version.
7344 DeviceObject - A pointer to the device object for the tape device.
7354 ClassUpdateInformationInRegistry(
7355 IN PDEVICE_OBJECT Fdo
,
7356 IN PCHAR DeviceName
,
7357 IN ULONG DeviceNumber
,
7358 IN PINQUIRYDATA InquiryData
,
7359 IN ULONG InquiryDataLength
7362 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
7364 SCSI_ADDRESS scsiAddress
;
7365 OBJECT_ATTRIBUTES objectAttributes
;
7368 UNICODE_STRING unicodeName
;
7369 UNICODE_STRING unicodeRegistryPath
;
7370 UNICODE_STRING unicodeData
;
7372 IO_STATUS_BLOCK ioStatus
;
7378 fdoExtension
= Fdo
->DeviceExtension
;
7381 RtlZeroMemory(&unicodeName
, sizeof(UNICODE_STRING
));
7382 RtlZeroMemory(&unicodeData
, sizeof(UNICODE_STRING
));
7383 RtlZeroMemory(&unicodeRegistryPath
, sizeof(UNICODE_STRING
));
7388 // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
7391 ClassSendDeviceIoControlSynchronous(
7392 IOCTL_SCSI_GET_ADDRESS
,
7396 sizeof(SCSI_ADDRESS
),
7401 if (!NT_SUCCESS(ioStatus
.Status
)) {
7403 status
= ioStatus
.Status
;
7405 "UpdateInformationInRegistry: Get Address failed %lx\n",
7412 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
7413 scsiAddress
.PortNumber
,
7415 scsiAddress
.TargetId
,
7421 // Allocate a buffer for the reg. spooge.
7424 buffer
= ExAllocatePoolWithTag(PagedPool
, 1024, '6BcS');
7426 if (buffer
== NULL
) {
7429 // There is not return value for this. Since this is done at
7430 // claim device time (currently only system initialization) getting
7431 // the registry information correct will be the least of the worries.
7438 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
7439 scsiAddress
.PortNumber
,
7441 scsiAddress
.TargetId
,
7444 RtlInitString(&string
, buffer
);
7446 status
= RtlAnsiStringToUnicodeString(&unicodeRegistryPath
,
7450 if (!NT_SUCCESS(status
)) {
7455 // Open the registry key for the scsi information for this
7456 // scsibus, target, lun.
7459 InitializeObjectAttributes(&objectAttributes
,
7460 &unicodeRegistryPath
,
7461 OBJ_CASE_INSENSITIVE
,
7465 status
= ZwOpenKey(&targetKey
,
7466 KEY_READ
| KEY_WRITE
,
7469 if (!NT_SUCCESS(status
)) {
7474 // Now construct and attempt to create the registry value
7475 // specifying the device name in the appropriate place in the
7479 RtlInitUnicodeString(&unicodeName
, L
"DeviceName");
7481 sprintf(buffer
, "%s%d", DeviceName
, DeviceNumber
);
7482 RtlInitString(&string
, buffer
);
7483 status
= RtlAnsiStringToUnicodeString(&unicodeData
,
7486 if (NT_SUCCESS(status
)) {
7487 status
= ZwSetValueKey(targetKey
,
7492 unicodeData
.Length
);
7496 // if they sent in data, update the registry
7499 if (InquiryDataLength
) {
7501 ASSERT(InquiryData
);
7503 RtlInitUnicodeString(&unicodeName
, L
"InquiryData");
7504 status
= ZwSetValueKey(targetKey
,
7512 // that's all, except to clean up.
7516 if (unicodeData
.Buffer
) {
7517 RtlFreeUnicodeString(&unicodeData
);
7519 if (unicodeRegistryPath
.Buffer
) {
7520 RtlFreeUnicodeString(&unicodeRegistryPath
);
7531 } // end ClassUpdateInformationInRegistry()
7533 /*++////////////////////////////////////////////////////////////////////////////
7535 ClasspSendSynchronousCompletion()
7537 Routine Description:
7539 This completion routine will set the user event in the irp after
7540 freeing the irp and the associated MDL (if any).
7544 DeviceObject - the device object which requested the completion routine
7546 Irp - the irp being completed
7552 STATUS_MORE_PROCESSING_REQUIRED
7556 ClasspSendSynchronousCompletion(
7557 IN PDEVICE_OBJECT DeviceObject
,
7562 DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
7563 DeviceObject
, Irp
, Context
));
7565 // First set the status and information fields in the io status block
7566 // provided by the caller.
7569 *(Irp
->UserIosb
) = Irp
->IoStatus
;
7572 // Unlock the pages for the data buffer.
7575 if(Irp
->MdlAddress
) {
7576 MmUnlockPages(Irp
->MdlAddress
);
7577 IoFreeMdl(Irp
->MdlAddress
);
7581 // Signal the caller's event.
7584 KeSetEvent(Irp
->UserEvent
, IO_NO_INCREMENT
, FALSE
);
7587 // Free the MDL and the IRP.
7592 return STATUS_MORE_PROCESSING_REQUIRED
;
7593 } // end ClasspSendSynchronousCompletion()
7597 ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
7601 ClasspRegisterMountedDeviceInterface(
7602 IN PDEVICE_OBJECT DeviceObject
7606 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7607 BOOLEAN isFdo
= commonExtension
->IsFdo
;
7610 UNICODE_STRING interfaceName
;
7616 PFUNCTIONAL_DEVICE_EXTENSION functionalExtension
;
7618 functionalExtension
=
7619 (PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
;
7620 pdo
= functionalExtension
->LowerPdo
;
7625 status
= IoRegisterDeviceInterface(
7627 &MOUNTDEV_MOUNTED_DEVICE_GUID
,
7632 if(NT_SUCCESS(status
)) {
7635 // Copy the interface name before setting the interface state - the
7636 // name is needed by the components we notify.
7639 commonExtension
->MountedDeviceInterfaceName
= interfaceName
;
7640 status
= IoSetDeviceInterfaceState(&interfaceName
, TRUE
);
7642 if(!NT_SUCCESS(status
)) {
7643 RtlFreeUnicodeString(&interfaceName
);
7647 if(!NT_SUCCESS(status
)) {
7648 RtlInitUnicodeString(&(commonExtension
->MountedDeviceInterfaceName
),
7652 } // end ClasspRegisterMountedDeviceInterface()
7654 /*++////////////////////////////////////////////////////////////////////////////
7656 ClassSendDeviceIoControlSynchronous()
7658 Routine Description:
7660 This routine is based upon IoBuildDeviceIoControlRequest(). It has been
7661 modified to reduce code and memory by not double-buffering the io, using
7662 the same buffer for both input and output, allocating and deallocating
7663 the mdl on behalf of the caller, and waiting for the io to complete.
7665 This routine also works around the rare cases in which APC's are disabled.
7666 Since IoBuildDeviceIoControl() used APC's to signal completion, this had
7667 led to a number of difficult-to-detect hangs, where the irp was completed,
7668 but the event passed to IoBuild..() was still being waited upon by the
7673 IoControlCode - the IOCTL to send
7675 TargetDeviceObject - the device object that should handle the ioctl
7677 Buffer - the input and output buffer, or NULL if no input/output
7679 InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
7681 OutputBufferLength - the number of bytes to be filled in upon success
7683 InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
7685 IoStatus - the status block that contains the results of the operation
7693 ClassSendDeviceIoControlSynchronous(
7694 IN ULONG IoControlCode
,
7695 IN PDEVICE_OBJECT TargetDeviceObject
,
7696 IN OUT PVOID Buffer OPTIONAL
,
7697 IN ULONG InputBufferLength
,
7698 IN ULONG OutputBufferLength
,
7699 IN BOOLEAN InternalDeviceIoControl
,
7700 OUT PIO_STATUS_BLOCK IoStatus
7704 PIO_STACK_LOCATION irpSp
;
7710 method
= IoControlCode
& 3;
7713 #if DBG // Begin Argument Checking (nop in fre version)
7715 ASSERT(ARGUMENT_PRESENT(IoStatus
));
7717 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7718 ASSERT(ARGUMENT_PRESENT(Buffer
));
7721 ASSERT(!ARGUMENT_PRESENT(Buffer
));
7726 // Begin by allocating the IRP for this request. Do not charge quota to
7727 // the current process for this IRP.
7730 irp
= IoAllocateIrp(TargetDeviceObject
->StackSize
, FALSE
);
7732 (*IoStatus
).Information
= 0;
7733 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7738 // Get a pointer to the stack location of the first driver which will be
7739 // invoked. This is where the function codes and the parameters are set.
7742 irpSp
= IoGetNextIrpStackLocation(irp
);
7745 // Set the major function code based on the type of device I/O control
7746 // function the caller has specified.
7749 if (InternalDeviceIoControl
) {
7750 irpSp
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
7752 irpSp
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
7756 // Copy the caller's parameters to the service-specific portion of the
7757 // IRP for those parameters that are the same for all four methods.
7760 irpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
= OutputBufferLength
;
7761 irpSp
->Parameters
.DeviceIoControl
.InputBufferLength
= InputBufferLength
;
7762 irpSp
->Parameters
.DeviceIoControl
.IoControlCode
= IoControlCode
;
7765 // Get the method bits from the I/O control code to determine how the
7766 // buffers are to be passed to the driver.
7771 case METHOD_BUFFERED
: {
7772 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7774 irp
->AssociatedIrp
.SystemBuffer
=
7775 ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
7776 max(InputBufferLength
, OutputBufferLength
),
7777 CLASS_TAG_DEVICE_CONTROL
7780 if (irp
->AssociatedIrp
.SystemBuffer
== NULL
) {
7782 (*IoStatus
).Information
= 0;
7783 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7787 if (InputBufferLength
!= 0) {
7788 RtlCopyMemory(irp
->AssociatedIrp
.SystemBuffer
,
7792 } // end of buffering
7794 irp
->UserBuffer
= Buffer
;
7799 case METHOD_IN_DIRECT
:
7800 case METHOD_OUT_DIRECT
: {
7803 if (InputBufferLength
!= 0) {
7804 irp
->AssociatedIrp
.SystemBuffer
= Buffer
;
7807 if (OutputBufferLength
!= 0) {
7809 irp
->MdlAddress
= IoAllocateMdl(Buffer
,
7814 if (irp
->MdlAddress
== NULL
) {
7816 (*IoStatus
).Information
= 0;
7817 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7821 if (method
== METHOD_IN_DIRECT
) {
7822 MmProbeAndLockPages(irp
->MdlAddress
,
7825 } else if (method
== METHOD_OUT_DIRECT
) {
7826 MmProbeAndLockPages(irp
->MdlAddress
,
7830 ASSERT(!"If other methods reach here, code is out of date");
7837 case METHOD_NEITHER
: {
7839 ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
7840 IoStatus
->Information
= 0;
7841 IoStatus
->Status
= STATUS_NOT_SUPPORTED
;
7845 } // end of switch(method)
7847 irp
->Tail
.Overlay
.Thread
= PsGetCurrentThread();
7850 // send the irp synchronously
7853 ClassSendIrpSynchronous(TargetDeviceObject
, irp
);
7856 // copy the iostatus block for the caller
7859 *IoStatus
= irp
->IoStatus
;
7862 // free any allocated resources
7866 case METHOD_BUFFERED
: {
7868 ASSERT(irp
->UserBuffer
== Buffer
);
7871 // first copy the buffered result, if any
7872 // Note that there are no security implications in
7873 // not checking for success since only drivers can
7874 // call into this routine anyways...
7877 if (OutputBufferLength
!= 0) {
7878 RtlCopyMemory(Buffer
, // irp->UserBuffer
7879 irp
->AssociatedIrp
.SystemBuffer
,
7885 // then free the memory allocated to buffer the io
7888 if ((InputBufferLength
!=0) || (OutputBufferLength
!= 0)) {
7889 ExFreePool(irp
->AssociatedIrp
.SystemBuffer
);
7890 irp
->AssociatedIrp
.SystemBuffer
= NULL
;
7895 case METHOD_IN_DIRECT
:
7896 case METHOD_OUT_DIRECT
: {
7899 // we alloc a mdl if there is an output buffer specified
7900 // free it here after unlocking the pages
7903 if (OutputBufferLength
!= 0) {
7904 ASSERT(irp
->MdlAddress
!= NULL
);
7905 MmUnlockPages(irp
->MdlAddress
);
7906 IoFreeMdl(irp
->MdlAddress
);
7907 irp
->MdlAddress
= (PMDL
) NULL
;
7912 case METHOD_NEITHER
: {
7913 ASSERT(!"Code is out of date");
7919 // we always have allocated an irp. free it here.
7926 // return the io status block's status to the caller
7930 } // end ClassSendDeviceIoControlSynchronous()
7932 /*++////////////////////////////////////////////////////////////////////////////
7934 ClassForwardIrpSynchronous()
7936 Routine Description:
7938 Forwards a given irp to the next lower device object.
7942 CommonExtension - the common class extension
7944 Irp - the request to forward down the stack
7952 ClassForwardIrpSynchronous(
7953 IN PCOMMON_DEVICE_EXTENSION CommonExtension
,
7957 IoCopyCurrentIrpStackLocationToNext(Irp
);
7958 return ClassSendIrpSynchronous(CommonExtension
->LowerDeviceObject
, Irp
);
7959 } // end ClassForwardIrpSynchronous()
7961 /*++////////////////////////////////////////////////////////////////////////////
7963 ClassSendIrpSynchronous()
7965 Routine Description:
7967 This routine sends the given irp to the given device object, and waits for
7968 it to complete. On debug versions, will print out a debug message and
7969 optionally assert for "lost" irps based upon classpnp's globals
7973 TargetDeviceObject - the device object to handle this irp
7975 Irp - the request to be sent
7983 ClassSendIrpSynchronous(
7984 IN PDEVICE_OBJECT TargetDeviceObject
,
7991 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL
);
7992 ASSERT(TargetDeviceObject
!= NULL
);
7993 ASSERT(Irp
!= NULL
);
7994 ASSERT(Irp
->StackCount
>= TargetDeviceObject
->StackSize
);
7997 // ISSUE-2000/02/20-henrygab What if APCs are disabled?
7998 // May need to enter critical section before IoCallDriver()
7999 // until the event is hit?
8002 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
8003 IoSetCompletionRoutine(Irp
, ClassSignalCompletion
, &event
,
8006 status
= IoCallDriver(TargetDeviceObject
, Irp
);
8008 if (status
== STATUS_PENDING
) {
8011 LARGE_INTEGER timeout
;
8013 timeout
.QuadPart
= (LONGLONG
)(-1 * 10 * 1000 * (LONGLONG
)1000 *
8014 ClasspnpGlobals
.SecondsToWaitForIrps
);
8017 status
= KeWaitForSingleObject(&event
,
8024 if (status
== STATUS_TIMEOUT
) {
8027 // This DebugPrint should almost always be investigated by the
8028 // party who sent the irp and/or the current owner of the irp.
8029 // Synchronous Irps should not take this long (currently 30
8030 // seconds) without good reason. This points to a potentially
8031 // serious problem in the underlying device stack.
8034 DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
8035 "complete within %x seconds\n",
8036 TargetDeviceObject
, Irp
,
8037 ClasspnpGlobals
.SecondsToWaitForIrps
8040 if (ClasspnpGlobals
.BreakOnLostIrps
!= 0) {
8041 ASSERT(!" - Irp failed to complete within 30 seconds - ");
8046 } while (status
==STATUS_TIMEOUT
);
8048 KeWaitForSingleObject(&event
,
8055 status
= Irp
->IoStatus
.Status
;
8059 } // end ClassSendIrpSynchronous()
8061 /*++////////////////////////////////////////////////////////////////////////////
8065 Routine Description:
8067 This routine returns the current VPB (Volume Parameter Block) for the
8068 given device object.
8069 The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
8070 of DEVICE_OBJECT; hence this exported function.
8074 DeviceObject - the device to get the VPB for
8078 the VPB, or NULL if none.
8085 IN PDEVICE_OBJECT DeviceObject
8088 return DeviceObject
->Vpb
;
8089 } // end ClassGetVpb()
8093 ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
8097 ClasspAllocateReleaseRequest(
8098 IN PDEVICE_OBJECT Fdo
8101 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8102 PIO_STACK_LOCATION irpStack
;
8104 KeInitializeSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
));
8106 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8107 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8108 fdoExtension
->ReleaseQueueIrpFromPool
= FALSE
;
8111 // The class driver is responsible for allocating a properly sized irp,
8112 // or ClassReleaseQueue will attempt to do it on the first error.
8115 fdoExtension
->ReleaseQueueIrp
= NULL
;
8118 // Write length to SRB.
8121 fdoExtension
->ReleaseQueueSrb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
8123 return STATUS_SUCCESS
;
8124 } // end ClasspAllocateReleaseRequest()
8128 ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
8132 ClasspFreeReleaseRequest(
8133 IN PDEVICE_OBJECT Fdo
8136 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8139 ASSERT(fdoExtension
->CommonExtension
.IsRemoved
!= NO_REMOVE
);
8142 // free anything the driver allocated
8145 if (fdoExtension
->ReleaseQueueIrp
) {
8146 if (fdoExtension
->ReleaseQueueIrpFromPool
) {
8147 ExFreePool(fdoExtension
->ReleaseQueueIrp
);
8149 IoFreeIrp(fdoExtension
->ReleaseQueueIrp
);
8151 fdoExtension
->ReleaseQueueIrp
= NULL
;
8155 // free anything that we allocated
8158 if ((fdoExtension
->PrivateFdoData
) &&
8159 (fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
)) {
8161 ExFreePool(fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
);
8162 fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= FALSE
;
8163 fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
= NULL
;
8167 } // end ClasspFreeReleaseRequest()
8169 /*++////////////////////////////////////////////////////////////////////////////
8173 Routine Description:
8175 This routine issues an internal device control command
8176 to the port driver to release a frozen queue. The call
8177 is issued asynchronously as ClassReleaseQueue will be invoked
8178 from the IO completion DPC (and will have no context to
8179 wait for a synchronous call to complete).
8181 This routine must be called with the remove lock held.
8185 Fdo - The functional device object for the device with the frozen queue.
8196 IN PDEVICE_OBJECT Fdo
8199 ClasspReleaseQueue(Fdo
, NULL
);
8201 } // end ClassReleaseQueue()
8203 /*++////////////////////////////////////////////////////////////////////////////
8205 ClasspAllocateReleaseQueueIrp()
8207 Routine Description:
8209 This routine allocates the release queue irp held in classpnp's private
8210 extension. This was added to allow no-memory conditions to be more
8219 Does not grab the spinlock. Should only be called from StartDevice()
8220 routine. May be called elsewhere for poorly-behaved drivers that cause
8221 the queue to lockup before the device is started. This should *never*
8222 occur, since it's illegal to send a request to a non-started PDO. This
8223 condition is checked for in ClasspReleaseQueue().
8227 ClasspAllocateReleaseQueueIrp(
8228 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8232 UCHAR lowerStackSize
;
8235 // do an initial check w/o the spinlock
8238 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8239 return STATUS_SUCCESS
;
8243 lowerStackSize
= FdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
;
8246 // don't allocate one if one is in progress! this means whoever called
8247 // this routine didn't check if one was in progress.
8250 ASSERT(!(FdoExtension
->ReleaseQueueInProgress
));
8252 FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
=
8253 ExAllocatePoolWithTag(NonPagedPool
,
8254 IoSizeOfIrp(lowerStackSize
),
8255 CLASS_TAG_RELEASE_QUEUE
8258 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
== NULL
) {
8259 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
8260 "release queue irp\n"));
8261 return STATUS_INSUFFICIENT_RESOURCES
;
8263 IoInitializeIrp(FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
,
8264 IoSizeOfIrp(lowerStackSize
),
8266 FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= TRUE
;
8268 return STATUS_SUCCESS
;
8272 /*++////////////////////////////////////////////////////////////////////////////
8274 ClasspReleaseQueue()
8276 Routine Description:
8278 This routine issues an internal device control command
8279 to the port driver to release a frozen queue. The call
8280 is issued asynchronously as ClassReleaseQueue will be invoked
8281 from the IO completion DPC (and will have no context to
8282 wait for a synchronous call to complete).
8284 This routine must be called with the remove lock held.
8288 Fdo - The functional device object for the device with the frozen queue.
8290 ReleaseQueueIrp - If this irp is supplied then the test to determine whether
8291 a release queue request is in progress will be ignored.
8292 The irp provided must be the IRP originally allocated
8293 for release queue requests (so this parameter can only
8294 really be provided by the release queue completion
8304 IN PDEVICE_OBJECT Fdo
,
8305 IN PIRP ReleaseQueueIrp OPTIONAL
8308 PIO_STACK_LOCATION irpStack
;
8310 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8311 PDEVICE_OBJECT lowerDevice
;
8312 PSCSI_REQUEST_BLOCK srb
;
8315 lowerDevice
= fdoExtension
->CommonExtension
.LowerDeviceObject
;
8318 // we raise irql seperately so we're not swapped out or suspended
8319 // while holding the release queue irp in this routine. this lets
8320 // us release the spin lock before lowering irql.
8323 KeRaiseIrql(DISPATCH_LEVEL
, ¤tIrql
);
8325 KeAcquireSpinLockAtDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8328 // make sure that if they passed us an irp, it matches our allocated irp.
8331 ASSERT((ReleaseQueueIrp
== NULL
) ||
8332 (ReleaseQueueIrp
== fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
));
8335 // ASSERT that we've already allocated this. (should not occur)
8336 // try to allocate it anyways, then finally bugcheck if
8337 // there's still no memory...
8340 ASSERT(fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
);
8341 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8342 ClasspAllocateReleaseQueueIrp(fdoExtension
);
8344 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8345 KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL
, 0x12, (ULONG_PTR
)Fdo
, 0x0, 0x0);
8348 if ((fdoExtension
->ReleaseQueueInProgress
) && (ReleaseQueueIrp
== NULL
)) {
8351 // Someone is already using the irp - just set the flag to indicate that
8352 // we need to release the queue again.
8355 fdoExtension
->ReleaseQueueNeeded
= TRUE
;
8356 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8357 KeLowerIrql(currentIrql
);
8363 // Mark that there is a release queue in progress and drop the spinlock.
8366 fdoExtension
->ReleaseQueueInProgress
= TRUE
;
8367 if (ReleaseQueueIrp
) {
8368 irp
= ReleaseQueueIrp
;
8370 irp
= fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
;
8372 srb
= &(fdoExtension
->ReleaseQueueSrb
);
8374 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8376 ASSERT(irp
!= NULL
);
8378 irpStack
= IoGetNextIrpStackLocation(irp
);
8380 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
8382 srb
->OriginalRequest
= irp
;
8385 // Store the SRB address in next stack for port driver.
8388 irpStack
->Parameters
.Scsi
.Srb
= srb
;
8391 // If this device is removable then flush the queue. This will also
8395 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
8396 srb
->Function
= SRB_FUNCTION_FLUSH_QUEUE
;
8399 srb
->Function
= SRB_FUNCTION_RELEASE_QUEUE
;
8402 ClassAcquireRemoveLock(Fdo
, irp
);
8404 IoSetCompletionRoutine(irp
,
8405 ClassReleaseQueueCompletion
,
8411 IoCallDriver(lowerDevice
, irp
);
8413 KeLowerIrql(currentIrql
);
8417 } // end ClassReleaseQueue()
8419 /*++////////////////////////////////////////////////////////////////////////////
8421 ClassReleaseQueueCompletion()
8423 Routine Description:
8425 This routine is called when an asynchronous I/O request
8426 which was issused by the class driver completes. Examples of such requests
8427 are release queue or START UNIT. This routine releases the queue if
8428 necessary. It then frees the context and the IRP.
8432 DeviceObject - The device object for the logical unit; however since this
8433 is the top stack location the value is NULL.
8435 Irp - Supplies a pointer to the Irp to be processed.
8437 Context - Supplies the context to be used to process this request.
8445 ClassReleaseQueueCompletion(
8446 PDEVICE_OBJECT DeviceObject
,
8451 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8454 BOOLEAN releaseQueueNeeded
;
8456 DeviceObject
= Context
;
8458 fdoExtension
= DeviceObject
->DeviceExtension
;
8460 ClassReleaseRemoveLock(DeviceObject
, Irp
);
8463 // Grab the spinlock and clear the release queue in progress flag so others
8464 // can run. Save (and clear) the state of the release queue needed flag
8465 // so that we can issue a new release queue outside the spinlock.
8468 KeAcquireSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), &oldIrql
);
8470 releaseQueueNeeded
= fdoExtension
->ReleaseQueueNeeded
;
8472 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8473 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8475 KeReleaseSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), oldIrql
);
8478 // If we need a release queue then issue one now. Another processor may
8479 // have already started one in which case we'll try to issue this one after
8480 // it is done - but we should never recurse more than one deep.
8483 if(releaseQueueNeeded
) {
8484 ClasspReleaseQueue(DeviceObject
, Irp
);
8488 // Indicate the I/O system should stop processing the Irp completion.
8491 return STATUS_MORE_PROCESSING_REQUIRED
;
8493 } // ClassAsynchronousCompletion()
8495 /*++////////////////////////////////////////////////////////////////////////////
8497 ClassAcquireChildLock()
8499 Routine Description:
8501 This routine acquires the lock protecting children PDOs. It may be
8502 acquired recursively by the same thread, but must be release by the
8503 thread once for each acquisition.
8507 FdoExtension - the device whose child list is protected.
8517 ClassAcquireChildLock(
8518 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8523 if(FdoExtension
->ChildLockOwner
!= KeGetCurrentThread()) {
8524 KeWaitForSingleObject(&FdoExtension
->ChildLock
,
8525 Executive
, KernelMode
,
8528 ASSERT(FdoExtension
->ChildLockOwner
== NULL
);
8529 ASSERT(FdoExtension
->ChildLockAcquisitionCount
== 0);
8531 FdoExtension
->ChildLockOwner
= KeGetCurrentThread();
8533 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8536 FdoExtension
->ChildLockAcquisitionCount
++;
8540 /*++////////////////////////////////////////////////////////////////////////////
8542 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
8544 Routine Description:
8546 This routine releases the lock protecting children PDOs. It must be
8547 called once for each time ClassAcquireChildLock was called.
8551 FdoExtension - the device whose child list is protected
8561 ClassReleaseChildLock(
8562 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8565 ASSERT(FdoExtension
->ChildLockOwner
== KeGetCurrentThread());
8566 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8568 FdoExtension
->ChildLockAcquisitionCount
-= 1;
8570 if(FdoExtension
->ChildLockAcquisitionCount
== 0) {
8571 FdoExtension
->ChildLockOwner
= NULL
;
8572 KeSetEvent(&FdoExtension
->ChildLock
, IO_NO_INCREMENT
, FALSE
);
8576 } // end ClassReleaseChildLock(
8578 /*++////////////////////////////////////////////////////////////////////////////
8582 Routine Description:
8584 This routine will insert a new child into the head of the child list.
8588 Parent - the child's parent (contains the head of the list)
8589 Child - the child to be inserted.
8590 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8591 it's already been acquired by or on behalf of the caller
8601 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8602 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8603 IN BOOLEAN AcquireLock
8607 ClassAcquireChildLock(Parent
);
8612 // Make sure this child's not already in the list.
8615 PPHYSICAL_DEVICE_EXTENSION testChild
;
8617 for (testChild
= Parent
->CommonExtension
.ChildList
;
8619 testChild
= testChild
->CommonExtension
.ChildList
) {
8621 ASSERT(testChild
!= Child
);
8626 Child
->CommonExtension
.ChildList
= Parent
->CommonExtension
.ChildList
;
8627 Parent
->CommonExtension
.ChildList
= Child
;
8630 ClassReleaseChildLock(Parent
);
8633 } // end ClassAddChild()
8635 /*++////////////////////////////////////////////////////////////////////////////
8639 Routine Description:
8641 This routine will remove a child from the child list.
8645 Parent - the parent to be removed from.
8647 Child - the child to be removed or NULL if the first child should be
8650 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8651 it's already been acquired by or on behalf of the caller
8656 A pointer to the child which was removed or NULL if no such child could
8657 be found in the list (or if Child was NULL but the list is empty).
8660 PPHYSICAL_DEVICE_EXTENSION
8662 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8663 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8664 IN BOOLEAN AcquireLock
8668 ClassAcquireChildLock(Parent
);
8672 PCOMMON_DEVICE_EXTENSION previousChild
= &Parent
->CommonExtension
;
8675 // If the list is empty then bail out now.
8678 if(Parent
->CommonExtension
.ChildList
== NULL
) {
8684 // If the caller specified a child then find the child object before
8685 // it. If none was specified then the FDO is the child object before
8686 // the one we want to remove.
8692 // Scan through the child list to find the entry which points to
8697 ASSERT(previousChild
!= &Child
->CommonExtension
);
8699 if(previousChild
->ChildList
== Child
) {
8703 previousChild
= &previousChild
->ChildList
->CommonExtension
;
8704 } while(previousChild
!= NULL
);
8706 if(previousChild
== NULL
) {
8713 // Save the next child away then unlink it from the list.
8716 Child
= previousChild
->ChildList
;
8717 previousChild
->ChildList
= Child
->CommonExtension
.ChildList
;
8718 Child
->CommonExtension
.ChildList
= NULL
;
8722 ClassReleaseChildLock(Parent
);
8726 } // end ClassRemoveChild()
8731 ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
8735 ClasspRetryRequestDpc(
8737 IN PDEVICE_OBJECT DeviceObject
,
8742 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8743 PCOMMON_DEVICE_EXTENSION commonExtension
;
8744 PCLASS_PRIVATE_FDO_DATA fdoData
;
8745 PCLASS_RETRY_INFO retryList
;
8749 commonExtension
= DeviceObject
->DeviceExtension
;
8750 ASSERT(commonExtension
->IsFdo
);
8751 fdoExtension
= DeviceObject
->DeviceExtension
;
8752 fdoData
= fdoExtension
->PrivateFdoData
;
8755 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8758 KeQueryTickCount(&now
);
8761 // if CurrentTick is less than now
8764 // retry entire list
8768 if (now
.QuadPart
< fdoData
->Retry
.Tick
.QuadPart
) {
8770 ClasspRetryDpcTimer(fdoData
);
8775 retryList
= fdoData
->Retry
.ListHead
;
8776 fdoData
->Retry
.ListHead
= NULL
;
8777 fdoData
->Retry
.Delta
.QuadPart
= (LONGLONG
)0;
8778 fdoData
->Retry
.Tick
.QuadPart
= (LONGLONG
)0;
8782 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8784 while (retryList
!= NULL
) {
8788 irp
= CONTAINING_RECORD(retryList
, IRP
, Tail
.Overlay
.DriverContext
[0]);
8789 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: -- %p\n", irp
));
8790 retryList
= retryList
->Next
;
8792 irp
->Tail
.Overlay
.DriverContext
[0] = ULongToPtr(0xdddddddd); // invalidate data
8793 irp
->Tail
.Overlay
.DriverContext
[1] = ULongToPtr(0xdddddddd); // invalidate data
8794 irp
->Tail
.Overlay
.DriverContext
[2] = ULongToPtr(0xdddddddd); // invalidate data
8795 irp
->Tail
.Overlay
.DriverContext
[3] = ULongToPtr(0xdddddddd); // invalidate data
8798 IoCallDriver(commonExtension
->LowerDeviceObject
, irp
);
8803 } // end ClasspRetryRequestDpc()
8807 ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
8812 IN PDEVICE_OBJECT SelfDeviceObject
,
8814 IN LARGE_INTEGER TimeDelta100ns
// in 100ns units
8817 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8818 PCLASS_PRIVATE_FDO_DATA fdoData
;
8819 PCLASS_RETRY_INFO retryInfo
;
8820 PCLASS_RETRY_INFO
*previousNext
;
8821 LARGE_INTEGER delta
;
8825 // this checks we aren't destroying irps
8827 ASSERT(sizeof(CLASS_RETRY_INFO
) <= (4*sizeof(PVOID
)));
8829 fdoExtension
= SelfDeviceObject
->DeviceExtension
;
8830 fdoData
= fdoExtension
->PrivateFdoData
;
8832 if (!fdoExtension
->CommonExtension
.IsFdo
) {
8835 // this debug print/assertion should ALWAYS be investigated.
8836 // ClassRetryRequest can currently only be used by FDO's
8839 DebugPrint((ClassDebugError
, "ClassRetryRequestEx: LOST IRP %p\n", Irp
));
8840 ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
8845 if (TimeDelta100ns
.QuadPart
< 0) {
8846 ASSERT(!"ClassRetryRequest - must use positive delay");
8847 TimeDelta100ns
.QuadPart
*= -1;
8851 // prepare what we can out of the loop
8854 retryInfo
= (PCLASS_RETRY_INFO
)(&Irp
->Tail
.Overlay
.DriverContext
[0]);
8855 RtlZeroMemory(retryInfo
, sizeof(CLASS_RETRY_INFO
));
8857 delta
.QuadPart
= (TimeDelta100ns
.QuadPart
/ fdoData
->Retry
.Granularity
);
8858 if (TimeDelta100ns
.QuadPart
% fdoData
->Retry
.Granularity
) {
8859 delta
.QuadPart
++; // round up to next tick
8861 if (delta
.QuadPart
== (LONGLONG
)0) {
8862 delta
.QuadPart
= MINIMUM_RETRY_UNITS
;
8866 // now determine if we should fire another DPC or not
8869 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8872 // always add request to the list
8875 retryInfo
->Next
= fdoData
->Retry
.ListHead
;
8876 fdoData
->Retry
.ListHead
= retryInfo
;
8878 if (fdoData
->Retry
.Delta
.QuadPart
== (LONGLONG
)0) {
8880 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: +++ %p\n", Irp
));
8883 // must be exactly one item on list
8886 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8887 ASSERT(fdoData
->Retry
.ListHead
->Next
== NULL
);
8890 // if currentDelta is zero, always fire a DPC
8893 KeQueryTickCount(&fdoData
->Retry
.Tick
);
8894 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8895 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8896 ClasspRetryDpcTimer(fdoData
);
8898 } else if (delta
.QuadPart
> fdoData
->Retry
.Delta
.QuadPart
) {
8901 // if delta is greater than the list's current delta,
8902 // increase the DPC handling time by difference
8903 // and update the delta to new larger value
8904 // allow the DPC to re-fire itself if needed
8907 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8910 // must be at least two items on list
8913 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8914 ASSERT(fdoData
->Retry
.ListHead
->Next
!= NULL
);
8916 fdoData
->Retry
.Tick
.QuadPart
-= fdoData
->Retry
.Delta
.QuadPart
;
8917 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8919 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8924 // just inserting it on the list was enough
8927 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8932 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8935 } // end ClassRetryRequest()
8939 ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
8943 ClasspRetryDpcTimer(
8944 IN PCLASS_PRIVATE_FDO_DATA FdoData
8949 ASSERT(FdoData
->Retry
.Tick
.QuadPart
!= (LONGLONG
)0);
8950 ASSERT(FdoData
->Retry
.ListHead
!= NULL
); // never fire an empty list
8953 // fire == (CurrentTick - now) * (100ns per tick)
8955 // NOTE: Overflow is nearly impossible and is ignored here
8958 KeQueryTickCount(&fire
);
8959 fire
.QuadPart
= FdoData
->Retry
.Tick
.QuadPart
- fire
.QuadPart
;
8960 fire
.QuadPart
*= FdoData
->Retry
.Granularity
;
8963 // fire is now multiples of 100ns until should fire the timer.
8964 // if timer should already have expired, or would fire too quickly,
8965 // fire it in some arbitrary number of ticks to prevent infinitely
8969 if (fire
.QuadPart
< MINIMUM_RETRY_UNITS
) {
8970 fire
.QuadPart
= MINIMUM_RETRY_UNITS
;
8973 DebugPrint((ClassDebugDelayedRetry
,
8974 "ClassRetry: ======= %I64x ticks\n",
8978 // must use negative to specify relative time to fire
8981 fire
.QuadPart
= fire
.QuadPart
* ((LONGLONG
)-1);
8984 // set the timer, since this is the first addition
8987 KeSetTimerEx(&FdoData
->Retry
.Timer
, fire
, 0, &FdoData
->Retry
.Dpc
);
8990 } // end ClasspRetryDpcTimer()
8993 ClasspInitializeHotplugInfo(
8994 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8997 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
8998 DEVICE_REMOVAL_POLICY deviceRemovalPolicy
;
9000 ULONG resultLength
= 0;
9001 ULONG writeCacheOverride
;
9006 // start with some default settings
9008 RtlZeroMemory(&(fdoData
->HotplugInfo
), sizeof(STORAGE_HOTPLUG_INFO
));
9011 // set the size (aka version)
9014 fdoData
->HotplugInfo
.Size
= sizeof(STORAGE_HOTPLUG_INFO
);
9017 // set if the device has removable media
9020 if (FdoExtension
->DeviceDescriptor
->RemovableMedia
) {
9021 fdoData
->HotplugInfo
.MediaRemovable
= TRUE
;
9023 fdoData
->HotplugInfo
.MediaRemovable
= FALSE
;
9027 // this refers to devices which, for reasons not yet understood,
9028 // do not fail PREVENT_MEDIA_REMOVAL requests even though they
9029 // have no way to lock the media into the drive. this allows
9030 // the filesystems to turn off delayed-write caching for these
9034 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
9035 FDO_HACK_CANNOT_LOCK_MEDIA
)) {
9036 fdoData
->HotplugInfo
.MediaHotplug
= TRUE
;
9038 fdoData
->HotplugInfo
.MediaHotplug
= FALSE
;
9043 // Look into the registry to see if the user has chosen
9044 // to override the default setting for the removal policy
9047 RtlZeroMemory(&deviceRemovalPolicy
, sizeof(DEVICE_REMOVAL_POLICY
));
9049 ClassGetDeviceParameter(FdoExtension
,
9050 CLASSP_REG_SUBKEY_NAME
,
9051 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
9052 (PULONG
)&deviceRemovalPolicy
);
9054 if (deviceRemovalPolicy
== 0)
9057 // Query the default removal policy from the kernel
9060 status
= IoGetDeviceProperty(FdoExtension
->LowerPdo
,
9061 DevicePropertyRemovalPolicy
,
9062 sizeof(DEVICE_REMOVAL_POLICY
),
9063 (PVOID
)&deviceRemovalPolicy
,
9065 if (!NT_SUCCESS(status
))
9070 if (resultLength
!= sizeof(DEVICE_REMOVAL_POLICY
))
9072 return STATUS_UNSUCCESSFUL
;
9077 // use this info to set the DeviceHotplug setting
9078 // don't rely on DeviceCapabilities, since it can't properly
9079 // determine device relations, etc. let the kernel figure this
9080 // stuff out instead.
9083 if (deviceRemovalPolicy
== RemovalPolicyExpectSurpriseRemoval
) {
9084 fdoData
->HotplugInfo
.DeviceHotplug
= TRUE
;
9086 fdoData
->HotplugInfo
.DeviceHotplug
= FALSE
;
9090 // this refers to the *filesystem* caching, but has to be included
9091 // here since it's a per-device setting. this may change to be
9092 // stored by the system in the future.
9095 writeCacheOverride
= FALSE
;
9096 ClassGetDeviceParameter(FdoExtension
,
9097 CLASSP_REG_SUBKEY_NAME
,
9098 CLASSP_REG_WRITE_CACHE_VALUE_NAME
,
9099 &writeCacheOverride
);
9101 if (writeCacheOverride
) {
9102 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= TRUE
;
9104 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= FALSE
;
9107 return STATUS_SUCCESS
;
9111 ClasspScanForClassHacks(
9112 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
9119 // remove invalid flags and save
9122 CLEAR_FLAG(Data
, FDO_HACK_INVALID_FLAGS
);
9123 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, Data
);
9128 ClasspScanForSpecialInRegistry(
9129 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
9132 HANDLE deviceParameterHandle
; // device instance key
9133 HANDLE classParameterHandle
; // classpnp subkey
9134 OBJECT_ATTRIBUTES objectAttributes
;
9135 UNICODE_STRING subkeyName
;
9139 // seeded in the ENUM tree by ClassInstaller
9142 RTL_QUERY_REGISTRY_TABLE queryTable
[2]; // null terminated array
9146 deviceParameterHandle
= NULL
;
9147 classParameterHandle
= NULL
;
9150 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
9151 PLUGPLAY_REGKEY_DEVICE
,
9153 &deviceParameterHandle
9156 if (!NT_SUCCESS(status
)) {
9157 goto cleanupScanForSpecial
;
9160 RtlInitUnicodeString(&subkeyName
, CLASSP_REG_SUBKEY_NAME
);
9161 InitializeObjectAttributes(&objectAttributes
,
9163 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
9164 deviceParameterHandle
,
9168 status
= ZwOpenKey( &classParameterHandle
,
9173 if (!NT_SUCCESS(status
)) {
9174 goto cleanupScanForSpecial
;
9178 // Zero out the memory
9181 RtlZeroMemory(&queryTable
[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
9184 // Setup the structure to read
9187 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
9188 queryTable
[0].Name
= CLASSP_REG_HACK_VALUE_NAME
;
9189 queryTable
[0].EntryContext
= &deviceHacks
;
9190 queryTable
[0].DefaultType
= REG_DWORD
;
9191 queryTable
[0].DefaultData
= &deviceHacks
;
9192 queryTable
[0].DefaultLength
= 0;
9198 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
9199 (PWSTR
)classParameterHandle
,
9204 if (!NT_SUCCESS(status
)) {
9205 goto cleanupScanForSpecial
;
9209 // remove unknown values and save...
9212 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
9213 "Classpnp => ScanForSpecial: HackFlags %#08x\n",
9216 CLEAR_FLAG(deviceHacks
, FDO_HACK_INVALID_FLAGS
);
9217 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, deviceHacks
);
9220 cleanupScanForSpecial
:
9222 if (deviceParameterHandle
) {
9223 ZwClose(deviceParameterHandle
);
9226 if (classParameterHandle
) {
9227 ZwClose(classParameterHandle
);
9231 // we should modify the system hive to include another key for us to grab
9232 // settings from. in this case: Classpnp\HackFlags
9234 // the use of a DWORD value for the HackFlags allows 32 hacks w/o
9235 // significant use of the registry, and also reduces OEM exposure.
9237 // definition of bit flags:
9238 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
9239 // cannot actually prevent removal.
9240 // 0x00000002 -- Device hard-hangs or times out for GESN requests.
9241 // 0xfffffffc -- Currently reserved, may be used later.