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
;
95 /*++////////////////////////////////////////////////////////////////////////////
101 This routine is called by a class driver during its
102 DriverEntry routine to initialize the driver.
106 Argument1 - Driver Object.
107 Argument2 - Registry Path.
108 InitializationData - Device-specific driver's initialization data.
112 A valid return code for a DriverEntry routine.
120 IN PCLASS_INIT_DATA InitializationData
123 PDRIVER_OBJECT DriverObject
= Argument1
;
124 PUNICODE_STRING RegistryPath
= Argument2
;
126 PCLASS_DRIVER_EXTENSION driverExtension
;
132 DebugPrint((3,"\n\nSCSI Class Driver\n"));
134 ClasspInitializeDebugGlobals();
137 // Validate the length of this structure. This is effectively a
141 if (InitializationData
->InitializationDataSize
!= sizeof(CLASS_INIT_DATA
)) {
144 // This DebugPrint is to help third-party driver writers
147 DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
148 return (ULONG
) STATUS_REVISION_MISMATCH
;
152 // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
153 // are not required entry points.
156 if ((!InitializationData
->FdoData
.ClassDeviceControl
) ||
157 (!((InitializationData
->FdoData
.ClassReadWriteVerification
) ||
158 (InitializationData
->ClassStartIo
))) ||
159 (!InitializationData
->ClassAddDevice
) ||
160 (!InitializationData
->FdoData
.ClassStartDevice
)) {
163 // This DebugPrint is to help third-party driver writers
167 "ClassInitialize: Class device-specific driver missing required "
170 return (ULONG
) STATUS_REVISION_MISMATCH
;
173 if ((InitializationData
->ClassEnumerateDevice
) &&
174 ((!InitializationData
->PdoData
.ClassDeviceControl
) ||
175 (!InitializationData
->PdoData
.ClassStartDevice
) ||
176 (!((InitializationData
->PdoData
.ClassReadWriteVerification
) ||
177 (InitializationData
->ClassStartIo
))))) {
180 // This DebugPrint is to help third-party driver writers
183 DebugPrint((0, "ClassInitialize: Class device-specific missing "
184 "required PDO entry\n"));
186 return (ULONG
) STATUS_REVISION_MISMATCH
;
189 if((InitializationData
->FdoData
.ClassStopDevice
== NULL
) ||
190 ((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
191 (InitializationData
->PdoData
.ClassStopDevice
== NULL
))) {
194 // This DebugPrint is to help third-party driver writers
197 DebugPrint((0, "ClassInitialize: Class device-specific missing "
198 "required PDO entry\n"));
200 return (ULONG
) STATUS_REVISION_MISMATCH
;
204 // Setup the default power handlers if the class driver didn't provide
208 if(InitializationData
->FdoData
.ClassPowerDevice
== NULL
) {
209 InitializationData
->FdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
212 if((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
213 (InitializationData
->PdoData
.ClassPowerDevice
== NULL
)) {
214 InitializationData
->PdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
218 // warn that unload is not supported
220 // ISSUE-2000/02/03-peterwie
221 // We should think about making this a fatal error.
224 if(InitializationData
->ClassUnload
== NULL
) {
227 // This DebugPrint is to help third-party driver writers
230 DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
235 // Create an extension for the driver object
238 status
= IoAllocateDriverObjectExtension(DriverObject
,
239 CLASS_DRIVER_EXTENSION_KEY
,
240 sizeof(CLASS_DRIVER_EXTENSION
),
243 if(NT_SUCCESS(status
)) {
246 // Copy the registry path into the driver extension so we can use it later
249 driverExtension
->RegistryPath
.Length
= RegistryPath
->Length
;
250 driverExtension
->RegistryPath
.MaximumLength
= RegistryPath
->MaximumLength
;
252 driverExtension
->RegistryPath
.Buffer
=
253 ExAllocatePoolWithTag(PagedPool
,
254 RegistryPath
->MaximumLength
,
257 if(driverExtension
->RegistryPath
.Buffer
== NULL
) {
259 status
= STATUS_INSUFFICIENT_RESOURCES
;
263 RtlCopyUnicodeString(
264 &(driverExtension
->RegistryPath
),
268 // Copy the initialization data into the driver extension so we can reuse
269 // it during our add device routine
273 &(driverExtension
->InitData
),
275 sizeof(CLASS_INIT_DATA
));
277 driverExtension
->DeviceCount
= 0;
279 } else if (status
== STATUS_OBJECT_NAME_COLLISION
) {
282 // The extension already exists - get a pointer to it
285 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
286 CLASS_DRIVER_EXTENSION_KEY
);
288 ASSERT(driverExtension
!= NULL
);
292 DebugPrint((1, "ClassInitialize: Class driver extension could not be "
293 "allocated %lx\n", status
));
298 // Update driver object with entry points.
301 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = ClassCreateClose
;
302 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = ClassCreateClose
;
303 DriverObject
->MajorFunction
[IRP_MJ_READ
] = ClassReadWrite
;
304 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = ClassReadWrite
;
305 DriverObject
->MajorFunction
[IRP_MJ_SCSI
] = ClassInternalIoControl
;
306 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = ClassDeviceControlDispatch
;
307 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = ClassShutdownFlush
;
308 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = ClassShutdownFlush
;
309 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = ClassDispatchPnp
;
310 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = ClassDispatchPower
;
311 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = ClassSystemControl
;
313 if (InitializationData
->ClassStartIo
) {
314 DriverObject
->DriverStartIo
= ClasspStartIo
;
317 if ((InitializationData
->ClassUnload
) && (ClassPnpAllowUnload
== TRUE
)) {
318 DriverObject
->DriverUnload
= ClassUnload
;
320 DriverObject
->DriverUnload
= NULL
;
323 DriverObject
->DriverExtension
->AddDevice
= ClassAddDevice
;
325 DbgPrint("Driver is ready to go\n");
326 status
= STATUS_SUCCESS
;
328 } // end ClassInitialize()
330 /*++////////////////////////////////////////////////////////////////////////////
336 This routine is allows the caller to do any extra initialization or
337 setup that is not done in ClassInitialize. The operation is
338 controlled by the GUID that is passed and the contents of the Data
339 parameter is dependent upon the GUID.
341 This is the list of supported operations:
343 Guid - GUID_CLASSPNP_QUERY_REGINFOEX
344 Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
346 Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
347 callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
348 former callback allows the driver to specify the name of the
365 IN PDRIVER_OBJECT DriverObject
,
370 PCLASS_DRIVER_EXTENSION driverExtension
;
376 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
377 CLASS_DRIVER_EXTENSION_KEY
379 if (IsEqualGUID(Guid
, &ClassGuidQueryRegInfoEx
))
381 PCLASS_QUERY_WMI_REGINFO_EX_LIST List
;
384 // Indicate the device supports PCLASS_QUERY_REGINFO_EX
385 // callback instead of PCLASS_QUERY_REGINFO callback.
387 List
= (PCLASS_QUERY_WMI_REGINFO_EX_LIST
)Data
;
389 if (List
->Size
== sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST
))
391 driverExtension
->ClassFdoQueryWmiRegInfoEx
= List
->ClassFdoQueryWmiRegInfoEx
;
392 driverExtension
->ClassPdoQueryWmiRegInfoEx
= List
->ClassPdoQueryWmiRegInfoEx
;
393 status
= STATUS_SUCCESS
;
395 status
= STATUS_INVALID_PARAMETER
;
398 status
= STATUS_NOT_SUPPORTED
;
403 } // end ClassInitializeEx()
405 /*++////////////////////////////////////////////////////////////////////////////
411 called when there are no more references to the driver. this allows
412 drivers to be updated without rebooting.
416 DriverObject - a pointer to the driver object that is being unloaded
424 IN PDRIVER_OBJECT DriverObject
427 PCLASS_DRIVER_EXTENSION driverExtension
;
432 ASSERT( DriverObject
->DeviceObject
== NULL
);
434 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
435 CLASS_DRIVER_EXTENSION_KEY
438 ASSERT(driverExtension
!= NULL
);
439 ASSERT(driverExtension
->RegistryPath
.Buffer
!= NULL
);
440 ASSERT(driverExtension
->InitData
.ClassUnload
!= NULL
);
442 DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
443 &driverExtension
->RegistryPath
));
446 // attempt to process the driver's unload routine first.
449 driverExtension
->InitData
.ClassUnload(DriverObject
);
452 // free own allocated resources and return
455 ExFreePool( driverExtension
->RegistryPath
.Buffer
);
456 driverExtension
->RegistryPath
.Buffer
= NULL
;
457 driverExtension
->RegistryPath
.Length
= 0;
458 driverExtension
->RegistryPath
.MaximumLength
= 0;
461 } // end ClassUnload()
463 /*++////////////////////////////////////////////////////////////////////////////
469 SCSI class driver add device routine. This is called by pnp when a new
470 physical device come into being.
472 This routine will call out to the class driver to verify that it should
473 own this device then will create and attach a device object and then hand
474 it to the driver to initialize and create symbolic links
478 DriverObject - a pointer to the driver object that this is being created for
479 PhysicalDeviceObject - a pointer to the physical device object
481 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
482 STATUS_SUCCESS if the creation and attachment was successful
483 status of device creation and initialization
489 IN PDRIVER_OBJECT DriverObject
,
490 IN PDEVICE_OBJECT PhysicalDeviceObject
493 PCLASS_DRIVER_EXTENSION driverExtension
=
494 IoGetDriverObjectExtension(DriverObject
,
495 CLASS_DRIVER_EXTENSION_KEY
);
501 DbgPrint("got a device\n");
502 status
= driverExtension
->InitData
.ClassAddDevice(DriverObject
,
503 PhysicalDeviceObject
);
505 } // end ClassAddDevice()
507 /*++////////////////////////////////////////////////////////////////////////////
513 Storage class driver pnp routine. This is called by the io system when
514 a PNP request is sent to the device.
518 DeviceObject - pointer to the device object
520 Irp - pointer to the io request packet
530 IN PDEVICE_OBJECT DeviceObject
,
534 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
535 BOOLEAN isFdo
= commonExtension
->IsFdo
;
537 PCLASS_DRIVER_EXTENSION driverExtension
;
538 PCLASS_INIT_DATA initData
;
539 PCLASS_DEV_INFO devInfo
;
541 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
542 PIO_STACK_LOCATION nextIrpStack
= IoGetNextIrpStackLocation(Irp
);
544 NTSTATUS status
= Irp
->IoStatus
.Status
;
545 BOOLEAN completeRequest
= TRUE
;
546 BOOLEAN lockReleased
= FALSE
;
553 // Extract all the useful information out of the driver object
557 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
558 CLASS_DRIVER_EXTENSION_KEY
);
559 if (driverExtension
){
561 initData
= &(driverExtension
->InitData
);
564 devInfo
= &(initData
->FdoData
);
566 devInfo
= &(initData
->PdoData
);
569 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
571 DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
573 irpStack
->MinorFunction
,
574 isFdo
? "fdo" : "pdo",
576 DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
578 commonExtension
->PreviousState
,
579 commonExtension
->CurrentState
));
581 switch(irpStack
->MinorFunction
) {
583 case IRP_MN_START_DEVICE
: {
586 // if this is sent to the FDO we should forward it down the
587 // attachment chain before we start the FDO.
591 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
594 status
= STATUS_SUCCESS
;
597 if (NT_SUCCESS(status
)){
598 status
= Irp
->IoStatus
.Status
= ClassPnpStartDevice(DeviceObject
);
605 case IRP_MN_QUERY_DEVICE_RELATIONS
: {
607 DEVICE_RELATION_TYPE type
=
608 irpStack
->Parameters
.QueryDeviceRelations
.Type
;
610 PDEVICE_RELATIONS deviceRelations
= NULL
;
614 if(type
== TargetDeviceRelation
) {
617 // Device relations has one entry built in to it's size.
620 status
= STATUS_INSUFFICIENT_RESOURCES
;
622 deviceRelations
= ExAllocatePoolWithTag(PagedPool
,
623 sizeof(DEVICE_RELATIONS
),
626 if(deviceRelations
!= NULL
) {
628 RtlZeroMemory(deviceRelations
,
629 sizeof(DEVICE_RELATIONS
));
631 Irp
->IoStatus
.Information
= (ULONG_PTR
) deviceRelations
;
633 deviceRelations
->Count
= 1;
634 deviceRelations
->Objects
[0] = DeviceObject
;
635 ObReferenceObject(deviceRelations
->Objects
[0]);
637 status
= STATUS_SUCCESS
;
642 // PDO's just complete enumeration requests without altering
646 status
= Irp
->IoStatus
.Status
;
651 } else if (type
== BusRelations
) {
653 ASSERT(commonExtension
->IsInitialized
);
656 // Make sure we support enumeration
659 if(initData
->ClassEnumerateDevice
== NULL
) {
662 // Just send the request down to the lower driver. Perhaps
663 // It can enumerate children.
669 // Re-enumerate the device
672 status
= ClassPnpQueryFdoRelations(DeviceObject
, Irp
);
674 if(!NT_SUCCESS(status
)) {
675 completeRequest
= TRUE
;
681 IoCopyCurrentIrpStackLocationToNext(Irp
);
682 ClassReleaseRemoveLock(DeviceObject
, Irp
);
683 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
684 completeRequest
= FALSE
;
689 case IRP_MN_QUERY_ID
: {
691 BUS_QUERY_ID_TYPE idType
= irpStack
->Parameters
.QueryId
.IdType
;
692 UNICODE_STRING unicodeString
;
697 // FDO's should just forward the query down to the lower
701 IoCopyCurrentIrpStackLocationToNext(Irp
);
702 ClassReleaseRemoveLock(DeviceObject
, Irp
);
704 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
705 completeRequest
= FALSE
;
710 // PDO's need to give an answer - this is easy for now
713 RtlInitUnicodeString(&unicodeString
, NULL
);
715 status
= ClassGetPdoId(DeviceObject
,
719 if(status
== STATUS_NOT_IMPLEMENTED
) {
721 // The driver doesn't implement this ID (whatever it is).
722 // Use the status out of the IRP so that we don't mangle a
723 // response from someone else.
726 status
= Irp
->IoStatus
.Status
;
727 } else if(NT_SUCCESS(status
)) {
728 Irp
->IoStatus
.Information
= (ULONG_PTR
) unicodeString
.Buffer
;
730 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
736 case IRP_MN_QUERY_STOP_DEVICE
:
737 case IRP_MN_QUERY_REMOVE_DEVICE
: {
739 DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
741 ((irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) ?
742 "STOP" : "REMOVE")));
745 // If this device is in use for some reason (paging, etc...)
746 // then we need to fail the request.
749 if(commonExtension
->PagingPathCount
!= 0) {
751 DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
752 "path and cannot be removed\n",
754 status
= STATUS_DEVICE_BUSY
;
759 // Check with the class driver to see if the query operation
763 if(irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) {
764 status
= devInfo
->ClassStopDevice(DeviceObject
,
765 irpStack
->MinorFunction
);
767 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
768 irpStack
->MinorFunction
);
771 if(NT_SUCCESS(status
)) {
774 // ASSERT that we never get two queries in a row, as
775 // this will severly mess up the state machine
777 ASSERT(commonExtension
->CurrentState
!= irpStack
->MinorFunction
);
778 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
779 commonExtension
->CurrentState
= irpStack
->MinorFunction
;
782 DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
783 "%s irp\n", DeviceObject
, Irp
,
784 ((irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) ?
785 "STOP" : "REMOVE")));
786 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
789 DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
790 DeviceObject
, Irp
, status
));
795 case IRP_MN_CANCEL_STOP_DEVICE
:
796 case IRP_MN_CANCEL_REMOVE_DEVICE
: {
799 // Check with the class driver to see if the query or cancel
800 // operation can succeed.
803 if(irpStack
->MinorFunction
== IRP_MN_CANCEL_STOP_DEVICE
) {
804 status
= devInfo
->ClassStopDevice(DeviceObject
,
805 irpStack
->MinorFunction
);
806 ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
807 "never be failed\n", NT_SUCCESS(status
));
809 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
810 irpStack
->MinorFunction
);
811 ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
812 "never be failed\n", NT_SUCCESS(status
));
815 Irp
->IoStatus
.Status
= status
;
818 // We got a CANCEL - roll back to the previous state only
819 // if the current state is the respective QUERY state.
822 if(((irpStack
->MinorFunction
== IRP_MN_CANCEL_STOP_DEVICE
) &&
823 (commonExtension
->CurrentState
== IRP_MN_QUERY_STOP_DEVICE
)
825 ((irpStack
->MinorFunction
== IRP_MN_CANCEL_REMOVE_DEVICE
) &&
826 (commonExtension
->CurrentState
== IRP_MN_QUERY_REMOVE_DEVICE
)
830 commonExtension
->CurrentState
=
831 commonExtension
->PreviousState
;
832 commonExtension
->PreviousState
= 0xff;
837 IoCopyCurrentIrpStackLocationToNext(Irp
);
838 ClassReleaseRemoveLock(DeviceObject
, Irp
);
839 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
840 completeRequest
= FALSE
;
842 status
= STATUS_SUCCESS
;
848 case IRP_MN_STOP_DEVICE
: {
851 // These all mean nothing to the class driver currently. The
852 // port driver will handle all queueing when necessary.
855 DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
857 (isFdo
? "fdo" : "pdo")
860 ASSERT(commonExtension
->PagingPathCount
== 0);
863 // ISSUE-2000/02/03-peterwie
864 // if we stop the timer here then it means no class driver can
865 // do i/o in its ClassStopDevice routine. This is because the
866 // retry (among other things) is tied into the tick handler
867 // and disabling retries could cause the class driver to deadlock.
868 // Currently no class driver we're aware of issues i/o in its
869 // Stop routine but this is a case we may want to defend ourself
873 if (DeviceObject
->Timer
) {
874 IoStopTimer(DeviceObject
);
877 status
= devInfo
->ClassStopDevice(DeviceObject
, IRP_MN_STOP_DEVICE
);
879 ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
880 "never be failed\n", NT_SUCCESS(status
));
883 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
886 if(NT_SUCCESS(status
)) {
887 commonExtension
->CurrentState
= irpStack
->MinorFunction
;
888 commonExtension
->PreviousState
= 0xff;
894 case IRP_MN_REMOVE_DEVICE
:
895 case IRP_MN_SURPRISE_REMOVAL
: {
897 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
898 UCHAR removeType
= irpStack
->MinorFunction
;
900 if (commonExtension
->PagingPathCount
!= 0) {
901 DBGTRACE(ClassDebugWarning
, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject
, Irp
));
905 // Release the lock for this IRP before calling in.
907 ClassReleaseRemoveLock(DeviceObject
, Irp
);
911 * If a timer was started on the device, stop it.
913 if (DeviceObject
->Timer
) {
914 IoStopTimer(DeviceObject
);
918 * "Fire-and-forget" the remove irp to the lower stack.
919 * Don't touch the irp (or the irp stack!) after this.
922 IoCopyCurrentIrpStackLocationToNext(Irp
);
923 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
924 ASSERT(NT_SUCCESS(status
));
925 completeRequest
= FALSE
;
928 status
= STATUS_SUCCESS
;
932 * Do our own cleanup and call the class driver's remove
934 * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
935 * so don't touch the extension after this.
937 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
938 commonExtension
->CurrentState
= removeType
;
939 ClassRemoveDevice(DeviceObject
, removeType
);
944 case IRP_MN_DEVICE_USAGE_NOTIFICATION
: {
946 switch(irpStack
->Parameters
.UsageNotification
.Type
) {
948 case DeviceUsageTypePaging
: {
952 if((irpStack
->Parameters
.UsageNotification
.InPath
) &&
953 (commonExtension
->CurrentState
!= IRP_MN_START_DEVICE
)) {
956 // Device isn't started. Don't allow adding a
957 // paging file, but allow a removal of one.
960 status
= STATUS_DEVICE_NOT_READY
;
964 ASSERT(commonExtension
->IsInitialized
);
967 // need to synchronize this now...
970 KeEnterCriticalRegion();
971 status
= KeWaitForSingleObject(&commonExtension
->PathCountEvent
,
972 Executive
, KernelMode
,
974 ASSERT(NT_SUCCESS(status
));
975 status
= STATUS_SUCCESS
;
978 // If the volume is removable we should try to lock it in
979 // place or unlock it once per paging path count
982 if (commonExtension
->IsFdo
){
983 status
= ClasspEjectionControl(
987 (BOOLEAN
)irpStack
->Parameters
.UsageNotification
.InPath
);
990 if (!NT_SUCCESS(status
)){
991 KeSetEvent(&commonExtension
->PathCountEvent
, IO_NO_INCREMENT
, FALSE
);
992 KeLeaveCriticalRegion();
997 // if removing last paging device, need to set DO_POWER_PAGABLE
998 // bit here, and possible re-set it below on failure.
1003 if (!irpStack
->Parameters
.UsageNotification
.InPath
&&
1004 commonExtension
->PagingPathCount
== 1
1008 // removing last paging file
1009 // must have DO_POWER_PAGABLE bits set, but only
1010 // if noone set the DO_POWER_INRUSH bit
1014 if (TEST_FLAG(DeviceObject
->Flags
, DO_POWER_INRUSH
)) {
1015 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1016 "paging file removed, but "
1017 "DO_POWER_INRUSH was set, so NOT "
1018 "setting DO_POWER_PAGABLE\n",
1019 DeviceObject
, Irp
));
1021 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1022 "paging file removed, "
1023 "setting DO_POWER_PAGABLE\n",
1024 DeviceObject
, Irp
));
1025 SET_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1032 // forward the irp before finishing handling the
1036 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1039 // now deal with the failure and success cases.
1040 // note that we are not allowed to fail the irp
1041 // once it is sent to the lower drivers.
1044 if (NT_SUCCESS(status
)) {
1046 IoAdjustPagingPathCount(
1047 &commonExtension
->PagingPathCount
,
1048 irpStack
->Parameters
.UsageNotification
.InPath
);
1050 if (irpStack
->Parameters
.UsageNotification
.InPath
) {
1051 if (commonExtension
->PagingPathCount
== 1) {
1052 DebugPrint((2, "ClassDispatchPnp (%p,%p): "
1053 "Clearing PAGABLE bit\n",
1054 DeviceObject
, Irp
));
1055 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1062 // cleanup the changes done above
1065 if (setPagable
== TRUE
) {
1066 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
1067 "PAGABLE bit due to irp failure\n",
1068 DeviceObject
, Irp
));
1069 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1074 // relock or unlock the media if needed.
1077 if (commonExtension
->IsFdo
) {
1079 ClasspEjectionControl(
1083 (BOOLEAN
)!irpStack
->Parameters
.UsageNotification
.InPath
);
1088 // set the event so the next one can occur.
1091 KeSetEvent(&commonExtension
->PathCountEvent
,
1092 IO_NO_INCREMENT
, FALSE
);
1093 KeLeaveCriticalRegion();
1097 case DeviceUsageTypeHibernation
: {
1099 IoAdjustPagingPathCount(
1100 &commonExtension
->HibernationPathCount
,
1101 irpStack
->Parameters
.UsageNotification
.InPath
1103 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1104 if (!NT_SUCCESS(status
)) {
1105 IoAdjustPagingPathCount(
1106 &commonExtension
->HibernationPathCount
,
1107 !irpStack
->Parameters
.UsageNotification
.InPath
1114 case DeviceUsageTypeDumpFile
: {
1115 IoAdjustPagingPathCount(
1116 &commonExtension
->DumpPathCount
,
1117 irpStack
->Parameters
.UsageNotification
.InPath
1119 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1120 if (!NT_SUCCESS(status
)) {
1121 IoAdjustPagingPathCount(
1122 &commonExtension
->DumpPathCount
,
1123 !irpStack
->Parameters
.UsageNotification
.InPath
1131 status
= STATUS_INVALID_PARAMETER
;
1138 case IRP_MN_QUERY_CAPABILITIES
: {
1140 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1141 DeviceObject
, Irp
));
1145 status
= ClassQueryPnpCapabilities(
1147 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
1154 PDEVICE_CAPABILITIES deviceCapabilities
;
1155 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
1156 PCLASS_PRIVATE_FDO_DATA fdoData
;
1158 fdoExtension
= DeviceObject
->DeviceExtension
;
1159 fdoData
= fdoExtension
->PrivateFdoData
;
1160 deviceCapabilities
=
1161 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
;
1164 // forward the irp before handling the special cases
1167 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1168 if (!NT_SUCCESS(status
)) {
1173 // we generally want to remove the device from the hotplug
1174 // applet, which requires the SR-OK bit to be set.
1175 // only when the user specifies that they are capable of
1176 // safely removing things do we want to clear this bit
1177 // (saved in WriteCacheEnableOverride)
1179 // setting of this bit is done either above, or by the
1182 // note: may not be started, so check we have FDO data first.
1186 fdoData
->HotplugInfo
.WriteCacheEnableOverride
) {
1187 if (deviceCapabilities
->SurpriseRemovalOK
) {
1188 DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
1189 "device capabilities due to hotplug "
1190 "device or media\n"));
1192 deviceCapabilities
->SurpriseRemovalOK
= FALSE
;
1196 } // end QUERY_CAPABILITIES for FDOs
1202 } // end QUERY_CAPABILITIES
1207 IoCopyCurrentIrpStackLocationToNext(Irp
);
1209 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1210 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
1212 completeRequest
= FALSE
;
1220 ASSERT(driverExtension
);
1221 status
= STATUS_INTERNAL_ERROR
;
1224 if (completeRequest
){
1225 Irp
->IoStatus
.Status
= status
;
1228 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1231 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1233 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject
, Irp
, commonExtension
->PreviousState
, commonExtension
->CurrentState
));
1237 * The irp is already completed so don't touch it.
1238 * This may be a remove so don't touch the device extension.
1240 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject
, Irp
));
1244 } // end ClassDispatchPnp()
1246 /*++////////////////////////////////////////////////////////////////////////////
1248 ClassPnpStartDevice()
1250 Routine Description:
1252 Storage class driver routine for IRP_MN_START_DEVICE requests.
1253 This routine kicks off any device specific initialization
1257 DeviceObject - a pointer to the device object
1259 Irp - a pointer to the io request packet
1266 NTSTATUS NTAPI
ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject
)
1268 PCLASS_DRIVER_EXTENSION driverExtension
;
1269 PCLASS_INIT_DATA initData
;
1271 PCLASS_DEV_INFO devInfo
;
1273 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1274 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
1275 BOOLEAN isFdo
= commonExtension
->IsFdo
;
1277 BOOLEAN isMountedDevice
= TRUE
;
1278 //UNICODE_STRING interfaceName;
1280 BOOLEAN timerStarted
;
1282 NTSTATUS status
= STATUS_SUCCESS
;
1286 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
1287 CLASS_DRIVER_EXTENSION_KEY
);
1289 initData
= &(driverExtension
->InitData
);
1291 devInfo
= &(initData
->FdoData
);
1293 devInfo
= &(initData
->PdoData
);
1296 ASSERT(devInfo
->ClassInitDevice
!= NULL
);
1297 ASSERT(devInfo
->ClassStartDevice
!= NULL
);
1299 if (!commonExtension
->IsInitialized
){
1302 // perform FDO/PDO specific initialization
1306 STORAGE_PROPERTY_ID propertyId
;
1309 // allocate a private extension for class data
1312 if (fdoExtension
->PrivateFdoData
== NULL
) {
1313 fdoExtension
->PrivateFdoData
=
1314 ExAllocatePoolWithTag(NonPagedPool
,
1315 sizeof(CLASS_PRIVATE_FDO_DATA
),
1316 CLASS_TAG_PRIVATE_DATA
1320 if (fdoExtension
->PrivateFdoData
== NULL
) {
1321 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
1322 "private fdo data\n"));
1323 return STATUS_INSUFFICIENT_RESOURCES
;
1327 // initialize the struct's various fields.
1330 RtlZeroMemory(fdoExtension
->PrivateFdoData
,
1331 sizeof(CLASS_PRIVATE_FDO_DATA
)
1333 KeInitializeTimer(&fdoExtension
->PrivateFdoData
->Retry
.Timer
);
1334 KeInitializeDpc(&fdoExtension
->PrivateFdoData
->Retry
.Dpc
,
1335 ClasspRetryRequestDpc
,
1337 KeInitializeSpinLock(&fdoExtension
->PrivateFdoData
->Retry
.Lock
);
1338 fdoExtension
->PrivateFdoData
->Retry
.Granularity
=
1339 KeQueryTimeIncrement();
1340 commonExtension
->Reserved4
= (ULONG_PTR
)(' GPH'); // debug aid
1343 // NOTE: the old interface allowed the class driver to allocate
1344 // this. this was unsafe for low-memory conditions. allocate one
1345 // unconditionally now, and modify our internal functions to use
1346 // our own exclusively as it is the only safe way to do this.
1349 status
= ClasspAllocateReleaseQueueIrp(fdoExtension
);
1350 if (!NT_SUCCESS(status
)) {
1351 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
1352 "private release queue irp\n"));
1357 // Call port driver to get adapter capabilities.
1360 propertyId
= StorageAdapterProperty
;
1362 status
= ClassGetDescriptor(
1363 commonExtension
->LowerDeviceObject
,
1365 &fdoExtension
->AdapterDescriptor
);
1367 if(!NT_SUCCESS(status
)) {
1370 // This DebugPrint is to help third-party driver writers
1373 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1374 "[ADAPTER] failed %lx\n", status
));
1379 // Call port driver to get device descriptor.
1382 propertyId
= StorageDeviceProperty
;
1384 status
= ClassGetDescriptor(
1385 commonExtension
->LowerDeviceObject
,
1387 &fdoExtension
->DeviceDescriptor
);
1389 if(!NT_SUCCESS(status
)) {
1392 // This DebugPrint is to help third-party driver writers
1395 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1396 "[DEVICE] failed %lx\n", status
));
1400 ClasspScanForSpecialInRegistry(fdoExtension
);
1401 ClassScanForSpecial(fdoExtension
,
1403 ClasspScanForClassHacks
);
1406 // allow perf to be re-enabled after a given number of failed IOs
1407 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
1412 ClassGetDeviceParameter(fdoExtension
,
1413 CLASSP_REG_SUBKEY_NAME
,
1414 CLASSP_REG_PERF_RESTORE_VALUE_NAME
,
1416 if (t
>= CLASS_PERF_RESTORE_MINIMUM
) {
1417 fdoExtension
->PrivateFdoData
->Perf
.ReEnableThreshhold
= t
;
1423 // compatibility comes first. writable cd media will not
1424 // get a SYNCH_CACHE on power down.
1427 if (fdoExtension
->DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) {
1428 SET_FLAG(fdoExtension
->PrivateFdoData
->HackFlags
,
1429 FDO_HACK_NO_SYNC_CACHE
);
1433 // initialize the hotplug information only after the ScanForSpecial
1434 // routines, as it relies upon the hack flags.
1437 status
= ClasspInitializeHotplugInfo(fdoExtension
);
1439 if (!NT_SUCCESS(status
)) {
1440 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
1441 "hotplug information %lx\n", status
));
1446 * Allocate/initialize TRANSFER_PACKETs and related resources.
1448 status
= InitializeTransferPackets(DeviceObject
);
1452 // ISSUE - drivers need to disable write caching on the media
1453 // if hotplug and !useroverride. perhaps we should
1454 // allow registration of a callback to enable/disable
1455 // write cache instead.
1458 if (NT_SUCCESS(status
)){
1459 status
= devInfo
->ClassInitDevice(DeviceObject
);
1464 if (!NT_SUCCESS(status
)){
1467 // Just bail out - the remove that comes down will clean up the
1468 // initialized scraps.
1473 commonExtension
->IsInitialized
= TRUE
;
1475 if (commonExtension
->IsFdo
) {
1476 fdoExtension
->PrivateFdoData
->Perf
.OriginalSrbFlags
= fdoExtension
->SrbFlags
;
1482 // If device requests autorun functionality or a once a second callback
1483 // then enable the once per second timer.
1485 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
1486 // called in the context of the ClassInitDevice callback. If called
1487 // after then this check will have already been made and the
1488 // once a second timer will not have been enabled.
1491 ((initData
->ClassTick
!= NULL
) ||
1492 (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) ||
1493 ((fdoExtension
->FailurePredictionInfo
!= NULL
) &&
1494 (fdoExtension
->FailurePredictionInfo
->Method
!= FailurePredictionNone
))))
1496 ClasspEnableTimer(DeviceObject
);
1497 timerStarted
= TRUE
;
1499 timerStarted
= FALSE
;
1503 // NOTE: the timer looks at commonExtension->CurrentState now
1504 // to prevent Media Change Notification code from running
1505 // until the device is started, but allows the device
1506 // specific tick handler to run. therefore it is imperative
1507 // that commonExtension->CurrentState not be updated until
1508 // the device specific startdevice handler has finished.
1511 status
= devInfo
->ClassStartDevice(DeviceObject
);
1513 if(NT_SUCCESS(status
)) {
1514 commonExtension
->CurrentState
= IRP_MN_START_DEVICE
;
1516 if((isFdo
) && (initData
->ClassEnumerateDevice
!= NULL
)) {
1517 isMountedDevice
= FALSE
;
1520 if((DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) &&
1521 (DeviceObject
->DeviceType
!= FILE_DEVICE_CD_ROM
)) {
1523 isMountedDevice
= FALSE
;
1527 if(isMountedDevice
) {
1528 ClasspRegisterMountedDeviceInterface(DeviceObject
);
1531 if((commonExtension
->IsFdo
) &&
1532 (devInfo
->ClassWmiInfo
.GuidRegInfo
!= NULL
)) {
1534 IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_REGISTER
);
1539 ClasspDisableTimer(DeviceObject
);
1547 /*++////////////////////////////////////////////////////////////////////////////
1551 Routine Description:
1553 This is the system entry point for read and write requests. The
1554 device-specific handler is invoked to perform any validation necessary.
1556 If the device object is a PDO (partition object) then the request will
1557 simply be adjusted for Partition0 and issued to the lower device driver.
1559 IF the device object is an FDO (paritition 0 object), the number of bytes
1560 in the request are checked against the maximum byte counts that the adapter
1561 supports and requests are broken up into
1562 smaller sizes if necessary.
1566 DeviceObject - a pointer to the device object for this request
1575 NTSTATUS NTAPI
ClassReadWrite(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
1577 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1578 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
1579 PIO_STACK_LOCATION currentIrpStack
= IoGetCurrentIrpStackLocation(Irp
);
1580 //LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
1581 ULONG transferByteCount
= currentIrpStack
->Parameters
.Read
.Length
;
1586 * Grab the remove lock. If we can't acquire it, bail out.
1588 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
1590 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1591 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1592 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1593 status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1595 else if (TEST_FLAG(DeviceObject
->Flags
, DO_VERIFY_VOLUME
) &&
1596 (currentIrpStack
->MinorFunction
!= CLASSP_VOLUME_VERIFY_CHECKED
) &&
1597 !TEST_FLAG(currentIrpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
)){
1600 * DO_VERIFY_VOLUME is set for the device object,
1601 * but this request is not itself a verify request.
1602 * So fail this request.
1604 IoSetHardErrorOrVerifyDevice(Irp
, DeviceObject
);
1605 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
1606 Irp
->IoStatus
.Information
= 0;
1607 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1608 ClassCompleteRequest(DeviceObject
, Irp
, 0);
1609 status
= STATUS_VERIFY_REQUIRED
;
1614 * Since we've bypassed the verify-required tests we don't need to repeat
1615 * them with this IRP - in particular we don't want to worry about
1616 * hitting them at the partition 0 level if the request has come through
1617 * a non-zero partition.
1619 currentIrpStack
->MinorFunction
= CLASSP_VOLUME_VERIFY_CHECKED
;
1622 * Call the miniport driver's pre-pass filter to check if we
1623 * should continue with this transfer.
1625 ASSERT(commonExtension
->DevInfo
->ClassReadWriteVerification
);
1626 status
= commonExtension
->DevInfo
->ClassReadWriteVerification(DeviceObject
, Irp
);
1627 if (!NT_SUCCESS(status
)){
1628 ASSERT(Irp
->IoStatus
.Status
== status
);
1629 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1630 ClassCompleteRequest (DeviceObject
, Irp
, IO_NO_INCREMENT
);
1632 else if (status
== STATUS_PENDING
){
1634 * ClassReadWriteVerification queued this request.
1635 * So don't touch the irp anymore.
1640 if (transferByteCount
== 0) {
1642 * Several parts of the code turn 0 into 0xffffffff,
1643 * so don't process a zero-length request any further.
1645 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1646 Irp
->IoStatus
.Information
= 0;
1647 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1648 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1649 status
= STATUS_SUCCESS
;
1653 * If the driver has its own StartIo routine, call it.
1655 if (commonExtension
->DriverExtension
->InitData
.ClassStartIo
) {
1656 IoMarkIrpPending(Irp
);
1657 IoStartPacket(DeviceObject
, Irp
, NULL
, NULL
);
1658 status
= STATUS_PENDING
;
1662 * The driver does not have its own StartIo routine.
1663 * So process this request ourselves.
1667 * Add partition byte offset to make starting byte relative to
1668 * beginning of disk.
1670 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1671 commonExtension
->StartingOffset
.QuadPart
;
1673 if (commonExtension
->IsFdo
){
1676 * Add in any skew for the disk manager software.
1678 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1679 commonExtension
->PartitionZeroExtension
->DMByteSkew
;
1682 * Perform the actual transfer(s) on the hardware
1683 * to service this request.
1685 ServiceTransferRequest(DeviceObject
, Irp
);
1686 status
= STATUS_PENDING
;
1690 * This is a child PDO enumerated for our FDO by e.g. disk.sys
1691 * and owned by e.g. partmgr. Send it down to the next device
1692 * and the same irp will come back to us for the FDO.
1694 IoCopyCurrentIrpStackLocationToNext(Irp
);
1695 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1696 status
= IoCallDriver(lowerDeviceObject
, Irp
);
1707 /*++////////////////////////////////////////////////////////////////////////////
1709 ClassReadDriveCapacity()
1711 Routine Description:
1713 This routine sends a READ CAPACITY to the requested device, updates
1714 the geometry information in the device object and returns
1715 when it is complete. This routine is synchronous.
1717 This routine must be called with the remove lock held or some other
1718 assurance that the Fdo will not be removed while processing.
1722 DeviceObject - Supplies a pointer to the device object that represents
1723 the device whose capacity is to be read.
1730 NTSTATUS NTAPI
ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo
)
1732 READ_CAPACITY_DATA readCapacityBuffer
= {0};
1736 driveCapMdl
= BuildDeviceInputMdl(&readCapacityBuffer
, sizeof(READ_CAPACITY_DATA
));
1739 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1741 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
1743 //NTSTATUS pktStatus;
1744 IRP pseudoIrp
= {0};
1747 * Our engine needs an "original irp" to write the status back to
1748 * and to count down packets (one in this case).
1749 * Just use a pretend irp for this.
1751 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1752 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1753 pseudoIrp
.IoStatus
.Information
= 0;
1754 pseudoIrp
.MdlAddress
= driveCapMdl
;
1757 * Set this up as a SYNCHRONOUS transfer, submit it,
1758 * and wait for the packet to complete. The result
1759 * status will be written to the original irp.
1761 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1762 SetupDriveCapacityTransferPacket( pkt
,
1763 &readCapacityBuffer
,
1764 sizeof(READ_CAPACITY_DATA
),
1767 SubmitTransferPacket(pkt
);
1768 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1770 status
= pseudoIrp
.IoStatus
.Status
;
1773 * If we got an UNDERRUN, retry exactly once.
1774 * (The transfer_packet engine didn't retry because the result
1775 * status was success).
1777 if (NT_SUCCESS(status
) &&
1778 (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
))){
1779 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG
)pseudoIrp
.IoStatus
.Information
, sizeof(READ_CAPACITY_DATA
)));
1781 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1783 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1784 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1785 pseudoIrp
.IoStatus
.Information
= 0;
1786 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1787 SetupDriveCapacityTransferPacket( pkt
,
1788 &readCapacityBuffer
,
1789 sizeof(READ_CAPACITY_DATA
),
1792 SubmitTransferPacket(pkt
);
1793 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1794 status
= pseudoIrp
.IoStatus
.Status
;
1795 if (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
)){
1796 status
= STATUS_DEVICE_BUSY
;
1800 status
= STATUS_INSUFFICIENT_RESOURCES
;
1805 if (NT_SUCCESS(status
)){
1807 * The request succeeded.
1808 * Read out and store the drive information.
1811 ULONG bytesPerSector
;
1816 * Read the bytesPerSector value,
1817 * which is big-endian in the returned buffer.
1818 * Default to the standard 512 bytes.
1820 tmp
= readCapacityBuffer
.BytesPerBlock
;
1821 ((PFOUR_BYTE
)&bytesPerSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1822 ((PFOUR_BYTE
)&bytesPerSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1823 ((PFOUR_BYTE
)&bytesPerSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1824 ((PFOUR_BYTE
)&bytesPerSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1825 if (bytesPerSector
== 0) {
1826 bytesPerSector
= 512;
1830 * Clear all but the highest set bit.
1831 * That will give us a bytesPerSector value that is a power of 2.
1833 while (bytesPerSector
& (bytesPerSector
-1)) {
1834 bytesPerSector
&= bytesPerSector
-1;
1837 fdoExt
->DiskGeometry
.BytesPerSector
= bytesPerSector
;
1840 // Copy last sector in reverse byte order.
1843 tmp
= readCapacityBuffer
.LogicalBlockAddress
;
1844 ((PFOUR_BYTE
)&lastSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1845 ((PFOUR_BYTE
)&lastSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1846 ((PFOUR_BYTE
)&lastSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1847 ((PFOUR_BYTE
)&lastSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1850 // Calculate sector to byte shift.
1853 WHICH_BIT(fdoExt
->DiskGeometry
.BytesPerSector
, fdoExt
->SectorShift
);
1855 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
1856 fdoExt
->DiskGeometry
.BytesPerSector
));
1858 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
1861 if (fdoExt
->DMActive
){
1862 DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
1864 lastSector
-= fdoExt
->DMSkew
;
1868 * Check to see if we have a geometry we should be using already.
1870 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1871 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1872 if (cylinderSize
== 0){
1873 DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
1874 "values from %#x/%#x to %#x/%#x\n",
1875 fdoExt
->DiskGeometry
.TracksPerCylinder
,
1876 fdoExt
->DiskGeometry
.SectorsPerTrack
,
1880 fdoExt
->DiskGeometry
.TracksPerCylinder
= 0xff;
1881 fdoExt
->DiskGeometry
.SectorsPerTrack
= 0x3f;
1884 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1885 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1889 // Calculate number of cylinders.
1892 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= (LONGLONG
)((lastSector
+ 1)/cylinderSize
);
1895 // if there are zero cylinders, then the device lied AND it's
1896 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
1897 // this can fit into a single LONGLONG, so create another usable
1898 // geometry, even if it's unusual looking. This allows small,
1899 // non-standard devices, such as Sony's Memory Stick, to show
1900 // up as having a partition.
1903 if (fdoExt
->DiskGeometry
.Cylinders
.QuadPart
== (LONGLONG
)0) {
1904 fdoExt
->DiskGeometry
.SectorsPerTrack
= 1;
1905 fdoExt
->DiskGeometry
.TracksPerCylinder
= 1;
1906 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= lastSector
;
1911 // Calculate media capacity in bytes.
1914 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
=
1915 ((LONGLONG
)(lastSector
+ 1)) << fdoExt
->SectorShift
;
1918 * Is this removable or fixed media
1920 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1921 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1924 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1929 * The request failed.
1933 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
1934 // what happens when the disk's sector size is bigger than
1935 // 512 bytes and we hit this code path? this is untested.
1937 // If the read capacity fails, set the geometry to reasonable parameter
1938 // so things don't fail at unexpected places. Zero the geometry
1939 // except for the bytes per sector and sector shift.
1943 * This request can sometimes fail legitimately
1944 * (e.g. when a SCSI device is attached but turned off)
1945 * so this is not necessarily a device/driver bug.
1947 DBGTRACE(ClassDebugWarning
, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo
, status
));
1950 * Write in a default disk geometry which we HOPE is right (??).
1953 RtlZeroMemory(&fdoExt
->DiskGeometry
, sizeof(DISK_GEOMETRY
));
1954 fdoExt
->DiskGeometry
.BytesPerSector
= 512;
1955 fdoExt
->SectorShift
= 9;
1956 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
= (LONGLONG
) 0;
1959 * Is this removable or fixed media
1961 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1962 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1965 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1971 status
= STATUS_INSUFFICIENT_RESOURCES
;
1974 FreeDeviceInputMdl(driveCapMdl
);
1977 status
= STATUS_INSUFFICIENT_RESOURCES
;
1984 /*++////////////////////////////////////////////////////////////////////////////
1986 ClassSendStartUnit()
1988 Routine Description:
1990 Send command to SCSI unit to start or power up.
1991 Because this command is issued asynchronounsly, that is, without
1992 waiting on it to complete, the IMMEDIATE flag is not set. This
1993 means that the CDB will not return until the drive has powered up.
1994 This should keep subsequent requests from being submitted to the
1995 device before it has completely spun up.
1997 This routine is called from the InterpretSense routine, when a
1998 request sense returns data indicating that a drive must be
2001 This routine may also be called from a class driver's error handler,
2002 or anytime a non-critical start device should be sent to the device.
2006 Fdo - The functional device object for the stopped device.
2016 IN PDEVICE_OBJECT Fdo
2019 PIO_STACK_LOCATION irpStack
;
2021 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2022 PSCSI_REQUEST_BLOCK srb
;
2023 PCOMPLETION_CONTEXT context
;
2027 // Allocate Srb from nonpaged pool.
2030 context
= ExAllocatePoolWithTag(NonPagedPool
,
2031 sizeof(COMPLETION_CONTEXT
),
2034 if(context
== NULL
) {
2037 // ISSUE-2000/02/03-peterwie
2038 // This code path was inheritted from the NT 4.0 class2.sys driver.
2039 // It needs to be changed to survive low-memory conditions.
2042 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2046 // Save the device object in the context for use by the completion
2050 context
->DeviceObject
= Fdo
;
2051 srb
= &context
->Srb
;
2057 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
2060 // Write length to SRB.
2063 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2065 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2068 // Set timeout value large enough for drive to spin up.
2071 srb
->TimeOutValue
= START_UNIT_TIMEOUT
;
2074 // Set the transfer length.
2077 srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
|
2078 SRB_FLAGS_DISABLE_AUTOSENSE
|
2079 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
;
2082 // Build the start unit CDB.
2086 cdb
= (PCDB
)srb
->Cdb
;
2088 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
2089 cdb
->START_STOP
.Start
= 1;
2090 cdb
->START_STOP
.Immediate
= 0;
2091 cdb
->START_STOP
.LogicalUnitNumber
= srb
->Lun
;
2094 // Build the asynchronous request to be sent to the port driver.
2095 // Since this routine is called from a DPC the IRP should always be
2099 irp
= IoAllocateIrp(Fdo
->StackSize
, FALSE
);
2104 // ISSUE-2000/02/03-peterwie
2105 // This code path was inheritted from the NT 4.0 class2.sys driver.
2106 // It needs to be changed to survive low-memory conditions.
2109 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2113 ClassAcquireRemoveLock(Fdo
, irp
);
2115 IoSetCompletionRoutine(irp
,
2116 (PIO_COMPLETION_ROUTINE
)ClassAsynchronousCompletion
,
2122 irpStack
= IoGetNextIrpStackLocation(irp
);
2123 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2124 srb
->OriginalRequest
= irp
;
2127 // Store the SRB address in next stack for port driver.
2130 irpStack
->Parameters
.Scsi
.Srb
= srb
;
2133 // Call the port driver with the IRP.
2136 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2140 } // end StartUnit()
2142 /*++////////////////////////////////////////////////////////////////////////////
2144 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
2146 Routine Description:
2148 This routine is called when an asynchronous I/O request
2149 which was issused by the class driver completes. Examples of such requests
2150 are release queue or START UNIT. This routine releases the queue if
2151 necessary. It then frees the context and the IRP.
2155 DeviceObject - The device object for the logical unit; however since this
2156 is the top stack location the value is NULL.
2158 Irp - Supplies a pointer to the Irp to be processed.
2160 Context - Supplies the context to be used to process this request.
2169 ClassAsynchronousCompletion(
2170 PDEVICE_OBJECT DeviceObject
,
2175 PCOMPLETION_CONTEXT context
= Context
;
2176 PSCSI_REQUEST_BLOCK srb
;
2178 if(DeviceObject
== NULL
) {
2180 DeviceObject
= context
->DeviceObject
;
2183 srb
= &context
->Srb
;
2186 // If this is an execute srb, then check the return status and make sure.
2187 // the queue is not frozen.
2190 if (srb
->Function
== SRB_FUNCTION_EXECUTE_SCSI
) {
2193 // Check for a frozen queue.
2196 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2199 // Unfreeze the queue getting the device object from the context.
2202 ClassReleaseQueue(context
->DeviceObject
);
2206 { // free port-allocated sense buffer if we can detect
2208 if (((PCOMMON_DEVICE_EXTENSION
)(DeviceObject
->DeviceExtension
))->IsFdo
) {
2210 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
2211 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2212 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2217 ASSERT(!TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
2224 // Free the context and the Irp.
2227 if (Irp
->MdlAddress
!= NULL
) {
2228 MmUnlockPages(Irp
->MdlAddress
);
2229 IoFreeMdl(Irp
->MdlAddress
);
2231 Irp
->MdlAddress
= NULL
;
2234 ClassReleaseRemoveLock(DeviceObject
, Irp
);
2236 ExFreePool(context
);
2240 // Indicate the I/O system should stop processing the Irp completion.
2243 return STATUS_MORE_PROCESSING_REQUIRED
;
2245 } // end ClassAsynchronousCompletion()
2247 VOID NTAPI
ServiceTransferRequest(PDEVICE_OBJECT Fdo
, PIRP Irp
)
2249 //PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
2250 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
2251 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
2252 //PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
2253 PIO_STACK_LOCATION currentSp
= IoGetCurrentIrpStackLocation(Irp
);
2254 ULONG entireXferLen
= currentSp
->Parameters
.Read
.Length
;
2255 PUCHAR bufPtr
= MmGetMdlVirtualAddress(Irp
->MdlAddress
);
2256 LARGE_INTEGER targetLocation
= currentSp
->Parameters
.Read
.ByteOffset
;
2257 PTRANSFER_PACKET pkt
;
2258 SINGLE_LIST_ENTRY pktList
;
2259 PSINGLE_LIST_ENTRY slistEntry
;
2265 * Compute the number of hw xfers we'll have to do.
2266 * Calculate this without allowing for an overflow condition.
2268 ASSERT(fdoData
->HwMaxXferLen
>= PAGE_SIZE
);
2269 numPackets
= entireXferLen
/fdoData
->HwMaxXferLen
;
2270 if (entireXferLen
% fdoData
->HwMaxXferLen
){
2275 * First get all the TRANSFER_PACKETs that we'll need at once.
2276 * Use our 'simple' slist functions since we don't need interlocked.
2278 SimpleInitSlistHdr(&pktList
);
2279 for (i
= 0; i
< numPackets
; i
++){
2280 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
2282 SimplePushSlist(&pktList
, &pkt
->SlistEntry
);
2289 if (i
== numPackets
){
2291 * Initialize the original IRP's status to success.
2292 * If any of the packets fail, they will set it to an error status.
2293 * The IoStatus.Information field will be incremented to the
2294 * transfer length as the pieces complete.
2296 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2297 Irp
->IoStatus
.Information
= 0;
2300 * Store the number of transfer pieces inside the original IRP.
2301 * It will be used to count down the pieces as they complete.
2303 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(numPackets
);
2306 * We are proceeding with the transfer.
2307 * Mark the client IRP pending since it may complete on a different thread.
2309 IoMarkIrpPending(Irp
);
2312 * Transmit the pieces of the transfer.
2314 while (entireXferLen
> 0){
2315 ULONG thisPieceLen
= MIN(fdoData
->HwMaxXferLen
, entireXferLen
);
2318 * Set up a TRANSFER_PACKET for this piece and send it.
2320 slistEntry
= SimplePopSlist(&pktList
);
2322 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2323 SetupReadWriteTransferPacket( pkt
,
2328 SubmitTransferPacket(pkt
);
2330 entireXferLen
-= thisPieceLen
;
2331 bufPtr
+= thisPieceLen
;
2332 targetLocation
.QuadPart
+= thisPieceLen
;
2334 ASSERT(SimpleIsSlistEmpty(&pktList
));
2338 * We were unable to get all the TRANSFER_PACKETs we need,
2339 * but we did get at least one.
2340 * That means that we are in extreme low-memory stress.
2341 * We'll try doing this transfer using a single packet.
2342 * The port driver is certainly also in stress, so use one-page
2347 * Free all but one of the TRANSFER_PACKETs.
2350 slistEntry
= SimplePopSlist(&pktList
);
2352 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2353 EnqueueFreeTransferPacket(Fdo
, pkt
);
2357 * Get the single TRANSFER_PACKET that we'll be using.
2359 slistEntry
= SimplePopSlist(&pktList
);
2361 ASSERT(SimpleIsSlistEmpty(&pktList
));
2362 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2363 DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt
));
2366 * Set default status and the number of transfer packets (one)
2367 * inside the original irp.
2369 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2370 Irp
->IoStatus
.Information
= 0;
2371 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
2374 * Mark the client irp pending since it may complete on
2377 IoMarkIrpPending(Irp
);
2380 * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
2382 SetupReadWriteTransferPacket( pkt
,
2387 InitLowMemRetry(pkt
, bufPtr
, entireXferLen
, targetLocation
);
2388 StepLowMemRetry(pkt
);
2392 * We were unable to get ANY TRANSFER_PACKETs.
2393 * Defer this client irp until some TRANSFER_PACKETs free up.
2395 DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp
));
2396 IoMarkIrpPending(Irp
);
2397 EnqueueDeferredClientIrp(fdoData
, Irp
);
2402 /*++////////////////////////////////////////////////////////////////////////////
2406 Routine Description:
2408 This routine executes when the port driver has completed a request.
2409 It looks at the SRB status in the completing SRB and if not success
2410 it checks for valid request sense buffer information. If valid, the
2411 info is used to update status with more precise message of type of
2412 error. This routine deallocates the SRB.
2414 This routine should only be placed on the stack location for a class
2419 Fdo - Supplies the device object which represents the logical
2422 Irp - Supplies the Irp which has completed.
2424 Context - Supplies a pointer to the SRB.
2434 IN PDEVICE_OBJECT Fdo
,
2439 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2440 PSCSI_REQUEST_BLOCK srb
= Context
;
2441 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2442 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2445 BOOLEAN callStartNextPacket
;
2447 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2450 // Check SRB status for success of completing request.
2453 if (SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2454 ULONG retryInterval
;
2456 DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp
, srb
));
2459 // Release the queue if it is frozen.
2462 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2463 ClassReleaseQueue(Fdo
);
2466 retry
= ClassInterpretSenseInfo(
2469 irpStack
->MajorFunction
,
2470 irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
?
2471 irpStack
->Parameters
.DeviceIoControl
.IoControlCode
:
2474 ((ULONG
)(ULONG_PTR
)irpStack
->Parameters
.Others
.Argument4
),
2479 // If the status is verified required and the this request
2480 // should bypass verify required then retry the request.
2483 if (TEST_FLAG(irpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
) &&
2484 status
== STATUS_VERIFY_REQUIRED
) {
2486 status
= STATUS_IO_DEVICE_ERROR
;
2490 if (retry
&& ((*(PCHAR
*)&irpStack
->Parameters
.Others
.Argument4
)--)) {
2496 DebugPrint((1, "Retry request %p\n", Irp
));
2498 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2499 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2502 RetryRequest(Fdo
, Irp
, srb
, FALSE
, retryInterval
);
2503 return STATUS_MORE_PROCESSING_REQUIRED
;
2509 // Set status for successful request
2511 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2512 ClasspPerfIncrementSuccessfulIo(fdoExtension
);
2513 status
= STATUS_SUCCESS
;
2514 } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
2518 // ensure we have returned some info, and it matches what the
2519 // original request wanted for PAGING operations only
2522 if ((NT_SUCCESS(status
)) && TEST_FLAG(Irp
->Flags
, IRP_PAGING_IO
)) {
2523 ASSERT(Irp
->IoStatus
.Information
!= 0);
2524 ASSERT(irpStack
->Parameters
.Read
.Length
== Irp
->IoStatus
.Information
);
2528 // remember if the caller wanted to skip calling IoStartNextPacket.
2529 // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
2530 // calls. this setting only affects device objects with StartIo routines.
2533 callStartNextPacket
= !TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_DONT_START_NEXT_PACKET
);
2534 if (irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
) {
2535 callStartNextPacket
= FALSE
;
2542 if(!TEST_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_PERSISTANT
)) {
2544 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2545 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2548 if (fdoExtension
->CommonExtension
.IsSrbLookasideListInitialized
){
2549 ClassFreeOrReuseSrb(fdoExtension
, srb
);
2552 DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
2558 DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
2559 "SRB_CLASS_FLAGS_PERSISTANT set\n", srb
));
2560 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2561 DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
2562 " because SRB_CLASS_FLAGS_PERSISTANT set\n",
2563 srb
->SenseInfoBuffer
));
2569 // Set status in completing IRP.
2572 Irp
->IoStatus
.Status
= status
;
2575 // Set the hard error if necessary.
2578 if (!NT_SUCCESS(status
) &&
2579 IoIsErrorUserInduced(status
) &&
2580 (Irp
->Tail
.Overlay
.Thread
!= NULL
)
2584 // Store DeviceObject for filesystem, and clear
2585 // in IoStatus.Information field.
2588 IoSetHardErrorOrVerifyDevice(Irp
, Fdo
);
2589 Irp
->IoStatus
.Information
= 0;
2593 // If pending has be returned for this irp then mark the current stack as
2597 if (Irp
->PendingReturned
) {
2598 IoMarkIrpPending(Irp
);
2601 if (fdoExtension
->CommonExtension
.DriverExtension
->InitData
.ClassStartIo
) {
2602 if (callStartNextPacket
) {
2604 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
2605 IoStartNextPacket(Fdo
, FALSE
);
2606 KeLowerIrql(oldIrql
);
2610 ClassReleaseRemoveLock(Fdo
, Irp
);
2614 } // end ClassIoComplete()
2616 /*++////////////////////////////////////////////////////////////////////////////
2618 ClassSendSrbSynchronous()
2620 Routine Description:
2622 This routine is called by SCSI device controls to complete an
2623 SRB and send it to the port driver synchronously (ie wait for
2624 completion). The CDB is already completed along with the SRB CDB
2625 size and request timeout value.
2629 Fdo - Supplies the functional device object which represents the target.
2631 Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
2633 BufferAddress - Supplies the address of the buffer.
2635 BufferLength - Supplies the length in bytes of the buffer.
2637 WriteToDevice - Indicates the data should be transfer to the device.
2641 NTSTATUS indicating the final results of the operation.
2643 If NT_SUCCESS(), then the amount of usable data is contained in the field
2644 Srb->DataTransferLength
2649 ClassSendSrbSynchronous(
2651 PSCSI_REQUEST_BLOCK Srb
,
2652 PVOID BufferAddress
,
2654 BOOLEAN WriteToDevice
2658 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2659 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2660 IO_STATUS_BLOCK ioStatus
;
2661 //ULONG controlType;
2663 PIO_STACK_LOCATION irpStack
;
2665 PUCHAR senseInfoBuffer
;
2666 ULONG retryCount
= MAXIMUM_RETRIES
;
2671 // NOTE: This code is only pagable because we are not freezing
2672 // the queue. Allowing the queue to be frozen from a pagable
2673 // routine could leave the queue frozen as we try to page in
2674 // the code to unfreeze the queue. The result would be a nice
2675 // case of deadlock. Therefore, since we are unfreezing the
2676 // queue regardless of the result, just set the NO_FREEZE_QUEUE
2680 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2681 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2684 // Write length to SRB.
2687 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2690 // Set SCSI bus address.
2693 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2696 // Enable auto request sense.
2699 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
2702 // Sense buffer is in aligned nonpaged pool.
2705 senseInfoBuffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
2709 if (senseInfoBuffer
== NULL
) {
2711 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
2713 return(STATUS_INSUFFICIENT_RESOURCES
);
2716 Srb
->SenseInfoBuffer
= senseInfoBuffer
;
2717 Srb
->DataBuffer
= BufferAddress
;
2720 // Start retries here.
2726 // use fdoextension's flags by default.
2727 // do not move out of loop, as the flag may change due to errors
2728 // sending this command.
2731 Srb
->SrbFlags
= fdoExtension
->SrbFlags
;
2733 if(BufferAddress
!= NULL
) {
2735 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_OUT
);
2737 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
2742 // Initialize the QueueAction field.
2745 Srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
2748 // Disable synchronous transfer for these requests.
2749 // Disable freezing the queue, since all we do is unfreeze it anyways.
2752 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
2753 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
2756 // Set the event object to the unsignaled state.
2757 // It will be used to signal request completion.
2760 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
2763 // Build device I/O control request with METHOD_NEITHER data transfer.
2764 // We'll queue a completion routine to cleanup the MDL's and such ourself.
2767 irp
= IoAllocateIrp(
2768 (CCHAR
) (fdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
+ 1),
2772 ExFreePool(senseInfoBuffer
);
2773 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
2774 return(STATUS_INSUFFICIENT_RESOURCES
);
2778 // Get next stack location.
2781 irpStack
= IoGetNextIrpStackLocation(irp
);
2784 // Set up SRB for execute scsi request. Save SRB address in next stack
2785 // for the port driver.
2788 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2789 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
2791 IoSetCompletionRoutine(irp
,
2792 ClasspSendSynchronousCompletion
,
2798 irp
->UserIosb
= &ioStatus
;
2799 irp
->UserEvent
= &event
;
2803 // Build an MDL for the data buffer and stick it into the irp. The
2804 // completion routine will unlock the pages and free the MDL.
2807 irp
->MdlAddress
= IoAllocateMdl( BufferAddress
,
2812 if (irp
->MdlAddress
== NULL
) {
2813 ExFreePool(senseInfoBuffer
);
2814 Srb
->SenseInfoBuffer
= NULL
;
2816 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
2817 return STATUS_INSUFFICIENT_RESOURCES
;
2823 // the io manager unlocks these pages upon completion
2826 MmProbeAndLockPages( irp
->MdlAddress
,
2828 (WriteToDevice
? IoReadAccess
:
2831 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
2832 status
= _SEH2_GetExceptionCode();
2834 ExFreePool(senseInfoBuffer
);
2835 Srb
->SenseInfoBuffer
= NULL
;
2836 IoFreeMdl(irp
->MdlAddress
);
2839 DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
2840 "locking buffer\n", status
));
2841 _SEH2_YIELD(return status
);
2846 // Set the transfer length.
2849 Srb
->DataTransferLength
= BufferLength
;
2855 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
2859 // Set up IRP Address.
2862 Srb
->OriginalRequest
= irp
;
2865 // Call the port driver with the request and wait for it to complete.
2868 status
= IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2870 if (status
== STATUS_PENDING
) {
2871 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
2872 status
= ioStatus
.Status
;
2876 // Check that request completed without error.
2879 if (SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2881 ULONG retryInterval
;
2883 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
)));
2886 // assert that the queue is not frozen
2889 ASSERT(!TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
2892 // Update status and determine if request should be retried.
2895 retry
= ClassInterpretSenseInfo(Fdo
,
2899 MAXIMUM_RETRIES
- retryCount
,
2906 if ((status
== STATUS_DEVICE_NOT_READY
&&
2907 ((PSENSE_DATA
) senseInfoBuffer
)->AdditionalSenseCode
==
2908 SCSI_ADSENSE_LUN_NOT_READY
) ||
2909 (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)) {
2911 LARGE_INTEGER delay
;
2914 // Delay for at least 2 seconds.
2917 if(retryInterval
< 2) {
2921 delay
.QuadPart
= (LONGLONG
)( - 10 * 1000 * (LONGLONG
)1000 * retryInterval
);
2924 // Stall for a while to let the device become ready
2927 KeDelayExecutionThread(KernelMode
, FALSE
, &delay
);
2932 // If retries are not exhausted then retry this operation.
2937 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2938 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2946 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2947 status
= STATUS_SUCCESS
;
2951 // required even though we allocated our own, since the port driver may
2952 // have allocated one also
2955 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2956 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2959 Srb
->SenseInfoBuffer
= NULL
;
2960 ExFreePool(senseInfoBuffer
);
2965 /*++////////////////////////////////////////////////////////////////////////////
2967 ClassInterpretSenseInfo()
2969 Routine Description:
2971 This routine interprets the data returned from the SCSI
2972 request sense. It determines the status to return in the
2973 IRP and whether this request can be retried.
2977 DeviceObject - Supplies the device object associated with this request.
2979 Srb - Supplies the scsi request block which failed.
2981 MajorFunctionCode - Supplies the function code to be used for logging.
2983 IoDeviceCode - Supplies the device code to be used for logging.
2985 Status - Returns the status for the request.
2989 BOOLEAN TRUE: Drivers should retry this request.
2990 FALSE: Drivers should not retry this request.
2995 ClassInterpretSenseInfo(
2996 IN PDEVICE_OBJECT Fdo
,
2997 IN PSCSI_REQUEST_BLOCK Srb
,
2998 IN UCHAR MajorFunctionCode
,
2999 IN ULONG IoDeviceCode
,
3000 IN ULONG RetryCount
,
3001 OUT NTSTATUS
*Status
,
3002 OUT OPTIONAL ULONG
*RetryInterval
3005 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
3006 PCOMMON_DEVICE_EXTENSION commonExtension
= Fdo
->DeviceExtension
;
3007 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
3009 PSENSE_DATA senseBuffer
= Srb
->SenseInfoBuffer
;
3011 BOOLEAN retry
= TRUE
;
3012 BOOLEAN logError
= FALSE
;
3013 BOOLEAN unhandledError
= FALSE
;
3014 BOOLEAN incrementErrorCount
= FALSE
;
3016 ULONG badSector
= 0;
3024 ULONG retryInterval
= 0;
3030 if(TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3033 // Log anything remotely incorrect about paging i/o
3038 logStatus
= IO_WARNING_PAGING_FAILURE
;
3042 // Check that request sense buffer is valid.
3045 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
3049 // must handle the SRB_STATUS_INTERNAL_ERROR case first,
3050 // as it has all the flags set.
3053 if (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_INTERNAL_ERROR
) {
3055 DebugPrint((ClassDebugSenseInfo
,
3056 "ClassInterpretSenseInfo: Internal Error code is %x\n",
3057 Srb
->InternalStatus
));
3060 *Status
= Srb
->InternalStatus
;
3062 } else if ((Srb
->SrbStatus
& SRB_STATUS_AUTOSENSE_VALID
) &&
3063 (Srb
->SenseInfoBufferLength
>=
3064 offsetof(SENSE_DATA
, CommandSpecificInformation
))) {
3067 // Zero the additional sense code and additional sense code qualifier
3068 // if they were not returned by the device.
3071 readSector
= senseBuffer
->AdditionalSenseLength
+
3072 offsetof(SENSE_DATA
, AdditionalSenseLength
);
3074 if (readSector
> Srb
->SenseInfoBufferLength
) {
3075 readSector
= Srb
->SenseInfoBufferLength
;
3078 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCode
)) {
3079 senseBuffer
->AdditionalSenseCode
= 0;
3082 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCodeQualifier
)) {
3083 senseBuffer
->AdditionalSenseCodeQualifier
= 0;
3086 DebugPrint((ClassDebugSenseInfo
,
3087 "ClassInterpretSenseInfo: Error code is %x\n",
3088 senseBuffer
->ErrorCode
));
3089 DebugPrint((ClassDebugSenseInfo
,
3090 "ClassInterpretSenseInfo: Sense key is %x\n",
3091 senseBuffer
->SenseKey
));
3092 DebugPrint((ClassDebugSenseInfo
,
3093 "ClassInterpretSenseInfo: Additional sense code is %x\n",
3094 senseBuffer
->AdditionalSenseCode
));
3095 DebugPrint((ClassDebugSenseInfo
,
3096 "ClassInterpretSenseInfo: Additional sense code qualifier "
3098 senseBuffer
->AdditionalSenseCodeQualifier
));
3101 switch (senseBuffer
->SenseKey
& 0xf) {
3103 case SCSI_SENSE_NOT_READY
: {
3105 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3106 "Device not ready\n"));
3107 *Status
= STATUS_DEVICE_NOT_READY
;
3109 switch (senseBuffer
->AdditionalSenseCode
) {
3111 case SCSI_ADSENSE_LUN_NOT_READY
: {
3113 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3114 "Lun not ready\n"));
3116 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3118 case SCSI_SENSEQ_OPERATION_IN_PROGRESS
: {
3119 DEVICE_EVENT_BECOMING_READY notReady
;
3121 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3122 "Operation In Progress\n"));
3123 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3125 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3126 notReady
.Version
= 1;
3127 notReady
.Reason
= 2;
3128 notReady
.Estimated100msToReady
= retryInterval
* 10;
3129 ClasspSendNotification(fdoExtension
,
3130 &GUID_IO_DEVICE_BECOMING_READY
,
3131 sizeof(DEVICE_EVENT_BECOMING_READY
),
3137 case SCSI_SENSEQ_BECOMING_READY
: {
3138 DEVICE_EVENT_BECOMING_READY notReady
;
3140 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3141 "In process of becoming ready\n"));
3142 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3144 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3145 notReady
.Version
= 1;
3146 notReady
.Reason
= 1;
3147 notReady
.Estimated100msToReady
= retryInterval
* 10;
3148 ClasspSendNotification(fdoExtension
,
3149 &GUID_IO_DEVICE_BECOMING_READY
,
3150 sizeof(DEVICE_EVENT_BECOMING_READY
),
3155 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS
: {
3156 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3157 "Long write in progress\n"));
3162 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED
: {
3163 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3164 "Manual intervention required\n"));
3165 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3170 case SCSI_SENSEQ_FORMAT_IN_PROGRESS
: {
3171 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3172 "Format in progress\n"));
3177 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE
: {
3179 if(!TEST_FLAG(fdoExtension
->ScanForSpecialFlags
,
3180 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
)) {
3182 DebugPrint((ClassDebugSenseInfo
,
3183 "ClassInterpretSenseInfo: "
3184 "not ready, cause unknown\n"));
3186 Many non-WHQL certified drives (mostly CD-RW) return
3187 this when they have no media instead of the obvious
3190 SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
3192 These drives should not pass WHQL certification due
3193 to this discrepency.
3202 // Treat this as init command required and fall through.
3207 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED
:
3209 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3210 "Initializing command required\n"));
3213 // This sense code/additional sense code
3214 // combination may indicate that the device
3215 // needs to be started. Send an start unit if this
3216 // is a disk device.
3219 if(TEST_FLAG(fdoExtension
->DeviceFlags
,
3220 DEV_SAFE_START_UNIT
) &&
3221 !TEST_FLAG(Srb
->SrbFlags
,
3222 SRB_CLASS_FLAGS_LOW_PRIORITY
)) {
3223 ClassSendStartUnit(Fdo
);
3229 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
3233 case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
: {
3234 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3235 "No Media in device.\n"));
3236 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3240 // signal MCN that there isn't any media in the device
3242 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3243 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3244 "No Media in a non-removable device %p\n",
3247 ClassSetMediaChangeState(fdoExtension
, MediaNotPresent
, FALSE
);
3251 } // end switch (senseBuffer->AdditionalSenseCode)
3254 } // end SCSI_SENSE_NOT_READY
3256 case SCSI_SENSE_DATA_PROTECT
: {
3257 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3258 "Media write protected\n"));
3259 *Status
= STATUS_MEDIA_WRITE_PROTECTED
;
3262 } // end SCSI_SENSE_DATA_PROTECT
3264 case SCSI_SENSE_MEDIUM_ERROR
: {
3265 DebugPrint((ClassDebugSenseInfo
,"ClassInterpretSenseInfo: "
3266 "Medium Error (bad block)\n"));
3267 *Status
= STATUS_DEVICE_DATA_ERROR
;
3272 logStatus
= IO_ERR_BAD_BLOCK
;
3275 // Check if this error is due to unknown format
3277 if (senseBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_INVALID_MEDIA
){
3279 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3281 case SCSI_SENSEQ_UNKNOWN_FORMAT
: {
3283 *Status
= STATUS_UNRECOGNIZED_MEDIA
;
3286 // Log error only if this is a paging request
3288 if(!TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3294 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED
: {
3296 *Status
= STATUS_CLEANER_CARTRIDGE_INSTALLED
;
3304 } // end switch AdditionalSenseCodeQualifier
3306 } // end SCSI_ADSENSE_INVALID_MEDIA
3310 } // end SCSI_SENSE_MEDIUM_ERROR
3312 case SCSI_SENSE_HARDWARE_ERROR
: {
3313 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3314 "Hardware error\n"));
3315 *Status
= STATUS_IO_DEVICE_ERROR
;
3318 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3320 } // end SCSI_SENSE_HARDWARE_ERROR
3322 case SCSI_SENSE_ILLEGAL_REQUEST
: {
3324 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3325 "Illegal SCSI request\n"));
3326 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3329 switch (senseBuffer
->AdditionalSenseCode
) {
3331 case SCSI_ADSENSE_ILLEGAL_COMMAND
: {
3332 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3333 "Illegal command\n"));
3337 case SCSI_ADSENSE_ILLEGAL_BLOCK
: {
3338 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3339 "Illegal block address\n"));
3340 *Status
= STATUS_NONEXISTENT_SECTOR
;
3344 case SCSI_ADSENSE_INVALID_LUN
: {
3345 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3347 *Status
= STATUS_NO_SUCH_DEVICE
;
3351 case SCSI_ADSENSE_MUSIC_AREA
: {
3352 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3357 case SCSI_ADSENSE_DATA_AREA
: {
3358 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3363 case SCSI_ADSENSE_VOLUME_OVERFLOW
: {
3364 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3365 "Volume overflow\n"));
3369 case SCSI_ADSENSE_COPY_PROTECTION_FAILURE
: {
3370 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3371 "Copy protection failure\n"));
3373 *Status
= STATUS_COPY_PROTECTION_FAILURE
;
3375 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3376 case SCSI_SENSEQ_AUTHENTICATION_FAILURE
:
3377 DebugPrint((ClassDebugSenseInfo
,
3378 "ClassInterpretSenseInfo: "
3379 "Authentication failure\n"));
3380 *Status
= STATUS_CSS_AUTHENTICATION_FAILURE
;
3382 case SCSI_SENSEQ_KEY_NOT_PRESENT
:
3383 DebugPrint((ClassDebugSenseInfo
,
3384 "ClassInterpretSenseInfo: "
3385 "Key not present\n"));
3386 *Status
= STATUS_CSS_KEY_NOT_PRESENT
;
3388 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED
:
3389 DebugPrint((ClassDebugSenseInfo
,
3390 "ClassInterpretSenseInfo: "
3391 "Key not established\n"));
3392 *Status
= STATUS_CSS_KEY_NOT_ESTABLISHED
;
3394 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION
:
3395 DebugPrint((ClassDebugSenseInfo
,
3396 "ClassInterpretSenseInfo: "
3397 "Read of scrambled sector w/o "
3398 "authentication\n"));
3399 *Status
= STATUS_CSS_SCRAMBLED_SECTOR
;
3401 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT
:
3402 DebugPrint((ClassDebugSenseInfo
,
3403 "ClassInterpretSenseInfo: "
3404 "Media region does not logical unit "
3406 *Status
= STATUS_CSS_REGION_MISMATCH
;
3408 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR
:
3409 DebugPrint((ClassDebugSenseInfo
,
3410 "ClassInterpretSenseInfo: "
3411 "Region set error -- region may "
3413 *Status
= STATUS_CSS_RESETS_EXHAUSTED
;
3415 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
3421 case SCSI_ADSENSE_INVALID_CDB
: {
3422 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3426 // Note: the retry interval is not typically used.
3427 // it is set here only because a ClassErrorHandler
3428 // cannot set the retryInterval, and the error may
3429 // require a few commands to be sent to clear whatever
3430 // caused this condition (i.e. disk clears the write
3431 // cache, requiring at least two commands)
3433 // hopefully, this shortcoming can be changed for
3441 } // end switch (senseBuffer->AdditionalSenseCode)
3444 } // end SCSI_SENSE_ILLEGAL_REQUEST
3446 case SCSI_SENSE_UNIT_ATTENTION
: {
3452 // A media change may have occured so increment the change
3453 // count for the physical device
3456 count
= InterlockedIncrement(&fdoExtension
->MediaChangeCount
);
3457 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3458 "Media change count for device %d incremented to %#lx\n",
3459 fdoExtension
->DeviceNumber
, count
));
3462 switch (senseBuffer
->AdditionalSenseCode
) {
3463 case SCSI_ADSENSE_MEDIUM_CHANGED
: {
3464 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3465 "Media changed\n"));
3467 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3468 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3469 "Media Changed on non-removable device %p\n",
3472 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
3476 case SCSI_ADSENSE_BUS_RESET
: {
3477 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3482 case SCSI_ADSENSE_OPERATOR_REQUEST
: {
3483 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3485 case SCSI_SENSEQ_MEDIUM_REMOVAL
: {
3486 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3487 "Ejection request received!\n"));
3488 ClassSendEjectionNotification(fdoExtension
);
3492 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE
: {
3493 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3494 "Operator selected write permit?! "
3495 "(unsupported!)\n"));
3499 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE
: {
3500 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3501 "Operator selected write protect?! "
3502 "(unsupported!)\n"));
3511 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3512 "Unit attention\n"));
3516 } // end switch (senseBuffer->AdditionalSenseCode)
3518 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
))
3521 // TODO : Is the media lockable?
3524 if ((ClassGetVpb(Fdo
) != NULL
) && (ClassGetVpb(Fdo
)->Flags
& VPB_MOUNTED
))
3527 // Set bit to indicate that media may have changed
3528 // and volume needs verification.
3531 SET_FLAG(Fdo
->Flags
, DO_VERIFY_VOLUME
);
3533 *Status
= STATUS_VERIFY_REQUIRED
;
3539 *Status
= STATUS_IO_DEVICE_ERROR
;
3544 } // end SCSI_SENSE_UNIT_ATTENTION
3546 case SCSI_SENSE_ABORTED_COMMAND
: {
3547 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3548 "Command aborted\n"));
3549 *Status
= STATUS_IO_DEVICE_ERROR
;
3552 } // end SCSI_SENSE_ABORTED_COMMAND
3554 case SCSI_SENSE_BLANK_CHECK
: {
3555 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3556 "Media blank check\n"));
3558 *Status
= STATUS_NO_DATA_DETECTED
;
3560 } // end SCSI_SENSE_BLANK_CHECK
3562 case SCSI_SENSE_RECOVERED_ERROR
: {
3564 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3565 "Recovered error\n"));
3566 *Status
= STATUS_SUCCESS
;
3571 switch(senseBuffer
->AdditionalSenseCode
) {
3572 case SCSI_ADSENSE_SEEK_ERROR
:
3573 case SCSI_ADSENSE_TRACK_ERROR
: {
3574 logStatus
= IO_ERR_SEEK_ERROR
;
3578 case SCSI_ADSENSE_REC_DATA_NOECC
:
3579 case SCSI_ADSENSE_REC_DATA_ECC
: {
3580 logStatus
= IO_RECOVERED_VIA_ECC
;
3584 case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED
: {
3585 UCHAR wmiEventData
[5];
3587 *((PULONG
)wmiEventData
) = sizeof(UCHAR
);
3588 wmiEventData
[sizeof(ULONG
)] = senseBuffer
->AdditionalSenseCodeQualifier
;
3591 // Don't log another eventlog if we have already logged once
3592 // NOTE: this should have been interlocked, but the structure
3593 // was publicly defined to use a BOOLEAN (char). Since
3594 // media only reports these errors once per X minutes,
3595 // the potential race condition is nearly non-existant.
3596 // the worst case is duplicate log entries, so ignore.
3599 if (fdoExtension
->FailurePredicted
== 0) {
3602 fdoExtension
->FailurePredicted
= TRUE
;
3603 fdoExtension
->FailureReason
= senseBuffer
->AdditionalSenseCodeQualifier
;
3604 logStatus
= IO_WRN_FAILURE_PREDICTED
;
3606 ClassNotifyFailurePredicted(fdoExtension
,
3607 (PUCHAR
)&wmiEventData
,
3608 sizeof(wmiEventData
),
3618 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3622 } // end switch(senseBuffer->AdditionalSenseCode)
3624 if (senseBuffer
->IncorrectLength
) {
3626 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3627 "Incorrect length detected.\n"));
3628 *Status
= STATUS_INVALID_BLOCK_LENGTH
;
3632 } // end SCSI_SENSE_RECOVERED_ERROR
3634 case SCSI_SENSE_NO_SENSE
: {
3637 // Check other indicators.
3640 if (senseBuffer
->IncorrectLength
) {
3642 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3643 "Incorrect length detected.\n"));
3644 *Status
= STATUS_INVALID_BLOCK_LENGTH
;
3649 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3650 "No specific sense key\n"));
3651 *Status
= STATUS_IO_DEVICE_ERROR
;
3656 } // end SCSI_SENSE_NO_SENSE
3659 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3660 "Unrecognized sense code\n"));
3661 *Status
= STATUS_IO_DEVICE_ERROR
;
3665 } // end switch (senseBuffer->SenseKey & 0xf)
3668 // Try to determine the bad sector from the inquiry data.
3671 if ((((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_READ
||
3672 ((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_VERIFY
||
3673 ((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_WRITE
)) {
3675 for (index
= 0; index
< 4; index
++) {
3676 badSector
= (badSector
<< 8) | senseBuffer
->Information
[index
];
3680 for (index
= 0; index
< 4; index
++) {
3681 readSector
= (readSector
<< 8) | Srb
->Cdb
[index
+2];
3684 index
= (((PCDB
)Srb
->Cdb
)->CDB10
.TransferBlocksMsb
<< 8) |
3685 ((PCDB
)Srb
->Cdb
)->CDB10
.TransferBlocksLsb
;
3688 // Make sure the bad sector is within the read sectors.
3691 if (!(badSector
>= readSector
&& badSector
< readSector
+ index
)) {
3692 badSector
= readSector
;
3699 // Request sense buffer not valid. No sense information
3700 // to pinpoint the error. Return general request fail.
3703 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3704 "Request sense info not valid. SrbStatus %2x\n",
3705 SRB_STATUS(Srb
->SrbStatus
)));
3708 switch (SRB_STATUS(Srb
->SrbStatus
)) {
3709 case SRB_STATUS_INVALID_LUN
:
3710 case SRB_STATUS_INVALID_TARGET_ID
:
3711 case SRB_STATUS_NO_DEVICE
:
3712 case SRB_STATUS_NO_HBA
:
3713 case SRB_STATUS_INVALID_PATH_ID
: {
3714 *Status
= STATUS_NO_SUCH_DEVICE
;
3719 case SRB_STATUS_COMMAND_TIMEOUT
:
3720 case SRB_STATUS_TIMEOUT
: {
3723 // Update the error count for the device.
3726 incrementErrorCount
= TRUE
;
3727 *Status
= STATUS_IO_TIMEOUT
;
3731 case SRB_STATUS_ABORTED
: {
3734 // Update the error count for the device.
3737 incrementErrorCount
= TRUE
;
3738 *Status
= STATUS_IO_TIMEOUT
;
3744 case SRB_STATUS_SELECTION_TIMEOUT
: {
3746 logStatus
= IO_ERR_NOT_READY
;
3748 *Status
= STATUS_DEVICE_NOT_CONNECTED
;
3753 case SRB_STATUS_DATA_OVERRUN
: {
3754 *Status
= STATUS_DATA_OVERRUN
;
3759 case SRB_STATUS_PHASE_SEQUENCE_FAILURE
: {
3762 // Update the error count for the device.
3765 incrementErrorCount
= TRUE
;
3766 *Status
= STATUS_IO_DEVICE_ERROR
;
3769 // If there was phase sequence error then limit the number of
3773 if (RetryCount
> 1 ) {
3780 case SRB_STATUS_REQUEST_FLUSHED
: {
3783 // If the status needs verification bit is set. Then set
3784 // the status to need verification and no retry; otherwise,
3785 // just retry the request.
3788 if (TEST_FLAG(Fdo
->Flags
, DO_VERIFY_VOLUME
)) {
3790 *Status
= STATUS_VERIFY_REQUIRED
;
3794 *Status
= STATUS_IO_DEVICE_ERROR
;
3800 case SRB_STATUS_INVALID_REQUEST
: {
3801 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3806 case SRB_STATUS_UNEXPECTED_BUS_FREE
:
3807 case SRB_STATUS_PARITY_ERROR
:
3810 // Update the error count for the device
3811 // and fall through to below
3814 incrementErrorCount
= TRUE
;
3816 case SRB_STATUS_BUS_RESET
: {
3817 *Status
= STATUS_IO_DEVICE_ERROR
;
3821 case SRB_STATUS_ERROR
: {
3823 *Status
= STATUS_IO_DEVICE_ERROR
;
3824 if (Srb
->ScsiStatus
== 0) {
3827 // This is some strange return code. Update the error
3828 // count for the device.
3831 incrementErrorCount
= TRUE
;
3833 } if (Srb
->ScsiStatus
== SCSISTAT_BUSY
) {
3835 *Status
= STATUS_DEVICE_NOT_READY
;
3837 } if (Srb
->ScsiStatus
== SCSISTAT_RESERVATION_CONFLICT
) {
3839 *Status
= STATUS_DEVICE_BUSY
;
3850 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3852 *Status
= STATUS_IO_DEVICE_ERROR
;
3853 unhandledError
= TRUE
;
3860 // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
3861 // we know from a previous poll when the device will be ready (ETA)
3862 // we should delay the retry more appropriately than just guessing.
3865 if (fdoExtension->MediaChangeDetectionInfo &&
3866 fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
3867 TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
3868 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
3870 // check if Gesn.ReadyTime if greater than current tick count
3871 // if so, delay that long (from 1 to 30 seconds max?)
3872 // else, leave the guess of time alone.
3878 if (incrementErrorCount
) {
3881 // if any error count occurred, delay the retry of this io by
3882 // at least one second, if caller supports it.
3885 if (retryInterval
== 0) {
3888 ClasspPerfIncrementErrorCount(fdoExtension
);
3892 // If there is a class specific error handler call it.
3895 if (fdoExtension
->CommonExtension
.DevInfo
->ClassError
!= NULL
) {
3897 fdoExtension
->CommonExtension
.DevInfo
->ClassError(Fdo
,
3904 // If the caller wants to know the suggested retry interval tell them.
3907 if(ARGUMENT_PRESENT(RetryInterval
)) {
3908 *RetryInterval
= retryInterval
;
3914 * Always log the error in our internal log.
3915 * If logError is set, also log the error in the system log.
3919 ULONG senseBufferSize
= 0;
3920 IO_ERROR_LOG_PACKET staticErrLogEntry
= {0};
3921 CLASS_ERROR_LOG_DATA staticErrLogData
= {0};
3924 // Calculate the total size of the error log entry.
3925 // add to totalSize in the order that they are used.
3926 // the advantage to calculating all the sizes here is
3927 // that we don't have to do a bunch of extraneous checks
3928 // later on in this code path.
3930 totalSize
= sizeof(IO_ERROR_LOG_PACKET
) // required
3931 - sizeof(ULONG
) // struct includes one ULONG
3932 + sizeof(CLASS_ERROR_LOG_DATA
);// struct for ease
3935 // also save any available extra sense data, up to the maximum errlog
3936 // packet size . WMI should be used for real-time analysis.
3937 // the event log should only be used for post-mortem debugging.
3939 if (TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_AUTOSENSE_VALID
)) {
3940 ULONG validSenseBytes
;
3944 // make sure we can at least access the AdditionalSenseLength field
3946 validSense
= RTL_CONTAINS_FIELD(senseBuffer
,
3947 Srb
->SenseInfoBufferLength
,
3948 AdditionalSenseLength
);
3952 // if extra info exists, copy the maximum amount of available
3953 // sense data that is safe into the the errlog.
3955 validSenseBytes
= senseBuffer
->AdditionalSenseLength
3956 + offsetof(SENSE_DATA
, AdditionalSenseLength
);
3959 // this is invalid because it causes overflow!
3960 // whoever sent this type of request would cause
3963 ASSERT(validSenseBytes
< MAX_ADDITIONAL_SENSE_BYTES
);
3966 // set to save the most sense buffer possible
3968 senseBufferSize
= max(validSenseBytes
, sizeof(SENSE_DATA
));
3969 senseBufferSize
= min(senseBufferSize
, Srb
->SenseInfoBufferLength
);
3972 // it's smaller than required to read the total number of
3973 // valid bytes, so just use the SenseInfoBufferLength field.
3975 senseBufferSize
= Srb
->SenseInfoBufferLength
;
3979 * Bump totalSize by the number of extra senseBuffer bytes
3980 * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
3981 * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
3983 if (senseBufferSize
> sizeof(SENSE_DATA
)){
3984 totalSize
+= senseBufferSize
-sizeof(SENSE_DATA
);
3985 if (totalSize
> ERROR_LOG_MAXIMUM_SIZE
){
3986 senseBufferSize
-= totalSize
-ERROR_LOG_MAXIMUM_SIZE
;
3987 totalSize
= ERROR_LOG_MAXIMUM_SIZE
;
3993 // If we've used up all of our retry attempts, set the final status to
3994 // reflect the appropriate result.
3996 if (retry
&& RetryCount
< MAXIMUM_RETRIES
) {
3997 staticErrLogEntry
.FinalStatus
= STATUS_SUCCESS
;
3998 staticErrLogData
.ErrorRetried
= TRUE
;
4000 staticErrLogEntry
.FinalStatus
= *Status
;
4002 if (TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
4003 staticErrLogData
.ErrorPaging
= TRUE
;
4005 if (unhandledError
) {
4006 staticErrLogData
.ErrorUnhandled
= TRUE
;
4010 // Calculate the device offset if there is a geometry.
4012 staticErrLogEntry
.DeviceOffset
.QuadPart
= (LONGLONG
)badSector
;
4013 staticErrLogEntry
.DeviceOffset
.QuadPart
*= (LONGLONG
)fdoExtension
->DiskGeometry
.BytesPerSector
;
4014 if (logStatus
== -1){
4015 staticErrLogEntry
.ErrorCode
= STATUS_IO_DEVICE_ERROR
;
4017 staticErrLogEntry
.ErrorCode
= logStatus
;
4021 * The dump data follows the IO_ERROR_LOG_PACKET,
4022 * with the first ULONG of dump data inside the packet.
4024 staticErrLogEntry
.DumpDataSize
= (USHORT
)totalSize
- sizeof(IO_ERROR_LOG_PACKET
) + sizeof(ULONG
);
4026 staticErrLogEntry
.SequenceNumber
= 0;
4027 staticErrLogEntry
.MajorFunctionCode
= MajorFunctionCode
;
4028 staticErrLogEntry
.IoControlCode
= IoDeviceCode
;
4029 staticErrLogEntry
.RetryCount
= (UCHAR
) RetryCount
;
4030 staticErrLogEntry
.UniqueErrorValue
= uniqueId
;
4032 KeQueryTickCount(&staticErrLogData
.TickCount
);
4033 staticErrLogData
.PortNumber
= (ULONG
)-1;
4036 * Save the entire contents of the SRB.
4038 staticErrLogData
.Srb
= *Srb
;
4041 * For our private log, save just the default length of the SENSE_DATA.
4043 if (senseBufferSize
!= 0){
4044 RtlCopyMemory(&staticErrLogData
.SenseData
, senseBuffer
, min(senseBufferSize
, sizeof(SENSE_DATA
)));
4048 * Save the error log in our context.
4049 * We only save the default sense buffer length.
4051 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
4052 fdoData
->ErrorLogs
[fdoData
->ErrorLogNextIndex
] = staticErrLogData
;
4053 fdoData
->ErrorLogNextIndex
++;
4054 fdoData
->ErrorLogNextIndex
%= NUM_ERROR_LOG_ENTRIES
;
4055 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
4058 * If logError is set, also save this log in the system's error log.
4059 * But make sure we don't log TUR failures over and over
4060 * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
4062 if ((((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_TEST_UNIT_READY
) && logError
){
4063 if (fdoData
->LoggedTURFailureSinceLastIO
){
4067 fdoData
->LoggedTURFailureSinceLastIO
= TRUE
;
4071 PIO_ERROR_LOG_PACKET errorLogEntry
;
4072 PCLASS_ERROR_LOG_DATA errlogData
;
4074 errorLogEntry
= (PIO_ERROR_LOG_PACKET
)IoAllocateErrorLogEntry(Fdo
, (UCHAR
)totalSize
);
4076 errlogData
= (PCLASS_ERROR_LOG_DATA
)errorLogEntry
->DumpData
;
4078 *errorLogEntry
= staticErrLogEntry
;
4079 *errlogData
= staticErrLogData
;
4082 * For the system log, copy as much of the sense buffer as possible.
4084 if (senseBufferSize
!= 0) {
4085 RtlCopyMemory(&errlogData
->SenseData
, senseBuffer
, senseBufferSize
);
4089 * Write the error log packet to the system error logging thread.
4091 IoWriteErrorLogEntry(errorLogEntry
);
4098 } // end ClassInterpretSenseInfo()
4100 /*++////////////////////////////////////////////////////////////////////////////
4104 Routine Description:
4106 This routine sends a mode sense command to a target ID and returns
4107 when it is complete.
4111 Fdo - Supplies the functional device object associated with this request.
4113 ModeSenseBuffer - Supplies a buffer to store the sense data.
4115 Length - Supplies the length in bytes of the mode sense buffer.
4117 PageMode - Supplies the page or pages of mode sense data to be retrived.
4121 Length of the transferred data is returned.
4124 ULONG NTAPI
ClassModeSense( IN PDEVICE_OBJECT Fdo
,
4125 IN PCHAR ModeSenseBuffer
,
4129 ULONG lengthTransferred
= 0;
4130 PMDL senseBufferMdl
;
4134 senseBufferMdl
= BuildDeviceInputMdl(ModeSenseBuffer
, Length
);
4135 if (senseBufferMdl
){
4137 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
4140 //NTSTATUS pktStatus;
4141 IRP pseudoIrp
= {0};
4144 * Store the number of packets servicing the irp (one)
4145 * inside the original IRP. It will be used to counted down
4146 * to zero when the packet completes.
4147 * Initialize the original IRP's status to success.
4148 * If the packet fails, we will set it to the error status.
4150 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
4151 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
4152 pseudoIrp
.IoStatus
.Information
= 0;
4153 pseudoIrp
.MdlAddress
= senseBufferMdl
;
4156 * Set this up as a SYNCHRONOUS transfer, submit it,
4157 * and wait for the packet to complete. The result
4158 * status will be written to the original irp.
4160 ASSERT(Length
<= 0x0ff);
4161 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
4162 SetupModeSenseTransferPacket(pkt
, &event
, ModeSenseBuffer
, (UCHAR
)Length
, PageMode
, &pseudoIrp
);
4163 SubmitTransferPacket(pkt
);
4164 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
4166 if (NT_SUCCESS(pseudoIrp
.IoStatus
.Status
)){
4167 lengthTransferred
= (ULONG
)pseudoIrp
.IoStatus
.Information
;
4171 * This request can sometimes fail legitimately
4172 * (e.g. when a SCSI device is attached but turned off)
4173 * so this is not necessarily a device/driver bug.
4175 DBGTRACE(ClassDebugWarning
, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo
, pseudoIrp
.IoStatus
.Status
));
4179 FreeDeviceInputMdl(senseBufferMdl
);
4182 return lengthTransferred
;
4185 /*++////////////////////////////////////////////////////////////////////////////
4189 Routine Description:
4191 This routine scans through the mode sense data and finds the requested
4192 mode sense page code.
4195 ModeSenseBuffer - Supplies a pointer to the mode sense data.
4197 Length - Indicates the length of valid data.
4199 PageMode - Supplies the page mode to be searched for.
4201 Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
4205 A pointer to the the requested mode page. If the mode page was not found
4206 then NULL is return.
4212 IN PCHAR ModeSenseBuffer
,
4219 ULONG parameterHeaderLength
;
4220 PVOID result
= NULL
;
4222 limit
= ModeSenseBuffer
+ Length
;
4223 parameterHeaderLength
= (Use6Byte
) ? sizeof(MODE_PARAMETER_HEADER
) : sizeof(MODE_PARAMETER_HEADER10
);
4225 if (Length
>= parameterHeaderLength
) {
4227 PMODE_PARAMETER_HEADER10 modeParam10
;
4228 ULONG blockDescriptorLength
;
4231 * Skip the mode select header and block descriptors.
4234 blockDescriptorLength
= ((PMODE_PARAMETER_HEADER
) ModeSenseBuffer
)->BlockDescriptorLength
;
4237 modeParam10
= (PMODE_PARAMETER_HEADER10
) ModeSenseBuffer
;
4238 blockDescriptorLength
= modeParam10
->BlockDescriptorLength
[1];
4241 ModeSenseBuffer
+= parameterHeaderLength
+ blockDescriptorLength
;
4244 // ModeSenseBuffer now points at pages. Walk the pages looking for the
4245 // requested page until the limit is reached.
4248 while (ModeSenseBuffer
+
4249 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
) < limit
) {
4251 if (((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageCode
== PageMode
) {
4254 * found the mode page. make sure it's safe to touch it all
4255 * before returning the pointer to caller
4258 if (ModeSenseBuffer
+ ((PMODE_DISCONNECT_PAGE
)ModeSenseBuffer
)->PageLength
> limit
) {
4260 * Return NULL since the page is not safe to access in full
4265 result
= ModeSenseBuffer
;
4271 // Advance to the next page which is 4-byte-aligned offset after this page.
4274 ((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageLength
+
4275 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
);
4281 } // end ClassFindModePage()
4283 /*++////////////////////////////////////////////////////////////////////////////
4285 ClassSendSrbAsynchronous()
4287 Routine Description:
4289 This routine takes a partially built Srb and an Irp and sends it down to
4292 This routine must be called with the remove lock held for the specified
4297 Fdo - Supplies the functional device object for the orginal request.
4299 Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
4300 CDB and the SRB timeout value must be filled in. The SRB must not be
4301 allocated from zone.
4303 Irp - Supplies the requesting Irp.
4305 BufferAddress - Supplies a pointer to the buffer to be transfered.
4307 BufferLength - Supplies the length of data transfer.
4309 WriteToDevice - Indicates the data transfer will be from system memory to
4314 Returns STATUS_PENDING if the request is dispatched (since the
4315 completion routine may change the irp's status value we cannot simply
4316 return the value of the dispatch)
4318 or returns a status value to indicate why it failed.
4323 ClassSendSrbAsynchronous(
4325 PSCSI_REQUEST_BLOCK Srb
,
4327 PVOID BufferAddress
,
4329 BOOLEAN WriteToDevice
4333 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
4334 PIO_STACK_LOCATION irpStack
;
4339 // Write length to SRB.
4342 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
4345 // Set SCSI bus address.
4348 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
4351 // This is a violation of the SCSI spec but it is required for
4355 // Srb->Cdb[1] |= deviceExtension->Lun << 5;
4358 // Indicate auto request sense by specifying buffer and size.
4361 Srb
->SenseInfoBuffer
= fdoExtension
->SenseData
;
4362 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
4363 Srb
->DataBuffer
= BufferAddress
;
4366 // Save the class driver specific flags away.
4369 savedFlags
= Srb
->SrbFlags
& SRB_FLAGS_CLASS_DRIVER_RESERVED
;
4372 // Allow the caller to specify that they do not wish
4373 // IoStartNextPacket() to be called in the completion routine.
4376 SET_FLAG(savedFlags
, (Srb
->SrbFlags
& SRB_FLAGS_DONT_START_NEXT_PACKET
));
4378 if (BufferAddress
!= NULL
) {
4381 // Build Mdl if necessary.
4384 if (Irp
->MdlAddress
== NULL
) {
4386 if (IoAllocateMdl(BufferAddress
,
4392 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4395 // ClassIoComplete() would have free'd the srb
4398 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
4399 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
4401 ClassFreeOrReuseSrb(fdoExtension
, Srb
);
4402 ClassReleaseRemoveLock(Fdo
, Irp
);
4403 ClassCompleteRequest(Fdo
, Irp
, IO_NO_INCREMENT
);
4405 return STATUS_INSUFFICIENT_RESOURCES
;
4408 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
4413 // Make sure the buffer requested matches the MDL.
4416 ASSERT(BufferAddress
== MmGetMdlVirtualAddress(Irp
->MdlAddress
));
4423 Srb
->SrbFlags
= WriteToDevice
? SRB_FLAGS_DATA_OUT
: SRB_FLAGS_DATA_IN
;
4431 Srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
;
4435 // Restore saved flags.
4438 SET_FLAG(Srb
->SrbFlags
, savedFlags
);
4441 // Disable synchronous transfer for these requests.
4444 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
4447 // Set the transfer length.
4450 Srb
->DataTransferLength
= BufferLength
;
4456 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
4461 // Save a few parameters in the current stack location.
4464 irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4467 // Save retry count in current Irp stack.
4470 irpStack
->Parameters
.Others
.Argument4
= (PVOID
)MAXIMUM_RETRIES
;
4473 // Set up IoCompletion routine address.
4476 IoSetCompletionRoutine(Irp
, ClassIoComplete
, Srb
, TRUE
, TRUE
, TRUE
);
4479 // Get next stack location and
4480 // set major function code.
4483 irpStack
= IoGetNextIrpStackLocation(Irp
);
4485 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
4488 // Save SRB address in next stack for port driver.
4491 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
4494 // Set up Irp Address.
4497 Srb
->OriginalRequest
= Irp
;
4500 // Call the port driver to process the request.
4503 IoMarkIrpPending(Irp
);
4505 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, Irp
);
4507 return STATUS_PENDING
;
4509 } // end ClassSendSrbAsynchronous()
4511 /*++////////////////////////////////////////////////////////////////////////////
4513 ClassDeviceControlDispatch()
4515 Routine Description:
4517 The routine is the common class driver device control dispatch entry point.
4518 This routine is invokes the device-specific drivers DeviceControl routine,
4519 (which may call the Class driver's common DeviceControl routine).
4523 DeviceObject - Supplies a pointer to the device object for this request.
4525 Irp - Supplies the Irp making the request.
4529 Returns the status returned from the device-specific driver.
4534 ClassDeviceControlDispatch(
4535 PDEVICE_OBJECT DeviceObject
,
4540 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
4543 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
4547 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4549 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
4550 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4551 return STATUS_DEVICE_DOES_NOT_EXIST
;
4555 // Call the class specific driver DeviceControl routine.
4556 // If it doesn't handle it, it will call back into ClassDeviceControl.
4559 ASSERT(commonExtension
->DevInfo
->ClassDeviceControl
);
4561 return commonExtension
->DevInfo
->ClassDeviceControl(DeviceObject
,Irp
);
4562 } // end ClassDeviceControlDispatch()
4564 /*++////////////////////////////////////////////////////////////////////////////
4566 ClassDeviceControl()
4568 Routine Description:
4570 The routine is the common class driver device control dispatch function.
4571 This routine is called by a class driver when it get an unrecognized
4572 device control request. This routine will perform the correct action for
4573 common requests such as lock media. If the device request is unknown it
4574 passed down to the next level.
4576 This routine must be called with the remove lock held for the specified
4581 DeviceObject - Supplies a pointer to the device object for this request.
4583 Irp - Supplies the Irp making the request.
4587 Returns back a STATUS_PENDING or a completion status.
4593 PDEVICE_OBJECT DeviceObject
,
4597 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
4599 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4600 PIO_STACK_LOCATION nextStack
= NULL
;
4602 ULONG controlCode
= irpStack
->Parameters
.DeviceIoControl
.IoControlCode
;
4604 PSCSI_REQUEST_BLOCK srb
= NULL
;
4608 ULONG modifiedIoControlCode
;
4611 // If this is a pass through I/O control, set the minor function code
4612 // and device address and pass it to the port driver.
4615 if ((controlCode
== IOCTL_SCSI_PASS_THROUGH
) ||
4616 (controlCode
== IOCTL_SCSI_PASS_THROUGH_DIRECT
)) {
4618 //PSCSI_PASS_THROUGH scsiPass;
4621 // Validiate the user buffer.
4623 #if defined (_WIN64)
4625 if (IoIs32bitProcess(Irp
)) {
4627 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(SCSI_PASS_THROUGH32
)){
4629 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
4631 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4632 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4634 status
= STATUS_INVALID_PARAMETER
;
4635 goto SetStatusAndReturn
;
4641 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
4642 sizeof(SCSI_PASS_THROUGH
)) {
4644 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
4646 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4647 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4649 status
= STATUS_INVALID_PARAMETER
;
4650 goto SetStatusAndReturn
;
4654 IoCopyCurrentIrpStackLocationToNext(Irp
);
4656 nextStack
= IoGetNextIrpStackLocation(Irp
);
4657 nextStack
->MinorFunction
= 1;
4659 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4661 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4662 goto SetStatusAndReturn
;
4665 Irp
->IoStatus
.Information
= 0;
4667 switch (controlCode
) {
4669 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
: {
4671 PMOUNTDEV_UNIQUE_ID uniqueId
;
4673 if (!commonExtension
->MountedDeviceInterfaceName
.Buffer
) {
4674 status
= STATUS_INVALID_PARAMETER
;
4678 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4679 sizeof(MOUNTDEV_UNIQUE_ID
)) {
4681 status
= STATUS_BUFFER_TOO_SMALL
;
4682 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4686 uniqueId
= Irp
->AssociatedIrp
.SystemBuffer
;
4687 uniqueId
->UniqueIdLength
=
4688 commonExtension
->MountedDeviceInterfaceName
.Length
;
4690 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4691 sizeof(USHORT
) + uniqueId
->UniqueIdLength
) {
4693 status
= STATUS_BUFFER_OVERFLOW
;
4694 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4698 RtlCopyMemory(uniqueId
->UniqueId
,
4699 commonExtension
->MountedDeviceInterfaceName
.Buffer
,
4700 uniqueId
->UniqueIdLength
);
4702 status
= STATUS_SUCCESS
;
4703 Irp
->IoStatus
.Information
= sizeof(USHORT
) +
4704 uniqueId
->UniqueIdLength
;
4708 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
: {
4710 PMOUNTDEV_NAME name
;
4712 ASSERT(commonExtension
->DeviceName
.Buffer
);
4714 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4715 sizeof(MOUNTDEV_NAME
)) {
4717 status
= STATUS_BUFFER_TOO_SMALL
;
4718 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4722 name
= Irp
->AssociatedIrp
.SystemBuffer
;
4723 name
->NameLength
= commonExtension
->DeviceName
.Length
;
4725 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4726 sizeof(USHORT
) + name
->NameLength
) {
4728 status
= STATUS_BUFFER_OVERFLOW
;
4729 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4733 RtlCopyMemory(name
->Name
, commonExtension
->DeviceName
.Buffer
,
4736 status
= STATUS_SUCCESS
;
4737 Irp
->IoStatus
.Information
= sizeof(USHORT
) + name
->NameLength
;
4741 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
: {
4743 PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName
;
4744 WCHAR driveLetterNameBuffer
[10];
4745 RTL_QUERY_REGISTRY_TABLE queryTable
[2];
4747 UNICODE_STRING driveLetterName
;
4749 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4750 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
)) {
4752 status
= STATUS_BUFFER_TOO_SMALL
;
4753 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
4757 valueName
= ExAllocatePoolWithTag(
4759 commonExtension
->DeviceName
.Length
+ sizeof(WCHAR
),
4763 status
= STATUS_INSUFFICIENT_RESOURCES
;
4767 RtlCopyMemory(valueName
, commonExtension
->DeviceName
.Buffer
,
4768 commonExtension
->DeviceName
.Length
);
4769 valueName
[commonExtension
->DeviceName
.Length
/sizeof(WCHAR
)] = 0;
4771 driveLetterName
.Buffer
= driveLetterNameBuffer
;
4772 driveLetterName
.MaximumLength
= 20;
4773 driveLetterName
.Length
= 0;
4775 RtlZeroMemory(queryTable
, 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
4776 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_REQUIRED
|
4777 RTL_QUERY_REGISTRY_DIRECT
;
4778 queryTable
[0].Name
= valueName
;
4779 queryTable
[0].EntryContext
= &driveLetterName
;
4781 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
4782 L
"\\Registry\\Machine\\System\\DISK",
4783 queryTable
, NULL
, NULL
);
4785 if (!NT_SUCCESS(status
)) {
4786 ExFreePool(valueName
);
4790 if (driveLetterName
.Length
== 4 &&
4791 driveLetterName
.Buffer
[0] == '%' &&
4792 driveLetterName
.Buffer
[1] == ':') {
4794 driveLetterName
.Buffer
[0] = 0xFF;
4796 } else if (driveLetterName
.Length
!= 4 ||
4797 driveLetterName
.Buffer
[0] < FirstDriveLetter
||
4798 driveLetterName
.Buffer
[0] > LastDriveLetter
||
4799 driveLetterName
.Buffer
[1] != ':') {
4801 status
= STATUS_NOT_FOUND
;
4802 ExFreePool(valueName
);
4806 suggestedName
= Irp
->AssociatedIrp
.SystemBuffer
;
4807 suggestedName
->UseOnlyIfThereAreNoOtherLinks
= TRUE
;
4808 suggestedName
->NameLength
= 28;
4810 Irp
->IoStatus
.Information
=
4811 FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME
, Name
) + 28;
4813 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4814 Irp
->IoStatus
.Information
) {
4816 Irp
->IoStatus
.Information
=
4817 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
4818 status
= STATUS_BUFFER_OVERFLOW
;
4819 ExFreePool(valueName
);
4823 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
4824 L
"\\Registry\\Machine\\System\\DISK",
4827 ExFreePool(valueName
);
4829 RtlCopyMemory(suggestedName
->Name
, L
"\\DosDevices\\", 24);
4830 suggestedName
->Name
[12] = driveLetterName
.Buffer
[0];
4831 suggestedName
->Name
[13] = ':';
4834 // NT_SUCCESS(status) based on RtlQueryRegistryValues
4836 status
= STATUS_SUCCESS
;
4842 status
= STATUS_PENDING
;
4846 if (status
!= STATUS_PENDING
) {
4847 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4848 Irp
->IoStatus
.Status
= status
;
4849 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4853 if (commonExtension
->IsFdo
){
4855 PULONG_PTR function
;
4857 srb
= ExAllocatePoolWithTag(NonPagedPool
,
4858 sizeof(SCSI_REQUEST_BLOCK
) +
4859 (sizeof(ULONG_PTR
) * 2),
4864 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4865 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4866 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4867 status
= STATUS_INSUFFICIENT_RESOURCES
;
4868 goto SetStatusAndReturn
;
4871 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
4873 cdb
= (PCDB
)srb
->Cdb
;
4876 // Save the function code and the device object in the memory after
4880 function
= (PULONG_PTR
) ((PSCSI_REQUEST_BLOCK
) (srb
+ 1));
4881 *function
= (ULONG_PTR
) DeviceObject
;
4883 *function
= (ULONG_PTR
) controlCode
;
4890 // Change the device type to storage for the switch statement, but only
4891 // if from a legacy device type
4894 if (((controlCode
& 0xffff0000) == (IOCTL_DISK_BASE
<< 16)) ||
4895 ((controlCode
& 0xffff0000) == (IOCTL_TAPE_BASE
<< 16)) ||
4896 ((controlCode
& 0xffff0000) == (IOCTL_CDROM_BASE
<< 16))
4899 modifiedIoControlCode
= (controlCode
& ~0xffff0000);
4900 modifiedIoControlCode
|= (IOCTL_STORAGE_BASE
<< 16);
4904 modifiedIoControlCode
= controlCode
;
4908 DBGTRACE(ClassDebugTrace
, ("> ioctl %xh (%s)", modifiedIoControlCode
, DBGGETIOCTLSTR(modifiedIoControlCode
)));
4910 switch (modifiedIoControlCode
) {
4912 case IOCTL_STORAGE_GET_HOTPLUG_INFO
: {
4919 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4920 sizeof(STORAGE_HOTPLUG_INFO
)) {
4923 // Indicate unsuccessful status and no data transferred.
4926 Irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
4927 Irp
->IoStatus
.Information
= sizeof(STORAGE_HOTPLUG_INFO
);
4929 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4930 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4931 status
= STATUS_BUFFER_TOO_SMALL
;
4933 } else if(!commonExtension
->IsFdo
) {
4936 // Just forward this down and return
4939 IoCopyCurrentIrpStackLocationToNext(Irp
);
4941 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4942 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4946 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
4947 PSTORAGE_HOTPLUG_INFO info
;
4949 fdoExtension
= (PFUNCTIONAL_DEVICE_EXTENSION
)commonExtension
;
4950 info
= Irp
->AssociatedIrp
.SystemBuffer
;
4952 *info
= fdoExtension
->PrivateFdoData
->HotplugInfo
;
4953 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
4954 Irp
->IoStatus
.Information
= sizeof(STORAGE_HOTPLUG_INFO
);
4955 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4956 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4957 status
= STATUS_SUCCESS
;
4963 case IOCTL_STORAGE_SET_HOTPLUG_INFO
: {
4971 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
4972 sizeof(STORAGE_HOTPLUG_INFO
)) {
4975 // Indicate unsuccessful status and no data transferred.
4978 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4980 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4981 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4982 status
= STATUS_INFO_LENGTH_MISMATCH
;
4983 goto SetStatusAndReturn
;
4987 if(!commonExtension
->IsFdo
) {
4990 // Just forward this down and return
4993 IoCopyCurrentIrpStackLocationToNext(Irp
);
4995 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4996 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5000 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PFUNCTIONAL_DEVICE_EXTENSION
)commonExtension
;
5001 PSTORAGE_HOTPLUG_INFO info
= Irp
->AssociatedIrp
.SystemBuffer
;
5003 status
= STATUS_SUCCESS
;
5005 if (info
->Size
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.Size
)
5007 status
= STATUS_INVALID_PARAMETER_1
;
5010 if (info
->MediaRemovable
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.MediaRemovable
)
5012 status
= STATUS_INVALID_PARAMETER_2
;
5015 if (info
->MediaHotplug
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.MediaHotplug
)
5017 status
= STATUS_INVALID_PARAMETER_3
;
5020 if (info
->WriteCacheEnableOverride
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.WriteCacheEnableOverride
)
5022 status
= STATUS_INVALID_PARAMETER_5
;
5025 if (NT_SUCCESS(status
))
5027 fdoExtension
->PrivateFdoData
->HotplugInfo
.DeviceHotplug
= info
->DeviceHotplug
;
5030 // Store the user-defined override in the registry
5033 ClassSetDeviceParameter(fdoExtension
,
5034 CLASSP_REG_SUBKEY_NAME
,
5035 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
5036 (info
->DeviceHotplug
) ? RemovalPolicyExpectSurpriseRemoval
: RemovalPolicyExpectOrderlyRemoval
);
5039 Irp
->IoStatus
.Status
= status
;
5041 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5042 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5048 case IOCTL_STORAGE_CHECK_VERIFY
:
5049 case IOCTL_STORAGE_CHECK_VERIFY2
: {
5052 PIO_STACK_LOCATION newStack
;
5054 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5056 DebugPrint((1,"DeviceIoControl: Check verify\n"));
5059 // If a buffer for a media change count was provided, make sure it's
5060 // big enough to hold the result
5063 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
) {
5066 // If the buffer is too small to hold the media change count
5067 // then return an error to the caller
5070 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
5073 DebugPrint((3,"DeviceIoControl: media count "
5074 "buffer too small\n"));
5076 Irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
5077 Irp
->IoStatus
.Information
= sizeof(ULONG
);
5083 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5084 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5086 status
= STATUS_BUFFER_TOO_SMALL
;
5087 goto SetStatusAndReturn
;
5092 if(!commonExtension
->IsFdo
) {
5095 // If this is a PDO then we should just forward the request down
5099 IoCopyCurrentIrpStackLocationToNext(Irp
);
5101 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5103 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5105 goto SetStatusAndReturn
;
5109 fdoExtension
= DeviceObject
->DeviceExtension
;
5113 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
) {
5116 // The caller has provided a valid buffer. Allocate an additional
5117 // irp and stick the CheckVerify completion routine on it. We will
5118 // then send this down to the port driver instead of the irp the
5122 DebugPrint((2,"DeviceIoControl: Check verify wants "
5126 // Allocate a new irp to send the TestUnitReady to the port driver
5129 irp2
= IoAllocateIrp((CCHAR
) (DeviceObject
->StackSize
+ 3), FALSE
);
5132 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
5133 Irp
->IoStatus
.Information
= 0;
5136 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5137 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5138 status
= STATUS_INSUFFICIENT_RESOURCES
;
5139 goto SetStatusAndReturn
;
5145 // Make sure to acquire the lock for the new irp.
5148 ClassAcquireRemoveLock(DeviceObject
, irp2
);
5150 irp2
->Tail
.Overlay
.Thread
= Irp
->Tail
.Overlay
.Thread
;
5151 IoSetNextIrpStackLocation(irp2
);
5154 // Set the top stack location and shove the master Irp into the
5158 newStack
= IoGetCurrentIrpStackLocation(irp2
);
5159 newStack
->Parameters
.Others
.Argument1
= Irp
;
5160 newStack
->DeviceObject
= DeviceObject
;
5163 // Stick the check verify completion routine onto the stack
5164 // and prepare the irp for the port driver
5167 IoSetCompletionRoutine(irp2
,
5168 ClassCheckVerifyComplete
,
5174 IoSetNextIrpStackLocation(irp2
);
5175 newStack
= IoGetCurrentIrpStackLocation(irp2
);
5176 newStack
->DeviceObject
= DeviceObject
;
5177 newStack
->MajorFunction
= irpStack
->MajorFunction
;
5178 newStack
->MinorFunction
= irpStack
->MinorFunction
;
5181 // Mark the master irp as pending - whether the lower level
5182 // driver completes it immediately or not this should allow it
5183 // to go all the way back up.
5186 IoMarkIrpPending(Irp
);
5197 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_TEST_UNIT_READY
;
5200 // Set timeout value.
5203 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5206 // If this was a CV2 then mark the request as low-priority so we don't
5207 // spin up the drive just to satisfy it.
5210 if(controlCode
== IOCTL_STORAGE_CHECK_VERIFY2
) {
5211 SET_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_LOW_PRIORITY
);
5215 // Since this routine will always hand the request to the
5216 // port driver if there isn't a data transfer to be done
5217 // we don't have to worry about completing the request here
5222 // This routine uses a completion routine so we don't want to release
5223 // the remove lock until then.
5226 status
= ClassSendSrbAsynchronous(DeviceObject
,
5236 case IOCTL_STORAGE_MEDIA_REMOVAL
:
5237 case IOCTL_STORAGE_EJECTION_CONTROL
: {
5239 PPREVENT_MEDIA_REMOVAL mediaRemoval
= Irp
->AssociatedIrp
.SystemBuffer
;
5241 DebugPrint((3, "DiskIoControl: ejection control\n"));
5247 if(irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
5248 sizeof(PREVENT_MEDIA_REMOVAL
)) {
5251 // Indicate unsuccessful status and no data transferred.
5254 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5256 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5257 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5258 status
= STATUS_INFO_LENGTH_MISMATCH
;
5259 goto SetStatusAndReturn
;
5262 if(!commonExtension
->IsFdo
) {
5265 // Just forward this down and return
5268 IoCopyCurrentIrpStackLocationToNext(Irp
);
5270 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5271 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5275 // i don't believe this assertion is valid. this is a request
5276 // from user-mode, so they could request this for any device
5277 // they want? also, we handle it properly.
5278 // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
5279 status
= ClasspEjectionControl(
5282 ((modifiedIoControlCode
==
5283 IOCTL_STORAGE_EJECTION_CONTROL
) ? SecureMediaLock
:
5285 mediaRemoval
->PreventMediaRemoval
);
5287 Irp
->IoStatus
.Status
= status
;
5288 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5289 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5295 case IOCTL_STORAGE_MCN_CONTROL
: {
5297 DebugPrint((3, "DiskIoControl: MCN control\n"));
5299 if(irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
5300 sizeof(PREVENT_MEDIA_REMOVAL
)) {
5303 // Indicate unsuccessful status and no data transferred.
5306 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5307 Irp
->IoStatus
.Information
= 0;
5313 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5314 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5315 status
= STATUS_INFO_LENGTH_MISMATCH
;
5316 goto SetStatusAndReturn
;
5319 if(!commonExtension
->IsFdo
) {
5322 // Just forward this down and return
5329 IoCopyCurrentIrpStackLocationToNext(Irp
);
5331 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5332 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5337 // Call to the FDO - handle the ejection control.
5340 status
= ClasspMcnControl(DeviceObject
->DeviceExtension
,
5344 goto SetStatusAndReturn
;
5347 case IOCTL_STORAGE_RESERVE
:
5348 case IOCTL_STORAGE_RELEASE
: {
5351 // Reserve logical unit.
5354 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5356 if(!commonExtension
->IsFdo
) {
5358 IoCopyCurrentIrpStackLocationToNext(Irp
);
5360 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5361 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5362 goto SetStatusAndReturn
;
5364 fdoExtension
= DeviceObject
->DeviceExtension
;
5369 if(modifiedIoControlCode
== IOCTL_STORAGE_RESERVE
) {
5370 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_RESERVE_UNIT
;
5372 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_RELEASE_UNIT
;
5376 // Set timeout value.
5379 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5381 status
= ClassSendSrbAsynchronous(DeviceObject
,
5391 case IOCTL_STORAGE_EJECT_MEDIA
:
5392 case IOCTL_STORAGE_LOAD_MEDIA
:
5393 case IOCTL_STORAGE_LOAD_MEDIA2
:{
5399 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5401 if(!commonExtension
->IsFdo
) {
5403 IoCopyCurrentIrpStackLocationToNext(Irp
);
5405 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5407 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5408 goto SetStatusAndReturn
;
5410 fdoExtension
= DeviceObject
->DeviceExtension
;
5413 if(commonExtension
->PagingPathCount
!= 0) {
5415 DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
5418 status
= STATUS_FILES_OPEN
;
5419 Irp
->IoStatus
.Status
= status
;
5421 Irp
->IoStatus
.Information
= 0;
5427 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5428 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5429 goto SetStatusAndReturn
;
5433 // Synchronize with ejection control and ejection cleanup code as
5434 // well as other eject/load requests.
5437 KeEnterCriticalRegion();
5438 KeWaitForSingleObject(&(fdoExtension
->EjectSynchronizationEvent
),
5444 if(fdoExtension
->ProtectedLockCount
!= 0) {
5446 DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
5447 "device - failure\n"));
5449 status
= STATUS_DEVICE_BUSY
;
5450 Irp
->IoStatus
.Status
= status
;
5451 Irp
->IoStatus
.Information
= 0;
5457 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5458 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5460 KeSetEvent(&fdoExtension
->EjectSynchronizationEvent
,
5463 KeLeaveCriticalRegion();
5465 goto SetStatusAndReturn
;
5470 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
5471 cdb
->START_STOP
.LoadEject
= 1;
5473 if(modifiedIoControlCode
== IOCTL_STORAGE_EJECT_MEDIA
) {
5474 cdb
->START_STOP
.Start
= 0;
5476 cdb
->START_STOP
.Start
= 1;
5480 // Set timeout value.
5483 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5484 status
= ClassSendSrbAsynchronous(DeviceObject
,
5491 KeSetEvent(&fdoExtension
->EjectSynchronizationEvent
, IO_NO_INCREMENT
, FALSE
);
5492 KeLeaveCriticalRegion();
5497 case IOCTL_STORAGE_FIND_NEW_DEVICES
: {
5503 if(commonExtension
->IsFdo
) {
5505 IoInvalidateDeviceRelations(
5506 ((PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
)->LowerPdo
,
5509 status
= STATUS_SUCCESS
;
5510 Irp
->IoStatus
.Status
= status
;
5512 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5513 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5517 IoCopyCurrentIrpStackLocationToNext(Irp
);
5519 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5520 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5525 case IOCTL_STORAGE_GET_DEVICE_NUMBER
: {
5531 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
>=
5532 sizeof(STORAGE_DEVICE_NUMBER
)) {
5534 PSTORAGE_DEVICE_NUMBER deviceNumber
=
5535 Irp
->AssociatedIrp
.SystemBuffer
;
5536 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
5537 commonExtension
->PartitionZeroExtension
;
5539 deviceNumber
->DeviceType
= fdoExtension
->CommonExtension
.DeviceObject
->DeviceType
;
5540 deviceNumber
->DeviceNumber
= fdoExtension
->DeviceNumber
;
5541 deviceNumber
->PartitionNumber
= commonExtension
->PartitionNumber
;
5543 status
= STATUS_SUCCESS
;
5544 Irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_NUMBER
);
5547 status
= STATUS_BUFFER_TOO_SMALL
;
5548 Irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_NUMBER
);
5551 Irp
->IoStatus
.Status
= status
;
5552 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5553 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5560 DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
5561 controlCode
, DeviceObject
));
5564 // Pass the device control to the next driver.
5572 // Copy the Irp stack parameters to the next stack location.
5575 IoCopyCurrentIrpStackLocationToNext(Irp
);
5577 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5578 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5582 } // end switch( ...
5586 DBGTRACE(ClassDebugTrace
, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode
, DBGGETIOCTLSTR(modifiedIoControlCode
), status
));
5589 } // end ClassDeviceControl()
5591 /*++////////////////////////////////////////////////////////////////////////////
5593 ClassShutdownFlush()
5595 Routine Description:
5597 This routine is called for a shutdown and flush IRPs. These are sent by the
5598 system before it actually shuts down or when the file system does a flush.
5599 If it exists, the device-specific driver's routine will be invoked. If there
5600 wasn't one specified, the Irp will be completed with an Invalid device request.
5604 DriverObject - Pointer to device object to being shutdown by system.
5616 IN PDEVICE_OBJECT DeviceObject
,
5620 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
5626 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
5630 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5632 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
5634 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5636 return STATUS_DEVICE_DOES_NOT_EXIST
;
5639 if (commonExtension
->DevInfo
->ClassShutdownFlush
) {
5642 // Call the device-specific driver's routine.
5645 return commonExtension
->DevInfo
->ClassShutdownFlush(DeviceObject
, Irp
);
5649 // Device-specific driver doesn't support this.
5652 Irp
->IoStatus
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
5654 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5655 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5657 return STATUS_INVALID_DEVICE_REQUEST
;
5658 } // end ClassShutdownFlush()
5660 /*++////////////////////////////////////////////////////////////////////////////
5662 ClassCreateDeviceObject()
5664 Routine Description:
5666 This routine creates an object for the physical device specified and
5667 sets up the deviceExtension's function pointers for each entry point
5668 in the device-specific driver.
5672 DriverObject - Pointer to driver object created by system.
5674 ObjectNameBuffer - Dir. name of the object to create.
5676 LowerDeviceObject - Pointer to the lower device object
5678 IsFdo - should this be an fdo or a pdo
5680 DeviceObject - Pointer to the device object pointer we will return.
5689 ClassCreateDeviceObject(
5690 IN PDRIVER_OBJECT DriverObject
,
5691 IN PCCHAR ObjectNameBuffer
,
5692 IN PDEVICE_OBJECT LowerDevice
,
5694 IN OUT PDEVICE_OBJECT
*DeviceObject
5697 BOOLEAN isPartitionable
;
5698 STRING ntNameString
;
5699 UNICODE_STRING ntUnicodeString
;
5701 PDEVICE_OBJECT deviceObject
= NULL
;
5703 ULONG characteristics
;
5705 PCLASS_DRIVER_EXTENSION
5706 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
5707 CLASS_DRIVER_EXTENSION_KEY
);
5709 PCLASS_DEV_INFO devInfo
;
5713 *DeviceObject
= NULL
;
5714 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5716 DebugPrint((2, "ClassCreateFdo: Create device object\n"));
5718 ASSERT(LowerDevice
);
5721 // Make sure that if we're making PDO's we have an enumeration routine
5724 isPartitionable
= (driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
5726 ASSERT(IsFdo
|| isPartitionable
);
5729 // Grab the correct dev-info structure out of the init data
5733 devInfo
= &(driverExtension
->InitData
.FdoData
);
5735 devInfo
= &(driverExtension
->InitData
.PdoData
);
5738 characteristics
= devInfo
->DeviceCharacteristics
;
5740 if(ARGUMENT_PRESENT(ObjectNameBuffer
)) {
5741 DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer
));
5743 RtlInitString(&ntNameString
, ObjectNameBuffer
);
5745 status
= RtlAnsiStringToUnicodeString(&ntUnicodeString
, &ntNameString
, TRUE
);
5747 if (!NT_SUCCESS(status
)) {
5750 "ClassCreateFdo: Cannot convert string %s\n",
5753 ntUnicodeString
.Buffer
= NULL
;
5757 DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
5759 if(IsFdo
== FALSE
) {
5762 // PDO's have to have some sort of name.
5765 SET_FLAG(characteristics
, FILE_AUTOGENERATED_DEVICE_NAME
);
5768 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5771 status
= IoCreateDevice(DriverObject
,
5772 devInfo
->DeviceExtensionSize
,
5774 devInfo
->DeviceType
,
5775 devInfo
->DeviceCharacteristics
,
5779 if (!NT_SUCCESS(status
)) {
5781 DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
5783 ASSERT(deviceObject
== NULL
);
5786 // buffer is not used any longer here.
5789 if (ntUnicodeString
.Buffer
!= NULL
) {
5790 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5791 ExFreePool(ntUnicodeString
.Buffer
);
5792 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5797 PCOMMON_DEVICE_EXTENSION commonExtension
= deviceObject
->DeviceExtension
;
5800 deviceObject
->DeviceExtension
,
5801 devInfo
->DeviceExtensionSize
);
5804 // Setup version code
5807 commonExtension
->Version
= 0x03;
5810 // Setup the remove lock and event
5813 commonExtension
->IsRemoved
= NO_REMOVE
;
5814 commonExtension
->RemoveLock
= 0;
5815 KeInitializeEvent(&commonExtension
->RemoveEvent
,
5816 SynchronizationEvent
,
5820 KeInitializeSpinLock(&commonExtension
->RemoveTrackingSpinlock
);
5821 commonExtension
->RemoveTrackingList
= NULL
;
5823 commonExtension
->RemoveTrackingSpinlock
= (ULONG_PTR
) -1;
5824 commonExtension
->RemoveTrackingList
= (PVOID
) -1;
5828 // Acquire the lock once. This reference will be released when the
5829 // remove IRP has been received.
5832 ClassAcquireRemoveLock(deviceObject
, (PIRP
) deviceObject
);
5835 // Store a pointer to the driver extension so we don't have to do
5836 // lookups to get it.
5839 commonExtension
->DriverExtension
= driverExtension
;
5842 // Fill in entry points
5845 commonExtension
->DevInfo
= devInfo
;
5848 // Initialize some of the common values in the structure
5851 commonExtension
->DeviceObject
= deviceObject
;
5853 commonExtension
->LowerDeviceObject
= NULL
;
5857 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PVOID
) commonExtension
;
5859 commonExtension
->PartitionZeroExtension
= deviceObject
->DeviceExtension
;
5862 // Set the initial device object flags.
5865 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5868 // Clear the PDO list
5871 commonExtension
->ChildList
= NULL
;
5873 commonExtension
->DriverData
=
5874 ((PFUNCTIONAL_DEVICE_EXTENSION
) deviceObject
->DeviceExtension
+ 1);
5876 if(isPartitionable
) {
5878 commonExtension
->PartitionNumber
= 0;
5880 commonExtension
->PartitionNumber
= (ULONG
) (-1L);
5883 fdoExtension
->DevicePowerState
= PowerDeviceD0
;
5885 KeInitializeEvent(&fdoExtension
->EjectSynchronizationEvent
,
5886 SynchronizationEvent
,
5889 KeInitializeEvent(&fdoExtension
->ChildLock
,
5890 SynchronizationEvent
,
5893 status
= ClasspAllocateReleaseRequest(deviceObject
);
5895 if(!NT_SUCCESS(status
)) {
5896 IoDeleteDevice(deviceObject
);
5897 *DeviceObject
= NULL
;
5899 if (ntUnicodeString
.Buffer
!= NULL
) {
5900 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5901 ExFreePool(ntUnicodeString
.Buffer
);
5902 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5910 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
5911 deviceObject
->DeviceExtension
;
5913 PFUNCTIONAL_DEVICE_EXTENSION p0Extension
=
5914 LowerDevice
->DeviceExtension
;
5916 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5918 commonExtension
->PartitionZeroExtension
= p0Extension
;
5921 // Stick this onto the PDO list
5924 ClassAddChild(p0Extension
, pdoExtension
, TRUE
);
5926 commonExtension
->DriverData
= (PVOID
) (pdoExtension
+ 1);
5929 // Get the top of stack for the lower device - this allows
5930 // filters to get stuck in between the partitions and the
5934 commonExtension
->LowerDeviceObject
=
5935 IoGetAttachedDeviceReference(LowerDevice
);
5938 // Pnp will keep a reference to the lower device object long
5939 // after this partition has been deleted. Dereference now so
5940 // we don't have to deal with it later.
5943 ObDereferenceObject(commonExtension
->LowerDeviceObject
);
5946 KeInitializeEvent(&commonExtension
->PathCountEvent
, SynchronizationEvent
, TRUE
);
5948 commonExtension
->IsFdo
= IsFdo
;
5950 commonExtension
->DeviceName
= ntUnicodeString
;
5952 commonExtension
->PreviousState
= 0xff;
5954 InitializeDictionary(&(commonExtension
->FileObjectDictionary
));
5956 commonExtension
->CurrentState
= IRP_MN_STOP_DEVICE
;
5959 *DeviceObject
= deviceObject
;
5962 } // end ClassCreateDeviceObject()
5964 /*++////////////////////////////////////////////////////////////////////////////
5968 Routine Description:
5970 This function claims a device in the port driver. The port driver object
5971 is updated with the correct driver object if the device is successfully
5976 LowerDeviceObject - Supplies the base port device object.
5978 Release - Indicates the logical unit should be released rather than claimed.
5982 Returns a status indicating success or failure of the operation.
5988 IN PDEVICE_OBJECT LowerDeviceObject
,
5992 IO_STATUS_BLOCK ioStatus
;
5994 PIO_STACK_LOCATION irpStack
;
5997 SCSI_REQUEST_BLOCK srb
;
6002 // Clear the SRB fields.
6005 RtlZeroMemory(&srb
, sizeof(SCSI_REQUEST_BLOCK
));
6008 // Write length to SRB.
6011 srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
6013 srb
.Function
= Release
? SRB_FUNCTION_RELEASE_DEVICE
:
6014 SRB_FUNCTION_CLAIM_DEVICE
;
6017 // Set the event object to the unsignaled state.
6018 // It will be used to signal request completion
6021 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
6024 // Build synchronous request with no transfer.
6027 irp
= IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE
,
6038 DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
6039 return STATUS_INSUFFICIENT_RESOURCES
;
6042 irpStack
= IoGetNextIrpStackLocation(irp
);
6045 // Save SRB address in next stack for port driver.
6048 irpStack
->Parameters
.Scsi
.Srb
= &srb
;
6051 // Set up IRP Address.
6054 srb
.OriginalRequest
= irp
;
6057 // Call the port driver with the request and wait for it to complete.
6060 status
= IoCallDriver(LowerDeviceObject
, irp
);
6061 if (status
== STATUS_PENDING
) {
6063 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
6064 status
= ioStatus
.Status
;
6068 // If this is a release request, then just decrement the reference count
6069 // and return. The status does not matter.
6074 // ObDereferenceObject(LowerDeviceObject);
6075 return STATUS_SUCCESS
;
6078 if (!NT_SUCCESS(status
)) {
6082 ASSERT(srb
.DataBuffer
!= NULL
);
6083 ASSERT(!TEST_FLAG(srb
.SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
6086 } // end ClassClaimDevice()
6088 /*++////////////////////////////////////////////////////////////////////////////
6090 ClassInternalIoControl()
6092 Routine Description:
6094 This routine passes internal device controls to the port driver.
6095 Internal device controls are used by higher level drivers both for ioctls
6096 and to pass through scsi requests.
6098 If the IoControlCode does not match any of the handled ioctls and is
6099 a valid system address then the request will be treated as an SRB and
6100 passed down to the lower driver. If the IoControlCode is not a valid
6101 system address the ioctl will be failed.
6103 Callers must therefore be extremely cautious to pass correct, initialized
6104 values to this function.
6108 DeviceObject - Supplies a pointer to the device object for this request.
6110 Irp - Supplies the Irp making the request.
6114 Returns back a STATUS_PENDING or a completion status.
6119 ClassInternalIoControl(
6120 IN PDEVICE_OBJECT DeviceObject
,
6124 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6126 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6127 PIO_STACK_LOCATION nextStack
= IoGetNextIrpStackLocation(Irp
);
6131 PSCSI_REQUEST_BLOCK srb
;
6133 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
6137 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
6139 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6141 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
6143 return STATUS_DEVICE_DOES_NOT_EXIST
;
6147 // Get a pointer to the SRB.
6150 srb
= irpStack
->Parameters
.Scsi
.Srb
;
6153 // Set the parameters in the next stack location.
6156 if(commonExtension
->IsFdo
) {
6157 nextStack
->Parameters
.Scsi
.Srb
= srb
;
6158 nextStack
->MajorFunction
= IRP_MJ_SCSI
;
6159 nextStack
->MinorFunction
= IRP_MN_SCSI_CLASS
;
6163 IoCopyCurrentIrpStackLocationToNext(Irp
);
6166 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6168 return IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
6169 } // end ClassInternalIoControl()
6171 /*++////////////////////////////////////////////////////////////////////////////
6173 ClassQueryTimeOutRegistryValue()
6175 Routine Description:
6177 This routine determines whether a reg key for a user-specified timeout
6178 value exists. This should be called at initialization time.
6182 DeviceObject - Pointer to the device object we are retrieving the timeout
6187 None, but it sets a new default timeout for a class of devices.
6192 ClassQueryTimeOutRegistryValue(
6193 IN PDEVICE_OBJECT DeviceObject
6197 // Find the appropriate reg. key
6200 PCLASS_DRIVER_EXTENSION
6201 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6202 CLASS_DRIVER_EXTENSION_KEY
);
6204 PUNICODE_STRING registryPath
= &(driverExtension
->RegistryPath
);
6206 PRTL_QUERY_REGISTRY_TABLE parameters
= NULL
;
6215 if (!registryPath
) {
6219 parameters
= ExAllocatePoolWithTag(NonPagedPool
,
6220 sizeof(RTL_QUERY_REGISTRY_TABLE
)*2,
6227 size
= registryPath
->MaximumLength
+ sizeof(WCHAR
);
6228 path
= ExAllocatePoolWithTag(NonPagedPool
, size
, '2BcS');
6231 ExFreePool(parameters
);
6235 RtlZeroMemory(path
,size
);
6236 RtlCopyMemory(path
, registryPath
->Buffer
, size
- sizeof(WCHAR
));
6240 // Check for the Timeout value.
6243 RtlZeroMemory(parameters
,
6244 (sizeof(RTL_QUERY_REGISTRY_TABLE
)*2));
6246 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
6247 parameters
[0].Name
= L
"TimeOutValue";
6248 parameters
[0].EntryContext
= &timeOut
;
6249 parameters
[0].DefaultType
= REG_DWORD
;
6250 parameters
[0].DefaultData
= &zero
;
6251 parameters
[0].DefaultLength
= sizeof(ULONG
);
6253 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
| RTL_REGISTRY_OPTIONAL
,
6259 if (!(NT_SUCCESS(status
))) {
6263 ExFreePool(parameters
);
6267 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
6273 } // end ClassQueryTimeOutRegistryValue()
6275 /*++////////////////////////////////////////////////////////////////////////////
6277 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
6279 Routine Description:
6281 This routine executes when the port driver has completed a check verify
6282 ioctl. It will set the status of the master Irp, copy the media change
6283 count and complete the request.
6287 Fdo - Supplies the functional device object which represents the logical unit.
6289 Irp - Supplies the Irp which has completed.
6300 ClassCheckVerifyComplete(
6301 IN PDEVICE_OBJECT Fdo
,
6306 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6307 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6313 originalIrp
= irpStack
->Parameters
.Others
.Argument1
;
6316 // Copy the media change count and status
6319 *((PULONG
) (originalIrp
->AssociatedIrp
.SystemBuffer
)) =
6320 fdoExtension
->MediaChangeCount
;
6322 DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
6323 "device %d is %lx - saved as %lx\n",
6324 fdoExtension
->DeviceNumber
,
6325 fdoExtension
->MediaChangeCount
,
6326 *((PULONG
) originalIrp
->AssociatedIrp
.SystemBuffer
)));
6328 originalIrp
->IoStatus
.Status
= Irp
->IoStatus
.Status
;
6329 originalIrp
->IoStatus
.Information
= sizeof(ULONG
);
6331 ClassReleaseRemoveLock(Fdo
, originalIrp
);
6332 ClassCompleteRequest(Fdo
, originalIrp
, IO_DISK_INCREMENT
);
6336 return STATUS_MORE_PROCESSING_REQUIRED
;
6338 } // end ClassCheckVerifyComplete()
6340 /*++////////////////////////////////////////////////////////////////////////////
6342 ClassGetDescriptor()
6344 Routine Description:
6346 This routine will perform a query for the specified property id and will
6347 allocate a non-paged buffer to store the data in. It is the responsibility
6348 of the caller to ensure that this buffer is freed.
6350 This routine must be run at IRQL_PASSIVE_LEVEL
6354 DeviceObject - the device to query
6355 DeviceInfo - a location to store a pointer to the buffer we allocate
6360 if status is unsuccessful *DeviceInfo will be set to NULL, else the
6361 buffer allocated on behalf of the caller.
6367 IN PDEVICE_OBJECT DeviceObject
,
6368 IN PSTORAGE_PROPERTY_ID PropertyId
,
6369 OUT PSTORAGE_DESCRIPTOR_HEADER
*Descriptor
6372 STORAGE_PROPERTY_QUERY query
;
6373 IO_STATUS_BLOCK ioStatus
;
6375 PSTORAGE_DESCRIPTOR_HEADER descriptor
= NULL
;
6383 // Set the passed-in descriptor pointer to NULL as default
6389 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6390 query
.PropertyId
= *PropertyId
;
6391 query
.QueryType
= PropertyStandardQuery
;
6394 // On the first pass we just want to get the first few
6395 // bytes of the descriptor so we can read it's size
6398 descriptor
= (PVOID
)&query
;
6400 ASSERT(sizeof(STORAGE_PROPERTY_QUERY
) >= (sizeof(ULONG
)*2));
6402 ClassSendDeviceIoControlSynchronous(
6403 IOCTL_STORAGE_QUERY_PROPERTY
,
6406 sizeof(STORAGE_PROPERTY_QUERY
),
6412 if(!NT_SUCCESS(ioStatus
.Status
)) {
6414 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6415 "query properties #1\n", ioStatus
.Status
));
6416 return ioStatus
.Status
;
6419 if (descriptor
->Size
== 0) {
6422 // This DebugPrint is to help third-party driver writers
6425 DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
6426 "%x\n", ioStatus
.Status
));
6427 return STATUS_UNSUCCESSFUL
;
6432 // This time we know how much data there is so we can
6433 // allocate a buffer of the correct size
6436 length
= descriptor
->Size
;
6438 descriptor
= ExAllocatePoolWithTag(NonPagedPool
, length
, '4BcS');
6440 if(descriptor
== NULL
) {
6442 DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
6443 "(%d bytes)\n", length
));
6444 return STATUS_INSUFFICIENT_RESOURCES
;
6448 // setup the query again, as it was overwritten above
6451 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6452 query
.PropertyId
= *PropertyId
;
6453 query
.QueryType
= PropertyStandardQuery
;
6456 // copy the input to the new outputbuffer
6459 RtlCopyMemory(descriptor
,
6461 sizeof(STORAGE_PROPERTY_QUERY
)
6464 ClassSendDeviceIoControlSynchronous(
6465 IOCTL_STORAGE_QUERY_PROPERTY
,
6468 sizeof(STORAGE_PROPERTY_QUERY
),
6474 if(!NT_SUCCESS(ioStatus
.Status
)) {
6476 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6477 "query properties #1\n", ioStatus
.Status
));
6478 ExFreePool(descriptor
);
6479 return ioStatus
.Status
;
6483 // return the memory we've allocated to the caller
6486 *Descriptor
= descriptor
;
6487 return ioStatus
.Status
;
6488 } // end ClassGetDescriptor()
6490 /*++////////////////////////////////////////////////////////////////////////////
6492 ClassSignalCompletion()
6494 Routine Description:
6496 This completion routine will signal the event given as context and then
6497 return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
6498 the responsibility of the routine waiting on the event to complete the
6499 request and free the event.
6503 DeviceObject - a pointer to the device object
6505 Irp - a pointer to the irp
6507 Event - a pointer to the event to signal
6511 STATUS_MORE_PROCESSING_REQUIRED
6516 ClassSignalCompletion(
6517 IN PDEVICE_OBJECT DeviceObject
,
6522 KeSetEvent(Event
, IO_NO_INCREMENT
, FALSE
);
6524 return STATUS_MORE_PROCESSING_REQUIRED
;
6525 } // end ClassSignalCompletion()
6527 /*++////////////////////////////////////////////////////////////////////////////
6529 ClassPnpQueryFdoRelations()
6531 Routine Description:
6533 This routine will call the driver's enumeration routine to update the
6534 list of PDO's. It will then build a response to the
6535 IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
6540 Fdo - a pointer to the functional device object we are enumerating
6542 Irp - a pointer to the enumeration request
6551 ClassPnpQueryFdoRelations(
6552 IN PDEVICE_OBJECT Fdo
,
6556 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6557 PCLASS_DRIVER_EXTENSION
6558 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6559 CLASS_DRIVER_EXTENSION_KEY
);
6565 // If there's already an enumeration in progress then don't start another
6569 if(InterlockedIncrement(&(fdoExtension
->EnumerationInterlock
)) == 1) {
6570 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6573 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
6575 Irp
->IoStatus
.Status
= ClassRetrieveDeviceRelations(
6578 (PDEVICE_RELATIONS
*)&Irp
->IoStatus
.Information
);
6579 InterlockedDecrement(&(fdoExtension
->EnumerationInterlock
));
6581 return Irp
->IoStatus
.Status
;
6582 } // end ClassPnpQueryFdoRelations()
6584 /*++////////////////////////////////////////////////////////////////////////////
6586 ClassMarkChildrenMissing()
6588 Routine Description:
6590 This routine will call ClassMarkChildMissing() for all children.
6591 It acquires the ChildLock before calling ClassMarkChildMissing().
6595 Fdo - the "bus's" device object, such as the disk FDO for non-removable
6596 disks with multiple partitions.
6605 ClassMarkChildrenMissing(
6606 IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
6609 PCOMMON_DEVICE_EXTENSION commonExtension
= &(Fdo
->CommonExtension
);
6610 PPHYSICAL_DEVICE_EXTENSION nextChild
= commonExtension
->ChildList
;
6614 ClassAcquireChildLock(Fdo
);
6617 PPHYSICAL_DEVICE_EXTENSION tmpChild
;
6620 * ClassMarkChildMissing will also dequeue the child extension.
6621 * So get the next pointer before calling ClassMarkChildMissing.
6623 tmpChild
= nextChild
;
6624 nextChild
= tmpChild
->CommonExtension
.ChildList
;
6625 ClassMarkChildMissing(tmpChild
, FALSE
);
6627 ClassReleaseChildLock(Fdo
);
6629 } // end ClassMarkChildrenMissing()
6631 /*++////////////////////////////////////////////////////////////////////////////
6633 ClassMarkChildMissing()
6635 Routine Description:
6637 This routine will make an active child "missing." If the device has never
6638 been enumerated then it will be deleted on the spot. If the device has
6639 not been enumerated then it will be marked as missing so that we can
6640 not report it in the next device enumeration.
6644 Child - the child device to be marked as missing.
6646 AcquireChildLock - TRUE if the child lock should be acquired before removing
6647 the missing child. FALSE if the child lock is already
6648 acquired by this thread.
6652 returns whether or not the child device object has previously been reported
6658 ClassMarkChildMissing(
6659 IN PPHYSICAL_DEVICE_EXTENSION Child
,
6660 IN BOOLEAN AcquireChildLock
6663 BOOLEAN returnValue
= Child
->IsEnumerated
;
6666 ASSERT_PDO(Child
->DeviceObject
);
6668 Child
->IsMissing
= TRUE
;
6671 // Make sure this child is not in the active list.
6674 ClassRemoveChild(Child
->CommonExtension
.PartitionZeroExtension
,
6678 if(Child
->IsEnumerated
== FALSE
) {
6679 ClassRemoveDevice(Child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
6683 } // end ClassMarkChildMissing()
6685 /*++////////////////////////////////////////////////////////////////////////////
6687 ClassRetrieveDeviceRelations()
6689 Routine Description:
6691 This routine will allocate a buffer to hold the specified list of
6692 relations. It will then fill in the list with referenced device pointers
6693 and will return the request.
6697 Fdo - pointer to the FDO being queried
6699 RelationType - what type of relations are being queried
6701 DeviceRelations - a location to store a pointer to the response
6710 ClassRetrieveDeviceRelations(
6711 IN PDEVICE_OBJECT Fdo
,
6712 IN DEVICE_RELATION_TYPE RelationType
,
6713 OUT PDEVICE_RELATIONS
*DeviceRelations
6716 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6721 PPHYSICAL_DEVICE_EXTENSION nextChild
;
6723 ULONG relationsSize
;
6724 PDEVICE_RELATIONS deviceRelations
= NULL
;
6730 ClassAcquireChildLock(fdoExtension
);
6732 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6735 // Count the number of PDO's attached to this disk
6738 while(nextChild
!= NULL
) {
6739 PCOMMON_DEVICE_EXTENSION commonExtension
;
6741 commonExtension
= &(nextChild
->CommonExtension
);
6743 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6744 (nextChild
->IsMissing
== FALSE
));
6746 nextChild
= commonExtension
->ChildList
;
6751 relationsSize
= (sizeof(DEVICE_RELATIONS
) +
6752 (count
* sizeof(PDEVICE_OBJECT
)));
6754 deviceRelations
= ExAllocatePoolWithTag(PagedPool
, relationsSize
, '5BcS');
6756 if(deviceRelations
== NULL
) {
6758 DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
6759 "%d bytes for device relations\n", relationsSize
));
6761 ClassReleaseChildLock(fdoExtension
);
6763 return STATUS_INSUFFICIENT_RESOURCES
;
6766 RtlZeroMemory(deviceRelations
, relationsSize
);
6768 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6771 while(nextChild
!= NULL
) {
6772 PCOMMON_DEVICE_EXTENSION commonExtension
;
6774 commonExtension
= &(nextChild
->CommonExtension
);
6776 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6777 (nextChild
->IsMissing
== FALSE
));
6779 deviceRelations
->Objects
[i
--] = nextChild
->DeviceObject
;
6781 status
= ObReferenceObjectByPointer(
6782 nextChild
->DeviceObject
,
6786 ASSERT(NT_SUCCESS(status
));
6788 nextChild
->IsEnumerated
= TRUE
;
6789 nextChild
= commonExtension
->ChildList
;
6792 ASSERTMSG("Child list has changed: ", i
== -1);
6794 deviceRelations
->Count
= count
;
6795 *DeviceRelations
= deviceRelations
;
6796 ClassReleaseChildLock(fdoExtension
);
6797 return STATUS_SUCCESS
;
6798 } // end ClassRetrieveDeviceRelations()
6800 /*++////////////////////////////////////////////////////////////////////////////
6804 Routine Description:
6806 This routine will call into the driver to retrieve a copy of one of it's
6811 Pdo - a pointer to the pdo being queried
6813 IdType - which type of id string is being queried
6815 IdString - an allocated unicode string structure which the driver
6826 IN PDEVICE_OBJECT Pdo
,
6827 IN BUS_QUERY_ID_TYPE IdType
,
6828 IN PUNICODE_STRING IdString
6831 PCLASS_DRIVER_EXTENSION
6832 driverExtension
= IoGetDriverObjectExtension(Pdo
->DriverObject
,
6833 CLASS_DRIVER_EXTENSION_KEY
);
6836 ASSERT(driverExtension
->InitData
.ClassQueryId
);
6840 return driverExtension
->InitData
.ClassQueryId( Pdo
, IdType
, IdString
);
6841 } // end ClassGetPdoId()
6843 /*++////////////////////////////////////////////////////////////////////////////
6845 ClassQueryPnpCapabilities()
6847 Routine Description:
6849 This routine will call into the class driver to retrieve it's pnp
6854 PhysicalDeviceObject - The physical device object to retrieve properties
6864 ClassQueryPnpCapabilities(
6865 IN PDEVICE_OBJECT DeviceObject
,
6866 IN PDEVICE_CAPABILITIES Capabilities
6869 PCLASS_DRIVER_EXTENSION driverExtension
=
6870 ClassGetDriverExtension(DeviceObject
->DriverObject
);
6871 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6873 PCLASS_QUERY_PNP_CAPABILITIES queryRoutine
= NULL
;
6877 ASSERT(DeviceObject
);
6878 ASSERT(Capabilities
);
6880 if(commonExtension
->IsFdo
) {
6881 queryRoutine
= driverExtension
->InitData
.FdoData
.ClassQueryPnpCapabilities
;
6883 queryRoutine
= driverExtension
->InitData
.PdoData
.ClassQueryPnpCapabilities
;
6887 return queryRoutine(DeviceObject
,
6890 return STATUS_NOT_IMPLEMENTED
;
6892 } // end ClassQueryPnpCapabilities()
6894 /*++////////////////////////////////////////////////////////////////////////////
6896 ClassInvalidateBusRelations()
6898 Routine Description:
6900 This routine re-enumerates the devices on the "bus". It will call into
6901 the driver's ClassEnumerate routine to update the device objects
6902 immediately. It will then schedule a bus re-enumeration for pnp by calling
6903 IoInvalidateDeviceRelations.
6907 Fdo - a pointer to the functional device object for this bus
6916 ClassInvalidateBusRelations(
6917 IN PDEVICE_OBJECT Fdo
6920 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6921 PCLASS_DRIVER_EXTENSION
6922 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6923 CLASS_DRIVER_EXTENSION_KEY
);
6925 NTSTATUS status
= STATUS_SUCCESS
;
6930 ASSERT(driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
6932 if(InterlockedIncrement(&(fdoExtension
->EnumerationInterlock
)) == 1) {
6933 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6935 InterlockedDecrement(&(fdoExtension
->EnumerationInterlock
));
6937 if(!NT_SUCCESS(status
)) {
6939 DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
6940 "returned %lx\n", status
));
6943 IoInvalidateDeviceRelations(fdoExtension
->LowerPdo
, BusRelations
);
6946 } // end ClassInvalidateBusRelations()
6948 /*++////////////////////////////////////////////////////////////////////////////
6950 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
6952 Routine Description:
6954 This routine is called to handle the "removal" of a device. It will
6955 forward the request downwards if necesssary, call into the driver
6956 to release any necessary resources (memory, events, etc) and then
6957 will delete the device object.
6961 DeviceObject - a pointer to the device object being removed
6963 RemoveType - indicates what type of remove this is (regular or surprise).
6973 IN PDEVICE_OBJECT DeviceObject
,
6977 PCLASS_DRIVER_EXTENSION
6978 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6979 CLASS_DRIVER_EXTENSION_KEY
);
6980 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6981 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
6982 PCLASS_WMI_INFO classWmiInfo
;
6983 BOOLEAN proceedWithRemove
= TRUE
;
6988 commonExtension
->IsRemoved
= REMOVE_PENDING
;
6991 * Deregister from WMI.
6993 classWmiInfo
= commonExtension
->IsFdo
?
6994 &driverExtension
->InitData
.FdoData
.ClassWmiInfo
:
6995 &driverExtension
->InitData
.PdoData
.ClassWmiInfo
;
6996 if (classWmiInfo
->GuidRegInfo
){
6997 status
= IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_DEREGISTER
);
6998 DBGTRACE(ClassDebugInfo
, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject
, status
));
7002 * If we exposed a "shingle" (a named device interface openable by CreateFile)
7003 * then delete it now.
7005 if (commonExtension
->MountedDeviceInterfaceName
.Buffer
){
7006 IoSetDeviceInterfaceState(&commonExtension
->MountedDeviceInterfaceName
, FALSE
);
7007 RtlFreeUnicodeString(&commonExtension
->MountedDeviceInterfaceName
);
7008 RtlInitUnicodeString(&commonExtension
->MountedDeviceInterfaceName
, NULL
);
7012 // If this is a surprise removal we leave the device around - which means
7013 // we don't have to (or want to) drop the remove lock and wait for pending
7014 // requests to complete.
7017 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7020 // Release the lock we acquired when the device object was created.
7023 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7025 DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
7026 commonExtension
->RemoveLock
));
7028 KeWaitForSingleObject(&commonExtension
->RemoveEvent
,
7034 DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject
));
7036 if(commonExtension
->IsFdo
) {
7038 DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
7039 "remove request.\n", DeviceObject
));
7043 PPHYSICAL_DEVICE_EXTENSION pdoExtension
= DeviceObject
->DeviceExtension
;
7045 if (pdoExtension
->IsMissing
){
7047 * The child partition PDO is missing, so we are going to go ahead
7048 * and delete it for the remove.
7050 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject
));
7054 * We got a remove for a child partition PDO which is not actually missing.
7055 * So we will NOT actually delete it.
7057 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject
));
7060 // Reacquire the remove lock for the next time this comes around.
7063 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7066 // the device wasn't missing so it's not really been removed.
7069 commonExtension
->IsRemoved
= NO_REMOVE
;
7071 IoInvalidateDeviceRelations(
7072 commonExtension
->PartitionZeroExtension
->LowerPdo
,
7075 proceedWithRemove
= FALSE
;
7081 if (proceedWithRemove
){
7084 * Call the class driver's remove handler.
7085 * All this is supposed to do is clean up its data and device interfaces.
7087 ASSERT(commonExtension
->DevInfo
->ClassRemoveDevice
);
7088 status
= commonExtension
->DevInfo
->ClassRemoveDevice(DeviceObject
, RemoveType
);
7089 ASSERT(NT_SUCCESS(status
));
7090 status
= STATUS_SUCCESS
;
7092 if (commonExtension
->IsFdo
){
7093 //PDEVICE_OBJECT pdo;
7094 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
7096 ClasspDisableTimer(fdoExtension
->DeviceObject
);
7098 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7100 PPHYSICAL_DEVICE_EXTENSION child
;
7103 // Cleanup the media detection resources now that the class driver
7104 // has stopped it's timer (if any) and we can be sure they won't
7105 // call us to do detection again.
7108 ClassCleanupMediaChangeDetection(fdoExtension
);
7111 // Cleanup any Failure Prediction stuff
7113 if (fdoExtension
->FailurePredictionInfo
) {
7114 ExFreePool(fdoExtension
->FailurePredictionInfo
);
7115 fdoExtension
->FailurePredictionInfo
= NULL
;
7119 * Ordinarily all child PDOs will be removed by the time
7120 * that the parent gets the REMOVE_DEVICE.
7121 * However, if a child PDO has been created but has not
7122 * been announced in a QueryDeviceRelations, then it is
7123 * just a private data structure unknown to pnp, and we have
7124 * to delete it ourselves.
7126 ClassAcquireChildLock(fdoExtension
);
7127 while (child
= ClassRemoveChild(fdoExtension
, NULL
, FALSE
)){
7130 // Yank the pdo. This routine will unlink the device from the
7131 // pdo list so NextPdo will point to the next one when it's
7134 child
->IsMissing
= TRUE
;
7135 ClassRemoveDevice(child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
7137 ClassReleaseChildLock(fdoExtension
);
7139 else if (RemoveType
== IRP_MN_SURPRISE_REMOVAL
){
7141 * This is a surprise-remove on the parent FDO.
7142 * We will mark the child PDOs as missing so that they
7143 * will actually get deleted when they get a REMOVE_DEVICE.
7145 ClassMarkChildrenMissing(fdoExtension
);
7148 ClasspFreeReleaseRequest(DeviceObject
);
7150 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7153 // Free FDO-specific data structs
7155 if (fdoExtension
->PrivateFdoData
){
7157 DestroyAllTransferPackets(DeviceObject
);
7159 ExFreePool(fdoExtension
->PrivateFdoData
);
7160 fdoExtension
->PrivateFdoData
= NULL
;
7163 if (commonExtension
->DeviceName
.Buffer
) {
7164 ExFreePool(commonExtension
->DeviceName
.Buffer
);
7165 RtlInitUnicodeString(&commonExtension
->DeviceName
, NULL
);
7168 if (fdoExtension
->AdapterDescriptor
) {
7169 ExFreePool(fdoExtension
->AdapterDescriptor
);
7170 fdoExtension
->AdapterDescriptor
= NULL
;
7173 if (fdoExtension
->DeviceDescriptor
) {
7174 ExFreePool(fdoExtension
->DeviceDescriptor
);
7175 fdoExtension
->DeviceDescriptor
= NULL
;
7179 // Detach our device object from the stack - there's no reason
7180 // to hold off our cleanup any longer.
7183 IoDetachDevice(lowerDeviceObject
);
7188 * This is a child partition PDO.
7189 * We have already determined that it was previously marked
7190 * as missing. So if this is a REMOVE_DEVICE, we will actually
7193 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7194 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
7195 commonExtension
->PartitionZeroExtension
;
7196 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
7197 (PPHYSICAL_DEVICE_EXTENSION
) commonExtension
;
7200 // See if this device is in the child list (if this was a suprise
7201 // removal it might be) and remove it.
7204 ClassRemoveChild(fdoExtension
, pdoExtension
, TRUE
);
7208 commonExtension
->PartitionLength
.QuadPart
= 0;
7210 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7211 IoDeleteDevice(DeviceObject
);
7215 return STATUS_SUCCESS
;
7216 } // end ClassRemoveDevice()
7218 /*++////////////////////////////////////////////////////////////////////////////
7220 ClassGetDriverExtension()
7222 Routine Description:
7224 This routine will return the classpnp's driver extension.
7228 DriverObject - the driver object for which to get classpnp's extension
7232 Either NULL if none, or a pointer to the driver extension
7235 PCLASS_DRIVER_EXTENSION
7237 ClassGetDriverExtension(
7238 IN PDRIVER_OBJECT DriverObject
7241 return IoGetDriverObjectExtension(DriverObject
, CLASS_DRIVER_EXTENSION_KEY
);
7242 } // end ClassGetDriverExtension()
7244 /*++////////////////////////////////////////////////////////////////////////////
7248 Routine Description:
7250 This routine wraps the class driver's start io routine. If the device
7251 is being removed it will complete any requests with
7252 STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
7264 IN PDEVICE_OBJECT DeviceObject
,
7268 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7271 // We're already holding the remove lock so just check the variable and
7272 // see what's going on.
7275 if(commonExtension
->IsRemoved
) {
7277 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
7279 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7280 ClassReleaseRemoveLock(DeviceObject
, Irp
);
7281 ClassCompleteRequest(DeviceObject
, Irp
, IO_DISK_INCREMENT
);
7282 IoStartNextPacket(DeviceObject
, FALSE
);
7283 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7287 commonExtension
->DriverExtension
->InitData
.ClassStartIo(
7292 } // ClasspStartIo()
7294 /*++////////////////////////////////////////////////////////////////////////////
7296 ClassUpdateInformationInRegistry()
7298 Routine Description:
7300 This routine has knowledge about the layout of the device map information
7301 in the registry. It will update this information to include a value
7302 entry specifying the dos device name that is assumed to get assigned
7303 to this NT device name. For more information on this assigning of the
7304 dos device name look in the drive support routine in the hal that assigns
7307 Since some versions of some device's firmware did not work and some
7308 vendors did not bother to follow the specification, the entire inquiry
7309 information must also be stored in the registry so than someone can
7310 figure out the firmware version.
7314 DeviceObject - A pointer to the device object for the tape device.
7323 ClassUpdateInformationInRegistry(
7324 IN PDEVICE_OBJECT Fdo
,
7325 IN PCHAR DeviceName
,
7326 IN ULONG DeviceNumber
,
7327 IN PINQUIRYDATA InquiryData
,
7328 IN ULONG InquiryDataLength
7331 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
7333 SCSI_ADDRESS scsiAddress
;
7334 OBJECT_ATTRIBUTES objectAttributes
;
7337 UNICODE_STRING unicodeName
;
7338 UNICODE_STRING unicodeRegistryPath
;
7339 UNICODE_STRING unicodeData
;
7341 IO_STATUS_BLOCK ioStatus
;
7347 fdoExtension
= Fdo
->DeviceExtension
;
7350 RtlZeroMemory(&unicodeName
, sizeof(UNICODE_STRING
));
7351 RtlZeroMemory(&unicodeData
, sizeof(UNICODE_STRING
));
7352 RtlZeroMemory(&unicodeRegistryPath
, sizeof(UNICODE_STRING
));
7357 // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
7360 ClassSendDeviceIoControlSynchronous(
7361 IOCTL_SCSI_GET_ADDRESS
,
7365 sizeof(SCSI_ADDRESS
),
7370 if (!NT_SUCCESS(ioStatus
.Status
)) {
7372 status
= ioStatus
.Status
;
7374 "UpdateInformationInRegistry: Get Address failed %lx\n",
7381 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
7382 scsiAddress
.PortNumber
,
7384 scsiAddress
.TargetId
,
7390 // Allocate a buffer for the reg. spooge.
7393 buffer
= ExAllocatePoolWithTag(PagedPool
, 1024, '6BcS');
7395 if (buffer
== NULL
) {
7398 // There is not return value for this. Since this is done at
7399 // claim device time (currently only system initialization) getting
7400 // the registry information correct will be the least of the worries.
7407 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
7408 scsiAddress
.PortNumber
,
7410 scsiAddress
.TargetId
,
7413 RtlInitString(&string
, buffer
);
7415 status
= RtlAnsiStringToUnicodeString(&unicodeRegistryPath
,
7419 if (!NT_SUCCESS(status
)) {
7424 // Open the registry key for the scsi information for this
7425 // scsibus, target, lun.
7428 InitializeObjectAttributes(&objectAttributes
,
7429 &unicodeRegistryPath
,
7430 OBJ_CASE_INSENSITIVE
,
7434 status
= ZwOpenKey(&targetKey
,
7435 KEY_READ
| KEY_WRITE
,
7438 if (!NT_SUCCESS(status
)) {
7443 // Now construct and attempt to create the registry value
7444 // specifying the device name in the appropriate place in the
7448 RtlInitUnicodeString(&unicodeName
, L
"DeviceName");
7450 sprintf(buffer
, "%s%d", DeviceName
, DeviceNumber
);
7451 RtlInitString(&string
, buffer
);
7452 status
= RtlAnsiStringToUnicodeString(&unicodeData
,
7455 if (NT_SUCCESS(status
)) {
7456 status
= ZwSetValueKey(targetKey
,
7461 unicodeData
.Length
);
7465 // if they sent in data, update the registry
7468 if (InquiryDataLength
) {
7470 ASSERT(InquiryData
);
7472 RtlInitUnicodeString(&unicodeName
, L
"InquiryData");
7473 status
= ZwSetValueKey(targetKey
,
7481 // that's all, except to clean up.
7485 if (unicodeData
.Buffer
) {
7486 RtlFreeUnicodeString(&unicodeData
);
7488 if (unicodeRegistryPath
.Buffer
) {
7489 RtlFreeUnicodeString(&unicodeRegistryPath
);
7500 } // end ClassUpdateInformationInRegistry()
7502 /*++////////////////////////////////////////////////////////////////////////////
7504 ClasspSendSynchronousCompletion()
7506 Routine Description:
7508 This completion routine will set the user event in the irp after
7509 freeing the irp and the associated MDL (if any).
7513 DeviceObject - the device object which requested the completion routine
7515 Irp - the irp being completed
7521 STATUS_MORE_PROCESSING_REQUIRED
7526 ClasspSendSynchronousCompletion(
7527 IN PDEVICE_OBJECT DeviceObject
,
7532 DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
7533 DeviceObject
, Irp
, Context
));
7535 // First set the status and information fields in the io status block
7536 // provided by the caller.
7539 *(Irp
->UserIosb
) = Irp
->IoStatus
;
7542 // Unlock the pages for the data buffer.
7545 if(Irp
->MdlAddress
) {
7546 MmUnlockPages(Irp
->MdlAddress
);
7547 IoFreeMdl(Irp
->MdlAddress
);
7551 // Signal the caller's event.
7554 KeSetEvent(Irp
->UserEvent
, IO_NO_INCREMENT
, FALSE
);
7557 // Free the MDL and the IRP.
7562 return STATUS_MORE_PROCESSING_REQUIRED
;
7563 } // end ClasspSendSynchronousCompletion()
7567 ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
7572 ClasspRegisterMountedDeviceInterface(
7573 IN PDEVICE_OBJECT DeviceObject
7577 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7578 BOOLEAN isFdo
= commonExtension
->IsFdo
;
7581 UNICODE_STRING interfaceName
;
7587 PFUNCTIONAL_DEVICE_EXTENSION functionalExtension
;
7589 functionalExtension
=
7590 (PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
;
7591 pdo
= functionalExtension
->LowerPdo
;
7596 status
= IoRegisterDeviceInterface(
7598 &MOUNTDEV_MOUNTED_DEVICE_GUID
,
7603 if(NT_SUCCESS(status
)) {
7606 // Copy the interface name before setting the interface state - the
7607 // name is needed by the components we notify.
7610 commonExtension
->MountedDeviceInterfaceName
= interfaceName
;
7611 status
= IoSetDeviceInterfaceState(&interfaceName
, TRUE
);
7613 if(!NT_SUCCESS(status
)) {
7614 RtlFreeUnicodeString(&interfaceName
);
7618 if(!NT_SUCCESS(status
)) {
7619 RtlInitUnicodeString(&(commonExtension
->MountedDeviceInterfaceName
),
7623 } // end ClasspRegisterMountedDeviceInterface()
7625 /*++////////////////////////////////////////////////////////////////////////////
7627 ClassSendDeviceIoControlSynchronous()
7629 Routine Description:
7631 This routine is based upon IoBuildDeviceIoControlRequest(). It has been
7632 modified to reduce code and memory by not double-buffering the io, using
7633 the same buffer for both input and output, allocating and deallocating
7634 the mdl on behalf of the caller, and waiting for the io to complete.
7636 This routine also works around the rare cases in which APC's are disabled.
7637 Since IoBuildDeviceIoControl() used APC's to signal completion, this had
7638 led to a number of difficult-to-detect hangs, where the irp was completed,
7639 but the event passed to IoBuild..() was still being waited upon by the
7644 IoControlCode - the IOCTL to send
7646 TargetDeviceObject - the device object that should handle the ioctl
7648 Buffer - the input and output buffer, or NULL if no input/output
7650 InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
7652 OutputBufferLength - the number of bytes to be filled in upon success
7654 InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
7656 IoStatus - the status block that contains the results of the operation
7663 ClassSendDeviceIoControlSynchronous(
7664 IN ULONG IoControlCode
,
7665 IN PDEVICE_OBJECT TargetDeviceObject
,
7666 IN OUT PVOID Buffer OPTIONAL
,
7667 IN ULONG InputBufferLength
,
7668 IN ULONG OutputBufferLength
,
7669 IN BOOLEAN InternalDeviceIoControl
,
7670 OUT PIO_STATUS_BLOCK IoStatus
7674 PIO_STACK_LOCATION irpSp
;
7680 method
= IoControlCode
& 3;
7683 #if DBG // Begin Argument Checking (nop in fre version)
7685 ASSERT(ARGUMENT_PRESENT(IoStatus
));
7687 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7688 ASSERT(ARGUMENT_PRESENT(Buffer
));
7691 ASSERT(!ARGUMENT_PRESENT(Buffer
));
7696 // Begin by allocating the IRP for this request. Do not charge quota to
7697 // the current process for this IRP.
7700 irp
= IoAllocateIrp(TargetDeviceObject
->StackSize
, FALSE
);
7702 (*IoStatus
).Information
= 0;
7703 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7708 // Get a pointer to the stack location of the first driver which will be
7709 // invoked. This is where the function codes and the parameters are set.
7712 irpSp
= IoGetNextIrpStackLocation(irp
);
7715 // Set the major function code based on the type of device I/O control
7716 // function the caller has specified.
7719 if (InternalDeviceIoControl
) {
7720 irpSp
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
7722 irpSp
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
7726 // Copy the caller's parameters to the service-specific portion of the
7727 // IRP for those parameters that are the same for all four methods.
7730 irpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
= OutputBufferLength
;
7731 irpSp
->Parameters
.DeviceIoControl
.InputBufferLength
= InputBufferLength
;
7732 irpSp
->Parameters
.DeviceIoControl
.IoControlCode
= IoControlCode
;
7735 // Get the method bits from the I/O control code to determine how the
7736 // buffers are to be passed to the driver.
7741 case METHOD_BUFFERED
: {
7742 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7744 irp
->AssociatedIrp
.SystemBuffer
=
7745 ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
7746 max(InputBufferLength
, OutputBufferLength
),
7747 CLASS_TAG_DEVICE_CONTROL
7750 if (irp
->AssociatedIrp
.SystemBuffer
== NULL
) {
7752 (*IoStatus
).Information
= 0;
7753 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7757 if (InputBufferLength
!= 0) {
7758 RtlCopyMemory(irp
->AssociatedIrp
.SystemBuffer
,
7762 } // end of buffering
7764 irp
->UserBuffer
= Buffer
;
7769 case METHOD_IN_DIRECT
:
7770 case METHOD_OUT_DIRECT
: {
7773 if (InputBufferLength
!= 0) {
7774 irp
->AssociatedIrp
.SystemBuffer
= Buffer
;
7777 if (OutputBufferLength
!= 0) {
7779 irp
->MdlAddress
= IoAllocateMdl(Buffer
,
7784 if (irp
->MdlAddress
== NULL
) {
7786 (*IoStatus
).Information
= 0;
7787 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7791 if (method
== METHOD_IN_DIRECT
) {
7792 MmProbeAndLockPages(irp
->MdlAddress
,
7795 } else if (method
== METHOD_OUT_DIRECT
) {
7796 MmProbeAndLockPages(irp
->MdlAddress
,
7800 ASSERT(!"If other methods reach here, code is out of date");
7807 case METHOD_NEITHER
: {
7809 ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
7810 IoStatus
->Information
= 0;
7811 IoStatus
->Status
= STATUS_NOT_SUPPORTED
;
7815 } // end of switch(method)
7817 irp
->Tail
.Overlay
.Thread
= PsGetCurrentThread();
7820 // send the irp synchronously
7823 ClassSendIrpSynchronous(TargetDeviceObject
, irp
);
7826 // copy the iostatus block for the caller
7829 *IoStatus
= irp
->IoStatus
;
7832 // free any allocated resources
7836 case METHOD_BUFFERED
: {
7838 ASSERT(irp
->UserBuffer
== Buffer
);
7841 // first copy the buffered result, if any
7842 // Note that there are no security implications in
7843 // not checking for success since only drivers can
7844 // call into this routine anyways...
7847 if (OutputBufferLength
!= 0) {
7848 RtlCopyMemory(Buffer
, // irp->UserBuffer
7849 irp
->AssociatedIrp
.SystemBuffer
,
7855 // then free the memory allocated to buffer the io
7858 if ((InputBufferLength
!=0) || (OutputBufferLength
!= 0)) {
7859 ExFreePool(irp
->AssociatedIrp
.SystemBuffer
);
7860 irp
->AssociatedIrp
.SystemBuffer
= NULL
;
7865 case METHOD_IN_DIRECT
:
7866 case METHOD_OUT_DIRECT
: {
7869 // we alloc a mdl if there is an output buffer specified
7870 // free it here after unlocking the pages
7873 if (OutputBufferLength
!= 0) {
7874 ASSERT(irp
->MdlAddress
!= NULL
);
7875 MmUnlockPages(irp
->MdlAddress
);
7876 IoFreeMdl(irp
->MdlAddress
);
7877 irp
->MdlAddress
= (PMDL
) NULL
;
7882 case METHOD_NEITHER
: {
7883 ASSERT(!"Code is out of date");
7889 // we always have allocated an irp. free it here.
7896 // return the io status block's status to the caller
7900 } // end ClassSendDeviceIoControlSynchronous()
7902 /*++////////////////////////////////////////////////////////////////////////////
7904 ClassForwardIrpSynchronous()
7906 Routine Description:
7908 Forwards a given irp to the next lower device object.
7912 CommonExtension - the common class extension
7914 Irp - the request to forward down the stack
7921 ClassForwardIrpSynchronous(
7922 IN PCOMMON_DEVICE_EXTENSION CommonExtension
,
7926 IoCopyCurrentIrpStackLocationToNext(Irp
);
7927 return ClassSendIrpSynchronous(CommonExtension
->LowerDeviceObject
, Irp
);
7928 } // end ClassForwardIrpSynchronous()
7930 /*++////////////////////////////////////////////////////////////////////////////
7932 ClassSendIrpSynchronous()
7934 Routine Description:
7936 This routine sends the given irp to the given device object, and waits for
7937 it to complete. On debug versions, will print out a debug message and
7938 optionally assert for "lost" irps based upon classpnp's globals
7942 TargetDeviceObject - the device object to handle this irp
7944 Irp - the request to be sent
7951 ClassSendIrpSynchronous(
7952 IN PDEVICE_OBJECT TargetDeviceObject
,
7959 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL
);
7960 ASSERT(TargetDeviceObject
!= NULL
);
7961 ASSERT(Irp
!= NULL
);
7962 ASSERT(Irp
->StackCount
>= TargetDeviceObject
->StackSize
);
7965 // ISSUE-2000/02/20-henrygab What if APCs are disabled?
7966 // May need to enter critical section before IoCallDriver()
7967 // until the event is hit?
7970 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
7971 IoSetCompletionRoutine(Irp
, ClassSignalCompletion
, &event
,
7974 status
= IoCallDriver(TargetDeviceObject
, Irp
);
7976 if (status
== STATUS_PENDING
) {
7979 LARGE_INTEGER timeout
;
7981 timeout
.QuadPart
= (LONGLONG
)(-1 * 10 * 1000 * (LONGLONG
)1000 *
7982 ClasspnpGlobals
.SecondsToWaitForIrps
);
7985 status
= KeWaitForSingleObject(&event
,
7992 if (status
== STATUS_TIMEOUT
) {
7995 // This DebugPrint should almost always be investigated by the
7996 // party who sent the irp and/or the current owner of the irp.
7997 // Synchronous Irps should not take this long (currently 30
7998 // seconds) without good reason. This points to a potentially
7999 // serious problem in the underlying device stack.
8002 DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
8003 "complete within %x seconds\n",
8004 TargetDeviceObject
, Irp
,
8005 ClasspnpGlobals
.SecondsToWaitForIrps
8008 if (ClasspnpGlobals
.BreakOnLostIrps
!= 0) {
8009 ASSERT(!" - Irp failed to complete within 30 seconds - ");
8014 } while (status
==STATUS_TIMEOUT
);
8016 KeWaitForSingleObject(&event
,
8023 status
= Irp
->IoStatus
.Status
;
8027 } // end ClassSendIrpSynchronous()
8029 /*++////////////////////////////////////////////////////////////////////////////
8033 Routine Description:
8035 This routine returns the current VPB (Volume Parameter Block) for the
8036 given device object.
8037 The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
8038 of DEVICE_OBJECT; hence this exported function.
8042 DeviceObject - the device to get the VPB for
8046 the VPB, or NULL if none.
8052 IN PDEVICE_OBJECT DeviceObject
8055 return DeviceObject
->Vpb
;
8056 } // end ClassGetVpb()
8060 ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
8065 ClasspAllocateReleaseRequest(
8066 IN PDEVICE_OBJECT Fdo
8069 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8070 //PIO_STACK_LOCATION irpStack;
8072 KeInitializeSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
));
8074 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8075 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8076 fdoExtension
->ReleaseQueueIrpFromPool
= FALSE
;
8079 // The class driver is responsible for allocating a properly sized irp,
8080 // or ClassReleaseQueue will attempt to do it on the first error.
8083 fdoExtension
->ReleaseQueueIrp
= NULL
;
8086 // Write length to SRB.
8089 fdoExtension
->ReleaseQueueSrb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
8091 return STATUS_SUCCESS
;
8092 } // end ClasspAllocateReleaseRequest()
8096 ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
8101 ClasspFreeReleaseRequest(
8102 IN PDEVICE_OBJECT Fdo
8105 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8108 ASSERT(fdoExtension
->CommonExtension
.IsRemoved
!= NO_REMOVE
);
8111 // free anything the driver allocated
8114 if (fdoExtension
->ReleaseQueueIrp
) {
8115 if (fdoExtension
->ReleaseQueueIrpFromPool
) {
8116 ExFreePool(fdoExtension
->ReleaseQueueIrp
);
8118 IoFreeIrp(fdoExtension
->ReleaseQueueIrp
);
8120 fdoExtension
->ReleaseQueueIrp
= NULL
;
8124 // free anything that we allocated
8127 if ((fdoExtension
->PrivateFdoData
) &&
8128 (fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
)) {
8130 ExFreePool(fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
);
8131 fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= FALSE
;
8132 fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
= NULL
;
8136 } // end ClasspFreeReleaseRequest()
8138 /*++////////////////////////////////////////////////////////////////////////////
8142 Routine Description:
8144 This routine issues an internal device control command
8145 to the port driver to release a frozen queue. The call
8146 is issued asynchronously as ClassReleaseQueue will be invoked
8147 from the IO completion DPC (and will have no context to
8148 wait for a synchronous call to complete).
8150 This routine must be called with the remove lock held.
8154 Fdo - The functional device object for the device with the frozen queue.
8164 IN PDEVICE_OBJECT Fdo
8167 ClasspReleaseQueue(Fdo
, NULL
);
8169 } // end ClassReleaseQueue()
8171 /*++////////////////////////////////////////////////////////////////////////////
8173 ClasspAllocateReleaseQueueIrp()
8175 Routine Description:
8177 This routine allocates the release queue irp held in classpnp's private
8178 extension. This was added to allow no-memory conditions to be more
8187 Does not grab the spinlock. Should only be called from StartDevice()
8188 routine. May be called elsewhere for poorly-behaved drivers that cause
8189 the queue to lockup before the device is started. This should *never*
8190 occur, since it's illegal to send a request to a non-started PDO. This
8191 condition is checked for in ClasspReleaseQueue().
8196 ClasspAllocateReleaseQueueIrp(
8197 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8201 UCHAR lowerStackSize
;
8204 // do an initial check w/o the spinlock
8207 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8208 return STATUS_SUCCESS
;
8212 lowerStackSize
= FdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
;
8215 // don't allocate one if one is in progress! this means whoever called
8216 // this routine didn't check if one was in progress.
8219 ASSERT(!(FdoExtension
->ReleaseQueueInProgress
));
8221 FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
=
8222 ExAllocatePoolWithTag(NonPagedPool
,
8223 IoSizeOfIrp(lowerStackSize
),
8224 CLASS_TAG_RELEASE_QUEUE
8227 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
== NULL
) {
8228 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
8229 "release queue irp\n"));
8230 return STATUS_INSUFFICIENT_RESOURCES
;
8232 IoInitializeIrp(FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
,
8233 IoSizeOfIrp(lowerStackSize
),
8235 FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= TRUE
;
8237 return STATUS_SUCCESS
;
8240 /*++////////////////////////////////////////////////////////////////////////////
8242 ClasspReleaseQueue()
8244 Routine Description:
8246 This routine issues an internal device control command
8247 to the port driver to release a frozen queue. The call
8248 is issued asynchronously as ClassReleaseQueue will be invoked
8249 from the IO completion DPC (and will have no context to
8250 wait for a synchronous call to complete).
8252 This routine must be called with the remove lock held.
8256 Fdo - The functional device object for the device with the frozen queue.
8258 ReleaseQueueIrp - If this irp is supplied then the test to determine whether
8259 a release queue request is in progress will be ignored.
8260 The irp provided must be the IRP originally allocated
8261 for release queue requests (so this parameter can only
8262 really be provided by the release queue completion
8273 IN PDEVICE_OBJECT Fdo
,
8274 IN PIRP ReleaseQueueIrp OPTIONAL
8277 PIO_STACK_LOCATION irpStack
;
8279 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8280 PDEVICE_OBJECT lowerDevice
;
8281 PSCSI_REQUEST_BLOCK srb
;
8284 lowerDevice
= fdoExtension
->CommonExtension
.LowerDeviceObject
;
8287 // we raise irql seperately so we're not swapped out or suspended
8288 // while holding the release queue irp in this routine. this lets
8289 // us release the spin lock before lowering irql.
8292 KeRaiseIrql(DISPATCH_LEVEL
, ¤tIrql
);
8294 KeAcquireSpinLockAtDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8297 // make sure that if they passed us an irp, it matches our allocated irp.
8300 ASSERT((ReleaseQueueIrp
== NULL
) ||
8301 (ReleaseQueueIrp
== fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
));
8304 // ASSERT that we've already allocated this. (should not occur)
8305 // try to allocate it anyways, then finally bugcheck if
8306 // there's still no memory...
8309 ASSERT(fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
);
8310 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8311 ClasspAllocateReleaseQueueIrp(fdoExtension
);
8313 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8314 KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL
, 0x12, (ULONG_PTR
)Fdo
, 0x0, 0x0);
8317 if ((fdoExtension
->ReleaseQueueInProgress
) && (ReleaseQueueIrp
== NULL
)) {
8320 // Someone is already using the irp - just set the flag to indicate that
8321 // we need to release the queue again.
8324 fdoExtension
->ReleaseQueueNeeded
= TRUE
;
8325 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8326 KeLowerIrql(currentIrql
);
8332 // Mark that there is a release queue in progress and drop the spinlock.
8335 fdoExtension
->ReleaseQueueInProgress
= TRUE
;
8336 if (ReleaseQueueIrp
) {
8337 irp
= ReleaseQueueIrp
;
8339 irp
= fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
;
8341 srb
= &(fdoExtension
->ReleaseQueueSrb
);
8343 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8345 ASSERT(irp
!= NULL
);
8347 irpStack
= IoGetNextIrpStackLocation(irp
);
8349 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
8351 srb
->OriginalRequest
= irp
;
8354 // Store the SRB address in next stack for port driver.
8357 irpStack
->Parameters
.Scsi
.Srb
= srb
;
8360 // If this device is removable then flush the queue. This will also
8364 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
8365 srb
->Function
= SRB_FUNCTION_FLUSH_QUEUE
;
8368 srb
->Function
= SRB_FUNCTION_RELEASE_QUEUE
;
8371 ClassAcquireRemoveLock(Fdo
, irp
);
8373 IoSetCompletionRoutine(irp
,
8374 ClassReleaseQueueCompletion
,
8380 IoCallDriver(lowerDevice
, irp
);
8382 KeLowerIrql(currentIrql
);
8386 } // end ClassReleaseQueue()
8388 /*++////////////////////////////////////////////////////////////////////////////
8390 ClassReleaseQueueCompletion()
8392 Routine Description:
8394 This routine is called when an asynchronous I/O request
8395 which was issused by the class driver completes. Examples of such requests
8396 are release queue or START UNIT. This routine releases the queue if
8397 necessary. It then frees the context and the IRP.
8401 DeviceObject - The device object for the logical unit; however since this
8402 is the top stack location the value is NULL.
8404 Irp - Supplies a pointer to the Irp to be processed.
8406 Context - Supplies the context to be used to process this request.
8415 ClassReleaseQueueCompletion(
8416 PDEVICE_OBJECT DeviceObject
,
8421 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8424 BOOLEAN releaseQueueNeeded
;
8426 DeviceObject
= Context
;
8428 fdoExtension
= DeviceObject
->DeviceExtension
;
8430 ClassReleaseRemoveLock(DeviceObject
, Irp
);
8433 // Grab the spinlock and clear the release queue in progress flag so others
8434 // can run. Save (and clear) the state of the release queue needed flag
8435 // so that we can issue a new release queue outside the spinlock.
8438 KeAcquireSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), &oldIrql
);
8440 releaseQueueNeeded
= fdoExtension
->ReleaseQueueNeeded
;
8442 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8443 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8445 KeReleaseSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), oldIrql
);
8448 // If we need a release queue then issue one now. Another processor may
8449 // have already started one in which case we'll try to issue this one after
8450 // it is done - but we should never recurse more than one deep.
8453 if(releaseQueueNeeded
) {
8454 ClasspReleaseQueue(DeviceObject
, Irp
);
8458 // Indicate the I/O system should stop processing the Irp completion.
8461 return STATUS_MORE_PROCESSING_REQUIRED
;
8463 } // ClassAsynchronousCompletion()
8465 /*++////////////////////////////////////////////////////////////////////////////
8467 ClassAcquireChildLock()
8469 Routine Description:
8471 This routine acquires the lock protecting children PDOs. It may be
8472 acquired recursively by the same thread, but must be release by the
8473 thread once for each acquisition.
8477 FdoExtension - the device whose child list is protected.
8486 ClassAcquireChildLock(
8487 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8492 if(FdoExtension
->ChildLockOwner
!= KeGetCurrentThread()) {
8493 KeWaitForSingleObject(&FdoExtension
->ChildLock
,
8494 Executive
, KernelMode
,
8497 ASSERT(FdoExtension
->ChildLockOwner
== NULL
);
8498 ASSERT(FdoExtension
->ChildLockAcquisitionCount
== 0);
8500 FdoExtension
->ChildLockOwner
= KeGetCurrentThread();
8502 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8505 FdoExtension
->ChildLockAcquisitionCount
++;
8509 /*++////////////////////////////////////////////////////////////////////////////
8511 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
8513 Routine Description:
8515 This routine releases the lock protecting children PDOs. It must be
8516 called once for each time ClassAcquireChildLock was called.
8520 FdoExtension - the device whose child list is protected
8529 ClassReleaseChildLock(
8530 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8533 ASSERT(FdoExtension
->ChildLockOwner
== KeGetCurrentThread());
8534 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8536 FdoExtension
->ChildLockAcquisitionCount
-= 1;
8538 if(FdoExtension
->ChildLockAcquisitionCount
== 0) {
8539 FdoExtension
->ChildLockOwner
= NULL
;
8540 KeSetEvent(&FdoExtension
->ChildLock
, IO_NO_INCREMENT
, FALSE
);
8544 } // end ClassReleaseChildLock(
8546 /*++////////////////////////////////////////////////////////////////////////////
8550 Routine Description:
8552 This routine will insert a new child into the head of the child list.
8556 Parent - the child's parent (contains the head of the list)
8557 Child - the child to be inserted.
8558 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8559 it's already been acquired by or on behalf of the caller
8570 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8571 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8572 IN BOOLEAN AcquireLock
8576 ClassAcquireChildLock(Parent
);
8581 // Make sure this child's not already in the list.
8584 PPHYSICAL_DEVICE_EXTENSION testChild
;
8586 for (testChild
= Parent
->CommonExtension
.ChildList
;
8588 testChild
= testChild
->CommonExtension
.ChildList
) {
8590 ASSERT(testChild
!= Child
);
8595 Child
->CommonExtension
.ChildList
= Parent
->CommonExtension
.ChildList
;
8596 Parent
->CommonExtension
.ChildList
= Child
;
8599 ClassReleaseChildLock(Parent
);
8602 } // end ClassAddChild()
8604 /*++////////////////////////////////////////////////////////////////////////////
8608 Routine Description:
8610 This routine will remove a child from the child list.
8614 Parent - the parent to be removed from.
8616 Child - the child to be removed or NULL if the first child should be
8619 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8620 it's already been acquired by or on behalf of the caller
8625 A pointer to the child which was removed or NULL if no such child could
8626 be found in the list (or if Child was NULL but the list is empty).
8629 PPHYSICAL_DEVICE_EXTENSION
8632 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8633 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8634 IN BOOLEAN AcquireLock
8638 ClassAcquireChildLock(Parent
);
8642 PCOMMON_DEVICE_EXTENSION previousChild
= &Parent
->CommonExtension
;
8645 // If the list is empty then bail out now.
8648 if(Parent
->CommonExtension
.ChildList
== NULL
) {
8654 // If the caller specified a child then find the child object before
8655 // it. If none was specified then the FDO is the child object before
8656 // the one we want to remove.
8662 // Scan through the child list to find the entry which points to
8667 ASSERT(previousChild
!= &Child
->CommonExtension
);
8669 if(previousChild
->ChildList
== Child
) {
8673 previousChild
= &previousChild
->ChildList
->CommonExtension
;
8674 } while(previousChild
!= NULL
);
8676 if(previousChild
== NULL
) {
8683 // Save the next child away then unlink it from the list.
8686 Child
= previousChild
->ChildList
;
8687 previousChild
->ChildList
= Child
->CommonExtension
.ChildList
;
8688 Child
->CommonExtension
.ChildList
= NULL
;
8692 ClassReleaseChildLock(Parent
);
8696 } // end ClassRemoveChild()
8700 ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
8705 ClasspRetryRequestDpc(
8707 IN PDEVICE_OBJECT DeviceObject
,
8712 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8713 PCOMMON_DEVICE_EXTENSION commonExtension
;
8714 PCLASS_PRIVATE_FDO_DATA fdoData
;
8715 PCLASS_RETRY_INFO retryList
;
8719 commonExtension
= DeviceObject
->DeviceExtension
;
8720 ASSERT(commonExtension
->IsFdo
);
8721 fdoExtension
= DeviceObject
->DeviceExtension
;
8722 fdoData
= fdoExtension
->PrivateFdoData
;
8725 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8728 KeQueryTickCount(&now
);
8731 // if CurrentTick is less than now
8734 // retry entire list
8738 if (now
.QuadPart
< fdoData
->Retry
.Tick
.QuadPart
) {
8740 ClasspRetryDpcTimer(fdoData
);
8745 retryList
= fdoData
->Retry
.ListHead
;
8746 fdoData
->Retry
.ListHead
= NULL
;
8747 fdoData
->Retry
.Delta
.QuadPart
= (LONGLONG
)0;
8748 fdoData
->Retry
.Tick
.QuadPart
= (LONGLONG
)0;
8752 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8754 while (retryList
!= NULL
) {
8758 irp
= CONTAINING_RECORD(retryList
, IRP
, Tail
.Overlay
.DriverContext
[0]);
8759 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: -- %p\n", irp
));
8760 retryList
= retryList
->Next
;
8762 irp
->Tail
.Overlay
.DriverContext
[0] = ULongToPtr(0xdddddddd); // invalidate data
8763 irp
->Tail
.Overlay
.DriverContext
[1] = ULongToPtr(0xdddddddd); // invalidate data
8764 irp
->Tail
.Overlay
.DriverContext
[2] = ULongToPtr(0xdddddddd); // invalidate data
8765 irp
->Tail
.Overlay
.DriverContext
[3] = ULongToPtr(0xdddddddd); // invalidate data
8768 IoCallDriver(commonExtension
->LowerDeviceObject
, irp
);
8773 } // end ClasspRetryRequestDpc()
8777 ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
8783 IN PDEVICE_OBJECT SelfDeviceObject
,
8785 IN LARGE_INTEGER TimeDelta100ns
// in 100ns units
8788 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8789 PCLASS_PRIVATE_FDO_DATA fdoData
;
8790 PCLASS_RETRY_INFO retryInfo
;
8791 //PCLASS_RETRY_INFO *previousNext;
8792 LARGE_INTEGER delta
;
8796 // this checks we aren't destroying irps
8798 ASSERT(sizeof(CLASS_RETRY_INFO
) <= (4*sizeof(PVOID
)));
8800 fdoExtension
= SelfDeviceObject
->DeviceExtension
;
8801 fdoData
= fdoExtension
->PrivateFdoData
;
8803 if (!fdoExtension
->CommonExtension
.IsFdo
) {
8806 // this debug print/assertion should ALWAYS be investigated.
8807 // ClassRetryRequest can currently only be used by FDO's
8810 DebugPrint((ClassDebugError
, "ClassRetryRequestEx: LOST IRP %p\n", Irp
));
8811 ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
8816 if (TimeDelta100ns
.QuadPart
< 0) {
8817 ASSERT(!"ClassRetryRequest - must use positive delay");
8818 TimeDelta100ns
.QuadPart
*= -1;
8822 // prepare what we can out of the loop
8825 retryInfo
= (PCLASS_RETRY_INFO
)(&Irp
->Tail
.Overlay
.DriverContext
[0]);
8826 RtlZeroMemory(retryInfo
, sizeof(CLASS_RETRY_INFO
));
8828 delta
.QuadPart
= (TimeDelta100ns
.QuadPart
/ fdoData
->Retry
.Granularity
);
8829 if (TimeDelta100ns
.QuadPart
% fdoData
->Retry
.Granularity
) {
8830 delta
.QuadPart
++; // round up to next tick
8832 if (delta
.QuadPart
== (LONGLONG
)0) {
8833 delta
.QuadPart
= MINIMUM_RETRY_UNITS
;
8837 // now determine if we should fire another DPC or not
8840 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8843 // always add request to the list
8846 retryInfo
->Next
= fdoData
->Retry
.ListHead
;
8847 fdoData
->Retry
.ListHead
= retryInfo
;
8849 if (fdoData
->Retry
.Delta
.QuadPart
== (LONGLONG
)0) {
8851 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: +++ %p\n", Irp
));
8854 // must be exactly one item on list
8857 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8858 ASSERT(fdoData
->Retry
.ListHead
->Next
== NULL
);
8861 // if currentDelta is zero, always fire a DPC
8864 KeQueryTickCount(&fdoData
->Retry
.Tick
);
8865 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8866 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8867 ClasspRetryDpcTimer(fdoData
);
8869 } else if (delta
.QuadPart
> fdoData
->Retry
.Delta
.QuadPart
) {
8872 // if delta is greater than the list's current delta,
8873 // increase the DPC handling time by difference
8874 // and update the delta to new larger value
8875 // allow the DPC to re-fire itself if needed
8878 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8881 // must be at least two items on list
8884 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8885 ASSERT(fdoData
->Retry
.ListHead
->Next
!= NULL
);
8887 fdoData
->Retry
.Tick
.QuadPart
-= fdoData
->Retry
.Delta
.QuadPart
;
8888 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8890 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8895 // just inserting it on the list was enough
8898 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8903 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8906 } // end ClassRetryRequest()
8910 ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
8915 ClasspRetryDpcTimer(
8916 IN PCLASS_PRIVATE_FDO_DATA FdoData
8921 ASSERT(FdoData
->Retry
.Tick
.QuadPart
!= (LONGLONG
)0);
8922 ASSERT(FdoData
->Retry
.ListHead
!= NULL
); // never fire an empty list
8925 // fire == (CurrentTick - now) * (100ns per tick)
8927 // NOTE: Overflow is nearly impossible and is ignored here
8930 KeQueryTickCount(&fire
);
8931 fire
.QuadPart
= FdoData
->Retry
.Tick
.QuadPart
- fire
.QuadPart
;
8932 fire
.QuadPart
*= FdoData
->Retry
.Granularity
;
8935 // fire is now multiples of 100ns until should fire the timer.
8936 // if timer should already have expired, or would fire too quickly,
8937 // fire it in some arbitrary number of ticks to prevent infinitely
8941 if (fire
.QuadPart
< MINIMUM_RETRY_UNITS
) {
8942 fire
.QuadPart
= MINIMUM_RETRY_UNITS
;
8945 DebugPrint((ClassDebugDelayedRetry
,
8946 "ClassRetry: ======= %I64x ticks\n",
8950 // must use negative to specify relative time to fire
8953 fire
.QuadPart
= fire
.QuadPart
* ((LONGLONG
)-1);
8956 // set the timer, since this is the first addition
8959 KeSetTimerEx(&FdoData
->Retry
.Timer
, fire
, 0, &FdoData
->Retry
.Dpc
);
8962 } // end ClasspRetryDpcTimer()
8966 ClasspInitializeHotplugInfo(
8967 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8970 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
8971 DEVICE_REMOVAL_POLICY deviceRemovalPolicy
;
8973 ULONG resultLength
= 0;
8974 ULONG writeCacheOverride
;
8979 // start with some default settings
8981 RtlZeroMemory(&(fdoData
->HotplugInfo
), sizeof(STORAGE_HOTPLUG_INFO
));
8984 // set the size (aka version)
8987 fdoData
->HotplugInfo
.Size
= sizeof(STORAGE_HOTPLUG_INFO
);
8990 // set if the device has removable media
8993 if (FdoExtension
->DeviceDescriptor
->RemovableMedia
) {
8994 fdoData
->HotplugInfo
.MediaRemovable
= TRUE
;
8996 fdoData
->HotplugInfo
.MediaRemovable
= FALSE
;
9000 // this refers to devices which, for reasons not yet understood,
9001 // do not fail PREVENT_MEDIA_REMOVAL requests even though they
9002 // have no way to lock the media into the drive. this allows
9003 // the filesystems to turn off delayed-write caching for these
9007 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
9008 FDO_HACK_CANNOT_LOCK_MEDIA
)) {
9009 fdoData
->HotplugInfo
.MediaHotplug
= TRUE
;
9011 fdoData
->HotplugInfo
.MediaHotplug
= FALSE
;
9016 // Look into the registry to see if the user has chosen
9017 // to override the default setting for the removal policy
9020 RtlZeroMemory(&deviceRemovalPolicy
, sizeof(DEVICE_REMOVAL_POLICY
));
9022 ClassGetDeviceParameter(FdoExtension
,
9023 CLASSP_REG_SUBKEY_NAME
,
9024 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
9025 (PULONG
)&deviceRemovalPolicy
);
9027 if (deviceRemovalPolicy
== 0)
9030 // Query the default removal policy from the kernel
9033 status
= IoGetDeviceProperty(FdoExtension
->LowerPdo
,
9034 DevicePropertyRemovalPolicy
,
9035 sizeof(DEVICE_REMOVAL_POLICY
),
9036 (PVOID
)&deviceRemovalPolicy
,
9038 if (!NT_SUCCESS(status
))
9043 if (resultLength
!= sizeof(DEVICE_REMOVAL_POLICY
))
9045 return STATUS_UNSUCCESSFUL
;
9050 // use this info to set the DeviceHotplug setting
9051 // don't rely on DeviceCapabilities, since it can't properly
9052 // determine device relations, etc. let the kernel figure this
9053 // stuff out instead.
9056 if (deviceRemovalPolicy
== RemovalPolicyExpectSurpriseRemoval
) {
9057 fdoData
->HotplugInfo
.DeviceHotplug
= TRUE
;
9059 fdoData
->HotplugInfo
.DeviceHotplug
= FALSE
;
9063 // this refers to the *filesystem* caching, but has to be included
9064 // here since it's a per-device setting. this may change to be
9065 // stored by the system in the future.
9068 writeCacheOverride
= FALSE
;
9069 ClassGetDeviceParameter(FdoExtension
,
9070 CLASSP_REG_SUBKEY_NAME
,
9071 CLASSP_REG_WRITE_CACHE_VALUE_NAME
,
9072 &writeCacheOverride
);
9074 if (writeCacheOverride
) {
9075 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= TRUE
;
9077 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= FALSE
;
9080 return STATUS_SUCCESS
;
9085 ClasspScanForClassHacks(
9086 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
9093 // remove invalid flags and save
9096 CLEAR_FLAG(Data
, FDO_HACK_INVALID_FLAGS
);
9097 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, Data
);
9103 ClasspScanForSpecialInRegistry(
9104 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
9107 HANDLE deviceParameterHandle
; // device instance key
9108 HANDLE classParameterHandle
; // classpnp subkey
9109 OBJECT_ATTRIBUTES objectAttributes
;
9110 UNICODE_STRING subkeyName
;
9114 // seeded in the ENUM tree by ClassInstaller
9117 RTL_QUERY_REGISTRY_TABLE queryTable
[2]; // null terminated array
9121 deviceParameterHandle
= NULL
;
9122 classParameterHandle
= NULL
;
9125 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
9126 PLUGPLAY_REGKEY_DEVICE
,
9128 &deviceParameterHandle
9131 if (!NT_SUCCESS(status
)) {
9132 goto cleanupScanForSpecial
;
9135 RtlInitUnicodeString(&subkeyName
, CLASSP_REG_SUBKEY_NAME
);
9136 InitializeObjectAttributes(&objectAttributes
,
9138 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
9139 deviceParameterHandle
,
9143 status
= ZwOpenKey( &classParameterHandle
,
9148 if (!NT_SUCCESS(status
)) {
9149 goto cleanupScanForSpecial
;
9153 // Zero out the memory
9156 RtlZeroMemory(&queryTable
[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
9159 // Setup the structure to read
9162 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
9163 queryTable
[0].Name
= CLASSP_REG_HACK_VALUE_NAME
;
9164 queryTable
[0].EntryContext
= &deviceHacks
;
9165 queryTable
[0].DefaultType
= REG_DWORD
;
9166 queryTable
[0].DefaultData
= &deviceHacks
;
9167 queryTable
[0].DefaultLength
= 0;
9173 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
9174 (PWSTR
)classParameterHandle
,
9179 if (!NT_SUCCESS(status
)) {
9180 goto cleanupScanForSpecial
;
9184 // remove unknown values and save...
9187 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
9188 "Classpnp => ScanForSpecial: HackFlags %#08x\n",
9191 CLEAR_FLAG(deviceHacks
, FDO_HACK_INVALID_FLAGS
);
9192 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, deviceHacks
);
9195 cleanupScanForSpecial
:
9197 if (deviceParameterHandle
) {
9198 ZwClose(deviceParameterHandle
);
9201 if (classParameterHandle
) {
9202 ZwClose(classParameterHandle
);
9206 // we should modify the system hive to include another key for us to grab
9207 // settings from. in this case: Classpnp\HackFlags
9209 // the use of a DWORD value for the HackFlags allows 32 hacks w/o
9210 // significant use of the registry, and also reduces OEM exposure.
9212 // definition of bit flags:
9213 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
9214 // cannot actually prevent removal.
9215 // 0x00000002 -- Device hard-hangs or times out for GESN requests.
9216 // 0xfffffffc -- Currently reserved, may be used later.