3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 SCSI class driver routines
24 #define CLASS_INIT_GUID 1
29 #pragma alloc_text(INIT, DriverEntry)
30 #pragma alloc_text(PAGE, ClassAddDevice)
31 #pragma alloc_text(PAGE, ClassClaimDevice)
32 #pragma alloc_text(PAGE, ClassCreateDeviceObject)
33 #pragma alloc_text(PAGE, ClassDispatchPnp)
34 #pragma alloc_text(PAGE, ClassGetDescriptor)
35 #pragma alloc_text(PAGE, ClassGetPdoId)
36 #pragma alloc_text(PAGE, ClassInitialize)
37 #pragma alloc_text(PAGE, ClassInitializeEx)
38 #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
39 #pragma alloc_text(PAGE, ClassMarkChildMissing)
40 #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
41 #pragma alloc_text(PAGE, ClassModeSense)
42 #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
43 #pragma alloc_text(PAGE, ClassPnpStartDevice)
44 #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
45 #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
46 #pragma alloc_text(PAGE, ClassRemoveDevice)
47 #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
48 #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
49 #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
50 #pragma alloc_text(PAGE, ClassUnload)
51 #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
52 #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
53 #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
54 #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
55 #pragma alloc_text(PAGE, ClasspScanForClassHacks)
56 #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
59 ULONG ClassPnpAllowUnload
= TRUE
;
62 #define FirstDriveLetter 'C'
63 #define LastDriveLetter 'Z'
67 /*++////////////////////////////////////////////////////////////////////////////
73 Temporary entry point needed to initialize the class system dll.
74 It doesn't do anything.
78 DriverObject - Pointer to the driver object created by the system.
88 IN PDRIVER_OBJECT DriverObject
,
89 IN PUNICODE_STRING RegistryPath
92 return STATUS_SUCCESS
;
98 /*++////////////////////////////////////////////////////////////////////////////
104 This routine is called by a class driver during its
105 DriverEntry routine to initialize the driver.
109 Argument1 - Driver Object.
110 Argument2 - Registry Path.
111 InitializationData - Device-specific driver's initialization data.
115 A valid return code for a DriverEntry routine.
122 IN PCLASS_INIT_DATA InitializationData
125 PDRIVER_OBJECT DriverObject
= Argument1
;
126 PUNICODE_STRING RegistryPath
= Argument2
;
128 PCLASS_DRIVER_EXTENSION driverExtension
;
134 DebugPrint((3,"\n\nSCSI Class Driver\n"));
136 ClasspInitializeDebugGlobals();
139 // Validate the length of this structure. This is effectively a
143 if (InitializationData
->InitializationDataSize
!= sizeof(CLASS_INIT_DATA
)) {
146 // This DebugPrint is to help third-party driver writers
149 DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
150 return (ULONG
) STATUS_REVISION_MISMATCH
;
154 // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
155 // are not required entry points.
158 if ((!InitializationData
->FdoData
.ClassDeviceControl
) ||
159 (!((InitializationData
->FdoData
.ClassReadWriteVerification
) ||
160 (InitializationData
->ClassStartIo
))) ||
161 (!InitializationData
->ClassAddDevice
) ||
162 (!InitializationData
->FdoData
.ClassStartDevice
)) {
165 // This DebugPrint is to help third-party driver writers
169 "ClassInitialize: Class device-specific driver missing required "
172 return (ULONG
) STATUS_REVISION_MISMATCH
;
175 if ((InitializationData
->ClassEnumerateDevice
) &&
176 ((!InitializationData
->PdoData
.ClassDeviceControl
) ||
177 (!InitializationData
->PdoData
.ClassStartDevice
) ||
178 (!((InitializationData
->PdoData
.ClassReadWriteVerification
) ||
179 (InitializationData
->ClassStartIo
))))) {
182 // This DebugPrint is to help third-party driver writers
185 DebugPrint((0, "ClassInitialize: Class device-specific missing "
186 "required PDO entry\n"));
188 return (ULONG
) STATUS_REVISION_MISMATCH
;
191 if((InitializationData
->FdoData
.ClassStopDevice
== NULL
) ||
192 ((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
193 (InitializationData
->PdoData
.ClassStopDevice
== NULL
))) {
196 // This DebugPrint is to help third-party driver writers
199 DebugPrint((0, "ClassInitialize: Class device-specific missing "
200 "required PDO entry\n"));
202 return (ULONG
) STATUS_REVISION_MISMATCH
;
206 // Setup the default power handlers if the class driver didn't provide
210 if(InitializationData
->FdoData
.ClassPowerDevice
== NULL
) {
211 InitializationData
->FdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
214 if((InitializationData
->ClassEnumerateDevice
!= NULL
) &&
215 (InitializationData
->PdoData
.ClassPowerDevice
== NULL
)) {
216 InitializationData
->PdoData
.ClassPowerDevice
= ClassMinimalPowerHandler
;
220 // warn that unload is not supported
222 // ISSUE-2000/02/03-peterwie
223 // We should think about making this a fatal error.
226 if(InitializationData
->ClassUnload
== NULL
) {
229 // This DebugPrint is to help third-party driver writers
232 DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
237 // Create an extension for the driver object
240 status
= IoAllocateDriverObjectExtension(DriverObject
,
241 CLASS_DRIVER_EXTENSION_KEY
,
242 sizeof(CLASS_DRIVER_EXTENSION
),
245 if(NT_SUCCESS(status
)) {
248 // Copy the registry path into the driver extension so we can use it later
251 driverExtension
->RegistryPath
.Length
= RegistryPath
->Length
;
252 driverExtension
->RegistryPath
.MaximumLength
= RegistryPath
->MaximumLength
;
254 driverExtension
->RegistryPath
.Buffer
=
255 ExAllocatePoolWithTag(PagedPool
,
256 RegistryPath
->MaximumLength
,
259 if(driverExtension
->RegistryPath
.Buffer
== NULL
) {
261 status
= STATUS_INSUFFICIENT_RESOURCES
;
265 RtlCopyUnicodeString(
266 &(driverExtension
->RegistryPath
),
270 // Copy the initialization data into the driver extension so we can reuse
271 // it during our add device routine
275 &(driverExtension
->InitData
),
277 sizeof(CLASS_INIT_DATA
));
279 driverExtension
->DeviceCount
= 0;
281 } else if (status
== STATUS_OBJECT_NAME_COLLISION
) {
284 // The extension already exists - get a pointer to it
287 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
288 CLASS_DRIVER_EXTENSION_KEY
);
290 ASSERT(driverExtension
!= NULL
);
294 DebugPrint((1, "ClassInitialize: Class driver extension could not be "
295 "allocated %lx\n", status
));
300 // Update driver object with entry points.
303 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = ClassCreateClose
;
304 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = ClassCreateClose
;
305 DriverObject
->MajorFunction
[IRP_MJ_READ
] = ClassReadWrite
;
306 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = ClassReadWrite
;
307 DriverObject
->MajorFunction
[IRP_MJ_SCSI
] = ClassInternalIoControl
;
308 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = ClassDeviceControlDispatch
;
309 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = ClassShutdownFlush
;
310 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = ClassShutdownFlush
;
311 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = ClassDispatchPnp
;
312 DriverObject
->MajorFunction
[IRP_MJ_POWER
] = ClassDispatchPower
;
313 DriverObject
->MajorFunction
[IRP_MJ_SYSTEM_CONTROL
] = ClassSystemControl
;
315 if (InitializationData
->ClassStartIo
) {
316 DriverObject
->DriverStartIo
= ClasspStartIo
;
319 if ((InitializationData
->ClassUnload
) && (ClassPnpAllowUnload
== TRUE
)) {
320 DriverObject
->DriverUnload
= ClassUnload
;
322 DriverObject
->DriverUnload
= NULL
;
325 DriverObject
->DriverExtension
->AddDevice
= ClassAddDevice
;
327 DbgPrint("Driver is ready to go\n");
328 status
= STATUS_SUCCESS
;
330 } // end ClassInitialize()
332 /*++////////////////////////////////////////////////////////////////////////////
338 This routine is allows the caller to do any extra initialization or
339 setup that is not done in ClassInitialize. The operation is
340 controlled by the GUID that is passed and the contents of the Data
341 parameter is dependent upon the GUID.
343 This is the list of supported operations:
345 Guid - GUID_CLASSPNP_QUERY_REGINFOEX
346 Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
348 Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
349 callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
350 former callback allows the driver to specify the name of the
366 IN PDRIVER_OBJECT DriverObject
,
371 PCLASS_DRIVER_EXTENSION driverExtension
;
377 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
378 CLASS_DRIVER_EXTENSION_KEY
380 if (IsEqualGUID(Guid
, &ClassGuidQueryRegInfoEx
))
382 PCLASS_QUERY_WMI_REGINFO_EX_LIST List
;
385 // Indicate the device supports PCLASS_QUERY_REGINFO_EX
386 // callback instead of PCLASS_QUERY_REGINFO callback.
388 List
= (PCLASS_QUERY_WMI_REGINFO_EX_LIST
)Data
;
390 if (List
->Size
== sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST
))
392 driverExtension
->ClassFdoQueryWmiRegInfoEx
= List
->ClassFdoQueryWmiRegInfoEx
;
393 driverExtension
->ClassPdoQueryWmiRegInfoEx
= List
->ClassPdoQueryWmiRegInfoEx
;
394 status
= STATUS_SUCCESS
;
396 status
= STATUS_INVALID_PARAMETER
;
399 status
= STATUS_NOT_SUPPORTED
;
404 } // end ClassInitializeEx()
406 /*++////////////////////////////////////////////////////////////////////////////
412 called when there are no more references to the driver. this allows
413 drivers to be updated without rebooting.
417 DriverObject - a pointer to the driver object that is being unloaded
424 IN PDRIVER_OBJECT DriverObject
427 PCLASS_DRIVER_EXTENSION driverExtension
;
432 ASSERT( DriverObject
->DeviceObject
== NULL
);
434 driverExtension
= IoGetDriverObjectExtension( DriverObject
,
435 CLASS_DRIVER_EXTENSION_KEY
438 ASSERT(driverExtension
!= NULL
);
439 ASSERT(driverExtension
->RegistryPath
.Buffer
!= NULL
);
440 ASSERT(driverExtension
->InitData
.ClassUnload
!= NULL
);
442 DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
443 &driverExtension
->RegistryPath
));
446 // attempt to process the driver's unload routine first.
449 driverExtension
->InitData
.ClassUnload(DriverObject
);
452 // free own allocated resources and return
455 ExFreePool( driverExtension
->RegistryPath
.Buffer
);
456 driverExtension
->RegistryPath
.Buffer
= NULL
;
457 driverExtension
->RegistryPath
.Length
= 0;
458 driverExtension
->RegistryPath
.MaximumLength
= 0;
461 } // end ClassUnload()
463 /*++////////////////////////////////////////////////////////////////////////////
469 SCSI class driver add device routine. This is called by pnp when a new
470 physical device come into being.
472 This routine will call out to the class driver to verify that it should
473 own this device then will create and attach a device object and then hand
474 it to the driver to initialize and create symbolic links
478 DriverObject - a pointer to the driver object that this is being created for
479 PhysicalDeviceObject - a pointer to the physical device object
481 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
482 STATUS_SUCCESS if the creation and attachment was successful
483 status of device creation and initialization
488 IN PDRIVER_OBJECT DriverObject
,
489 IN PDEVICE_OBJECT PhysicalDeviceObject
492 PCLASS_DRIVER_EXTENSION driverExtension
=
493 IoGetDriverObjectExtension(DriverObject
,
494 CLASS_DRIVER_EXTENSION_KEY
);
500 DbgPrint("got a device\n");
501 status
= driverExtension
->InitData
.ClassAddDevice(DriverObject
,
502 PhysicalDeviceObject
);
504 } // end ClassAddDevice()
506 /*++////////////////////////////////////////////////////////////////////////////
512 Storage class driver pnp routine. This is called by the io system when
513 a PNP request is sent to the device.
517 DeviceObject - pointer to the device object
519 Irp - pointer to the io request packet
528 IN PDEVICE_OBJECT DeviceObject
,
532 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
533 BOOLEAN isFdo
= commonExtension
->IsFdo
;
535 PCLASS_DRIVER_EXTENSION driverExtension
;
536 PCLASS_INIT_DATA initData
;
537 PCLASS_DEV_INFO devInfo
;
539 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
540 PIO_STACK_LOCATION nextIrpStack
= IoGetNextIrpStackLocation(Irp
);
542 NTSTATUS status
= Irp
->IoStatus
.Status
;
543 BOOLEAN completeRequest
= TRUE
;
544 BOOLEAN lockReleased
= FALSE
;
551 // Extract all the useful information out of the driver object
555 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
556 CLASS_DRIVER_EXTENSION_KEY
);
557 if (driverExtension
){
559 initData
= &(driverExtension
->InitData
);
562 devInfo
= &(initData
->FdoData
);
564 devInfo
= &(initData
->PdoData
);
567 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
569 DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
571 irpStack
->MinorFunction
,
572 isFdo
? "fdo" : "pdo",
574 DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
576 commonExtension
->PreviousState
,
577 commonExtension
->CurrentState
));
579 switch(irpStack
->MinorFunction
) {
581 case IRP_MN_START_DEVICE
: {
584 // if this is sent to the FDO we should forward it down the
585 // attachment chain before we start the FDO.
589 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
592 status
= STATUS_SUCCESS
;
595 if (NT_SUCCESS(status
)){
596 status
= Irp
->IoStatus
.Status
= ClassPnpStartDevice(DeviceObject
);
603 case IRP_MN_QUERY_DEVICE_RELATIONS
: {
605 DEVICE_RELATION_TYPE type
=
606 irpStack
->Parameters
.QueryDeviceRelations
.Type
;
608 PDEVICE_RELATIONS deviceRelations
= NULL
;
612 if(type
== TargetDeviceRelation
) {
615 // Device relations has one entry built in to it's size.
618 status
= STATUS_INSUFFICIENT_RESOURCES
;
620 deviceRelations
= ExAllocatePoolWithTag(PagedPool
,
621 sizeof(DEVICE_RELATIONS
),
624 if(deviceRelations
!= NULL
) {
626 RtlZeroMemory(deviceRelations
,
627 sizeof(DEVICE_RELATIONS
));
629 Irp
->IoStatus
.Information
= (ULONG_PTR
) deviceRelations
;
631 deviceRelations
->Count
= 1;
632 deviceRelations
->Objects
[0] = DeviceObject
;
633 ObReferenceObject(deviceRelations
->Objects
[0]);
635 status
= STATUS_SUCCESS
;
640 // PDO's just complete enumeration requests without altering
644 status
= Irp
->IoStatus
.Status
;
649 } else if (type
== BusRelations
) {
651 ASSERT(commonExtension
->IsInitialized
);
654 // Make sure we support enumeration
657 if(initData
->ClassEnumerateDevice
== NULL
) {
660 // Just send the request down to the lower driver. Perhaps
661 // It can enumerate children.
667 // Re-enumerate the device
670 status
= ClassPnpQueryFdoRelations(DeviceObject
, Irp
);
672 if(!NT_SUCCESS(status
)) {
673 completeRequest
= TRUE
;
679 IoCopyCurrentIrpStackLocationToNext(Irp
);
680 ClassReleaseRemoveLock(DeviceObject
, Irp
);
681 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
682 completeRequest
= FALSE
;
687 case IRP_MN_QUERY_ID
: {
689 BUS_QUERY_ID_TYPE idType
= irpStack
->Parameters
.QueryId
.IdType
;
690 UNICODE_STRING unicodeString
;
695 // FDO's should just forward the query down to the lower
699 IoCopyCurrentIrpStackLocationToNext(Irp
);
700 ClassReleaseRemoveLock(DeviceObject
, Irp
);
702 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
703 completeRequest
= FALSE
;
708 // PDO's need to give an answer - this is easy for now
711 RtlInitUnicodeString(&unicodeString
, NULL
);
713 status
= ClassGetPdoId(DeviceObject
,
717 if(status
== STATUS_NOT_IMPLEMENTED
) {
719 // The driver doesn't implement this ID (whatever it is).
720 // Use the status out of the IRP so that we don't mangle a
721 // response from someone else.
724 status
= Irp
->IoStatus
.Status
;
725 } else if(NT_SUCCESS(status
)) {
726 Irp
->IoStatus
.Information
= (ULONG_PTR
) unicodeString
.Buffer
;
728 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
734 case IRP_MN_QUERY_STOP_DEVICE
:
735 case IRP_MN_QUERY_REMOVE_DEVICE
: {
737 DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
739 ((irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) ?
740 "STOP" : "REMOVE")));
743 // If this device is in use for some reason (paging, etc...)
744 // then we need to fail the request.
747 if(commonExtension
->PagingPathCount
!= 0) {
749 DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
750 "path and cannot be removed\n",
752 status
= STATUS_DEVICE_BUSY
;
757 // Check with the class driver to see if the query operation
761 if(irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) {
762 status
= devInfo
->ClassStopDevice(DeviceObject
,
763 irpStack
->MinorFunction
);
765 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
766 irpStack
->MinorFunction
);
769 if(NT_SUCCESS(status
)) {
772 // ASSERT that we never get two queries in a row, as
773 // this will severly mess up the state machine
775 ASSERT(commonExtension
->CurrentState
!= irpStack
->MinorFunction
);
776 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
777 commonExtension
->CurrentState
= irpStack
->MinorFunction
;
780 DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
781 "%s irp\n", DeviceObject
, Irp
,
782 ((irpStack
->MinorFunction
== IRP_MN_QUERY_STOP_DEVICE
) ?
783 "STOP" : "REMOVE")));
784 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
787 DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
788 DeviceObject
, Irp
, status
));
793 case IRP_MN_CANCEL_STOP_DEVICE
:
794 case IRP_MN_CANCEL_REMOVE_DEVICE
: {
797 // Check with the class driver to see if the query or cancel
798 // operation can succeed.
801 if(irpStack
->MinorFunction
== IRP_MN_CANCEL_STOP_DEVICE
) {
802 status
= devInfo
->ClassStopDevice(DeviceObject
,
803 irpStack
->MinorFunction
);
804 ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
805 "never be failed\n", NT_SUCCESS(status
));
807 status
= devInfo
->ClassRemoveDevice(DeviceObject
,
808 irpStack
->MinorFunction
);
809 ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
810 "never be failed\n", NT_SUCCESS(status
));
813 Irp
->IoStatus
.Status
= status
;
816 // We got a CANCEL - roll back to the previous state only
817 // if the current state is the respective QUERY state.
820 if(((irpStack
->MinorFunction
== IRP_MN_CANCEL_STOP_DEVICE
) &&
821 (commonExtension
->CurrentState
== IRP_MN_QUERY_STOP_DEVICE
)
823 ((irpStack
->MinorFunction
== IRP_MN_CANCEL_REMOVE_DEVICE
) &&
824 (commonExtension
->CurrentState
== IRP_MN_QUERY_REMOVE_DEVICE
)
828 commonExtension
->CurrentState
=
829 commonExtension
->PreviousState
;
830 commonExtension
->PreviousState
= 0xff;
835 IoCopyCurrentIrpStackLocationToNext(Irp
);
836 ClassReleaseRemoveLock(DeviceObject
, Irp
);
837 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
838 completeRequest
= FALSE
;
840 status
= STATUS_SUCCESS
;
846 case IRP_MN_STOP_DEVICE
: {
849 // These all mean nothing to the class driver currently. The
850 // port driver will handle all queueing when necessary.
853 DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
855 (isFdo
? "fdo" : "pdo")
858 ASSERT(commonExtension
->PagingPathCount
== 0);
861 // ISSUE-2000/02/03-peterwie
862 // if we stop the timer here then it means no class driver can
863 // do i/o in its ClassStopDevice routine. This is because the
864 // retry (among other things) is tied into the tick handler
865 // and disabling retries could cause the class driver to deadlock.
866 // Currently no class driver we're aware of issues i/o in its
867 // Stop routine but this is a case we may want to defend ourself
871 if (DeviceObject
->Timer
) {
872 IoStopTimer(DeviceObject
);
875 status
= devInfo
->ClassStopDevice(DeviceObject
, IRP_MN_STOP_DEVICE
);
877 ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
878 "never be failed\n", NT_SUCCESS(status
));
881 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
884 if(NT_SUCCESS(status
)) {
885 commonExtension
->CurrentState
= irpStack
->MinorFunction
;
886 commonExtension
->PreviousState
= 0xff;
892 case IRP_MN_REMOVE_DEVICE
:
893 case IRP_MN_SURPRISE_REMOVAL
: {
895 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
896 UCHAR removeType
= irpStack
->MinorFunction
;
898 if (commonExtension
->PagingPathCount
!= 0) {
899 DBGTRACE(ClassDebugWarning
, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject
, Irp
));
903 // Release the lock for this IRP before calling in.
905 ClassReleaseRemoveLock(DeviceObject
, Irp
);
909 * If a timer was started on the device, stop it.
911 if (DeviceObject
->Timer
) {
912 IoStopTimer(DeviceObject
);
916 * "Fire-and-forget" the remove irp to the lower stack.
917 * Don't touch the irp (or the irp stack!) after this.
920 IoCopyCurrentIrpStackLocationToNext(Irp
);
921 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
922 ASSERT(NT_SUCCESS(status
));
923 completeRequest
= FALSE
;
926 status
= STATUS_SUCCESS
;
930 * Do our own cleanup and call the class driver's remove
932 * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
933 * so don't touch the extension after this.
935 commonExtension
->PreviousState
= commonExtension
->CurrentState
;
936 commonExtension
->CurrentState
= removeType
;
937 ClassRemoveDevice(DeviceObject
, removeType
);
942 case IRP_MN_DEVICE_USAGE_NOTIFICATION
: {
944 switch(irpStack
->Parameters
.UsageNotification
.Type
) {
946 case DeviceUsageTypePaging
: {
950 if((irpStack
->Parameters
.UsageNotification
.InPath
) &&
951 (commonExtension
->CurrentState
!= IRP_MN_START_DEVICE
)) {
954 // Device isn't started. Don't allow adding a
955 // paging file, but allow a removal of one.
958 status
= STATUS_DEVICE_NOT_READY
;
962 ASSERT(commonExtension
->IsInitialized
);
965 // need to synchronize this now...
968 KeEnterCriticalRegion();
969 status
= KeWaitForSingleObject(&commonExtension
->PathCountEvent
,
970 Executive
, KernelMode
,
972 ASSERT(NT_SUCCESS(status
));
973 status
= STATUS_SUCCESS
;
976 // If the volume is removable we should try to lock it in
977 // place or unlock it once per paging path count
980 if (commonExtension
->IsFdo
){
981 status
= ClasspEjectionControl(
985 (BOOLEAN
)irpStack
->Parameters
.UsageNotification
.InPath
);
988 if (!NT_SUCCESS(status
)){
989 KeSetEvent(&commonExtension
->PathCountEvent
, IO_NO_INCREMENT
, FALSE
);
990 KeLeaveCriticalRegion();
995 // if removing last paging device, need to set DO_POWER_PAGABLE
996 // bit here, and possible re-set it below on failure.
1001 if (!irpStack
->Parameters
.UsageNotification
.InPath
&&
1002 commonExtension
->PagingPathCount
== 1
1006 // removing last paging file
1007 // must have DO_POWER_PAGABLE bits set, but only
1008 // if noone set the DO_POWER_INRUSH bit
1012 if (TEST_FLAG(DeviceObject
->Flags
, DO_POWER_INRUSH
)) {
1013 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1014 "paging file removed, but "
1015 "DO_POWER_INRUSH was set, so NOT "
1016 "setting DO_POWER_PAGABLE\n",
1017 DeviceObject
, Irp
));
1019 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1020 "paging file removed, "
1021 "setting DO_POWER_PAGABLE\n",
1022 DeviceObject
, Irp
));
1023 SET_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1030 // forward the irp before finishing handling the
1034 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1037 // now deal with the failure and success cases.
1038 // note that we are not allowed to fail the irp
1039 // once it is sent to the lower drivers.
1042 if (NT_SUCCESS(status
)) {
1044 IoAdjustPagingPathCount(
1045 &commonExtension
->PagingPathCount
,
1046 irpStack
->Parameters
.UsageNotification
.InPath
);
1048 if (irpStack
->Parameters
.UsageNotification
.InPath
) {
1049 if (commonExtension
->PagingPathCount
== 1) {
1050 DebugPrint((2, "ClassDispatchPnp (%p,%p): "
1051 "Clearing PAGABLE bit\n",
1052 DeviceObject
, Irp
));
1053 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1060 // cleanup the changes done above
1063 if (setPagable
== TRUE
) {
1064 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
1065 "PAGABLE bit due to irp failure\n",
1066 DeviceObject
, Irp
));
1067 CLEAR_FLAG(DeviceObject
->Flags
, DO_POWER_PAGABLE
);
1072 // relock or unlock the media if needed.
1075 if (commonExtension
->IsFdo
) {
1077 ClasspEjectionControl(
1081 (BOOLEAN
)!irpStack
->Parameters
.UsageNotification
.InPath
);
1086 // set the event so the next one can occur.
1089 KeSetEvent(&commonExtension
->PathCountEvent
,
1090 IO_NO_INCREMENT
, FALSE
);
1091 KeLeaveCriticalRegion();
1095 case DeviceUsageTypeHibernation
: {
1097 IoAdjustPagingPathCount(
1098 &commonExtension
->HibernationPathCount
,
1099 irpStack
->Parameters
.UsageNotification
.InPath
1101 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1102 if (!NT_SUCCESS(status
)) {
1103 IoAdjustPagingPathCount(
1104 &commonExtension
->HibernationPathCount
,
1105 !irpStack
->Parameters
.UsageNotification
.InPath
1112 case DeviceUsageTypeDumpFile
: {
1113 IoAdjustPagingPathCount(
1114 &commonExtension
->DumpPathCount
,
1115 irpStack
->Parameters
.UsageNotification
.InPath
1117 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1118 if (!NT_SUCCESS(status
)) {
1119 IoAdjustPagingPathCount(
1120 &commonExtension
->DumpPathCount
,
1121 !irpStack
->Parameters
.UsageNotification
.InPath
1129 status
= STATUS_INVALID_PARAMETER
;
1136 case IRP_MN_QUERY_CAPABILITIES
: {
1138 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1139 DeviceObject
, Irp
));
1143 status
= ClassQueryPnpCapabilities(
1145 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
1152 PDEVICE_CAPABILITIES deviceCapabilities
;
1153 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
1154 PCLASS_PRIVATE_FDO_DATA fdoData
;
1156 fdoExtension
= DeviceObject
->DeviceExtension
;
1157 fdoData
= fdoExtension
->PrivateFdoData
;
1158 deviceCapabilities
=
1159 irpStack
->Parameters
.DeviceCapabilities
.Capabilities
;
1162 // forward the irp before handling the special cases
1165 status
= ClassForwardIrpSynchronous(commonExtension
, Irp
);
1166 if (!NT_SUCCESS(status
)) {
1171 // we generally want to remove the device from the hotplug
1172 // applet, which requires the SR-OK bit to be set.
1173 // only when the user specifies that they are capable of
1174 // safely removing things do we want to clear this bit
1175 // (saved in WriteCacheEnableOverride)
1177 // setting of this bit is done either above, or by the
1180 // note: may not be started, so check we have FDO data first.
1184 fdoData
->HotplugInfo
.WriteCacheEnableOverride
) {
1185 if (deviceCapabilities
->SurpriseRemovalOK
) {
1186 DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
1187 "device capabilities due to hotplug "
1188 "device or media\n"));
1190 deviceCapabilities
->SurpriseRemovalOK
= FALSE
;
1194 } // end QUERY_CAPABILITIES for FDOs
1200 } // end QUERY_CAPABILITIES
1205 IoCopyCurrentIrpStackLocationToNext(Irp
);
1207 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1208 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
1210 completeRequest
= FALSE
;
1218 ASSERT(driverExtension
);
1219 status
= STATUS_INTERNAL_ERROR
;
1222 if (completeRequest
){
1223 Irp
->IoStatus
.Status
= status
;
1226 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1229 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1231 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject
, Irp
, commonExtension
->PreviousState
, commonExtension
->CurrentState
));
1235 * The irp is already completed so don't touch it.
1236 * This may be a remove so don't touch the device extension.
1238 DBGTRACE(ClassDebugTrace
, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject
, Irp
));
1242 } // end ClassDispatchPnp()
1244 /*++////////////////////////////////////////////////////////////////////////////
1246 ClassPnpStartDevice()
1248 Routine Description:
1250 Storage class driver routine for IRP_MN_START_DEVICE requests.
1251 This routine kicks off any device specific initialization
1255 DeviceObject - a pointer to the device object
1257 Irp - a pointer to the io request packet
1264 NTSTATUS
ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject
)
1266 PCLASS_DRIVER_EXTENSION driverExtension
;
1267 PCLASS_INIT_DATA initData
;
1269 PCLASS_DEV_INFO devInfo
;
1271 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1272 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
1273 BOOLEAN isFdo
= commonExtension
->IsFdo
;
1275 BOOLEAN isMountedDevice
= TRUE
;
1276 UNICODE_STRING interfaceName
;
1278 BOOLEAN timerStarted
;
1280 NTSTATUS status
= STATUS_SUCCESS
;
1284 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
1285 CLASS_DRIVER_EXTENSION_KEY
);
1287 initData
= &(driverExtension
->InitData
);
1289 devInfo
= &(initData
->FdoData
);
1291 devInfo
= &(initData
->PdoData
);
1294 ASSERT(devInfo
->ClassInitDevice
!= NULL
);
1295 ASSERT(devInfo
->ClassStartDevice
!= NULL
);
1297 if (!commonExtension
->IsInitialized
){
1300 // perform FDO/PDO specific initialization
1304 STORAGE_PROPERTY_ID propertyId
;
1307 // allocate a private extension for class data
1310 if (fdoExtension
->PrivateFdoData
== NULL
) {
1311 fdoExtension
->PrivateFdoData
=
1312 ExAllocatePoolWithTag(NonPagedPool
,
1313 sizeof(CLASS_PRIVATE_FDO_DATA
),
1314 CLASS_TAG_PRIVATE_DATA
1318 if (fdoExtension
->PrivateFdoData
== NULL
) {
1319 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
1320 "private fdo data\n"));
1321 return STATUS_INSUFFICIENT_RESOURCES
;
1325 // initialize the struct's various fields.
1328 RtlZeroMemory(fdoExtension
->PrivateFdoData
,
1329 sizeof(CLASS_PRIVATE_FDO_DATA
)
1331 KeInitializeTimer(&fdoExtension
->PrivateFdoData
->Retry
.Timer
);
1332 KeInitializeDpc(&fdoExtension
->PrivateFdoData
->Retry
.Dpc
,
1333 ClasspRetryRequestDpc
,
1335 KeInitializeSpinLock(&fdoExtension
->PrivateFdoData
->Retry
.Lock
);
1336 fdoExtension
->PrivateFdoData
->Retry
.Granularity
=
1337 KeQueryTimeIncrement();
1338 commonExtension
->Reserved4
= (ULONG_PTR
)(' GPH'); // debug aid
1341 // NOTE: the old interface allowed the class driver to allocate
1342 // this. this was unsafe for low-memory conditions. allocate one
1343 // unconditionally now, and modify our internal functions to use
1344 // our own exclusively as it is the only safe way to do this.
1347 status
= ClasspAllocateReleaseQueueIrp(fdoExtension
);
1348 if (!NT_SUCCESS(status
)) {
1349 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
1350 "private release queue irp\n"));
1355 // Call port driver to get adapter capabilities.
1358 propertyId
= StorageAdapterProperty
;
1360 status
= ClassGetDescriptor(
1361 commonExtension
->LowerDeviceObject
,
1363 &fdoExtension
->AdapterDescriptor
);
1365 if(!NT_SUCCESS(status
)) {
1368 // This DebugPrint is to help third-party driver writers
1371 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1372 "[ADAPTER] failed %lx\n", status
));
1377 // Call port driver to get device descriptor.
1380 propertyId
= StorageDeviceProperty
;
1382 status
= ClassGetDescriptor(
1383 commonExtension
->LowerDeviceObject
,
1385 &fdoExtension
->DeviceDescriptor
);
1387 if(!NT_SUCCESS(status
)) {
1390 // This DebugPrint is to help third-party driver writers
1393 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1394 "[DEVICE] failed %lx\n", status
));
1398 ClasspScanForSpecialInRegistry(fdoExtension
);
1399 ClassScanForSpecial(fdoExtension
,
1401 ClasspScanForClassHacks
);
1404 // allow perf to be re-enabled after a given number of failed IOs
1405 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
1410 ClassGetDeviceParameter(fdoExtension
,
1411 CLASSP_REG_SUBKEY_NAME
,
1412 CLASSP_REG_PERF_RESTORE_VALUE_NAME
,
1414 if (t
>= CLASS_PERF_RESTORE_MINIMUM
) {
1415 fdoExtension
->PrivateFdoData
->Perf
.ReEnableThreshhold
= t
;
1421 // compatibility comes first. writable cd media will not
1422 // get a SYNCH_CACHE on power down.
1425 if (fdoExtension
->DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) {
1426 SET_FLAG(fdoExtension
->PrivateFdoData
->HackFlags
,
1427 FDO_HACK_NO_SYNC_CACHE
);
1431 // initialize the hotplug information only after the ScanForSpecial
1432 // routines, as it relies upon the hack flags.
1435 status
= ClasspInitializeHotplugInfo(fdoExtension
);
1437 if (!NT_SUCCESS(status
)) {
1438 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
1439 "hotplug information %lx\n", status
));
1444 * Allocate/initialize TRANSFER_PACKETs and related resources.
1446 status
= InitializeTransferPackets(DeviceObject
);
1450 // ISSUE - drivers need to disable write caching on the media
1451 // if hotplug and !useroverride. perhaps we should
1452 // allow registration of a callback to enable/disable
1453 // write cache instead.
1456 if (NT_SUCCESS(status
)){
1457 status
= devInfo
->ClassInitDevice(DeviceObject
);
1462 if (!NT_SUCCESS(status
)){
1465 // Just bail out - the remove that comes down will clean up the
1466 // initialized scraps.
1471 commonExtension
->IsInitialized
= TRUE
;
1473 if (commonExtension
->IsFdo
) {
1474 fdoExtension
->PrivateFdoData
->Perf
.OriginalSrbFlags
= fdoExtension
->SrbFlags
;
1480 // If device requests autorun functionality or a once a second callback
1481 // then enable the once per second timer.
1483 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
1484 // called in the context of the ClassInitDevice callback. If called
1485 // after then this check will have already been made and the
1486 // once a second timer will not have been enabled.
1489 ((initData
->ClassTick
!= NULL
) ||
1490 (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) ||
1491 ((fdoExtension
->FailurePredictionInfo
!= NULL
) &&
1492 (fdoExtension
->FailurePredictionInfo
->Method
!= FailurePredictionNone
))))
1494 ClasspEnableTimer(DeviceObject
);
1495 timerStarted
= TRUE
;
1497 timerStarted
= FALSE
;
1501 // NOTE: the timer looks at commonExtension->CurrentState now
1502 // to prevent Media Change Notification code from running
1503 // until the device is started, but allows the device
1504 // specific tick handler to run. therefore it is imperative
1505 // that commonExtension->CurrentState not be updated until
1506 // the device specific startdevice handler has finished.
1509 status
= devInfo
->ClassStartDevice(DeviceObject
);
1511 if(NT_SUCCESS(status
)) {
1512 commonExtension
->CurrentState
= IRP_MN_START_DEVICE
;
1514 if((isFdo
) && (initData
->ClassEnumerateDevice
!= NULL
)) {
1515 isMountedDevice
= FALSE
;
1518 if((DeviceObject
->DeviceType
!= FILE_DEVICE_DISK
) &&
1519 (DeviceObject
->DeviceType
!= FILE_DEVICE_CD_ROM
)) {
1521 isMountedDevice
= FALSE
;
1525 if(isMountedDevice
) {
1526 ClasspRegisterMountedDeviceInterface(DeviceObject
);
1529 if((commonExtension
->IsFdo
) &&
1530 (devInfo
->ClassWmiInfo
.GuidRegInfo
!= NULL
)) {
1532 IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_REGISTER
);
1537 ClasspDisableTimer(DeviceObject
);
1545 /*++////////////////////////////////////////////////////////////////////////////
1549 Routine Description:
1551 This is the system entry point for read and write requests. The
1552 device-specific handler is invoked to perform any validation necessary.
1554 If the device object is a PDO (partition object) then the request will
1555 simply be adjusted for Partition0 and issued to the lower device driver.
1557 IF the device object is an FDO (paritition 0 object), the number of bytes
1558 in the request are checked against the maximum byte counts that the adapter
1559 supports and requests are broken up into
1560 smaller sizes if necessary.
1564 DeviceObject - a pointer to the device object for this request
1573 NTSTATUS
ClassReadWrite(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
1575 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
1576 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
1577 PIO_STACK_LOCATION currentIrpStack
= IoGetCurrentIrpStackLocation(Irp
);
1578 LARGE_INTEGER startingOffset
= currentIrpStack
->Parameters
.Read
.ByteOffset
;
1579 ULONG transferByteCount
= currentIrpStack
->Parameters
.Read
.Length
;
1584 * Grab the remove lock. If we can't acquire it, bail out.
1586 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
1588 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1589 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1590 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1591 status
= STATUS_DEVICE_DOES_NOT_EXIST
;
1593 else if (TEST_FLAG(DeviceObject
->Flags
, DO_VERIFY_VOLUME
) &&
1594 (currentIrpStack
->MinorFunction
!= CLASSP_VOLUME_VERIFY_CHECKED
) &&
1595 !TEST_FLAG(currentIrpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
)){
1598 * DO_VERIFY_VOLUME is set for the device object,
1599 * but this request is not itself a verify request.
1600 * So fail this request.
1602 IoSetHardErrorOrVerifyDevice(Irp
, DeviceObject
);
1603 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
1604 Irp
->IoStatus
.Information
= 0;
1605 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1606 ClassCompleteRequest(DeviceObject
, Irp
, 0);
1607 status
= STATUS_VERIFY_REQUIRED
;
1612 * Since we've bypassed the verify-required tests we don't need to repeat
1613 * them with this IRP - in particular we don't want to worry about
1614 * hitting them at the partition 0 level if the request has come through
1615 * a non-zero partition.
1617 currentIrpStack
->MinorFunction
= CLASSP_VOLUME_VERIFY_CHECKED
;
1620 * Call the miniport driver's pre-pass filter to check if we
1621 * should continue with this transfer.
1623 ASSERT(commonExtension
->DevInfo
->ClassReadWriteVerification
);
1624 status
= commonExtension
->DevInfo
->ClassReadWriteVerification(DeviceObject
, Irp
);
1625 if (!NT_SUCCESS(status
)){
1626 ASSERT(Irp
->IoStatus
.Status
== status
);
1627 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1628 ClassCompleteRequest (DeviceObject
, Irp
, IO_NO_INCREMENT
);
1630 else if (status
== STATUS_PENDING
){
1632 * ClassReadWriteVerification queued this request.
1633 * So don't touch the irp anymore.
1638 if (transferByteCount
== 0) {
1640 * Several parts of the code turn 0 into 0xffffffff,
1641 * so don't process a zero-length request any further.
1643 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1644 Irp
->IoStatus
.Information
= 0;
1645 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1646 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
1647 status
= STATUS_SUCCESS
;
1651 * If the driver has its own StartIo routine, call it.
1653 if (commonExtension
->DriverExtension
->InitData
.ClassStartIo
) {
1654 IoMarkIrpPending(Irp
);
1655 IoStartPacket(DeviceObject
, Irp
, NULL
, NULL
);
1656 status
= STATUS_PENDING
;
1660 * The driver does not have its own StartIo routine.
1661 * So process this request ourselves.
1665 * Add partition byte offset to make starting byte relative to
1666 * beginning of disk.
1668 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1669 commonExtension
->StartingOffset
.QuadPart
;
1671 if (commonExtension
->IsFdo
){
1674 * Add in any skew for the disk manager software.
1676 currentIrpStack
->Parameters
.Read
.ByteOffset
.QuadPart
+=
1677 commonExtension
->PartitionZeroExtension
->DMByteSkew
;
1680 * Perform the actual transfer(s) on the hardware
1681 * to service this request.
1683 ServiceTransferRequest(DeviceObject
, Irp
);
1684 status
= STATUS_PENDING
;
1688 * This is a child PDO enumerated for our FDO by e.g. disk.sys
1689 * and owned by e.g. partmgr. Send it down to the next device
1690 * and the same irp will come back to us for the FDO.
1692 IoCopyCurrentIrpStackLocationToNext(Irp
);
1693 ClassReleaseRemoveLock(DeviceObject
, Irp
);
1694 status
= IoCallDriver(lowerDeviceObject
, Irp
);
1705 /*++////////////////////////////////////////////////////////////////////////////
1707 ClassReadDriveCapacity()
1709 Routine Description:
1711 This routine sends a READ CAPACITY to the requested device, updates
1712 the geometry information in the device object and returns
1713 when it is complete. This routine is synchronous.
1715 This routine must be called with the remove lock held or some other
1716 assurance that the Fdo will not be removed while processing.
1720 DeviceObject - Supplies a pointer to the device object that represents
1721 the device whose capacity is to be read.
1728 NTSTATUS
ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo
)
1730 READ_CAPACITY_DATA readCapacityBuffer
= {0};
1734 driveCapMdl
= BuildDeviceInputMdl(&readCapacityBuffer
, sizeof(READ_CAPACITY_DATA
));
1737 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1739 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
1742 IRP pseudoIrp
= {0};
1745 * Our engine needs an "original irp" to write the status back to
1746 * and to count down packets (one in this case).
1747 * Just use a pretend irp for this.
1749 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1750 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1751 pseudoIrp
.IoStatus
.Information
= 0;
1752 pseudoIrp
.MdlAddress
= driveCapMdl
;
1755 * Set this up as a SYNCHRONOUS transfer, submit it,
1756 * and wait for the packet to complete. The result
1757 * status will be written to the original irp.
1759 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1760 SetupDriveCapacityTransferPacket( pkt
,
1761 &readCapacityBuffer
,
1762 sizeof(READ_CAPACITY_DATA
),
1765 SubmitTransferPacket(pkt
);
1766 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1768 status
= pseudoIrp
.IoStatus
.Status
;
1771 * If we got an UNDERRUN, retry exactly once.
1772 * (The transfer_packet engine didn't retry because the result
1773 * status was success).
1775 if (NT_SUCCESS(status
) &&
1776 (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
))){
1777 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG
)pseudoIrp
.IoStatus
.Information
, sizeof(READ_CAPACITY_DATA
)));
1779 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
1781 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
1782 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
1783 pseudoIrp
.IoStatus
.Information
= 0;
1784 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1785 SetupDriveCapacityTransferPacket( pkt
,
1786 &readCapacityBuffer
,
1787 sizeof(READ_CAPACITY_DATA
),
1790 SubmitTransferPacket(pkt
);
1791 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1792 status
= pseudoIrp
.IoStatus
.Status
;
1793 if (pseudoIrp
.IoStatus
.Information
< sizeof(READ_CAPACITY_DATA
)){
1794 status
= STATUS_DEVICE_BUSY
;
1798 status
= STATUS_INSUFFICIENT_RESOURCES
;
1803 if (NT_SUCCESS(status
)){
1805 * The request succeeded.
1806 * Read out and store the drive information.
1809 ULONG bytesPerSector
;
1814 * Read the bytesPerSector value,
1815 * which is big-endian in the returned buffer.
1816 * Default to the standard 512 bytes.
1818 tmp
= readCapacityBuffer
.BytesPerBlock
;
1819 ((PFOUR_BYTE
)&bytesPerSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1820 ((PFOUR_BYTE
)&bytesPerSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1821 ((PFOUR_BYTE
)&bytesPerSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1822 ((PFOUR_BYTE
)&bytesPerSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1823 if (bytesPerSector
== 0) {
1824 bytesPerSector
= 512;
1828 * Clear all but the highest set bit.
1829 * That will give us a bytesPerSector value that is a power of 2.
1831 while (bytesPerSector
& (bytesPerSector
-1)) {
1832 bytesPerSector
&= bytesPerSector
-1;
1835 fdoExt
->DiskGeometry
.BytesPerSector
= bytesPerSector
;
1838 // Copy last sector in reverse byte order.
1841 tmp
= readCapacityBuffer
.LogicalBlockAddress
;
1842 ((PFOUR_BYTE
)&lastSector
)->Byte0
= ((PFOUR_BYTE
)&tmp
)->Byte3
;
1843 ((PFOUR_BYTE
)&lastSector
)->Byte1
= ((PFOUR_BYTE
)&tmp
)->Byte2
;
1844 ((PFOUR_BYTE
)&lastSector
)->Byte2
= ((PFOUR_BYTE
)&tmp
)->Byte1
;
1845 ((PFOUR_BYTE
)&lastSector
)->Byte3
= ((PFOUR_BYTE
)&tmp
)->Byte0
;
1848 // Calculate sector to byte shift.
1851 WHICH_BIT(fdoExt
->DiskGeometry
.BytesPerSector
, fdoExt
->SectorShift
);
1853 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
1854 fdoExt
->DiskGeometry
.BytesPerSector
));
1856 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
1859 if (fdoExt
->DMActive
){
1860 DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
1862 lastSector
-= fdoExt
->DMSkew
;
1866 * Check to see if we have a geometry we should be using already.
1868 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1869 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1870 if (cylinderSize
== 0){
1871 DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
1872 "values from %#x/%#x to %#x/%#x\n",
1873 fdoExt
->DiskGeometry
.TracksPerCylinder
,
1874 fdoExt
->DiskGeometry
.SectorsPerTrack
,
1878 fdoExt
->DiskGeometry
.TracksPerCylinder
= 0xff;
1879 fdoExt
->DiskGeometry
.SectorsPerTrack
= 0x3f;
1882 cylinderSize
= (fdoExt
->DiskGeometry
.TracksPerCylinder
*
1883 fdoExt
->DiskGeometry
.SectorsPerTrack
);
1887 // Calculate number of cylinders.
1890 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= (LONGLONG
)((lastSector
+ 1)/cylinderSize
);
1893 // if there are zero cylinders, then the device lied AND it's
1894 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
1895 // this can fit into a single LONGLONG, so create another usable
1896 // geometry, even if it's unusual looking. This allows small,
1897 // non-standard devices, such as Sony's Memory Stick, to show
1898 // up as having a partition.
1901 if (fdoExt
->DiskGeometry
.Cylinders
.QuadPart
== (LONGLONG
)0) {
1902 fdoExt
->DiskGeometry
.SectorsPerTrack
= 1;
1903 fdoExt
->DiskGeometry
.TracksPerCylinder
= 1;
1904 fdoExt
->DiskGeometry
.Cylinders
.QuadPart
= lastSector
;
1909 // Calculate media capacity in bytes.
1912 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
=
1913 ((LONGLONG
)(lastSector
+ 1)) << fdoExt
->SectorShift
;
1916 * Is this removable or fixed media
1918 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1919 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1922 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1927 * The request failed.
1931 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
1932 // what happens when the disk's sector size is bigger than
1933 // 512 bytes and we hit this code path? this is untested.
1935 // If the read capacity fails, set the geometry to reasonable parameter
1936 // so things don't fail at unexpected places. Zero the geometry
1937 // except for the bytes per sector and sector shift.
1941 * This request can sometimes fail legitimately
1942 * (e.g. when a SCSI device is attached but turned off)
1943 * so this is not necessarily a device/driver bug.
1945 DBGTRACE(ClassDebugWarning
, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo
, status
));
1948 * Write in a default disk geometry which we HOPE is right (??).
1951 RtlZeroMemory(&fdoExt
->DiskGeometry
, sizeof(DISK_GEOMETRY
));
1952 fdoExt
->DiskGeometry
.BytesPerSector
= 512;
1953 fdoExt
->SectorShift
= 9;
1954 fdoExt
->CommonExtension
.PartitionLength
.QuadPart
= (LONGLONG
) 0;
1957 * Is this removable or fixed media
1959 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
1960 fdoExt
->DiskGeometry
.MediaType
= RemovableMedia
;
1963 fdoExt
->DiskGeometry
.MediaType
= FixedMedia
;
1969 status
= STATUS_INSUFFICIENT_RESOURCES
;
1972 FreeDeviceInputMdl(driveCapMdl
);
1975 status
= STATUS_INSUFFICIENT_RESOURCES
;
1982 /*++////////////////////////////////////////////////////////////////////////////
1984 ClassSendStartUnit()
1986 Routine Description:
1988 Send command to SCSI unit to start or power up.
1989 Because this command is issued asynchronounsly, that is, without
1990 waiting on it to complete, the IMMEDIATE flag is not set. This
1991 means that the CDB will not return until the drive has powered up.
1992 This should keep subsequent requests from being submitted to the
1993 device before it has completely spun up.
1995 This routine is called from the InterpretSense routine, when a
1996 request sense returns data indicating that a drive must be
1999 This routine may also be called from a class driver's error handler,
2000 or anytime a non-critical start device should be sent to the device.
2004 Fdo - The functional device object for the stopped device.
2013 IN PDEVICE_OBJECT Fdo
2016 PIO_STACK_LOCATION irpStack
;
2018 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2019 PSCSI_REQUEST_BLOCK srb
;
2020 PCOMPLETION_CONTEXT context
;
2024 // Allocate Srb from nonpaged pool.
2027 context
= ExAllocatePoolWithTag(NonPagedPool
,
2028 sizeof(COMPLETION_CONTEXT
),
2031 if(context
== NULL
) {
2034 // ISSUE-2000/02/03-peterwie
2035 // This code path was inheritted from the NT 4.0 class2.sys driver.
2036 // It needs to be changed to survive low-memory conditions.
2039 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2043 // Save the device object in the context for use by the completion
2047 context
->DeviceObject
= Fdo
;
2048 srb
= &context
->Srb
;
2054 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
2057 // Write length to SRB.
2060 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2062 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2065 // Set timeout value large enough for drive to spin up.
2068 srb
->TimeOutValue
= START_UNIT_TIMEOUT
;
2071 // Set the transfer length.
2074 srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
|
2075 SRB_FLAGS_DISABLE_AUTOSENSE
|
2076 SRB_FLAGS_DISABLE_SYNCH_TRANSFER
;
2079 // Build the start unit CDB.
2083 cdb
= (PCDB
)srb
->Cdb
;
2085 cdb
->START_STOP
.OperationCode
= SCSIOP_START_STOP_UNIT
;
2086 cdb
->START_STOP
.Start
= 1;
2087 cdb
->START_STOP
.Immediate
= 0;
2088 cdb
->START_STOP
.LogicalUnitNumber
= srb
->Lun
;
2091 // Build the asynchronous request to be sent to the port driver.
2092 // Since this routine is called from a DPC the IRP should always be
2096 irp
= IoAllocateIrp(Fdo
->StackSize
, FALSE
);
2101 // ISSUE-2000/02/03-peterwie
2102 // This code path was inheritted from the NT 4.0 class2.sys driver.
2103 // It needs to be changed to survive low-memory conditions.
2106 KeBugCheck(SCSI_DISK_DRIVER_INTERNAL
);
2110 ClassAcquireRemoveLock(Fdo
, irp
);
2112 IoSetCompletionRoutine(irp
,
2113 (PIO_COMPLETION_ROUTINE
)ClassAsynchronousCompletion
,
2119 irpStack
= IoGetNextIrpStackLocation(irp
);
2120 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2121 srb
->OriginalRequest
= irp
;
2124 // Store the SRB address in next stack for port driver.
2127 irpStack
->Parameters
.Scsi
.Srb
= srb
;
2130 // Call the port driver with the IRP.
2133 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2137 } // end StartUnit()
2139 /*++////////////////////////////////////////////////////////////////////////////
2141 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
2143 Routine Description:
2145 This routine is called when an asynchronous I/O request
2146 which was issused by the class driver completes. Examples of such requests
2147 are release queue or START UNIT. This routine releases the queue if
2148 necessary. It then frees the context and the IRP.
2152 DeviceObject - The device object for the logical unit; however since this
2153 is the top stack location the value is NULL.
2155 Irp - Supplies a pointer to the Irp to be processed.
2157 Context - Supplies the context to be used to process this request.
2165 ClassAsynchronousCompletion(
2166 PDEVICE_OBJECT DeviceObject
,
2171 PCOMPLETION_CONTEXT context
= Context
;
2172 PSCSI_REQUEST_BLOCK srb
;
2174 if(DeviceObject
== NULL
) {
2176 DeviceObject
= context
->DeviceObject
;
2179 srb
= &context
->Srb
;
2182 // If this is an execute srb, then check the return status and make sure.
2183 // the queue is not frozen.
2186 if (srb
->Function
== SRB_FUNCTION_EXECUTE_SCSI
) {
2189 // Check for a frozen queue.
2192 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2195 // Unfreeze the queue getting the device object from the context.
2198 ClassReleaseQueue(context
->DeviceObject
);
2202 { // free port-allocated sense buffer if we can detect
2204 if (((PCOMMON_DEVICE_EXTENSION
)(DeviceObject
->DeviceExtension
))->IsFdo
) {
2206 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
2207 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2208 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2213 ASSERT(!TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
2220 // Free the context and the Irp.
2223 if (Irp
->MdlAddress
!= NULL
) {
2224 MmUnlockPages(Irp
->MdlAddress
);
2225 IoFreeMdl(Irp
->MdlAddress
);
2227 Irp
->MdlAddress
= NULL
;
2230 ClassReleaseRemoveLock(DeviceObject
, Irp
);
2232 ExFreePool(context
);
2236 // Indicate the I/O system should stop processing the Irp completion.
2239 return STATUS_MORE_PROCESSING_REQUIRED
;
2241 } // end ClassAsynchronousCompletion()
2245 VOID
ServiceTransferRequest(PDEVICE_OBJECT Fdo
, PIRP Irp
)
2247 PCOMMON_DEVICE_EXTENSION commonExt
= Fdo
->DeviceExtension
;
2248 PFUNCTIONAL_DEVICE_EXTENSION fdoExt
= Fdo
->DeviceExtension
;
2249 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExt
->PrivateFdoData
;
2250 PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc
= commonExt
->PartitionZeroExtension
->AdapterDescriptor
;
2251 PIO_STACK_LOCATION currentSp
= IoGetCurrentIrpStackLocation(Irp
);
2252 ULONG entireXferLen
= currentSp
->Parameters
.Read
.Length
;
2253 PUCHAR bufPtr
= MmGetMdlVirtualAddress(Irp
->MdlAddress
);
2254 LARGE_INTEGER targetLocation
= currentSp
->Parameters
.Read
.ByteOffset
;
2255 PTRANSFER_PACKET pkt
;
2256 SINGLE_LIST_ENTRY pktList
;
2257 PSINGLE_LIST_ENTRY slistEntry
;
2263 * Compute the number of hw xfers we'll have to do.
2264 * Calculate this without allowing for an overflow condition.
2266 ASSERT(fdoData
->HwMaxXferLen
>= PAGE_SIZE
);
2267 numPackets
= entireXferLen
/fdoData
->HwMaxXferLen
;
2268 if (entireXferLen
% fdoData
->HwMaxXferLen
){
2273 * First get all the TRANSFER_PACKETs that we'll need at once.
2274 * Use our 'simple' slist functions since we don't need interlocked.
2276 SimpleInitSlistHdr(&pktList
);
2277 for (i
= 0; i
< numPackets
; i
++){
2278 pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
2280 SimplePushSlist(&pktList
, &pkt
->SlistEntry
);
2287 if (i
== numPackets
){
2289 * Initialize the original IRP's status to success.
2290 * If any of the packets fail, they will set it to an error status.
2291 * The IoStatus.Information field will be incremented to the
2292 * transfer length as the pieces complete.
2294 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2295 Irp
->IoStatus
.Information
= 0;
2298 * Store the number of transfer pieces inside the original IRP.
2299 * It will be used to count down the pieces as they complete.
2301 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(numPackets
);
2304 * We are proceeding with the transfer.
2305 * Mark the client IRP pending since it may complete on a different thread.
2307 IoMarkIrpPending(Irp
);
2310 * Transmit the pieces of the transfer.
2312 while (entireXferLen
> 0){
2313 ULONG thisPieceLen
= MIN(fdoData
->HwMaxXferLen
, entireXferLen
);
2316 * Set up a TRANSFER_PACKET for this piece and send it.
2318 slistEntry
= SimplePopSlist(&pktList
);
2320 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2321 SetupReadWriteTransferPacket( pkt
,
2326 SubmitTransferPacket(pkt
);
2328 entireXferLen
-= thisPieceLen
;
2329 bufPtr
+= thisPieceLen
;
2330 targetLocation
.QuadPart
+= thisPieceLen
;
2332 ASSERT(SimpleIsSlistEmpty(&pktList
));
2336 * We were unable to get all the TRANSFER_PACKETs we need,
2337 * but we did get at least one.
2338 * That means that we are in extreme low-memory stress.
2339 * We'll try doing this transfer using a single packet.
2340 * The port driver is certainly also in stress, so use one-page
2345 * Free all but one of the TRANSFER_PACKETs.
2348 slistEntry
= SimplePopSlist(&pktList
);
2350 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2351 EnqueueFreeTransferPacket(Fdo
, pkt
);
2355 * Get the single TRANSFER_PACKET that we'll be using.
2357 slistEntry
= SimplePopSlist(&pktList
);
2359 ASSERT(SimpleIsSlistEmpty(&pktList
));
2360 pkt
= CONTAINING_RECORD(slistEntry
, TRANSFER_PACKET
, SlistEntry
);
2361 DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt
));
2364 * Set default status and the number of transfer packets (one)
2365 * inside the original irp.
2367 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
2368 Irp
->IoStatus
.Information
= 0;
2369 Irp
->Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
2372 * Mark the client irp pending since it may complete on
2375 IoMarkIrpPending(Irp
);
2378 * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
2380 SetupReadWriteTransferPacket( pkt
,
2385 InitLowMemRetry(pkt
, bufPtr
, entireXferLen
, targetLocation
);
2386 StepLowMemRetry(pkt
);
2390 * We were unable to get ANY TRANSFER_PACKETs.
2391 * Defer this client irp until some TRANSFER_PACKETs free up.
2393 DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp
));
2394 IoMarkIrpPending(Irp
);
2395 EnqueueDeferredClientIrp(fdoData
, Irp
);
2401 /*++////////////////////////////////////////////////////////////////////////////
2405 Routine Description:
2407 This routine executes when the port driver has completed a request.
2408 It looks at the SRB status in the completing SRB and if not success
2409 it checks for valid request sense buffer information. If valid, the
2410 info is used to update status with more precise message of type of
2411 error. This routine deallocates the SRB.
2413 This routine should only be placed on the stack location for a class
2418 Fdo - Supplies the device object which represents the logical
2421 Irp - Supplies the Irp which has completed.
2423 Context - Supplies a pointer to the SRB.
2432 IN PDEVICE_OBJECT Fdo
,
2437 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2438 PSCSI_REQUEST_BLOCK srb
= Context
;
2439 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2440 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2443 BOOLEAN callStartNextPacket
;
2445 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2448 // Check SRB status for success of completing request.
2451 if (SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2452 ULONG retryInterval
;
2454 DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp
, srb
));
2457 // Release the queue if it is frozen.
2460 if (srb
->SrbStatus
& SRB_STATUS_QUEUE_FROZEN
) {
2461 ClassReleaseQueue(Fdo
);
2464 retry
= ClassInterpretSenseInfo(
2467 irpStack
->MajorFunction
,
2468 irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
?
2469 irpStack
->Parameters
.DeviceIoControl
.IoControlCode
:
2472 ((ULONG
)(ULONG_PTR
)irpStack
->Parameters
.Others
.Argument4
),
2477 // If the status is verified required and the this request
2478 // should bypass verify required then retry the request.
2481 if (TEST_FLAG(irpStack
->Flags
, SL_OVERRIDE_VERIFY_VOLUME
) &&
2482 status
== STATUS_VERIFY_REQUIRED
) {
2484 status
= STATUS_IO_DEVICE_ERROR
;
2488 if (retry
&& (irpStack
->Parameters
.Others
.Argument4
--)) {
2494 DebugPrint((1, "Retry request %p\n", Irp
));
2496 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2497 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2500 RetryRequest(Fdo
, Irp
, srb
, FALSE
, retryInterval
);
2501 return STATUS_MORE_PROCESSING_REQUIRED
;
2507 // Set status for successful request
2509 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2510 ClasspPerfIncrementSuccessfulIo(fdoExtension
);
2511 status
= STATUS_SUCCESS
;
2512 } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
2516 // ensure we have returned some info, and it matches what the
2517 // original request wanted for PAGING operations only
2520 if ((NT_SUCCESS(status
)) && TEST_FLAG(Irp
->Flags
, IRP_PAGING_IO
)) {
2521 ASSERT(Irp
->IoStatus
.Information
!= 0);
2522 ASSERT(irpStack
->Parameters
.Read
.Length
== Irp
->IoStatus
.Information
);
2526 // remember if the caller wanted to skip calling IoStartNextPacket.
2527 // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
2528 // calls. this setting only affects device objects with StartIo routines.
2531 callStartNextPacket
= !TEST_FLAG(srb
->SrbFlags
, SRB_FLAGS_DONT_START_NEXT_PACKET
);
2532 if (irpStack
->MajorFunction
== IRP_MJ_DEVICE_CONTROL
) {
2533 callStartNextPacket
= FALSE
;
2540 if(!TEST_FLAG(srb
->SrbFlags
, SRB_CLASS_FLAGS_PERSISTANT
)) {
2542 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2543 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
2546 if (fdoExtension
->CommonExtension
.IsSrbLookasideListInitialized
){
2547 ClassFreeOrReuseSrb(fdoExtension
, srb
);
2550 DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
2556 DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
2557 "SRB_CLASS_FLAGS_PERSISTANT set\n", srb
));
2558 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
2559 DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
2560 " because SRB_CLASS_FLAGS_PERSISTANT set\n",
2561 srb
->SenseInfoBuffer
));
2567 // Set status in completing IRP.
2570 Irp
->IoStatus
.Status
= status
;
2573 // Set the hard error if necessary.
2576 if (!NT_SUCCESS(status
) &&
2577 IoIsErrorUserInduced(status
) &&
2578 (Irp
->Tail
.Overlay
.Thread
!= NULL
)
2582 // Store DeviceObject for filesystem, and clear
2583 // in IoStatus.Information field.
2586 IoSetHardErrorOrVerifyDevice(Irp
, Fdo
);
2587 Irp
->IoStatus
.Information
= 0;
2591 // If pending has be returned for this irp then mark the current stack as
2595 if (Irp
->PendingReturned
) {
2596 IoMarkIrpPending(Irp
);
2599 if (fdoExtension
->CommonExtension
.DriverExtension
->InitData
.ClassStartIo
) {
2600 if (callStartNextPacket
) {
2602 KeRaiseIrql(DISPATCH_LEVEL
, &oldIrql
);
2603 IoStartNextPacket(Fdo
, FALSE
);
2604 KeLowerIrql(oldIrql
);
2608 ClassReleaseRemoveLock(Fdo
, Irp
);
2612 } // end ClassIoComplete()
2615 /*++////////////////////////////////////////////////////////////////////////////
2617 ClassSendSrbSynchronous()
2619 Routine Description:
2621 This routine is called by SCSI device controls to complete an
2622 SRB and send it to the port driver synchronously (ie wait for
2623 completion). The CDB is already completed along with the SRB CDB
2624 size and request timeout value.
2628 Fdo - Supplies the functional device object which represents the target.
2630 Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
2632 BufferAddress - Supplies the address of the buffer.
2634 BufferLength - Supplies the length in bytes of the buffer.
2636 WriteToDevice - Indicates the data should be transfer to the device.
2640 NTSTATUS indicating the final results of the operation.
2642 If NT_SUCCESS(), then the amount of usable data is contained in the field
2643 Srb->DataTransferLength
2647 ClassSendSrbSynchronous(
2649 PSCSI_REQUEST_BLOCK Srb
,
2650 PVOID BufferAddress
,
2652 BOOLEAN WriteToDevice
2656 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
2657 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
2658 IO_STATUS_BLOCK ioStatus
;
2661 PIO_STACK_LOCATION irpStack
;
2663 PUCHAR senseInfoBuffer
;
2664 ULONG retryCount
= MAXIMUM_RETRIES
;
2669 // NOTE: This code is only pagable because we are not freezing
2670 // the queue. Allowing the queue to be frozen from a pagable
2671 // routine could leave the queue frozen as we try to page in
2672 // the code to unfreeze the queue. The result would be a nice
2673 // case of deadlock. Therefore, since we are unfreezing the
2674 // queue regardless of the result, just set the NO_FREEZE_QUEUE
2678 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
2679 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
2682 // Write length to SRB.
2685 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
2688 // Set SCSI bus address.
2691 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
2694 // Enable auto request sense.
2697 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
2700 // Sense buffer is in aligned nonpaged pool.
2703 senseInfoBuffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
2707 if (senseInfoBuffer
== NULL
) {
2709 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
2711 return(STATUS_INSUFFICIENT_RESOURCES
);
2714 Srb
->SenseInfoBuffer
= senseInfoBuffer
;
2715 Srb
->DataBuffer
= BufferAddress
;
2718 // Start retries here.
2724 // use fdoextension's flags by default.
2725 // do not move out of loop, as the flag may change due to errors
2726 // sending this command.
2729 Srb
->SrbFlags
= fdoExtension
->SrbFlags
;
2731 if(BufferAddress
!= NULL
) {
2733 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_OUT
);
2735 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
2740 // Initialize the QueueAction field.
2743 Srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
2746 // Disable synchronous transfer for these requests.
2747 // Disable freezing the queue, since all we do is unfreeze it anyways.
2750 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
2751 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
2754 // Set the event object to the unsignaled state.
2755 // It will be used to signal request completion.
2758 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
2761 // Build device I/O control request with METHOD_NEITHER data transfer.
2762 // We'll queue a completion routine to cleanup the MDL's and such ourself.
2765 irp
= IoAllocateIrp(
2766 (CCHAR
) (fdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
+ 1),
2770 ExFreePool(senseInfoBuffer
);
2771 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
2772 return(STATUS_INSUFFICIENT_RESOURCES
);
2776 // Get next stack location.
2779 irpStack
= IoGetNextIrpStackLocation(irp
);
2782 // Set up SRB for execute scsi request. Save SRB address in next stack
2783 // for the port driver.
2786 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
2787 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
2789 IoSetCompletionRoutine(irp
,
2790 ClasspSendSynchronousCompletion
,
2796 irp
->UserIosb
= &ioStatus
;
2797 irp
->UserEvent
= &event
;
2801 // Build an MDL for the data buffer and stick it into the irp. The
2802 // completion routine will unlock the pages and free the MDL.
2805 irp
->MdlAddress
= IoAllocateMdl( BufferAddress
,
2810 if (irp
->MdlAddress
== NULL
) {
2811 ExFreePool(senseInfoBuffer
);
2812 Srb
->SenseInfoBuffer
= NULL
;
2814 DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
2815 return STATUS_INSUFFICIENT_RESOURCES
;
2821 // the io manager unlocks these pages upon completion
2824 MmProbeAndLockPages( irp
->MdlAddress
,
2826 (WriteToDevice
? IoReadAccess
:
2829 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
2830 status
= _SEH2_GetExceptionCode();
2832 ExFreePool(senseInfoBuffer
);
2833 Srb
->SenseInfoBuffer
= NULL
;
2834 IoFreeMdl(irp
->MdlAddress
);
2837 DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
2838 "locking buffer\n", status
));
2844 // Set the transfer length.
2847 Srb
->DataTransferLength
= BufferLength
;
2853 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
2857 // Set up IRP Address.
2860 Srb
->OriginalRequest
= irp
;
2863 // Call the port driver with the request and wait for it to complete.
2866 status
= IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
2868 if (status
== STATUS_PENDING
) {
2869 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
2870 status
= ioStatus
.Status
;
2874 // Check that request completed without error.
2877 if (SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
2879 ULONG retryInterval
;
2881 DBGTRACE(ClassDebugWarning
, ("ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb
, DBGGETSCSIOPSTR(Srb
), DBGGETSRBSTATUSSTR(Srb
), (ULONG
)Srb
->SrbStatus
, status
, DBGGETSENSECODESTR(Srb
), DBGGETADSENSECODESTR(Srb
), DBGGETADSENSEQUALIFIERSTR(Srb
)));
2884 // assert that the queue is not frozen
2887 ASSERT(!TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
2890 // Update status and determine if request should be retried.
2893 retry
= ClassInterpretSenseInfo(Fdo
,
2897 MAXIMUM_RETRIES
- retryCount
,
2904 if ((status
== STATUS_DEVICE_NOT_READY
&&
2905 ((PSENSE_DATA
) senseInfoBuffer
)->AdditionalSenseCode
==
2906 SCSI_ADSENSE_LUN_NOT_READY
) ||
2907 (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_SELECTION_TIMEOUT
)) {
2909 LARGE_INTEGER delay
;
2912 // Delay for at least 2 seconds.
2915 if(retryInterval
< 2) {
2919 delay
.QuadPart
= (LONGLONG
)( - 10 * 1000 * (LONGLONG
)1000 * retryInterval
);
2922 // Stall for a while to let the device become ready
2925 KeDelayExecutionThread(KernelMode
, FALSE
, &delay
);
2930 // If retries are not exhausted then retry this operation.
2935 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2936 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2944 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
2945 status
= STATUS_SUCCESS
;
2949 // required even though we allocated our own, since the port driver may
2950 // have allocated one also
2953 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
2954 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
2957 Srb
->SenseInfoBuffer
= NULL
;
2958 ExFreePool(senseInfoBuffer
);
2964 /*++////////////////////////////////////////////////////////////////////////////
2966 ClassInterpretSenseInfo()
2968 Routine Description:
2970 This routine interprets the data returned from the SCSI
2971 request sense. It determines the status to return in the
2972 IRP and whether this request can be retried.
2976 DeviceObject - Supplies the device object associated with this request.
2978 Srb - Supplies the scsi request block which failed.
2980 MajorFunctionCode - Supplies the function code to be used for logging.
2982 IoDeviceCode - Supplies the device code to be used for logging.
2984 Status - Returns the status for the request.
2988 BOOLEAN TRUE: Drivers should retry this request.
2989 FALSE: Drivers should not retry this request.
2993 ClassInterpretSenseInfo(
2994 IN PDEVICE_OBJECT Fdo
,
2995 IN PSCSI_REQUEST_BLOCK Srb
,
2996 IN UCHAR MajorFunctionCode
,
2997 IN ULONG IoDeviceCode
,
2998 IN ULONG RetryCount
,
2999 OUT NTSTATUS
*Status
,
3000 OUT OPTIONAL ULONG
*RetryInterval
3003 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
3004 PCOMMON_DEVICE_EXTENSION commonExtension
= Fdo
->DeviceExtension
;
3005 PCLASS_PRIVATE_FDO_DATA fdoData
= fdoExtension
->PrivateFdoData
;
3007 PSENSE_DATA senseBuffer
= Srb
->SenseInfoBuffer
;
3009 BOOLEAN retry
= TRUE
;
3010 BOOLEAN logError
= FALSE
;
3011 BOOLEAN unhandledError
= FALSE
;
3012 BOOLEAN incrementErrorCount
= FALSE
;
3014 ULONG badSector
= 0;
3022 ULONG retryInterval
= 0;
3028 if(TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3031 // Log anything remotely incorrect about paging i/o
3036 logStatus
= IO_WARNING_PAGING_FAILURE
;
3040 // Check that request sense buffer is valid.
3043 ASSERT(fdoExtension
->CommonExtension
.IsFdo
);
3047 // must handle the SRB_STATUS_INTERNAL_ERROR case first,
3048 // as it has all the flags set.
3051 if (SRB_STATUS(Srb
->SrbStatus
) == SRB_STATUS_INTERNAL_ERROR
) {
3053 DebugPrint((ClassDebugSenseInfo
,
3054 "ClassInterpretSenseInfo: Internal Error code is %x\n",
3055 Srb
->InternalStatus
));
3058 *Status
= Srb
->InternalStatus
;
3060 } else if ((Srb
->SrbStatus
& SRB_STATUS_AUTOSENSE_VALID
) &&
3061 (Srb
->SenseInfoBufferLength
>=
3062 offsetof(SENSE_DATA
, CommandSpecificInformation
))) {
3065 // Zero the additional sense code and additional sense code qualifier
3066 // if they were not returned by the device.
3069 readSector
= senseBuffer
->AdditionalSenseLength
+
3070 offsetof(SENSE_DATA
, AdditionalSenseLength
);
3072 if (readSector
> Srb
->SenseInfoBufferLength
) {
3073 readSector
= Srb
->SenseInfoBufferLength
;
3076 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCode
)) {
3077 senseBuffer
->AdditionalSenseCode
= 0;
3080 if (readSector
<= offsetof(SENSE_DATA
, AdditionalSenseCodeQualifier
)) {
3081 senseBuffer
->AdditionalSenseCodeQualifier
= 0;
3084 DebugPrint((ClassDebugSenseInfo
,
3085 "ClassInterpretSenseInfo: Error code is %x\n",
3086 senseBuffer
->ErrorCode
));
3087 DebugPrint((ClassDebugSenseInfo
,
3088 "ClassInterpretSenseInfo: Sense key is %x\n",
3089 senseBuffer
->SenseKey
));
3090 DebugPrint((ClassDebugSenseInfo
,
3091 "ClassInterpretSenseInfo: Additional sense code is %x\n",
3092 senseBuffer
->AdditionalSenseCode
));
3093 DebugPrint((ClassDebugSenseInfo
,
3094 "ClassInterpretSenseInfo: Additional sense code qualifier "
3096 senseBuffer
->AdditionalSenseCodeQualifier
));
3099 switch (senseBuffer
->SenseKey
& 0xf) {
3101 case SCSI_SENSE_NOT_READY
: {
3103 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3104 "Device not ready\n"));
3105 *Status
= STATUS_DEVICE_NOT_READY
;
3107 switch (senseBuffer
->AdditionalSenseCode
) {
3109 case SCSI_ADSENSE_LUN_NOT_READY
: {
3111 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3112 "Lun not ready\n"));
3114 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3116 case SCSI_SENSEQ_OPERATION_IN_PROGRESS
: {
3117 DEVICE_EVENT_BECOMING_READY notReady
;
3119 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3120 "Operation In Progress\n"));
3121 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3123 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3124 notReady
.Version
= 1;
3125 notReady
.Reason
= 2;
3126 notReady
.Estimated100msToReady
= retryInterval
* 10;
3127 ClasspSendNotification(fdoExtension
,
3128 &GUID_IO_DEVICE_BECOMING_READY
,
3129 sizeof(DEVICE_EVENT_BECOMING_READY
),
3135 case SCSI_SENSEQ_BECOMING_READY
: {
3136 DEVICE_EVENT_BECOMING_READY notReady
;
3138 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3139 "In process of becoming ready\n"));
3140 retryInterval
= NOT_READY_RETRY_INTERVAL
;
3142 RtlZeroMemory(¬Ready
, sizeof(DEVICE_EVENT_BECOMING_READY
));
3143 notReady
.Version
= 1;
3144 notReady
.Reason
= 1;
3145 notReady
.Estimated100msToReady
= retryInterval
* 10;
3146 ClasspSendNotification(fdoExtension
,
3147 &GUID_IO_DEVICE_BECOMING_READY
,
3148 sizeof(DEVICE_EVENT_BECOMING_READY
),
3153 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS
: {
3154 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3155 "Long write in progress\n"));
3160 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED
: {
3161 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3162 "Manual intervention required\n"));
3163 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3168 case SCSI_SENSEQ_FORMAT_IN_PROGRESS
: {
3169 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3170 "Format in progress\n"));
3175 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE
: {
3177 if(!TEST_FLAG(fdoExtension
->ScanForSpecialFlags
,
3178 CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
)) {
3180 DebugPrint((ClassDebugSenseInfo
,
3181 "ClassInterpretSenseInfo: "
3182 "not ready, cause unknown\n"));
3184 Many non-WHQL certified drives (mostly CD-RW) return
3185 this when they have no media instead of the obvious
3188 SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
3190 These drives should not pass WHQL certification due
3191 to this discrepency.
3200 // Treat this as init command required and fall through.
3205 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED
:
3207 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3208 "Initializing command required\n"));
3211 // This sense code/additional sense code
3212 // combination may indicate that the device
3213 // needs to be started. Send an start unit if this
3214 // is a disk device.
3217 if(TEST_FLAG(fdoExtension
->DeviceFlags
,
3218 DEV_SAFE_START_UNIT
) &&
3219 !TEST_FLAG(Srb
->SrbFlags
,
3220 SRB_CLASS_FLAGS_LOW_PRIORITY
)) {
3221 ClassSendStartUnit(Fdo
);
3227 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
3231 case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
: {
3232 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3233 "No Media in device.\n"));
3234 *Status
= STATUS_NO_MEDIA_IN_DEVICE
;
3238 // signal MCN that there isn't any media in the device
3240 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3241 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3242 "No Media in a non-removable device %p\n",
3245 ClassSetMediaChangeState(fdoExtension
, MediaNotPresent
, FALSE
);
3249 } // end switch (senseBuffer->AdditionalSenseCode)
3252 } // end SCSI_SENSE_NOT_READY
3254 case SCSI_SENSE_DATA_PROTECT
: {
3255 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3256 "Media write protected\n"));
3257 *Status
= STATUS_MEDIA_WRITE_PROTECTED
;
3260 } // end SCSI_SENSE_DATA_PROTECT
3262 case SCSI_SENSE_MEDIUM_ERROR
: {
3263 DebugPrint((ClassDebugSenseInfo
,"ClassInterpretSenseInfo: "
3264 "Medium Error (bad block)\n"));
3265 *Status
= STATUS_DEVICE_DATA_ERROR
;
3270 logStatus
= IO_ERR_BAD_BLOCK
;
3273 // Check if this error is due to unknown format
3275 if (senseBuffer
->AdditionalSenseCode
== SCSI_ADSENSE_INVALID_MEDIA
){
3277 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3279 case SCSI_SENSEQ_UNKNOWN_FORMAT
: {
3281 *Status
= STATUS_UNRECOGNIZED_MEDIA
;
3284 // Log error only if this is a paging request
3286 if(!TEST_FLAG(Srb
->SrbFlags
, SRB_CLASS_FLAGS_PAGING
)) {
3292 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED
: {
3294 *Status
= STATUS_CLEANER_CARTRIDGE_INSTALLED
;
3302 } // end switch AdditionalSenseCodeQualifier
3304 } // end SCSI_ADSENSE_INVALID_MEDIA
3308 } // end SCSI_SENSE_MEDIUM_ERROR
3310 case SCSI_SENSE_HARDWARE_ERROR
: {
3311 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3312 "Hardware error\n"));
3313 *Status
= STATUS_IO_DEVICE_ERROR
;
3316 logStatus
= IO_ERR_CONTROLLER_ERROR
;
3318 } // end SCSI_SENSE_HARDWARE_ERROR
3320 case SCSI_SENSE_ILLEGAL_REQUEST
: {
3322 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3323 "Illegal SCSI request\n"));
3324 *Status
= STATUS_INVALID_DEVICE_REQUEST
;
3327 switch (senseBuffer
->AdditionalSenseCode
) {
3329 case SCSI_ADSENSE_ILLEGAL_COMMAND
: {
3330 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3331 "Illegal command\n"));
3335 case SCSI_ADSENSE_ILLEGAL_BLOCK
: {
3336 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3337 "Illegal block address\n"));
3338 *Status
= STATUS_NONEXISTENT_SECTOR
;
3342 case SCSI_ADSENSE_INVALID_LUN
: {
3343 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3345 *Status
= STATUS_NO_SUCH_DEVICE
;
3349 case SCSI_ADSENSE_MUSIC_AREA
: {
3350 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3355 case SCSI_ADSENSE_DATA_AREA
: {
3356 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3361 case SCSI_ADSENSE_VOLUME_OVERFLOW
: {
3362 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3363 "Volume overflow\n"));
3367 case SCSI_ADSENSE_COPY_PROTECTION_FAILURE
: {
3368 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3369 "Copy protection failure\n"));
3371 *Status
= STATUS_COPY_PROTECTION_FAILURE
;
3373 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3374 case SCSI_SENSEQ_AUTHENTICATION_FAILURE
:
3375 DebugPrint((ClassDebugSenseInfo
,
3376 "ClassInterpretSenseInfo: "
3377 "Authentication failure\n"));
3378 *Status
= STATUS_CSS_AUTHENTICATION_FAILURE
;
3380 case SCSI_SENSEQ_KEY_NOT_PRESENT
:
3381 DebugPrint((ClassDebugSenseInfo
,
3382 "ClassInterpretSenseInfo: "
3383 "Key not present\n"));
3384 *Status
= STATUS_CSS_KEY_NOT_PRESENT
;
3386 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED
:
3387 DebugPrint((ClassDebugSenseInfo
,
3388 "ClassInterpretSenseInfo: "
3389 "Key not established\n"));
3390 *Status
= STATUS_CSS_KEY_NOT_ESTABLISHED
;
3392 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION
:
3393 DebugPrint((ClassDebugSenseInfo
,
3394 "ClassInterpretSenseInfo: "
3395 "Read of scrambled sector w/o "
3396 "authentication\n"));
3397 *Status
= STATUS_CSS_SCRAMBLED_SECTOR
;
3399 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT
:
3400 DebugPrint((ClassDebugSenseInfo
,
3401 "ClassInterpretSenseInfo: "
3402 "Media region does not logical unit "
3404 *Status
= STATUS_CSS_REGION_MISMATCH
;
3406 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR
:
3407 DebugPrint((ClassDebugSenseInfo
,
3408 "ClassInterpretSenseInfo: "
3409 "Region set error -- region may "
3411 *Status
= STATUS_CSS_RESETS_EXHAUSTED
;
3413 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
3419 case SCSI_ADSENSE_INVALID_CDB
: {
3420 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3424 // Note: the retry interval is not typically used.
3425 // it is set here only because a ClassErrorHandler
3426 // cannot set the retryInterval, and the error may
3427 // require a few commands to be sent to clear whatever
3428 // caused this condition (i.e. disk clears the write
3429 // cache, requiring at least two commands)
3431 // hopefully, this shortcoming can be changed for
3439 } // end switch (senseBuffer->AdditionalSenseCode)
3442 } // end SCSI_SENSE_ILLEGAL_REQUEST
3444 case SCSI_SENSE_UNIT_ATTENTION
: {
3450 // A media change may have occured so increment the change
3451 // count for the physical device
3454 count
= InterlockedIncrement(&fdoExtension
->MediaChangeCount
);
3455 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3456 "Media change count for device %d incremented to %#lx\n",
3457 fdoExtension
->DeviceNumber
, count
));
3460 switch (senseBuffer
->AdditionalSenseCode
) {
3461 case SCSI_ADSENSE_MEDIUM_CHANGED
: {
3462 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3463 "Media changed\n"));
3465 if (!TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
3466 DebugPrint((ClassDebugError
, "ClassInterpretSenseInfo: "
3467 "Media Changed on non-removable device %p\n",
3470 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
3474 case SCSI_ADSENSE_BUS_RESET
: {
3475 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3480 case SCSI_ADSENSE_OPERATOR_REQUEST
: {
3481 switch (senseBuffer
->AdditionalSenseCodeQualifier
) {
3483 case SCSI_SENSEQ_MEDIUM_REMOVAL
: {
3484 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3485 "Ejection request received!\n"));
3486 ClassSendEjectionNotification(fdoExtension
);
3490 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE
: {
3491 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3492 "Operator selected write permit?! "
3493 "(unsupported!)\n"));
3497 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE
: {
3498 DebugPrint((ClassDebugSenseInfo
, "ClassInterpretSenseInfo: "
3499 "Operator selected write protect?! "
3500 "(unsupported!)\n"));
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()
4100 /*++////////////////////////////////////////////////////////////////////////////
4104 Routine Description:
4106 This routine sends a mode sense command to a target ID and returns
4107 when it is complete.
4111 Fdo - Supplies the functional device object associated with this request.
4113 ModeSenseBuffer - Supplies a buffer to store the sense data.
4115 Length - Supplies the length in bytes of the mode sense buffer.
4117 PageMode - Supplies the page or pages of mode sense data to be retrived.
4121 Length of the transferred data is returned.
4124 ULONG
ClassModeSense( IN PDEVICE_OBJECT Fdo
,
4125 IN PCHAR ModeSenseBuffer
,
4129 ULONG lengthTransferred
= 0;
4130 PMDL senseBufferMdl
;
4134 senseBufferMdl
= BuildDeviceInputMdl(ModeSenseBuffer
, Length
);
4135 if (senseBufferMdl
){
4137 TRANSFER_PACKET
*pkt
= DequeueFreeTransferPacket(Fdo
, TRUE
);
4141 IRP pseudoIrp
= {0};
4144 * Store the number of packets servicing the irp (one)
4145 * inside the original IRP. It will be used to counted down
4146 * to zero when the packet completes.
4147 * Initialize the original IRP's status to success.
4148 * If the packet fails, we will set it to the error status.
4150 pseudoIrp
.Tail
.Overlay
.DriverContext
[0] = LongToPtr(1);
4151 pseudoIrp
.IoStatus
.Status
= STATUS_SUCCESS
;
4152 pseudoIrp
.IoStatus
.Information
= 0;
4153 pseudoIrp
.MdlAddress
= senseBufferMdl
;
4156 * Set this up as a SYNCHRONOUS transfer, submit it,
4157 * and wait for the packet to complete. The result
4158 * status will be written to the original irp.
4160 ASSERT(Length
<= 0x0ff);
4161 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
4162 SetupModeSenseTransferPacket(pkt
, &event
, ModeSenseBuffer
, (UCHAR
)Length
, PageMode
, &pseudoIrp
);
4163 SubmitTransferPacket(pkt
);
4164 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
4166 if (NT_SUCCESS(pseudoIrp
.IoStatus
.Status
)){
4167 lengthTransferred
= (ULONG
)pseudoIrp
.IoStatus
.Information
;
4171 * This request can sometimes fail legitimately
4172 * (e.g. when a SCSI device is attached but turned off)
4173 * so this is not necessarily a device/driver bug.
4175 DBGTRACE(ClassDebugWarning
, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo
, pseudoIrp
.IoStatus
.Status
));
4179 FreeDeviceInputMdl(senseBufferMdl
);
4182 return lengthTransferred
;
4186 /*++////////////////////////////////////////////////////////////////////////////
4190 Routine Description:
4192 This routine scans through the mode sense data and finds the requested
4193 mode sense page code.
4196 ModeSenseBuffer - Supplies a pointer to the mode sense data.
4198 Length - Indicates the length of valid data.
4200 PageMode - Supplies the page mode to be searched for.
4202 Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
4206 A pointer to the the requested mode page. If the mode page was not found
4207 then NULL is return.
4212 IN PCHAR ModeSenseBuffer
,
4219 ULONG parameterHeaderLength
;
4220 PVOID result
= NULL
;
4222 limit
= ModeSenseBuffer
+ Length
;
4223 parameterHeaderLength
= (Use6Byte
) ? sizeof(MODE_PARAMETER_HEADER
) : sizeof(MODE_PARAMETER_HEADER10
);
4225 if (Length
>= parameterHeaderLength
) {
4227 PMODE_PARAMETER_HEADER10 modeParam10
;
4228 ULONG blockDescriptorLength
;
4231 * Skip the mode select header and block descriptors.
4234 blockDescriptorLength
= ((PMODE_PARAMETER_HEADER
) ModeSenseBuffer
)->BlockDescriptorLength
;
4237 modeParam10
= (PMODE_PARAMETER_HEADER10
) ModeSenseBuffer
;
4238 blockDescriptorLength
= modeParam10
->BlockDescriptorLength
[1];
4241 ModeSenseBuffer
+= parameterHeaderLength
+ blockDescriptorLength
;
4244 // ModeSenseBuffer now points at pages. Walk the pages looking for the
4245 // requested page until the limit is reached.
4248 while (ModeSenseBuffer
+
4249 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
) < limit
) {
4251 if (((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageCode
== PageMode
) {
4254 * found the mode page. make sure it's safe to touch it all
4255 * before returning the pointer to caller
4258 if (ModeSenseBuffer
+ ((PMODE_DISCONNECT_PAGE
)ModeSenseBuffer
)->PageLength
> limit
) {
4260 * Return NULL since the page is not safe to access in full
4265 result
= ModeSenseBuffer
;
4271 // Advance to the next page which is 4-byte-aligned offset after this page.
4274 ((PMODE_DISCONNECT_PAGE
) ModeSenseBuffer
)->PageLength
+
4275 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE
, PageLength
);
4281 } // end ClassFindModePage()
4283 /*++////////////////////////////////////////////////////////////////////////////
4285 ClassSendSrbAsynchronous()
4287 Routine Description:
4289 This routine takes a partially built Srb and an Irp and sends it down to
4292 This routine must be called with the remove lock held for the specified
4297 Fdo - Supplies the functional device object for the orginal request.
4299 Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
4300 CDB and the SRB timeout value must be filled in. The SRB must not be
4301 allocated from zone.
4303 Irp - Supplies the requesting Irp.
4305 BufferAddress - Supplies a pointer to the buffer to be transfered.
4307 BufferLength - Supplies the length of data transfer.
4309 WriteToDevice - Indicates the data transfer will be from system memory to
4314 Returns STATUS_PENDING if the request is dispatched (since the
4315 completion routine may change the irp's status value we cannot simply
4316 return the value of the dispatch)
4318 or returns a status value to indicate why it failed.
4322 ClassSendSrbAsynchronous(
4324 PSCSI_REQUEST_BLOCK Srb
,
4326 PVOID BufferAddress
,
4328 BOOLEAN WriteToDevice
4332 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
4333 PIO_STACK_LOCATION irpStack
;
4338 // Write length to SRB.
4341 Srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
4344 // Set SCSI bus address.
4347 Srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
4350 // This is a violation of the SCSI spec but it is required for
4354 // Srb->Cdb[1] |= deviceExtension->Lun << 5;
4357 // Indicate auto request sense by specifying buffer and size.
4360 Srb
->SenseInfoBuffer
= fdoExtension
->SenseData
;
4361 Srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
4362 Srb
->DataBuffer
= BufferAddress
;
4365 // Save the class driver specific flags away.
4368 savedFlags
= Srb
->SrbFlags
& SRB_FLAGS_CLASS_DRIVER_RESERVED
;
4371 // Allow the caller to specify that they do not wish
4372 // IoStartNextPacket() to be called in the completion routine.
4375 SET_FLAG(savedFlags
, (Srb
->SrbFlags
& SRB_FLAGS_DONT_START_NEXT_PACKET
));
4377 if (BufferAddress
!= NULL
) {
4380 // Build Mdl if necessary.
4383 if (Irp
->MdlAddress
== NULL
) {
4385 if (IoAllocateMdl(BufferAddress
,
4391 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
4394 // ClassIoComplete() would have free'd the srb
4397 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
4398 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
4400 ClassFreeOrReuseSrb(fdoExtension
, Srb
);
4401 ClassReleaseRemoveLock(Fdo
, Irp
);
4402 ClassCompleteRequest(Fdo
, Irp
, IO_NO_INCREMENT
);
4404 return STATUS_INSUFFICIENT_RESOURCES
;
4407 MmBuildMdlForNonPagedPool(Irp
->MdlAddress
);
4412 // Make sure the buffer requested matches the MDL.
4415 ASSERT(BufferAddress
== MmGetMdlVirtualAddress(Irp
->MdlAddress
));
4422 Srb
->SrbFlags
= WriteToDevice
? SRB_FLAGS_DATA_OUT
: SRB_FLAGS_DATA_IN
;
4430 Srb
->SrbFlags
= SRB_FLAGS_NO_DATA_TRANSFER
;
4434 // Restore saved flags.
4437 SET_FLAG(Srb
->SrbFlags
, savedFlags
);
4440 // Disable synchronous transfer for these requests.
4443 SET_FLAG(Srb
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
4446 // Set the transfer length.
4449 Srb
->DataTransferLength
= BufferLength
;
4455 Srb
->ScsiStatus
= Srb
->SrbStatus
= 0;
4460 // Save a few parameters in the current stack location.
4463 irpStack
= IoGetCurrentIrpStackLocation(Irp
);
4466 // Save retry count in current Irp stack.
4469 irpStack
->Parameters
.Others
.Argument4
= (PVOID
)MAXIMUM_RETRIES
;
4472 // Set up IoCompletion routine address.
4475 IoSetCompletionRoutine(Irp
, ClassIoComplete
, Srb
, TRUE
, TRUE
, TRUE
);
4478 // Get next stack location and
4479 // set major function code.
4482 irpStack
= IoGetNextIrpStackLocation(Irp
);
4484 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
4487 // Save SRB address in next stack for port driver.
4490 irpStack
->Parameters
.Scsi
.Srb
= Srb
;
4493 // Set up Irp Address.
4496 Srb
->OriginalRequest
= Irp
;
4499 // Call the port driver to process the request.
4502 IoMarkIrpPending(Irp
);
4504 IoCallDriver(fdoExtension
->CommonExtension
.LowerDeviceObject
, Irp
);
4506 return STATUS_PENDING
;
4508 } // end ClassSendSrbAsynchronous()
4510 /*++////////////////////////////////////////////////////////////////////////////
4512 ClassDeviceControlDispatch()
4514 Routine Description:
4516 The routine is the common class driver device control dispatch entry point.
4517 This routine is invokes the device-specific drivers DeviceControl routine,
4518 (which may call the Class driver's common DeviceControl routine).
4522 DeviceObject - Supplies a pointer to the device object for this request.
4524 Irp - Supplies the Irp making the request.
4528 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()
4563 /*++////////////////////////////////////////////////////////////////////////////
4565 ClassDeviceControl()
4567 Routine Description:
4569 The routine is the common class driver device control dispatch function.
4570 This routine is called by a class driver when it get an unrecognized
4571 device control request. This routine will perform the correct action for
4572 common requests such as lock media. If the device request is unknown it
4573 passed down to the next level.
4575 This routine must be called with the remove lock held for the specified
4580 DeviceObject - Supplies a pointer to the device object for this request.
4582 Irp - Supplies the Irp making the request.
4586 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 // Validiate 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.
5613 IN PDEVICE_OBJECT DeviceObject
,
5617 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
5623 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
5627 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5629 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
5631 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5633 return STATUS_DEVICE_DOES_NOT_EXIST
;
5636 if (commonExtension
->DevInfo
->ClassShutdownFlush
) {
5639 // Call the device-specific driver's routine.
5642 return commonExtension
->DevInfo
->ClassShutdownFlush(DeviceObject
, Irp
);
5646 // Device-specific driver doesn't support this.
5649 Irp
->IoStatus
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
5651 ClassReleaseRemoveLock(DeviceObject
, Irp
);
5652 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
5654 return STATUS_INVALID_DEVICE_REQUEST
;
5655 } // end ClassShutdownFlush()
5657 /*++////////////////////////////////////////////////////////////////////////////
5659 ClassCreateDeviceObject()
5661 Routine Description:
5663 This routine creates an object for the physical device specified and
5664 sets up the deviceExtension's function pointers for each entry point
5665 in the device-specific driver.
5669 DriverObject - Pointer to driver object created by system.
5671 ObjectNameBuffer - Dir. name of the object to create.
5673 LowerDeviceObject - Pointer to the lower device object
5675 IsFdo - should this be an fdo or a pdo
5677 DeviceObject - Pointer to the device object pointer we will return.
5685 ClassCreateDeviceObject(
5686 IN PDRIVER_OBJECT DriverObject
,
5687 IN PCCHAR ObjectNameBuffer
,
5688 IN PDEVICE_OBJECT LowerDevice
,
5690 IN OUT PDEVICE_OBJECT
*DeviceObject
5693 BOOLEAN isPartitionable
;
5694 STRING ntNameString
;
5695 UNICODE_STRING ntUnicodeString
;
5696 NTSTATUS status
, status2
;
5697 PDEVICE_OBJECT deviceObject
= NULL
;
5699 ULONG characteristics
;
5701 PCLASS_DRIVER_EXTENSION
5702 driverExtension
= IoGetDriverObjectExtension(DriverObject
,
5703 CLASS_DRIVER_EXTENSION_KEY
);
5705 PCLASS_DEV_INFO devInfo
;
5709 *DeviceObject
= NULL
;
5710 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5712 DebugPrint((2, "ClassCreateFdo: Create device object\n"));
5714 ASSERT(LowerDevice
);
5717 // Make sure that if we're making PDO's we have an enumeration routine
5720 isPartitionable
= (driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
5722 ASSERT(IsFdo
|| isPartitionable
);
5725 // Grab the correct dev-info structure out of the init data
5729 devInfo
= &(driverExtension
->InitData
.FdoData
);
5731 devInfo
= &(driverExtension
->InitData
.PdoData
);
5734 characteristics
= devInfo
->DeviceCharacteristics
;
5736 if(ARGUMENT_PRESENT(ObjectNameBuffer
)) {
5737 DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer
));
5739 RtlInitString(&ntNameString
, ObjectNameBuffer
);
5741 status
= RtlAnsiStringToUnicodeString(&ntUnicodeString
, &ntNameString
, TRUE
);
5743 if (!NT_SUCCESS(status
)) {
5746 "ClassCreateFdo: Cannot convert string %s\n",
5749 ntUnicodeString
.Buffer
= NULL
;
5753 DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
5755 if(IsFdo
== FALSE
) {
5758 // PDO's have to have some sort of name.
5761 SET_FLAG(characteristics
, FILE_AUTOGENERATED_DEVICE_NAME
);
5764 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5767 status
= IoCreateDevice(DriverObject
,
5768 devInfo
->DeviceExtensionSize
,
5770 devInfo
->DeviceType
,
5771 devInfo
->DeviceCharacteristics
,
5775 if (!NT_SUCCESS(status
)) {
5777 DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
5779 ASSERT(deviceObject
== NULL
);
5782 // buffer is not used any longer here.
5785 if (ntUnicodeString
.Buffer
!= NULL
) {
5786 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5787 ExFreePool(ntUnicodeString
.Buffer
);
5788 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5793 PCOMMON_DEVICE_EXTENSION commonExtension
= deviceObject
->DeviceExtension
;
5796 deviceObject
->DeviceExtension
,
5797 devInfo
->DeviceExtensionSize
);
5800 // Setup version code
5803 commonExtension
->Version
= 0x03;
5806 // Setup the remove lock and event
5809 commonExtension
->IsRemoved
= NO_REMOVE
;
5810 commonExtension
->RemoveLock
= 0;
5811 KeInitializeEvent(&commonExtension
->RemoveEvent
,
5812 SynchronizationEvent
,
5816 KeInitializeSpinLock(&commonExtension
->RemoveTrackingSpinlock
);
5817 commonExtension
->RemoveTrackingList
= NULL
;
5819 commonExtension
->RemoveTrackingSpinlock
= (ULONG_PTR
) -1;
5820 commonExtension
->RemoveTrackingList
= (PVOID
) -1;
5824 // Acquire the lock once. This reference will be released when the
5825 // remove IRP has been received.
5828 ClassAcquireRemoveLock(deviceObject
, (PIRP
) deviceObject
);
5831 // Store a pointer to the driver extension so we don't have to do
5832 // lookups to get it.
5835 commonExtension
->DriverExtension
= driverExtension
;
5838 // Fill in entry points
5841 commonExtension
->DevInfo
= devInfo
;
5844 // Initialize some of the common values in the structure
5847 commonExtension
->DeviceObject
= deviceObject
;
5849 commonExtension
->LowerDeviceObject
= NULL
;
5853 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= (PVOID
) commonExtension
;
5855 commonExtension
->PartitionZeroExtension
= deviceObject
->DeviceExtension
;
5858 // Set the initial device object flags.
5861 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5864 // Clear the PDO list
5867 commonExtension
->ChildList
= NULL
;
5869 commonExtension
->DriverData
=
5870 ((PFUNCTIONAL_DEVICE_EXTENSION
) deviceObject
->DeviceExtension
+ 1);
5872 if(isPartitionable
) {
5874 commonExtension
->PartitionNumber
= 0;
5876 commonExtension
->PartitionNumber
= (ULONG
) (-1L);
5879 fdoExtension
->DevicePowerState
= PowerDeviceD0
;
5881 KeInitializeEvent(&fdoExtension
->EjectSynchronizationEvent
,
5882 SynchronizationEvent
,
5885 KeInitializeEvent(&fdoExtension
->ChildLock
,
5886 SynchronizationEvent
,
5889 status
= ClasspAllocateReleaseRequest(deviceObject
);
5891 if(!NT_SUCCESS(status
)) {
5892 IoDeleteDevice(deviceObject
);
5893 *DeviceObject
= NULL
;
5895 if (ntUnicodeString
.Buffer
!= NULL
) {
5896 DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5897 ExFreePool(ntUnicodeString
.Buffer
);
5898 RtlInitUnicodeString(&ntUnicodeString
, NULL
);
5906 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
5907 deviceObject
->DeviceExtension
;
5909 PFUNCTIONAL_DEVICE_EXTENSION p0Extension
=
5910 LowerDevice
->DeviceExtension
;
5912 SET_FLAG(deviceObject
->Flags
, DO_POWER_PAGABLE
);
5914 commonExtension
->PartitionZeroExtension
= p0Extension
;
5917 // Stick this onto the PDO list
5920 ClassAddChild(p0Extension
, pdoExtension
, TRUE
);
5922 commonExtension
->DriverData
= (PVOID
) (pdoExtension
+ 1);
5925 // Get the top of stack for the lower device - this allows
5926 // filters to get stuck in between the partitions and the
5930 commonExtension
->LowerDeviceObject
=
5931 IoGetAttachedDeviceReference(LowerDevice
);
5934 // Pnp will keep a reference to the lower device object long
5935 // after this partition has been deleted. Dereference now so
5936 // we don't have to deal with it later.
5939 ObDereferenceObject(commonExtension
->LowerDeviceObject
);
5942 KeInitializeEvent(&commonExtension
->PathCountEvent
, SynchronizationEvent
, TRUE
);
5944 commonExtension
->IsFdo
= IsFdo
;
5946 commonExtension
->DeviceName
= ntUnicodeString
;
5948 commonExtension
->PreviousState
= 0xff;
5950 InitializeDictionary(&(commonExtension
->FileObjectDictionary
));
5952 commonExtension
->CurrentState
= IRP_MN_STOP_DEVICE
;
5955 *DeviceObject
= deviceObject
;
5958 } // end ClassCreateDeviceObject()
5960 /*++////////////////////////////////////////////////////////////////////////////
5964 Routine Description:
5966 This function claims a device in the port driver. The port driver object
5967 is updated with the correct driver object if the device is successfully
5972 LowerDeviceObject - Supplies the base port device object.
5974 Release - Indicates the logical unit should be released rather than claimed.
5978 Returns a status indicating success or failure of the operation.
5983 IN PDEVICE_OBJECT LowerDeviceObject
,
5987 IO_STATUS_BLOCK ioStatus
;
5989 PIO_STACK_LOCATION irpStack
;
5992 SCSI_REQUEST_BLOCK srb
;
5997 // Clear the SRB fields.
6000 RtlZeroMemory(&srb
, sizeof(SCSI_REQUEST_BLOCK
));
6003 // Write length to SRB.
6006 srb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
6008 srb
.Function
= Release
? SRB_FUNCTION_RELEASE_DEVICE
:
6009 SRB_FUNCTION_CLAIM_DEVICE
;
6012 // Set the event object to the unsignaled state.
6013 // It will be used to signal request completion
6016 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
6019 // Build synchronous request with no transfer.
6022 irp
= IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE
,
6033 DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
6034 return STATUS_INSUFFICIENT_RESOURCES
;
6037 irpStack
= IoGetNextIrpStackLocation(irp
);
6040 // Save SRB address in next stack for port driver.
6043 irpStack
->Parameters
.Scsi
.Srb
= &srb
;
6046 // Set up IRP Address.
6049 srb
.OriginalRequest
= irp
;
6052 // Call the port driver with the request and wait for it to complete.
6055 status
= IoCallDriver(LowerDeviceObject
, irp
);
6056 if (status
== STATUS_PENDING
) {
6058 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
6059 status
= ioStatus
.Status
;
6063 // If this is a release request, then just decrement the reference count
6064 // and return. The status does not matter.
6069 // ObDereferenceObject(LowerDeviceObject);
6070 return STATUS_SUCCESS
;
6073 if (!NT_SUCCESS(status
)) {
6077 ASSERT(srb
.DataBuffer
!= NULL
);
6078 ASSERT(!TEST_FLAG(srb
.SrbFlags
, SRB_FLAGS_FREE_SENSE_BUFFER
));
6081 } // end ClassClaimDevice()
6083 /*++////////////////////////////////////////////////////////////////////////////
6085 ClassInternalIoControl()
6087 Routine Description:
6089 This routine passes internal device controls to the port driver.
6090 Internal device controls are used by higher level drivers both for ioctls
6091 and to pass through scsi requests.
6093 If the IoControlCode does not match any of the handled ioctls and is
6094 a valid system address then the request will be treated as an SRB and
6095 passed down to the lower driver. If the IoControlCode is not a valid
6096 system address the ioctl will be failed.
6098 Callers must therefore be extremely cautious to pass correct, initialized
6099 values to this function.
6103 DeviceObject - Supplies a pointer to the device object for this request.
6105 Irp - Supplies the Irp making the request.
6109 Returns back a STATUS_PENDING or a completion status.
6113 ClassInternalIoControl(
6114 IN PDEVICE_OBJECT DeviceObject
,
6118 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6120 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6121 PIO_STACK_LOCATION nextStack
= IoGetNextIrpStackLocation(Irp
);
6125 PSCSI_REQUEST_BLOCK srb
;
6127 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, Irp
);
6131 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
6133 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6135 ClassCompleteRequest(DeviceObject
, Irp
, IO_NO_INCREMENT
);
6137 return STATUS_DEVICE_DOES_NOT_EXIST
;
6141 // Get a pointer to the SRB.
6144 srb
= irpStack
->Parameters
.Scsi
.Srb
;
6147 // Set the parameters in the next stack location.
6150 if(commonExtension
->IsFdo
) {
6151 nextStack
->Parameters
.Scsi
.Srb
= srb
;
6152 nextStack
->MajorFunction
= IRP_MJ_SCSI
;
6153 nextStack
->MinorFunction
= IRP_MN_SCSI_CLASS
;
6157 IoCopyCurrentIrpStackLocationToNext(Irp
);
6160 ClassReleaseRemoveLock(DeviceObject
, Irp
);
6162 return IoCallDriver(commonExtension
->LowerDeviceObject
, Irp
);
6163 } // end ClassInternalIoControl()
6165 /*++////////////////////////////////////////////////////////////////////////////
6167 ClassQueryTimeOutRegistryValue()
6169 Routine Description:
6171 This routine determines whether a reg key for a user-specified timeout
6172 value exists. This should be called at initialization time.
6176 DeviceObject - Pointer to the device object we are retrieving the timeout
6181 None, but it sets a new default timeout for a class of devices.
6185 ClassQueryTimeOutRegistryValue(
6186 IN PDEVICE_OBJECT DeviceObject
6190 // Find the appropriate reg. key
6193 PCLASS_DRIVER_EXTENSION
6194 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6195 CLASS_DRIVER_EXTENSION_KEY
);
6197 PUNICODE_STRING registryPath
= &(driverExtension
->RegistryPath
);
6199 PRTL_QUERY_REGISTRY_TABLE parameters
= NULL
;
6208 if (!registryPath
) {
6212 parameters
= ExAllocatePoolWithTag(NonPagedPool
,
6213 sizeof(RTL_QUERY_REGISTRY_TABLE
)*2,
6220 size
= registryPath
->MaximumLength
+ sizeof(WCHAR
);
6221 path
= ExAllocatePoolWithTag(NonPagedPool
, size
, '2BcS');
6224 ExFreePool(parameters
);
6228 RtlZeroMemory(path
,size
);
6229 RtlCopyMemory(path
, registryPath
->Buffer
, size
- sizeof(WCHAR
));
6233 // Check for the Timeout value.
6236 RtlZeroMemory(parameters
,
6237 (sizeof(RTL_QUERY_REGISTRY_TABLE
)*2));
6239 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
6240 parameters
[0].Name
= L
"TimeOutValue";
6241 parameters
[0].EntryContext
= &timeOut
;
6242 parameters
[0].DefaultType
= REG_DWORD
;
6243 parameters
[0].DefaultData
= &zero
;
6244 parameters
[0].DefaultLength
= sizeof(ULONG
);
6246 status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
| RTL_REGISTRY_OPTIONAL
,
6252 if (!(NT_SUCCESS(status
))) {
6256 ExFreePool(parameters
);
6260 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
6266 } // end ClassQueryTimeOutRegistryValue()
6268 /*++////////////////////////////////////////////////////////////////////////////
6270 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
6272 Routine Description:
6274 This routine executes when the port driver has completed a check verify
6275 ioctl. It will set the status of the master Irp, copy the media change
6276 count and complete the request.
6280 Fdo - Supplies the functional device object which represents the logical unit.
6282 Irp - Supplies the Irp which has completed.
6292 ClassCheckVerifyComplete(
6293 IN PDEVICE_OBJECT Fdo
,
6298 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
6299 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6305 originalIrp
= irpStack
->Parameters
.Others
.Argument1
;
6308 // Copy the media change count and status
6311 *((PULONG
) (originalIrp
->AssociatedIrp
.SystemBuffer
)) =
6312 fdoExtension
->MediaChangeCount
;
6314 DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
6315 "device %d is %lx - saved as %lx\n",
6316 fdoExtension
->DeviceNumber
,
6317 fdoExtension
->MediaChangeCount
,
6318 *((PULONG
) originalIrp
->AssociatedIrp
.SystemBuffer
)));
6320 originalIrp
->IoStatus
.Status
= Irp
->IoStatus
.Status
;
6321 originalIrp
->IoStatus
.Information
= sizeof(ULONG
);
6323 ClassReleaseRemoveLock(Fdo
, originalIrp
);
6324 ClassCompleteRequest(Fdo
, originalIrp
, IO_DISK_INCREMENT
);
6328 return STATUS_MORE_PROCESSING_REQUIRED
;
6330 } // end ClassCheckVerifyComplete()
6332 /*++////////////////////////////////////////////////////////////////////////////
6334 ClassGetDescriptor()
6336 Routine Description:
6338 This routine will perform a query for the specified property id and will
6339 allocate a non-paged buffer to store the data in. It is the responsibility
6340 of the caller to ensure that this buffer is freed.
6342 This routine must be run at IRQL_PASSIVE_LEVEL
6346 DeviceObject - the device to query
6347 DeviceInfo - a location to store a pointer to the buffer we allocate
6352 if status is unsuccessful *DeviceInfo will be set to NULL, else the
6353 buffer allocated on behalf of the caller.
6358 IN PDEVICE_OBJECT DeviceObject
,
6359 IN PSTORAGE_PROPERTY_ID PropertyId
,
6360 OUT PSTORAGE_DESCRIPTOR_HEADER
*Descriptor
6363 STORAGE_PROPERTY_QUERY query
;
6364 IO_STATUS_BLOCK ioStatus
;
6366 PSTORAGE_DESCRIPTOR_HEADER descriptor
= NULL
;
6374 // Set the passed-in descriptor pointer to NULL as default
6380 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6381 query
.PropertyId
= *PropertyId
;
6382 query
.QueryType
= PropertyStandardQuery
;
6385 // On the first pass we just want to get the first few
6386 // bytes of the descriptor so we can read it's size
6389 descriptor
= (PVOID
)&query
;
6391 ASSERT(sizeof(STORAGE_PROPERTY_QUERY
) >= (sizeof(ULONG
)*2));
6393 ClassSendDeviceIoControlSynchronous(
6394 IOCTL_STORAGE_QUERY_PROPERTY
,
6397 sizeof(STORAGE_PROPERTY_QUERY
),
6403 if(!NT_SUCCESS(ioStatus
.Status
)) {
6405 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6406 "query properties #1\n", ioStatus
.Status
));
6407 return ioStatus
.Status
;
6410 if (descriptor
->Size
== 0) {
6413 // This DebugPrint is to help third-party driver writers
6416 DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
6417 "%x\n", ioStatus
.Status
));
6418 return STATUS_UNSUCCESSFUL
;
6423 // This time we know how much data there is so we can
6424 // allocate a buffer of the correct size
6427 length
= descriptor
->Size
;
6429 descriptor
= ExAllocatePoolWithTag(NonPagedPool
, length
, '4BcS');
6431 if(descriptor
== NULL
) {
6433 DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
6434 "(%d bytes)\n", length
));
6435 return STATUS_INSUFFICIENT_RESOURCES
;
6439 // setup the query again, as it was overwritten above
6442 RtlZeroMemory(&query
, sizeof(STORAGE_PROPERTY_QUERY
));
6443 query
.PropertyId
= *PropertyId
;
6444 query
.QueryType
= PropertyStandardQuery
;
6447 // copy the input to the new outputbuffer
6450 RtlCopyMemory(descriptor
,
6452 sizeof(STORAGE_PROPERTY_QUERY
)
6455 ClassSendDeviceIoControlSynchronous(
6456 IOCTL_STORAGE_QUERY_PROPERTY
,
6459 sizeof(STORAGE_PROPERTY_QUERY
),
6465 if(!NT_SUCCESS(ioStatus
.Status
)) {
6467 DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6468 "query properties #1\n", ioStatus
.Status
));
6469 ExFreePool(descriptor
);
6470 return ioStatus
.Status
;
6474 // return the memory we've allocated to the caller
6477 *Descriptor
= descriptor
;
6478 return ioStatus
.Status
;
6479 } // end ClassGetDescriptor()
6481 /*++////////////////////////////////////////////////////////////////////////////
6483 ClassSignalCompletion()
6485 Routine Description:
6487 This completion routine will signal the event given as context and then
6488 return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
6489 the responsibility of the routine waiting on the event to complete the
6490 request and free the event.
6494 DeviceObject - a pointer to the device object
6496 Irp - a pointer to the irp
6498 Event - a pointer to the event to signal
6502 STATUS_MORE_PROCESSING_REQUIRED
6506 ClassSignalCompletion(
6507 IN PDEVICE_OBJECT DeviceObject
,
6512 KeSetEvent(Event
, IO_NO_INCREMENT
, FALSE
);
6514 return STATUS_MORE_PROCESSING_REQUIRED
;
6515 } // end ClassSignalCompletion()
6517 /*++////////////////////////////////////////////////////////////////////////////
6519 ClassPnpQueryFdoRelations()
6521 Routine Description:
6523 This routine will call the driver's enumeration routine to update the
6524 list of PDO's. It will then build a response to the
6525 IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
6530 Fdo - a pointer to the functional device object we are enumerating
6532 Irp - a pointer to the enumeration request
6540 ClassPnpQueryFdoRelations(
6541 IN PDEVICE_OBJECT Fdo
,
6545 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6546 PCLASS_DRIVER_EXTENSION
6547 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6548 CLASS_DRIVER_EXTENSION_KEY
);
6554 // If there's already an enumeration in progress then don't start another
6558 if(InterlockedIncrement(&(fdoExtension
->EnumerationInterlock
)) == 1) {
6559 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6562 Irp
->IoStatus
.Information
= (ULONG_PTR
) NULL
;
6564 Irp
->IoStatus
.Status
= ClassRetrieveDeviceRelations(
6567 (PDEVICE_RELATIONS
)&Irp
->IoStatus
.Information
);
6568 InterlockedDecrement(&(fdoExtension
->EnumerationInterlock
));
6570 return Irp
->IoStatus
.Status
;
6571 } // end ClassPnpQueryFdoRelations()
6573 /*++////////////////////////////////////////////////////////////////////////////
6575 ClassMarkChildrenMissing()
6577 Routine Description:
6579 This routine will call ClassMarkChildMissing() for all children.
6580 It acquires the ChildLock before calling ClassMarkChildMissing().
6584 Fdo - the "bus's" device object, such as the disk FDO for non-removable
6585 disks with multiple partitions.
6593 ClassMarkChildrenMissing(
6594 IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
6597 PCOMMON_DEVICE_EXTENSION commonExtension
= &(Fdo
->CommonExtension
);
6598 PPHYSICAL_DEVICE_EXTENSION nextChild
= commonExtension
->ChildList
;
6602 ClassAcquireChildLock(Fdo
);
6605 PPHYSICAL_DEVICE_EXTENSION tmpChild
;
6608 * ClassMarkChildMissing will also dequeue the child extension.
6609 * So get the next pointer before calling ClassMarkChildMissing.
6611 tmpChild
= nextChild
;
6612 nextChild
= tmpChild
->CommonExtension
.ChildList
;
6613 ClassMarkChildMissing(tmpChild
, FALSE
);
6615 ClassReleaseChildLock(Fdo
);
6617 } // end ClassMarkChildrenMissing()
6619 /*++////////////////////////////////////////////////////////////////////////////
6621 ClassMarkChildMissing()
6623 Routine Description:
6625 This routine will make an active child "missing." If the device has never
6626 been enumerated then it will be deleted on the spot. If the device has
6627 not been enumerated then it will be marked as missing so that we can
6628 not report it in the next device enumeration.
6632 Child - the child device to be marked as missing.
6634 AcquireChildLock - TRUE if the child lock should be acquired before removing
6635 the missing child. FALSE if the child lock is already
6636 acquired by this thread.
6640 returns whether or not the child device object has previously been reported
6645 ClassMarkChildMissing(
6646 IN PPHYSICAL_DEVICE_EXTENSION Child
,
6647 IN BOOLEAN AcquireChildLock
6650 BOOLEAN returnValue
= Child
->IsEnumerated
;
6653 ASSERT_PDO(Child
->DeviceObject
);
6655 Child
->IsMissing
= TRUE
;
6658 // Make sure this child is not in the active list.
6661 ClassRemoveChild(Child
->CommonExtension
.PartitionZeroExtension
,
6665 if(Child
->IsEnumerated
== FALSE
) {
6666 ClassRemoveDevice(Child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
6670 } // end ClassMarkChildMissing()
6672 /*++////////////////////////////////////////////////////////////////////////////
6674 ClassRetrieveDeviceRelations()
6676 Routine Description:
6678 This routine will allocate a buffer to hold the specified list of
6679 relations. It will then fill in the list with referenced device pointers
6680 and will return the request.
6684 Fdo - pointer to the FDO being queried
6686 RelationType - what type of relations are being queried
6688 DeviceRelations - a location to store a pointer to the response
6696 ClassRetrieveDeviceRelations(
6697 IN PDEVICE_OBJECT Fdo
,
6698 IN DEVICE_RELATION_TYPE RelationType
,
6699 OUT PDEVICE_RELATIONS
*DeviceRelations
6702 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6707 PPHYSICAL_DEVICE_EXTENSION nextChild
;
6709 ULONG relationsSize
;
6710 PDEVICE_RELATIONS deviceRelations
= NULL
;
6716 ClassAcquireChildLock(fdoExtension
);
6718 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6721 // Count the number of PDO's attached to this disk
6724 while(nextChild
!= NULL
) {
6725 PCOMMON_DEVICE_EXTENSION commonExtension
;
6727 commonExtension
= &(nextChild
->CommonExtension
);
6729 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6730 (nextChild
->IsMissing
== FALSE
));
6732 nextChild
= commonExtension
->ChildList
;
6737 relationsSize
= (sizeof(DEVICE_RELATIONS
) +
6738 (count
* sizeof(PDEVICE_OBJECT
)));
6740 deviceRelations
= ExAllocatePoolWithTag(PagedPool
, relationsSize
, '5BcS');
6742 if(deviceRelations
== NULL
) {
6744 DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
6745 "%d bytes for device relations\n", relationsSize
));
6747 ClassReleaseChildLock(fdoExtension
);
6749 return STATUS_INSUFFICIENT_RESOURCES
;
6752 RtlZeroMemory(deviceRelations
, relationsSize
);
6754 nextChild
= fdoExtension
->CommonExtension
.ChildList
;
6757 while(nextChild
!= NULL
) {
6758 PCOMMON_DEVICE_EXTENSION commonExtension
;
6760 commonExtension
= &(nextChild
->CommonExtension
);
6762 ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6763 (nextChild
->IsMissing
== FALSE
));
6765 deviceRelations
->Objects
[i
--] = nextChild
->DeviceObject
;
6767 status
= ObReferenceObjectByPointer(
6768 nextChild
->DeviceObject
,
6772 ASSERT(NT_SUCCESS(status
));
6774 nextChild
->IsEnumerated
= TRUE
;
6775 nextChild
= commonExtension
->ChildList
;
6778 ASSERTMSG("Child list has changed: ", i
== -1);
6780 deviceRelations
->Count
= count
;
6781 *DeviceRelations
= deviceRelations
;
6782 ClassReleaseChildLock(fdoExtension
);
6783 return STATUS_SUCCESS
;
6784 } // end ClassRetrieveDeviceRelations()
6786 /*++////////////////////////////////////////////////////////////////////////////
6790 Routine Description:
6792 This routine will call into the driver to retrieve a copy of one of it's
6797 Pdo - a pointer to the pdo being queried
6799 IdType - which type of id string is being queried
6801 IdString - an allocated unicode string structure which the driver
6811 IN PDEVICE_OBJECT Pdo
,
6812 IN BUS_QUERY_ID_TYPE IdType
,
6813 IN PUNICODE_STRING IdString
6816 PCLASS_DRIVER_EXTENSION
6817 driverExtension
= IoGetDriverObjectExtension(Pdo
->DriverObject
,
6818 CLASS_DRIVER_EXTENSION_KEY
);
6821 ASSERT(driverExtension
->InitData
.ClassQueryId
);
6825 return driverExtension
->InitData
.ClassQueryId( Pdo
, IdType
, IdString
);
6826 } // end ClassGetPdoId()
6828 /*++////////////////////////////////////////////////////////////////////////////
6830 ClassQueryPnpCapabilities()
6832 Routine Description:
6834 This routine will call into the class driver to retrieve it's pnp
6839 PhysicalDeviceObject - The physical device object to retrieve properties
6848 ClassQueryPnpCapabilities(
6849 IN PDEVICE_OBJECT DeviceObject
,
6850 IN PDEVICE_CAPABILITIES Capabilities
6853 PCLASS_DRIVER_EXTENSION driverExtension
=
6854 ClassGetDriverExtension(DeviceObject
->DriverObject
);
6855 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6857 PCLASS_QUERY_PNP_CAPABILITIES queryRoutine
= NULL
;
6861 ASSERT(DeviceObject
);
6862 ASSERT(Capabilities
);
6864 if(commonExtension
->IsFdo
) {
6865 queryRoutine
= driverExtension
->InitData
.FdoData
.ClassQueryPnpCapabilities
;
6867 queryRoutine
= driverExtension
->InitData
.PdoData
.ClassQueryPnpCapabilities
;
6871 return queryRoutine(DeviceObject
,
6874 return STATUS_NOT_IMPLEMENTED
;
6876 } // end ClassQueryPnpCapabilities()
6878 /*++////////////////////////////////////////////////////////////////////////////
6880 ClassInvalidateBusRelations()
6882 Routine Description:
6884 This routine re-enumerates the devices on the "bus". It will call into
6885 the driver's ClassEnumerate routine to update the device objects
6886 immediately. It will then schedule a bus re-enumeration for pnp by calling
6887 IoInvalidateDeviceRelations.
6891 Fdo - a pointer to the functional device object for this bus
6899 ClassInvalidateBusRelations(
6900 IN PDEVICE_OBJECT Fdo
6903 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
6904 PCLASS_DRIVER_EXTENSION
6905 driverExtension
= IoGetDriverObjectExtension(Fdo
->DriverObject
,
6906 CLASS_DRIVER_EXTENSION_KEY
);
6908 NTSTATUS status
= STATUS_SUCCESS
;
6913 ASSERT(driverExtension
->InitData
.ClassEnumerateDevice
!= NULL
);
6915 if(InterlockedIncrement(&(fdoExtension
->EnumerationInterlock
)) == 1) {
6916 status
= driverExtension
->InitData
.ClassEnumerateDevice(Fdo
);
6918 InterlockedDecrement(&(fdoExtension
->EnumerationInterlock
));
6920 if(!NT_SUCCESS(status
)) {
6922 DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
6923 "returned %lx\n", status
));
6926 IoInvalidateDeviceRelations(fdoExtension
->LowerPdo
, BusRelations
);
6929 } // end ClassInvalidateBusRelations()
6931 /*++////////////////////////////////////////////////////////////////////////////
6933 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
6935 Routine Description:
6937 This routine is called to handle the "removal" of a device. It will
6938 forward the request downwards if necesssary, call into the driver
6939 to release any necessary resources (memory, events, etc) and then
6940 will delete the device object.
6944 DeviceObject - a pointer to the device object being removed
6946 RemoveType - indicates what type of remove this is (regular or surprise).
6955 IN PDEVICE_OBJECT DeviceObject
,
6959 PCLASS_DRIVER_EXTENSION
6960 driverExtension
= IoGetDriverObjectExtension(DeviceObject
->DriverObject
,
6961 CLASS_DRIVER_EXTENSION_KEY
);
6962 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
6963 PDEVICE_OBJECT lowerDeviceObject
= commonExtension
->LowerDeviceObject
;
6964 PCLASS_WMI_INFO classWmiInfo
;
6965 BOOLEAN proceedWithRemove
= TRUE
;
6970 commonExtension
->IsRemoved
= REMOVE_PENDING
;
6973 * Deregister from WMI.
6975 classWmiInfo
= commonExtension
->IsFdo
?
6976 &driverExtension
->InitData
.FdoData
.ClassWmiInfo
:
6977 &driverExtension
->InitData
.PdoData
.ClassWmiInfo
;
6978 if (classWmiInfo
->GuidRegInfo
){
6979 status
= IoWMIRegistrationControl(DeviceObject
, WMIREG_ACTION_DEREGISTER
);
6980 DBGTRACE(ClassDebugInfo
, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject
, status
));
6984 * If we exposed a "shingle" (a named device interface openable by CreateFile)
6985 * then delete it now.
6987 if (commonExtension
->MountedDeviceInterfaceName
.Buffer
){
6988 IoSetDeviceInterfaceState(&commonExtension
->MountedDeviceInterfaceName
, FALSE
);
6989 RtlFreeUnicodeString(&commonExtension
->MountedDeviceInterfaceName
);
6990 RtlInitUnicodeString(&commonExtension
->MountedDeviceInterfaceName
, NULL
);
6994 // If this is a surprise removal we leave the device around - which means
6995 // we don't have to (or want to) drop the remove lock and wait for pending
6996 // requests to complete.
6999 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7002 // Release the lock we acquired when the device object was created.
7005 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7007 DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
7008 commonExtension
->RemoveLock
));
7010 KeWaitForSingleObject(&commonExtension
->RemoveEvent
,
7016 DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject
));
7018 if(commonExtension
->IsFdo
) {
7020 DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
7021 "remove request.\n", DeviceObject
));
7025 PPHYSICAL_DEVICE_EXTENSION pdoExtension
= DeviceObject
->DeviceExtension
;
7027 if (pdoExtension
->IsMissing
){
7029 * The child partition PDO is missing, so we are going to go ahead
7030 * and delete it for the remove.
7032 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject
));
7036 * We got a remove for a child partition PDO which is not actually missing.
7037 * So we will NOT actually delete it.
7039 DBGTRACE(ClassDebugWarning
, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject
));
7042 // Reacquire the remove lock for the next time this comes around.
7045 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) DeviceObject
);
7048 // the device wasn't missing so it's not really been removed.
7051 commonExtension
->IsRemoved
= NO_REMOVE
;
7053 IoInvalidateDeviceRelations(
7054 commonExtension
->PartitionZeroExtension
->LowerPdo
,
7057 proceedWithRemove
= FALSE
;
7063 if (proceedWithRemove
){
7066 * Call the class driver's remove handler.
7067 * All this is supposed to do is clean up its data and device interfaces.
7069 ASSERT(commonExtension
->DevInfo
->ClassRemoveDevice
);
7070 status
= commonExtension
->DevInfo
->ClassRemoveDevice(DeviceObject
, RemoveType
);
7071 ASSERT(NT_SUCCESS(status
));
7072 status
= STATUS_SUCCESS
;
7074 if (commonExtension
->IsFdo
){
7076 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
7078 ClasspDisableTimer(fdoExtension
->DeviceObject
);
7080 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7082 PPHYSICAL_DEVICE_EXTENSION child
;
7085 // Cleanup the media detection resources now that the class driver
7086 // has stopped it's timer (if any) and we can be sure they won't
7087 // call us to do detection again.
7090 ClassCleanupMediaChangeDetection(fdoExtension
);
7093 // Cleanup any Failure Prediction stuff
7095 if (fdoExtension
->FailurePredictionInfo
) {
7096 ExFreePool(fdoExtension
->FailurePredictionInfo
);
7097 fdoExtension
->FailurePredictionInfo
= NULL
;
7101 * Ordinarily all child PDOs will be removed by the time
7102 * that the parent gets the REMOVE_DEVICE.
7103 * However, if a child PDO has been created but has not
7104 * been announced in a QueryDeviceRelations, then it is
7105 * just a private data structure unknown to pnp, and we have
7106 * to delete it ourselves.
7108 ClassAcquireChildLock(fdoExtension
);
7109 while (child
= ClassRemoveChild(fdoExtension
, NULL
, FALSE
)){
7112 // Yank the pdo. This routine will unlink the device from the
7113 // pdo list so NextPdo will point to the next one when it's
7116 child
->IsMissing
= TRUE
;
7117 ClassRemoveDevice(child
->DeviceObject
, IRP_MN_REMOVE_DEVICE
);
7119 ClassReleaseChildLock(fdoExtension
);
7121 else if (RemoveType
== IRP_MN_SURPRISE_REMOVAL
){
7123 * This is a surprise-remove on the parent FDO.
7124 * We will mark the child PDOs as missing so that they
7125 * will actually get deleted when they get a REMOVE_DEVICE.
7127 ClassMarkChildrenMissing(fdoExtension
);
7130 ClasspFreeReleaseRequest(DeviceObject
);
7132 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7135 // Free FDO-specific data structs
7137 if (fdoExtension
->PrivateFdoData
){
7139 DestroyAllTransferPackets(DeviceObject
);
7141 ExFreePool(fdoExtension
->PrivateFdoData
);
7142 fdoExtension
->PrivateFdoData
= NULL
;
7145 if (commonExtension
->DeviceName
.Buffer
) {
7146 ExFreePool(commonExtension
->DeviceName
.Buffer
);
7147 RtlInitUnicodeString(&commonExtension
->DeviceName
, NULL
);
7150 if (fdoExtension
->AdapterDescriptor
) {
7151 ExFreePool(fdoExtension
->AdapterDescriptor
);
7152 fdoExtension
->AdapterDescriptor
= NULL
;
7155 if (fdoExtension
->DeviceDescriptor
) {
7156 ExFreePool(fdoExtension
->DeviceDescriptor
);
7157 fdoExtension
->DeviceDescriptor
= NULL
;
7161 // Detach our device object from the stack - there's no reason
7162 // to hold off our cleanup any longer.
7165 IoDetachDevice(lowerDeviceObject
);
7170 * This is a child partition PDO.
7171 * We have already determined that it was previously marked
7172 * as missing. So if this is a REMOVE_DEVICE, we will actually
7175 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7176 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
=
7177 commonExtension
->PartitionZeroExtension
;
7178 PPHYSICAL_DEVICE_EXTENSION pdoExtension
=
7179 (PPHYSICAL_DEVICE_EXTENSION
) commonExtension
;
7182 // See if this device is in the child list (if this was a suprise
7183 // removal it might be) and remove it.
7186 ClassRemoveChild(fdoExtension
, pdoExtension
, TRUE
);
7190 commonExtension
->PartitionLength
.QuadPart
= 0;
7192 if (RemoveType
== IRP_MN_REMOVE_DEVICE
){
7193 IoDeleteDevice(DeviceObject
);
7197 return STATUS_SUCCESS
;
7198 } // end ClassRemoveDevice()
7200 /*++////////////////////////////////////////////////////////////////////////////
7202 ClassGetDriverExtension()
7204 Routine Description:
7206 This routine will return the classpnp's driver extension.
7210 DriverObject - the driver object for which to get classpnp's extension
7214 Either NULL if none, or a pointer to the driver extension
7217 PCLASS_DRIVER_EXTENSION
7218 ClassGetDriverExtension(
7219 IN PDRIVER_OBJECT DriverObject
7222 return IoGetDriverObjectExtension(DriverObject
, CLASS_DRIVER_EXTENSION_KEY
);
7223 } // end ClassGetDriverExtension()
7225 /*++////////////////////////////////////////////////////////////////////////////
7229 Routine Description:
7231 This routine wraps the class driver's start io routine. If the device
7232 is being removed it will complete any requests with
7233 STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
7244 IN PDEVICE_OBJECT DeviceObject
,
7248 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7251 // We're already holding the remove lock so just check the variable and
7252 // see what's going on.
7255 if(commonExtension
->IsRemoved
) {
7257 Irp
->IoStatus
.Status
= STATUS_DEVICE_DOES_NOT_EXIST
;
7259 ClassAcquireRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7260 ClassReleaseRemoveLock(DeviceObject
, Irp
);
7261 ClassCompleteRequest(DeviceObject
, Irp
, IO_DISK_INCREMENT
);
7262 IoStartNextPacket(DeviceObject
, FALSE
);
7263 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) ClasspStartIo
);
7267 commonExtension
->DriverExtension
->InitData
.ClassStartIo(
7272 } // ClasspStartIo()
7274 /*++////////////////////////////////////////////////////////////////////////////
7276 ClassUpdateInformationInRegistry()
7278 Routine Description:
7280 This routine has knowledge about the layout of the device map information
7281 in the registry. It will update this information to include a value
7282 entry specifying the dos device name that is assumed to get assigned
7283 to this NT device name. For more information on this assigning of the
7284 dos device name look in the drive support routine in the hal that assigns
7287 Since some versions of some device's firmware did not work and some
7288 vendors did not bother to follow the specification, the entire inquiry
7289 information must also be stored in the registry so than someone can
7290 figure out the firmware version.
7294 DeviceObject - A pointer to the device object for the tape device.
7302 ClassUpdateInformationInRegistry(
7303 IN PDEVICE_OBJECT Fdo
,
7304 IN PCHAR DeviceName
,
7305 IN ULONG DeviceNumber
,
7306 IN PINQUIRYDATA InquiryData
,
7307 IN ULONG InquiryDataLength
7310 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
7312 SCSI_ADDRESS scsiAddress
;
7313 OBJECT_ATTRIBUTES objectAttributes
;
7316 UNICODE_STRING unicodeName
;
7317 UNICODE_STRING unicodeRegistryPath
;
7318 UNICODE_STRING unicodeData
;
7320 IO_STATUS_BLOCK ioStatus
;
7326 fdoExtension
= Fdo
->DeviceExtension
;
7329 RtlZeroMemory(&unicodeName
, sizeof(UNICODE_STRING
));
7330 RtlZeroMemory(&unicodeData
, sizeof(UNICODE_STRING
));
7331 RtlZeroMemory(&unicodeRegistryPath
, sizeof(UNICODE_STRING
));
7336 // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
7339 ClassSendDeviceIoControlSynchronous(
7340 IOCTL_SCSI_GET_ADDRESS
,
7344 sizeof(SCSI_ADDRESS
),
7349 if (!NT_SUCCESS(ioStatus
.Status
)) {
7351 status
= ioStatus
.Status
;
7353 "UpdateInformationInRegistry: Get Address failed %lx\n",
7360 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
7361 scsiAddress
.PortNumber
,
7363 scsiAddress
.TargetId
,
7369 // Allocate a buffer for the reg. spooge.
7372 buffer
= ExAllocatePoolWithTag(PagedPool
, 1024, '6BcS');
7374 if (buffer
== NULL
) {
7377 // There is not return value for this. Since this is done at
7378 // claim device time (currently only system initialization) getting
7379 // the registry information correct will be the least of the worries.
7386 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
7387 scsiAddress
.PortNumber
,
7389 scsiAddress
.TargetId
,
7392 RtlInitString(&string
, buffer
);
7394 status
= RtlAnsiStringToUnicodeString(&unicodeRegistryPath
,
7398 if (!NT_SUCCESS(status
)) {
7403 // Open the registry key for the scsi information for this
7404 // scsibus, target, lun.
7407 InitializeObjectAttributes(&objectAttributes
,
7408 &unicodeRegistryPath
,
7409 OBJ_CASE_INSENSITIVE
,
7413 status
= ZwOpenKey(&targetKey
,
7414 KEY_READ
| KEY_WRITE
,
7417 if (!NT_SUCCESS(status
)) {
7422 // Now construct and attempt to create the registry value
7423 // specifying the device name in the appropriate place in the
7427 RtlInitUnicodeString(&unicodeName
, L
"DeviceName");
7429 sprintf(buffer
, "%s%d", DeviceName
, DeviceNumber
);
7430 RtlInitString(&string
, buffer
);
7431 status
= RtlAnsiStringToUnicodeString(&unicodeData
,
7434 if (NT_SUCCESS(status
)) {
7435 status
= ZwSetValueKey(targetKey
,
7440 unicodeData
.Length
);
7444 // if they sent in data, update the registry
7447 if (InquiryDataLength
) {
7449 ASSERT(InquiryData
);
7451 RtlInitUnicodeString(&unicodeName
, L
"InquiryData");
7452 status
= ZwSetValueKey(targetKey
,
7460 // that's all, except to clean up.
7464 if (unicodeData
.Buffer
) {
7465 RtlFreeUnicodeString(&unicodeData
);
7467 if (unicodeRegistryPath
.Buffer
) {
7468 RtlFreeUnicodeString(&unicodeRegistryPath
);
7479 } // end ClassUpdateInformationInRegistry()
7481 /*++////////////////////////////////////////////////////////////////////////////
7483 ClasspSendSynchronousCompletion()
7485 Routine Description:
7487 This completion routine will set the user event in the irp after
7488 freeing the irp and the associated MDL (if any).
7492 DeviceObject - the device object which requested the completion routine
7494 Irp - the irp being completed
7500 STATUS_MORE_PROCESSING_REQUIRED
7504 ClasspSendSynchronousCompletion(
7505 IN PDEVICE_OBJECT DeviceObject
,
7510 DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
7511 DeviceObject
, Irp
, Context
));
7513 // First set the status and information fields in the io status block
7514 // provided by the caller.
7517 *(Irp
->UserIosb
) = Irp
->IoStatus
;
7520 // Unlock the pages for the data buffer.
7523 if(Irp
->MdlAddress
) {
7524 MmUnlockPages(Irp
->MdlAddress
);
7525 IoFreeMdl(Irp
->MdlAddress
);
7529 // Signal the caller's event.
7532 KeSetEvent(Irp
->UserEvent
, IO_NO_INCREMENT
, FALSE
);
7535 // Free the MDL and the IRP.
7540 return STATUS_MORE_PROCESSING_REQUIRED
;
7541 } // end ClasspSendSynchronousCompletion()
7545 ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
7549 ClasspRegisterMountedDeviceInterface(
7550 IN PDEVICE_OBJECT DeviceObject
7554 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
7555 BOOLEAN isFdo
= commonExtension
->IsFdo
;
7558 UNICODE_STRING interfaceName
;
7564 PFUNCTIONAL_DEVICE_EXTENSION functionalExtension
;
7566 functionalExtension
=
7567 (PFUNCTIONAL_DEVICE_EXTENSION
) commonExtension
;
7568 pdo
= functionalExtension
->LowerPdo
;
7573 status
= IoRegisterDeviceInterface(
7575 &MOUNTDEV_MOUNTED_DEVICE_GUID
,
7580 if(NT_SUCCESS(status
)) {
7583 // Copy the interface name before setting the interface state - the
7584 // name is needed by the components we notify.
7587 commonExtension
->MountedDeviceInterfaceName
= interfaceName
;
7588 status
= IoSetDeviceInterfaceState(&interfaceName
, TRUE
);
7590 if(!NT_SUCCESS(status
)) {
7591 RtlFreeUnicodeString(&interfaceName
);
7595 if(!NT_SUCCESS(status
)) {
7596 RtlInitUnicodeString(&(commonExtension
->MountedDeviceInterfaceName
),
7600 } // end ClasspRegisterMountedDeviceInterface()
7602 /*++////////////////////////////////////////////////////////////////////////////
7604 ClassSendDeviceIoControlSynchronous()
7606 Routine Description:
7608 This routine is based upon IoBuildDeviceIoControlRequest(). It has been
7609 modified to reduce code and memory by not double-buffering the io, using
7610 the same buffer for both input and output, allocating and deallocating
7611 the mdl on behalf of the caller, and waiting for the io to complete.
7613 This routine also works around the rare cases in which APC's are disabled.
7614 Since IoBuildDeviceIoControl() used APC's to signal completion, this had
7615 led to a number of difficult-to-detect hangs, where the irp was completed,
7616 but the event passed to IoBuild..() was still being waited upon by the
7621 IoControlCode - the IOCTL to send
7623 TargetDeviceObject - the device object that should handle the ioctl
7625 Buffer - the input and output buffer, or NULL if no input/output
7627 InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
7629 OutputBufferLength - the number of bytes to be filled in upon success
7631 InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
7633 IoStatus - the status block that contains the results of the operation
7639 ClassSendDeviceIoControlSynchronous(
7640 IN ULONG IoControlCode
,
7641 IN PDEVICE_OBJECT TargetDeviceObject
,
7642 IN OUT PVOID Buffer OPTIONAL
,
7643 IN ULONG InputBufferLength
,
7644 IN ULONG OutputBufferLength
,
7645 IN BOOLEAN InternalDeviceIoControl
,
7646 OUT PIO_STATUS_BLOCK IoStatus
7650 PIO_STACK_LOCATION irpSp
;
7656 method
= IoControlCode
& 3;
7659 #if DBG // Begin Argument Checking (nop in fre version)
7661 ASSERT(ARGUMENT_PRESENT(IoStatus
));
7663 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7664 ASSERT(ARGUMENT_PRESENT(Buffer
));
7667 ASSERT(!ARGUMENT_PRESENT(Buffer
));
7672 // Begin by allocating the IRP for this request. Do not charge quota to
7673 // the current process for this IRP.
7676 irp
= IoAllocateIrp(TargetDeviceObject
->StackSize
, FALSE
);
7678 (*IoStatus
).Information
= 0;
7679 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7684 // Get a pointer to the stack location of the first driver which will be
7685 // invoked. This is where the function codes and the parameters are set.
7688 irpSp
= IoGetNextIrpStackLocation(irp
);
7691 // Set the major function code based on the type of device I/O control
7692 // function the caller has specified.
7695 if (InternalDeviceIoControl
) {
7696 irpSp
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
7698 irpSp
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
7702 // Copy the caller's parameters to the service-specific portion of the
7703 // IRP for those parameters that are the same for all four methods.
7706 irpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
= OutputBufferLength
;
7707 irpSp
->Parameters
.DeviceIoControl
.InputBufferLength
= InputBufferLength
;
7708 irpSp
->Parameters
.DeviceIoControl
.IoControlCode
= IoControlCode
;
7711 // Get the method bits from the I/O control code to determine how the
7712 // buffers are to be passed to the driver.
7717 case METHOD_BUFFERED
: {
7718 if ((InputBufferLength
!= 0) || (OutputBufferLength
!= 0)) {
7720 irp
->AssociatedIrp
.SystemBuffer
=
7721 ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
7722 max(InputBufferLength
, OutputBufferLength
),
7723 CLASS_TAG_DEVICE_CONTROL
7726 if (irp
->AssociatedIrp
.SystemBuffer
== NULL
) {
7728 (*IoStatus
).Information
= 0;
7729 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7733 if (InputBufferLength
!= 0) {
7734 RtlCopyMemory(irp
->AssociatedIrp
.SystemBuffer
,
7738 } // end of buffering
7740 irp
->UserBuffer
= Buffer
;
7745 case METHOD_IN_DIRECT
:
7746 case METHOD_OUT_DIRECT
: {
7749 if (InputBufferLength
!= 0) {
7750 irp
->AssociatedIrp
.SystemBuffer
= Buffer
;
7753 if (OutputBufferLength
!= 0) {
7755 irp
->MdlAddress
= IoAllocateMdl(Buffer
,
7760 if (irp
->MdlAddress
== NULL
) {
7762 (*IoStatus
).Information
= 0;
7763 (*IoStatus
).Status
= STATUS_INSUFFICIENT_RESOURCES
;
7767 if (method
== METHOD_IN_DIRECT
) {
7768 MmProbeAndLockPages(irp
->MdlAddress
,
7771 } else if (method
== METHOD_OUT_DIRECT
) {
7772 MmProbeAndLockPages(irp
->MdlAddress
,
7776 ASSERT(!"If other methods reach here, code is out of date");
7783 case METHOD_NEITHER
: {
7785 ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
7786 IoStatus
->Information
= 0;
7787 IoStatus
->Status
= STATUS_NOT_SUPPORTED
;
7791 } // end of switch(method)
7793 irp
->Tail
.Overlay
.Thread
= PsGetCurrentThread();
7796 // send the irp synchronously
7799 ClassSendIrpSynchronous(TargetDeviceObject
, irp
);
7802 // copy the iostatus block for the caller
7805 *IoStatus
= irp
->IoStatus
;
7808 // free any allocated resources
7812 case METHOD_BUFFERED
: {
7814 ASSERT(irp
->UserBuffer
== Buffer
);
7817 // first copy the buffered result, if any
7818 // Note that there are no security implications in
7819 // not checking for success since only drivers can
7820 // call into this routine anyways...
7823 if (OutputBufferLength
!= 0) {
7824 RtlCopyMemory(Buffer
, // irp->UserBuffer
7825 irp
->AssociatedIrp
.SystemBuffer
,
7831 // then free the memory allocated to buffer the io
7834 if ((InputBufferLength
!=0) || (OutputBufferLength
!= 0)) {
7835 ExFreePool(irp
->AssociatedIrp
.SystemBuffer
);
7836 irp
->AssociatedIrp
.SystemBuffer
= NULL
;
7841 case METHOD_IN_DIRECT
:
7842 case METHOD_OUT_DIRECT
: {
7845 // we alloc a mdl if there is an output buffer specified
7846 // free it here after unlocking the pages
7849 if (OutputBufferLength
!= 0) {
7850 ASSERT(irp
->MdlAddress
!= NULL
);
7851 MmUnlockPages(irp
->MdlAddress
);
7852 IoFreeMdl(irp
->MdlAddress
);
7853 irp
->MdlAddress
= (PMDL
) NULL
;
7858 case METHOD_NEITHER
: {
7859 ASSERT(!"Code is out of date");
7865 // we always have allocated an irp. free it here.
7872 // return the io status block's status to the caller
7876 } // end ClassSendDeviceIoControlSynchronous()
7878 /*++////////////////////////////////////////////////////////////////////////////
7880 ClassForwardIrpSynchronous()
7882 Routine Description:
7884 Forwards a given irp to the next lower device object.
7888 CommonExtension - the common class extension
7890 Irp - the request to forward down the stack
7896 ClassForwardIrpSynchronous(
7897 IN PCOMMON_DEVICE_EXTENSION CommonExtension
,
7901 IoCopyCurrentIrpStackLocationToNext(Irp
);
7902 return ClassSendIrpSynchronous(CommonExtension
->LowerDeviceObject
, Irp
);
7903 } // end ClassForwardIrpSynchronous()
7905 /*++////////////////////////////////////////////////////////////////////////////
7907 ClassSendIrpSynchronous()
7909 Routine Description:
7911 This routine sends the given irp to the given device object, and waits for
7912 it to complete. On debug versions, will print out a debug message and
7913 optionally assert for "lost" irps based upon classpnp's globals
7917 TargetDeviceObject - the device object to handle this irp
7919 Irp - the request to be sent
7925 ClassSendIrpSynchronous(
7926 IN PDEVICE_OBJECT TargetDeviceObject
,
7933 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL
);
7934 ASSERT(TargetDeviceObject
!= NULL
);
7935 ASSERT(Irp
!= NULL
);
7936 ASSERT(Irp
->StackCount
>= TargetDeviceObject
->StackSize
);
7939 // ISSUE-2000/02/20-henrygab What if APCs are disabled?
7940 // May need to enter critical section before IoCallDriver()
7941 // until the event is hit?
7944 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
7945 IoSetCompletionRoutine(Irp
, ClassSignalCompletion
, &event
,
7948 status
= IoCallDriver(TargetDeviceObject
, Irp
);
7950 if (status
== STATUS_PENDING
) {
7953 LARGE_INTEGER timeout
;
7955 timeout
.QuadPart
= (LONGLONG
)(-1 * 10 * 1000 * (LONGLONG
)1000 *
7956 ClasspnpGlobals
.SecondsToWaitForIrps
);
7959 status
= KeWaitForSingleObject(&event
,
7966 if (status
== STATUS_TIMEOUT
) {
7969 // This DebugPrint should almost always be investigated by the
7970 // party who sent the irp and/or the current owner of the irp.
7971 // Synchronous Irps should not take this long (currently 30
7972 // seconds) without good reason. This points to a potentially
7973 // serious problem in the underlying device stack.
7976 DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
7977 "complete within %x seconds\n",
7978 TargetDeviceObject
, Irp
,
7979 ClasspnpGlobals
.SecondsToWaitForIrps
7982 if (ClasspnpGlobals
.BreakOnLostIrps
!= 0) {
7983 ASSERT(!" - Irp failed to complete within 30 seconds - ");
7988 } while (status
==STATUS_TIMEOUT
);
7990 KeWaitForSingleObject(&event
,
7997 status
= Irp
->IoStatus
.Status
;
8001 } // end ClassSendIrpSynchronous()
8003 /*++////////////////////////////////////////////////////////////////////////////
8007 Routine Description:
8009 This routine returns the current VPB (Volume Parameter Block) for the
8010 given device object.
8011 The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
8012 of DEVICE_OBJECT; hence this exported function.
8016 DeviceObject - the device to get the VPB for
8020 the VPB, or NULL if none.
8025 IN PDEVICE_OBJECT DeviceObject
8028 return DeviceObject
->Vpb
;
8029 } // end ClassGetVpb()
8033 ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
8037 ClasspAllocateReleaseRequest(
8038 IN PDEVICE_OBJECT Fdo
8041 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8042 PIO_STACK_LOCATION irpStack
;
8044 KeInitializeSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
));
8046 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8047 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8048 fdoExtension
->ReleaseQueueIrpFromPool
= FALSE
;
8051 // The class driver is responsible for allocating a properly sized irp,
8052 // or ClassReleaseQueue will attempt to do it on the first error.
8055 fdoExtension
->ReleaseQueueIrp
= NULL
;
8058 // Write length to SRB.
8061 fdoExtension
->ReleaseQueueSrb
.Length
= sizeof(SCSI_REQUEST_BLOCK
);
8063 return STATUS_SUCCESS
;
8064 } // end ClasspAllocateReleaseRequest()
8068 ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
8072 ClasspFreeReleaseRequest(
8073 IN PDEVICE_OBJECT Fdo
8076 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8079 ASSERT(fdoExtension
->CommonExtension
.IsRemoved
!= NO_REMOVE
);
8082 // free anything the driver allocated
8085 if (fdoExtension
->ReleaseQueueIrp
) {
8086 if (fdoExtension
->ReleaseQueueIrpFromPool
) {
8087 ExFreePool(fdoExtension
->ReleaseQueueIrp
);
8089 IoFreeIrp(fdoExtension
->ReleaseQueueIrp
);
8091 fdoExtension
->ReleaseQueueIrp
= NULL
;
8095 // free anything that we allocated
8098 if ((fdoExtension
->PrivateFdoData
) &&
8099 (fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
)) {
8101 ExFreePool(fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
);
8102 fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= FALSE
;
8103 fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
= NULL
;
8107 } // end ClasspFreeReleaseRequest()
8109 /*++////////////////////////////////////////////////////////////////////////////
8113 Routine Description:
8115 This routine issues an internal device control command
8116 to the port driver to release a frozen queue. The call
8117 is issued asynchronously as ClassReleaseQueue will be invoked
8118 from the IO completion DPC (and will have no context to
8119 wait for a synchronous call to complete).
8121 This routine must be called with the remove lock held.
8125 Fdo - The functional device object for the device with the frozen queue.
8134 IN PDEVICE_OBJECT Fdo
8137 ClasspReleaseQueue(Fdo
, NULL
);
8139 } // end ClassReleaseQueue()
8141 /*++////////////////////////////////////////////////////////////////////////////
8143 ClasspAllocateReleaseQueueIrp()
8145 Routine Description:
8147 This routine allocates the release queue irp held in classpnp's private
8148 extension. This was added to allow no-memory conditions to be more
8157 Does not grab the spinlock. Should only be called from StartDevice()
8158 routine. May be called elsewhere for poorly-behaved drivers that cause
8159 the queue to lockup before the device is started. This should *never*
8160 occur, since it's illegal to send a request to a non-started PDO. This
8161 condition is checked for in ClasspReleaseQueue().
8165 ClasspAllocateReleaseQueueIrp(
8166 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8170 UCHAR lowerStackSize
;
8173 // do an initial check w/o the spinlock
8176 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8177 return STATUS_SUCCESS
;
8181 lowerStackSize
= FdoExtension
->CommonExtension
.LowerDeviceObject
->StackSize
;
8184 // don't allocate one if one is in progress! this means whoever called
8185 // this routine didn't check if one was in progress.
8188 ASSERT(!(FdoExtension
->ReleaseQueueInProgress
));
8190 FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
=
8191 ExAllocatePoolWithTag(NonPagedPool
,
8192 IoSizeOfIrp(lowerStackSize
),
8193 CLASS_TAG_RELEASE_QUEUE
8196 if (FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
== NULL
) {
8197 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
8198 "release queue irp\n"));
8199 return STATUS_INSUFFICIENT_RESOURCES
;
8201 IoInitializeIrp(FdoExtension
->PrivateFdoData
->ReleaseQueueIrp
,
8202 IoSizeOfIrp(lowerStackSize
),
8204 FdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
= TRUE
;
8206 return STATUS_SUCCESS
;
8210 /*++////////////////////////////////////////////////////////////////////////////
8212 ClasspReleaseQueue()
8214 Routine Description:
8216 This routine issues an internal device control command
8217 to the port driver to release a frozen queue. The call
8218 is issued asynchronously as ClassReleaseQueue will be invoked
8219 from the IO completion DPC (and will have no context to
8220 wait for a synchronous call to complete).
8222 This routine must be called with the remove lock held.
8226 Fdo - The functional device object for the device with the frozen queue.
8228 ReleaseQueueIrp - If this irp is supplied then the test to determine whether
8229 a release queue request is in progress will be ignored.
8230 The irp provided must be the IRP originally allocated
8231 for release queue requests (so this parameter can only
8232 really be provided by the release queue completion
8242 IN PDEVICE_OBJECT Fdo
,
8243 IN PIRP ReleaseQueueIrp OPTIONAL
8246 PIO_STACK_LOCATION irpStack
;
8248 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
8249 PDEVICE_OBJECT lowerDevice
;
8250 PSCSI_REQUEST_BLOCK srb
;
8253 lowerDevice
= fdoExtension
->CommonExtension
.LowerDeviceObject
;
8256 // we raise irql seperately so we're not swapped out or suspended
8257 // while holding the release queue irp in this routine. this lets
8258 // us release the spin lock before lowering irql.
8261 KeRaiseIrql(DISPATCH_LEVEL
, ¤tIrql
);
8263 KeAcquireSpinLockAtDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8266 // make sure that if they passed us an irp, it matches our allocated irp.
8269 ASSERT((ReleaseQueueIrp
== NULL
) ||
8270 (ReleaseQueueIrp
== fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
));
8273 // ASSERT that we've already allocated this. (should not occur)
8274 // try to allocate it anyways, then finally bugcheck if
8275 // there's still no memory...
8278 ASSERT(fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
);
8279 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8280 ClasspAllocateReleaseQueueIrp(fdoExtension
);
8282 if (!fdoExtension
->PrivateFdoData
->ReleaseQueueIrpAllocated
) {
8283 KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL
, 0x12, (ULONG_PTR
)Fdo
, 0x0, 0x0);
8286 if ((fdoExtension
->ReleaseQueueInProgress
) && (ReleaseQueueIrp
== NULL
)) {
8289 // Someone is already using the irp - just set the flag to indicate that
8290 // we need to release the queue again.
8293 fdoExtension
->ReleaseQueueNeeded
= TRUE
;
8294 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8295 KeLowerIrql(currentIrql
);
8301 // Mark that there is a release queue in progress and drop the spinlock.
8304 fdoExtension
->ReleaseQueueInProgress
= TRUE
;
8305 if (ReleaseQueueIrp
) {
8306 irp
= ReleaseQueueIrp
;
8308 irp
= fdoExtension
->PrivateFdoData
->ReleaseQueueIrp
;
8310 srb
= &(fdoExtension
->ReleaseQueueSrb
);
8312 KeReleaseSpinLockFromDpcLevel(&(fdoExtension
->ReleaseQueueSpinLock
));
8314 ASSERT(irp
!= NULL
);
8316 irpStack
= IoGetNextIrpStackLocation(irp
);
8318 irpStack
->MajorFunction
= IRP_MJ_SCSI
;
8320 srb
->OriginalRequest
= irp
;
8323 // Store the SRB address in next stack for port driver.
8326 irpStack
->Parameters
.Scsi
.Srb
= srb
;
8329 // If this device is removable then flush the queue. This will also
8333 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)){
8334 srb
->Function
= SRB_FUNCTION_FLUSH_QUEUE
;
8337 srb
->Function
= SRB_FUNCTION_RELEASE_QUEUE
;
8340 ClassAcquireRemoveLock(Fdo
, irp
);
8342 IoSetCompletionRoutine(irp
,
8343 ClassReleaseQueueCompletion
,
8349 IoCallDriver(lowerDevice
, irp
);
8351 KeLowerIrql(currentIrql
);
8355 } // end ClassReleaseQueue()
8357 /*++////////////////////////////////////////////////////////////////////////////
8359 ClassReleaseQueueCompletion()
8361 Routine Description:
8363 This routine is called when an asynchronous I/O request
8364 which was issused by the class driver completes. Examples of such requests
8365 are release queue or START UNIT. This routine releases the queue if
8366 necessary. It then frees the context and the IRP.
8370 DeviceObject - The device object for the logical unit; however since this
8371 is the top stack location the value is NULL.
8373 Irp - Supplies a pointer to the Irp to be processed.
8375 Context - Supplies the context to be used to process this request.
8383 ClassReleaseQueueCompletion(
8384 PDEVICE_OBJECT DeviceObject
,
8389 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8392 BOOLEAN releaseQueueNeeded
;
8394 DeviceObject
= Context
;
8396 fdoExtension
= DeviceObject
->DeviceExtension
;
8398 ClassReleaseRemoveLock(DeviceObject
, Irp
);
8401 // Grab the spinlock and clear the release queue in progress flag so others
8402 // can run. Save (and clear) the state of the release queue needed flag
8403 // so that we can issue a new release queue outside the spinlock.
8406 KeAcquireSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), &oldIrql
);
8408 releaseQueueNeeded
= fdoExtension
->ReleaseQueueNeeded
;
8410 fdoExtension
->ReleaseQueueNeeded
= FALSE
;
8411 fdoExtension
->ReleaseQueueInProgress
= FALSE
;
8413 KeReleaseSpinLock(&(fdoExtension
->ReleaseQueueSpinLock
), oldIrql
);
8416 // If we need a release queue then issue one now. Another processor may
8417 // have already started one in which case we'll try to issue this one after
8418 // it is done - but we should never recurse more than one deep.
8421 if(releaseQueueNeeded
) {
8422 ClasspReleaseQueue(DeviceObject
, Irp
);
8426 // Indicate the I/O system should stop processing the Irp completion.
8429 return STATUS_MORE_PROCESSING_REQUIRED
;
8431 } // ClassAsynchronousCompletion()
8433 /*++////////////////////////////////////////////////////////////////////////////
8435 ClassAcquireChildLock()
8437 Routine Description:
8439 This routine acquires the lock protecting children PDOs. It may be
8440 acquired recursively by the same thread, but must be release by the
8441 thread once for each acquisition.
8445 FdoExtension - the device whose child list is protected.
8453 ClassAcquireChildLock(
8454 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8459 if(FdoExtension
->ChildLockOwner
!= KeGetCurrentThread()) {
8460 KeWaitForSingleObject(&FdoExtension
->ChildLock
,
8461 Executive
, KernelMode
,
8464 ASSERT(FdoExtension
->ChildLockOwner
== NULL
);
8465 ASSERT(FdoExtension
->ChildLockAcquisitionCount
== 0);
8467 FdoExtension
->ChildLockOwner
= KeGetCurrentThread();
8469 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8472 FdoExtension
->ChildLockAcquisitionCount
++;
8476 /*++////////////////////////////////////////////////////////////////////////////
8478 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
8480 Routine Description:
8482 This routine releases the lock protecting children PDOs. It must be
8483 called once for each time ClassAcquireChildLock was called.
8487 FdoExtension - the device whose child list is protected
8495 ClassReleaseChildLock(
8496 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8499 ASSERT(FdoExtension
->ChildLockOwner
== KeGetCurrentThread());
8500 ASSERT(FdoExtension
->ChildLockAcquisitionCount
!= 0);
8502 FdoExtension
->ChildLockAcquisitionCount
-= 1;
8504 if(FdoExtension
->ChildLockAcquisitionCount
== 0) {
8505 FdoExtension
->ChildLockOwner
= NULL
;
8506 KeSetEvent(&FdoExtension
->ChildLock
, IO_NO_INCREMENT
, FALSE
);
8510 } // end ClassReleaseChildLock(
8512 /*++////////////////////////////////////////////////////////////////////////////
8516 Routine Description:
8518 This routine will insert a new child into the head of the child list.
8522 Parent - the child's parent (contains the head of the list)
8523 Child - the child to be inserted.
8524 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8525 it's already been acquired by or on behalf of the caller
8535 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8536 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8537 IN BOOLEAN AcquireLock
8541 ClassAcquireChildLock(Parent
);
8546 // Make sure this child's not already in the list.
8549 PPHYSICAL_DEVICE_EXTENSION testChild
;
8551 for (testChild
= Parent
->CommonExtension
.ChildList
;
8553 testChild
= testChild
->CommonExtension
.ChildList
) {
8555 ASSERT(testChild
!= Child
);
8560 Child
->CommonExtension
.ChildList
= Parent
->CommonExtension
.ChildList
;
8561 Parent
->CommonExtension
.ChildList
= Child
;
8564 ClassReleaseChildLock(Parent
);
8567 } // end ClassAddChild()
8569 /*++////////////////////////////////////////////////////////////////////////////
8573 Routine Description:
8575 This routine will remove a child from the child list.
8579 Parent - the parent to be removed from.
8581 Child - the child to be removed or NULL if the first child should be
8584 AcquireLock - whether the child lock should be acquired (TRUE) or whether
8585 it's already been acquired by or on behalf of the caller
8590 A pointer to the child which was removed or NULL if no such child could
8591 be found in the list (or if Child was NULL but the list is empty).
8594 PPHYSICAL_DEVICE_EXTENSION
8596 IN PFUNCTIONAL_DEVICE_EXTENSION Parent
,
8597 IN PPHYSICAL_DEVICE_EXTENSION Child
,
8598 IN BOOLEAN AcquireLock
8602 ClassAcquireChildLock(Parent
);
8606 PCOMMON_DEVICE_EXTENSION previousChild
= &Parent
->CommonExtension
;
8609 // If the list is empty then bail out now.
8612 if(Parent
->CommonExtension
.ChildList
== NULL
) {
8618 // If the caller specified a child then find the child object before
8619 // it. If none was specified then the FDO is the child object before
8620 // the one we want to remove.
8626 // Scan through the child list to find the entry which points to
8631 ASSERT(previousChild
!= &Child
->CommonExtension
);
8633 if(previousChild
->ChildList
== Child
) {
8637 previousChild
= &previousChild
->ChildList
->CommonExtension
;
8638 } while(previousChild
!= NULL
);
8640 if(previousChild
== NULL
) {
8647 // Save the next child away then unlink it from the list.
8650 Child
= previousChild
->ChildList
;
8651 previousChild
->ChildList
= Child
->CommonExtension
.ChildList
;
8652 Child
->CommonExtension
.ChildList
= NULL
;
8656 ClassReleaseChildLock(Parent
);
8660 } // end ClassRemoveChild()
8665 ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
8669 ClasspRetryRequestDpc(
8671 IN PDEVICE_OBJECT DeviceObject
,
8676 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8677 PCOMMON_DEVICE_EXTENSION commonExtension
;
8678 PCLASS_PRIVATE_FDO_DATA fdoData
;
8679 PCLASS_RETRY_INFO retryList
;
8683 commonExtension
= DeviceObject
->DeviceExtension
;
8684 ASSERT(commonExtension
->IsFdo
);
8685 fdoExtension
= DeviceObject
->DeviceExtension
;
8686 fdoData
= fdoExtension
->PrivateFdoData
;
8689 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8692 KeQueryTickCount(&now
);
8695 // if CurrentTick is less than now
8698 // retry entire list
8702 if (now
.QuadPart
< fdoData
->Retry
.Tick
.QuadPart
) {
8704 ClasspRetryDpcTimer(fdoData
);
8709 retryList
= fdoData
->Retry
.ListHead
;
8710 fdoData
->Retry
.ListHead
= NULL
;
8711 fdoData
->Retry
.Delta
.QuadPart
= (LONGLONG
)0;
8712 fdoData
->Retry
.Tick
.QuadPart
= (LONGLONG
)0;
8716 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8718 while (retryList
!= NULL
) {
8722 irp
= CONTAINING_RECORD(retryList
, IRP
, Tail
.Overlay
.DriverContext
[0]);
8723 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: -- %p\n", irp
));
8724 retryList
= retryList
->Next
;
8726 irp
->Tail
.Overlay
.DriverContext
[0] = ULongToPtr(0xdddddddd); // invalidate data
8727 irp
->Tail
.Overlay
.DriverContext
[1] = ULongToPtr(0xdddddddd); // invalidate data
8728 irp
->Tail
.Overlay
.DriverContext
[2] = ULongToPtr(0xdddddddd); // invalidate data
8729 irp
->Tail
.Overlay
.DriverContext
[3] = ULongToPtr(0xdddddddd); // invalidate data
8732 IoCallDriver(commonExtension
->LowerDeviceObject
, irp
);
8737 } // end ClasspRetryRequestDpc()
8741 ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
8746 IN PDEVICE_OBJECT SelfDeviceObject
,
8748 IN LARGE_INTEGER TimeDelta100ns
// in 100ns units
8751 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
8752 PCLASS_PRIVATE_FDO_DATA fdoData
;
8753 PCLASS_RETRY_INFO retryInfo
;
8754 PCLASS_RETRY_INFO
*previousNext
;
8755 LARGE_INTEGER delta
;
8759 // this checks we aren't destroying irps
8761 ASSERT(sizeof(CLASS_RETRY_INFO
) <= (4*sizeof(PVOID
)));
8763 fdoExtension
= SelfDeviceObject
->DeviceExtension
;
8764 fdoData
= fdoExtension
->PrivateFdoData
;
8766 if (!fdoExtension
->CommonExtension
.IsFdo
) {
8769 // this debug print/assertion should ALWAYS be investigated.
8770 // ClassRetryRequest can currently only be used by FDO's
8773 DebugPrint((ClassDebugError
, "ClassRetryRequestEx: LOST IRP %p\n", Irp
));
8774 ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
8779 if (TimeDelta100ns
.QuadPart
< 0) {
8780 ASSERT(!"ClassRetryRequest - must use positive delay");
8781 TimeDelta100ns
.QuadPart
*= -1;
8785 // prepare what we can out of the loop
8788 retryInfo
= (PCLASS_RETRY_INFO
)(&Irp
->Tail
.Overlay
.DriverContext
[0]);
8789 RtlZeroMemory(retryInfo
, sizeof(CLASS_RETRY_INFO
));
8791 delta
.QuadPart
= (TimeDelta100ns
.QuadPart
/ fdoData
->Retry
.Granularity
);
8792 if (TimeDelta100ns
.QuadPart
% fdoData
->Retry
.Granularity
) {
8793 delta
.QuadPart
++; // round up to next tick
8795 if (delta
.QuadPart
== (LONGLONG
)0) {
8796 delta
.QuadPart
= MINIMUM_RETRY_UNITS
;
8800 // now determine if we should fire another DPC or not
8803 KeAcquireSpinLock(&fdoData
->Retry
.Lock
, &irql
);
8806 // always add request to the list
8809 retryInfo
->Next
= fdoData
->Retry
.ListHead
;
8810 fdoData
->Retry
.ListHead
= retryInfo
;
8812 if (fdoData
->Retry
.Delta
.QuadPart
== (LONGLONG
)0) {
8814 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: +++ %p\n", Irp
));
8817 // must be exactly one item on list
8820 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8821 ASSERT(fdoData
->Retry
.ListHead
->Next
== NULL
);
8824 // if currentDelta is zero, always fire a DPC
8827 KeQueryTickCount(&fdoData
->Retry
.Tick
);
8828 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8829 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8830 ClasspRetryDpcTimer(fdoData
);
8832 } else if (delta
.QuadPart
> fdoData
->Retry
.Delta
.QuadPart
) {
8835 // if delta is greater than the list's current delta,
8836 // increase the DPC handling time by difference
8837 // and update the delta to new larger value
8838 // allow the DPC to re-fire itself if needed
8841 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8844 // must be at least two items on list
8847 ASSERT(fdoData
->Retry
.ListHead
!= NULL
);
8848 ASSERT(fdoData
->Retry
.ListHead
->Next
!= NULL
);
8850 fdoData
->Retry
.Tick
.QuadPart
-= fdoData
->Retry
.Delta
.QuadPart
;
8851 fdoData
->Retry
.Tick
.QuadPart
+= delta
.QuadPart
;
8853 fdoData
->Retry
.Delta
.QuadPart
= delta
.QuadPart
;
8858 // just inserting it on the list was enough
8861 DebugPrint((ClassDebugDelayedRetry
, "ClassRetry: ++ %p\n", Irp
));
8866 KeReleaseSpinLock(&fdoData
->Retry
.Lock
, irql
);
8869 } // end ClassRetryRequest()
8873 ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
8877 ClasspRetryDpcTimer(
8878 IN PCLASS_PRIVATE_FDO_DATA FdoData
8883 ASSERT(FdoData
->Retry
.Tick
.QuadPart
!= (LONGLONG
)0);
8884 ASSERT(FdoData
->Retry
.ListHead
!= NULL
); // never fire an empty list
8887 // fire == (CurrentTick - now) * (100ns per tick)
8889 // NOTE: Overflow is nearly impossible and is ignored here
8892 KeQueryTickCount(&fire
);
8893 fire
.QuadPart
= FdoData
->Retry
.Tick
.QuadPart
- fire
.QuadPart
;
8894 fire
.QuadPart
*= FdoData
->Retry
.Granularity
;
8897 // fire is now multiples of 100ns until should fire the timer.
8898 // if timer should already have expired, or would fire too quickly,
8899 // fire it in some arbitrary number of ticks to prevent infinitely
8903 if (fire
.QuadPart
< MINIMUM_RETRY_UNITS
) {
8904 fire
.QuadPart
= MINIMUM_RETRY_UNITS
;
8907 DebugPrint((ClassDebugDelayedRetry
,
8908 "ClassRetry: ======= %I64x ticks\n",
8912 // must use negative to specify relative time to fire
8915 fire
.QuadPart
= fire
.QuadPart
* ((LONGLONG
)-1);
8918 // set the timer, since this is the first addition
8921 KeSetTimerEx(&FdoData
->Retry
.Timer
, fire
, 0, &FdoData
->Retry
.Dpc
);
8924 } // end ClasspRetryDpcTimer()
8927 ClasspInitializeHotplugInfo(
8928 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8931 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
8932 DEVICE_REMOVAL_POLICY deviceRemovalPolicy
;
8934 ULONG resultLength
= 0;
8935 ULONG writeCacheOverride
;
8940 // start with some default settings
8942 RtlZeroMemory(&(fdoData
->HotplugInfo
), sizeof(STORAGE_HOTPLUG_INFO
));
8945 // set the size (aka version)
8948 fdoData
->HotplugInfo
.Size
= sizeof(STORAGE_HOTPLUG_INFO
);
8951 // set if the device has removable media
8954 if (FdoExtension
->DeviceDescriptor
->RemovableMedia
) {
8955 fdoData
->HotplugInfo
.MediaRemovable
= TRUE
;
8957 fdoData
->HotplugInfo
.MediaRemovable
= FALSE
;
8961 // this refers to devices which, for reasons not yet understood,
8962 // do not fail PREVENT_MEDIA_REMOVAL requests even though they
8963 // have no way to lock the media into the drive. this allows
8964 // the filesystems to turn off delayed-write caching for these
8968 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
8969 FDO_HACK_CANNOT_LOCK_MEDIA
)) {
8970 fdoData
->HotplugInfo
.MediaHotplug
= TRUE
;
8972 fdoData
->HotplugInfo
.MediaHotplug
= FALSE
;
8977 // Look into the registry to see if the user has chosen
8978 // to override the default setting for the removal policy
8981 RtlZeroMemory(&deviceRemovalPolicy
, sizeof(DEVICE_REMOVAL_POLICY
));
8983 ClassGetDeviceParameter(FdoExtension
,
8984 CLASSP_REG_SUBKEY_NAME
,
8985 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME
,
8986 (PULONG
)&deviceRemovalPolicy
);
8988 if (deviceRemovalPolicy
== 0)
8991 // Query the default removal policy from the kernel
8994 status
= IoGetDeviceProperty(FdoExtension
->LowerPdo
,
8995 DevicePropertyRemovalPolicy
,
8996 sizeof(DEVICE_REMOVAL_POLICY
),
8997 (PVOID
)&deviceRemovalPolicy
,
8999 if (!NT_SUCCESS(status
))
9004 if (resultLength
!= sizeof(DEVICE_REMOVAL_POLICY
))
9006 return STATUS_UNSUCCESSFUL
;
9011 // use this info to set the DeviceHotplug setting
9012 // don't rely on DeviceCapabilities, since it can't properly
9013 // determine device relations, etc. let the kernel figure this
9014 // stuff out instead.
9017 if (deviceRemovalPolicy
== RemovalPolicyExpectSurpriseRemoval
) {
9018 fdoData
->HotplugInfo
.DeviceHotplug
= TRUE
;
9020 fdoData
->HotplugInfo
.DeviceHotplug
= FALSE
;
9024 // this refers to the *filesystem* caching, but has to be included
9025 // here since it's a per-device setting. this may change to be
9026 // stored by the system in the future.
9029 writeCacheOverride
= FALSE
;
9030 ClassGetDeviceParameter(FdoExtension
,
9031 CLASSP_REG_SUBKEY_NAME
,
9032 CLASSP_REG_WRITE_CACHE_VALUE_NAME
,
9033 &writeCacheOverride
);
9035 if (writeCacheOverride
) {
9036 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= TRUE
;
9038 fdoData
->HotplugInfo
.WriteCacheEnableOverride
= FALSE
;
9041 return STATUS_SUCCESS
;
9045 ClasspScanForClassHacks(
9046 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
9053 // remove invalid flags and save
9056 CLEAR_FLAG(Data
, FDO_HACK_INVALID_FLAGS
);
9057 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, Data
);
9062 ClasspScanForSpecialInRegistry(
9063 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
9066 HANDLE deviceParameterHandle
; // device instance key
9067 HANDLE classParameterHandle
; // classpnp subkey
9068 OBJECT_ATTRIBUTES objectAttributes
;
9069 UNICODE_STRING subkeyName
;
9073 // seeded in the ENUM tree by ClassInstaller
9076 RTL_QUERY_REGISTRY_TABLE queryTable
[2]; // null terminated array
9080 deviceParameterHandle
= NULL
;
9081 classParameterHandle
= NULL
;
9084 status
= IoOpenDeviceRegistryKey(FdoExtension
->LowerPdo
,
9085 PLUGPLAY_REGKEY_DEVICE
,
9087 &deviceParameterHandle
9090 if (!NT_SUCCESS(status
)) {
9091 goto cleanupScanForSpecial
;
9094 RtlInitUnicodeString(&subkeyName
, CLASSP_REG_SUBKEY_NAME
);
9095 InitializeObjectAttributes(&objectAttributes
,
9097 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
9098 deviceParameterHandle
,
9102 status
= ZwOpenKey( &classParameterHandle
,
9107 if (!NT_SUCCESS(status
)) {
9108 goto cleanupScanForSpecial
;
9112 // Zero out the memory
9115 RtlZeroMemory(&queryTable
[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE
));
9118 // Setup the structure to read
9121 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
9122 queryTable
[0].Name
= CLASSP_REG_HACK_VALUE_NAME
;
9123 queryTable
[0].EntryContext
= &deviceHacks
;
9124 queryTable
[0].DefaultType
= REG_DWORD
;
9125 queryTable
[0].DefaultData
= &deviceHacks
;
9126 queryTable
[0].DefaultLength
= 0;
9132 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
9133 (PWSTR
)classParameterHandle
,
9138 if (!NT_SUCCESS(status
)) {
9139 goto cleanupScanForSpecial
;
9143 // remove unknown values and save...
9146 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
9147 "Classpnp => ScanForSpecial: HackFlags %#08x\n",
9150 CLEAR_FLAG(deviceHacks
, FDO_HACK_INVALID_FLAGS
);
9151 SET_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
, deviceHacks
);
9154 cleanupScanForSpecial
:
9156 if (deviceParameterHandle
) {
9157 ZwClose(deviceParameterHandle
);
9160 if (classParameterHandle
) {
9161 ZwClose(classParameterHandle
);
9165 // we should modify the system hive to include another key for us to grab
9166 // settings from. in this case: Classpnp\HackFlags
9168 // the use of a DWORD value for the HackFlags allows 32 hacks w/o
9169 // significant use of the registry, and also reduces OEM exposure.
9171 // definition of bit flags:
9172 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
9173 // cannot actually prevent removal.
9174 // 0x00000002 -- Device hard-hangs or times out for GESN requests.
9175 // 0xfffffffc -- Currently reserved, may be used later.