3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 SCSI class driver routines
32 #pragma alloc_text(INIT, DriverEntry)
33 #pragma alloc_text(PAGE, ClassAddDevice)
34 #pragma alloc_text(PAGE, ClassClaimDevice)
35 #pragma alloc_text(PAGE, ClassCreateDeviceObject)
36 #pragma alloc_text(PAGE, ClassDispatchPnp)
37 #pragma alloc_text(PAGE, ClassGetDescriptor)
38 #pragma alloc_text(PAGE, ClassGetPdoId)
39 #pragma alloc_text(PAGE, ClassInitialize)
40 #pragma alloc_text(PAGE, ClassInitializeEx)
41 #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
42 #pragma alloc_text(PAGE, ClassMarkChildMissing)
43 #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
44 #pragma alloc_text(PAGE, ClassModeSense)
45 #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
46 #pragma alloc_text(PAGE, ClassPnpStartDevice)
47 #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
48 #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
49 #pragma alloc_text(PAGE, ClassRemoveDevice)
50 #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
51 #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
52 #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
53 #pragma alloc_text(PAGE, ClassUnload)
54 #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
55 #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
56 #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
57 #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
58 #pragma alloc_text(PAGE, ClasspScanForClassHacks)
59 #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
62 ULONG ClassPnpAllowUnload
= TRUE
;
65 #define FirstDriveLetter 'C'
66 #define LastDriveLetter 'Z'
70 /*++////////////////////////////////////////////////////////////////////////////
76 Temporary entry point needed to initialize the class system dll.
77 It doesn't do anything.
81 DriverObject - Pointer to the driver object created by the system.
91 IN PDRIVER_OBJECT DriverObject
,
92 IN PUNICODE_STRING RegistryPath
95 return STATUS_SUCCESS
;
98 /*++////////////////////////////////////////////////////////////////////////////
104 This routine is called by a class driver during its
105 DriverEntry routine to initialize the driver.
109 Argument1 - Driver Object.
110 Argument2 - Registry Path.
111 InitializationData - Device-specific driver's initialization data.
115 A valid return code for a DriverEntry routine.
123 IN PCLASS_INIT_DATA InitializationData
126 PDRIVER_OBJECT DriverObject
= Argument1
;
127 PUNICODE_STRING RegistryPath
= Argument2
;
129 PCLASS_DRIVER_EXTENSION driverExtension
;
135 DebugPrint((3,"\n\nSCSI Class Driver\n"));
137 ClasspInitializeDebugGlobals();
140 // Validate the length of this structure. This is effectively a
144 if (InitializationData
->InitializationDataSize
!= sizeof(CLASS_INIT_DATA
)) {
147 // This DebugPrint is to help third-party driver writers
150 DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
151 return (ULONG
) STATUS_REVISION_MISMATCH
;
155 // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
156 // are not required entry points.
159 if ((!InitializationData
->FdoData
.ClassDeviceControl
) ||
160 (!((InitializationData
->FdoData
.ClassReadWriteVerification
) ||
161 (InitializationData
->ClassStartIo
))) ||
162 (!InitializationData
->ClassAddDevice
) ||
163 (!InitializationData
->FdoData
.ClassStartDevice
)) {
166 // This DebugPrint is to help third-party driver writers
170 "ClassInitialize: Class device-specific driver missing required "
173 return (ULONG
) STATUS_REVISION_MISMATCH
;
176 if ((InitializationData
->ClassEnumerateDevice
) &&
177 ((!InitializationData
->PdoData
.ClassDeviceControl
) ||
178 (!InitializationData
->PdoData
.ClassStartDevice
) ||
179 (!((InitializationData
->PdoData
.ClassReadWriteVerification
) ||
180 (InitializationData
->ClassStartIo
))))) {
183 // This DebugPrint is to help third-party driver writers
186 DebugPrint((0, "ClassInitialize: Class device-specific missing "
187 "required PDO entry\n"));
189 return (ULONG
) STATUS_REVISION_MISMATCH
;
192 if((InitializationData
->FdoData
.ClassStopDevice
== NULL
) ||
193 ((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
194 (InitializationData
->PdoData
.ClassStopDevice
== NULL
))) {
197 // This DebugPrint is to help third-party driver writers
200 DebugPrint((0, "ClassInitialize: Class device-specific missing "
201 "required PDO entry\n"));
203 return (ULONG
) STATUS_REVISION_MISMATCH
;
207 // Setup the default power handlers if the class driver didn't provide
211 if(InitializationData
->FdoData
.ClassPowerDevice
== NULL
) {
212 InitializationData
->FdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
215 if((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
216 (InitializationData
->PdoData
.ClassPowerDevice
== NULL
)) {
217 InitializationData
->PdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
221 // warn that unload is not supported
223 // ISSUE-2000/02/03-peterwie
224 // We should think about making this a fatal error.
227 if(InitializationData
->ClassUnload
== NULL
) {
230 // This DebugPrint is to help third-party driver writers
233 DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
238 // Create an extension for the driver object
241 status
= IoAllocateDriverObjectExtension(DriverObject
,
242 CLASS_DRIVER_EXTENSION_KEY
,
243 sizeof(CLASS_DRIVER_EXTENSION
),
244 (PVOID
*)&driverExtension
);
246 if(NT_SUCCESS(status
)) {
249 // Copy the registry path into the driver extension so we can use it later
252 driverExtension
->RegistryPath
.Length
= RegistryPath
->Length
;
253 driverExtension
->RegistryPath
.MaximumLength
= RegistryPath
->MaximumLength
;
255 driverExtension
->RegistryPath
.Buffer
=
256 ExAllocatePoolWithTag(PagedPool
,
257 RegistryPath
->MaximumLength
,
260 if(driverExtension
->RegistryPath
.Buffer
== NULL
) {
262 status
= STATUS_INSUFFICIENT_RESOURCES
;
266 RtlCopyUnicodeString(
267 &(driverExtension
->RegistryPath
),
271 // Copy the initialization data into the driver extension so we can reuse
272 // it during our add device routine
276 &(driverExtension
->InitData
),
278 sizeof(CLASS_INIT_DATA
));
280 driverExtension
->DeviceCount
= 0;
282 } else if (status
== STATUS_OBJECT_NAME_COLLISION
) {
285 // The extension already exists - get a pointer to it
288 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
289 CLASS_DRIVER_EXTENSION_KEY
);
291 ASSERT(driverExtension
!= NULL
);
295 DebugPrint((1, "ClassInitialize: Class driver extension could not be "
296 "allocated %lx\n", status
));
301 // Update driver object with entry points.
304 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = ClassCreateClose
;
305 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = ClassCreateClose
;
306 DriverObject
->MajorFunction
[IRP_MJ_READ
] = ClassReadWrite
;
307 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = ClassReadWrite
;
308 DriverObject
->MajorFunction
[IRP_MJ_SCSI
] = ClassInternalIoControl
;
309 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = ClassDeviceControlDispatch
;
310 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = ClassShutdownFlush
;
311 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = ClassShutdownFlush
;
312 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = ClassDispatchPnp
;
313 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = ClassDispatchPower
;
314 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = ClassSystemControl
;
316 if (InitializationData
->ClassStartIo
) {
317 DriverObject
->DriverStartIo
= ClasspStartIo
;
320 if ((InitializationData
->ClassUnload
) && (ClassPnpAllowUnload
== TRUE
)) {
321 DriverObject
->DriverUnload
= ClassUnload
;
323 DriverObject
->DriverUnload
= NULL
;
326 DriverObject
->DriverExtension
->AddDevice
= ClassAddDevice
;
328 DbgPrint("Driver is ready to go\n");
329 status
= STATUS_SUCCESS
;
331 } // end ClassInitialize()
333 /*++////////////////////////////////////////////////////////////////////////////
339 This routine is allows the caller to do any extra initialization or
340 setup that is not done in ClassInitialize. The operation is
341 controlled by the GUID that is passed and the contents of the Data
342 parameter is dependent upon the GUID.
344 This is the list of supported operations:
346 Guid - GUID_CLASSPNP_QUERY_REGINFOEX
347 Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
349 Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
350 callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
351 former callback allows the driver to specify the name of the
368 IN PDRIVER_OBJECT DriverObject
,
373 PCLASS_DRIVER_EXTENSION driverExtension
;
379 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
380 CLASS_DRIVER_EXTENSION_KEY
382 if (IsEqualGUID(Guid
, &ClassGuidQueryRegInfoEx
))
384 PCLASS_QUERY_WMI_REGINFO_EX_LIST List
;
387 // Indicate the device supports PCLASS_QUERY_REGINFO_EX
388 // callback instead of PCLASS_QUERY_REGINFO callback.
390 List
= (PCLASS_QUERY_WMI_REGINFO_EX_LIST
)Data
;
392 if (List
->Size
== sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST
))
394 driverExtension
->ClassFdoQueryWmiRegInfoEx
= List
->ClassFdoQueryWmiRegInfoEx
;
395 driverExtension
->ClassPdoQueryWmiRegInfoEx
= List
->ClassPdoQueryWmiRegInfoEx
;
396 status
= STATUS_SUCCESS
;
398 status
= STATUS_INVALID_PARAMETER
;
401 status
= STATUS_NOT_SUPPORTED
;
406 } // end ClassInitializeEx()
408 /*++////////////////////////////////////////////////////////////////////////////
414 called when there are no more references to the driver. this allows
415 drivers to be updated without rebooting.
419 DriverObject - a pointer to the driver object that is being unloaded
427 IN PDRIVER_OBJECT DriverObject
430 PCLASS_DRIVER_EXTENSION driverExtension
;
435 ASSERT( DriverObject
->DeviceObject
== NULL
);
437 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
438 CLASS_DRIVER_EXTENSION_KEY
441 ASSERT(driverExtension
!= NULL
);
442 ASSERT(driverExtension
->RegistryPath
.Buffer
!= NULL
);
443 ASSERT(driverExtension
->InitData
.ClassUnload
!= NULL
);
445 DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
446 &driverExtension
->RegistryPath
));
449 // attempt to process the driver's unload routine first.
452 driverExtension
->InitData
.ClassUnload(DriverObject
);
455 // free own allocated resources and return
458 ExFreePool( driverExtension
->RegistryPath
.Buffer
);
459 driverExtension
->RegistryPath
.Buffer
= NULL
;
460 driverExtension
->RegistryPath
.Length
= 0;
461 driverExtension
->RegistryPath
.MaximumLength
= 0;
464 } // end ClassUnload()
466 /*++////////////////////////////////////////////////////////////////////////////
472 SCSI class driver add device routine. This is called by pnp when a new
473 physical device come into being.
475 This routine will call out to the class driver to verify that it should
476 own this device then will create and attach a device object and then hand
477 it to the driver to initialize and create symbolic links
481 DriverObject - a pointer to the driver object that this is being created for
482 PhysicalDeviceObject - a pointer to the physical device object
484 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
485 STATUS_SUCCESS if the creation and attachment was successful
486 status of device creation and initialization
492 IN PDRIVER_OBJECT DriverObject
,
493 IN PDEVICE_OBJECT PhysicalDeviceObject
496 PCLASS_DRIVER_EXTENSION driverExtension
=
497 IoGetDriverObjectExtension(DriverObject
,
498 CLASS_DRIVER_EXTENSION_KEY
);
504 DbgPrint("got a device\n");
505 status
= driverExtension
->InitData
.ClassAddDevice(DriverObject
,
506 PhysicalDeviceObject
);
508 } // end ClassAddDevice()
510 /*++////////////////////////////////////////////////////////////////////////////
516 Storage class driver pnp routine. This is called by the io system when
517 a PNP request is sent to the device.
521 DeviceObject - pointer to the device object
523 Irp - pointer to the io request packet
533 IN PDEVICE_OBJECT DeviceObject
,
537 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
538 BOOLEAN isFdo
= commonExtension
->IsFdo
;
540 PCLASS_DRIVER_EXTENSION driverExtension
;
541 PCLASS_INIT_DATA initData
;
542 PCLASS_DEV_INFO devInfo
;
544 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
546 NTSTATUS status
= Irp
->IoStatus
.Status
;
547 BOOLEAN completeRequest
= TRUE
;
548 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 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 severely 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 NT_ASSERTMSGW(L
"ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
807 L
"never be failed\n", NT_SUCCESS(status
));
809 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
810 irpStack
->MinorFunction
);
811 NT_ASSERTMSGW(L
"ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
812 L
"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 NT_ASSERTMSGW(L
"ClassDispatchPnp !! STOP_DEVICE should "
880 L
"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 UCHAR removeType
= irpStack
->MinorFunction
;
899 if (commonExtension
->PagingPathCount
!= 0) {
900 DBGTRACE(ClassDebugWarning
, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject
, Irp
));
904 // Release the lock for this IRP before calling in.
906 ClassReleaseRemoveLock(DeviceObject
, Irp
);
910 * If a timer was started on the device, stop it.
912 if (DeviceObject
->Timer
) {
913 IoStopTimer(DeviceObject
);
917 * "Fire-and-forget" the remove irp to the lower stack.
918 * Don't touch the irp (or the irp stack!) after this.
921 IoCopyCurrentIrpStackLocationToNext(Irp
);
922 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
923 ASSERT(NT_SUCCESS(status
));
924 completeRequest
= FALSE
;
927 status
= STATUS_SUCCESS
;
931 * Do our own cleanup and call the class driver's remove
933 * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
934 * so don't touch the extension after this.
936 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
937 commonExtension
->CurrentState
= removeType
;
938 ClassRemoveDevice(DeviceObject
, removeType
);
943 case IRP_MN_DEVICE_USAGE_NOTIFICATION
: {
945 switch(irpStack
->Parameters
.UsageNotification
.Type
) {
947 case DeviceUsageTypePaging
: {
951 if((irpStack
->Parameters
.UsageNotification
.InPath
) &&
952 (commonExtension
->CurrentState
!= IRP_MN_START_DEVICE
)) {
955 // Device isn't started. Don't allow adding a
956 // paging file, but allow a removal of one.
959 status
= STATUS_DEVICE_NOT_READY
;
963 ASSERT(commonExtension
->IsInitialized
);
966 // need to synchronize this now...
969 KeEnterCriticalRegion();
970 status
= KeWaitForSingleObject(&commonExtension
->PathCountEvent
,
971 Executive
, KernelMode
,
973 ASSERT(NT_SUCCESS(status
));
974 status
= STATUS_SUCCESS
;
977 // If the volume is removable we should try to lock it in
978 // place or unlock it once per paging path count
981 if (commonExtension
->IsFdo
){
982 status
= ClasspEjectionControl(
986 (BOOLEAN
)irpStack
->Parameters
.UsageNotification
.InPath
);
989 if (!NT_SUCCESS(status
)){
990 KeSetEvent(&commonExtension
->PathCountEvent
, IO_NO_INCREMENT
, FALSE
);
991 KeLeaveCriticalRegion();
996 // if removing last paging device, need to set DO_POWER_PAGABLE
997 // bit here, and possible re-set it below on failure.
1002 if (!irpStack
->Parameters
.UsageNotification
.InPath
&&
1003 commonExtension
->PagingPathCount
== 1
1007 // removing last paging file
1008 // must have DO_POWER_PAGABLE bits set, but only
1009 // if noone set the DO_POWER_INRUSH bit
1013 if (TEST_FLAG(DeviceObject
->Flags
, DO_POWER_INRUSH
)) {
1014 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1015 "paging file removed, but "
1016 "DO_POWER_INRUSH was set, so NOT "
1017 "setting DO_POWER_PAGABLE\n",
1018 DeviceObject
, Irp
));
1020 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1021 "paging file removed, "
1022 "setting DO_POWER_PAGABLE\n",
1023 DeviceObject
, Irp
));
1024 SET_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1031 // forward the irp before finishing handling the
1035 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1038 // now deal with the failure and success cases.
1039 // note that we are not allowed to fail the irp
1040 // once it is sent to the lower drivers.
1043 if (NT_SUCCESS(status
)) {
1045 IoAdjustPagingPathCount(
1046 (PLONG
)&commonExtension
->PagingPathCount
,
1047 irpStack
->Parameters
.UsageNotification
.InPath
);
1049 if (irpStack
->Parameters
.UsageNotification
.InPath
) {
1050 if (commonExtension
->PagingPathCount
== 1) {
1051 DebugPrint((2, "ClassDispatchPnp (%p,%p): "
1052 "Clearing PAGABLE bit\n",
1053 DeviceObject
, Irp
));
1054 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1061 // cleanup the changes done above
1064 if (setPagable
== TRUE
) {
1065 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
1066 "PAGABLE bit due to irp failure\n",
1067 DeviceObject
, Irp
));
1068 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1073 // relock or unlock the media if needed.
1076 if (commonExtension
->IsFdo
) {
1078 ClasspEjectionControl(
1082 (BOOLEAN
)!irpStack
->Parameters
.UsageNotification
.InPath
);
1087 // set the event so the next one can occur.
1090 KeSetEvent(&commonExtension
->PathCountEvent
,
1091 IO_NO_INCREMENT
, FALSE
);
1092 KeLeaveCriticalRegion();
1096 case DeviceUsageTypeHibernation
: {
1098 IoAdjustPagingPathCount(
1099 (PLONG
)&commonExtension
->HibernationPathCount
,
1100 irpStack
->Parameters
.UsageNotification
.InPath
1102 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1103 if (!NT_SUCCESS(status
)) {
1104 IoAdjustPagingPathCount(
1105 (PLONG
)&commonExtension
->HibernationPathCount
,
1106 !irpStack
->Parameters
.UsageNotification
.InPath
1113 case DeviceUsageTypeDumpFile
: {
1114 IoAdjustPagingPathCount(
1115 (PLONG
)&commonExtension
->DumpPathCount
,
1116 irpStack
->Parameters
.UsageNotification
.InPath
1118 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1119 if (!NT_SUCCESS(status
)) {
1120 IoAdjustPagingPathCount(
1121 (PLONG
)&commonExtension
->DumpPathCount
,
1122 !irpStack
->Parameters
.UsageNotification
.InPath
1130 status
= STATUS_INVALID_PARAMETER
;
1137 case IRP_MN_QUERY_CAPABILITIES
: {
1139 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1140 DeviceObject
, Irp
));
1144 status
= ClassQueryPnpCapabilities(
1146 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
1153 PDEVICE_CAPABILITIES deviceCapabilities
;
1154 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
1155 PCLASS_PRIVATE_FDO_DATA fdoData
;
1157 fdoExtension
= DeviceObject
->DeviceExtension
;
1158 fdoData
= fdoExtension
->PrivateFdoData
;
1159 deviceCapabilities
=
1160 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
;
1163 // forward the irp before handling the special cases
1166 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1167 if (!NT_SUCCESS(status
)) {
1172 // we generally want to remove the device from the hotplug
1173 // applet, which requires the SR-OK bit to be set.
1174 // only when the user specifies that they are capable of
1175 // safely removing things do we want to clear this bit
1176 // (saved in WriteCacheEnableOverride)
1178 // setting of this bit is done either above, or by the
1181 // note: may not be started, so check we have FDO data first.
1185 fdoData
->HotplugInfo
.WriteCacheEnableOverride
) {
1186 if (deviceCapabilities
->SurpriseRemovalOK
) {
1187 DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
1188 "device capabilities due to hotplug "
1189 "device or media\n"));
1191 deviceCapabilities
->SurpriseRemovalOK
= FALSE
;
1195 } // end QUERY_CAPABILITIES for FDOs
1201 } // end QUERY_CAPABILITIES
1206 IoCopyCurrentIrpStackLocationToNext(Irp
);
1208 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1209 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
1211 completeRequest
= FALSE
;
1219 ASSERT(driverExtension
);
1220 status
= STATUS_INTERNAL_ERROR
;
1223 if (completeRequest
){
1224 Irp
->IoStatus
.Status
= status
;
1227 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1230 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1232 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject
, Irp
, commonExtension
->PreviousState
, commonExtension
->CurrentState
));
1236 * The irp is already completed so don't touch it.
1237 * This may be a remove so don't touch the device extension.
1239 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject
, Irp
));
1243 } // end ClassDispatchPnp()
1245 /*++////////////////////////////////////////////////////////////////////////////
1247 ClassPnpStartDevice()
1249 Routine Description:
1251 Storage class driver routine for IRP_MN_START_DEVICE requests.
1252 This routine kicks off any device specific initialization
1256 DeviceObject - a pointer to the device object
1258 Irp - a pointer to the io request packet
1265 NTSTATUS NTAPI
ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject
)
1267 PCLASS_DRIVER_EXTENSION driverExtension
;
1268 PCLASS_INIT_DATA initData
;
1270 PCLASS_DEV_INFO devInfo
;
1272 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1273 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
1274 BOOLEAN isFdo
= commonExtension
->IsFdo
;
1276 BOOLEAN isMountedDevice
= TRUE
;
1277 //UNICODE_STRING interfaceName;
1279 BOOLEAN timerStarted
;
1281 NTSTATUS status
= STATUS_SUCCESS
;
1285 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
1286 CLASS_DRIVER_EXTENSION_KEY
);
1288 initData
= &(driverExtension
->InitData
);
1290 devInfo
= &(initData
->FdoData
);
1292 devInfo
= &(initData
->PdoData
);
1295 ASSERT(devInfo
->ClassInitDevice
!= NULL
);
1296 ASSERT(devInfo
->ClassStartDevice
!= NULL
);
1298 if (!commonExtension
->IsInitialized
){
1301 // perform FDO/PDO specific initialization
1305 STORAGE_PROPERTY_ID propertyId
;
1308 // allocate a private extension for class data
1311 if (fdoExtension
->PrivateFdoData
== NULL
) {
1312 fdoExtension
->PrivateFdoData
=
1313 ExAllocatePoolWithTag(NonPagedPool
,
1314 sizeof(CLASS_PRIVATE_FDO_DATA
),
1315 CLASS_TAG_PRIVATE_DATA
1319 if (fdoExtension
->PrivateFdoData
== NULL
) {
1320 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
1321 "private fdo data\n"));
1322 return STATUS_INSUFFICIENT_RESOURCES
;
1326 // initialize the struct's various fields.
1329 RtlZeroMemory(fdoExtension
->PrivateFdoData
,
1330 sizeof(CLASS_PRIVATE_FDO_DATA
)
1332 KeInitializeTimer(&fdoExtension
->PrivateFdoData
->Retry
.Timer
);
1333 KeInitializeDpc(&fdoExtension
->PrivateFdoData
->Retry
.Dpc
,
1334 ClasspRetryRequestDpc
,
1336 KeInitializeSpinLock(&fdoExtension
->PrivateFdoData
->Retry
.Lock
);
1337 fdoExtension
->PrivateFdoData
->Retry
.Granularity
=
1338 KeQueryTimeIncrement();
1339 commonExtension
->Reserved4
= (ULONG_PTR
)(' GPH'); // debug aid
1342 // NOTE: the old interface allowed the class driver to allocate
1343 // this. this was unsafe for low-memory conditions. allocate one
1344 // unconditionally now, and modify our internal functions to use
1345 // our own exclusively as it is the only safe way to do this.
1348 status
= ClasspAllocateReleaseQueueIrp(fdoExtension
);
1349 if (!NT_SUCCESS(status
)) {
1350 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
1351 "private release queue irp\n"));
1356 // Call port driver to get adapter capabilities.
1359 propertyId
= StorageAdapterProperty
;
1361 status
= ClassGetDescriptor(
1362 commonExtension
->LowerDeviceObject
,
1364 (PSTORAGE_DESCRIPTOR_HEADER
*)&fdoExtension
->AdapterDescriptor
);
1366 if(!NT_SUCCESS(status
)) {
1369 // This DebugPrint is to help third-party driver writers
1372 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1373 "[ADAPTER] failed %lx\n", status
));
1378 // Call port driver to get device descriptor.
1381 propertyId
= StorageDeviceProperty
;
1383 status
= ClassGetDescriptor(
1384 commonExtension
->LowerDeviceObject
,
1386 (PSTORAGE_DESCRIPTOR_HEADER
*)&fdoExtension
->DeviceDescriptor
);
1388 if(!NT_SUCCESS(status
)) {
1391 // This DebugPrint is to help third-party driver writers
1394 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1395 "[DEVICE] failed %lx\n", status
));
1399 ClasspScanForSpecialInRegistry(fdoExtension
);
1400 ClassScanForSpecial(fdoExtension
,
1402 ClasspScanForClassHacks
);
1405 // allow perf to be re-enabled after a given number of failed IOs
1406 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
1411 ClassGetDeviceParameter(fdoExtension
,
1412 CLASSP_REG_SUBKEY_NAME
,
1413 CLASSP_REG_PERF_RESTORE_VALUE_NAME
,
1415 if (t
>= CLASS_PERF_RESTORE_MINIMUM
) {
1416 fdoExtension
->PrivateFdoData
->Perf
.ReEnableThreshold
= t
;
1422 // compatibility comes first. writable cd media will not
1423 // get a SYNCH_CACHE on power down.
1426 if (fdoExtension
->DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) {
1427 SET_FLAG(fdoExtension
->PrivateFdoData
->HackFlags
,
1428 FDO_HACK_NO_SYNC_CACHE
);
1432 // initialize the hotplug information only after the ScanForSpecial
1433 // routines, as it relies upon the hack flags.
1436 status
= ClasspInitializeHotplugInfo(fdoExtension
);
1438 if (!NT_SUCCESS(status
)) {
1439 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
1440 "hotplug information %lx\n", status
));
1445 * Allocate/initialize TRANSFER_PACKETs and related resources.
1447 status
= InitializeTransferPackets(DeviceObject
);
1451 // ISSUE - drivers need to disable write caching on the media
1452 // if hotplug and !useroverride. perhaps we should
1453 // allow registration of a callback to enable/disable
1454 // write cache instead.
1457 if (NT_SUCCESS(status
)){
1458 status
= devInfo
->ClassInitDevice(DeviceObject
);
1463 if (!NT_SUCCESS(status
)){
1466 // Just bail out - the remove that comes down will clean up the
1467 // initialized scraps.
1472 commonExtension
->IsInitialized
= TRUE
;
1474 if (commonExtension
->IsFdo
) {
1475 fdoExtension
->PrivateFdoData
->Perf
.OriginalSrbFlags
= fdoExtension
->SrbFlags
;
1481 // If device requests autorun functionality or a once a second callback
1482 // then enable the once per second timer.
1484 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
1485 // called in the context of the ClassInitDevice callback. If called
1486 // after then this check will have already been made and the
1487 // once a second timer will not have been enabled.
1490 ((initData
->ClassTick
!= NULL
) ||
1491 (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) ||
1492 ((fdoExtension
->FailurePredictionInfo
!= NULL
) &&
1493 (fdoExtension
->FailurePredictionInfo
->Method
!= FailurePredictionNone
))))
1495 ClasspEnableTimer(DeviceObject
);
1496 timerStarted
= TRUE
;
1498 timerStarted
= FALSE
;
1502 // NOTE: the timer looks at commonExtension->CurrentState now
1503 // to prevent Media Change Notification code from running
1504 // until the device is started, but allows the device
1505 // specific tick handler to run. therefore it is imperative
1506 // that commonExtension->CurrentState not be updated until
1507 // the device specific startdevice handler has finished.
1510 status
= devInfo
->ClassStartDevice(DeviceObject
);
1512 if(NT_SUCCESS(status
)) {
1513 commonExtension
->CurrentState
= IRP_MN_START_DEVICE
;
1515 if((isFdo
) && (initData
->ClassEnumerateDevice
!= NULL
)) {
1516 isMountedDevice
= FALSE
;
1519 if((DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) &&
1520 (DeviceObject
->DeviceType
!= FILE_DEVICE_CD_ROM
)) {
1522 isMountedDevice
= FALSE
;
1526 if(isMountedDevice
) {
1527 ClasspRegisterMountedDeviceInterface(DeviceObject
);
1530 if((commonExtension
->IsFdo
) &&
1531 (devInfo
->ClassWmiInfo
.GuidRegInfo
!= NULL
)) {
1533 IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_REGISTER
);
1538 ClasspDisableTimer(DeviceObject
);
1546 /*++////////////////////////////////////////////////////////////////////////////
1550 Routine Description:
1552 This is the system entry point for read and write requests. The
1553 device-specific handler is invoked to perform any validation necessary.
1555 If the device object is a PDO (partition object) then the request will
1556 simply be adjusted for Partition0 and issued to the lower device driver.
1558 IF the device object is an FDO (partition 0 object), the number of bytes
1559 in the request are checked against the maximum byte counts that the adapter
1560 supports and requests are broken up into
1561 smaller sizes if necessary.
1565 DeviceObject - a pointer to the device object for this request
1574 NTSTATUS NTAPI
ClassReadWrite(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
1576 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1577 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
1578 PIO_STACK_LOCATION currentIrpStack
= IoGetCurrentIrpStackLocation(Irp
);
1579 //LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
1580 ULONG transferByteCount
= currentIrpStack
->Parameters
.Read
.Length
;
1585 * Grab the remove lock. If we can't acquire it, bail out.
1587 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
1589 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1590 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1591 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1592 status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1594 else if (TEST_FLAG(DeviceObject
->Flags
, DO_VERIFY_VOLUME
) &&
1595 (currentIrpStack
->MinorFunction
!= CLASSP_VOLUME_VERIFY_CHECKED
) &&
1596 !TEST_FLAG(currentIrpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
)){
1599 * DO_VERIFY_VOLUME is set for the device object,
1600 * but this request is not itself a verify request.
1601 * So fail this request.
1603 IoSetHardErrorOrVerifyDevice(Irp
, DeviceObject
);
1604 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
1605 Irp
->IoStatus
.Information
= 0;
1606 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1607 ClassCompleteRequest(DeviceObject
, Irp
, 0);
1608 status
= STATUS_VERIFY_REQUIRED
;
1613 * Since we've bypassed the verify-required tests we don't need to repeat
1614 * them with this IRP - in particular we don't want to worry about
1615 * hitting them at the partition 0 level if the request has come through
1616 * a non-zero partition.
1618 currentIrpStack
->MinorFunction
= CLASSP_VOLUME_VERIFY_CHECKED
;
1621 * Call the miniport driver's pre-pass filter to check if we
1622 * should continue with this transfer.
1624 ASSERT(commonExtension
->DevInfo
->ClassReadWriteVerification
);
1625 status
= commonExtension
->DevInfo
->ClassReadWriteVerification(DeviceObject
, Irp
);
1626 if (!NT_SUCCESS(status
)){
1627 ASSERT(Irp
->IoStatus
.Status
== status
);
1628 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1629 ClassCompleteRequest (DeviceObject
, Irp
, IO_NO_INCREMENT
);
1631 else if (status
== STATUS_PENDING
){
1633 * ClassReadWriteVerification queued this request.
1634 * So don't touch the irp anymore.
1639 if (transferByteCount
== 0) {
1641 * Several parts of the code turn 0 into 0xffffffff,
1642 * so don't process a zero-length request any further.
1644 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1645 Irp
->IoStatus
.Information
= 0;
1646 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1647 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1648 status
= STATUS_SUCCESS
;
1652 * If the driver has its own StartIo routine, call it.
1654 if (commonExtension
->DriverExtension
->InitData
.ClassStartIo
) {
1655 IoMarkIrpPending(Irp
);
1656 IoStartPacket(DeviceObject
, Irp
, NULL
, NULL
);
1657 status
= STATUS_PENDING
;
1661 * The driver does not have its own StartIo routine.
1662 * So process this request ourselves.
1666 * Add partition byte offset to make starting byte relative to
1667 * beginning of disk.
1669 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1670 commonExtension
->StartingOffset
.QuadPart
;
1672 if (commonExtension
->IsFdo
){
1675 * Add in any skew for the disk manager software.
1677 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1678 commonExtension
->PartitionZeroExtension
->DMByteSkew
;
1681 * Perform the actual transfer(s) on the hardware
1682 * to service this request.
1684 ServiceTransferRequest(DeviceObject
, Irp
);
1685 status
= STATUS_PENDING
;
1689 * This is a child PDO enumerated for our FDO by e.g. disk.sys
1690 * and owned by e.g. partmgr. Send it down to the next device
1691 * and the same irp will come back to us for the FDO.
1693 IoCopyCurrentIrpStackLocationToNext(Irp
);
1694 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1695 status
= IoCallDriver(lowerDeviceObject
, Irp
);
1706 /*++////////////////////////////////////////////////////////////////////////////
1708 ClassReadDriveCapacity()
1710 Routine Description:
1712 This routine sends a READ CAPACITY to the requested device, updates
1713 the geometry information in the device object and returns
1714 when it is complete. This routine is synchronous.
1716 This routine must be called with the remove lock held or some other
1717 assurance that the Fdo will not be removed while processing.
1721 DeviceObject - Supplies a pointer to the device object that represents
1722 the device whose capacity is to be read.
1729 NTSTATUS NTAPI
ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo
)
1731 READ_CAPACITY_DATA readCapacityBuffer
= {0};
1735 driveCapMdl
= BuildDeviceInputMdl(&readCapacityBuffer
, sizeof(READ_CAPACITY_DATA
));
1738 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1740 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
1742 //NTSTATUS pktStatus;
1743 IRP pseudoIrp
= {0};
1746 * Our engine needs an "original irp" to write the status back to
1747 * and to count down packets (one in this case).
1748 * Just use a pretend irp for this.
1750 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1751 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1752 pseudoIrp
.IoStatus
.Information
= 0;
1753 pseudoIrp
.MdlAddress
= driveCapMdl
;
1756 * Set this up as a SYNCHRONOUS transfer, submit it,
1757 * and wait for the packet to complete. The result
1758 * status will be written to the original irp.
1760 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1761 SetupDriveCapacityTransferPacket( pkt
,
1762 &readCapacityBuffer
,
1763 sizeof(READ_CAPACITY_DATA
),
1766 SubmitTransferPacket(pkt
);
1767 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1769 status
= pseudoIrp
.IoStatus
.Status
;
1772 * If we got an UNDERRUN, retry exactly once.
1773 * (The transfer_packet engine didn't retry because the result
1774 * status was success).
1776 if (NT_SUCCESS(status
) &&
1777 (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
))){
1778 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG
)pseudoIrp
.IoStatus
.Information
, sizeof(READ_CAPACITY_DATA
)));
1780 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1782 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1783 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1784 pseudoIrp
.IoStatus
.Information
= 0;
1785 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1786 SetupDriveCapacityTransferPacket( pkt
,
1787 &readCapacityBuffer
,
1788 sizeof(READ_CAPACITY_DATA
),
1791 SubmitTransferPacket(pkt
);
1792 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1793 status
= pseudoIrp
.IoStatus
.Status
;
1794 if (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
)){
1795 status
= STATUS_DEVICE_BUSY
;
1799 status
= STATUS_INSUFFICIENT_RESOURCES
;
1804 if (NT_SUCCESS(status
)){
1806 * The request succeeded.
1807 * Read out and store the drive information.
1810 ULONG bytesPerSector
;
1815 * Read the bytesPerSector value,
1816 * which is big-endian in the returned buffer.
1817 * Default to the standard 512 bytes.
1819 tmp
= readCapacityBuffer
.BytesPerBlock
;
1820 ((PFOUR_BYTE
)&bytesPerSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1821 ((PFOUR_BYTE
)&bytesPerSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1822 ((PFOUR_BYTE
)&bytesPerSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1823 ((PFOUR_BYTE
)&bytesPerSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1824 if (bytesPerSector
== 0) {
1825 bytesPerSector
= 512;
1829 * Clear all but the highest set bit.
1830 * That will give us a bytesPerSector value that is a power of 2.
1832 while (bytesPerSector
& (bytesPerSector
-1)) {
1833 bytesPerSector
&= bytesPerSector
-1;
1836 fdoExt
->DiskGeometry
.BytesPerSector
= bytesPerSector
;
1839 // Copy last sector in reverse byte order.
1842 tmp
= readCapacityBuffer
.LogicalBlockAddress
;
1843 ((PFOUR_BYTE
)&lastSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1844 ((PFOUR_BYTE
)&lastSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1845 ((PFOUR_BYTE
)&lastSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1846 ((PFOUR_BYTE
)&lastSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1849 // Calculate sector to byte shift.
1852 WHICH_BIT(fdoExt
->DiskGeometry
.BytesPerSector
, fdoExt
->SectorShift
);
1854 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
1855 fdoExt
->DiskGeometry
.BytesPerSector
));
1857 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
1860 if (fdoExt
->DMActive
){
1861 DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
1863 lastSector
-= fdoExt
->DMSkew
;
1867 * Check to see if we have a geometry we should be using already.
1869 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1870 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1871 if (cylinderSize
== 0){
1872 DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
1873 "values from %#x/%#x to %#x/%#x\n",
1874 fdoExt
->DiskGeometry
.TracksPerCylinder
,
1875 fdoExt
->DiskGeometry
.SectorsPerTrack
,
1879 fdoExt
->DiskGeometry
.TracksPerCylinder
= 0xff;
1880 fdoExt
->DiskGeometry
.SectorsPerTrack
= 0x3f;
1883 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1884 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1888 // Calculate number of cylinders.
1891 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= (LONGLONG
)((lastSector
+ 1)/cylinderSize
);
1894 // if there are zero cylinders, then the device lied AND it's
1895 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
1896 // this can fit into a single LONGLONG, so create another usable
1897 // geometry, even if it's unusual looking. This allows small,
1898 // non-standard devices, such as Sony's Memory Stick, to show
1899 // up as having a partition.
1902 if (fdoExt
->DiskGeometry
.Cylinders
.QuadPart
== (LONGLONG
)0) {
1903 fdoExt
->DiskGeometry
.SectorsPerTrack
= 1;
1904 fdoExt
->DiskGeometry
.TracksPerCylinder
= 1;
1905 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= lastSector
;
1910 // Calculate media capacity in bytes.
1913 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
=
1914 ((LONGLONG
)(lastSector
+ 1)) << fdoExt
->SectorShift
;
1917 * Is this removable or fixed media
1919 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1920 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1923 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1928 * The request failed.
1932 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
1933 // what happens when the disk's sector size is bigger than
1934 // 512 bytes and we hit this code path? this is untested.
1936 // If the read capacity fails, set the geometry to reasonable parameter
1937 // so things don't fail at unexpected places. Zero the geometry
1938 // except for the bytes per sector and sector shift.
1942 * This request can sometimes fail legitimately
1943 * (e.g. when a SCSI device is attached but turned off)
1944 * so this is not necessarily a device/driver bug.
1946 DBGTRACE(ClassDebugWarning
, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo
, status
));
1949 * Write in a default disk geometry which we HOPE is right (??).
1952 RtlZeroMemory(&fdoExt
->DiskGeometry
, sizeof(DISK_GEOMETRY
));
1953 fdoExt
->DiskGeometry
.BytesPerSector
= 512;
1954 fdoExt
->SectorShift
= 9;
1955 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
= (LONGLONG
) 0;
1958 * Is this removable or fixed media
1960 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1961 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1964 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1970 status
= STATUS_INSUFFICIENT_RESOURCES
;
1973 FreeDeviceInputMdl(driveCapMdl
);
1976 status
= STATUS_INSUFFICIENT_RESOURCES
;
1983 /*++////////////////////////////////////////////////////////////////////////////
1985 ClassSendStartUnit()
1987 Routine Description:
1989 Send command to SCSI unit to start or power up.
1990 Because this command is issued asynchronously, that is, without
1991 waiting on it to complete, the IMMEDIATE flag is not set. This
1992 means that the CDB will not return until the drive has powered up.
1993 This should keep subsequent requests from being submitted to the
1994 device before it has completely spun up.
1996 This routine is called from the InterpretSense routine, when a
1997 request sense returns data indicating that a drive must be
2000 This routine may also be called from a class driver's error handler,
2001 or anytime a non-critical start device should be sent to the device.
2005 Fdo - The functional device object for the stopped device.
2015 IN PDEVICE_OBJECT Fdo
2018 PIO_STACK_LOCATION irpStack
;
2020 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2021 PSCSI_REQUEST_BLOCK srb
;
2022 PCOMPLETION_CONTEXT context
;
2026 // Allocate Srb from nonpaged pool.
2029 context
= ExAllocatePoolWithTag(NonPagedPool
,
2030 sizeof(COMPLETION_CONTEXT
),
2033 if(context
== NULL
) {
2036 // ISSUE-2000/02/03-peterwie
2037 // This code path was inherited from the NT 4.0 class2.sys driver.
2038 // It needs to be changed to survive low-memory conditions.
2041 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2045 // Save the device object in the context for use by the completion
2049 context
->DeviceObject
= Fdo
;
2050 srb
= &context
->Srb
;
2056 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
2059 // Write length to SRB.
2062 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2064 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2067 // Set timeout value large enough for drive to spin up.
2070 srb
->TimeOutValue
= START_UNIT_TIMEOUT
;
2073 // Set the transfer length.
2076 srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
|
2077 SRB_FLAGS_DISABLE_AUTOSENSE
|
2078 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
;
2081 // Build the start unit CDB.
2085 cdb
= (PCDB
)srb
->Cdb
;
2087 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
2088 cdb
->START_STOP
.Start
= 1;
2089 cdb
->START_STOP
.Immediate
= 0;
2090 cdb
->START_STOP
.LogicalUnitNumber
= srb
->Lun
;
2093 // Build the asynchronous request to be sent to the port driver.
2094 // Since this routine is called from a DPC the IRP should always be
2098 irp
= IoAllocateIrp(Fdo
->StackSize
, FALSE
);
2103 // ISSUE-2000/02/03-peterwie
2104 // This code path was inherited from the NT 4.0 class2.sys driver.
2105 // It needs to be changed to survive low-memory conditions.
2108 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2112 ClassAcquireRemoveLock(Fdo
, irp
);
2114 IoSetCompletionRoutine(irp
,
2115 ClassAsynchronousCompletion
,
2121 irpStack
= IoGetNextIrpStackLocation(irp
);
2122 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2123 srb
->OriginalRequest
= irp
;
2126 // Store the SRB address in next stack for port driver.
2129 irpStack
->Parameters
.Scsi
.Srb
= srb
;
2132 // Call the port driver with the IRP.
2135 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2139 } // end StartUnit()
2141 /*++////////////////////////////////////////////////////////////////////////////
2143 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
2145 Routine Description:
2147 This routine is called when an asynchronous I/O request
2148 which was issued by the class driver completes. Examples of such requests
2149 are release queue or START UNIT. This routine releases the queue if
2150 necessary. It then frees the context and the IRP.
2154 DeviceObject - The device object for the logical unit; however since this
2155 is the top stack location the value is NULL.
2157 Irp - Supplies a pointer to the Irp to be processed.
2159 Context - Supplies the context to be used to process this request.
2168 ClassAsynchronousCompletion(
2169 PDEVICE_OBJECT DeviceObject
,
2174 PCOMPLETION_CONTEXT context
= Context
;
2175 PSCSI_REQUEST_BLOCK srb
;
2177 if(DeviceObject
== NULL
) {
2179 DeviceObject
= context
->DeviceObject
;
2182 srb
= &context
->Srb
;
2185 // If this is an execute srb, then check the return status and make sure.
2186 // the queue is not frozen.
2189 if (srb
->Function
== SRB_FUNCTION_EXECUTE_SCSI
) {
2192 // Check for a frozen queue.
2195 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2198 // Unfreeze the queue getting the device object from the context.
2201 ClassReleaseQueue(context
->DeviceObject
);
2205 { // free port-allocated sense buffer if we can detect
2207 if (((PCOMMON_DEVICE_EXTENSION
)(DeviceObject
->DeviceExtension
))->IsFdo
) {
2209 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
2210 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2211 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2216 ASSERT(!TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
2223 // Free the context and the Irp.
2226 if (Irp
->MdlAddress
!= NULL
) {
2227 MmUnlockPages(Irp
->MdlAddress
);
2228 IoFreeMdl(Irp
->MdlAddress
);
2230 Irp
->MdlAddress
= NULL
;
2233 ClassReleaseRemoveLock(DeviceObject
, Irp
);
2235 ExFreePool(context
);
2239 // Indicate the I/O system should stop processing the Irp completion.
2242 return STATUS_MORE_PROCESSING_REQUIRED
;
2244 } // end ClassAsynchronousCompletion()
2246 VOID NTAPI
ServiceTransferRequest(PDEVICE_OBJECT Fdo
, PIRP Irp
)
2248 //PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
2249 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
2250 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
2251 //PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
2252 PIO_STACK_LOCATION currentSp
= IoGetCurrentIrpStackLocation(Irp
);
2253 ULONG entireXferLen
= currentSp
->Parameters
.Read
.Length
;
2254 PUCHAR bufPtr
= MmGetMdlVirtualAddress(Irp
->MdlAddress
);
2255 LARGE_INTEGER targetLocation
= currentSp
->Parameters
.Read
.ByteOffset
;
2256 PTRANSFER_PACKET pkt
;
2257 SINGLE_LIST_ENTRY pktList
;
2258 PSINGLE_LIST_ENTRY slistEntry
;
2264 * Compute the number of hw xfers we'll have to do.
2265 * Calculate this without allowing for an overflow condition.
2267 ASSERT(fdoData
->HwMaxXferLen
>= PAGE_SIZE
);
2268 numPackets
= entireXferLen
/fdoData
->HwMaxXferLen
;
2269 if (entireXferLen
% fdoData
->HwMaxXferLen
){
2274 * First get all the TRANSFER_PACKETs that we'll need at once.
2275 * Use our 'simple' slist functions since we don't need interlocked.
2277 SimpleInitSlistHdr(&pktList
);
2278 for (i
= 0; i
< numPackets
; i
++){
2279 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
2281 SimplePushSlist(&pktList
, &pkt
->SlistEntry
);
2288 if (i
== numPackets
){
2290 * Initialize the original IRP's status to success.
2291 * If any of the packets fail, they will set it to an error status.
2292 * The IoStatus.Information field will be incremented to the
2293 * transfer length as the pieces complete.
2295 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2296 Irp
->IoStatus
.Information
= 0;
2299 * Store the number of transfer pieces inside the original IRP.
2300 * It will be used to count down the pieces as they complete.
2302 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(numPackets
);
2305 * We are proceeding with the transfer.
2306 * Mark the client IRP pending since it may complete on a different thread.
2308 IoMarkIrpPending(Irp
);
2311 * Transmit the pieces of the transfer.
2313 while (entireXferLen
> 0){
2314 ULONG thisPieceLen
= MIN(fdoData
->HwMaxXferLen
, entireXferLen
);
2317 * Set up a TRANSFER_PACKET for this piece and send it.
2319 slistEntry
= SimplePopSlist(&pktList
);
2321 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2322 SetupReadWriteTransferPacket( pkt
,
2327 SubmitTransferPacket(pkt
);
2329 entireXferLen
-= thisPieceLen
;
2330 bufPtr
+= thisPieceLen
;
2331 targetLocation
.QuadPart
+= thisPieceLen
;
2333 ASSERT(SimpleIsSlistEmpty(&pktList
));
2337 * We were unable to get all the TRANSFER_PACKETs we need,
2338 * but we did get at least one.
2339 * That means that we are in extreme low-memory stress.
2340 * We'll try doing this transfer using a single packet.
2341 * The port driver is certainly also in stress, so use one-page
2346 * Free all but one of the TRANSFER_PACKETs.
2349 slistEntry
= SimplePopSlist(&pktList
);
2351 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2352 EnqueueFreeTransferPacket(Fdo
, pkt
);
2356 * Get the single TRANSFER_PACKET that we'll be using.
2358 slistEntry
= SimplePopSlist(&pktList
);
2360 ASSERT(SimpleIsSlistEmpty(&pktList
));
2361 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2362 DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt
));
2365 * Set default status and the number of transfer packets (one)
2366 * inside the original irp.
2368 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2369 Irp
->IoStatus
.Information
= 0;
2370 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
2373 * Mark the client irp pending since it may complete on
2376 IoMarkIrpPending(Irp
);
2379 * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
2381 SetupReadWriteTransferPacket( pkt
,
2386 InitLowMemRetry(pkt
, bufPtr
, entireXferLen
, targetLocation
);
2387 StepLowMemRetry(pkt
);
2391 * We were unable to get ANY TRANSFER_PACKETs.
2392 * Defer this client irp until some TRANSFER_PACKETs free up.
2394 DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp
));
2395 IoMarkIrpPending(Irp
);
2396 EnqueueDeferredClientIrp(fdoData
, Irp
);
2401 /*++////////////////////////////////////////////////////////////////////////////
2405 Routine Description:
2407 This routine executes when the port driver has completed a request.
2408 It looks at the SRB status in the completing SRB and if not success
2409 it checks for valid request sense buffer information. If valid, the
2410 info is used to update status with more precise message of type of
2411 error. This routine deallocates the SRB.
2413 This routine should only be placed on the stack location for a class
2418 Fdo - Supplies the device object which represents the logical
2421 Irp - Supplies the Irp which has completed.
2423 Context - Supplies a pointer to the SRB.
2433 IN PDEVICE_OBJECT Fdo
,
2438 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2439 PSCSI_REQUEST_BLOCK srb
= Context
;
2440 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2441 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2444 BOOLEAN callStartNextPacket
;
2446 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2449 // Check SRB status for success of completing request.
2452 if (SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2453 ULONG retryInterval
;
2455 DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp
, srb
));
2458 // Release the queue if it is frozen.
2461 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2462 ClassReleaseQueue(Fdo
);
2465 retry
= ClassInterpretSenseInfo(
2468 irpStack
->MajorFunction
,
2469 irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
?
2470 irpStack
->Parameters
.DeviceIoControl
.IoControlCode
:
2473 ((ULONG
)(ULONG_PTR
)irpStack
->Parameters
.Others
.Argument4
),
2478 // If the status is verified required and the this request
2479 // should bypass verify required then retry the request.
2482 if (TEST_FLAG(irpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
) &&
2483 status
== STATUS_VERIFY_REQUIRED
) {
2485 status
= STATUS_IO_DEVICE_ERROR
;
2489 if (retry
&& ((*(PCHAR
*)&irpStack
->Parameters
.Others
.Argument4
)--)) {
2495 DebugPrint((1, "Retry request %p\n", Irp
));
2497 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2498 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2501 RetryRequest(Fdo
, Irp
, srb
, FALSE
, retryInterval
);
2502 return STATUS_MORE_PROCESSING_REQUIRED
;
2508 // Set status for successful request
2510 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2511 ClasspPerfIncrementSuccessfulIo(fdoExtension
);
2512 status
= STATUS_SUCCESS
;
2513 } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
2517 // ensure we have returned some info, and it matches what the
2518 // original request wanted for PAGING operations only
2521 if ((NT_SUCCESS(status
)) && TEST_FLAG(Irp
->Flags
, IRP_PAGING_IO
)) {
2522 ASSERT(Irp
->IoStatus
.Information
!= 0);
2523 ASSERT(irpStack
->Parameters
.Read
.Length
== Irp
->IoStatus
.Information
);
2527 // remember if the caller wanted to skip calling IoStartNextPacket.
2528 // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
2529 // calls. this setting only affects device objects with StartIo routines.
2532 callStartNextPacket
= !TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_DONT_START_NEXT_PACKET
);
2533 if (irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
) {
2534 callStartNextPacket
= FALSE
;
2541 if(!TEST_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_PERSISTANT
)) {
2543 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2544 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2547 if (fdoExtension
->CommonExtension
.IsSrbLookasideListInitialized
){
2548 ClassFreeOrReuseSrb(fdoExtension
, srb
);
2551 DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
2557 DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
2558 "SRB_CLASS_FLAGS_PERSISTANT set\n", srb
));
2559 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2560 DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
2561 " because SRB_CLASS_FLAGS_PERSISTANT set\n",
2562 srb
->SenseInfoBuffer
));
2568 // Set status in completing IRP.
2571 Irp
->IoStatus
.Status
= status
;
2574 // Set the hard error if necessary.
2577 if (!NT_SUCCESS(status
) &&
2578 IoIsErrorUserInduced(status
) &&
2579 (Irp
->Tail
.Overlay
.Thread
!= NULL
)
2583 // Store DeviceObject for filesystem, and clear
2584 // in IoStatus.Information field.
2587 IoSetHardErrorOrVerifyDevice(Irp
, Fdo
);
2588 Irp
->IoStatus
.Information
= 0;
2592 // If pending has be returned for this irp then mark the current stack as
2596 if (Irp
->PendingReturned
) {
2597 IoMarkIrpPending(Irp
);
2600 if (fdoExtension
->CommonExtension
.DriverExtension
->InitData
.ClassStartIo
) {
2601 if (callStartNextPacket
) {
2603 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
2604 IoStartNextPacket(Fdo
, FALSE
);
2605 KeLowerIrql(oldIrql
);
2609 ClassReleaseRemoveLock(Fdo
, Irp
);
2613 } // end ClassIoComplete()
2615 /*++////////////////////////////////////////////////////////////////////////////
2617 ClassSendSrbSynchronous()
2619 Routine Description:
2621 This routine is called by SCSI device controls to complete an
2622 SRB and send it to the port driver synchronously (ie wait for
2623 completion). The CDB is already completed along with the SRB CDB
2624 size and request timeout value.
2628 Fdo - Supplies the functional device object which represents the target.
2630 Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
2632 BufferAddress - Supplies the address of the buffer.
2634 BufferLength - Supplies the length in bytes of the buffer.
2636 WriteToDevice - Indicates the data should be transfer to the device.
2640 NTSTATUS indicating the final results of the operation.
2642 If NT_SUCCESS(), then the amount of usable data is contained in the field
2643 Srb->DataTransferLength
2648 ClassSendSrbSynchronous(
2650 PSCSI_REQUEST_BLOCK Srb
,
2651 PVOID BufferAddress
,
2653 BOOLEAN WriteToDevice
2657 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2658 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2659 IO_STATUS_BLOCK ioStatus
;
2660 //ULONG controlType;
2662 PIO_STACK_LOCATION irpStack
;
2664 PUCHAR senseInfoBuffer
;
2665 ULONG retryCount
= MAXIMUM_RETRIES
;
2670 // NOTE: This code is only pageable because we are not freezing
2671 // the queue. Allowing the queue to be frozen from a pageable
2672 // routine could leave the queue frozen as we try to page in
2673 // the code to unfreeze the queue. The result would be a nice
2674 // case of deadlock. Therefore, since we are unfreezing the
2675 // queue regardless of the result, just set the NO_FREEZE_QUEUE
2679 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2680 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2683 // Write length to SRB.
2686 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2689 // Set SCSI bus address.
2692 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2695 // Enable auto request sense.
2698 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
2701 // Sense buffer is in aligned nonpaged pool.
2704 senseInfoBuffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
2708 if (senseInfoBuffer
== NULL
) {
2710 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
2712 return(STATUS_INSUFFICIENT_RESOURCES
);
2715 Srb
->SenseInfoBuffer
= senseInfoBuffer
;
2716 Srb
->DataBuffer
= BufferAddress
;
2719 // Start retries here.
2725 // use fdoextension's flags by default.
2726 // do not move out of loop, as the flag may change due to errors
2727 // sending this command.
2730 Srb
->SrbFlags
= fdoExtension
->SrbFlags
;
2732 if(BufferAddress
!= NULL
) {
2734 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_OUT
);
2736 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
2741 // Initialize the QueueAction field.
2744 Srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
2747 // Disable synchronous transfer for these requests.
2748 // Disable freezing the queue, since all we do is unfreeze it anyways.
2751 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
2752 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
2755 // Set the event object to the unsignaled state.
2756 // It will be used to signal request completion.
2759 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
2762 // Build device I/O control request with METHOD_NEITHER data transfer.
2763 // We'll queue a completion routine to cleanup the MDL's and such ourself.
2766 irp
= IoAllocateIrp(
2767 (CCHAR
) (fdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
+ 1),
2771 ExFreePool(senseInfoBuffer
);
2772 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
2773 return(STATUS_INSUFFICIENT_RESOURCES
);
2777 // Get next stack location.
2780 irpStack
= IoGetNextIrpStackLocation(irp
);
2783 // Set up SRB for execute scsi request. Save SRB address in next stack
2784 // for the port driver.
2787 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2788 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
2790 IoSetCompletionRoutine(irp
,
2791 ClasspSendSynchronousCompletion
,
2797 irp
->UserIosb
= &ioStatus
;
2798 irp
->UserEvent
= &event
;
2802 // Build an MDL for the data buffer and stick it into the irp. The
2803 // completion routine will unlock the pages and free the MDL.
2806 irp
->MdlAddress
= IoAllocateMdl( BufferAddress
,
2811 if (irp
->MdlAddress
== NULL
) {
2812 ExFreePool(senseInfoBuffer
);
2813 Srb
->SenseInfoBuffer
= NULL
;
2815 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
2816 return STATUS_INSUFFICIENT_RESOURCES
;
2822 // the io manager unlocks these pages upon completion
2825 MmProbeAndLockPages( irp
->MdlAddress
,
2827 (WriteToDevice
? IoReadAccess
:
2830 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
2831 status
= _SEH2_GetExceptionCode();
2833 ExFreePool(senseInfoBuffer
);
2834 Srb
->SenseInfoBuffer
= NULL
;
2835 IoFreeMdl(irp
->MdlAddress
);
2838 DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
2839 "locking buffer\n", status
));
2840 _SEH2_YIELD(return status
);
2845 // Set the transfer length.
2848 Srb
->DataTransferLength
= BufferLength
;
2854 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
2858 // Set up IRP Address.
2861 Srb
->OriginalRequest
= irp
;
2864 // Call the port driver with the request and wait for it to complete.
2867 status
= IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2869 if (status
== STATUS_PENDING
) {
2870 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
2871 status
= ioStatus
.Status
;
2875 // Check that request completed without error.
2878 if (SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2880 ULONG retryInterval
;
2882 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
)));
2885 // assert that the queue is not frozen
2888 ASSERT(!TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
2891 // Update status and determine if request should be retried.
2894 retry
= ClassInterpretSenseInfo(Fdo
,
2898 MAXIMUM_RETRIES
- retryCount
,
2905 if ((status
== STATUS_DEVICE_NOT_READY
&&
2906 ((PSENSE_DATA
) senseInfoBuffer
)->AdditionalSenseCode
==
2907 SCSI_ADSENSE_LUN_NOT_READY
) ||
2908 (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)) {
2910 LARGE_INTEGER delay
;
2913 // Delay for at least 2 seconds.
2916 if(retryInterval
< 2) {
2920 delay
.QuadPart
= (LONGLONG
)( - 10 * 1000 * (LONGLONG
)1000 * retryInterval
);
2923 // Stall for a while to let the device become ready
2926 KeDelayExecutionThread(KernelMode
, FALSE
, &delay
);
2931 // If retries are not exhausted then retry this operation.
2936 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2937 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2945 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2946 status
= STATUS_SUCCESS
;
2950 // required even though we allocated our own, since the port driver may
2951 // have allocated one also
2954 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2955 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2958 Srb
->SenseInfoBuffer
= NULL
;
2959 ExFreePool(senseInfoBuffer
);
2964 /*++////////////////////////////////////////////////////////////////////////////
2966 ClassInterpretSenseInfo()
2968 Routine Description:
2970 This routine interprets the data returned from the SCSI
2971 request sense. It determines the status to return in the
2972 IRP and whether this request can be retried.
2976 DeviceObject - Supplies the device object associated with this request.
2978 Srb - Supplies the scsi request block which failed.
2980 MajorFunctionCode - Supplies the function code to be used for logging.
2982 IoDeviceCode - Supplies the device code to be used for logging.
2984 Status - Returns the status for the request.
2988 BOOLEAN TRUE: Drivers should retry this request.
2989 FALSE: Drivers should not retry this request.
2994 ClassInterpretSenseInfo(
2995 IN PDEVICE_OBJECT Fdo
,
2996 IN PSCSI_REQUEST_BLOCK Srb
,
2997 IN UCHAR MajorFunctionCode
,
2998 IN ULONG IoDeviceCode
,
2999 IN ULONG RetryCount
,
3000 OUT NTSTATUS
*Status
,
3001 OUT OPTIONAL ULONG
*RetryInterval
3004 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
3005 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
3007 PSENSE_DATA senseBuffer
= Srb
->SenseInfoBuffer
;
3009 BOOLEAN retry
= TRUE
;
3010 BOOLEAN logError
= FALSE
;
3011 BOOLEAN unhandledError
= FALSE
;
3012 BOOLEAN incrementErrorCount
= FALSE
;
3014 ULONG badSector
= 0;
3022 ULONG retryInterval
= 0;
3028 if(TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3031 // Log anything remotely incorrect about paging i/o
3036 logStatus
= IO_WARNING_PAGING_FAILURE
;
3040 // Check that request sense buffer is valid.
3043 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
3047 // must handle the SRB_STATUS_INTERNAL_ERROR case first,
3048 // as it has all the flags set.
3051 if (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_INTERNAL_ERROR
) {
3053 DebugPrint((ClassDebugSenseInfo
,
3054 "ClassInterpretSenseInfo: Internal Error code is %x\n",
3055 Srb
->InternalStatus
));
3058 *Status
= Srb
->InternalStatus
;
3060 } else if ((Srb
->SrbStatus
& SRB_STATUS_AUTOSENSE_VALID
) &&
3061 (Srb
->SenseInfoBufferLength
>=
3062 offsetof(SENSE_DATA
, CommandSpecificInformation
))) {
3065 // Zero the additional sense code and additional sense code qualifier
3066 // if they were not returned by the device.
3069 readSector
= senseBuffer
->AdditionalSenseLength
+
3070 offsetof(SENSE_DATA
, AdditionalSenseLength
);
3072 if (readSector
> Srb
->SenseInfoBufferLength
) {
3073 readSector
= Srb
->SenseInfoBufferLength
;
3076 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCode
)) {
3077 senseBuffer
->AdditionalSenseCode
= 0;
3080 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCodeQualifier
)) {
3081 senseBuffer
->AdditionalSenseCodeQualifier
= 0;
3084 DebugPrint((ClassDebugSenseInfo
,
3085 "ClassInterpretSenseInfo: Error code is %x\n",
3086 senseBuffer
->ErrorCode
));
3087 DebugPrint((ClassDebugSenseInfo
,
3088 "ClassInterpretSenseInfo: Sense key is %x\n",
3089 senseBuffer
->SenseKey
));
3090 DebugPrint((ClassDebugSenseInfo
,
3091 "ClassInterpretSenseInfo: Additional sense code is %x\n",
3092 senseBuffer
->AdditionalSenseCode
));
3093 DebugPrint((ClassDebugSenseInfo
,
3094 "ClassInterpretSenseInfo: Additional sense code qualifier "
3096 senseBuffer
->AdditionalSenseCodeQualifier
));
3099 switch (senseBuffer
->SenseKey
& 0xf) {
3101 case SCSI_SENSE_NOT_READY
: {
3103 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3104 "Device not ready\n"));
3105 *Status
= STATUS_DEVICE_NOT_READY
;
3107 switch (senseBuffer
->AdditionalSenseCode
) {
3109 case SCSI_ADSENSE_LUN_NOT_READY
: {
3111 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3112 "Lun not ready\n"));
3114 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3116 case SCSI_SENSEQ_OPERATION_IN_PROGRESS
: {
3117 DEVICE_EVENT_BECOMING_READY notReady
;
3119 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3120 "Operation In Progress\n"));
3121 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3123 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3124 notReady
.Version
= 1;
3125 notReady
.Reason
= 2;
3126 notReady
.Estimated100msToReady
= retryInterval
* 10;
3127 ClasspSendNotification(fdoExtension
,
3128 &GUID_IO_DEVICE_BECOMING_READY
,
3129 sizeof(DEVICE_EVENT_BECOMING_READY
),
3135 case SCSI_SENSEQ_BECOMING_READY
: {
3136 DEVICE_EVENT_BECOMING_READY notReady
;
3138 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3139 "In process of becoming ready\n"));
3140 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3142 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3143 notReady
.Version
= 1;
3144 notReady
.Reason
= 1;
3145 notReady
.Estimated100msToReady
= retryInterval
* 10;
3146 ClasspSendNotification(fdoExtension
,
3147 &GUID_IO_DEVICE_BECOMING_READY
,
3148 sizeof(DEVICE_EVENT_BECOMING_READY
),
3153 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS
: {
3154 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3155 "Long write in progress\n"));
3160 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED
: {
3161 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3162 "Manual intervention required\n"));
3163 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3168 case SCSI_SENSEQ_FORMAT_IN_PROGRESS
: {
3169 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3170 "Format in progress\n"));
3175 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE
: {
3177 if(!TEST_FLAG(fdoExtension
->ScanForSpecialFlags
,
3178 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
)) {
3180 DebugPrint((ClassDebugSenseInfo
,
3181 "ClassInterpretSenseInfo: "
3182 "not ready, cause unknown\n"));
3184 Many non-WHQL certified drives (mostly CD-RW) return
3185 this when they have no media instead of the obvious
3188 SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
3190 These drives should not pass WHQL certification due
3191 to this discrepancy.
3200 // Treat this as init command required and fall through.
3205 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED
:
3207 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3208 "Initializing command required\n"));
3211 // This sense code/additional sense code
3212 // combination may indicate that the device
3213 // needs to be started. Send an start unit if this
3214 // is a disk device.
3217 if(TEST_FLAG(fdoExtension
->DeviceFlags
,
3218 DEV_SAFE_START_UNIT
) &&
3219 !TEST_FLAG(Srb
->SrbFlags
,
3220 SRB_CLASS_FLAGS_LOW_PRIORITY
)) {
3221 ClassSendStartUnit(Fdo
);
3227 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
3231 case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
: {
3232 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3233 "No Media in device.\n"));
3234 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3238 // signal MCN that there isn't any media in the device
3240 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3241 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3242 "No Media in a non-removable device %p\n",
3245 ClassSetMediaChangeState(fdoExtension
, MediaNotPresent
, FALSE
);
3249 } // end switch (senseBuffer->AdditionalSenseCode)
3252 } // end SCSI_SENSE_NOT_READY
3254 case SCSI_SENSE_DATA_PROTECT
: {
3255 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3256 "Media write protected\n"));
3257 *Status
= STATUS_MEDIA_WRITE_PROTECTED
;
3260 } // end SCSI_SENSE_DATA_PROTECT
3262 case SCSI_SENSE_MEDIUM_ERROR
: {
3263 DebugPrint((ClassDebugSenseInfo
,"ClassInterpretSenseInfo: "
3264 "Medium Error (bad block)\n"));
3265 *Status
= STATUS_DEVICE_DATA_ERROR
;
3270 logStatus
= IO_ERR_BAD_BLOCK
;
3273 // Check if this error is due to unknown format
3275 if (senseBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_INVALID_MEDIA
){
3277 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3279 case SCSI_SENSEQ_UNKNOWN_FORMAT
: {
3281 *Status
= STATUS_UNRECOGNIZED_MEDIA
;
3284 // Log error only if this is a paging request
3286 if(!TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3292 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED
: {
3294 *Status
= STATUS_CLEANER_CARTRIDGE_INSTALLED
;
3302 } // end switch AdditionalSenseCodeQualifier
3304 } // end SCSI_ADSENSE_INVALID_MEDIA
3308 } // end SCSI_SENSE_MEDIUM_ERROR
3310 case SCSI_SENSE_HARDWARE_ERROR
: {
3311 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3312 "Hardware error\n"));
3313 *Status
= STATUS_IO_DEVICE_ERROR
;
3316 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3318 } // end SCSI_SENSE_HARDWARE_ERROR
3320 case SCSI_SENSE_ILLEGAL_REQUEST
: {
3322 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3323 "Illegal SCSI request\n"));
3324 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3327 switch (senseBuffer
->AdditionalSenseCode
) {
3329 case SCSI_ADSENSE_ILLEGAL_COMMAND
: {
3330 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3331 "Illegal command\n"));
3335 case SCSI_ADSENSE_ILLEGAL_BLOCK
: {
3336 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3337 "Illegal block address\n"));
3338 *Status
= STATUS_NONEXISTENT_SECTOR
;
3342 case SCSI_ADSENSE_INVALID_LUN
: {
3343 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3345 *Status
= STATUS_NO_SUCH_DEVICE
;
3349 case SCSI_ADSENSE_MUSIC_AREA
: {
3350 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3355 case SCSI_ADSENSE_DATA_AREA
: {
3356 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3361 case SCSI_ADSENSE_VOLUME_OVERFLOW
: {
3362 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3363 "Volume overflow\n"));
3367 case SCSI_ADSENSE_COPY_PROTECTION_FAILURE
: {
3368 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3369 "Copy protection failure\n"));
3371 *Status
= STATUS_COPY_PROTECTION_FAILURE
;
3373 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3374 case SCSI_SENSEQ_AUTHENTICATION_FAILURE
:
3375 DebugPrint((ClassDebugSenseInfo
,
3376 "ClassInterpretSenseInfo: "
3377 "Authentication failure\n"));
3378 *Status
= STATUS_CSS_AUTHENTICATION_FAILURE
;
3380 case SCSI_SENSEQ_KEY_NOT_PRESENT
:
3381 DebugPrint((ClassDebugSenseInfo
,
3382 "ClassInterpretSenseInfo: "
3383 "Key not present\n"));
3384 *Status
= STATUS_CSS_KEY_NOT_PRESENT
;
3386 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED
:
3387 DebugPrint((ClassDebugSenseInfo
,
3388 "ClassInterpretSenseInfo: "
3389 "Key not established\n"));
3390 *Status
= STATUS_CSS_KEY_NOT_ESTABLISHED
;
3392 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION
:
3393 DebugPrint((ClassDebugSenseInfo
,
3394 "ClassInterpretSenseInfo: "
3395 "Read of scrambled sector w/o "
3396 "authentication\n"));
3397 *Status
= STATUS_CSS_SCRAMBLED_SECTOR
;
3399 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT
:
3400 DebugPrint((ClassDebugSenseInfo
,
3401 "ClassInterpretSenseInfo: "
3402 "Media region does not logical unit "
3404 *Status
= STATUS_CSS_REGION_MISMATCH
;
3406 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR
:
3407 DebugPrint((ClassDebugSenseInfo
,
3408 "ClassInterpretSenseInfo: "
3409 "Region set error -- region may "
3411 *Status
= STATUS_CSS_RESETS_EXHAUSTED
;
3413 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
3419 case SCSI_ADSENSE_INVALID_CDB
: {
3420 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3424 // Note: the retry interval is not typically used.
3425 // it is set here only because a ClassErrorHandler
3426 // cannot set the retryInterval, and the error may
3427 // require a few commands to be sent to clear whatever
3428 // caused this condition (i.e. disk clears the write
3429 // cache, requiring at least two commands)
3431 // hopefully, this shortcoming can be changed for
3439 } // end switch (senseBuffer->AdditionalSenseCode)
3442 } // end SCSI_SENSE_ILLEGAL_REQUEST
3444 case SCSI_SENSE_UNIT_ATTENTION
: {
3450 // A media change may have occured so increment the change
3451 // count for the physical device
3454 count
= InterlockedIncrement((PLONG
)&fdoExtension
->MediaChangeCount
);
3455 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3456 "Media change count for device %d incremented to %#lx\n",
3457 fdoExtension
->DeviceNumber
, count
));
3460 switch (senseBuffer
->AdditionalSenseCode
) {
3461 case SCSI_ADSENSE_MEDIUM_CHANGED
: {
3462 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3463 "Media changed\n"));
3465 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3466 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3467 "Media Changed on non-removable device %p\n",
3470 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
3474 case SCSI_ADSENSE_BUS_RESET
: {
3475 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3480 case SCSI_ADSENSE_OPERATOR_REQUEST
: {
3481 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3483 case SCSI_SENSEQ_MEDIUM_REMOVAL
: {
3484 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3485 "Ejection request received!\n"));
3486 ClassSendEjectionNotification(fdoExtension
);
3490 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE
: {
3491 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3492 "Operator selected write permit?! "
3493 "(unsupported!)\n"));
3497 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE
: {
3498 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3499 "Operator selected write protect?! "
3500 "(unsupported!)\n"));
3509 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3510 "Unit attention\n"));
3514 } // end switch (senseBuffer->AdditionalSenseCode)
3516 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
))
3519 // TODO : Is the media lockable?
3522 if ((ClassGetVpb(Fdo
) != NULL
) && (ClassGetVpb(Fdo
)->Flags
& VPB_MOUNTED
))
3525 // Set bit to indicate that media may have changed
3526 // and volume needs verification.
3529 SET_FLAG(Fdo
->Flags
, DO_VERIFY_VOLUME
);
3531 *Status
= STATUS_VERIFY_REQUIRED
;
3537 *Status
= STATUS_IO_DEVICE_ERROR
;
3542 } // end SCSI_SENSE_UNIT_ATTENTION
3544 case SCSI_SENSE_ABORTED_COMMAND
: {
3545 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3546 "Command aborted\n"));
3547 *Status
= STATUS_IO_DEVICE_ERROR
;
3550 } // end SCSI_SENSE_ABORTED_COMMAND
3552 case SCSI_SENSE_BLANK_CHECK
: {
3553 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3554 "Media blank check\n"));
3556 *Status
= STATUS_NO_DATA_DETECTED
;
3558 } // end SCSI_SENSE_BLANK_CHECK
3560 case SCSI_SENSE_RECOVERED_ERROR
: {
3562 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3563 "Recovered error\n"));
3564 *Status
= STATUS_SUCCESS
;
3569 switch(senseBuffer
->AdditionalSenseCode
) {
3570 case SCSI_ADSENSE_SEEK_ERROR
:
3571 case SCSI_ADSENSE_TRACK_ERROR
: {
3572 logStatus
= IO_ERR_SEEK_ERROR
;
3576 case SCSI_ADSENSE_REC_DATA_NOECC
:
3577 case SCSI_ADSENSE_REC_DATA_ECC
: {
3578 logStatus
= IO_RECOVERED_VIA_ECC
;
3582 case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED
: {
3583 UCHAR wmiEventData
[5];
3585 *((PULONG
)wmiEventData
) = sizeof(UCHAR
);
3586 wmiEventData
[sizeof(ULONG
)] = senseBuffer
->AdditionalSenseCodeQualifier
;
3589 // Don't log another eventlog if we have already logged once
3590 // NOTE: this should have been interlocked, but the structure
3591 // was publicly defined to use a BOOLEAN (char). Since
3592 // media only reports these errors once per X minutes,
3593 // the potential race condition is nearly non-existant.
3594 // the worst case is duplicate log entries, so ignore.
3597 if (fdoExtension
->FailurePredicted
== 0) {
3600 fdoExtension
->FailurePredicted
= TRUE
;
3601 fdoExtension
->FailureReason
= senseBuffer
->AdditionalSenseCodeQualifier
;
3602 logStatus
= IO_WRN_FAILURE_PREDICTED
;
3604 ClassNotifyFailurePredicted(fdoExtension
,
3605 (PUCHAR
)&wmiEventData
,
3606 sizeof(wmiEventData
),
3616 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3620 } // end switch(senseBuffer->AdditionalSenseCode)
3622 if (senseBuffer
->IncorrectLength
) {
3624 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3625 "Incorrect length detected.\n"));
3626 *Status
= STATUS_INVALID_BLOCK_LENGTH
;
3630 } // end SCSI_SENSE_RECOVERED_ERROR
3632 case SCSI_SENSE_NO_SENSE
: {
3635 // Check other indicators.
3638 if (senseBuffer
->IncorrectLength
) {
3640 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3641 "Incorrect length detected.\n"));
3642 *Status
= STATUS_INVALID_BLOCK_LENGTH
;
3647 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3648 "No specific sense key\n"));
3649 *Status
= STATUS_IO_DEVICE_ERROR
;
3654 } // end SCSI_SENSE_NO_SENSE
3657 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3658 "Unrecognized sense code\n"));
3659 *Status
= STATUS_IO_DEVICE_ERROR
;
3663 } // end switch (senseBuffer->SenseKey & 0xf)
3666 // Try to determine the bad sector from the inquiry data.
3669 if ((((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_READ
||
3670 ((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_VERIFY
||
3671 ((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_WRITE
)) {
3673 for (index
= 0; index
< 4; index
++) {
3674 badSector
= (badSector
<< 8) | senseBuffer
->Information
[index
];
3678 for (index
= 0; index
< 4; index
++) {
3679 readSector
= (readSector
<< 8) | Srb
->Cdb
[index
+2];
3682 index
= (((PCDB
)Srb
->Cdb
)->CDB10
.TransferBlocksMsb
<< 8) |
3683 ((PCDB
)Srb
->Cdb
)->CDB10
.TransferBlocksLsb
;
3686 // Make sure the bad sector is within the read sectors.
3689 if (!(badSector
>= readSector
&& badSector
< readSector
+ index
)) {
3690 badSector
= readSector
;
3697 // Request sense buffer not valid. No sense information
3698 // to pinpoint the error. Return general request fail.
3701 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3702 "Request sense info not valid. SrbStatus %2x\n",
3703 SRB_STATUS(Srb
->SrbStatus
)));
3706 switch (SRB_STATUS(Srb
->SrbStatus
)) {
3707 case SRB_STATUS_INVALID_LUN
:
3708 case SRB_STATUS_INVALID_TARGET_ID
:
3709 case SRB_STATUS_NO_DEVICE
:
3710 case SRB_STATUS_NO_HBA
:
3711 case SRB_STATUS_INVALID_PATH_ID
: {
3712 *Status
= STATUS_NO_SUCH_DEVICE
;
3717 case SRB_STATUS_COMMAND_TIMEOUT
:
3718 case SRB_STATUS_TIMEOUT
: {
3721 // Update the error count for the device.
3724 incrementErrorCount
= TRUE
;
3725 *Status
= STATUS_IO_TIMEOUT
;
3729 case SRB_STATUS_ABORTED
: {
3732 // Update the error count for the device.
3735 incrementErrorCount
= TRUE
;
3736 *Status
= STATUS_IO_TIMEOUT
;
3742 case SRB_STATUS_SELECTION_TIMEOUT
: {
3744 logStatus
= IO_ERR_NOT_READY
;
3746 *Status
= STATUS_DEVICE_NOT_CONNECTED
;
3751 case SRB_STATUS_DATA_OVERRUN
: {
3752 *Status
= STATUS_DATA_OVERRUN
;
3757 case SRB_STATUS_PHASE_SEQUENCE_FAILURE
: {
3760 // Update the error count for the device.
3763 incrementErrorCount
= TRUE
;
3764 *Status
= STATUS_IO_DEVICE_ERROR
;
3767 // If there was phase sequence error then limit the number of
3771 if (RetryCount
> 1 ) {
3778 case SRB_STATUS_REQUEST_FLUSHED
: {
3781 // If the status needs verification bit is set. Then set
3782 // the status to need verification and no retry; otherwise,
3783 // just retry the request.
3786 if (TEST_FLAG(Fdo
->Flags
, DO_VERIFY_VOLUME
)) {
3788 *Status
= STATUS_VERIFY_REQUIRED
;
3792 *Status
= STATUS_IO_DEVICE_ERROR
;
3798 case SRB_STATUS_INVALID_REQUEST
: {
3799 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3804 case SRB_STATUS_UNEXPECTED_BUS_FREE
:
3805 case SRB_STATUS_PARITY_ERROR
:
3808 // Update the error count for the device
3809 // and fall through to below
3812 incrementErrorCount
= TRUE
;
3814 case SRB_STATUS_BUS_RESET
: {
3815 *Status
= STATUS_IO_DEVICE_ERROR
;
3819 case SRB_STATUS_ERROR
: {
3821 *Status
= STATUS_IO_DEVICE_ERROR
;
3822 if (Srb
->ScsiStatus
== 0) {
3825 // This is some strange return code. Update the error
3826 // count for the device.
3829 incrementErrorCount
= TRUE
;
3831 } if (Srb
->ScsiStatus
== SCSISTAT_BUSY
) {
3833 *Status
= STATUS_DEVICE_NOT_READY
;
3835 } if (Srb
->ScsiStatus
== SCSISTAT_RESERVATION_CONFLICT
) {
3837 *Status
= STATUS_DEVICE_BUSY
;
3848 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3850 *Status
= STATUS_IO_DEVICE_ERROR
;
3851 unhandledError
= TRUE
;
3858 // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
3859 // we know from a previous poll when the device will be ready (ETA)
3860 // we should delay the retry more appropriately than just guessing.
3863 if (fdoExtension->MediaChangeDetectionInfo &&
3864 fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
3865 TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
3866 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
3868 // check if Gesn.ReadyTime if greater than current tick count
3869 // if so, delay that long (from 1 to 30 seconds max?)
3870 // else, leave the guess of time alone.
3876 if (incrementErrorCount
) {
3879 // if any error count occurred, delay the retry of this io by
3880 // at least one second, if caller supports it.
3883 if (retryInterval
== 0) {
3886 ClasspPerfIncrementErrorCount(fdoExtension
);
3890 // If there is a class specific error handler call it.
3893 if (fdoExtension
->CommonExtension
.DevInfo
->ClassError
!= NULL
) {
3895 fdoExtension
->CommonExtension
.DevInfo
->ClassError(Fdo
,
3902 // If the caller wants to know the suggested retry interval tell them.
3905 if(ARGUMENT_PRESENT(RetryInterval
)) {
3906 *RetryInterval
= retryInterval
;
3912 * Always log the error in our internal log.
3913 * If logError is set, also log the error in the system log.
3917 ULONG senseBufferSize
= 0;
3918 IO_ERROR_LOG_PACKET staticErrLogEntry
= {0};
3919 CLASS_ERROR_LOG_DATA staticErrLogData
= { { { 0 } } };
3922 // Calculate the total size of the error log entry.
3923 // add to totalSize in the order that they are used.
3924 // the advantage to calculating all the sizes here is
3925 // that we don't have to do a bunch of extraneous checks
3926 // later on in this code path.
3928 totalSize
= sizeof(IO_ERROR_LOG_PACKET
) // required
3929 - sizeof(ULONG
) // struct includes one ULONG
3930 + sizeof(CLASS_ERROR_LOG_DATA
);// struct for ease
3933 // also save any available extra sense data, up to the maximum errlog
3934 // packet size . WMI should be used for real-time analysis.
3935 // the event log should only be used for post-mortem debugging.
3937 if (TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_AUTOSENSE_VALID
)) {
3938 ULONG validSenseBytes
;
3942 // make sure we can at least access the AdditionalSenseLength field
3944 validSense
= RTL_CONTAINS_FIELD(senseBuffer
,
3945 Srb
->SenseInfoBufferLength
,
3946 AdditionalSenseLength
);
3950 // if extra info exists, copy the maximum amount of available
3951 // sense data that is safe into the the errlog.
3953 validSenseBytes
= senseBuffer
->AdditionalSenseLength
3954 + offsetof(SENSE_DATA
, AdditionalSenseLength
);
3957 // this is invalid because it causes overflow!
3958 // whoever sent this type of request would cause
3961 ASSERT(validSenseBytes
< MAX_ADDITIONAL_SENSE_BYTES
);
3964 // set to save the most sense buffer possible
3966 senseBufferSize
= max(validSenseBytes
, sizeof(SENSE_DATA
));
3967 senseBufferSize
= min(senseBufferSize
, Srb
->SenseInfoBufferLength
);
3970 // it's smaller than required to read the total number of
3971 // valid bytes, so just use the SenseInfoBufferLength field.
3973 senseBufferSize
= Srb
->SenseInfoBufferLength
;
3977 * Bump totalSize by the number of extra senseBuffer bytes
3978 * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
3979 * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
3981 if (senseBufferSize
> sizeof(SENSE_DATA
)){
3982 totalSize
+= senseBufferSize
-sizeof(SENSE_DATA
);
3983 if (totalSize
> ERROR_LOG_MAXIMUM_SIZE
){
3984 senseBufferSize
-= totalSize
-ERROR_LOG_MAXIMUM_SIZE
;
3985 totalSize
= ERROR_LOG_MAXIMUM_SIZE
;
3991 // If we've used up all of our retry attempts, set the final status to
3992 // reflect the appropriate result.
3994 if (retry
&& RetryCount
< MAXIMUM_RETRIES
) {
3995 staticErrLogEntry
.FinalStatus
= STATUS_SUCCESS
;
3996 staticErrLogData
.ErrorRetried
= TRUE
;
3998 staticErrLogEntry
.FinalStatus
= *Status
;
4000 if (TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
4001 staticErrLogData
.ErrorPaging
= TRUE
;
4003 if (unhandledError
) {
4004 staticErrLogData
.ErrorUnhandled
= TRUE
;
4008 // Calculate the device offset if there is a geometry.
4010 staticErrLogEntry
.DeviceOffset
.QuadPart
= (LONGLONG
)badSector
;
4011 staticErrLogEntry
.DeviceOffset
.QuadPart
*= (LONGLONG
)fdoExtension
->DiskGeometry
.BytesPerSector
;
4012 if (logStatus
== -1){
4013 staticErrLogEntry
.ErrorCode
= STATUS_IO_DEVICE_ERROR
;
4015 staticErrLogEntry
.ErrorCode
= logStatus
;
4019 * The dump data follows the IO_ERROR_LOG_PACKET,
4020 * with the first ULONG of dump data inside the packet.
4022 staticErrLogEntry
.DumpDataSize
= (USHORT
)totalSize
- sizeof(IO_ERROR_LOG_PACKET
) + sizeof(ULONG
);
4024 staticErrLogEntry
.SequenceNumber
= 0;
4025 staticErrLogEntry
.MajorFunctionCode
= MajorFunctionCode
;
4026 staticErrLogEntry
.IoControlCode
= IoDeviceCode
;
4027 staticErrLogEntry
.RetryCount
= (UCHAR
) RetryCount
;
4028 staticErrLogEntry
.UniqueErrorValue
= uniqueId
;
4030 KeQueryTickCount(&staticErrLogData
.TickCount
);
4031 staticErrLogData
.PortNumber
= (ULONG
)-1;
4034 * Save the entire contents of the SRB.
4036 staticErrLogData
.Srb
= *Srb
;
4039 * For our private log, save just the default length of the SENSE_DATA.
4041 if (senseBufferSize
!= 0){
4042 RtlCopyMemory(&staticErrLogData
.SenseData
, senseBuffer
, min(senseBufferSize
, sizeof(SENSE_DATA
)));
4046 * Save the error log in our context.
4047 * We only save the default sense buffer length.
4049 KeAcquireSpinLock(&fdoData
->SpinLock
, &oldIrql
);
4050 fdoData
->ErrorLogs
[fdoData
->ErrorLogNextIndex
] = staticErrLogData
;
4051 fdoData
->ErrorLogNextIndex
++;
4052 fdoData
->ErrorLogNextIndex
%= NUM_ERROR_LOG_ENTRIES
;
4053 KeReleaseSpinLock(&fdoData
->SpinLock
, oldIrql
);
4056 * If logError is set, also save this log in the system's error log.
4057 * But make sure we don't log TUR failures over and over
4058 * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
4060 if ((((PCDB
)Srb
->Cdb
)->CDB10
.OperationCode
== SCSIOP_TEST_UNIT_READY
) && logError
){
4061 if (fdoData
->LoggedTURFailureSinceLastIO
){
4065 fdoData
->LoggedTURFailureSinceLastIO
= TRUE
;
4069 PIO_ERROR_LOG_PACKET errorLogEntry
;
4070 PCLASS_ERROR_LOG_DATA errlogData
;
4072 errorLogEntry
= (PIO_ERROR_LOG_PACKET
)IoAllocateErrorLogEntry(Fdo
, (UCHAR
)totalSize
);
4074 errlogData
= (PCLASS_ERROR_LOG_DATA
)errorLogEntry
->DumpData
;
4076 *errorLogEntry
= staticErrLogEntry
;
4077 *errlogData
= staticErrLogData
;
4080 * For the system log, copy as much of the sense buffer as possible.
4082 if (senseBufferSize
!= 0) {
4083 RtlCopyMemory(&errlogData
->SenseData
, senseBuffer
, senseBufferSize
);
4087 * Write the error log packet to the system error logging thread.
4089 IoWriteErrorLogEntry(errorLogEntry
);
4096 } // end ClassInterpretSenseInfo()
4098 /*++////////////////////////////////////////////////////////////////////////////
4102 Routine Description:
4104 This routine sends a mode sense command to a target ID and returns
4105 when it is complete.
4109 Fdo - Supplies the functional device object associated with this request.
4111 ModeSenseBuffer - Supplies a buffer to store the sense data.
4113 Length - Supplies the length in bytes of the mode sense buffer.
4115 PageMode - Supplies the page or pages of mode sense data to be retrieved.
4119 Length of the transferred data is returned.
4122 ULONG NTAPI
ClassModeSense( IN PDEVICE_OBJECT Fdo
,
4123 IN PCHAR ModeSenseBuffer
,
4127 ULONG lengthTransferred
= 0;
4128 PMDL senseBufferMdl
;
4132 senseBufferMdl
= BuildDeviceInputMdl(ModeSenseBuffer
, Length
);
4133 if (senseBufferMdl
){
4135 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
4138 //NTSTATUS pktStatus;
4139 IRP pseudoIrp
= {0};
4142 * Store the number of packets servicing the irp (one)
4143 * inside the original IRP. It will be used to counted down
4144 * to zero when the packet completes.
4145 * Initialize the original IRP's status to success.
4146 * If the packet fails, we will set it to the error status.
4148 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
4149 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
4150 pseudoIrp
.IoStatus
.Information
= 0;
4151 pseudoIrp
.MdlAddress
= senseBufferMdl
;
4154 * Set this up as a SYNCHRONOUS transfer, submit it,
4155 * and wait for the packet to complete. The result
4156 * status will be written to the original irp.
4158 ASSERT(Length
<= 0x0ff);
4159 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
4160 SetupModeSenseTransferPacket(pkt
, &event
, ModeSenseBuffer
, (UCHAR
)Length
, PageMode
, &pseudoIrp
);
4161 SubmitTransferPacket(pkt
);
4162 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
4164 if (NT_SUCCESS(pseudoIrp
.IoStatus
.Status
)){
4165 lengthTransferred
= (ULONG
)pseudoIrp
.IoStatus
.Information
;
4169 * This request can sometimes fail legitimately
4170 * (e.g. when a SCSI device is attached but turned off)
4171 * so this is not necessarily a device/driver bug.
4173 DBGTRACE(ClassDebugWarning
, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo
, pseudoIrp
.IoStatus
.Status
));
4177 FreeDeviceInputMdl(senseBufferMdl
);
4180 return lengthTransferred
;
4183 /*++////////////////////////////////////////////////////////////////////////////
4187 Routine Description:
4189 This routine scans through the mode sense data and finds the requested
4190 mode sense page code.
4193 ModeSenseBuffer - Supplies a pointer to the mode sense data.
4195 Length - Indicates the length of valid data.
4197 PageMode - Supplies the page mode to be searched for.
4199 Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
4203 A pointer to the the requested mode page. If the mode page was not found
4204 then NULL is return.
4210 IN PCHAR ModeSenseBuffer
,
4217 ULONG parameterHeaderLength
;
4218 PVOID result
= NULL
;
4220 limit
= ModeSenseBuffer
+ Length
;
4221 parameterHeaderLength
= (Use6Byte
) ? sizeof(MODE_PARAMETER_HEADER
) : sizeof(MODE_PARAMETER_HEADER10
);
4223 if (Length
>= parameterHeaderLength
) {
4225 PMODE_PARAMETER_HEADER10 modeParam10
;
4226 ULONG blockDescriptorLength
;
4229 * Skip the mode select header and block descriptors.
4232 blockDescriptorLength
= ((PMODE_PARAMETER_HEADER
) ModeSenseBuffer
)->BlockDescriptorLength
;
4235 modeParam10
= (PMODE_PARAMETER_HEADER10
) ModeSenseBuffer
;
4236 blockDescriptorLength
= modeParam10
->BlockDescriptorLength
[1];
4239 ModeSenseBuffer
+= parameterHeaderLength
+ blockDescriptorLength
;
4242 // ModeSenseBuffer now points at pages. Walk the pages looking for the
4243 // requested page until the limit is reached.
4246 while (ModeSenseBuffer
+
4247 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
) < limit
) {
4249 if (((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageCode
== PageMode
) {
4252 * found the mode page. make sure it's safe to touch it all
4253 * before returning the pointer to caller
4256 if (ModeSenseBuffer
+ ((PMODE_DISCONNECT_PAGE
)ModeSenseBuffer
)->PageLength
> limit
) {
4258 * Return NULL since the page is not safe to access in full
4263 result
= ModeSenseBuffer
;
4269 // Advance to the next page which is 4-byte-aligned offset after this page.
4272 ((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageLength
+
4273 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
);
4279 } // end ClassFindModePage()
4281 /*++////////////////////////////////////////////////////////////////////////////
4283 ClassSendSrbAsynchronous()
4285 Routine Description:
4287 This routine takes a partially built Srb and an Irp and sends it down to
4290 This routine must be called with the remove lock held for the specified
4295 Fdo - Supplies the functional device object for the original request.
4297 Srb - Supplies a partially built ScsiRequestBlock. In particular, the
4298 CDB and the SRB timeout value must be filled in. The SRB must not be
4299 allocated from zone.
4301 Irp - Supplies the requesting Irp.
4303 BufferAddress - Supplies a pointer to the buffer to be transfered.
4305 BufferLength - Supplies the length of data transfer.
4307 WriteToDevice - Indicates the data transfer will be from system memory to
4312 Returns STATUS_PENDING if the request is dispatched (since the
4313 completion routine may change the irp's status value we cannot simply
4314 return the value of the dispatch)
4316 or returns a status value to indicate why it failed.
4321 ClassSendSrbAsynchronous(
4323 PSCSI_REQUEST_BLOCK Srb
,
4325 PVOID BufferAddress
,
4327 BOOLEAN WriteToDevice
4331 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
4332 PIO_STACK_LOCATION irpStack
;
4337 // Write length to SRB.
4340 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
4343 // Set SCSI bus address.
4346 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
4349 // This is a violation of the SCSI spec but it is required for
4353 // Srb->Cdb[1] |= deviceExtension->Lun << 5;
4356 // Indicate auto request sense by specifying buffer and size.
4359 Srb
->SenseInfoBuffer
= fdoExtension
->SenseData
;
4360 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
4361 Srb
->DataBuffer
= BufferAddress
;
4364 // Save the class driver specific flags away.
4367 savedFlags
= Srb
->SrbFlags
& SRB_FLAGS_CLASS_DRIVER_RESERVED
;
4370 // Allow the caller to specify that they do not wish
4371 // IoStartNextPacket() to be called in the completion routine.
4374 SET_FLAG(savedFlags
, (Srb
->SrbFlags
& SRB_FLAGS_DONT_START_NEXT_PACKET
));
4376 if (BufferAddress
!= NULL
) {
4379 // Build Mdl if necessary.
4382 if (Irp
->MdlAddress
== NULL
) {
4384 if (IoAllocateMdl(BufferAddress
,
4390 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4393 // ClassIoComplete() would have free'd the srb
4396 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
4397 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
4399 ClassFreeOrReuseSrb(fdoExtension
, Srb
);
4400 ClassReleaseRemoveLock(Fdo
, Irp
);
4401 ClassCompleteRequest(Fdo
, Irp
, IO_NO_INCREMENT
);
4403 return STATUS_INSUFFICIENT_RESOURCES
;
4406 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
4411 // Make sure the buffer requested matches the MDL.
4414 ASSERT(BufferAddress
== MmGetMdlVirtualAddress(Irp
->MdlAddress
));
4421 Srb
->SrbFlags
= WriteToDevice
? SRB_FLAGS_DATA_OUT
: SRB_FLAGS_DATA_IN
;
4429 Srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
;
4433 // Restore saved flags.
4436 SET_FLAG(Srb
->SrbFlags
, savedFlags
);
4439 // Disable synchronous transfer for these requests.
4442 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
4445 // Set the transfer length.
4448 Srb
->DataTransferLength
= BufferLength
;
4454 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
4459 // Save a few parameters in the current stack location.
4462 irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4465 // Save retry count in current Irp stack.
4468 irpStack
->Parameters
.Others
.Argument4
= (PVOID
)MAXIMUM_RETRIES
;
4471 // Set up IoCompletion routine address.
4474 IoSetCompletionRoutine(Irp
, ClassIoComplete
, Srb
, TRUE
, TRUE
, TRUE
);
4477 // Get next stack location and
4478 // set major function code.
4481 irpStack
= IoGetNextIrpStackLocation(Irp
);
4483 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
4486 // Save SRB address in next stack for port driver.
4489 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
4492 // Set up Irp Address.
4495 Srb
->OriginalRequest
= Irp
;
4498 // Call the port driver to process the request.
4501 IoMarkIrpPending(Irp
);
4503 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, Irp
);
4505 return STATUS_PENDING
;
4507 } // end ClassSendSrbAsynchronous()
4509 /*++////////////////////////////////////////////////////////////////////////////
4511 ClassDeviceControlDispatch()
4513 Routine Description:
4515 The routine is the common class driver device control dispatch entry point.
4516 This routine is invokes the device-specific drivers DeviceControl routine,
4517 (which may call the Class driver's common DeviceControl routine).
4521 DeviceObject - Supplies a pointer to the device object for this request.
4523 Irp - Supplies the Irp making the request.
4527 Returns the status returned from the device-specific driver.
4532 ClassDeviceControlDispatch(
4533 PDEVICE_OBJECT DeviceObject
,
4538 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
4541 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
4545 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4547 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
4548 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4549 return STATUS_DEVICE_DOES_NOT_EXIST
;
4553 // Call the class specific driver DeviceControl routine.
4554 // If it doesn't handle it, it will call back into ClassDeviceControl.
4557 ASSERT(commonExtension
->DevInfo
->ClassDeviceControl
);
4559 return commonExtension
->DevInfo
->ClassDeviceControl(DeviceObject
,Irp
);
4560 } // end ClassDeviceControlDispatch()
4562 /*++////////////////////////////////////////////////////////////////////////////
4564 ClassDeviceControl()
4566 Routine Description:
4568 The routine is the common class driver device control dispatch function.
4569 This routine is called by a class driver when it get an unrecognized
4570 device control request. This routine will perform the correct action for
4571 common requests such as lock media. If the device request is unknown it
4572 passed down to the next level.
4574 This routine must be called with the remove lock held for the specified
4579 DeviceObject - Supplies a pointer to the device object for this request.
4581 Irp - Supplies the Irp making the request.
4585 Returns back a STATUS_PENDING or a completion status.
4591 PDEVICE_OBJECT DeviceObject
,
4595 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
4597 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4598 PIO_STACK_LOCATION nextStack
= NULL
;
4600 ULONG controlCode
= irpStack
->Parameters
.DeviceIoControl
.IoControlCode
;
4602 PSCSI_REQUEST_BLOCK srb
= NULL
;
4606 ULONG modifiedIoControlCode
;
4609 // If this is a pass through I/O control, set the minor function code
4610 // and device address and pass it to the port driver.
4613 if ((controlCode
== IOCTL_SCSI_PASS_THROUGH
) ||
4614 (controlCode
== IOCTL_SCSI_PASS_THROUGH_DIRECT
)) {
4616 //PSCSI_PASS_THROUGH scsiPass;
4619 // Validate the user buffer.
4621 #if defined (_WIN64)
4623 if (IoIs32bitProcess(Irp
)) {
4625 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(SCSI_PASS_THROUGH32
)){
4627 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
4629 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4630 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4632 status
= STATUS_INVALID_PARAMETER
;
4633 goto SetStatusAndReturn
;
4639 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
4640 sizeof(SCSI_PASS_THROUGH
)) {
4642 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
4644 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4645 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4647 status
= STATUS_INVALID_PARAMETER
;
4648 goto SetStatusAndReturn
;
4652 IoCopyCurrentIrpStackLocationToNext(Irp
);
4654 nextStack
= IoGetNextIrpStackLocation(Irp
);
4655 nextStack
->MinorFunction
= 1;
4657 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4659 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4660 goto SetStatusAndReturn
;
4663 Irp
->IoStatus
.Information
= 0;
4665 switch (controlCode
) {
4667 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
: {
4669 PMOUNTDEV_UNIQUE_ID uniqueId
;
4671 if (!commonExtension
->MountedDeviceInterfaceName
.Buffer
) {
4672 status
= STATUS_INVALID_PARAMETER
;
4676 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4677 sizeof(MOUNTDEV_UNIQUE_ID
)) {
4679 status
= STATUS_BUFFER_TOO_SMALL
;
4680 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4684 uniqueId
= Irp
->AssociatedIrp
.SystemBuffer
;
4685 uniqueId
->UniqueIdLength
=
4686 commonExtension
->MountedDeviceInterfaceName
.Length
;
4688 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4689 sizeof(USHORT
) + uniqueId
->UniqueIdLength
) {
4691 status
= STATUS_BUFFER_OVERFLOW
;
4692 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4696 RtlCopyMemory(uniqueId
->UniqueId
,
4697 commonExtension
->MountedDeviceInterfaceName
.Buffer
,
4698 uniqueId
->UniqueIdLength
);
4700 status
= STATUS_SUCCESS
;
4701 Irp
->IoStatus
.Information
= sizeof(USHORT
) +
4702 uniqueId
->UniqueIdLength
;
4706 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
: {
4708 PMOUNTDEV_NAME name
;
4710 ASSERT(commonExtension
->DeviceName
.Buffer
);
4712 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4713 sizeof(MOUNTDEV_NAME
)) {
4715 status
= STATUS_BUFFER_TOO_SMALL
;
4716 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4720 name
= Irp
->AssociatedIrp
.SystemBuffer
;
4721 name
->NameLength
= commonExtension
->DeviceName
.Length
;
4723 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4724 sizeof(USHORT
) + name
->NameLength
) {
4726 status
= STATUS_BUFFER_OVERFLOW
;
4727 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4731 RtlCopyMemory(name
->Name
, commonExtension
->DeviceName
.Buffer
,
4734 status
= STATUS_SUCCESS
;
4735 Irp
->IoStatus
.Information
= sizeof(USHORT
) + name
->NameLength
;
4739 case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
: {
4741 PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName
;
4742 WCHAR driveLetterNameBuffer
[10];
4743 RTL_QUERY_REGISTRY_TABLE queryTable
[2];
4745 UNICODE_STRING driveLetterName
;
4747 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4748 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
)) {
4750 status
= STATUS_BUFFER_TOO_SMALL
;
4751 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
4755 valueName
= ExAllocatePoolWithTag(
4757 commonExtension
->DeviceName
.Length
+ sizeof(WCHAR
),
4761 status
= STATUS_INSUFFICIENT_RESOURCES
;
4765 RtlCopyMemory(valueName
, commonExtension
->DeviceName
.Buffer
,
4766 commonExtension
->DeviceName
.Length
);
4767 valueName
[commonExtension
->DeviceName
.Length
/sizeof(WCHAR
)] = 0;
4769 driveLetterName
.Buffer
= driveLetterNameBuffer
;
4770 driveLetterName
.MaximumLength
= 20;
4771 driveLetterName
.Length
= 0;
4773 RtlZeroMemory(queryTable
, 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
4774 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_REQUIRED
|
4775 RTL_QUERY_REGISTRY_DIRECT
;
4776 queryTable
[0].Name
= valueName
;
4777 queryTable
[0].EntryContext
= &driveLetterName
;
4779 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
4780 L
"\\Registry\\Machine\\System\\DISK",
4781 queryTable
, NULL
, NULL
);
4783 if (!NT_SUCCESS(status
)) {
4784 ExFreePool(valueName
);
4788 if (driveLetterName
.Length
== 4 &&
4789 driveLetterName
.Buffer
[0] == '%' &&
4790 driveLetterName
.Buffer
[1] == ':') {
4792 driveLetterName
.Buffer
[0] = 0xFF;
4794 } else if (driveLetterName
.Length
!= 4 ||
4795 driveLetterName
.Buffer
[0] < FirstDriveLetter
||
4796 driveLetterName
.Buffer
[0] > LastDriveLetter
||
4797 driveLetterName
.Buffer
[1] != ':') {
4799 status
= STATUS_NOT_FOUND
;
4800 ExFreePool(valueName
);
4804 suggestedName
= Irp
->AssociatedIrp
.SystemBuffer
;
4805 suggestedName
->UseOnlyIfThereAreNoOtherLinks
= TRUE
;
4806 suggestedName
->NameLength
= 28;
4808 Irp
->IoStatus
.Information
=
4809 FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME
, Name
) + 28;
4811 if (irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4812 Irp
->IoStatus
.Information
) {
4814 Irp
->IoStatus
.Information
=
4815 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
4816 status
= STATUS_BUFFER_OVERFLOW
;
4817 ExFreePool(valueName
);
4821 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
4822 L
"\\Registry\\Machine\\System\\DISK",
4825 ExFreePool(valueName
);
4827 RtlCopyMemory(suggestedName
->Name
, L
"\\DosDevices\\", 24);
4828 suggestedName
->Name
[12] = driveLetterName
.Buffer
[0];
4829 suggestedName
->Name
[13] = ':';
4832 // NT_SUCCESS(status) based on RtlQueryRegistryValues
4834 status
= STATUS_SUCCESS
;
4840 status
= STATUS_PENDING
;
4844 if (status
!= STATUS_PENDING
) {
4845 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4846 Irp
->IoStatus
.Status
= status
;
4847 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4851 if (commonExtension
->IsFdo
){
4853 PULONG_PTR function
;
4855 srb
= ExAllocatePoolWithTag(NonPagedPool
,
4856 sizeof(SCSI_REQUEST_BLOCK
) +
4857 (sizeof(ULONG_PTR
) * 2),
4862 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4863 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4864 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4865 status
= STATUS_INSUFFICIENT_RESOURCES
;
4866 goto SetStatusAndReturn
;
4869 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
4871 cdb
= (PCDB
)srb
->Cdb
;
4874 // Save the function code and the device object in the memory after
4878 function
= (PULONG_PTR
) ((PSCSI_REQUEST_BLOCK
) (srb
+ 1));
4879 *function
= (ULONG_PTR
) DeviceObject
;
4881 *function
= (ULONG_PTR
) controlCode
;
4888 // Change the device type to storage for the switch statement, but only
4889 // if from a legacy device type
4892 if (((controlCode
& 0xffff0000) == (IOCTL_DISK_BASE
<< 16)) ||
4893 ((controlCode
& 0xffff0000) == (IOCTL_TAPE_BASE
<< 16)) ||
4894 ((controlCode
& 0xffff0000) == (IOCTL_CDROM_BASE
<< 16))
4897 modifiedIoControlCode
= (controlCode
& ~0xffff0000);
4898 modifiedIoControlCode
|= (IOCTL_STORAGE_BASE
<< 16);
4902 modifiedIoControlCode
= controlCode
;
4906 DBGTRACE(ClassDebugTrace
, ("> ioctl %xh (%s)", modifiedIoControlCode
, DBGGETIOCTLSTR(modifiedIoControlCode
)));
4908 switch (modifiedIoControlCode
) {
4910 case IOCTL_STORAGE_GET_HOTPLUG_INFO
: {
4917 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
4918 sizeof(STORAGE_HOTPLUG_INFO
)) {
4921 // Indicate unsuccessful status and no data transferred.
4924 Irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
4925 Irp
->IoStatus
.Information
= sizeof(STORAGE_HOTPLUG_INFO
);
4927 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4928 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4929 status
= STATUS_BUFFER_TOO_SMALL
;
4931 } else if(!commonExtension
->IsFdo
) {
4934 // Just forward this down and return
4937 IoCopyCurrentIrpStackLocationToNext(Irp
);
4939 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4940 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4944 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
4945 PSTORAGE_HOTPLUG_INFO info
;
4947 fdoExtension
= (PFUNCTIONAL_DEVICE_EXTENSION
)commonExtension
;
4948 info
= Irp
->AssociatedIrp
.SystemBuffer
;
4950 *info
= fdoExtension
->PrivateFdoData
->HotplugInfo
;
4951 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
4952 Irp
->IoStatus
.Information
= sizeof(STORAGE_HOTPLUG_INFO
);
4953 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4954 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4955 status
= STATUS_SUCCESS
;
4961 case IOCTL_STORAGE_SET_HOTPLUG_INFO
: {
4969 if (irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
4970 sizeof(STORAGE_HOTPLUG_INFO
)) {
4973 // Indicate unsuccessful status and no data transferred.
4976 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
4978 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4979 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
4980 status
= STATUS_INFO_LENGTH_MISMATCH
;
4981 goto SetStatusAndReturn
;
4985 if(!commonExtension
->IsFdo
) {
4988 // Just forward this down and return
4991 IoCopyCurrentIrpStackLocationToNext(Irp
);
4993 ClassReleaseRemoveLock(DeviceObject
, Irp
);
4994 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
4998 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PFUNCTIONAL_DEVICE_EXTENSION
)commonExtension
;
4999 PSTORAGE_HOTPLUG_INFO info
= Irp
->AssociatedIrp
.SystemBuffer
;
5001 status
= STATUS_SUCCESS
;
5003 if (info
->Size
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.Size
)
5005 status
= STATUS_INVALID_PARAMETER_1
;
5008 if (info
->MediaRemovable
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.MediaRemovable
)
5010 status
= STATUS_INVALID_PARAMETER_2
;
5013 if (info
->MediaHotplug
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.MediaHotplug
)
5015 status
= STATUS_INVALID_PARAMETER_3
;
5018 if (info
->WriteCacheEnableOverride
!= fdoExtension
->PrivateFdoData
->HotplugInfo
.WriteCacheEnableOverride
)
5020 status
= STATUS_INVALID_PARAMETER_5
;
5023 if (NT_SUCCESS(status
))
5025 fdoExtension
->PrivateFdoData
->HotplugInfo
.DeviceHotplug
= info
->DeviceHotplug
;
5028 // Store the user-defined override in the registry
5031 ClassSetDeviceParameter(fdoExtension
,
5032 CLASSP_REG_SUBKEY_NAME
,
5033 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
5034 (info
->DeviceHotplug
) ? RemovalPolicyExpectSurpriseRemoval
: RemovalPolicyExpectOrderlyRemoval
);
5037 Irp
->IoStatus
.Status
= status
;
5039 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5040 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5046 case IOCTL_STORAGE_CHECK_VERIFY
:
5047 case IOCTL_STORAGE_CHECK_VERIFY2
: {
5050 PIO_STACK_LOCATION newStack
;
5052 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5054 DebugPrint((1,"DeviceIoControl: Check verify\n"));
5057 // If a buffer for a media change count was provided, make sure it's
5058 // big enough to hold the result
5061 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
) {
5064 // If the buffer is too small to hold the media change count
5065 // then return an error to the caller
5068 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
5071 DebugPrint((3,"DeviceIoControl: media count "
5072 "buffer too small\n"));
5074 Irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
5075 Irp
->IoStatus
.Information
= sizeof(ULONG
);
5081 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5082 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5084 status
= STATUS_BUFFER_TOO_SMALL
;
5085 goto SetStatusAndReturn
;
5090 if(!commonExtension
->IsFdo
) {
5093 // If this is a PDO then we should just forward the request down
5097 IoCopyCurrentIrpStackLocationToNext(Irp
);
5099 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5101 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5103 goto SetStatusAndReturn
;
5107 fdoExtension
= DeviceObject
->DeviceExtension
;
5111 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
) {
5114 // The caller has provided a valid buffer. Allocate an additional
5115 // irp and stick the CheckVerify completion routine on it. We will
5116 // then send this down to the port driver instead of the irp the
5120 DebugPrint((2,"DeviceIoControl: Check verify wants "
5124 // Allocate a new irp to send the TestUnitReady to the port driver
5127 irp2
= IoAllocateIrp((CCHAR
) (DeviceObject
->StackSize
+ 3), FALSE
);
5130 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
5131 Irp
->IoStatus
.Information
= 0;
5134 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5135 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5136 status
= STATUS_INSUFFICIENT_RESOURCES
;
5137 goto SetStatusAndReturn
;
5143 // Make sure to acquire the lock for the new irp.
5146 ClassAcquireRemoveLock(DeviceObject
, irp2
);
5148 irp2
->Tail
.Overlay
.Thread
= Irp
->Tail
.Overlay
.Thread
;
5149 IoSetNextIrpStackLocation(irp2
);
5152 // Set the top stack location and shove the master Irp into the
5156 newStack
= IoGetCurrentIrpStackLocation(irp2
);
5157 newStack
->Parameters
.Others
.Argument1
= Irp
;
5158 newStack
->DeviceObject
= DeviceObject
;
5161 // Stick the check verify completion routine onto the stack
5162 // and prepare the irp for the port driver
5165 IoSetCompletionRoutine(irp2
,
5166 ClassCheckVerifyComplete
,
5172 IoSetNextIrpStackLocation(irp2
);
5173 newStack
= IoGetCurrentIrpStackLocation(irp2
);
5174 newStack
->DeviceObject
= DeviceObject
;
5175 newStack
->MajorFunction
= irpStack
->MajorFunction
;
5176 newStack
->MinorFunction
= irpStack
->MinorFunction
;
5179 // Mark the master irp as pending - whether the lower level
5180 // driver completes it immediately or not this should allow it
5181 // to go all the way back up.
5184 IoMarkIrpPending(Irp
);
5195 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_TEST_UNIT_READY
;
5198 // Set timeout value.
5201 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5204 // If this was a CV2 then mark the request as low-priority so we don't
5205 // spin up the drive just to satisfy it.
5208 if(controlCode
== IOCTL_STORAGE_CHECK_VERIFY2
) {
5209 SET_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_LOW_PRIORITY
);
5213 // Since this routine will always hand the request to the
5214 // port driver if there isn't a data transfer to be done
5215 // we don't have to worry about completing the request here
5220 // This routine uses a completion routine so we don't want to release
5221 // the remove lock until then.
5224 status
= ClassSendSrbAsynchronous(DeviceObject
,
5234 case IOCTL_STORAGE_MEDIA_REMOVAL
:
5235 case IOCTL_STORAGE_EJECTION_CONTROL
: {
5237 PPREVENT_MEDIA_REMOVAL mediaRemoval
= Irp
->AssociatedIrp
.SystemBuffer
;
5239 DebugPrint((3, "DiskIoControl: ejection control\n"));
5245 if(irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
5246 sizeof(PREVENT_MEDIA_REMOVAL
)) {
5249 // Indicate unsuccessful status and no data transferred.
5252 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5254 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5255 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5256 status
= STATUS_INFO_LENGTH_MISMATCH
;
5257 goto SetStatusAndReturn
;
5260 if(!commonExtension
->IsFdo
) {
5263 // Just forward this down and return
5266 IoCopyCurrentIrpStackLocationToNext(Irp
);
5268 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5269 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5273 // i don't believe this assertion is valid. this is a request
5274 // from user-mode, so they could request this for any device
5275 // they want? also, we handle it properly.
5276 // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
5277 status
= ClasspEjectionControl(
5280 ((modifiedIoControlCode
==
5281 IOCTL_STORAGE_EJECTION_CONTROL
) ? SecureMediaLock
:
5283 mediaRemoval
->PreventMediaRemoval
);
5285 Irp
->IoStatus
.Status
= status
;
5286 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5287 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5293 case IOCTL_STORAGE_MCN_CONTROL
: {
5295 DebugPrint((3, "DiskIoControl: MCN control\n"));
5297 if(irpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
5298 sizeof(PREVENT_MEDIA_REMOVAL
)) {
5301 // Indicate unsuccessful status and no data transferred.
5304 Irp
->IoStatus
.Status
= STATUS_INFO_LENGTH_MISMATCH
;
5305 Irp
->IoStatus
.Information
= 0;
5311 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5312 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5313 status
= STATUS_INFO_LENGTH_MISMATCH
;
5314 goto SetStatusAndReturn
;
5317 if(!commonExtension
->IsFdo
) {
5320 // Just forward this down and return
5327 IoCopyCurrentIrpStackLocationToNext(Irp
);
5329 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5330 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5335 // Call to the FDO - handle the ejection control.
5338 status
= ClasspMcnControl(DeviceObject
->DeviceExtension
,
5342 goto SetStatusAndReturn
;
5345 case IOCTL_STORAGE_RESERVE
:
5346 case IOCTL_STORAGE_RELEASE
: {
5349 // Reserve logical unit.
5352 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5354 if(!commonExtension
->IsFdo
) {
5356 IoCopyCurrentIrpStackLocationToNext(Irp
);
5358 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5359 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5360 goto SetStatusAndReturn
;
5362 fdoExtension
= DeviceObject
->DeviceExtension
;
5367 if(modifiedIoControlCode
== IOCTL_STORAGE_RESERVE
) {
5368 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_RESERVE_UNIT
;
5370 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_RELEASE_UNIT
;
5374 // Set timeout value.
5377 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5379 status
= ClassSendSrbAsynchronous(DeviceObject
,
5389 case IOCTL_STORAGE_EJECT_MEDIA
:
5390 case IOCTL_STORAGE_LOAD_MEDIA
:
5391 case IOCTL_STORAGE_LOAD_MEDIA2
:{
5397 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= NULL
;
5399 if(!commonExtension
->IsFdo
) {
5401 IoCopyCurrentIrpStackLocationToNext(Irp
);
5403 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5405 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5406 goto SetStatusAndReturn
;
5408 fdoExtension
= DeviceObject
->DeviceExtension
;
5411 if(commonExtension
->PagingPathCount
!= 0) {
5413 DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
5416 status
= STATUS_FILES_OPEN
;
5417 Irp
->IoStatus
.Status
= status
;
5419 Irp
->IoStatus
.Information
= 0;
5425 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5426 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5427 goto SetStatusAndReturn
;
5431 // Synchronize with ejection control and ejection cleanup code as
5432 // well as other eject/load requests.
5435 KeEnterCriticalRegion();
5436 KeWaitForSingleObject(&(fdoExtension
->EjectSynchronizationEvent
),
5442 if(fdoExtension
->ProtectedLockCount
!= 0) {
5444 DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
5445 "device - failure\n"));
5447 status
= STATUS_DEVICE_BUSY
;
5448 Irp
->IoStatus
.Status
= status
;
5449 Irp
->IoStatus
.Information
= 0;
5455 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5456 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5458 KeSetEvent(&fdoExtension
->EjectSynchronizationEvent
,
5461 KeLeaveCriticalRegion();
5463 goto SetStatusAndReturn
;
5468 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
5469 cdb
->START_STOP
.LoadEject
= 1;
5471 if(modifiedIoControlCode
== IOCTL_STORAGE_EJECT_MEDIA
) {
5472 cdb
->START_STOP
.Start
= 0;
5474 cdb
->START_STOP
.Start
= 1;
5478 // Set timeout value.
5481 srb
->TimeOutValue
= fdoExtension
->TimeOutValue
;
5482 status
= ClassSendSrbAsynchronous(DeviceObject
,
5489 KeSetEvent(&fdoExtension
->EjectSynchronizationEvent
, IO_NO_INCREMENT
, FALSE
);
5490 KeLeaveCriticalRegion();
5495 case IOCTL_STORAGE_FIND_NEW_DEVICES
: {
5501 if(commonExtension
->IsFdo
) {
5503 IoInvalidateDeviceRelations(
5504 ((PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
)->LowerPdo
,
5507 status
= STATUS_SUCCESS
;
5508 Irp
->IoStatus
.Status
= status
;
5510 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5511 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5515 IoCopyCurrentIrpStackLocationToNext(Irp
);
5517 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5518 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5523 case IOCTL_STORAGE_GET_DEVICE_NUMBER
: {
5529 if(irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
>=
5530 sizeof(STORAGE_DEVICE_NUMBER
)) {
5532 PSTORAGE_DEVICE_NUMBER deviceNumber
=
5533 Irp
->AssociatedIrp
.SystemBuffer
;
5534 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
5535 commonExtension
->PartitionZeroExtension
;
5537 deviceNumber
->DeviceType
= fdoExtension
->CommonExtension
.DeviceObject
->DeviceType
;
5538 deviceNumber
->DeviceNumber
= fdoExtension
->DeviceNumber
;
5539 deviceNumber
->PartitionNumber
= commonExtension
->PartitionNumber
;
5541 status
= STATUS_SUCCESS
;
5542 Irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_NUMBER
);
5545 status
= STATUS_BUFFER_TOO_SMALL
;
5546 Irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_NUMBER
);
5549 Irp
->IoStatus
.Status
= status
;
5550 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5551 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5558 DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
5559 controlCode
, DeviceObject
));
5562 // Pass the device control to the next driver.
5570 // Copy the Irp stack parameters to the next stack location.
5573 IoCopyCurrentIrpStackLocationToNext(Irp
);
5575 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5576 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
5580 } // end switch( ...
5584 DBGTRACE(ClassDebugTrace
, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode
, DBGGETIOCTLSTR(modifiedIoControlCode
), status
));
5587 } // end ClassDeviceControl()
5589 /*++////////////////////////////////////////////////////////////////////////////
5591 ClassShutdownFlush()
5593 Routine Description:
5595 This routine is called for a shutdown and flush IRPs. These are sent by the
5596 system before it actually shuts down or when the file system does a flush.
5597 If it exists, the device-specific driver's routine will be invoked. If there
5598 wasn't one specified, the Irp will be completed with an Invalid device request.
5602 DriverObject - Pointer to device object to being shutdown by system.
5614 IN PDEVICE_OBJECT DeviceObject
,
5618 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
5624 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
5628 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5630 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
5632 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5634 return STATUS_DEVICE_DOES_NOT_EXIST
;
5637 if (commonExtension
->DevInfo
->ClassShutdownFlush
) {
5640 // Call the device-specific driver's routine.
5643 return commonExtension
->DevInfo
->ClassShutdownFlush(DeviceObject
, Irp
);
5647 // Device-specific driver doesn't support this.
5650 Irp
->IoStatus
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
5652 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5653 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5655 return STATUS_INVALID_DEVICE_REQUEST
;
5656 } // end ClassShutdownFlush()
5658 /*++////////////////////////////////////////////////////////////////////////////
5660 ClassCreateDeviceObject()
5662 Routine Description:
5664 This routine creates an object for the physical device specified and
5665 sets up the deviceExtension's function pointers for each entry point
5666 in the device-specific driver.
5670 DriverObject - Pointer to driver object created by system.
5672 ObjectNameBuffer - Dir. name of the object to create.
5674 LowerDeviceObject - Pointer to the lower device object
5676 IsFdo - should this be an fdo or a pdo
5678 DeviceObject - Pointer to the device object pointer we will return.
5687 ClassCreateDeviceObject(
5688 IN PDRIVER_OBJECT DriverObject
,
5689 IN PCCHAR ObjectNameBuffer
,
5690 IN PDEVICE_OBJECT LowerDevice
,
5692 IN OUT PDEVICE_OBJECT
*DeviceObject
5695 BOOLEAN isPartitionable
;
5696 STRING ntNameString
;
5697 UNICODE_STRING ntUnicodeString
;
5699 PDEVICE_OBJECT deviceObject
= NULL
;
5701 ULONG characteristics
;
5703 PCLASS_DRIVER_EXTENSION
5704 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
5705 CLASS_DRIVER_EXTENSION_KEY
);
5707 PCLASS_DEV_INFO devInfo
;
5711 *DeviceObject
= NULL
;
5712 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5714 DebugPrint((2, "ClassCreateFdo: Create device object\n"));
5716 ASSERT(LowerDevice
);
5719 // Make sure that if we're making PDO's we have an enumeration routine
5722 isPartitionable
= (driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
5724 ASSERT(IsFdo
|| isPartitionable
);
5727 // Grab the correct dev-info structure out of the init data
5731 devInfo
= &(driverExtension
->InitData
.FdoData
);
5733 devInfo
= &(driverExtension
->InitData
.PdoData
);
5736 characteristics
= devInfo
->DeviceCharacteristics
;
5738 if(ARGUMENT_PRESENT(ObjectNameBuffer
)) {
5739 DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer
));
5741 RtlInitString(&ntNameString
, ObjectNameBuffer
);
5743 status
= RtlAnsiStringToUnicodeString(&ntUnicodeString
, &ntNameString
, TRUE
);
5745 if (!NT_SUCCESS(status
)) {
5748 "ClassCreateFdo: Cannot convert string %s\n",
5751 ntUnicodeString
.Buffer
= NULL
;
5755 DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
5757 if(IsFdo
== FALSE
) {
5760 // PDO's have to have some sort of name.
5763 SET_FLAG(characteristics
, FILE_AUTOGENERATED_DEVICE_NAME
);
5766 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5769 status
= IoCreateDevice(DriverObject
,
5770 devInfo
->DeviceExtensionSize
,
5772 devInfo
->DeviceType
,
5773 devInfo
->DeviceCharacteristics
,
5777 if (!NT_SUCCESS(status
)) {
5779 DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
5781 ASSERT(deviceObject
== NULL
);
5784 // buffer is not used any longer here.
5787 if (ntUnicodeString
.Buffer
!= NULL
) {
5788 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5789 ExFreePool(ntUnicodeString
.Buffer
);
5790 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5795 PCOMMON_DEVICE_EXTENSION commonExtension
= deviceObject
->DeviceExtension
;
5798 deviceObject
->DeviceExtension
,
5799 devInfo
->DeviceExtensionSize
);
5802 // Setup version code
5805 commonExtension
->Version
= 0x03;
5808 // Setup the remove lock and event
5811 commonExtension
->IsRemoved
= NO_REMOVE
;
5812 commonExtension
->RemoveLock
= 0;
5813 KeInitializeEvent(&commonExtension
->RemoveEvent
,
5814 SynchronizationEvent
,
5818 KeInitializeSpinLock(&commonExtension
->RemoveTrackingSpinlock
);
5819 commonExtension
->RemoveTrackingList
= NULL
;
5821 commonExtension
->RemoveTrackingSpinlock
= (ULONG_PTR
) -1;
5822 commonExtension
->RemoveTrackingList
= (PVOID
) -1;
5826 // Acquire the lock once. This reference will be released when the
5827 // remove IRP has been received.
5830 ClassAcquireRemoveLock(deviceObject
, (PIRP
) deviceObject
);
5833 // Store a pointer to the driver extension so we don't have to do
5834 // lookups to get it.
5837 commonExtension
->DriverExtension
= driverExtension
;
5840 // Fill in entry points
5843 commonExtension
->DevInfo
= devInfo
;
5846 // Initialize some of the common values in the structure
5849 commonExtension
->DeviceObject
= deviceObject
;
5851 commonExtension
->LowerDeviceObject
= NULL
;
5855 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PVOID
) commonExtension
;
5857 commonExtension
->PartitionZeroExtension
= deviceObject
->DeviceExtension
;
5860 // Set the initial device object flags.
5863 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5866 // Clear the PDO list
5869 commonExtension
->ChildList
= NULL
;
5871 commonExtension
->DriverData
=
5872 ((PFUNCTIONAL_DEVICE_EXTENSION
) deviceObject
->DeviceExtension
+ 1);
5874 if(isPartitionable
) {
5876 commonExtension
->PartitionNumber
= 0;
5878 commonExtension
->PartitionNumber
= (ULONG
) (-1L);
5881 fdoExtension
->DevicePowerState
= PowerDeviceD0
;
5883 KeInitializeEvent(&fdoExtension
->EjectSynchronizationEvent
,
5884 SynchronizationEvent
,
5887 KeInitializeEvent(&fdoExtension
->ChildLock
,
5888 SynchronizationEvent
,
5891 status
= ClasspAllocateReleaseRequest(deviceObject
);
5893 if(!NT_SUCCESS(status
)) {
5894 IoDeleteDevice(deviceObject
);
5895 *DeviceObject
= NULL
;
5897 if (ntUnicodeString
.Buffer
!= NULL
) {
5898 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5899 ExFreePool(ntUnicodeString
.Buffer
);
5900 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5908 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
5909 deviceObject
->DeviceExtension
;
5911 PFUNCTIONAL_DEVICE_EXTENSION p0Extension
=
5912 LowerDevice
->DeviceExtension
;
5914 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5916 commonExtension
->PartitionZeroExtension
= p0Extension
;
5919 // Stick this onto the PDO list
5922 ClassAddChild(p0Extension
, pdoExtension
, TRUE
);
5924 commonExtension
->DriverData
= (PVOID
) (pdoExtension
+ 1);
5927 // Get the top of stack for the lower device - this allows
5928 // filters to get stuck in between the partitions and the
5932 commonExtension
->LowerDeviceObject
=
5933 IoGetAttachedDeviceReference(LowerDevice
);
5936 // Pnp will keep a reference to the lower device object long
5937 // after this partition has been deleted. Dereference now so
5938 // we don't have to deal with it later.
5941 ObDereferenceObject(commonExtension
->LowerDeviceObject
);
5944 KeInitializeEvent(&commonExtension
->PathCountEvent
, SynchronizationEvent
, TRUE
);
5946 commonExtension
->IsFdo
= IsFdo
;
5948 commonExtension
->DeviceName
= ntUnicodeString
;
5950 commonExtension
->PreviousState
= 0xff;
5952 InitializeDictionary(&(commonExtension
->FileObjectDictionary
));
5954 commonExtension
->CurrentState
= IRP_MN_STOP_DEVICE
;
5957 *DeviceObject
= deviceObject
;
5960 } // end ClassCreateDeviceObject()
5962 /*++////////////////////////////////////////////////////////////////////////////
5966 Routine Description:
5968 This function claims a device in the port driver. The port driver object
5969 is updated with the correct driver object if the device is successfully
5974 LowerDeviceObject - Supplies the base port device object.
5976 Release - Indicates the logical unit should be released rather than claimed.
5980 Returns a status indicating success or failure of the operation.
5986 IN PDEVICE_OBJECT LowerDeviceObject
,
5990 IO_STATUS_BLOCK ioStatus
;
5992 PIO_STACK_LOCATION irpStack
;
5995 SCSI_REQUEST_BLOCK srb
;
6000 // Clear the SRB fields.
6003 RtlZeroMemory(&srb
, sizeof(SCSI_REQUEST_BLOCK
));
6006 // Write length to SRB.
6009 srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
6011 srb
.Function
= Release
? SRB_FUNCTION_RELEASE_DEVICE
:
6012 SRB_FUNCTION_CLAIM_DEVICE
;
6015 // Set the event object to the unsignaled state.
6016 // It will be used to signal request completion
6019 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
6022 // Build synchronous request with no transfer.
6025 irp
= IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE
,
6036 DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
6037 return STATUS_INSUFFICIENT_RESOURCES
;
6040 irpStack
= IoGetNextIrpStackLocation(irp
);
6043 // Save SRB address in next stack for port driver.
6046 irpStack
->Parameters
.Scsi
.Srb
= &srb
;
6049 // Set up IRP Address.
6052 srb
.OriginalRequest
= irp
;
6055 // Call the port driver with the request and wait for it to complete.
6058 status
= IoCallDriver(LowerDeviceObject
, irp
);
6059 if (status
== STATUS_PENDING
) {
6061 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
6062 status
= ioStatus
.Status
;
6066 // If this is a release request, then just decrement the reference count
6067 // and return. The status does not matter.
6072 // ObDereferenceObject(LowerDeviceObject);
6073 return STATUS_SUCCESS
;
6076 if (!NT_SUCCESS(status
)) {
6080 ASSERT(srb
.DataBuffer
!= NULL
);
6081 ASSERT(!TEST_FLAG(srb
.SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
6084 } // end ClassClaimDevice()
6086 /*++////////////////////////////////////////////////////////////////////////////
6088 ClassInternalIoControl()
6090 Routine Description:
6092 This routine passes internal device controls to the port driver.
6093 Internal device controls are used by higher level drivers both for ioctls
6094 and to pass through scsi requests.
6096 If the IoControlCode does not match any of the handled ioctls and is
6097 a valid system address then the request will be treated as an SRB and
6098 passed down to the lower driver. If the IoControlCode is not a valid
6099 system address the ioctl will be failed.
6101 Callers must therefore be extremely cautious to pass correct, initialized
6102 values to this function.
6106 DeviceObject - Supplies a pointer to the device object for this request.
6108 Irp - Supplies the Irp making the request.
6112 Returns back a STATUS_PENDING or a completion status.
6117 ClassInternalIoControl(
6118 IN PDEVICE_OBJECT DeviceObject
,
6122 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6124 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6125 PIO_STACK_LOCATION nextStack
= IoGetNextIrpStackLocation(Irp
);
6129 PSCSI_REQUEST_BLOCK srb
;
6131 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
6135 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
6137 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6139 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
6141 return STATUS_DEVICE_DOES_NOT_EXIST
;
6145 // Get a pointer to the SRB.
6148 srb
= irpStack
->Parameters
.Scsi
.Srb
;
6151 // Set the parameters in the next stack location.
6154 if(commonExtension
->IsFdo
) {
6155 nextStack
->Parameters
.Scsi
.Srb
= srb
;
6156 nextStack
->MajorFunction
= IRP_MJ_SCSI
;
6157 nextStack
->MinorFunction
= IRP_MN_SCSI_CLASS
;
6161 IoCopyCurrentIrpStackLocationToNext(Irp
);
6164 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6166 return IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
6167 } // end ClassInternalIoControl()
6169 /*++////////////////////////////////////////////////////////////////////////////
6171 ClassQueryTimeOutRegistryValue()
6173 Routine Description:
6175 This routine determines whether a reg key for a user-specified timeout
6176 value exists. This should be called at initialization time.
6180 DeviceObject - Pointer to the device object we are retrieving the timeout
6185 None, but it sets a new default timeout for a class of devices.
6190 ClassQueryTimeOutRegistryValue(
6191 IN PDEVICE_OBJECT DeviceObject
6195 // Find the appropriate reg. key
6198 PCLASS_DRIVER_EXTENSION
6199 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6200 CLASS_DRIVER_EXTENSION_KEY
);
6202 PUNICODE_STRING registryPath
= &(driverExtension
->RegistryPath
);
6204 PRTL_QUERY_REGISTRY_TABLE parameters
= NULL
;
6213 if (!registryPath
) {
6217 parameters
= ExAllocatePoolWithTag(NonPagedPool
,
6218 sizeof(RTL_QUERY_REGISTRY_TABLE
)*2,
6225 size
= registryPath
->MaximumLength
+ sizeof(WCHAR
);
6226 path
= ExAllocatePoolWithTag(NonPagedPool
, size
, '2BcS');
6229 ExFreePool(parameters
);
6233 RtlZeroMemory(path
,size
);
6234 RtlCopyMemory(path
, registryPath
->Buffer
, size
- sizeof(WCHAR
));
6238 // Check for the Timeout value.
6241 RtlZeroMemory(parameters
,
6242 (sizeof(RTL_QUERY_REGISTRY_TABLE
)*2));
6244 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
6245 parameters
[0].Name
= L
"TimeOutValue";
6246 parameters
[0].EntryContext
= &timeOut
;
6247 parameters
[0].DefaultType
= REG_DWORD
;
6248 parameters
[0].DefaultData
= &zero
;
6249 parameters
[0].DefaultLength
= sizeof(ULONG
);
6251 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
| RTL_REGISTRY_OPTIONAL
,
6257 if (!(NT_SUCCESS(status
))) {
6261 ExFreePool(parameters
);
6265 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
6271 } // end ClassQueryTimeOutRegistryValue()
6273 /*++////////////////////////////////////////////////////////////////////////////
6275 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
6277 Routine Description:
6279 This routine executes when the port driver has completed a check verify
6280 ioctl. It will set the status of the master Irp, copy the media change
6281 count and complete the request.
6285 Fdo - Supplies the functional device object which represents the logical unit.
6287 Irp - Supplies the Irp which has completed.
6298 ClassCheckVerifyComplete(
6299 IN PDEVICE_OBJECT Fdo
,
6304 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6305 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6311 originalIrp
= irpStack
->Parameters
.Others
.Argument1
;
6314 // Copy the media change count and status
6317 *((PULONG
) (originalIrp
->AssociatedIrp
.SystemBuffer
)) =
6318 fdoExtension
->MediaChangeCount
;
6320 DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
6321 "device %d is %lx - saved as %lx\n",
6322 fdoExtension
->DeviceNumber
,
6323 fdoExtension
->MediaChangeCount
,
6324 *((PULONG
) originalIrp
->AssociatedIrp
.SystemBuffer
)));
6326 originalIrp
->IoStatus
.Status
= Irp
->IoStatus
.Status
;
6327 originalIrp
->IoStatus
.Information
= sizeof(ULONG
);
6329 ClassReleaseRemoveLock(Fdo
, originalIrp
);
6330 ClassCompleteRequest(Fdo
, originalIrp
, IO_DISK_INCREMENT
);
6334 return STATUS_MORE_PROCESSING_REQUIRED
;
6336 } // end ClassCheckVerifyComplete()
6338 /*++////////////////////////////////////////////////////////////////////////////
6340 ClassGetDescriptor()
6342 Routine Description:
6344 This routine will perform a query for the specified property id and will
6345 allocate a non-paged buffer to store the data in. It is the responsibility
6346 of the caller to ensure that this buffer is freed.
6348 This routine must be run at IRQL_PASSIVE_LEVEL
6352 DeviceObject - the device to query
6353 DeviceInfo - a location to store a pointer to the buffer we allocate
6358 if status is unsuccessful *DeviceInfo will be set to NULL, else the
6359 buffer allocated on behalf of the caller.
6365 IN PDEVICE_OBJECT DeviceObject
,
6366 IN PSTORAGE_PROPERTY_ID PropertyId
,
6367 OUT PSTORAGE_DESCRIPTOR_HEADER
*Descriptor
6370 STORAGE_PROPERTY_QUERY query
;
6371 IO_STATUS_BLOCK ioStatus
;
6373 PSTORAGE_DESCRIPTOR_HEADER descriptor
= NULL
;
6381 // Set the passed-in descriptor pointer to NULL as default
6387 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6388 query
.PropertyId
= *PropertyId
;
6389 query
.QueryType
= PropertyStandardQuery
;
6392 // On the first pass we just want to get the first few
6393 // bytes of the descriptor so we can read it's size
6396 descriptor
= (PVOID
)&query
;
6398 ASSERT(sizeof(STORAGE_PROPERTY_QUERY
) >= (sizeof(ULONG
)*2));
6400 ClassSendDeviceIoControlSynchronous(
6401 IOCTL_STORAGE_QUERY_PROPERTY
,
6404 sizeof(STORAGE_PROPERTY_QUERY
),
6410 if(!NT_SUCCESS(ioStatus
.Status
)) {
6412 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6413 "query properties #1\n", ioStatus
.Status
));
6414 return ioStatus
.Status
;
6417 if (descriptor
->Size
== 0) {
6420 // This DebugPrint is to help third-party driver writers
6423 DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
6424 "%x\n", ioStatus
.Status
));
6425 return STATUS_UNSUCCESSFUL
;
6430 // This time we know how much data there is so we can
6431 // allocate a buffer of the correct size
6434 length
= descriptor
->Size
;
6436 descriptor
= ExAllocatePoolWithTag(NonPagedPool
, length
, '4BcS');
6438 if(descriptor
== NULL
) {
6440 DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
6441 "(%d bytes)\n", length
));
6442 return STATUS_INSUFFICIENT_RESOURCES
;
6446 // setup the query again, as it was overwritten above
6449 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6450 query
.PropertyId
= *PropertyId
;
6451 query
.QueryType
= PropertyStandardQuery
;
6454 // copy the input to the new outputbuffer
6457 RtlCopyMemory(descriptor
,
6459 sizeof(STORAGE_PROPERTY_QUERY
)
6462 ClassSendDeviceIoControlSynchronous(
6463 IOCTL_STORAGE_QUERY_PROPERTY
,
6466 sizeof(STORAGE_PROPERTY_QUERY
),
6472 if(!NT_SUCCESS(ioStatus
.Status
)) {
6474 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6475 "query properties #1\n", ioStatus
.Status
));
6476 ExFreePool(descriptor
);
6477 return ioStatus
.Status
;
6481 // return the memory we've allocated to the caller
6484 *Descriptor
= descriptor
;
6485 return ioStatus
.Status
;
6486 } // end ClassGetDescriptor()
6488 /*++////////////////////////////////////////////////////////////////////////////
6490 ClassSignalCompletion()
6492 Routine Description:
6494 This completion routine will signal the event given as context and then
6495 return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
6496 the responsibility of the routine waiting on the event to complete the
6497 request and free the event.
6501 DeviceObject - a pointer to the device object
6503 Irp - a pointer to the irp
6505 Event - a pointer to the event to signal
6509 STATUS_MORE_PROCESSING_REQUIRED
6514 ClassSignalCompletion(
6515 IN PDEVICE_OBJECT DeviceObject
,
6520 PKEVENT Event
= Context
;
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
);
6564 // If there's already an enumeration in progress then don't start another
6568 if(InterlockedIncrement((PLONG
)&fdoExtension
->EnumerationInterlock
) == 1) {
6569 driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6572 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
6574 Irp
->IoStatus
.Status
= ClassRetrieveDeviceRelations(
6577 (PDEVICE_RELATIONS
*)&Irp
->IoStatus
.Information
);
6578 InterlockedDecrement((PLONG
)&fdoExtension
->EnumerationInterlock
);
6580 return Irp
->IoStatus
.Status
;
6581 } // end ClassPnpQueryFdoRelations()
6583 /*++////////////////////////////////////////////////////////////////////////////
6585 ClassMarkChildrenMissing()
6587 Routine Description:
6589 This routine will call ClassMarkChildMissing() for all children.
6590 It acquires the ChildLock before calling ClassMarkChildMissing().
6594 Fdo - the "bus's" device object, such as the disk FDO for non-removable
6595 disks with multiple partitions.
6604 ClassMarkChildrenMissing(
6605 IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
6608 PCOMMON_DEVICE_EXTENSION commonExtension
= &(Fdo
->CommonExtension
);
6609 PPHYSICAL_DEVICE_EXTENSION nextChild
= commonExtension
->ChildList
;
6613 ClassAcquireChildLock(Fdo
);
6616 PPHYSICAL_DEVICE_EXTENSION tmpChild
;
6619 * ClassMarkChildMissing will also dequeue the child extension.
6620 * So get the next pointer before calling ClassMarkChildMissing.
6622 tmpChild
= nextChild
;
6623 nextChild
= tmpChild
->CommonExtension
.ChildList
;
6624 ClassMarkChildMissing(tmpChild
, FALSE
);
6626 ClassReleaseChildLock(Fdo
);
6628 } // end ClassMarkChildrenMissing()
6630 /*++////////////////////////////////////////////////////////////////////////////
6632 ClassMarkChildMissing()
6634 Routine Description:
6636 This routine will make an active child "missing." If the device has never
6637 been enumerated then it will be deleted on the spot. If the device has
6638 not been enumerated then it will be marked as missing so that we can
6639 not report it in the next device enumeration.
6643 Child - the child device to be marked as missing.
6645 AcquireChildLock - TRUE if the child lock should be acquired before removing
6646 the missing child. FALSE if the child lock is already
6647 acquired by this thread.
6651 returns whether or not the child device object has previously been reported
6657 ClassMarkChildMissing(
6658 IN PPHYSICAL_DEVICE_EXTENSION Child
,
6659 IN BOOLEAN AcquireChildLock
6662 BOOLEAN returnValue
= Child
->IsEnumerated
;
6665 ASSERT_PDO(Child
->DeviceObject
);
6667 Child
->IsMissing
= TRUE
;
6670 // Make sure this child is not in the active list.
6673 ClassRemoveChild(Child
->CommonExtension
.PartitionZeroExtension
,
6677 if(Child
->IsEnumerated
== FALSE
) {
6678 ClassRemoveDevice(Child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
6682 } // end ClassMarkChildMissing()
6684 /*++////////////////////////////////////////////////////////////////////////////
6686 ClassRetrieveDeviceRelations()
6688 Routine Description:
6690 This routine will allocate a buffer to hold the specified list of
6691 relations. It will then fill in the list with referenced device pointers
6692 and will return the request.
6696 Fdo - pointer to the FDO being queried
6698 RelationType - what type of relations are being queried
6700 DeviceRelations - a location to store a pointer to the response
6709 ClassRetrieveDeviceRelations(
6710 IN PDEVICE_OBJECT Fdo
,
6711 IN DEVICE_RELATION_TYPE RelationType
,
6712 OUT PDEVICE_RELATIONS
*DeviceRelations
6715 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6720 PPHYSICAL_DEVICE_EXTENSION nextChild
;
6722 ULONG relationsSize
;
6723 PDEVICE_RELATIONS deviceRelations
= NULL
;
6729 ClassAcquireChildLock(fdoExtension
);
6731 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6734 // Count the number of PDO's attached to this disk
6737 while(nextChild
!= NULL
) {
6738 PCOMMON_DEVICE_EXTENSION commonExtension
;
6740 commonExtension
= &(nextChild
->CommonExtension
);
6742 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6743 (nextChild
->IsMissing
== FALSE
));
6745 nextChild
= commonExtension
->ChildList
;
6750 relationsSize
= (sizeof(DEVICE_RELATIONS
) +
6751 (count
* sizeof(PDEVICE_OBJECT
)));
6753 deviceRelations
= ExAllocatePoolWithTag(PagedPool
, relationsSize
, '5BcS');
6755 if(deviceRelations
== NULL
) {
6757 DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
6758 "%d bytes for device relations\n", relationsSize
));
6760 ClassReleaseChildLock(fdoExtension
);
6762 return STATUS_INSUFFICIENT_RESOURCES
;
6765 RtlZeroMemory(deviceRelations
, relationsSize
);
6767 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6770 while(nextChild
!= NULL
) {
6771 PCOMMON_DEVICE_EXTENSION commonExtension
;
6773 commonExtension
= &(nextChild
->CommonExtension
);
6775 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6776 (nextChild
->IsMissing
== FALSE
));
6778 deviceRelations
->Objects
[i
--] = nextChild
->DeviceObject
;
6780 status
= ObReferenceObjectByPointer(
6781 nextChild
->DeviceObject
,
6785 ASSERT(NT_SUCCESS(status
));
6787 nextChild
->IsEnumerated
= TRUE
;
6788 nextChild
= commonExtension
->ChildList
;
6791 ASSERTMSG("Child list has changed: ", i
== -1);
6793 deviceRelations
->Count
= count
;
6794 *DeviceRelations
= deviceRelations
;
6795 ClassReleaseChildLock(fdoExtension
);
6796 return STATUS_SUCCESS
;
6797 } // end ClassRetrieveDeviceRelations()
6799 /*++////////////////////////////////////////////////////////////////////////////
6803 Routine Description:
6805 This routine will call into the driver to retrieve a copy of one of it's
6810 Pdo - a pointer to the pdo being queried
6812 IdType - which type of id string is being queried
6814 IdString - an allocated unicode string structure which the driver
6825 IN PDEVICE_OBJECT Pdo
,
6826 IN BUS_QUERY_ID_TYPE IdType
,
6827 IN PUNICODE_STRING IdString
6830 PCLASS_DRIVER_EXTENSION
6831 driverExtension
= IoGetDriverObjectExtension(Pdo
->DriverObject
,
6832 CLASS_DRIVER_EXTENSION_KEY
);
6835 ASSERT(driverExtension
->InitData
.ClassQueryId
);
6839 return driverExtension
->InitData
.ClassQueryId( Pdo
, IdType
, IdString
);
6840 } // end ClassGetPdoId()
6842 /*++////////////////////////////////////////////////////////////////////////////
6844 ClassQueryPnpCapabilities()
6846 Routine Description:
6848 This routine will call into the class driver to retrieve it's pnp
6853 PhysicalDeviceObject - The physical device object to retrieve properties
6863 ClassQueryPnpCapabilities(
6864 IN PDEVICE_OBJECT DeviceObject
,
6865 IN PDEVICE_CAPABILITIES Capabilities
6868 PCLASS_DRIVER_EXTENSION driverExtension
=
6869 ClassGetDriverExtension(DeviceObject
->DriverObject
);
6870 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6872 PCLASS_QUERY_PNP_CAPABILITIES queryRoutine
= NULL
;
6876 ASSERT(DeviceObject
);
6877 ASSERT(Capabilities
);
6879 if(commonExtension
->IsFdo
) {
6880 queryRoutine
= driverExtension
->InitData
.FdoData
.ClassQueryPnpCapabilities
;
6882 queryRoutine
= driverExtension
->InitData
.PdoData
.ClassQueryPnpCapabilities
;
6886 return queryRoutine(DeviceObject
,
6889 return STATUS_NOT_IMPLEMENTED
;
6891 } // end ClassQueryPnpCapabilities()
6893 /*++////////////////////////////////////////////////////////////////////////////
6895 ClassInvalidateBusRelations()
6897 Routine Description:
6899 This routine re-enumerates the devices on the "bus". It will call into
6900 the driver's ClassEnumerate routine to update the device objects
6901 immediately. It will then schedule a bus re-enumeration for pnp by calling
6902 IoInvalidateDeviceRelations.
6906 Fdo - a pointer to the functional device object for this bus
6915 ClassInvalidateBusRelations(
6916 IN PDEVICE_OBJECT Fdo
6919 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6920 PCLASS_DRIVER_EXTENSION
6921 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6922 CLASS_DRIVER_EXTENSION_KEY
);
6924 NTSTATUS status
= STATUS_SUCCESS
;
6929 ASSERT(driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
6931 if(InterlockedIncrement((PLONG
)&fdoExtension
->EnumerationInterlock
) == 1) {
6932 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6934 InterlockedDecrement((PLONG
)&fdoExtension
->EnumerationInterlock
);
6936 if(!NT_SUCCESS(status
)) {
6938 DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
6939 "returned %lx\n", status
));
6942 IoInvalidateDeviceRelations(fdoExtension
->LowerPdo
, BusRelations
);
6945 } // end ClassInvalidateBusRelations()
6947 /*++////////////////////////////////////////////////////////////////////////////
6949 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
6951 Routine Description:
6953 This routine is called to handle the "removal" of a device. It will
6954 forward the request downwards if necessary, call into the driver
6955 to release any necessary resources (memory, events, etc) and then
6956 will delete the device object.
6960 DeviceObject - a pointer to the device object being removed
6962 RemoveType - indicates what type of remove this is (regular or surprise).
6972 IN PDEVICE_OBJECT DeviceObject
,
6976 PCLASS_DRIVER_EXTENSION
6977 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6978 CLASS_DRIVER_EXTENSION_KEY
);
6979 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6980 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
6981 PCLASS_WMI_INFO classWmiInfo
;
6982 BOOLEAN proceedWithRemove
= TRUE
;
6987 commonExtension
->IsRemoved
= REMOVE_PENDING
;
6990 * Deregister from WMI.
6992 classWmiInfo
= commonExtension
->IsFdo
?
6993 &driverExtension
->InitData
.FdoData
.ClassWmiInfo
:
6994 &driverExtension
->InitData
.PdoData
.ClassWmiInfo
;
6995 if (classWmiInfo
->GuidRegInfo
){
6996 status
= IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_DEREGISTER
);
6997 DBGTRACE(ClassDebugInfo
, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject
, status
));
7001 * If we exposed a "shingle" (a named device interface openable by CreateFile)
7002 * then delete it now.
7004 if (commonExtension
->MountedDeviceInterfaceName
.Buffer
){
7005 IoSetDeviceInterfaceState(&commonExtension
->MountedDeviceInterfaceName
, FALSE
);
7006 RtlFreeUnicodeString(&commonExtension
->MountedDeviceInterfaceName
);
7007 RtlInitUnicodeString(&commonExtension
->MountedDeviceInterfaceName
, NULL
);
7011 // If this is a surprise removal we leave the device around - which means
7012 // we don't have to (or want to) drop the remove lock and wait for pending
7013 // requests to complete.
7016 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7019 // Release the lock we acquired when the device object was created.
7022 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7024 DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
7025 commonExtension
->RemoveLock
));
7027 KeWaitForSingleObject(&commonExtension
->RemoveEvent
,
7033 DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject
));
7035 if(commonExtension
->IsFdo
) {
7037 DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
7038 "remove request.\n", DeviceObject
));
7042 PPHYSICAL_DEVICE_EXTENSION pdoExtension
= DeviceObject
->DeviceExtension
;
7044 if (pdoExtension
->IsMissing
){
7046 * The child partition PDO is missing, so we are going to go ahead
7047 * and delete it for the remove.
7049 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject
));
7053 * We got a remove for a child partition PDO which is not actually missing.
7054 * So we will NOT actually delete it.
7056 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject
));
7059 // Reacquire the remove lock for the next time this comes around.
7062 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7065 // the device wasn't missing so it's not really been removed.
7068 commonExtension
->IsRemoved
= NO_REMOVE
;
7070 IoInvalidateDeviceRelations(
7071 commonExtension
->PartitionZeroExtension
->LowerPdo
,
7074 proceedWithRemove
= FALSE
;
7080 if (proceedWithRemove
){
7083 * Call the class driver's remove handler.
7084 * All this is supposed to do is clean up its data and device interfaces.
7086 ASSERT(commonExtension
->DevInfo
->ClassRemoveDevice
);
7087 status
= commonExtension
->DevInfo
->ClassRemoveDevice(DeviceObject
, RemoveType
);
7088 ASSERT(NT_SUCCESS(status
));
7089 status
= STATUS_SUCCESS
;
7091 if (commonExtension
->IsFdo
){
7092 //PDEVICE_OBJECT pdo;
7093 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
7095 ClasspDisableTimer(fdoExtension
->DeviceObject
);
7097 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7099 PPHYSICAL_DEVICE_EXTENSION child
;
7102 // Cleanup the media detection resources now that the class driver
7103 // has stopped it's timer (if any) and we can be sure they won't
7104 // call us to do detection again.
7107 ClassCleanupMediaChangeDetection(fdoExtension
);
7110 // Cleanup any Failure Prediction stuff
7112 if (fdoExtension
->FailurePredictionInfo
) {
7113 ExFreePool(fdoExtension
->FailurePredictionInfo
);
7114 fdoExtension
->FailurePredictionInfo
= NULL
;
7118 * Ordinarily all child PDOs will be removed by the time
7119 * that the parent gets the REMOVE_DEVICE.
7120 * However, if a child PDO has been created but has not
7121 * been announced in a QueryDeviceRelations, then it is
7122 * just a private data structure unknown to pnp, and we have
7123 * to delete it ourselves.
7125 ClassAcquireChildLock(fdoExtension
);
7126 while ((child
= ClassRemoveChild(fdoExtension
, NULL
, FALSE
))){
7129 // Yank the pdo. This routine will unlink the device from the
7130 // pdo list so NextPdo will point to the next one when it's
7133 child
->IsMissing
= TRUE
;
7134 ClassRemoveDevice(child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
7136 ClassReleaseChildLock(fdoExtension
);
7138 else if (RemoveType
== IRP_MN_SURPRISE_REMOVAL
){
7140 * This is a surprise-remove on the parent FDO.
7141 * We will mark the child PDOs as missing so that they
7142 * will actually get deleted when they get a REMOVE_DEVICE.
7144 ClassMarkChildrenMissing(fdoExtension
);
7147 ClasspFreeReleaseRequest(DeviceObject
);
7149 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7152 // Free FDO-specific data structs
7154 if (fdoExtension
->PrivateFdoData
){
7156 DestroyAllTransferPackets(DeviceObject
);
7158 ExFreePool(fdoExtension
->PrivateFdoData
);
7159 fdoExtension
->PrivateFdoData
= NULL
;
7162 if (commonExtension
->DeviceName
.Buffer
) {
7163 ExFreePool(commonExtension
->DeviceName
.Buffer
);
7164 RtlInitUnicodeString(&commonExtension
->DeviceName
, NULL
);
7167 if (fdoExtension
->AdapterDescriptor
) {
7168 ExFreePool(fdoExtension
->AdapterDescriptor
);
7169 fdoExtension
->AdapterDescriptor
= NULL
;
7172 if (fdoExtension
->DeviceDescriptor
) {
7173 ExFreePool(fdoExtension
->DeviceDescriptor
);
7174 fdoExtension
->DeviceDescriptor
= NULL
;
7178 // Detach our device object from the stack - there's no reason
7179 // to hold off our cleanup any longer.
7182 IoDetachDevice(lowerDeviceObject
);
7187 * This is a child partition PDO.
7188 * We have already determined that it was previously marked
7189 * as missing. So if this is a REMOVE_DEVICE, we will actually
7192 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7193 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
7194 commonExtension
->PartitionZeroExtension
;
7195 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
7196 (PPHYSICAL_DEVICE_EXTENSION
) commonExtension
;
7199 // See if this device is in the child list (if this was a surprise
7200 // removal it might be) and remove it.
7203 ClassRemoveChild(fdoExtension
, pdoExtension
, TRUE
);
7207 commonExtension
->PartitionLength
.QuadPart
= 0;
7209 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7210 IoDeleteDevice(DeviceObject
);
7214 return STATUS_SUCCESS
;
7215 } // end ClassRemoveDevice()
7217 /*++////////////////////////////////////////////////////////////////////////////
7219 ClassGetDriverExtension()
7221 Routine Description:
7223 This routine will return the classpnp's driver extension.
7227 DriverObject - the driver object for which to get classpnp's extension
7231 Either NULL if none, or a pointer to the driver extension
7234 PCLASS_DRIVER_EXTENSION
7236 ClassGetDriverExtension(
7237 IN PDRIVER_OBJECT DriverObject
7240 return IoGetDriverObjectExtension(DriverObject
, CLASS_DRIVER_EXTENSION_KEY
);
7241 } // end ClassGetDriverExtension()
7243 /*++////////////////////////////////////////////////////////////////////////////
7247 Routine Description:
7249 This routine wraps the class driver's start io routine. If the device
7250 is being removed it will complete any requests with
7251 STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
7263 IN PDEVICE_OBJECT DeviceObject
,
7267 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7270 // We're already holding the remove lock so just check the variable and
7271 // see what's going on.
7274 if(commonExtension
->IsRemoved
) {
7276 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
7278 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7279 ClassReleaseRemoveLock(DeviceObject
, Irp
);
7280 ClassCompleteRequest(DeviceObject
, Irp
, IO_DISK_INCREMENT
);
7281 IoStartNextPacket(DeviceObject
, FALSE
);
7282 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7286 commonExtension
->DriverExtension
->InitData
.ClassStartIo(
7291 } // ClasspStartIo()
7293 /*++////////////////////////////////////////////////////////////////////////////
7295 ClassUpdateInformationInRegistry()
7297 Routine Description:
7299 This routine has knowledge about the layout of the device map information
7300 in the registry. It will update this information to include a value
7301 entry specifying the dos device name that is assumed to get assigned
7302 to this NT device name. For more information on this assigning of the
7303 dos device name look in the drive support routine in the hal that assigns
7306 Since some versions of some device's firmware did not work and some
7307 vendors did not bother to follow the specification, the entire inquiry
7308 information must also be stored in the registry so than someone can
7309 figure out the firmware version.
7313 DeviceObject - A pointer to the device object for the tape device.
7322 ClassUpdateInformationInRegistry(
7323 IN PDEVICE_OBJECT Fdo
,
7324 IN PCHAR DeviceName
,
7325 IN ULONG DeviceNumber
,
7326 IN PINQUIRYDATA InquiryData
,
7327 IN ULONG InquiryDataLength
7331 SCSI_ADDRESS scsiAddress
;
7332 OBJECT_ATTRIBUTES objectAttributes
;
7335 UNICODE_STRING unicodeName
;
7336 UNICODE_STRING unicodeRegistryPath
;
7337 UNICODE_STRING unicodeData
;
7339 IO_STATUS_BLOCK ioStatus
;
7347 RtlZeroMemory(&unicodeName
, sizeof(UNICODE_STRING
));
7348 RtlZeroMemory(&unicodeData
, sizeof(UNICODE_STRING
));
7349 RtlZeroMemory(&unicodeRegistryPath
, sizeof(UNICODE_STRING
));
7354 // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
7357 ClassSendDeviceIoControlSynchronous(
7358 IOCTL_SCSI_GET_ADDRESS
,
7362 sizeof(SCSI_ADDRESS
),
7367 if (!NT_SUCCESS(ioStatus
.Status
)) {
7369 status
= ioStatus
.Status
;
7371 "UpdateInformationInRegistry: Get Address failed %lx\n",
7378 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
7379 scsiAddress
.PortNumber
,
7381 scsiAddress
.TargetId
,
7387 // Allocate a buffer for the reg. spooge.
7390 buffer
= ExAllocatePoolWithTag(PagedPool
, 1024, '6BcS');
7392 if (buffer
== NULL
) {
7395 // There is not return value for this. Since this is done at
7396 // claim device time (currently only system initialization) getting
7397 // the registry information correct will be the least of the worries.
7404 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
7405 scsiAddress
.PortNumber
,
7407 scsiAddress
.TargetId
,
7410 RtlInitString(&string
, buffer
);
7412 status
= RtlAnsiStringToUnicodeString(&unicodeRegistryPath
,
7416 if (!NT_SUCCESS(status
)) {
7421 // Open the registry key for the scsi information for this
7422 // scsibus, target, lun.
7425 InitializeObjectAttributes(&objectAttributes
,
7426 &unicodeRegistryPath
,
7427 OBJ_CASE_INSENSITIVE
,
7431 status
= ZwOpenKey(&targetKey
,
7432 KEY_READ
| KEY_WRITE
,
7435 if (!NT_SUCCESS(status
)) {
7440 // Now construct and attempt to create the registry value
7441 // specifying the device name in the appropriate place in the
7445 RtlInitUnicodeString(&unicodeName
, L
"DeviceName");
7447 sprintf(buffer
, "%s%lu", DeviceName
, DeviceNumber
);
7448 RtlInitString(&string
, buffer
);
7449 status
= RtlAnsiStringToUnicodeString(&unicodeData
,
7452 if (NT_SUCCESS(status
)) {
7453 status
= ZwSetValueKey(targetKey
,
7458 unicodeData
.Length
);
7462 // if they sent in data, update the registry
7465 if (InquiryDataLength
) {
7467 ASSERT(InquiryData
);
7469 RtlInitUnicodeString(&unicodeName
, L
"InquiryData");
7470 status
= ZwSetValueKey(targetKey
,
7478 // that's all, except to clean up.
7482 if (unicodeData
.Buffer
) {
7483 RtlFreeUnicodeString(&unicodeData
);
7485 if (unicodeRegistryPath
.Buffer
) {
7486 RtlFreeUnicodeString(&unicodeRegistryPath
);
7497 } // end ClassUpdateInformationInRegistry()
7499 /*++////////////////////////////////////////////////////////////////////////////
7501 ClasspSendSynchronousCompletion()
7503 Routine Description:
7505 This completion routine will set the user event in the irp after
7506 freeing the irp and the associated MDL (if any).
7510 DeviceObject - the device object which requested the completion routine
7512 Irp - the irp being completed
7518 STATUS_MORE_PROCESSING_REQUIRED
7523 ClasspSendSynchronousCompletion(
7524 IN PDEVICE_OBJECT DeviceObject
,
7529 DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
7530 DeviceObject
, Irp
, Context
));
7532 // First set the status and information fields in the io status block
7533 // provided by the caller.
7536 *(Irp
->UserIosb
) = Irp
->IoStatus
;
7539 // Unlock the pages for the data buffer.
7542 if(Irp
->MdlAddress
) {
7543 MmUnlockPages(Irp
->MdlAddress
);
7544 IoFreeMdl(Irp
->MdlAddress
);
7548 // Signal the caller's event.
7551 KeSetEvent(Irp
->UserEvent
, IO_NO_INCREMENT
, FALSE
);
7554 // Free the MDL and the IRP.
7559 return STATUS_MORE_PROCESSING_REQUIRED
;
7560 } // end ClasspSendSynchronousCompletion()
7564 ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
7569 ClasspRegisterMountedDeviceInterface(
7570 IN PDEVICE_OBJECT DeviceObject
7574 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7575 BOOLEAN isFdo
= commonExtension
->IsFdo
;
7578 UNICODE_STRING interfaceName
;
7584 PFUNCTIONAL_DEVICE_EXTENSION functionalExtension
;
7586 functionalExtension
=
7587 (PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
;
7588 pdo
= functionalExtension
->LowerPdo
;
7593 status
= IoRegisterDeviceInterface(
7595 &MOUNTDEV_MOUNTED_DEVICE_GUID
,
7600 if(NT_SUCCESS(status
)) {
7603 // Copy the interface name before setting the interface state - the
7604 // name is needed by the components we notify.
7607 commonExtension
->MountedDeviceInterfaceName
= interfaceName
;
7608 status
= IoSetDeviceInterfaceState(&interfaceName
, TRUE
);
7610 if(!NT_SUCCESS(status
)) {
7611 RtlFreeUnicodeString(&interfaceName
);
7615 if(!NT_SUCCESS(status
)) {
7616 RtlInitUnicodeString(&(commonExtension
->MountedDeviceInterfaceName
),
7620 } // end ClasspRegisterMountedDeviceInterface()
7622 /*++////////////////////////////////////////////////////////////////////////////
7624 ClassSendDeviceIoControlSynchronous()
7626 Routine Description:
7628 This routine is based upon IoBuildDeviceIoControlRequest(). It has been
7629 modified to reduce code and memory by not double-buffering the io, using
7630 the same buffer for both input and output, allocating and deallocating
7631 the mdl on behalf of the caller, and waiting for the io to complete.
7633 This routine also works around the rare cases in which APC's are disabled.
7634 Since IoBuildDeviceIoControl() used APC's to signal completion, this had
7635 led to a number of difficult-to-detect hangs, where the irp was completed,
7636 but the event passed to IoBuild..() was still being waited upon by the
7641 IoControlCode - the IOCTL to send
7643 TargetDeviceObject - the device object that should handle the ioctl
7645 Buffer - the input and output buffer, or NULL if no input/output
7647 InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
7649 OutputBufferLength - the number of bytes to be filled in upon success
7651 InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
7653 IoStatus - the status block that contains the results of the operation
7660 ClassSendDeviceIoControlSynchronous(
7661 IN ULONG IoControlCode
,
7662 IN PDEVICE_OBJECT TargetDeviceObject
,
7663 IN OUT PVOID Buffer OPTIONAL
,
7664 IN ULONG InputBufferLength
,
7665 IN ULONG OutputBufferLength
,
7666 IN BOOLEAN InternalDeviceIoControl
,
7667 OUT PIO_STATUS_BLOCK IoStatus
7671 PIO_STACK_LOCATION irpSp
;
7677 method
= IoControlCode
& 3;
7680 #if DBG // Begin Argument Checking (nop in fre version)
7682 ASSERT(ARGUMENT_PRESENT(IoStatus
));
7684 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7685 ASSERT(ARGUMENT_PRESENT(Buffer
));
7688 ASSERT(!ARGUMENT_PRESENT(Buffer
));
7693 // Begin by allocating the IRP for this request. Do not charge quota to
7694 // the current process for this IRP.
7697 irp
= IoAllocateIrp(TargetDeviceObject
->StackSize
, FALSE
);
7699 (*IoStatus
).Information
= 0;
7700 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7705 // Get a pointer to the stack location of the first driver which will be
7706 // invoked. This is where the function codes and the parameters are set.
7709 irpSp
= IoGetNextIrpStackLocation(irp
);
7712 // Set the major function code based on the type of device I/O control
7713 // function the caller has specified.
7716 if (InternalDeviceIoControl
) {
7717 irpSp
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
7719 irpSp
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
7723 // Copy the caller's parameters to the service-specific portion of the
7724 // IRP for those parameters that are the same for all four methods.
7727 irpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
= OutputBufferLength
;
7728 irpSp
->Parameters
.DeviceIoControl
.InputBufferLength
= InputBufferLength
;
7729 irpSp
->Parameters
.DeviceIoControl
.IoControlCode
= IoControlCode
;
7732 // Get the method bits from the I/O control code to determine how the
7733 // buffers are to be passed to the driver.
7738 case METHOD_BUFFERED
: {
7739 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7741 irp
->AssociatedIrp
.SystemBuffer
=
7742 ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
7743 max(InputBufferLength
, OutputBufferLength
),
7744 CLASS_TAG_DEVICE_CONTROL
7747 if (irp
->AssociatedIrp
.SystemBuffer
== NULL
) {
7749 (*IoStatus
).Information
= 0;
7750 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7754 if (InputBufferLength
!= 0) {
7755 RtlCopyMemory(irp
->AssociatedIrp
.SystemBuffer
,
7759 } // end of buffering
7761 irp
->UserBuffer
= Buffer
;
7766 case METHOD_IN_DIRECT
:
7767 case METHOD_OUT_DIRECT
: {
7770 if (InputBufferLength
!= 0) {
7771 irp
->AssociatedIrp
.SystemBuffer
= Buffer
;
7774 if (OutputBufferLength
!= 0) {
7776 irp
->MdlAddress
= IoAllocateMdl(Buffer
,
7781 if (irp
->MdlAddress
== NULL
) {
7783 (*IoStatus
).Information
= 0;
7784 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7788 if (method
== METHOD_IN_DIRECT
) {
7789 MmProbeAndLockPages(irp
->MdlAddress
,
7792 } else if (method
== METHOD_OUT_DIRECT
) {
7793 MmProbeAndLockPages(irp
->MdlAddress
,
7797 ASSERT(!"If other methods reach here, code is out of date");
7804 case METHOD_NEITHER
: {
7806 ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
7807 IoStatus
->Information
= 0;
7808 IoStatus
->Status
= STATUS_NOT_SUPPORTED
;
7812 } // end of switch(method)
7814 irp
->Tail
.Overlay
.Thread
= PsGetCurrentThread();
7817 // send the irp synchronously
7820 ClassSendIrpSynchronous(TargetDeviceObject
, irp
);
7823 // copy the iostatus block for the caller
7826 *IoStatus
= irp
->IoStatus
;
7829 // free any allocated resources
7833 case METHOD_BUFFERED
: {
7835 ASSERT(irp
->UserBuffer
== Buffer
);
7838 // first copy the buffered result, if any
7839 // Note that there are no security implications in
7840 // not checking for success since only drivers can
7841 // call into this routine anyways...
7844 if (OutputBufferLength
!= 0) {
7845 RtlCopyMemory(Buffer
, // irp->UserBuffer
7846 irp
->AssociatedIrp
.SystemBuffer
,
7852 // then free the memory allocated to buffer the io
7855 if ((InputBufferLength
!=0) || (OutputBufferLength
!= 0)) {
7856 ExFreePool(irp
->AssociatedIrp
.SystemBuffer
);
7857 irp
->AssociatedIrp
.SystemBuffer
= NULL
;
7862 case METHOD_IN_DIRECT
:
7863 case METHOD_OUT_DIRECT
: {
7866 // we alloc a mdl if there is an output buffer specified
7867 // free it here after unlocking the pages
7870 if (OutputBufferLength
!= 0) {
7871 ASSERT(irp
->MdlAddress
!= NULL
);
7872 MmUnlockPages(irp
->MdlAddress
);
7873 IoFreeMdl(irp
->MdlAddress
);
7874 irp
->MdlAddress
= (PMDL
) NULL
;
7879 case METHOD_NEITHER
: {
7880 ASSERT(!"Code is out of date");
7886 // we always have allocated an irp. free it here.
7893 // return the io status block's status to the caller
7897 } // end ClassSendDeviceIoControlSynchronous()
7899 /*++////////////////////////////////////////////////////////////////////////////
7901 ClassForwardIrpSynchronous()
7903 Routine Description:
7905 Forwards a given irp to the next lower device object.
7909 CommonExtension - the common class extension
7911 Irp - the request to forward down the stack
7918 ClassForwardIrpSynchronous(
7919 IN PCOMMON_DEVICE_EXTENSION CommonExtension
,
7923 IoCopyCurrentIrpStackLocationToNext(Irp
);
7924 return ClassSendIrpSynchronous(CommonExtension
->LowerDeviceObject
, Irp
);
7925 } // end ClassForwardIrpSynchronous()
7927 /*++////////////////////////////////////////////////////////////////////////////
7929 ClassSendIrpSynchronous()
7931 Routine Description:
7933 This routine sends the given irp to the given device object, and waits for
7934 it to complete. On debug versions, will print out a debug message and
7935 optionally assert for "lost" irps based upon classpnp's globals
7939 TargetDeviceObject - the device object to handle this irp
7941 Irp - the request to be sent
7948 ClassSendIrpSynchronous(
7949 IN PDEVICE_OBJECT TargetDeviceObject
,
7956 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL
);
7957 ASSERT(TargetDeviceObject
!= NULL
);
7958 ASSERT(Irp
!= NULL
);
7959 ASSERT(Irp
->StackCount
>= TargetDeviceObject
->StackSize
);
7962 // ISSUE-2000/02/20-henrygab What if APCs are disabled?
7963 // May need to enter critical section before IoCallDriver()
7964 // until the event is hit?
7967 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
7968 IoSetCompletionRoutine(Irp
, ClassSignalCompletion
, &event
,
7971 status
= IoCallDriver(TargetDeviceObject
, Irp
);
7973 if (status
== STATUS_PENDING
) {
7976 LARGE_INTEGER timeout
;
7978 timeout
.QuadPart
= (LONGLONG
)(-1 * 10 * 1000 * (LONGLONG
)1000 *
7979 ClasspnpGlobals
.SecondsToWaitForIrps
);
7982 status
= KeWaitForSingleObject(&event
,
7989 if (status
== STATUS_TIMEOUT
) {
7992 // This DebugPrint should almost always be investigated by the
7993 // party who sent the irp and/or the current owner of the irp.
7994 // Synchronous Irps should not take this long (currently 30
7995 // seconds) without good reason. This points to a potentially
7996 // serious problem in the underlying device stack.
7999 DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
8000 "complete within %x seconds\n",
8001 TargetDeviceObject
, Irp
,
8002 ClasspnpGlobals
.SecondsToWaitForIrps
8005 if (ClasspnpGlobals
.BreakOnLostIrps
!= 0) {
8006 ASSERT(!" - Irp failed to complete within 30 seconds - ");
8011 } while (status
==STATUS_TIMEOUT
);
8013 KeWaitForSingleObject(&event
,
8020 status
= Irp
->IoStatus
.Status
;
8024 } // end ClassSendIrpSynchronous()
8026 /*++////////////////////////////////////////////////////////////////////////////
8030 Routine Description:
8032 This routine returns the current VPB (Volume Parameter Block) for the
8033 given device object.
8034 The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
8035 of DEVICE_OBJECT; hence this exported function.
8039 DeviceObject - the device to get the VPB for
8043 the VPB, or NULL if none.
8049 IN PDEVICE_OBJECT DeviceObject
8052 return DeviceObject
->Vpb
;
8053 } // end ClassGetVpb()
8057 ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
8062 ClasspAllocateReleaseRequest(
8063 IN PDEVICE_OBJECT Fdo
8066 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8067 //PIO_STACK_LOCATION irpStack;
8069 KeInitializeSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
));
8071 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8072 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8073 fdoExtension
->ReleaseQueueIrpFromPool
= FALSE
;
8076 // The class driver is responsible for allocating a properly sized irp,
8077 // or ClassReleaseQueue will attempt to do it on the first error.
8080 fdoExtension
->ReleaseQueueIrp
= NULL
;
8083 // Write length to SRB.
8086 fdoExtension
->ReleaseQueueSrb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
8088 return STATUS_SUCCESS
;
8089 } // end ClasspAllocateReleaseRequest()
8093 ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
8098 ClasspFreeReleaseRequest(
8099 IN PDEVICE_OBJECT Fdo
8102 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8105 ASSERT(fdoExtension
->CommonExtension
.IsRemoved
!= NO_REMOVE
);
8108 // free anything the driver allocated
8111 if (fdoExtension
->ReleaseQueueIrp
) {
8112 if (fdoExtension
->ReleaseQueueIrpFromPool
) {
8113 ExFreePool(fdoExtension
->ReleaseQueueIrp
);
8115 IoFreeIrp(fdoExtension
->ReleaseQueueIrp
);
8117 fdoExtension
->ReleaseQueueIrp
= NULL
;
8121 // free anything that we allocated
8124 if ((fdoExtension
->PrivateFdoData
) &&
8125 (fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
)) {
8127 ExFreePool(fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
);
8128 fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= FALSE
;
8129 fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
= NULL
;
8133 } // end ClasspFreeReleaseRequest()
8135 /*++////////////////////////////////////////////////////////////////////////////
8139 Routine Description:
8141 This routine issues an internal device control command
8142 to the port driver to release a frozen queue. The call
8143 is issued asynchronously as ClassReleaseQueue will be invoked
8144 from the IO completion DPC (and will have no context to
8145 wait for a synchronous call to complete).
8147 This routine must be called with the remove lock held.
8151 Fdo - The functional device object for the device with the frozen queue.
8161 IN PDEVICE_OBJECT Fdo
8164 ClasspReleaseQueue(Fdo
, NULL
);
8166 } // end ClassReleaseQueue()
8168 /*++////////////////////////////////////////////////////////////////////////////
8170 ClasspAllocateReleaseQueueIrp()
8172 Routine Description:
8174 This routine allocates the release queue irp held in classpnp's private
8175 extension. This was added to allow no-memory conditions to be more
8184 Does not grab the spinlock. Should only be called from StartDevice()
8185 routine. May be called elsewhere for poorly-behaved drivers that cause
8186 the queue to lockup before the device is started. This should *never*
8187 occur, since it's illegal to send a request to a non-started PDO. This
8188 condition is checked for in ClasspReleaseQueue().
8193 ClasspAllocateReleaseQueueIrp(
8194 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8198 UCHAR lowerStackSize
;
8201 // do an initial check w/o the spinlock
8204 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8205 return STATUS_SUCCESS
;
8209 lowerStackSize
= FdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
;
8212 // don't allocate one if one is in progress! this means whoever called
8213 // this routine didn't check if one was in progress.
8216 ASSERT(!(FdoExtension
->ReleaseQueueInProgress
));
8218 FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
=
8219 ExAllocatePoolWithTag(NonPagedPool
,
8220 IoSizeOfIrp(lowerStackSize
),
8221 CLASS_TAG_RELEASE_QUEUE
8224 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
== NULL
) {
8225 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
8226 "release queue irp\n"));
8227 return STATUS_INSUFFICIENT_RESOURCES
;
8229 IoInitializeIrp(FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
,
8230 IoSizeOfIrp(lowerStackSize
),
8232 FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= TRUE
;
8234 return STATUS_SUCCESS
;
8237 /*++////////////////////////////////////////////////////////////////////////////
8239 ClasspReleaseQueue()
8241 Routine Description:
8243 This routine issues an internal device control command
8244 to the port driver to release a frozen queue. The call
8245 is issued asynchronously as ClassReleaseQueue will be invoked
8246 from the IO completion DPC (and will have no context to
8247 wait for a synchronous call to complete).
8249 This routine must be called with the remove lock held.
8253 Fdo - The functional device object for the device with the frozen queue.
8255 ReleaseQueueIrp - If this irp is supplied then the test to determine whether
8256 a release queue request is in progress will be ignored.
8257 The irp provided must be the IRP originally allocated
8258 for release queue requests (so this parameter can only
8259 really be provided by the release queue completion
8270 IN PDEVICE_OBJECT Fdo
,
8271 IN PIRP ReleaseQueueIrp OPTIONAL
8274 PIO_STACK_LOCATION irpStack
;
8276 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8277 PDEVICE_OBJECT lowerDevice
;
8278 PSCSI_REQUEST_BLOCK srb
;
8281 lowerDevice
= fdoExtension
->CommonExtension
.LowerDeviceObject
;
8284 // we raise irql separately so we're not swapped out or suspended
8285 // while holding the release queue irp in this routine. this lets
8286 // us release the spin lock before lowering irql.
8289 KeRaiseIrql(DISPATCH_LEVEL
, ¤tIrql
);
8291 KeAcquireSpinLockAtDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8294 // make sure that if they passed us an irp, it matches our allocated irp.
8297 ASSERT((ReleaseQueueIrp
== NULL
) ||
8298 (ReleaseQueueIrp
== fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
));
8301 // ASSERT that we've already allocated this. (should not occur)
8302 // try to allocate it anyways, then finally bugcheck if
8303 // there's still no memory...
8306 ASSERT(fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
);
8307 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8308 ClasspAllocateReleaseQueueIrp(fdoExtension
);
8310 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8311 KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL
, 0x12, (ULONG_PTR
)Fdo
, 0x0, 0x0);
8314 if ((fdoExtension
->ReleaseQueueInProgress
) && (ReleaseQueueIrp
== NULL
)) {
8317 // Someone is already using the irp - just set the flag to indicate that
8318 // we need to release the queue again.
8321 fdoExtension
->ReleaseQueueNeeded
= TRUE
;
8322 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8323 KeLowerIrql(currentIrql
);
8329 // Mark that there is a release queue in progress and drop the spinlock.
8332 fdoExtension
->ReleaseQueueInProgress
= TRUE
;
8333 if (ReleaseQueueIrp
) {
8334 irp
= ReleaseQueueIrp
;
8336 irp
= fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
;
8338 srb
= &(fdoExtension
->ReleaseQueueSrb
);
8340 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8342 ASSERT(irp
!= NULL
);
8344 irpStack
= IoGetNextIrpStackLocation(irp
);
8346 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
8348 srb
->OriginalRequest
= irp
;
8351 // Store the SRB address in next stack for port driver.
8354 irpStack
->Parameters
.Scsi
.Srb
= srb
;
8357 // If this device is removable then flush the queue. This will also
8361 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
8362 srb
->Function
= SRB_FUNCTION_FLUSH_QUEUE
;
8365 srb
->Function
= SRB_FUNCTION_RELEASE_QUEUE
;
8368 ClassAcquireRemoveLock(Fdo
, irp
);
8370 IoSetCompletionRoutine(irp
,
8371 ClassReleaseQueueCompletion
,
8377 IoCallDriver(lowerDevice
, irp
);
8379 KeLowerIrql(currentIrql
);
8383 } // end ClassReleaseQueue()
8385 /*++////////////////////////////////////////////////////////////////////////////
8387 ClassReleaseQueueCompletion()
8389 Routine Description:
8391 This routine is called when an asynchronous I/O request
8392 which was issued by the class driver completes. Examples of such requests
8393 are release queue or START UNIT. This routine releases the queue if
8394 necessary. It then frees the context and the IRP.
8398 DeviceObject - The device object for the logical unit; however since this
8399 is the top stack location the value is NULL.
8401 Irp - Supplies a pointer to the Irp to be processed.
8403 Context - Supplies the context to be used to process this request.
8412 ClassReleaseQueueCompletion(
8413 PDEVICE_OBJECT DeviceObject
,
8418 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8421 BOOLEAN releaseQueueNeeded
;
8423 DeviceObject
= Context
;
8425 fdoExtension
= DeviceObject
->DeviceExtension
;
8427 ClassReleaseRemoveLock(DeviceObject
, Irp
);
8430 // Grab the spinlock and clear the release queue in progress flag so others
8431 // can run. Save (and clear) the state of the release queue needed flag
8432 // so that we can issue a new release queue outside the spinlock.
8435 KeAcquireSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), &oldIrql
);
8437 releaseQueueNeeded
= fdoExtension
->ReleaseQueueNeeded
;
8439 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8440 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8442 KeReleaseSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), oldIrql
);
8445 // If we need a release queue then issue one now. Another processor may
8446 // have already started one in which case we'll try to issue this one after
8447 // it is done - but we should never recurse more than one deep.
8450 if(releaseQueueNeeded
) {
8451 ClasspReleaseQueue(DeviceObject
, Irp
);
8455 // Indicate the I/O system should stop processing the Irp completion.
8458 return STATUS_MORE_PROCESSING_REQUIRED
;
8460 } // ClassAsynchronousCompletion()
8462 /*++////////////////////////////////////////////////////////////////////////////
8464 ClassAcquireChildLock()
8466 Routine Description:
8468 This routine acquires the lock protecting children PDOs. It may be
8469 acquired recursively by the same thread, but must be release by the
8470 thread once for each acquisition.
8474 FdoExtension - the device whose child list is protected.
8483 ClassAcquireChildLock(
8484 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8489 if(FdoExtension
->ChildLockOwner
!= KeGetCurrentThread()) {
8490 KeWaitForSingleObject(&FdoExtension
->ChildLock
,
8491 Executive
, KernelMode
,
8494 ASSERT(FdoExtension
->ChildLockOwner
== NULL
);
8495 ASSERT(FdoExtension
->ChildLockAcquisitionCount
== 0);
8497 FdoExtension
->ChildLockOwner
= KeGetCurrentThread();
8499 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8502 FdoExtension
->ChildLockAcquisitionCount
++;
8506 /*++////////////////////////////////////////////////////////////////////////////
8508 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
8510 Routine Description:
8512 This routine releases the lock protecting children PDOs. It must be
8513 called once for each time ClassAcquireChildLock was called.
8517 FdoExtension - the device whose child list is protected
8526 ClassReleaseChildLock(
8527 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8530 ASSERT(FdoExtension
->ChildLockOwner
== KeGetCurrentThread());
8531 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8533 FdoExtension
->ChildLockAcquisitionCount
-= 1;
8535 if(FdoExtension
->ChildLockAcquisitionCount
== 0) {
8536 FdoExtension
->ChildLockOwner
= NULL
;
8537 KeSetEvent(&FdoExtension
->ChildLock
, IO_NO_INCREMENT
, FALSE
);
8541 } // end ClassReleaseChildLock(
8543 /*++////////////////////////////////////////////////////////////////////////////
8547 Routine Description:
8549 This routine will insert a new child into the head of the child list.
8553 Parent - the child's parent (contains the head of the list)
8554 Child - the child to be inserted.
8555 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8556 it's already been acquired by or on behalf of the caller
8567 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8568 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8569 IN BOOLEAN AcquireLock
8573 ClassAcquireChildLock(Parent
);
8578 // Make sure this child's not already in the list.
8581 PPHYSICAL_DEVICE_EXTENSION testChild
;
8583 for (testChild
= Parent
->CommonExtension
.ChildList
;
8585 testChild
= testChild
->CommonExtension
.ChildList
) {
8587 ASSERT(testChild
!= Child
);
8592 Child
->CommonExtension
.ChildList
= Parent
->CommonExtension
.ChildList
;
8593 Parent
->CommonExtension
.ChildList
= Child
;
8596 ClassReleaseChildLock(Parent
);
8599 } // end ClassAddChild()
8601 /*++////////////////////////////////////////////////////////////////////////////
8605 Routine Description:
8607 This routine will remove a child from the child list.
8611 Parent - the parent to be removed from.
8613 Child - the child to be removed or NULL if the first child should be
8616 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8617 it's already been acquired by or on behalf of the caller
8622 A pointer to the child which was removed or NULL if no such child could
8623 be found in the list (or if Child was NULL but the list is empty).
8626 PPHYSICAL_DEVICE_EXTENSION
8629 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8630 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8631 IN BOOLEAN AcquireLock
8635 ClassAcquireChildLock(Parent
);
8639 PCOMMON_DEVICE_EXTENSION previousChild
= &Parent
->CommonExtension
;
8642 // If the list is empty then bail out now.
8645 if(Parent
->CommonExtension
.ChildList
== NULL
) {
8651 // If the caller specified a child then find the child object before
8652 // it. If none was specified then the FDO is the child object before
8653 // the one we want to remove.
8659 // Scan through the child list to find the entry which points to
8664 ASSERT(previousChild
!= &Child
->CommonExtension
);
8666 if(previousChild
->ChildList
== Child
) {
8670 previousChild
= &previousChild
->ChildList
->CommonExtension
;
8671 } while(previousChild
!= NULL
);
8673 if(previousChild
== NULL
) {
8680 // Save the next child away then unlink it from the list.
8683 Child
= previousChild
->ChildList
;
8684 previousChild
->ChildList
= Child
->CommonExtension
.ChildList
;
8685 Child
->CommonExtension
.ChildList
= NULL
;
8689 ClassReleaseChildLock(Parent
);
8693 } // end ClassRemoveChild()
8697 ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
8702 ClasspRetryRequestDpc(
8709 PDEVICE_OBJECT deviceObject
= Context
;
8710 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8711 PCOMMON_DEVICE_EXTENSION commonExtension
;
8712 PCLASS_PRIVATE_FDO_DATA fdoData
;
8713 PCLASS_RETRY_INFO retryList
;
8717 commonExtension
= deviceObject
->DeviceExtension
;
8718 ASSERT(commonExtension
->IsFdo
);
8719 fdoExtension
= deviceObject
->DeviceExtension
;
8720 fdoData
= fdoExtension
->PrivateFdoData
;
8723 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8726 KeQueryTickCount(&now
);
8729 // if CurrentTick is less than now
8732 // retry entire list
8736 if (now
.QuadPart
< fdoData
->Retry
.Tick
.QuadPart
) {
8738 ClasspRetryDpcTimer(fdoData
);
8743 retryList
= fdoData
->Retry
.ListHead
;
8744 fdoData
->Retry
.ListHead
= NULL
;
8745 fdoData
->Retry
.Delta
.QuadPart
= (LONGLONG
)0;
8746 fdoData
->Retry
.Tick
.QuadPart
= (LONGLONG
)0;
8750 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8752 while (retryList
!= NULL
) {
8756 irp
= CONTAINING_RECORD(retryList
, IRP
, Tail
.Overlay
.DriverContext
[0]);
8757 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: -- %p\n", irp
));
8758 retryList
= retryList
->Next
;
8760 irp
->Tail
.Overlay
.DriverContext
[0] = ULongToPtr(0xdddddddd); // invalidate data
8761 irp
->Tail
.Overlay
.DriverContext
[1] = ULongToPtr(0xdddddddd); // invalidate data
8762 irp
->Tail
.Overlay
.DriverContext
[2] = ULongToPtr(0xdddddddd); // invalidate data
8763 irp
->Tail
.Overlay
.DriverContext
[3] = ULongToPtr(0xdddddddd); // invalidate data
8766 IoCallDriver(commonExtension
->LowerDeviceObject
, irp
);
8771 } // end ClasspRetryRequestDpc()
8775 ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
8781 IN PDEVICE_OBJECT SelfDeviceObject
,
8783 IN LARGE_INTEGER TimeDelta100ns
// in 100ns units
8786 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8787 PCLASS_PRIVATE_FDO_DATA fdoData
;
8788 PCLASS_RETRY_INFO retryInfo
;
8789 //PCLASS_RETRY_INFO *previousNext;
8790 LARGE_INTEGER delta
;
8794 // this checks we aren't destroying irps
8796 ASSERT(sizeof(CLASS_RETRY_INFO
) <= (4*sizeof(PVOID
)));
8798 fdoExtension
= SelfDeviceObject
->DeviceExtension
;
8799 fdoData
= fdoExtension
->PrivateFdoData
;
8801 if (!fdoExtension
->CommonExtension
.IsFdo
) {
8804 // this debug print/assertion should ALWAYS be investigated.
8805 // ClassRetryRequest can currently only be used by FDO's
8808 DebugPrint((ClassDebugError
, "ClassRetryRequestEx: LOST IRP %p\n", Irp
));
8809 ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
8814 if (TimeDelta100ns
.QuadPart
< 0) {
8815 ASSERT(!"ClassRetryRequest - must use positive delay");
8816 TimeDelta100ns
.QuadPart
*= -1;
8820 // prepare what we can out of the loop
8823 retryInfo
= (PCLASS_RETRY_INFO
)(&Irp
->Tail
.Overlay
.DriverContext
[0]);
8824 RtlZeroMemory(retryInfo
, sizeof(CLASS_RETRY_INFO
));
8826 delta
.QuadPart
= (TimeDelta100ns
.QuadPart
/ fdoData
->Retry
.Granularity
);
8827 if (TimeDelta100ns
.QuadPart
% fdoData
->Retry
.Granularity
) {
8828 delta
.QuadPart
++; // round up to next tick
8830 if (delta
.QuadPart
== (LONGLONG
)0) {
8831 delta
.QuadPart
= MINIMUM_RETRY_UNITS
;
8835 // now determine if we should fire another DPC or not
8838 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8841 // always add request to the list
8844 retryInfo
->Next
= fdoData
->Retry
.ListHead
;
8845 fdoData
->Retry
.ListHead
= retryInfo
;
8847 if (fdoData
->Retry
.Delta
.QuadPart
== (LONGLONG
)0) {
8849 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: +++ %p\n", Irp
));
8852 // must be exactly one item on list
8855 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8856 ASSERT(fdoData
->Retry
.ListHead
->Next
== NULL
);
8859 // if currentDelta is zero, always fire a DPC
8862 KeQueryTickCount(&fdoData
->Retry
.Tick
);
8863 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8864 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8865 ClasspRetryDpcTimer(fdoData
);
8867 } else if (delta
.QuadPart
> fdoData
->Retry
.Delta
.QuadPart
) {
8870 // if delta is greater than the list's current delta,
8871 // increase the DPC handling time by difference
8872 // and update the delta to new larger value
8873 // allow the DPC to re-fire itself if needed
8876 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8879 // must be at least two items on list
8882 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8883 ASSERT(fdoData
->Retry
.ListHead
->Next
!= NULL
);
8885 fdoData
->Retry
.Tick
.QuadPart
-= fdoData
->Retry
.Delta
.QuadPart
;
8886 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8888 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8893 // just inserting it on the list was enough
8896 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8901 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8904 } // end ClassRetryRequest()
8908 ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
8913 ClasspRetryDpcTimer(
8914 IN PCLASS_PRIVATE_FDO_DATA FdoData
8919 ASSERT(FdoData
->Retry
.Tick
.QuadPart
!= (LONGLONG
)0);
8920 ASSERT(FdoData
->Retry
.ListHead
!= NULL
); // never fire an empty list
8923 // fire == (CurrentTick - now) * (100ns per tick)
8925 // NOTE: Overflow is nearly impossible and is ignored here
8928 KeQueryTickCount(&fire
);
8929 fire
.QuadPart
= FdoData
->Retry
.Tick
.QuadPart
- fire
.QuadPart
;
8930 fire
.QuadPart
*= FdoData
->Retry
.Granularity
;
8933 // fire is now multiples of 100ns until should fire the timer.
8934 // if timer should already have expired, or would fire too quickly,
8935 // fire it in some arbitrary number of ticks to prevent infinitely
8939 if (fire
.QuadPart
< MINIMUM_RETRY_UNITS
) {
8940 fire
.QuadPart
= MINIMUM_RETRY_UNITS
;
8943 DebugPrint((ClassDebugDelayedRetry
,
8944 "ClassRetry: ======= %I64x ticks\n",
8948 // must use negative to specify relative time to fire
8951 fire
.QuadPart
= fire
.QuadPart
* ((LONGLONG
)-1);
8954 // set the timer, since this is the first addition
8957 KeSetTimerEx(&FdoData
->Retry
.Timer
, fire
, 0, &FdoData
->Retry
.Dpc
);
8960 } // end ClasspRetryDpcTimer()
8964 ClasspInitializeHotplugInfo(
8965 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8968 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
8969 DEVICE_REMOVAL_POLICY deviceRemovalPolicy
;
8971 ULONG resultLength
= 0;
8972 ULONG writeCacheOverride
;
8977 // start with some default settings
8979 RtlZeroMemory(&(fdoData
->HotplugInfo
), sizeof(STORAGE_HOTPLUG_INFO
));
8982 // set the size (aka version)
8985 fdoData
->HotplugInfo
.Size
= sizeof(STORAGE_HOTPLUG_INFO
);
8988 // set if the device has removable media
8991 if (FdoExtension
->DeviceDescriptor
->RemovableMedia
) {
8992 fdoData
->HotplugInfo
.MediaRemovable
= TRUE
;
8994 fdoData
->HotplugInfo
.MediaRemovable
= FALSE
;
8998 // this refers to devices which, for reasons not yet understood,
8999 // do not fail PREVENT_MEDIA_REMOVAL requests even though they
9000 // have no way to lock the media into the drive. this allows
9001 // the filesystems to turn off delayed-write caching for these
9005 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
9006 FDO_HACK_CANNOT_LOCK_MEDIA
)) {
9007 fdoData
->HotplugInfo
.MediaHotplug
= TRUE
;
9009 fdoData
->HotplugInfo
.MediaHotplug
= FALSE
;
9014 // Look into the registry to see if the user has chosen
9015 // to override the default setting for the removal policy
9018 RtlZeroMemory(&deviceRemovalPolicy
, sizeof(DEVICE_REMOVAL_POLICY
));
9020 ClassGetDeviceParameter(FdoExtension
,
9021 CLASSP_REG_SUBKEY_NAME
,
9022 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
9023 (PULONG
)&deviceRemovalPolicy
);
9025 if (deviceRemovalPolicy
== 0)
9028 // Query the default removal policy from the kernel
9031 status
= IoGetDeviceProperty(FdoExtension
->LowerPdo
,
9032 DevicePropertyRemovalPolicy
,
9033 sizeof(DEVICE_REMOVAL_POLICY
),
9034 (PVOID
)&deviceRemovalPolicy
,
9036 if (!NT_SUCCESS(status
))
9041 if (resultLength
!= sizeof(DEVICE_REMOVAL_POLICY
))
9043 return STATUS_UNSUCCESSFUL
;
9048 // use this info to set the DeviceHotplug setting
9049 // don't rely on DeviceCapabilities, since it can't properly
9050 // determine device relations, etc. let the kernel figure this
9051 // stuff out instead.
9054 if (deviceRemovalPolicy
== RemovalPolicyExpectSurpriseRemoval
) {
9055 fdoData
->HotplugInfo
.DeviceHotplug
= TRUE
;
9057 fdoData
->HotplugInfo
.DeviceHotplug
= FALSE
;
9061 // this refers to the *filesystem* caching, but has to be included
9062 // here since it's a per-device setting. this may change to be
9063 // stored by the system in the future.
9066 writeCacheOverride
= FALSE
;
9067 ClassGetDeviceParameter(FdoExtension
,
9068 CLASSP_REG_SUBKEY_NAME
,
9069 CLASSP_REG_WRITE_CACHE_VALUE_NAME
,
9070 &writeCacheOverride
);
9072 if (writeCacheOverride
) {
9073 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= TRUE
;
9075 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= FALSE
;
9078 return STATUS_SUCCESS
;
9083 ClasspScanForClassHacks(
9084 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
9091 // remove invalid flags and save
9094 CLEAR_FLAG(Data
, FDO_HACK_INVALID_FLAGS
);
9095 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, Data
);
9101 ClasspScanForSpecialInRegistry(
9102 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
9105 HANDLE deviceParameterHandle
; // device instance key
9106 HANDLE classParameterHandle
; // classpnp subkey
9107 OBJECT_ATTRIBUTES objectAttributes
;
9108 UNICODE_STRING subkeyName
;
9112 // seeded in the ENUM tree by ClassInstaller
9115 RTL_QUERY_REGISTRY_TABLE queryTable
[2]; // null terminated array
9119 deviceParameterHandle
= NULL
;
9120 classParameterHandle
= NULL
;
9123 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
9124 PLUGPLAY_REGKEY_DEVICE
,
9126 &deviceParameterHandle
9129 if (!NT_SUCCESS(status
)) {
9130 goto cleanupScanForSpecial
;
9133 RtlInitUnicodeString(&subkeyName
, CLASSP_REG_SUBKEY_NAME
);
9134 InitializeObjectAttributes(&objectAttributes
,
9136 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
9137 deviceParameterHandle
,
9141 status
= ZwOpenKey( &classParameterHandle
,
9146 if (!NT_SUCCESS(status
)) {
9147 goto cleanupScanForSpecial
;
9151 // Zero out the memory
9154 RtlZeroMemory(&queryTable
[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
9157 // Setup the structure to read
9160 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
9161 queryTable
[0].Name
= CLASSP_REG_HACK_VALUE_NAME
;
9162 queryTable
[0].EntryContext
= &deviceHacks
;
9163 queryTable
[0].DefaultType
= REG_DWORD
;
9164 queryTable
[0].DefaultData
= &deviceHacks
;
9165 queryTable
[0].DefaultLength
= 0;
9171 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
9172 (PWSTR
)classParameterHandle
,
9177 if (!NT_SUCCESS(status
)) {
9178 goto cleanupScanForSpecial
;
9182 // remove unknown values and save...
9185 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
9186 "Classpnp => ScanForSpecial: HackFlags %#08x\n",
9189 CLEAR_FLAG(deviceHacks
, FDO_HACK_INVALID_FLAGS
);
9190 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, deviceHacks
);
9193 cleanupScanForSpecial
:
9195 if (deviceParameterHandle
) {
9196 ZwClose(deviceParameterHandle
);
9199 if (classParameterHandle
) {
9200 ZwClose(classParameterHandle
);
9204 // we should modify the system hive to include another key for us to grab
9205 // settings from. in this case: Classpnp\HackFlags
9207 // the use of a DWORD value for the HackFlags allows 32 hacks w/o
9208 // significant use of the registry, and also reduces OEM exposure.
9210 // definition of bit flags:
9211 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
9212 // cannot actually prevent removal.
9213 // 0x00000002 -- Device hard-hangs or times out for GESN requests.
9214 // 0xfffffffc -- Currently reserved, may be used later.