xref: /reactos/drivers/storage/class/classpnp/class.c (revision 803b5e13)
1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4 
5 Module Name:
6 
7     class.c
8 
9 Abstract:
10 
11     SCSI class driver routines
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "classp.h"
25 
26 #include <stddef.h>
27 
28 #include <initguid.h>
29 #include <mountdev.h>
30 
31 #ifdef ALLOC_PRAGMA
32     #pragma alloc_text(INIT, DriverEntry)
33     #pragma alloc_text(PAGE, ClassAddDevice)
34     #pragma alloc_text(PAGE, ClassClaimDevice)
35     #pragma alloc_text(PAGE, ClassCreateDeviceObject)
36     #pragma alloc_text(PAGE, ClassDispatchPnp)
37     #pragma alloc_text(PAGE, ClassGetDescriptor)
38     #pragma alloc_text(PAGE, ClassGetPdoId)
39     #pragma alloc_text(PAGE, ClassInitialize)
40     #pragma alloc_text(PAGE, ClassInitializeEx)
41     #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
42     #pragma alloc_text(PAGE, ClassMarkChildMissing)
43     #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
44     #pragma alloc_text(PAGE, ClassModeSense)
45     #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
46     #pragma alloc_text(PAGE, ClassPnpStartDevice)
47     #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
48     #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
49     #pragma alloc_text(PAGE, ClassRemoveDevice)
50     #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
51     #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
52     #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
53     #pragma alloc_text(PAGE, ClassUnload)
54     #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
55     #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
56     #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
57     #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
58     #pragma alloc_text(PAGE, ClasspScanForClassHacks)
59     #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
60 #endif
61 
62 ULONG ClassPnpAllowUnload = TRUE;
63 
64 
65 #define FirstDriveLetter 'C'
66 #define LastDriveLetter  'Z'
67 
68 
69 
70 /*++////////////////////////////////////////////////////////////////////////////
71 
72 DriverEntry()
73 
74 Routine Description:
75 
76     Temporary entry point needed to initialize the class system dll.
77     It doesn't do anything.
78 
79 Arguments:
80 
81     DriverObject - Pointer to the driver object created by the system.
82 
83 Return Value:
84 
85    STATUS_SUCCESS
86 
87 --*/
88 NTSTATUS
89 NTAPI
90 DriverEntry(
91     IN PDRIVER_OBJECT DriverObject,
92     IN PUNICODE_STRING RegistryPath
93     )
94 {
95     return STATUS_SUCCESS;
96 }
97 
98 /*++////////////////////////////////////////////////////////////////////////////
99 
100 ClassInitialize()
101 
102 Routine Description:
103 
104     This routine is called by a class driver during its
105     DriverEntry routine to initialize the driver.
106 
107 Arguments:
108 
109     Argument1          - Driver Object.
110     Argument2          - Registry Path.
111     InitializationData - Device-specific driver's initialization data.
112 
113 Return Value:
114 
115     A valid return code for a DriverEntry routine.
116 
117 --*/
118 ULONG
119 NTAPI
120 ClassInitialize(
121     IN  PVOID            Argument1,
122     IN  PVOID            Argument2,
123     IN  PCLASS_INIT_DATA InitializationData
124     )
125 {
126     PDRIVER_OBJECT  DriverObject = Argument1;
127     PUNICODE_STRING RegistryPath = Argument2;
128 
129     PCLASS_DRIVER_EXTENSION driverExtension;
130 
131     NTSTATUS        status;
132 
133     PAGED_CODE();
134 
135     DebugPrint((3,"\n\nSCSI Class Driver\n"));
136 
137     ClasspInitializeDebugGlobals();
138 
139     //
140     // Validate the length of this structure. This is effectively a
141     // version check.
142     //
143 
144     if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
145 
146         //
147         // This DebugPrint is to help third-party driver writers
148         //
149 
150         DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
151         return (ULONG) STATUS_REVISION_MISMATCH;
152     }
153 
154     //
155     // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
156     // are not required entry points.
157     //
158 
159     if ((!InitializationData->FdoData.ClassDeviceControl) ||
160         (!((InitializationData->FdoData.ClassReadWriteVerification) ||
161            (InitializationData->ClassStartIo))) ||
162         (!InitializationData->ClassAddDevice) ||
163         (!InitializationData->FdoData.ClassStartDevice)) {
164 
165         //
166         // This DebugPrint is to help third-party driver writers
167         //
168 
169         DebugPrint((0,
170             "ClassInitialize: Class device-specific driver missing required "
171             "FDO entry\n"));
172 
173         return (ULONG) STATUS_REVISION_MISMATCH;
174     }
175 
176     if ((InitializationData->ClassEnumerateDevice) &&
177         ((!InitializationData->PdoData.ClassDeviceControl) ||
178          (!InitializationData->PdoData.ClassStartDevice) ||
179          (!((InitializationData->PdoData.ClassReadWriteVerification) ||
180             (InitializationData->ClassStartIo))))) {
181 
182         //
183         // This DebugPrint is to help third-party driver writers
184         //
185 
186         DebugPrint((0, "ClassInitialize: Class device-specific missing "
187                        "required PDO entry\n"));
188 
189         return (ULONG) STATUS_REVISION_MISMATCH;
190     }
191 
192     if((InitializationData->FdoData.ClassStopDevice == NULL) ||
193         ((InitializationData->ClassEnumerateDevice != NULL) &&
194          (InitializationData->PdoData.ClassStopDevice == NULL))) {
195 
196         //
197         // This DebugPrint is to help third-party driver writers
198         //
199 
200         DebugPrint((0, "ClassInitialize: Class device-specific missing "
201                        "required PDO entry\n"));
202         ASSERT(FALSE);
203         return (ULONG) STATUS_REVISION_MISMATCH;
204     }
205 
206     //
207     // Setup the default power handlers if the class driver didn't provide
208     // any.
209     //
210 
211     if(InitializationData->FdoData.ClassPowerDevice == NULL) {
212         InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
213     }
214 
215     if((InitializationData->ClassEnumerateDevice != NULL) &&
216        (InitializationData->PdoData.ClassPowerDevice == NULL)) {
217         InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
218     }
219 
220     //
221     // warn that unload is not supported
222     //
223     // ISSUE-2000/02/03-peterwie
224     // We should think about making this a fatal error.
225     //
226 
227     if(InitializationData->ClassUnload == NULL) {
228 
229         //
230         // This DebugPrint is to help third-party driver writers
231         //
232 
233         DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
234                     RegistryPath));
235     }
236 
237     //
238     // Create an extension for the driver object
239     //
240 
241     status = IoAllocateDriverObjectExtension(DriverObject,
242                                              CLASS_DRIVER_EXTENSION_KEY,
243                                              sizeof(CLASS_DRIVER_EXTENSION),
244                                              (PVOID *)&driverExtension);
245 
246     if(NT_SUCCESS(status)) {
247 
248         //
249         // Copy the registry path into the driver extension so we can use it later
250         //
251 
252         driverExtension->RegistryPath.Length = RegistryPath->Length;
253         driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
254 
255         driverExtension->RegistryPath.Buffer =
256             ExAllocatePoolWithTag(PagedPool,
257                                   RegistryPath->MaximumLength,
258                                   '1CcS');
259 
260         if(driverExtension->RegistryPath.Buffer == NULL) {
261 
262             status = STATUS_INSUFFICIENT_RESOURCES;
263             return status;
264         }
265 
266         RtlCopyUnicodeString(
267             &(driverExtension->RegistryPath),
268             RegistryPath);
269 
270         //
271         // Copy the initialization data into the driver extension so we can reuse
272         // it during our add device routine
273         //
274 
275         RtlCopyMemory(
276             &(driverExtension->InitData),
277             InitializationData,
278             sizeof(CLASS_INIT_DATA));
279 
280         driverExtension->DeviceCount = 0;
281 
282     } else if (status == STATUS_OBJECT_NAME_COLLISION) {
283 
284         //
285         // The extension already exists - get a pointer to it
286         //
287 
288         driverExtension = IoGetDriverObjectExtension(DriverObject,
289                                                      CLASS_DRIVER_EXTENSION_KEY);
290 
291         ASSERT(driverExtension != NULL);
292 
293     } else {
294 
295         DebugPrint((1, "ClassInitialize: Class driver extension could not be "
296                        "allocated %lx\n", status));
297         return status;
298     }
299 
300     //
301     // Update driver object with entry points.
302     //
303 
304     DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreateClose;
305     DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassCreateClose;
306     DriverObject->MajorFunction[IRP_MJ_READ] = ClassReadWrite;
307     DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassReadWrite;
308     DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassInternalIoControl;
309     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch;
310     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassShutdownFlush;
311     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush;
312     DriverObject->MajorFunction[IRP_MJ_PNP] = ClassDispatchPnp;
313     DriverObject->MajorFunction[IRP_MJ_POWER] = ClassDispatchPower;
314     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl;
315 
316     if (InitializationData->ClassStartIo) {
317         DriverObject->DriverStartIo = ClasspStartIo;
318     }
319 
320     if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload != FALSE)) {
321         DriverObject->DriverUnload = ClassUnload;
322     } else {
323         DriverObject->DriverUnload = NULL;
324     }
325 
326     DriverObject->DriverExtension->AddDevice = ClassAddDevice;
327 
328     DbgPrint("Driver is ready to go\n");
329     status = STATUS_SUCCESS;
330     return status;
331 } // end ClassInitialize()
332 
333 /*++////////////////////////////////////////////////////////////////////////////
334 
335 ClassInitializeEx()
336 
337 Routine Description:
338 
339     This routine is allows the caller to do any extra initialization or
340     setup that is not done in ClassInitialize. The operation is
341     controlled by the GUID that is passed and the contents of the Data
342     parameter is dependent upon the GUID.
343 
344     This is the list of supported operations:
345 
346     Guid - GUID_CLASSPNP_QUERY_REGINFOEX
347     Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
348 
349         Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
350         callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
351         former callback allows the driver to specify the name of the
352         mof resource.
353 
354 Arguments:
355 
356     DriverObject
357     Guid
358     Data
359 
360 Return Value:
361 
362     Status Code
363 
364 --*/
365 ULONG
366 NTAPI
367 ClassInitializeEx(
368     IN  PDRIVER_OBJECT   DriverObject,
369     IN  LPGUID           Guid,
370     IN  PVOID            Data
371     )
372 {
373     PCLASS_DRIVER_EXTENSION driverExtension;
374 
375     NTSTATUS        status;
376 
377     PAGED_CODE();
378 
379     driverExtension = IoGetDriverObjectExtension( DriverObject,
380                                                   CLASS_DRIVER_EXTENSION_KEY
381                                                   );
382     if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
383     {
384         PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
385 
386         //
387         // Indicate the device supports PCLASS_QUERY_REGINFO_EX
388         // callback instead of PCLASS_QUERY_REGINFO callback.
389         //
390         List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
391 
392         if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
393         {
394             driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
395             driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
396             status = STATUS_SUCCESS;
397         } else {
398             status = STATUS_INVALID_PARAMETER;
399         }
400     } else {
401         status = STATUS_NOT_SUPPORTED;
402     }
403 
404     return(status);
405 
406 } // end ClassInitializeEx()
407 
408 /*++////////////////////////////////////////////////////////////////////////////
409 
410 ClassUnload()
411 
412 Routine Description:
413 
414     called when there are no more references to the driver.  this allows
415     drivers to be updated without rebooting.
416 
417 Arguments:
418 
419     DriverObject - a pointer to the driver object that is being unloaded
420 
421 Status:
422 
423 --*/
424 VOID
425 NTAPI
426 ClassUnload(
427     IN PDRIVER_OBJECT DriverObject
428     )
429 {
430     PCLASS_DRIVER_EXTENSION driverExtension;
431     //NTSTATUS status;
432 
433     PAGED_CODE();
434 
435     ASSERT( DriverObject->DeviceObject == NULL );
436 
437     driverExtension = IoGetDriverObjectExtension( DriverObject,
438                                                   CLASS_DRIVER_EXTENSION_KEY
439                                                   );
440 
441     ASSERT(driverExtension != NULL);
442     ASSERT(driverExtension->RegistryPath.Buffer != NULL);
443     ASSERT(driverExtension->InitData.ClassUnload != NULL);
444 
445     DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
446                 &driverExtension->RegistryPath));
447 
448     //
449     // attempt to process the driver's unload routine first.
450     //
451 
452     driverExtension->InitData.ClassUnload(DriverObject);
453 
454     //
455     // free own allocated resources and return
456     //
457 
458     ExFreePool( driverExtension->RegistryPath.Buffer );
459     driverExtension->RegistryPath.Buffer = NULL;
460     driverExtension->RegistryPath.Length = 0;
461     driverExtension->RegistryPath.MaximumLength = 0;
462 
463     return;
464 } // end ClassUnload()
465 
466 /*++////////////////////////////////////////////////////////////////////////////
467 
468 ClassAddDevice()
469 
470 Routine Description:
471 
472     SCSI class driver add device routine.  This is called by pnp when a new
473     physical device come into being.
474 
475     This routine will call out to the class driver to verify that it should
476     own this device then will create and attach a device object and then hand
477     it to the driver to initialize and create symbolic links
478 
479 Arguments:
480 
481     DriverObject - a pointer to the driver object that this is being created for
482     PhysicalDeviceObject - a pointer to the physical device object
483 
484 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
485     STATUS_SUCCESS if the creation and attachment was successful
486     status of device creation and initialization
487 
488 --*/
489 NTSTATUS
490 NTAPI
491 ClassAddDevice(
492     IN PDRIVER_OBJECT DriverObject,
493     IN PDEVICE_OBJECT PhysicalDeviceObject
494     )
495 {
496     PCLASS_DRIVER_EXTENSION driverExtension =
497         IoGetDriverObjectExtension(DriverObject,
498                                    CLASS_DRIVER_EXTENSION_KEY);
499 
500     NTSTATUS status;
501 
502     PAGED_CODE();
503 
504     DbgPrint("got a device\n");
505     status = driverExtension->InitData.ClassAddDevice(DriverObject,
506                                                       PhysicalDeviceObject);
507     return status;
508 } // end ClassAddDevice()
509 
510 /*++////////////////////////////////////////////////////////////////////////////
511 
512 ClassDispatchPnp()
513 
514 Routine Description:
515 
516     Storage class driver pnp routine.  This is called by the io system when
517     a PNP request is sent to the device.
518 
519 Arguments:
520 
521     DeviceObject - pointer to the device object
522 
523     Irp - pointer to the io request packet
524 
525 Return Value:
526 
527     status
528 
529 --*/
530 NTSTATUS
531 NTAPI
532 ClassDispatchPnp(
533     IN PDEVICE_OBJECT DeviceObject,
534     IN PIRP Irp
535     )
536 {
537     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
538     BOOLEAN isFdo = commonExtension->IsFdo;
539 
540     PCLASS_DRIVER_EXTENSION driverExtension;
541     PCLASS_INIT_DATA initData;
542     PCLASS_DEV_INFO devInfo;
543 
544     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
545 
546     NTSTATUS status = Irp->IoStatus.Status;
547     BOOLEAN completeRequest = TRUE;
548     BOOLEAN lockReleased = FALSE;
549 
550     PAGED_CODE();
551 
552     //
553     // Extract all the useful information out of the driver object
554     // extension
555     //
556 
557     driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
558                                                  CLASS_DRIVER_EXTENSION_KEY);
559     if (driverExtension){
560 
561         initData = &(driverExtension->InitData);
562 
563         if(isFdo) {
564             devInfo = &(initData->FdoData);
565         } else {
566             devInfo = &(initData->PdoData);
567         }
568 
569         ClassAcquireRemoveLock(DeviceObject, Irp);
570 
571         DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
572                        DeviceObject, Irp,
573                        irpStack->MinorFunction,
574                        isFdo ? "fdo" : "pdo",
575                        DeviceObject));
576         DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
577                        DeviceObject, Irp,
578                        commonExtension->PreviousState,
579                        commonExtension->CurrentState));
580 
581         switch(irpStack->MinorFunction) {
582 
583             case IRP_MN_START_DEVICE: {
584 
585                 //
586                 // if this is sent to the FDO we should forward it down the
587                 // attachment chain before we start the FDO.
588                 //
589 
590                 if (isFdo) {
591                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
592                 }
593                 else {
594                     status = STATUS_SUCCESS;
595                 }
596 
597                 if (NT_SUCCESS(status)){
598                     status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
599                 }
600 
601                 break;
602             }
603 
604 
605             case IRP_MN_QUERY_DEVICE_RELATIONS: {
606 
607                 DEVICE_RELATION_TYPE type =
608                     irpStack->Parameters.QueryDeviceRelations.Type;
609 
610                 PDEVICE_RELATIONS deviceRelations = NULL;
611 
612                 if(!isFdo) {
613 
614                     if(type == TargetDeviceRelation) {
615 
616                         //
617                         // Device relations has one entry built in to it's size.
618                         //
619 
620                         status = STATUS_INSUFFICIENT_RESOURCES;
621 
622                         deviceRelations = ExAllocatePoolWithTag(PagedPool,
623                                                          sizeof(DEVICE_RELATIONS),
624                                                          '2CcS');
625 
626                         if(deviceRelations != NULL) {
627 
628                             RtlZeroMemory(deviceRelations,
629                                           sizeof(DEVICE_RELATIONS));
630 
631                             Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
632 
633                             deviceRelations->Count = 1;
634                             deviceRelations->Objects[0] = DeviceObject;
635                             ObReferenceObject(deviceRelations->Objects[0]);
636 
637                             status = STATUS_SUCCESS;
638                         }
639 
640                     } else {
641                         //
642                         // PDO's just complete enumeration requests without altering
643                         // the status.
644                         //
645 
646                         status = Irp->IoStatus.Status;
647                     }
648 
649                     break;
650 
651                 } else if (type == BusRelations) {
652 
653                     ASSERT(commonExtension->IsInitialized);
654 
655                     //
656                     // Make sure we support enumeration
657                     //
658 
659                     if(initData->ClassEnumerateDevice == NULL) {
660 
661                         //
662                         // Just send the request down to the lower driver.  Perhaps
663                         // It can enumerate children.
664                         //
665 
666                     } else {
667 
668                         //
669                         // Re-enumerate the device
670                         //
671 
672                         status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
673 
674                         if(!NT_SUCCESS(status)) {
675                             completeRequest = TRUE;
676                             break;
677                         }
678                     }
679                 }
680 
681                 IoCopyCurrentIrpStackLocationToNext(Irp);
682                 ClassReleaseRemoveLock(DeviceObject, Irp);
683                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
684                 completeRequest = FALSE;
685 
686                 break;
687             }
688 
689             case IRP_MN_QUERY_ID: {
690 
691                 BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
692                 UNICODE_STRING unicodeString;
693 
694                 if(isFdo) {
695 
696                     //
697                     // FDO's should just forward the query down to the lower
698                     // device objects
699                     //
700 
701                     IoCopyCurrentIrpStackLocationToNext(Irp);
702                     ClassReleaseRemoveLock(DeviceObject, Irp);
703 
704                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
705                     completeRequest = FALSE;
706                     break;
707                 }
708 
709                 //
710                 // PDO's need to give an answer - this is easy for now
711                 //
712 
713                 RtlInitUnicodeString(&unicodeString, NULL);
714 
715                 status = ClassGetPdoId(DeviceObject,
716                                        idType,
717                                        &unicodeString);
718 
719                 if(status == STATUS_NOT_IMPLEMENTED) {
720                     //
721                     // The driver doesn't implement this ID (whatever it is).
722                     // Use the status out of the IRP so that we don't mangle a
723                     // response from someone else.
724                     //
725 
726                     status = Irp->IoStatus.Status;
727                 } else if(NT_SUCCESS(status)) {
728                     Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
729                 } else {
730                     Irp->IoStatus.Information = (ULONG_PTR) NULL;
731                 }
732 
733                 break;
734             }
735 
736             case IRP_MN_QUERY_STOP_DEVICE:
737             case IRP_MN_QUERY_REMOVE_DEVICE: {
738 
739                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
740                             DeviceObject, Irp,
741                             ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
742                              "STOP" : "REMOVE")));
743 
744                 //
745                 // If this device is in use for some reason (paging, etc...)
746                 // then we need to fail the request.
747                 //
748 
749                 if(commonExtension->PagingPathCount != 0) {
750 
751                     DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
752                                 "path and cannot be removed\n",
753                                 DeviceObject, Irp));
754                     status = STATUS_DEVICE_BUSY;
755                     break;
756                 }
757 
758                 //
759                 // Check with the class driver to see if the query operation
760                 // can succeed.
761                 //
762 
763                 if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
764                     status = devInfo->ClassStopDevice(DeviceObject,
765                                                       irpStack->MinorFunction);
766                 } else {
767                     status = devInfo->ClassRemoveDevice(DeviceObject,
768                                                         irpStack->MinorFunction);
769                 }
770 
771                 if(NT_SUCCESS(status)) {
772 
773                     //
774                     // ASSERT that we never get two queries in a row, as
775                     // this will severely mess up the state machine
776                     //
777                     ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
778                     commonExtension->PreviousState = commonExtension->CurrentState;
779                     commonExtension->CurrentState = irpStack->MinorFunction;
780 
781                     if(isFdo) {
782                         DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
783                                     "%s irp\n", DeviceObject, Irp,
784                                     ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
785                                      "STOP" : "REMOVE")));
786                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
787                     }
788                 }
789                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
790                             DeviceObject, Irp, status));
791 
792                 break;
793             }
794 
795             case IRP_MN_CANCEL_STOP_DEVICE:
796             case IRP_MN_CANCEL_REMOVE_DEVICE: {
797 
798                 //
799                 // Check with the class driver to see if the query or cancel
800                 // operation can succeed.
801                 //
802 
803                 if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
804                     status = devInfo->ClassStopDevice(DeviceObject,
805                                                       irpStack->MinorFunction);
806                     NT_ASSERTMSGW(L"ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
807                                   L"never be failed\n", NT_SUCCESS(status));
808                 } else {
809                     status = devInfo->ClassRemoveDevice(DeviceObject,
810                                                         irpStack->MinorFunction);
811                     NT_ASSERTMSGW(L"ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
812                                   L"never be failed\n", NT_SUCCESS(status));
813                 }
814 
815                 Irp->IoStatus.Status = status;
816 
817                 //
818                 // We got a CANCEL - roll back to the previous state only
819                 // if the current state is the respective QUERY state.
820                 //
821 
822                 if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
823                     (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
824                     ) ||
825                    ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
826                     (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
827                     )
828                    ) {
829 
830                     commonExtension->CurrentState =
831                         commonExtension->PreviousState;
832                     commonExtension->PreviousState = 0xff;
833 
834                 }
835 
836                 if(isFdo) {
837                     IoCopyCurrentIrpStackLocationToNext(Irp);
838                     ClassReleaseRemoveLock(DeviceObject, Irp);
839                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
840                     completeRequest = FALSE;
841                 } else {
842                     status = STATUS_SUCCESS;
843                 }
844 
845                 break;
846             }
847 
848             case IRP_MN_STOP_DEVICE: {
849 
850                 //
851                 // These all mean nothing to the class driver currently.  The
852                 // port driver will handle all queueing when necessary.
853                 //
854 
855                 DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
856                             DeviceObject, Irp,
857                             (isFdo ? "fdo" : "pdo")
858                             ));
859 
860                 ASSERT(commonExtension->PagingPathCount == 0);
861 
862                 //
863                 // ISSUE-2000/02/03-peterwie
864                 // if we stop the timer here then it means no class driver can
865                 // do i/o in its ClassStopDevice routine.  This is because the
866                 // retry (among other things) is tied into the tick handler
867                 // and disabling retries could cause the class driver to deadlock.
868                 // Currently no class driver we're aware of issues i/o in its
869                 // Stop routine but this is a case we may want to defend ourself
870                 // against.
871                 //
872 
873                 if (DeviceObject->Timer) {
874                     IoStopTimer(DeviceObject);
875                 }
876 
877                 status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
878 
879                 NT_ASSERTMSGW(L"ClassDispatchPnp !! STOP_DEVICE should "
880                               L"never be failed\n", NT_SUCCESS(status));
881 
882                 if(isFdo) {
883                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
884                 }
885 
886                 if(NT_SUCCESS(status)) {
887                     commonExtension->CurrentState = irpStack->MinorFunction;
888                     commonExtension->PreviousState = 0xff;
889                 }
890 
891                 break;
892             }
893 
894             case IRP_MN_REMOVE_DEVICE:
895             case IRP_MN_SURPRISE_REMOVAL: {
896 
897                 UCHAR removeType = irpStack->MinorFunction;
898 
899                 if (commonExtension->PagingPathCount != 0) {
900                     DBGTRACE(ClassDebugWarning, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
901                 }
902 
903                 //
904                 // Release the lock for this IRP before calling in.
905                 //
906                 ClassReleaseRemoveLock(DeviceObject, Irp);
907                 lockReleased = TRUE;
908 
909                 /*
910                  *  If a timer was started on the device, stop it.
911                  */
912                 if (DeviceObject->Timer) {
913                     IoStopTimer(DeviceObject);
914                 }
915 
916                 /*
917                  *  "Fire-and-forget" the remove irp to the lower stack.
918                  *  Don't touch the irp (or the irp stack!) after this.
919                  */
920                 if (isFdo) {
921                     IoCopyCurrentIrpStackLocationToNext(Irp);
922                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
923                     ASSERT(NT_SUCCESS(status));
924                     completeRequest = FALSE;
925                 }
926                 else {
927                     status = STATUS_SUCCESS;
928                 }
929 
930                 /*
931                  *  Do our own cleanup and call the class driver's remove
932                  *  cleanup routine.
933                  *  For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
934                  *  so don't touch the extension after this.
935                  */
936                 commonExtension->PreviousState = commonExtension->CurrentState;
937                 commonExtension->CurrentState = removeType;
938                 ClassRemoveDevice(DeviceObject, removeType);
939 
940                 break;
941             }
942 
943             case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
944 
945                 switch(irpStack->Parameters.UsageNotification.Type) {
946 
947                     case DeviceUsageTypePaging: {
948 
949                         BOOLEAN setPagable;
950 
951                         if((irpStack->Parameters.UsageNotification.InPath) &&
952                            (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
953 
954                             //
955                             // Device isn't started.  Don't allow adding a
956                             // paging file, but allow a removal of one.
957                             //
958 
959                             status = STATUS_DEVICE_NOT_READY;
960                             break;
961                         }
962 
963                         ASSERT(commonExtension->IsInitialized);
964 
965                         //
966                         // need to synchronize this now...
967                         //
968 
969                         KeEnterCriticalRegion();
970                         status = KeWaitForSingleObject(&commonExtension->PathCountEvent,
971                                                        Executive, KernelMode,
972                                                        FALSE, NULL);
973                         ASSERT(NT_SUCCESS(status));
974                         status = STATUS_SUCCESS;
975 
976                         //
977                         // If the volume is removable we should try to lock it in
978                         // place or unlock it once per paging path count
979                         //
980 
981                         if (commonExtension->IsFdo){
982                             status = ClasspEjectionControl(
983                                             DeviceObject,
984                                             Irp,
985                                             InternalMediaLock,
986                                             (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
987                         }
988 
989                         if (!NT_SUCCESS(status)){
990                             KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
991                             KeLeaveCriticalRegion();
992                             break;
993                         }
994 
995                         //
996                         // if removing last paging device, need to set DO_POWER_PAGABLE
997                         // bit here, and possible re-set it below on failure.
998                         //
999 
1000                         setPagable = FALSE;
1001 
1002                         if (!irpStack->Parameters.UsageNotification.InPath &&
1003                             commonExtension->PagingPathCount == 1
1004                             ) {
1005 
1006                             //
1007                             // removing last paging file
1008                             // must have DO_POWER_PAGABLE bits set, but only
1009                             // if noone set the DO_POWER_INRUSH bit
1010                             //
1011 
1012 
1013                             if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1014                                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1015                                             "paging file removed, but "
1016                                             "DO_POWER_INRUSH was set, so NOT "
1017                                             "setting DO_POWER_PAGABLE\n",
1018                                             DeviceObject, Irp));
1019                             } else {
1020                                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
1021                                             "paging file removed, "
1022                                             "setting DO_POWER_PAGABLE\n",
1023                                             DeviceObject, Irp));
1024                                 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1025                                 setPagable = TRUE;
1026                             }
1027 
1028                         }
1029 
1030                         //
1031                         // forward the irp before finishing handling the
1032                         // special cases
1033                         //
1034 
1035                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1036 
1037                         //
1038                         // now deal with the failure and success cases.
1039                         // note that we are not allowed to fail the irp
1040                         // once it is sent to the lower drivers.
1041                         //
1042 
1043                         if (NT_SUCCESS(status)) {
1044 
1045                             IoAdjustPagingPathCount(
1046                                 (PLONG)&commonExtension->PagingPathCount,
1047                                 irpStack->Parameters.UsageNotification.InPath);
1048 
1049                             if (irpStack->Parameters.UsageNotification.InPath) {
1050                                 if (commonExtension->PagingPathCount == 1) {
1051                                     DebugPrint((2, "ClassDispatchPnp (%p,%p): "
1052                                                 "Clearing PAGABLE bit\n",
1053                                                 DeviceObject, Irp));
1054                                     CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1055                                 }
1056                             }
1057 
1058                         } else {
1059 
1060                             //
1061                             // cleanup the changes done above
1062                             //
1063 
1064                             if (setPagable != FALSE) {
1065                                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
1066                                             "PAGABLE bit due to irp failure\n",
1067                                             DeviceObject, Irp));
1068                                 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1069                                 setPagable = FALSE;
1070                             }
1071 
1072                             //
1073                             // relock or unlock the media if needed.
1074                             //
1075 
1076                             if (commonExtension->IsFdo) {
1077 
1078                                 ClasspEjectionControl(
1079                                         DeviceObject,
1080                                         Irp,
1081                                         InternalMediaLock,
1082                                         (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
1083                             }
1084                         }
1085 
1086                         //
1087                         // set the event so the next one can occur.
1088                         //
1089 
1090                         KeSetEvent(&commonExtension->PathCountEvent,
1091                                    IO_NO_INCREMENT, FALSE);
1092                         KeLeaveCriticalRegion();
1093                         break;
1094                     }
1095 
1096                     case DeviceUsageTypeHibernation: {
1097 
1098                         IoAdjustPagingPathCount(
1099                             (PLONG)&commonExtension->HibernationPathCount,
1100                             irpStack->Parameters.UsageNotification.InPath
1101                             );
1102                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1103                         if (!NT_SUCCESS(status)) {
1104                             IoAdjustPagingPathCount(
1105                                 (PLONG)&commonExtension->HibernationPathCount,
1106                                 !irpStack->Parameters.UsageNotification.InPath
1107                                 );
1108                         }
1109 
1110                         break;
1111                     }
1112 
1113                     case DeviceUsageTypeDumpFile: {
1114                         IoAdjustPagingPathCount(
1115                             (PLONG)&commonExtension->DumpPathCount,
1116                             irpStack->Parameters.UsageNotification.InPath
1117                             );
1118                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1119                         if (!NT_SUCCESS(status)) {
1120                             IoAdjustPagingPathCount(
1121                                 (PLONG)&commonExtension->DumpPathCount,
1122                                 !irpStack->Parameters.UsageNotification.InPath
1123                                 );
1124                         }
1125 
1126                         break;
1127                     }
1128 
1129                     default: {
1130                         status = STATUS_INVALID_PARAMETER;
1131                         break;
1132                     }
1133                 }
1134                 break;
1135             }
1136 
1137             case IRP_MN_QUERY_CAPABILITIES: {
1138 
1139                 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1140                             DeviceObject, Irp));
1141 
1142                 if(!isFdo) {
1143 
1144                     status = ClassQueryPnpCapabilities(
1145                                 DeviceObject,
1146                                 irpStack->Parameters.DeviceCapabilities.Capabilities
1147                                 );
1148 
1149                     break;
1150 
1151                 } else {
1152 
1153                     PDEVICE_CAPABILITIES deviceCapabilities;
1154                     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1155                     PCLASS_PRIVATE_FDO_DATA fdoData;
1156 
1157                     fdoExtension = DeviceObject->DeviceExtension;
1158                     fdoData = fdoExtension->PrivateFdoData;
1159                     deviceCapabilities =
1160                         irpStack->Parameters.DeviceCapabilities.Capabilities;
1161 
1162                     //
1163                     // forward the irp before handling the special cases
1164                     //
1165 
1166                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
1167                     if (!NT_SUCCESS(status)) {
1168                         break;
1169                     }
1170 
1171                     //
1172                     // we generally want to remove the device from the hotplug
1173                     // applet, which requires the SR-OK bit to be set.
1174                     // only when the user specifies that they are capable of
1175                     // safely removing things do we want to clear this bit
1176                     // (saved in WriteCacheEnableOverride)
1177                     //
1178                     // setting of this bit is done either above, or by the
1179                     // lower driver.
1180                     //
1181                     // note: may not be started, so check we have FDO data first.
1182                     //
1183 
1184                     if (fdoData &&
1185                         fdoData->HotplugInfo.WriteCacheEnableOverride) {
1186                         if (deviceCapabilities->SurpriseRemovalOK) {
1187                             DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
1188                                         "device capabilities due to hotplug "
1189                                         "device or media\n"));
1190                         }
1191                         deviceCapabilities->SurpriseRemovalOK = FALSE;
1192                     }
1193                     break;
1194 
1195                 } // end QUERY_CAPABILITIES for FDOs
1196 
1197                 ASSERT(FALSE);
1198                 break;
1199 
1200 
1201             } // end QUERY_CAPABILITIES
1202 
1203             default: {
1204 
1205                 if (isFdo){
1206                     IoCopyCurrentIrpStackLocationToNext(Irp);
1207 
1208                     ClassReleaseRemoveLock(DeviceObject, Irp);
1209                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1210 
1211                     completeRequest = FALSE;
1212                 }
1213 
1214                 break;
1215             }
1216         }
1217     }
1218     else {
1219         ASSERT(driverExtension);
1220         status = STATUS_INTERNAL_ERROR;
1221     }
1222 
1223     if (completeRequest){
1224         Irp->IoStatus.Status = status;
1225 
1226         if (!lockReleased){
1227             ClassReleaseRemoveLock(DeviceObject, Irp);
1228         }
1229 
1230         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1231 
1232         DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
1233     }
1234     else {
1235         /*
1236          *  The irp is already completed so don't touch it.
1237          *  This may be a remove so don't touch the device extension.
1238          */
1239         DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
1240     }
1241 
1242     return status;
1243 } // end ClassDispatchPnp()
1244 
1245 /*++////////////////////////////////////////////////////////////////////////////
1246 
1247 ClassPnpStartDevice()
1248 
1249 Routine Description:
1250 
1251     Storage class driver routine for IRP_MN_START_DEVICE requests.
1252     This routine kicks off any device specific initialization
1253 
1254 Arguments:
1255 
1256     DeviceObject - a pointer to the device object
1257 
1258     Irp - a pointer to the io request packet
1259 
1260 Return Value:
1261 
1262     none
1263 
1264 --*/
1265 NTSTATUS NTAPI ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
1266 {
1267     PCLASS_DRIVER_EXTENSION driverExtension;
1268     PCLASS_INIT_DATA initData;
1269 
1270     PCLASS_DEV_INFO devInfo;
1271 
1272     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1273     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1274     BOOLEAN isFdo = commonExtension->IsFdo;
1275 
1276     BOOLEAN isMountedDevice = TRUE;
1277     //UNICODE_STRING  interfaceName;
1278 
1279     BOOLEAN timerStarted;
1280 
1281     NTSTATUS status = STATUS_SUCCESS;
1282 
1283     PAGED_CODE();
1284 
1285     driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
1286                                                  CLASS_DRIVER_EXTENSION_KEY);
1287 
1288     initData = &(driverExtension->InitData);
1289     if(isFdo) {
1290         devInfo = &(initData->FdoData);
1291     } else {
1292         devInfo = &(initData->PdoData);
1293     }
1294 
1295     ASSERT(devInfo->ClassInitDevice != NULL);
1296     ASSERT(devInfo->ClassStartDevice != NULL);
1297 
1298     if (!commonExtension->IsInitialized){
1299 
1300         //
1301         // perform FDO/PDO specific initialization
1302         //
1303 
1304         if (isFdo){
1305             STORAGE_PROPERTY_ID propertyId;
1306 
1307             //
1308             // allocate a private extension for class data
1309             //
1310 
1311             if (fdoExtension->PrivateFdoData == NULL) {
1312                 fdoExtension->PrivateFdoData =
1313                     ExAllocatePoolWithTag(NonPagedPool,
1314                                           sizeof(CLASS_PRIVATE_FDO_DATA),
1315                                           CLASS_TAG_PRIVATE_DATA
1316                                           );
1317             }
1318 
1319             if (fdoExtension->PrivateFdoData == NULL) {
1320                 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
1321                             "private fdo data\n"));
1322                 return STATUS_INSUFFICIENT_RESOURCES;
1323             }
1324 
1325             //
1326             // initialize the struct's various fields.
1327             //
1328 
1329             RtlZeroMemory(fdoExtension->PrivateFdoData,
1330                           sizeof(CLASS_PRIVATE_FDO_DATA)
1331                           );
1332             KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
1333             KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
1334                             ClasspRetryRequestDpc,
1335                             DeviceObject);
1336             KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
1337             fdoExtension->PrivateFdoData->Retry.Granularity =
1338                 KeQueryTimeIncrement();
1339             commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
1340 
1341             //
1342             // NOTE: the old interface allowed the class driver to allocate
1343             // this.  this was unsafe for low-memory conditions. allocate one
1344             // unconditionally now, and modify our internal functions to use
1345             // our own exclusively as it is the only safe way to do this.
1346             //
1347 
1348             status = ClasspAllocateReleaseQueueIrp(fdoExtension);
1349             if (!NT_SUCCESS(status)) {
1350                 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
1351                             "private release queue irp\n"));
1352                 return status;
1353             }
1354 
1355             //
1356             // Call port driver to get adapter capabilities.
1357             //
1358 
1359             propertyId = StorageAdapterProperty;
1360 
1361             status = ClassGetDescriptor(
1362                         commonExtension->LowerDeviceObject,
1363                         &propertyId,
1364                         (PSTORAGE_DESCRIPTOR_HEADER *)&fdoExtension->AdapterDescriptor);
1365 
1366             if(!NT_SUCCESS(status)) {
1367 
1368                 //
1369                 // This DebugPrint is to help third-party driver writers
1370                 //
1371 
1372                 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1373                                "[ADAPTER] failed %lx\n", status));
1374                 return status;
1375             }
1376 
1377             //
1378             // Call port driver to get device descriptor.
1379             //
1380 
1381             propertyId = StorageDeviceProperty;
1382 
1383             status = ClassGetDescriptor(
1384                         commonExtension->LowerDeviceObject,
1385                         &propertyId,
1386                         (PSTORAGE_DESCRIPTOR_HEADER *)&fdoExtension->DeviceDescriptor);
1387 
1388             if(!NT_SUCCESS(status)) {
1389 
1390                 //
1391                 // This DebugPrint is to help third-party driver writers
1392                 //
1393 
1394                 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
1395                                "[DEVICE] failed %lx\n", status));
1396                 return status;
1397             }
1398 
1399             ClasspScanForSpecialInRegistry(fdoExtension);
1400             ClassScanForSpecial(fdoExtension,
1401                                 ClassBadItems,
1402                                 ClasspScanForClassHacks);
1403 
1404             //
1405             // allow perf to be re-enabled after a given number of failed IOs
1406             // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
1407             //
1408 
1409             {
1410                 ULONG t = 0;
1411                 ClassGetDeviceParameter(fdoExtension,
1412                                         CLASSP_REG_SUBKEY_NAME,
1413                                         CLASSP_REG_PERF_RESTORE_VALUE_NAME,
1414                                         &t);
1415                 if (t >= CLASS_PERF_RESTORE_MINIMUM) {
1416                     fdoExtension->PrivateFdoData->Perf.ReEnableThreshold = t;
1417                 }
1418             }
1419 
1420 
1421             //
1422             // compatibility comes first.  writable cd media will not
1423             // get a SYNCH_CACHE on power down.
1424             //
1425 
1426             if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
1427                 SET_FLAG(fdoExtension->PrivateFdoData->HackFlags,
1428                          FDO_HACK_NO_SYNC_CACHE);
1429             }
1430 
1431             //
1432             // initialize the hotplug information only after the ScanForSpecial
1433             // routines, as it relies upon the hack flags.
1434             //
1435 
1436             status = ClasspInitializeHotplugInfo(fdoExtension);
1437 
1438             if (!NT_SUCCESS(status)) {
1439                 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
1440                             "hotplug information %lx\n", status));
1441                 return status;
1442             }
1443 
1444             /*
1445              *  Allocate/initialize TRANSFER_PACKETs and related resources.
1446              */
1447             status = InitializeTransferPackets(DeviceObject);
1448         }
1449 
1450         //
1451         // ISSUE - drivers need to disable write caching on the media
1452         //         if hotplug and !useroverride.  perhaps we should
1453         //         allow registration of a callback to enable/disable
1454         //         write cache instead.
1455         //
1456 
1457         if (NT_SUCCESS(status)){
1458             status = devInfo->ClassInitDevice(DeviceObject);
1459         }
1460 
1461     }
1462 
1463     if (!NT_SUCCESS(status)){
1464 
1465         //
1466         // Just bail out - the remove that comes down will clean up the
1467         // initialized scraps.
1468         //
1469 
1470         return status;
1471     } else {
1472         commonExtension->IsInitialized = TRUE;
1473 
1474         if (commonExtension->IsFdo) {
1475             fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
1476         }
1477 
1478     }
1479 
1480     //
1481     // If device requests autorun functionality or a once a second callback
1482     // then enable the once per second timer.
1483     //
1484     // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
1485     //       called in the context of the ClassInitDevice callback. If called
1486     //       after then this check will have already been made and the
1487     //       once a second timer will not have been enabled.
1488     //
1489     if ((isFdo) &&
1490         ((initData->ClassTick != NULL) ||
1491          (fdoExtension->MediaChangeDetectionInfo != NULL) ||
1492          ((fdoExtension->FailurePredictionInfo != NULL) &&
1493           (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
1494     {
1495         ClasspEnableTimer(DeviceObject);
1496         timerStarted = TRUE;
1497     } else {
1498         timerStarted = FALSE;
1499     }
1500 
1501     //
1502     // NOTE: the timer looks at commonExtension->CurrentState now
1503     //       to prevent Media Change Notification code from running
1504     //       until the device is started, but allows the device
1505     //       specific tick handler to run.  therefore it is imperative
1506     //       that commonExtension->CurrentState not be updated until
1507     //       the device specific startdevice handler has finished.
1508     //
1509 
1510     status = devInfo->ClassStartDevice(DeviceObject);
1511 
1512     if(NT_SUCCESS(status)) {
1513         commonExtension->CurrentState = IRP_MN_START_DEVICE;
1514 
1515         if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
1516             isMountedDevice = FALSE;
1517         }
1518 
1519         if((DeviceObject->DeviceType != FILE_DEVICE_DISK) &&
1520            (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM)) {
1521 
1522             isMountedDevice = FALSE;
1523         }
1524 
1525 
1526         if(isMountedDevice) {
1527             ClasspRegisterMountedDeviceInterface(DeviceObject);
1528         }
1529 
1530         if((commonExtension->IsFdo) &&
1531            (devInfo->ClassWmiInfo.GuidRegInfo != NULL)) {
1532 
1533             IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
1534         }
1535     } else {
1536 
1537         if (timerStarted) {
1538             ClasspDisableTimer(DeviceObject);
1539         }
1540     }
1541 
1542     return status;
1543 }
1544 
1545 
1546 /*++////////////////////////////////////////////////////////////////////////////
1547 
1548 ClassReadWrite()
1549 
1550 Routine Description:
1551 
1552     This is the system entry point for read and write requests. The
1553     device-specific handler is invoked to perform any validation necessary.
1554 
1555     If the device object is a PDO (partition object) then the request will
1556     simply be adjusted for Partition0 and issued to the lower device driver.
1557 
1558     IF the device object is an FDO (partition 0 object), the number of bytes
1559     in the request are checked against the maximum byte counts that the adapter
1560     supports and requests are broken up into
1561     smaller sizes if necessary.
1562 
1563 Arguments:
1564 
1565     DeviceObject - a pointer to the device object for this request
1566 
1567     Irp - IO request
1568 
1569 Return Value:
1570 
1571     NT Status
1572 
1573 --*/
1574 NTSTATUS NTAPI ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
1575 {
1576     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1577     PDEVICE_OBJECT      lowerDeviceObject = commonExtension->LowerDeviceObject;
1578     PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
1579     //LARGE_INTEGER       startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
1580     ULONG               transferByteCount = currentIrpStack->Parameters.Read.Length;
1581     ULONG               isRemoved;
1582     NTSTATUS            status;
1583 
1584     /*
1585      *  Grab the remove lock.  If we can't acquire it, bail out.
1586      */
1587     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
1588     if (isRemoved) {
1589         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
1590         ClassReleaseRemoveLock(DeviceObject, Irp);
1591         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1592         status = STATUS_DEVICE_DOES_NOT_EXIST;
1593     }
1594     else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
1595              (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
1596              !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
1597 
1598         /*
1599          *  DO_VERIFY_VOLUME is set for the device object,
1600          *  but this request is not itself a verify request.
1601          *  So fail this request.
1602          */
1603         IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
1604         Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
1605         Irp->IoStatus.Information = 0;
1606         ClassReleaseRemoveLock(DeviceObject, Irp);
1607         ClassCompleteRequest(DeviceObject, Irp, 0);
1608         status = STATUS_VERIFY_REQUIRED;
1609     }
1610     else {
1611 
1612         /*
1613          *  Since we've bypassed the verify-required tests we don't need to repeat
1614          *  them with this IRP - in particular we don't want to worry about
1615          *  hitting them at the partition 0 level if the request has come through
1616          *  a non-zero partition.
1617          */
1618         currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
1619 
1620         /*
1621          *  Call the miniport driver's pre-pass filter to check if we
1622          *  should continue with this transfer.
1623          */
1624         ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
1625         status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
1626         if (!NT_SUCCESS(status)){
1627             ASSERT(Irp->IoStatus.Status == status);
1628             ClassReleaseRemoveLock(DeviceObject, Irp);
1629             ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
1630         }
1631         else if (status == STATUS_PENDING){
1632             /*
1633              *  ClassReadWriteVerification queued this request.
1634              *  So don't touch the irp anymore.
1635              */
1636         }
1637         else {
1638 
1639             if (transferByteCount == 0) {
1640                 /*
1641                  *  Several parts of the code turn 0 into 0xffffffff,
1642                  *  so don't process a zero-length request any further.
1643                  */
1644                 Irp->IoStatus.Status = STATUS_SUCCESS;
1645                 Irp->IoStatus.Information = 0;
1646                 ClassReleaseRemoveLock(DeviceObject, Irp);
1647                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1648                 status = STATUS_SUCCESS;
1649             }
1650             else {
1651                 /*
1652                  *  If the driver has its own StartIo routine, call it.
1653                  */
1654                 if (commonExtension->DriverExtension->InitData.ClassStartIo) {
1655                     IoMarkIrpPending(Irp);
1656                     IoStartPacket(DeviceObject, Irp, NULL, NULL);
1657                     status = STATUS_PENDING;
1658                 }
1659                 else {
1660                     /*
1661                      *  The driver does not have its own StartIo routine.
1662                      *  So process this request ourselves.
1663                      */
1664 
1665                     /*
1666                      *  Add partition byte offset to make starting byte relative to
1667                      *  beginning of disk.
1668                      */
1669                     currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
1670                         commonExtension->StartingOffset.QuadPart;
1671 
1672                     if (commonExtension->IsFdo){
1673 
1674                         /*
1675                          *  Add in any skew for the disk manager software.
1676                          */
1677                         currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
1678                              commonExtension->PartitionZeroExtension->DMByteSkew;
1679 
1680                         /*
1681                          *  Perform the actual transfer(s) on the hardware
1682                          *  to service this request.
1683                          */
1684                         ServiceTransferRequest(DeviceObject, Irp);
1685                         status = STATUS_PENDING;
1686                     }
1687                     else {
1688                         /*
1689                          *  This is a child PDO enumerated for our FDO by e.g. disk.sys
1690                          *  and owned by e.g. partmgr.  Send it down to the next device
1691                          *  and the same irp will come back to us for the FDO.
1692                          */
1693                         IoCopyCurrentIrpStackLocationToNext(Irp);
1694                         ClassReleaseRemoveLock(DeviceObject, Irp);
1695                         status = IoCallDriver(lowerDeviceObject, Irp);
1696                     }
1697                 }
1698             }
1699         }
1700     }
1701 
1702     return status;
1703 }
1704 
1705 
1706 /*++////////////////////////////////////////////////////////////////////////////
1707 
1708 ClassReadDriveCapacity()
1709 
1710 Routine Description:
1711 
1712     This routine sends a READ CAPACITY to the requested device, updates
1713     the geometry information in the device object and returns
1714     when it is complete.  This routine is synchronous.
1715 
1716     This routine must be called with the remove lock held or some other
1717     assurance that the Fdo will not be removed while processing.
1718 
1719 Arguments:
1720 
1721     DeviceObject - Supplies a pointer to the device object that represents
1722         the device whose capacity is to be read.
1723 
1724 Return Value:
1725 
1726     Status is returned.
1727 
1728 --*/
1729 NTSTATUS NTAPI ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo)
1730 {
1731     READ_CAPACITY_DATA readCapacityBuffer = {0};
1732     NTSTATUS status;
1733     PMDL driveCapMdl;
1734 
1735     driveCapMdl = BuildDeviceInputMdl(&readCapacityBuffer, sizeof(READ_CAPACITY_DATA));
1736     if (driveCapMdl){
1737 
1738         TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
1739         if (pkt){
1740             PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
1741             KEVENT event;
1742             //NTSTATUS pktStatus;
1743             IRP pseudoIrp = {0};
1744 
1745             /*
1746              *  Our engine needs an "original irp" to write the status back to
1747              *  and to count down packets (one in this case).
1748              *  Just use a pretend irp for this.
1749              */
1750             pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
1751             pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
1752             pseudoIrp.IoStatus.Information = 0;
1753             pseudoIrp.MdlAddress = driveCapMdl;
1754 
1755             /*
1756              *  Set this up as a SYNCHRONOUS transfer, submit it,
1757              *  and wait for the packet to complete.  The result
1758              *  status will be written to the original irp.
1759              */
1760             KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1761             SetupDriveCapacityTransferPacket(   pkt,
1762                                             &readCapacityBuffer,
1763                                             sizeof(READ_CAPACITY_DATA),
1764                                             &event,
1765                                             &pseudoIrp);
1766             SubmitTransferPacket(pkt);
1767             KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1768 
1769             status = pseudoIrp.IoStatus.Status;
1770 
1771             /*
1772              *  If we got an UNDERRUN, retry exactly once.
1773              *  (The transfer_packet engine didn't retry because the result
1774              *   status was success).
1775              */
1776             if (NT_SUCCESS(status) &&
1777                (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA))){
1778                 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG)pseudoIrp.IoStatus.Information, sizeof(READ_CAPACITY_DATA)));
1779 
1780                 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
1781                 if (pkt){
1782                     pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
1783                     pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
1784                     pseudoIrp.IoStatus.Information = 0;
1785                     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1786                     SetupDriveCapacityTransferPacket(   pkt,
1787                                                     &readCapacityBuffer,
1788                                                     sizeof(READ_CAPACITY_DATA),
1789                                                     &event,
1790                                                     &pseudoIrp);
1791                     SubmitTransferPacket(pkt);
1792                     KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1793                     status = pseudoIrp.IoStatus.Status;
1794                     if (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA)){
1795                         status = STATUS_DEVICE_BUSY;
1796                     }
1797                 }
1798                 else {
1799                     status = STATUS_INSUFFICIENT_RESOURCES;
1800                 }
1801             }
1802 
1803 
1804             if (NT_SUCCESS(status)){
1805                 /*
1806                  *  The request succeeded.
1807                  *  Read out and store the drive information.
1808                  */
1809                 ULONG cylinderSize;
1810                 ULONG bytesPerSector;
1811                 ULONG tmp;
1812                 ULONG lastSector;
1813 
1814                 /*
1815                  *  Read the bytesPerSector value,
1816                  *  which is big-endian in the returned buffer.
1817                  *  Default to the standard 512 bytes.
1818                  */
1819                 tmp = readCapacityBuffer.BytesPerBlock;
1820                 ((PFOUR_BYTE)&bytesPerSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
1821                 ((PFOUR_BYTE)&bytesPerSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
1822                 ((PFOUR_BYTE)&bytesPerSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
1823                 ((PFOUR_BYTE)&bytesPerSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
1824                 if (bytesPerSector == 0) {
1825                     bytesPerSector = 512;
1826                 }
1827                 else {
1828                     /*
1829                      *  Clear all but the highest set bit.
1830                      *  That will give us a bytesPerSector value that is a power of 2.
1831                      */
1832                     while (bytesPerSector & (bytesPerSector-1)) {
1833                         bytesPerSector &= bytesPerSector-1;
1834                     }
1835                 }
1836                 fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
1837 
1838                 //
1839                 // Copy last sector in reverse byte order.
1840                 //
1841 
1842                 tmp = readCapacityBuffer.LogicalBlockAddress;
1843                 ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
1844                 ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
1845                 ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
1846                 ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
1847 
1848                 //
1849                 // Calculate sector to byte shift.
1850                 //
1851 
1852                 WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
1853 
1854                 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
1855                     fdoExt->DiskGeometry.BytesPerSector));
1856 
1857                 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
1858                     lastSector + 1));
1859 
1860                 if (fdoExt->DMActive){
1861                     DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
1862                                 fdoExt->DMSkew));
1863                     lastSector -= fdoExt->DMSkew;
1864                 }
1865 
1866                 /*
1867                  *  Check to see if we have a geometry we should be using already.
1868                  */
1869                 cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
1870                                 fdoExt->DiskGeometry.SectorsPerTrack);
1871                 if (cylinderSize == 0){
1872                     DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
1873                                    "values from %#x/%#x to %#x/%#x\n",
1874                                 fdoExt->DiskGeometry.TracksPerCylinder,
1875                                 fdoExt->DiskGeometry.SectorsPerTrack,
1876                                 0xff,
1877                                 0x3f));
1878 
1879                     fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
1880                     fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
1881 
1882 
1883                     cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
1884                                     fdoExt->DiskGeometry.SectorsPerTrack);
1885                 }
1886 
1887                 //
1888                 // Calculate number of cylinders.
1889                 //
1890 
1891                 fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/cylinderSize);
1892 
1893                 //
1894                 // if there are zero cylinders, then the device lied AND it's
1895                 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
1896                 // this can fit into a single LONGLONG, so create another usable
1897                 // geometry, even if it's unusual looking.  This allows small,
1898                 // non-standard devices, such as Sony's Memory Stick, to show
1899                 // up as having a partition.
1900                 //
1901 
1902                 if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
1903                     fdoExt->DiskGeometry.SectorsPerTrack    = 1;
1904                     fdoExt->DiskGeometry.TracksPerCylinder  = 1;
1905                     fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector;
1906                 }
1907 
1908 
1909                 //
1910                 // Calculate media capacity in bytes.
1911                 //
1912 
1913                 fdoExt->CommonExtension.PartitionLength.QuadPart =
1914                     ((LONGLONG)(lastSector + 1)) << fdoExt->SectorShift;
1915 
1916                 /*
1917                  *  Is this removable or fixed media
1918                  */
1919                 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
1920                     fdoExt->DiskGeometry.MediaType = RemovableMedia;
1921                 }
1922                 else {
1923                     fdoExt->DiskGeometry.MediaType = FixedMedia;
1924                 }
1925             }
1926             else {
1927                 /*
1928                  *  The request failed.
1929                  */
1930 
1931                 //
1932                 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
1933                 //    what happens when the disk's sector size is bigger than
1934                 //    512 bytes and we hit this code path?  this is untested.
1935                 //
1936                 // If the read capacity fails, set the geometry to reasonable parameter
1937                 // so things don't fail at unexpected places.  Zero the geometry
1938                 // except for the bytes per sector and sector shift.
1939                 //
1940 
1941                 /*
1942                  *  This request can sometimes fail legitimately
1943                  *  (e.g. when a SCSI device is attached but turned off)
1944                  *  so this is not necessarily a device/driver bug.
1945                  */
1946                 DBGTRACE(ClassDebugWarning, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo, status));
1947 
1948                 /*
1949                  *  Write in a default disk geometry which we HOPE is right (??).
1950                  *      BUGBUG !!
1951                  */
1952                 RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
1953                 fdoExt->DiskGeometry.BytesPerSector = 512;
1954                 fdoExt->SectorShift = 9;
1955                 fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
1956 
1957                 /*
1958                  *  Is this removable or fixed media
1959                  */
1960                 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
1961                     fdoExt->DiskGeometry.MediaType = RemovableMedia;
1962                 }
1963                 else {
1964                     fdoExt->DiskGeometry.MediaType = FixedMedia;
1965                 }
1966             }
1967 
1968         }
1969         else {
1970             status = STATUS_INSUFFICIENT_RESOURCES;
1971         }
1972 
1973         FreeDeviceInputMdl(driveCapMdl);
1974     }
1975     else {
1976         status = STATUS_INSUFFICIENT_RESOURCES;
1977     }
1978 
1979     return status;
1980 }
1981 
1982 
1983 /*++////////////////////////////////////////////////////////////////////////////
1984 
1985 ClassSendStartUnit()
1986 
1987 Routine Description:
1988 
1989     Send command to SCSI unit to start or power up.
1990     Because this command is issued asynchronously, that is, without
1991     waiting on it to complete, the IMMEDIATE flag is not set. This
1992     means that the CDB will not return until the drive has powered up.
1993     This should keep subsequent requests from being submitted to the
1994     device before it has completely spun up.
1995 
1996     This routine is called from the InterpretSense routine, when a
1997     request sense returns data indicating that a drive must be
1998     powered up.
1999 
2000     This routine may also be called from a class driver's error handler,
2001     or anytime a non-critical start device should be sent to the device.
2002 
2003 Arguments:
2004 
2005     Fdo - The functional device object for the stopped device.
2006 
2007 Return Value:
2008 
2009     None.
2010 
2011 --*/
2012 VOID
2013 NTAPI
2014 ClassSendStartUnit(
2015     IN PDEVICE_OBJECT Fdo
2016     )
2017 {
2018     PIO_STACK_LOCATION irpStack;
2019     PIRP irp;
2020     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2021     PSCSI_REQUEST_BLOCK srb;
2022     PCOMPLETION_CONTEXT context;
2023     PCDB cdb;
2024 
2025     //
2026     // Allocate Srb from nonpaged pool.
2027     //
2028 
2029     context = ExAllocatePoolWithTag(NonPagedPool,
2030                              sizeof(COMPLETION_CONTEXT),
2031                              '6CcS');
2032 
2033     if(context == NULL) {
2034 
2035         //
2036         // ISSUE-2000/02/03-peterwie
2037         // This code path was inherited from the NT 4.0 class2.sys driver.
2038         // It needs to be changed to survive low-memory conditions.
2039         //
2040 
2041         KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
2042     }
2043 
2044     //
2045     // Save the device object in the context for use by the completion
2046     // routine.
2047     //
2048 
2049     context->DeviceObject = Fdo;
2050     srb = &context->Srb;
2051 
2052     //
2053     // Zero out srb.
2054     //
2055 
2056     RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
2057 
2058     //
2059     // Write length to SRB.
2060     //
2061 
2062     srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2063 
2064     srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2065 
2066     //
2067     // Set timeout value large enough for drive to spin up.
2068     //
2069 
2070     srb->TimeOutValue = START_UNIT_TIMEOUT;
2071 
2072     //
2073     // Set the transfer length.
2074     //
2075 
2076     srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
2077                     SRB_FLAGS_DISABLE_AUTOSENSE |
2078                     SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
2079 
2080     //
2081     // Build the start unit CDB.
2082     //
2083 
2084     srb->CdbLength = 6;
2085     cdb = (PCDB)srb->Cdb;
2086 
2087     cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
2088     cdb->START_STOP.Start = 1;
2089     cdb->START_STOP.Immediate = 0;
2090     cdb->START_STOP.LogicalUnitNumber = srb->Lun;
2091 
2092     //
2093     // Build the asynchronous request to be sent to the port driver.
2094     // Since this routine is called from a DPC the IRP should always be
2095     // available.
2096     //
2097 
2098     irp = IoAllocateIrp(Fdo->StackSize, FALSE);
2099 
2100     if(irp == NULL) {
2101 
2102         //
2103         // ISSUE-2000/02/03-peterwie
2104         // This code path was inherited from the NT 4.0 class2.sys driver.
2105         // It needs to be changed to survive low-memory conditions.
2106         //
2107 
2108         KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
2109 
2110     }
2111 
2112     ClassAcquireRemoveLock(Fdo, irp);
2113 
2114     IoSetCompletionRoutine(irp,
2115                            ClassAsynchronousCompletion,
2116                            context,
2117                            TRUE,
2118                            TRUE,
2119                            TRUE);
2120 
2121     irpStack = IoGetNextIrpStackLocation(irp);
2122     irpStack->MajorFunction = IRP_MJ_SCSI;
2123     srb->OriginalRequest = irp;
2124 
2125     //
2126     // Store the SRB address in next stack for port driver.
2127     //
2128 
2129     irpStack->Parameters.Scsi.Srb = srb;
2130 
2131     //
2132     // Call the port driver with the IRP.
2133     //
2134 
2135     IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
2136 
2137     return;
2138 
2139 } // end StartUnit()
2140 
2141 /*++////////////////////////////////////////////////////////////////////////////
2142 
2143 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
2144 
2145 Routine Description:
2146 
2147     This routine is called when an asynchronous I/O request
2148     which was issued by the class driver completes.  Examples of such requests
2149     are release queue or START UNIT. This routine releases the queue if
2150     necessary.  It then frees the context and the IRP.
2151 
2152 Arguments:
2153 
2154     DeviceObject - The device object for the logical unit; however since this
2155         is the top stack location the value is NULL.
2156 
2157     Irp - Supplies a pointer to the Irp to be processed.
2158 
2159     Context - Supplies the context to be used to process this request.
2160 
2161 Return Value:
2162 
2163     None.
2164 
2165 --*/
2166 NTSTATUS
2167 NTAPI
2168 ClassAsynchronousCompletion(
2169     PDEVICE_OBJECT DeviceObject,
2170     PIRP Irp,
2171     PVOID Context
2172     )
2173 {
2174     PCOMPLETION_CONTEXT context = Context;
2175     PSCSI_REQUEST_BLOCK srb;
2176 
2177     if(DeviceObject == NULL) {
2178 
2179         DeviceObject = context->DeviceObject;
2180     }
2181 
2182     srb = &context->Srb;
2183 
2184     //
2185     // If this is an execute srb, then check the return status and make sure.
2186     // the queue is not frozen.
2187     //
2188 
2189     if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
2190 
2191         //
2192         // Check for a frozen queue.
2193         //
2194 
2195         if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
2196 
2197             //
2198             // Unfreeze the queue getting the device object from the context.
2199             //
2200 
2201             ClassReleaseQueue(context->DeviceObject);
2202         }
2203     }
2204 
2205     { // free port-allocated sense buffer if we can detect
2206 
2207         if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
2208 
2209             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
2210             if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2211                 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2212             }
2213 
2214         } else {
2215 
2216             ASSERT(!TEST_FLAG(srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
2217 
2218         }
2219     }
2220 
2221 
2222     //
2223     // Free the context and the Irp.
2224     //
2225 
2226     if (Irp->MdlAddress != NULL) {
2227         MmUnlockPages(Irp->MdlAddress);
2228         IoFreeMdl(Irp->MdlAddress);
2229 
2230         Irp->MdlAddress = NULL;
2231     }
2232 
2233     ClassReleaseRemoveLock(DeviceObject, Irp);
2234 
2235     ExFreePool(context);
2236     IoFreeIrp(Irp);
2237 
2238     //
2239     // Indicate the I/O system should stop processing the Irp completion.
2240     //
2241 
2242     return STATUS_MORE_PROCESSING_REQUIRED;
2243 
2244 } // end ClassAsynchronousCompletion()
2245 
2246 VOID NTAPI ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp)
2247 {
2248     //PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
2249     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
2250     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
2251     //PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
2252     PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
2253     ULONG entireXferLen = currentSp->Parameters.Read.Length;
2254     PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
2255     LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
2256     PTRANSFER_PACKET pkt;
2257     SLIST_ENTRY pktList;
2258     PSLIST_ENTRY slistEntry;
2259     ULONG numPackets;
2260     //KIRQL oldIrql;
2261     ULONG i;
2262 
2263     /*
2264      *  Compute the number of hw xfers we'll have to do.
2265      *  Calculate this without allowing for an overflow condition.
2266      */
2267     ASSERT(fdoData->HwMaxXferLen >= PAGE_SIZE);
2268     numPackets = entireXferLen/fdoData->HwMaxXferLen;
2269     if (entireXferLen % fdoData->HwMaxXferLen){
2270         numPackets++;
2271     }
2272 
2273     /*
2274      *  First get all the TRANSFER_PACKETs that we'll need at once.
2275      *  Use our 'simple' slist functions since we don't need interlocked.
2276      */
2277     SimpleInitSlistHdr(&pktList);
2278     for (i = 0; i < numPackets; i++){
2279         pkt = DequeueFreeTransferPacket(Fdo, TRUE);
2280         if (pkt){
2281             SimplePushSlist(&pktList, &pkt->SlistEntry);
2282         }
2283         else {
2284             break;
2285         }
2286     }
2287 
2288     if (i == numPackets){
2289         /*
2290          *  Initialize the original IRP's status to success.
2291          *  If any of the packets fail, they will set it to an error status.
2292          *  The IoStatus.Information field will be incremented to the
2293          *  transfer length as the pieces complete.
2294          */
2295         Irp->IoStatus.Status = STATUS_SUCCESS;
2296         Irp->IoStatus.Information = 0;
2297 
2298         /*
2299          *  Store the number of transfer pieces inside the original IRP.
2300          *  It will be used to count down the pieces as they complete.
2301          */
2302         Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
2303 
2304         /*
2305          *  We are proceeding with the transfer.
2306          *  Mark the client IRP pending since it may complete on a different thread.
2307          */
2308         IoMarkIrpPending(Irp);
2309 
2310         /*
2311          *  Transmit the pieces of the transfer.
2312          */
2313         while (entireXferLen > 0){
2314             ULONG thisPieceLen = MIN(fdoData->HwMaxXferLen, entireXferLen);
2315 
2316             /*
2317              *  Set up a TRANSFER_PACKET for this piece and send it.
2318              */
2319             slistEntry = SimplePopSlist(&pktList);
2320             ASSERT(slistEntry);
2321             pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2322             SetupReadWriteTransferPacket(   pkt,
2323                                         bufPtr,
2324                                         thisPieceLen,
2325                                         targetLocation,
2326                                         Irp);
2327             SubmitTransferPacket(pkt);
2328 
2329             entireXferLen -= thisPieceLen;
2330             bufPtr += thisPieceLen;
2331             targetLocation.QuadPart += thisPieceLen;
2332         }
2333         ASSERT(SimpleIsSlistEmpty(&pktList));
2334     }
2335     else if (i >= 1){
2336         /*
2337          *  We were unable to get all the TRANSFER_PACKETs we need,
2338          *  but we did get at least one.
2339          *  That means that we are in extreme low-memory stress.
2340          *  We'll try doing this transfer using a single packet.
2341          *  The port driver is certainly also in stress, so use one-page
2342          *  transfers.
2343          */
2344 
2345         /*
2346          *  Free all but one of the TRANSFER_PACKETs.
2347          */
2348         while (i-- > 1){
2349             slistEntry = SimplePopSlist(&pktList);
2350             ASSERT(slistEntry);
2351             pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2352             EnqueueFreeTransferPacket(Fdo, pkt);
2353         }
2354 
2355         /*
2356          *  Get the single TRANSFER_PACKET that we'll be using.
2357          */
2358         slistEntry = SimplePopSlist(&pktList);
2359         ASSERT(slistEntry);
2360         ASSERT(SimpleIsSlistEmpty(&pktList));
2361         pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
2362         DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt));
2363 
2364         /*
2365          *  Set default status and the number of transfer packets (one)
2366          *  inside the original irp.
2367          */
2368         Irp->IoStatus.Status = STATUS_SUCCESS;
2369         Irp->IoStatus.Information = 0;
2370         Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
2371 
2372         /*
2373          *  Mark the client irp pending since it may complete on
2374          *  another thread.
2375          */
2376         IoMarkIrpPending(Irp);
2377 
2378         /*
2379          *  Set up the TRANSFER_PACKET for a lowMem transfer and launch.
2380          */
2381         SetupReadWriteTransferPacket(  pkt,
2382                                     bufPtr,
2383                                     entireXferLen,
2384                                     targetLocation,
2385                                     Irp);
2386         InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
2387         StepLowMemRetry(pkt);
2388     }
2389     else {
2390         /*
2391          *  We were unable to get ANY TRANSFER_PACKETs.
2392          *  Defer this client irp until some TRANSFER_PACKETs free up.
2393          */
2394         DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp));
2395         IoMarkIrpPending(Irp);
2396         EnqueueDeferredClientIrp(fdoData, Irp);
2397     }
2398 
2399 }
2400 
2401 /*++////////////////////////////////////////////////////////////////////////////
2402 
2403 ClassIoComplete()
2404 
2405 Routine Description:
2406 
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.
2412 
2413     This routine should only be placed on the stack location for a class
2414     driver FDO.
2415 
2416 Arguments:
2417 
2418     Fdo - Supplies the device object which represents the logical
2419         unit.
2420 
2421     Irp - Supplies the Irp which has completed.
2422 
2423     Context - Supplies a pointer to the SRB.
2424 
2425 Return Value:
2426 
2427     NT status
2428 
2429 --*/
2430 NTSTATUS
2431 NTAPI
2432 ClassIoComplete(
2433     IN PDEVICE_OBJECT Fdo,
2434     IN PIRP Irp,
2435     IN PVOID Context
2436     )
2437 {
2438     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
2439     PSCSI_REQUEST_BLOCK srb = Context;
2440     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2441     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
2442     NTSTATUS status;
2443     BOOLEAN retry;
2444     BOOLEAN callStartNextPacket;
2445 
2446     ASSERT(fdoExtension->CommonExtension.IsFdo);
2447 
2448     //
2449     // Check SRB status for success of completing request.
2450     //
2451 
2452     if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
2453         ULONG retryInterval;
2454 
2455         DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
2456 
2457         //
2458         // Release the queue if it is frozen.
2459         //
2460 
2461         if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
2462             ClassReleaseQueue(Fdo);
2463         }
2464 
2465         retry = ClassInterpretSenseInfo(
2466                     Fdo,
2467                     srb,
2468                     irpStack->MajorFunction,
2469                     irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
2470                      irpStack->Parameters.DeviceIoControl.IoControlCode :
2471                      0,
2472                     MAXIMUM_RETRIES -
2473                         ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
2474                     &status,
2475                     &retryInterval);
2476 
2477         //
2478         // If the status is verified required and the this request
2479         // should bypass verify required then retry the request.
2480         //
2481 
2482         if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
2483             status == STATUS_VERIFY_REQUIRED) {
2484 
2485             status = STATUS_IO_DEVICE_ERROR;
2486             retry = TRUE;
2487         }
2488 
2489         if (retry && ((*(PCHAR*)&irpStack->Parameters.Others.Argument4)--)) {
2490 
2491             //
2492             // Retry request.
2493             //
2494 
2495             DebugPrint((1, "Retry request %p\n", Irp));
2496 
2497             if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2498                 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2499             }
2500 
2501             RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
2502             return STATUS_MORE_PROCESSING_REQUIRED;
2503         }
2504 
2505     } else {
2506 
2507         //
2508         // Set status for successful request
2509         //
2510         fdoData->LoggedTURFailureSinceLastIO = FALSE;
2511         ClasspPerfIncrementSuccessfulIo(fdoExtension);
2512         status = STATUS_SUCCESS;
2513     } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
2514 
2515 
2516     //
2517     // ensure we have returned some info, and it matches what the
2518     // original request wanted for PAGING operations only
2519     //
2520 
2521     if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
2522         ASSERT(Irp->IoStatus.Information != 0);
2523         ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
2524     }
2525 
2526     //
2527     // remember if the caller wanted to skip calling IoStartNextPacket.
2528     // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
2529     // calls.  this setting only affects device objects with StartIo routines.
2530     //
2531 
2532     callStartNextPacket = !TEST_FLAG(srb->SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
2533     if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
2534         callStartNextPacket = FALSE;
2535     }
2536 
2537     //
2538     // Free the srb
2539     //
2540 
2541     if(!TEST_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
2542 
2543         if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2544             FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
2545         }
2546 
2547         if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
2548             ClassFreeOrReuseSrb(fdoExtension, srb);
2549         }
2550         else {
2551             DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
2552             ExFreePool(srb);
2553         }
2554 
2555     } else {
2556 
2557         DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
2558                     "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
2559         if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
2560             DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
2561                         " because SRB_CLASS_FLAGS_PERSISTANT set\n",
2562                         srb->SenseInfoBuffer));
2563         }
2564 
2565     }
2566 
2567     //
2568     // Set status in completing IRP.
2569     //
2570 
2571     Irp->IoStatus.Status = status;
2572 
2573     //
2574     // Set the hard error if necessary.
2575     //
2576 
2577     if (!NT_SUCCESS(status) &&
2578         IoIsErrorUserInduced(status) &&
2579         (Irp->Tail.Overlay.Thread != NULL)
2580         ) {
2581 
2582         //
2583         // Store DeviceObject for filesystem, and clear
2584         // in IoStatus.Information field.
2585         //
2586 
2587         IoSetHardErrorOrVerifyDevice(Irp, Fdo);
2588         Irp->IoStatus.Information = 0;
2589     }
2590 
2591     //
2592     // If pending has be returned for this irp then mark the current stack as
2593     // pending.
2594     //
2595 
2596     if (Irp->PendingReturned) {
2597         IoMarkIrpPending(Irp);
2598     }
2599 
2600     if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
2601         if (callStartNextPacket) {
2602             KIRQL oldIrql;
2603             KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
2604             IoStartNextPacket(Fdo, FALSE);
2605             KeLowerIrql(oldIrql);
2606         }
2607     }
2608 
2609     ClassReleaseRemoveLock(Fdo, Irp);
2610 
2611     return status;
2612 
2613 } // end ClassIoComplete()
2614 
2615 /*++////////////////////////////////////////////////////////////////////////////
2616 
2617 ClassSendSrbSynchronous()
2618 
2619 Routine Description:
2620 
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.
2625 
2626 Arguments:
2627 
2628     Fdo - Supplies the functional device object which represents the target.
2629 
2630     Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
2631 
2632     BufferAddress - Supplies the address of the buffer.
2633 
2634     BufferLength - Supplies the length in bytes of the buffer.
2635 
2636     WriteToDevice - Indicates the data should be transfer to the device.
2637 
2638 Return Value:
2639 
2640     NTSTATUS indicating the final results of the operation.
2641 
2642     If NT_SUCCESS(), then the amount of usable data is contained in the field
2643        Srb->DataTransferLength
2644 
2645 --*/
2646 NTSTATUS
2647 NTAPI
2648 ClassSendSrbSynchronous(
2649     PDEVICE_OBJECT Fdo,
2650     PSCSI_REQUEST_BLOCK Srb,
2651     PVOID BufferAddress,
2652     ULONG BufferLength,
2653     BOOLEAN WriteToDevice
2654     )
2655 {
2656 
2657     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
2658     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
2659     IO_STATUS_BLOCK ioStatus;
2660     //ULONG controlType;
2661     PIRP irp;
2662     PIO_STACK_LOCATION irpStack;
2663     KEVENT event;
2664     PUCHAR senseInfoBuffer;
2665     ULONG retryCount = MAXIMUM_RETRIES;
2666     NTSTATUS status;
2667     BOOLEAN retry;
2668 
2669     //
2670     // NOTE: This code is only pageable because we are not freezing
2671     //       the queue.  Allowing the queue to be frozen from a pageable
2672     //       routine could leave the queue frozen as we try to page in
2673     //       the code to unfreeze the queue.  The result would be a nice
2674     //       case of deadlock.  Therefore, since we are unfreezing the
2675     //       queue regardless of the result, just set the NO_FREEZE_QUEUE
2676     //       flag in the SRB.
2677     //
2678 
2679     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
2680     ASSERT(fdoExtension->CommonExtension.IsFdo);
2681 
2682     //
2683     // Write length to SRB.
2684     //
2685 
2686     Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
2687 
2688     //
2689     // Set SCSI bus address.
2690     //
2691 
2692     Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2693 
2694     //
2695     // Enable auto request sense.
2696     //
2697 
2698     Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
2699 
2700     //
2701     // Sense buffer is in aligned nonpaged pool.
2702     //
2703         //
2704     senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
2705                                      SENSE_BUFFER_SIZE,
2706                                      '7CcS');
2707 
2708     if (senseInfoBuffer == NULL) {
2709 
2710         DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
2711                        "buffer\n"));
2712         return(STATUS_INSUFFICIENT_RESOURCES);
2713     }
2714 
2715     Srb->SenseInfoBuffer = senseInfoBuffer;
2716     Srb->DataBuffer = BufferAddress;
2717 
2718     //
2719     // Start retries here.
2720     //
2721 
2722 retry:
2723 
2724     //
2725     // use fdoextension's flags by default.
2726     // do not move out of loop, as the flag may change due to errors
2727     // sending this command.
2728     //
2729 
2730     Srb->SrbFlags = fdoExtension->SrbFlags;
2731 
2732     if(BufferAddress != NULL) {
2733         if(WriteToDevice) {
2734             SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
2735         } else {
2736             SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
2737         }
2738     }
2739 
2740     //
2741     // Initialize the QueueAction field.
2742     //
2743 
2744     Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
2745 
2746     //
2747     // Disable synchronous transfer for these requests.
2748     // Disable freezing the queue, since all we do is unfreeze it anyways.
2749     //
2750 
2751     SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
2752     SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
2753 
2754     //
2755     // Set the event object to the unsignaled state.
2756     // It will be used to signal request completion.
2757     //
2758 
2759     KeInitializeEvent(&event, NotificationEvent, FALSE);
2760 
2761     //
2762     // Build device I/O control request with METHOD_NEITHER data transfer.
2763     // We'll queue a completion routine to cleanup the MDL's and such ourself.
2764     //
2765 
2766     irp = IoAllocateIrp(
2767             (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
2768             FALSE);
2769 
2770     if(irp == NULL) {
2771         ExFreePool(senseInfoBuffer);
2772         DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
2773         return(STATUS_INSUFFICIENT_RESOURCES);
2774     }
2775 
2776     //
2777     // Get next stack location.
2778     //
2779 
2780     irpStack = IoGetNextIrpStackLocation(irp);
2781 
2782     //
2783     // Set up SRB for execute scsi request. Save SRB address in next stack
2784     // for the port driver.
2785     //
2786 
2787     irpStack->MajorFunction = IRP_MJ_SCSI;
2788     irpStack->Parameters.Scsi.Srb = Srb;
2789 
2790     IoSetCompletionRoutine(irp,
2791                            ClasspSendSynchronousCompletion,
2792                            Srb,
2793                            TRUE,
2794                            TRUE,
2795                            TRUE);
2796 
2797     irp->UserIosb = &ioStatus;
2798     irp->UserEvent = &event;
2799 
2800     if(BufferAddress) {
2801         //
2802         // Build an MDL for the data buffer and stick it into the irp.  The
2803         // completion routine will unlock the pages and free the MDL.
2804         //
2805 
2806         irp->MdlAddress = IoAllocateMdl( BufferAddress,
2807                                          BufferLength,
2808                                          FALSE,
2809                                          FALSE,
2810                                          irp );
2811         if (irp->MdlAddress == NULL) {
2812             ExFreePool(senseInfoBuffer);
2813             Srb->SenseInfoBuffer = NULL;
2814             IoFreeIrp( irp );
2815             DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
2816             return STATUS_INSUFFICIENT_RESOURCES;
2817         }
2818 
2819         _SEH2_TRY {
2820 
2821             //
2822             // the io manager unlocks these pages upon completion
2823             //
2824 
2825             MmProbeAndLockPages( irp->MdlAddress,
2826                                  KernelMode,
2827                                  (WriteToDevice ? IoReadAccess :
2828                                                   IoWriteAccess));
2829 
2830         } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2831             status = _SEH2_GetExceptionCode();
2832 
2833             ExFreePool(senseInfoBuffer);
2834             Srb->SenseInfoBuffer = NULL;
2835             IoFreeMdl(irp->MdlAddress);
2836             IoFreeIrp(irp);
2837 
2838             DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
2839                            "locking buffer\n", status));
2840             _SEH2_YIELD(return status);
2841         } _SEH2_END;
2842     }
2843 
2844     //
2845     // Set the transfer length.
2846     //
2847 
2848     Srb->DataTransferLength = BufferLength;
2849 
2850     //
2851     // Zero out status.
2852     //
2853 
2854     Srb->ScsiStatus = Srb->SrbStatus = 0;
2855     Srb->NextSrb = 0;
2856 
2857     //
2858     // Set up IRP Address.
2859     //
2860 
2861     Srb->OriginalRequest = irp;
2862 
2863     //
2864     // Call the port driver with the request and wait for it to complete.
2865     //
2866 
2867     status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
2868 
2869     if (status == STATUS_PENDING) {
2870         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
2871         status = ioStatus.Status;
2872     }
2873 
2874     //
2875     // Check that request completed without error.
2876     //
2877 
2878     if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
2879 
2880         ULONG retryInterval;
2881 
2882         DBGTRACE(ClassDebugWarning, ("ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb, DBGGETSCSIOPSTR(Srb), DBGGETSRBSTATUSSTR(Srb), (ULONG)Srb->SrbStatus, status, DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));
2883 
2884         //
2885         // assert that the queue is not frozen
2886         //
2887 
2888         ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
2889 
2890         //
2891         // Update status and determine if request should be retried.
2892         //
2893 
2894         retry = ClassInterpretSenseInfo(Fdo,
2895                                         Srb,
2896                                         IRP_MJ_SCSI,
2897                                         0,
2898                                         MAXIMUM_RETRIES  - retryCount,
2899                                         &status,
2900                                         &retryInterval);
2901 
2902 
2903         if (retry) {
2904 
2905             if ((status == STATUS_DEVICE_NOT_READY &&
2906                  ((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode ==
2907                                 SCSI_ADSENSE_LUN_NOT_READY) ||
2908                 (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
2909 
2910                 LARGE_INTEGER delay;
2911 
2912                 //
2913                 // Delay for at least 2 seconds.
2914                 //
2915 
2916                 if(retryInterval < 2) {
2917                     retryInterval = 2;
2918                 }
2919 
2920                 delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval);
2921 
2922                 //
2923                 // Stall for a while to let the device become ready
2924                 //
2925 
2926                 KeDelayExecutionThread(KernelMode, FALSE, &delay);
2927 
2928             }
2929 
2930             //
2931             // If retries are not exhausted then retry this operation.
2932             //
2933 
2934             if (retryCount--) {
2935 
2936                 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
2937                     FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
2938                 }
2939 
2940                 goto retry;
2941             }
2942         }
2943 
2944     } else {
2945         fdoData->LoggedTURFailureSinceLastIO = FALSE;
2946         status = STATUS_SUCCESS;
2947     }
2948 
2949     //
2950     // required even though we allocated our own, since the port driver may
2951     // have allocated one also
2952     //
2953 
2954     if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
2955         FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
2956     }
2957 
2958     Srb->SenseInfoBuffer = NULL;
2959     ExFreePool(senseInfoBuffer);
2960 
2961     return status;
2962 }
2963 
2964 /*++////////////////////////////////////////////////////////////////////////////
2965 
2966 ClassInterpretSenseInfo()
2967 
2968 Routine Description:
2969 
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.
2973 
2974 Arguments:
2975 
2976     DeviceObject - Supplies the device object associated with this request.
2977 
2978     Srb - Supplies the scsi request block which failed.
2979 
2980     MajorFunctionCode - Supplies the function code to be used for logging.
2981 
2982     IoDeviceCode - Supplies the device code to be used for logging.
2983 
2984     Status - Returns the status for the request.
2985 
2986 Return Value:
2987 
2988     BOOLEAN TRUE: Drivers should retry this request.
2989             FALSE: Drivers should not retry this request.
2990 
2991 --*/
2992 BOOLEAN
2993 NTAPI
2994 ClassInterpretSenseInfo(
2995     IN PDEVICE_OBJECT Fdo,
2996     IN PSCSI_REQUEST_BLOCK Srb,
2997     IN UCHAR MajorFunctionCode,
2998     IN ULONG IoDeviceCode,
2999     IN ULONG RetryCount,
3000     OUT NTSTATUS *Status,
3001     OUT OPTIONAL ULONG *RetryInterval
3002     )
3003 {
3004     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
3005     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
3006 
3007     PSENSE_DATA       senseBuffer = Srb->SenseInfoBuffer;
3008 
3009     BOOLEAN           retry = TRUE;
3010     BOOLEAN           logError = FALSE;
3011     BOOLEAN           unhandledError = FALSE;
3012     BOOLEAN           incrementErrorCount = FALSE;
3013 
3014     ULONG             badSector = 0;
3015     ULONG             uniqueId = 0;
3016 
3017     NTSTATUS          logStatus;
3018 
3019     ULONG             readSector;
3020     ULONG             index;
3021 
3022     ULONG             retryInterval = 0;
3023     KIRQL oldIrql;
3024 
3025 
3026     logStatus = -1;
3027 
3028     if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
3029 
3030         //
3031         // Log anything remotely incorrect about paging i/o
3032         //
3033 
3034         logError = TRUE;
3035         uniqueId = 301;
3036         logStatus = IO_WARNING_PAGING_FAILURE;
3037     }
3038 
3039     //
3040     // Check that request sense buffer is valid.
3041     //
3042 
3043     ASSERT(fdoExtension->CommonExtension.IsFdo);
3044 
3045 
3046     //
3047     // must handle the SRB_STATUS_INTERNAL_ERROR case first,
3048     // as it has  all the flags set.
3049     //
3050 
3051     if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
3052 
3053         DebugPrint((ClassDebugSenseInfo,
3054                     "ClassInterpretSenseInfo: Internal Error code is %x\n",
3055                     Srb->InternalStatus));
3056 
3057         retry = FALSE;
3058         *Status = Srb->InternalStatus;
3059 
3060     } else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
3061         (Srb->SenseInfoBufferLength >=
3062             offsetof(SENSE_DATA, CommandSpecificInformation))) {
3063 
3064         //
3065         // Zero the additional sense code and additional sense code qualifier
3066         // if they were not returned by the device.
3067         //
3068 
3069         readSector = senseBuffer->AdditionalSenseLength +
3070             offsetof(SENSE_DATA, AdditionalSenseLength);
3071 
3072         if (readSector > Srb->SenseInfoBufferLength) {
3073             readSector = Srb->SenseInfoBufferLength;
3074         }
3075 
3076         if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCode)) {
3077             senseBuffer->AdditionalSenseCode = 0;
3078         }
3079 
3080         if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCodeQualifier)) {
3081             senseBuffer->AdditionalSenseCodeQualifier = 0;
3082         }
3083 
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 "
3095                     "is %x\n",
3096                     senseBuffer->AdditionalSenseCodeQualifier));
3097 
3098 
3099         switch (senseBuffer->SenseKey & 0xf) {
3100 
3101         case SCSI_SENSE_NOT_READY: {
3102 
3103             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3104                         "Device not ready\n"));
3105             *Status = STATUS_DEVICE_NOT_READY;
3106 
3107             switch (senseBuffer->AdditionalSenseCode) {
3108 
3109             case SCSI_ADSENSE_LUN_NOT_READY: {
3110 
3111                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3112                             "Lun not ready\n"));
3113 
3114                 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3115 
3116                 case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
3117                     DEVICE_EVENT_BECOMING_READY notReady;
3118 
3119                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3120                                 "Operation In Progress\n"));
3121                     retryInterval = NOT_READY_RETRY_INTERVAL;
3122 
3123                     RtlZeroMemory(&notReady, 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),
3130                                            &notReady);
3131 
3132                     break;
3133                 }
3134 
3135                 case SCSI_SENSEQ_BECOMING_READY: {
3136                     DEVICE_EVENT_BECOMING_READY notReady;
3137 
3138                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3139                                 "In process of becoming ready\n"));
3140                     retryInterval = NOT_READY_RETRY_INTERVAL;
3141 
3142                     RtlZeroMemory(&notReady, 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),
3149                                            &notReady);
3150                     break;
3151                 }
3152 
3153                 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
3154                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3155                                 "Long write in progress\n"));
3156                     retry = FALSE;
3157                     break;
3158                 }
3159 
3160                 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
3161                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3162                                 "Manual intervention required\n"));
3163                     *Status = STATUS_NO_MEDIA_IN_DEVICE;
3164                     retry = FALSE;
3165                     break;
3166                 }
3167 
3168                 case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
3169                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3170                                 "Format in progress\n"));
3171                     retry = FALSE;
3172                     break;
3173                 }
3174 
3175                 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
3176 
3177                     if(!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
3178                                  CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
3179 
3180                         DebugPrint((ClassDebugSenseInfo,
3181                                     "ClassInterpretSenseInfo: "
3182                                     "not ready, cause unknown\n"));
3183                         /*
3184                         Many non-WHQL certified drives (mostly CD-RW) return
3185                         this when they have no media instead of the obvious
3186                         choice of:
3187 
3188                         SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
3189 
3190                         These drives should not pass WHQL certification due
3191                         to this discrepancy.
3192 
3193                         */
3194                         retry = FALSE;
3195                         break;
3196 
3197                     } else {
3198 
3199                         //
3200                         // Treat this as init command required and fall through.
3201                         //
3202                     }
3203                 }
3204 
3205                 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
3206                 default: {
3207                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3208                                 "Initializing command required\n"));
3209 
3210                     //
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.
3215                     //
3216 
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);
3222                     }
3223                     break;
3224                 }
3225 
3226 
3227                 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
3228                 break;
3229             }
3230 
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;
3235                 retry = FALSE;
3236 
3237                 //
3238                 // signal MCN that there isn't any media in the device
3239                 //
3240                 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
3241                     DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
3242                                 "No Media in a non-removable device %p\n",
3243                                 Fdo));
3244                 }
3245                 ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
3246 
3247                 break;
3248             }
3249             } // end switch (senseBuffer->AdditionalSenseCode)
3250 
3251             break;
3252         } // end SCSI_SENSE_NOT_READY
3253 
3254         case SCSI_SENSE_DATA_PROTECT: {
3255             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3256                         "Media write protected\n"));
3257             *Status = STATUS_MEDIA_WRITE_PROTECTED;
3258             retry = FALSE;
3259             break;
3260         } // end SCSI_SENSE_DATA_PROTECT
3261 
3262         case SCSI_SENSE_MEDIUM_ERROR: {
3263             DebugPrint((ClassDebugSenseInfo,"ClassInterpretSenseInfo: "
3264                         "Medium Error (bad block)\n"));
3265             *Status = STATUS_DEVICE_DATA_ERROR;
3266 
3267             retry = FALSE;
3268             logError = TRUE;
3269             uniqueId = 256;
3270             logStatus = IO_ERR_BAD_BLOCK;
3271 
3272             //
3273             // Check if this error is due to unknown format
3274             //
3275             if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA){
3276 
3277                 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3278 
3279                 case SCSI_SENSEQ_UNKNOWN_FORMAT: {
3280 
3281                     *Status = STATUS_UNRECOGNIZED_MEDIA;
3282 
3283                     //
3284                     // Log error only if this is a paging request
3285                     //
3286                     if(!TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
3287                         logError = FALSE;
3288                     }
3289                     break;
3290                 }
3291 
3292                 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
3293 
3294                     *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
3295                     logError = FALSE;
3296                     break;
3297 
3298                 }
3299                 default: {
3300                     break;
3301                 }
3302                 } // end switch AdditionalSenseCodeQualifier
3303 
3304             } // end SCSI_ADSENSE_INVALID_MEDIA
3305 
3306             break;
3307 
3308         } // end SCSI_SENSE_MEDIUM_ERROR
3309 
3310         case SCSI_SENSE_HARDWARE_ERROR: {
3311             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3312                         "Hardware error\n"));
3313             *Status = STATUS_IO_DEVICE_ERROR;
3314             logError = TRUE;
3315             uniqueId = 257;
3316             logStatus = IO_ERR_CONTROLLER_ERROR;
3317             break;
3318         } // end SCSI_SENSE_HARDWARE_ERROR
3319 
3320         case SCSI_SENSE_ILLEGAL_REQUEST: {
3321 
3322             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3323                         "Illegal SCSI request\n"));
3324             *Status = STATUS_INVALID_DEVICE_REQUEST;
3325             retry = FALSE;
3326 
3327             switch (senseBuffer->AdditionalSenseCode) {
3328 
3329             case SCSI_ADSENSE_ILLEGAL_COMMAND: {
3330                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3331                             "Illegal command\n"));
3332                 break;
3333             }
3334 
3335             case SCSI_ADSENSE_ILLEGAL_BLOCK: {
3336                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3337                             "Illegal block address\n"));
3338                 *Status = STATUS_NONEXISTENT_SECTOR;
3339                 break;
3340             }
3341 
3342             case SCSI_ADSENSE_INVALID_LUN: {
3343                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3344                             "Invalid LUN\n"));
3345                 *Status = STATUS_NO_SUCH_DEVICE;
3346                 break;
3347             }
3348 
3349             case SCSI_ADSENSE_MUSIC_AREA: {
3350                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3351                             "Music area\n"));
3352                 break;
3353             }
3354 
3355             case SCSI_ADSENSE_DATA_AREA: {
3356                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3357                             "Data area\n"));
3358                 break;
3359             }
3360 
3361             case SCSI_ADSENSE_VOLUME_OVERFLOW: {
3362                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3363                             "Volume overflow\n"));
3364                 break;
3365             }
3366 
3367             case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
3368                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3369                             "Copy protection failure\n"));
3370 
3371                 *Status = STATUS_COPY_PROTECTION_FAILURE;
3372 
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;
3379                         break;
3380                     case SCSI_SENSEQ_KEY_NOT_PRESENT:
3381                         DebugPrint((ClassDebugSenseInfo,
3382                                     "ClassInterpretSenseInfo: "
3383                                     "Key not present\n"));
3384                         *Status = STATUS_CSS_KEY_NOT_PRESENT;
3385                         break;
3386                     case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
3387                         DebugPrint((ClassDebugSenseInfo,
3388                                     "ClassInterpretSenseInfo: "
3389                                     "Key not established\n"));
3390                         *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
3391                         break;
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;
3398                         break;
3399                     case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
3400                         DebugPrint((ClassDebugSenseInfo,
3401                                     "ClassInterpretSenseInfo: "
3402                                     "Media region does not logical unit "
3403                                     "region\n"));
3404                         *Status = STATUS_CSS_REGION_MISMATCH;
3405                         break;
3406                     case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
3407                         DebugPrint((ClassDebugSenseInfo,
3408                                     "ClassInterpretSenseInfo: "
3409                                     "Region set error -- region may "
3410                                     "be permanent\n"));
3411                         *Status = STATUS_CSS_RESETS_EXHAUSTED;
3412                         break;
3413                 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
3414 
3415                 break;
3416             }
3417 
3418 
3419             case SCSI_ADSENSE_INVALID_CDB: {
3420                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3421                             "Invalid CDB\n"));
3422 
3423                 //
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)
3430                 //
3431                 // hopefully, this shortcoming can be changed for
3432                 // blackcomb.
3433                 //
3434 
3435                 retryInterval = 3;
3436                 break;
3437             }
3438 
3439             } // end switch (senseBuffer->AdditionalSenseCode)
3440 
3441             break;
3442         } // end SCSI_SENSE_ILLEGAL_REQUEST
3443 
3444         case SCSI_SENSE_UNIT_ATTENTION: {
3445 
3446             //PVPB vpb;
3447             ULONG count;
3448 
3449             //
3450             // A media change may have occured so increment the change
3451             // count for the physical device
3452             //
3453 
3454             count = InterlockedIncrement((PLONG)&fdoExtension->MediaChangeCount);
3455             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3456                         "Media change count for device %d incremented to %#lx\n",
3457                         fdoExtension->DeviceNumber, count));
3458 
3459 
3460             switch (senseBuffer->AdditionalSenseCode) {
3461             case SCSI_ADSENSE_MEDIUM_CHANGED: {
3462                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3463                             "Media changed\n"));
3464 
3465                 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
3466                     DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
3467                                 "Media Changed on non-removable device %p\n",
3468                                 Fdo));
3469                 }
3470                 ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
3471                 break;
3472             }
3473 
3474             case SCSI_ADSENSE_BUS_RESET: {
3475                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3476                             "Bus reset\n"));
3477                 break;
3478             }
3479 
3480             case SCSI_ADSENSE_OPERATOR_REQUEST: {
3481                 switch (senseBuffer->AdditionalSenseCodeQualifier) {
3482 
3483                 case SCSI_SENSEQ_MEDIUM_REMOVAL: {
3484                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3485                                 "Ejection request received!\n"));
3486                     ClassSendEjectionNotification(fdoExtension);
3487                     break;
3488                 }
3489 
3490                 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
3491                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3492                                 "Operator selected write permit?! "
3493                                 "(unsupported!)\n"));
3494                     break;
3495                 }
3496 
3497                 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
3498                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3499                                 "Operator selected write protect?! "
3500                                 "(unsupported!)\n"));
3501                     break;
3502                 }
3503 
3504                 }
3505                 break;
3506             }
3507 
3508             default: {
3509                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3510                             "Unit attention\n"));
3511                 break;
3512             }
3513 
3514             } // end  switch (senseBuffer->AdditionalSenseCode)
3515 
3516             if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
3517             {
3518                 //
3519                 // TODO : Is the media lockable?
3520                 //
3521 
3522                 if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
3523                 {
3524                     //
3525                     // Set bit to indicate that media may have changed
3526                     // and volume needs verification.
3527                     //
3528 
3529                     SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
3530 
3531                     *Status = STATUS_VERIFY_REQUIRED;
3532                     retry = FALSE;
3533                 }
3534             }
3535             else
3536             {
3537                 *Status = STATUS_IO_DEVICE_ERROR;
3538             }
3539 
3540             break;
3541 
3542         } // end SCSI_SENSE_UNIT_ATTENTION
3543 
3544         case SCSI_SENSE_ABORTED_COMMAND: {
3545             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3546                         "Command aborted\n"));
3547             *Status = STATUS_IO_DEVICE_ERROR;
3548             retryInterval = 1;
3549             break;
3550         } // end SCSI_SENSE_ABORTED_COMMAND
3551 
3552         case SCSI_SENSE_BLANK_CHECK: {
3553             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3554                         "Media blank check\n"));
3555             retry = FALSE;
3556             *Status = STATUS_NO_DATA_DETECTED;
3557             break;
3558         } // end SCSI_SENSE_BLANK_CHECK
3559 
3560         case SCSI_SENSE_RECOVERED_ERROR: {
3561 
3562             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3563                         "Recovered error\n"));
3564             *Status = STATUS_SUCCESS;
3565             retry = FALSE;
3566             logError = TRUE;
3567             uniqueId = 258;
3568 
3569             switch(senseBuffer->AdditionalSenseCode) {
3570             case SCSI_ADSENSE_SEEK_ERROR:
3571             case SCSI_ADSENSE_TRACK_ERROR: {
3572                 logStatus = IO_ERR_SEEK_ERROR;
3573                 break;
3574             }
3575 
3576             case SCSI_ADSENSE_REC_DATA_NOECC:
3577             case SCSI_ADSENSE_REC_DATA_ECC: {
3578                 logStatus = IO_RECOVERED_VIA_ECC;
3579                 break;
3580             }
3581 
3582             case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
3583                 UCHAR wmiEventData[5];
3584 
3585                 *((PULONG)wmiEventData) = sizeof(UCHAR);
3586                 wmiEventData[sizeof(ULONG)] = senseBuffer->AdditionalSenseCodeQualifier;
3587 
3588                 //
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.
3595                 //
3596 
3597                 if (fdoExtension->FailurePredicted == 0) {
3598                     logError = TRUE;
3599                 }
3600                 fdoExtension->FailurePredicted = TRUE;
3601                 fdoExtension->FailureReason = senseBuffer->AdditionalSenseCodeQualifier;
3602                 logStatus = IO_WRN_FAILURE_PREDICTED;
3603 
3604                 ClassNotifyFailurePredicted(fdoExtension,
3605                                             (PUCHAR)&wmiEventData,
3606                                             sizeof(wmiEventData),
3607                                             0,
3608                                             4,
3609                                             Srb->PathId,
3610                                             Srb->TargetId,
3611                                             Srb->Lun);
3612                 break;
3613             }
3614 
3615             default: {
3616                 logStatus = IO_ERR_CONTROLLER_ERROR;
3617                 break;
3618             }
3619 
3620             } // end switch(senseBuffer->AdditionalSenseCode)
3621 
3622             if (senseBuffer->IncorrectLength) {
3623 
3624                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3625                             "Incorrect length detected.\n"));
3626                 *Status = STATUS_INVALID_BLOCK_LENGTH ;
3627             }
3628 
3629             break;
3630         } // end SCSI_SENSE_RECOVERED_ERROR
3631 
3632         case SCSI_SENSE_NO_SENSE: {
3633 
3634             //
3635             // Check other indicators.
3636             //
3637 
3638             if (senseBuffer->IncorrectLength) {
3639 
3640                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3641                             "Incorrect length detected.\n"));
3642                 *Status = STATUS_INVALID_BLOCK_LENGTH ;
3643                 retry   = FALSE;
3644 
3645             } else {
3646 
3647                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3648                             "No specific sense key\n"));
3649                 *Status = STATUS_IO_DEVICE_ERROR;
3650                 retry   = TRUE;
3651             }
3652 
3653             break;
3654         } // end SCSI_SENSE_NO_SENSE
3655 
3656         default: {
3657             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3658                         "Unrecognized sense code\n"));
3659             *Status = STATUS_IO_DEVICE_ERROR;
3660             break;
3661         }
3662 
3663         } // end switch (senseBuffer->SenseKey & 0xf)
3664 
3665         //
3666         // Try to determine the bad sector from the inquiry data.
3667         //
3668 
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)) {
3672 
3673             for (index = 0; index < 4; index++) {
3674                 badSector = (badSector << 8) | senseBuffer->Information[index];
3675             }
3676 
3677             readSector = 0;
3678             for (index = 0; index < 4; index++) {
3679                 readSector = (readSector << 8) | Srb->Cdb[index+2];
3680             }
3681 
3682             index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
3683                 ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
3684 
3685             //
3686             // Make sure the bad sector is within the read sectors.
3687             //
3688 
3689             if (!(badSector >= readSector && badSector < readSector + index)) {
3690                 badSector = readSector;
3691             }
3692         }
3693 
3694     } else {
3695 
3696         //
3697         // Request sense buffer not valid. No sense information
3698         // to pinpoint the error. Return general request fail.
3699         //
3700 
3701         DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
3702                     "Request sense info not valid. SrbStatus %2x\n",
3703                     SRB_STATUS(Srb->SrbStatus)));
3704         retry = TRUE;
3705 
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;
3713             retry = FALSE;
3714             break;
3715         }
3716 
3717         case SRB_STATUS_COMMAND_TIMEOUT:
3718         case SRB_STATUS_TIMEOUT: {
3719 
3720             //
3721             // Update the error count for the device.
3722             //
3723 
3724             incrementErrorCount = TRUE;
3725             *Status = STATUS_IO_TIMEOUT;
3726             break;
3727         }
3728 
3729         case SRB_STATUS_ABORTED: {
3730 
3731             //
3732             // Update the error count for the device.
3733             //
3734 
3735             incrementErrorCount = TRUE;
3736             *Status = STATUS_IO_TIMEOUT;
3737             retryInterval = 1;
3738             break;
3739         }
3740 
3741 
3742         case SRB_STATUS_SELECTION_TIMEOUT: {
3743             logError = TRUE;
3744             logStatus = IO_ERR_NOT_READY;
3745             uniqueId = 260;
3746             *Status = STATUS_DEVICE_NOT_CONNECTED;
3747             retry = FALSE;
3748             break;
3749         }
3750 
3751         case SRB_STATUS_DATA_OVERRUN: {
3752             *Status = STATUS_DATA_OVERRUN;
3753             retry = FALSE;
3754             break;
3755         }
3756 
3757         case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
3758 
3759             //
3760             // Update the error count for the device.
3761             //
3762 
3763             incrementErrorCount = TRUE;
3764             *Status = STATUS_IO_DEVICE_ERROR;
3765 
3766             //
3767             // If there was  phase sequence error then limit the number of
3768             // retries.
3769             //
3770 
3771             if (RetryCount > 1 ) {
3772                 retry = FALSE;
3773             }
3774 
3775             break;
3776         }
3777 
3778         case SRB_STATUS_REQUEST_FLUSHED: {
3779 
3780             //
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.
3784             //
3785 
3786             if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
3787 
3788                 *Status = STATUS_VERIFY_REQUIRED;
3789                 retry = FALSE;
3790 
3791             } else {
3792                 *Status = STATUS_IO_DEVICE_ERROR;
3793             }
3794 
3795             break;
3796         }
3797 
3798         case SRB_STATUS_INVALID_REQUEST: {
3799             *Status = STATUS_INVALID_DEVICE_REQUEST;
3800             retry = FALSE;
3801             break;
3802         }
3803 
3804         case SRB_STATUS_UNEXPECTED_BUS_FREE:
3805         case SRB_STATUS_PARITY_ERROR:
3806 
3807             //
3808             // Update the error count for the device
3809             // and fall through to below
3810             //
3811 
3812             incrementErrorCount = TRUE;
3813 
3814         case SRB_STATUS_BUS_RESET: {
3815             *Status = STATUS_IO_DEVICE_ERROR;
3816             break;
3817         }
3818 
3819         case SRB_STATUS_ERROR: {
3820 
3821             *Status = STATUS_IO_DEVICE_ERROR;
3822             if (Srb->ScsiStatus == 0) {
3823 
3824                 //
3825                 // This is some strange return code.  Update the error
3826                 // count for the device.
3827                 //
3828 
3829                 incrementErrorCount = TRUE;
3830 
3831             } if (Srb->ScsiStatus == SCSISTAT_BUSY) {
3832 
3833                 *Status = STATUS_DEVICE_NOT_READY;
3834 
3835             } if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) {
3836 
3837                 *Status = STATUS_DEVICE_BUSY;
3838                 retry = FALSE;
3839                 logError = FALSE;
3840 
3841             }
3842 
3843             break;
3844         }
3845 
3846         default: {
3847             logError = TRUE;
3848             logStatus = IO_ERR_CONTROLLER_ERROR;
3849             uniqueId = 259;
3850             *Status = STATUS_IO_DEVICE_ERROR;
3851             unhandledError = TRUE;
3852             break;
3853         }
3854 
3855         }
3856 
3857         //
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.
3861         //
3862         /*
3863         if (fdoExtension->MediaChangeDetectionInfo &&
3864             fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
3865             TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
3866                       NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
3867             ) {
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.
3871         }
3872         */
3873 
3874     }
3875 
3876     if (incrementErrorCount) {
3877 
3878         //
3879         // if any error count occurred, delay the retry of this io by
3880         // at least one second, if caller supports it.
3881         //
3882 
3883         if (retryInterval == 0) {
3884             retryInterval = 1;
3885         }
3886         ClasspPerfIncrementErrorCount(fdoExtension);
3887     }
3888 
3889     //
3890     // If there is a class specific error handler call it.
3891     //
3892 
3893     if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
3894 
3895         fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
3896                                                           Srb,
3897                                                           Status,
3898                                                           &retry);
3899     }
3900 
3901     //
3902     // If the caller wants to know the suggested retry interval tell them.
3903     //
3904 
3905     if(ARGUMENT_PRESENT(RetryInterval)) {
3906         *RetryInterval = retryInterval;
3907     }
3908 
3909 
3910     /*
3911      *  LOG the error:
3912      *      Always log the error in our internal log.
3913      *      If logError is set, also log the error in the system log.
3914      */
3915     {
3916         ULONG totalSize;
3917         ULONG senseBufferSize = 0;
3918         IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
3919         CLASS_ERROR_LOG_DATA staticErrLogData = { { { 0 } } };
3920 
3921         //
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.
3927         //
3928         totalSize = sizeof(IO_ERROR_LOG_PACKET)  // required
3929                   - sizeof(ULONG)                // struct includes one ULONG
3930                   + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
3931 
3932         //
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.
3936         //
3937         if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
3938             ULONG validSenseBytes;
3939             BOOLEAN validSense;
3940 
3941             //
3942             // make sure we can at least access the AdditionalSenseLength field
3943             //
3944             validSense = RTL_CONTAINS_FIELD(senseBuffer,
3945                                             Srb->SenseInfoBufferLength,
3946                                             AdditionalSenseLength);
3947             if (validSense) {
3948 
3949                 //
3950                 // if extra info exists, copy the maximum amount of available
3951                 // sense data that is safe into the the errlog.
3952                 //
3953                 validSenseBytes = senseBuffer->AdditionalSenseLength
3954                                 + offsetof(SENSE_DATA, AdditionalSenseLength);
3955 
3956                 //
3957                 // this is invalid because it causes overflow!
3958                 // whoever sent this type of request would cause
3959                 // a system crash.
3960                 //
3961                 ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
3962 
3963                 //
3964                 // set to save the most sense buffer possible
3965                 //
3966                 senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
3967                 senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
3968             } else {
3969                 //
3970                 // it's smaller than required to read the total number of
3971                 // valid bytes, so just use the SenseInfoBufferLength field.
3972                 //
3973                 senseBufferSize = Srb->SenseInfoBufferLength;
3974             }
3975 
3976             /*
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.
3980              */
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;
3986                 }
3987             }
3988         }
3989 
3990         //
3991         // If we've used up all of our retry attempts, set the final status to
3992         // reflect the appropriate result.
3993         //
3994         if (retry && RetryCount < MAXIMUM_RETRIES) {
3995             staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
3996             staticErrLogData.ErrorRetried = TRUE;
3997         } else {
3998             staticErrLogEntry.FinalStatus = *Status;
3999         }
4000         if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
4001             staticErrLogData.ErrorPaging = TRUE;
4002         }
4003         if (unhandledError) {
4004             staticErrLogData.ErrorUnhandled = TRUE;
4005         }
4006 
4007         //
4008         // Calculate the device offset if there is a geometry.
4009         //
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;
4014         } else {
4015             staticErrLogEntry.ErrorCode = logStatus;
4016         }
4017 
4018         /*
4019          *  The dump data follows the IO_ERROR_LOG_PACKET,
4020          *  with the first ULONG of dump data inside the packet.
4021          */
4022         staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG);
4023 
4024         staticErrLogEntry.SequenceNumber = 0;
4025         staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
4026         staticErrLogEntry.IoControlCode = IoDeviceCode;
4027         staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
4028         staticErrLogEntry.UniqueErrorValue = uniqueId;
4029 
4030         KeQueryTickCount(&staticErrLogData.TickCount);
4031         staticErrLogData.PortNumber = (ULONG)-1;
4032 
4033         /*
4034          *  Save the entire contents of the SRB.
4035          */
4036         staticErrLogData.Srb = *Srb;
4037 
4038         /*
4039          *  For our private log, save just the default length of the SENSE_DATA.
4040          */
4041         if (senseBufferSize != 0){
4042             RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
4043         }
4044 
4045         /*
4046          *  Save the error log in our context.
4047          *  We only save the default sense buffer length.
4048          */
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);
4054 
4055         /*
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).
4059          */
4060         if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_TEST_UNIT_READY) && logError){
4061             if (fdoData->LoggedTURFailureSinceLastIO){
4062                 logError = FALSE;
4063             }
4064             else {
4065                 fdoData->LoggedTURFailureSinceLastIO = TRUE;
4066             }
4067         }
4068         if (logError){
4069             PIO_ERROR_LOG_PACKET errorLogEntry;
4070             PCLASS_ERROR_LOG_DATA errlogData;
4071 
4072             errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
4073             if (errorLogEntry){
4074                 errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
4075 
4076                 *errorLogEntry = staticErrLogEntry;
4077                 *errlogData = staticErrLogData;
4078 
4079                 /*
4080                  *  For the system log, copy as much of the sense buffer as possible.
4081                  */
4082                 if (senseBufferSize != 0) {
4083                     RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
4084                 }
4085 
4086                 /*
4087                  *  Write the error log packet to the system error logging thread.
4088                  */
4089                 IoWriteErrorLogEntry(errorLogEntry);
4090             }
4091         }
4092     }
4093 
4094     return retry;
4095 
4096 } // end ClassInterpretSenseInfo()
4097 
4098 /*++////////////////////////////////////////////////////////////////////////////
4099 
4100 ClassModeSense()
4101 
4102 Routine Description:
4103 
4104     This routine sends a mode sense command to a target ID and returns
4105     when it is complete.
4106 
4107 Arguments:
4108 
4109     Fdo - Supplies the functional device object associated with this request.
4110 
4111     ModeSenseBuffer - Supplies a buffer to store the sense data.
4112 
4113     Length - Supplies the length in bytes of the mode sense buffer.
4114 
4115     PageMode - Supplies the page or pages of mode sense data to be retrieved.
4116 
4117 Return Value:
4118 
4119     Length of the transferred data is returned.
4120 
4121 --*/
4122 ULONG NTAPI ClassModeSense(   IN PDEVICE_OBJECT Fdo,
4123                         IN PCHAR ModeSenseBuffer,
4124                         IN ULONG Length,
4125                         IN UCHAR PageMode)
4126 {
4127     ULONG lengthTransferred = 0;
4128     PMDL senseBufferMdl;
4129 
4130     PAGED_CODE();
4131 
4132     senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
4133     if (senseBufferMdl){
4134 
4135         TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
4136         if (pkt){
4137             KEVENT event;
4138             //NTSTATUS pktStatus;
4139             IRP pseudoIrp = {0};
4140 
4141             /*
4142              *  Store the number of packets servicing the irp (one)
4143              *  inside the original IRP.  It will be used to counted down
4144              *  to zero when the packet completes.
4145              *  Initialize the original IRP's status to success.
4146              *  If the packet fails, we will set it to the error status.
4147              */
4148             pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
4149             pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
4150             pseudoIrp.IoStatus.Information = 0;
4151             pseudoIrp.MdlAddress = senseBufferMdl;
4152 
4153             /*
4154              *  Set this up as a SYNCHRONOUS transfer, submit it,
4155              *  and wait for the packet to complete.  The result
4156              *  status will be written to the original irp.
4157              */
4158             ASSERT(Length <= 0x0ff);
4159             KeInitializeEvent(&event, SynchronizationEvent, FALSE);
4160             SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, &pseudoIrp);
4161             SubmitTransferPacket(pkt);
4162             KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
4163 
4164             if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
4165                 lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
4166             }
4167             else {
4168                 /*
4169                  *  This request can sometimes fail legitimately
4170                  *  (e.g. when a SCSI device is attached but turned off)
4171                  *  so this is not necessarily a device/driver bug.
4172                  */
4173                 DBGTRACE(ClassDebugWarning, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
4174             }
4175         }
4176 
4177         FreeDeviceInputMdl(senseBufferMdl);
4178     }
4179 
4180     return lengthTransferred;
4181 }
4182 
4183 /*++////////////////////////////////////////////////////////////////////////////
4184 
4185 ClassFindModePage()
4186 
4187 Routine Description:
4188 
4189     This routine scans through the mode sense data and finds the requested
4190     mode sense page code.
4191 
4192 Arguments:
4193     ModeSenseBuffer - Supplies a pointer to the mode sense data.
4194 
4195     Length - Indicates the length of valid data.
4196 
4197     PageMode - Supplies the page mode to be searched for.
4198 
4199     Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
4200 
4201 Return Value:
4202 
4203     A pointer to the the requested mode page.  If the mode page was not found
4204     then NULL is return.
4205 
4206 --*/
4207 PVOID
4208 NTAPI
4209 ClassFindModePage(
4210     IN PCHAR ModeSenseBuffer,
4211     IN ULONG Length,
4212     IN UCHAR PageMode,
4213     IN BOOLEAN Use6Byte
4214     )
4215 {
4216     PCHAR limit;
4217     ULONG  parameterHeaderLength;
4218     PVOID result = NULL;
4219 
4220     limit = ModeSenseBuffer + Length;
4221     parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
4222 
4223     if (Length >= parameterHeaderLength) {
4224 
4225         PMODE_PARAMETER_HEADER10 modeParam10;
4226         ULONG blockDescriptorLength;
4227 
4228         /*
4229          *  Skip the mode select header and block descriptors.
4230          */
4231         if (Use6Byte){
4232             blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
4233         }
4234         else {
4235             modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
4236             blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
4237         }
4238 
4239         ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
4240 
4241         //
4242         // ModeSenseBuffer now points at pages.  Walk the pages looking for the
4243         // requested page until the limit is reached.
4244         //
4245 
4246         while (ModeSenseBuffer +
4247                RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit) {
4248 
4249             if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
4250 
4251                 /*
4252                  * found the mode page.  make sure it's safe to touch it all
4253                  * before returning the pointer to caller
4254                  */
4255 
4256                 if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit) {
4257                     /*
4258                      *  Return NULL since the page is not safe to access in full
4259                      */
4260                     result = NULL;
4261                 }
4262                 else {
4263                     result = ModeSenseBuffer;
4264                 }
4265                 break;
4266             }
4267 
4268             //
4269             // Advance to the next page which is 4-byte-aligned offset after this page.
4270             //
4271             ModeSenseBuffer +=
4272                 ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
4273                 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
4274 
4275         }
4276     }
4277 
4278     return result;
4279 } // end ClassFindModePage()
4280 
4281 /*++////////////////////////////////////////////////////////////////////////////
4282 
4283 ClassSendSrbAsynchronous()
4284 
4285 Routine Description:
4286 
4287     This routine takes a partially built Srb and an Irp and sends it down to
4288     the port driver.
4289 
4290     This routine must be called with the remove lock held for the specified
4291     Irp.
4292 
4293 Arguments:
4294 
4295     Fdo - Supplies the functional device object for the original request.
4296 
4297     Srb - Supplies a partially built ScsiRequestBlock.  In particular, the
4298         CDB and the SRB timeout value must be filled in.  The SRB must not be
4299         allocated from zone.
4300 
4301     Irp - Supplies the requesting Irp.
4302 
4303     BufferAddress - Supplies a pointer to the buffer to be transfered.
4304 
4305     BufferLength - Supplies the length of data transfer.
4306 
4307     WriteToDevice - Indicates the data transfer will be from system memory to
4308         device.
4309 
4310 Return Value:
4311 
4312     Returns STATUS_PENDING if the request is dispatched (since the
4313     completion routine may change the irp's status value we cannot simply
4314     return the value of the dispatch)
4315 
4316     or returns a status value to indicate why it failed.
4317 
4318 --*/
4319 NTSTATUS
4320 NTAPI
4321 ClassSendSrbAsynchronous(
4322     PDEVICE_OBJECT Fdo,
4323     PSCSI_REQUEST_BLOCK Srb,
4324     PIRP Irp,
4325     PVOID BufferAddress,
4326     ULONG BufferLength,
4327     BOOLEAN WriteToDevice
4328     )
4329 {
4330 
4331     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
4332     PIO_STACK_LOCATION irpStack;
4333 
4334     ULONG savedFlags;
4335 
4336     //
4337     // Write length to SRB.
4338     //
4339 
4340     Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
4341 
4342     //
4343     // Set SCSI bus address.
4344     //
4345 
4346     Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
4347 
4348     //
4349     // This is a violation of the SCSI spec but it is required for
4350     // some targets.
4351     //
4352 
4353     // Srb->Cdb[1] |= deviceExtension->Lun << 5;
4354 
4355     //
4356     // Indicate auto request sense by specifying buffer and size.
4357     //
4358 
4359     Srb->SenseInfoBuffer = fdoExtension->SenseData;
4360     Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
4361     Srb->DataBuffer = BufferAddress;
4362 
4363     //
4364     // Save the class driver specific flags away.
4365     //
4366 
4367     savedFlags = Srb->SrbFlags & SRB_FLAGS_CLASS_DRIVER_RESERVED;
4368 
4369     //
4370     // Allow the caller to specify that they do not wish
4371     // IoStartNextPacket() to be called in the completion routine.
4372     //
4373 
4374     SET_FLAG(savedFlags, (Srb->SrbFlags & SRB_FLAGS_DONT_START_NEXT_PACKET));
4375 
4376     if (BufferAddress != NULL) {
4377 
4378         //
4379         // Build Mdl if necessary.
4380         //
4381 
4382         if (Irp->MdlAddress == NULL) {
4383 
4384             if (IoAllocateMdl(BufferAddress,
4385                               BufferLength,
4386                               FALSE,
4387                               FALSE,
4388                               Irp) == NULL) {
4389 
4390                 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
4391 
4392                 //
4393                 // ClassIoComplete() would have free'd the srb
4394                 //
4395 
4396                 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
4397                     FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
4398                 }
4399                 ClassFreeOrReuseSrb(fdoExtension, Srb);
4400                 ClassReleaseRemoveLock(Fdo, Irp);
4401                 ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
4402 
4403                 return STATUS_INSUFFICIENT_RESOURCES;
4404             }
4405 
4406             MmBuildMdlForNonPagedPool(Irp->MdlAddress);
4407 
4408         } else {
4409 
4410             //
4411             // Make sure the buffer requested matches the MDL.
4412             //
4413 
4414             ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
4415         }
4416 
4417         //
4418         // Set read flag.
4419         //
4420 
4421         Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN;
4422 
4423     } else {
4424 
4425         //
4426         // Clear flags.
4427         //
4428 
4429         Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
4430     }
4431 
4432     //
4433     // Restore saved flags.
4434     //
4435 
4436     SET_FLAG(Srb->SrbFlags, savedFlags);
4437 
4438     //
4439     // Disable synchronous transfer for these requests.
4440     //
4441 
4442     SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
4443 
4444     //
4445     // Set the transfer length.
4446     //
4447 
4448     Srb->DataTransferLength = BufferLength;
4449 
4450     //
4451     // Zero out status.
4452     //
4453 
4454     Srb->ScsiStatus = Srb->SrbStatus = 0;
4455 
4456     Srb->NextSrb = 0;
4457 
4458     //
4459     // Save a few parameters in the current stack location.
4460     //
4461 
4462     irpStack = IoGetCurrentIrpStackLocation(Irp);
4463 
4464     //
4465     // Save retry count in current Irp stack.
4466     //
4467 
4468     irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
4469 
4470     //
4471     // Set up IoCompletion routine address.
4472     //
4473 
4474     IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
4475 
4476     //
4477     // Get next stack location and
4478     // set major function code.
4479     //
4480 
4481     irpStack = IoGetNextIrpStackLocation(Irp);
4482 
4483     irpStack->MajorFunction = IRP_MJ_SCSI;
4484 
4485     //
4486     // Save SRB address in next stack for port driver.
4487     //
4488 
4489     irpStack->Parameters.Scsi.Srb = Srb;
4490 
4491     //
4492     // Set up Irp Address.
4493     //
4494 
4495     Srb->OriginalRequest = Irp;
4496 
4497     //
4498     // Call the port driver to process the request.
4499     //
4500 
4501     IoMarkIrpPending(Irp);
4502 
4503     IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
4504 
4505     return STATUS_PENDING;
4506 
4507 } // end ClassSendSrbAsynchronous()
4508 
4509 /*++////////////////////////////////////////////////////////////////////////////
4510 
4511 ClassDeviceControlDispatch()
4512 
4513 Routine Description:
4514 
4515     The routine is the common class driver device control dispatch entry point.
4516     This routine is invokes the device-specific drivers DeviceControl routine,
4517     (which may call the Class driver's common DeviceControl routine).
4518 
4519 Arguments:
4520 
4521     DeviceObject - Supplies a pointer to the device object for this request.
4522 
4523     Irp - Supplies the Irp making the request.
4524 
4525 Return Value:
4526 
4527    Returns the status returned from the device-specific driver.
4528 
4529 --*/
4530 NTSTATUS
4531 NTAPI
4532 ClassDeviceControlDispatch(
4533     PDEVICE_OBJECT DeviceObject,
4534     PIRP Irp
4535     )
4536 {
4537 
4538     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
4539     ULONG isRemoved;
4540 
4541     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
4542 
4543     if(isRemoved) {
4544 
4545         ClassReleaseRemoveLock(DeviceObject, Irp);
4546 
4547         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
4548         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4549         return STATUS_DEVICE_DOES_NOT_EXIST;
4550     }
4551 
4552     //
4553     // Call the class specific driver DeviceControl routine.
4554     // If it doesn't handle it, it will call back into ClassDeviceControl.
4555     //
4556 
4557     ASSERT(commonExtension->DevInfo->ClassDeviceControl);
4558 
4559     return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
4560 } // end ClassDeviceControlDispatch()
4561 
4562 /*++////////////////////////////////////////////////////////////////////////////
4563 
4564 ClassDeviceControl()
4565 
4566 Routine Description:
4567 
4568     The routine is the common class driver device control dispatch function.
4569     This routine is called by a class driver when it get an unrecognized
4570     device control request.  This routine will perform the correct action for
4571     common requests such as lock media.  If the device request is unknown it
4572     passed down to the next level.
4573 
4574     This routine must be called with the remove lock held for the specified
4575     irp.
4576 
4577 Arguments:
4578 
4579     DeviceObject - Supplies a pointer to the device object for this request.
4580 
4581     Irp - Supplies the Irp making the request.
4582 
4583 Return Value:
4584 
4585    Returns back a STATUS_PENDING or a completion status.
4586 
4587 --*/
4588 NTSTATUS
4589 NTAPI
4590 ClassDeviceControl(
4591     PDEVICE_OBJECT DeviceObject,
4592     PIRP Irp
4593     )
4594 {
4595     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
4596 
4597     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
4598     PIO_STACK_LOCATION nextStack = NULL;
4599 
4600     ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
4601 
4602     PSCSI_REQUEST_BLOCK srb = NULL;
4603     PCDB cdb = NULL;
4604 
4605     NTSTATUS status;
4606     ULONG modifiedIoControlCode;
4607 
4608     //
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.
4611     //
4612 
4613     if ((controlCode == IOCTL_SCSI_PASS_THROUGH) ||
4614         (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT)) {
4615 
4616         //PSCSI_PASS_THROUGH scsiPass;
4617 
4618         //
4619         // Validate the user buffer.
4620         //
4621         #if defined (_WIN64)
4622 
4623             if (IoIs32bitProcess(Irp)) {
4624 
4625                 if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
4626 
4627                     Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
4628 
4629                     ClassReleaseRemoveLock(DeviceObject, Irp);
4630                     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4631 
4632                     status = STATUS_INVALID_PARAMETER;
4633                     goto SetStatusAndReturn;
4634                 }
4635             }
4636             else
4637         #endif
4638             {
4639                 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
4640                     sizeof(SCSI_PASS_THROUGH)) {
4641 
4642                     Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
4643 
4644                     ClassReleaseRemoveLock(DeviceObject, Irp);
4645                     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4646 
4647                     status = STATUS_INVALID_PARAMETER;
4648                     goto SetStatusAndReturn;
4649                 }
4650             }
4651 
4652         IoCopyCurrentIrpStackLocationToNext(Irp);
4653 
4654         nextStack = IoGetNextIrpStackLocation(Irp);
4655         nextStack->MinorFunction = 1;
4656 
4657         ClassReleaseRemoveLock(DeviceObject, Irp);
4658 
4659         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4660         goto SetStatusAndReturn;
4661     }
4662 
4663     Irp->IoStatus.Information = 0;
4664 
4665     switch (controlCode) {
4666 
4667         case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
4668 
4669             PMOUNTDEV_UNIQUE_ID uniqueId;
4670 
4671             if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
4672                 status = STATUS_INVALID_PARAMETER;
4673                 break;
4674             }
4675 
4676             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4677                 sizeof(MOUNTDEV_UNIQUE_ID)) {
4678 
4679                 status = STATUS_BUFFER_TOO_SMALL;
4680                 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
4681                 break;
4682             }
4683 
4684             uniqueId = Irp->AssociatedIrp.SystemBuffer;
4685             uniqueId->UniqueIdLength =
4686                     commonExtension->MountedDeviceInterfaceName.Length;
4687 
4688             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4689                 sizeof(USHORT) + uniqueId->UniqueIdLength) {
4690 
4691                 status = STATUS_BUFFER_OVERFLOW;
4692                 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
4693                 break;
4694             }
4695 
4696             RtlCopyMemory(uniqueId->UniqueId,
4697                           commonExtension->MountedDeviceInterfaceName.Buffer,
4698                           uniqueId->UniqueIdLength);
4699 
4700             status = STATUS_SUCCESS;
4701             Irp->IoStatus.Information = sizeof(USHORT) +
4702                                         uniqueId->UniqueIdLength;
4703             break;
4704         }
4705 
4706         case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
4707 
4708             PMOUNTDEV_NAME name;
4709 
4710             ASSERT(commonExtension->DeviceName.Buffer);
4711 
4712             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4713                 sizeof(MOUNTDEV_NAME)) {
4714 
4715                 status = STATUS_BUFFER_TOO_SMALL;
4716                 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4717                 break;
4718             }
4719 
4720             name = Irp->AssociatedIrp.SystemBuffer;
4721             name->NameLength = commonExtension->DeviceName.Length;
4722 
4723             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4724                 sizeof(USHORT) + name->NameLength) {
4725 
4726                 status = STATUS_BUFFER_OVERFLOW;
4727                 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
4728                 break;
4729             }
4730 
4731             RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
4732                           name->NameLength);
4733 
4734             status = STATUS_SUCCESS;
4735             Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
4736             break;
4737         }
4738 
4739         case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
4740 
4741             PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
4742             WCHAR driveLetterNameBuffer[10];
4743             RTL_QUERY_REGISTRY_TABLE queryTable[2];
4744             PWSTR valueName;
4745             UNICODE_STRING driveLetterName;
4746 
4747             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4748                 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
4749 
4750                 status = STATUS_BUFFER_TOO_SMALL;
4751                 Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
4752                 break;
4753             }
4754 
4755             valueName = ExAllocatePoolWithTag(
4756                             PagedPool,
4757                             commonExtension->DeviceName.Length + sizeof(WCHAR),
4758                             '8CcS');
4759 
4760             if (!valueName) {
4761                 status = STATUS_INSUFFICIENT_RESOURCES;
4762                 break;
4763             }
4764 
4765             RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
4766                           commonExtension->DeviceName.Length);
4767             valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
4768 
4769             driveLetterName.Buffer = driveLetterNameBuffer;
4770             driveLetterName.MaximumLength = 20;
4771             driveLetterName.Length = 0;
4772 
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;
4778 
4779             status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
4780                                             L"\\Registry\\Machine\\System\\DISK",
4781                                             queryTable, NULL, NULL);
4782 
4783             if (!NT_SUCCESS(status)) {
4784                 ExFreePool(valueName);
4785                 break;
4786             }
4787 
4788             if (driveLetterName.Length == 4 &&
4789                 driveLetterName.Buffer[0] == '%' &&
4790                 driveLetterName.Buffer[1] == ':') {
4791 
4792                 driveLetterName.Buffer[0] = 0xFF;
4793 
4794             } else if (driveLetterName.Length != 4 ||
4795                 driveLetterName.Buffer[0] < FirstDriveLetter ||
4796                 driveLetterName.Buffer[0] > LastDriveLetter ||
4797                 driveLetterName.Buffer[1] != ':') {
4798 
4799                 status = STATUS_NOT_FOUND;
4800                 ExFreePool(valueName);
4801                 break;
4802             }
4803 
4804             suggestedName = Irp->AssociatedIrp.SystemBuffer;
4805             suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
4806             suggestedName->NameLength = 28;
4807 
4808             Irp->IoStatus.Information =
4809                     FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
4810 
4811             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4812                 Irp->IoStatus.Information) {
4813 
4814                 Irp->IoStatus.Information =
4815                         sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
4816                 status = STATUS_BUFFER_OVERFLOW;
4817                 ExFreePool(valueName);
4818                 break;
4819             }
4820 
4821             RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
4822                                    L"\\Registry\\Machine\\System\\DISK",
4823                                    valueName);
4824 
4825             ExFreePool(valueName);
4826 
4827             RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
4828             suggestedName->Name[12] = driveLetterName.Buffer[0];
4829             suggestedName->Name[13] = ':';
4830 
4831             //
4832             // NT_SUCCESS(status) based on RtlQueryRegistryValues
4833             //
4834             status = STATUS_SUCCESS;
4835 
4836             break;
4837         }
4838 
4839         default:
4840             status = STATUS_PENDING;
4841             break;
4842     }
4843 
4844     if (status != STATUS_PENDING) {
4845         ClassReleaseRemoveLock(DeviceObject, Irp);
4846         Irp->IoStatus.Status = status;
4847         IoCompleteRequest(Irp, IO_NO_INCREMENT);
4848         return status;
4849     }
4850 
4851     if (commonExtension->IsFdo){
4852 
4853         PULONG_PTR function;
4854 
4855         srb = ExAllocatePoolWithTag(NonPagedPool,
4856                              sizeof(SCSI_REQUEST_BLOCK) +
4857                              (sizeof(ULONG_PTR) * 2),
4858                              '9CcS');
4859 
4860         if (srb == NULL) {
4861 
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;
4867         }
4868 
4869         RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
4870 
4871         cdb = (PCDB)srb->Cdb;
4872 
4873         //
4874         // Save the function code and the device object in the memory after
4875         // the SRB.
4876         //
4877 
4878         function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
4879         *function = (ULONG_PTR) DeviceObject;
4880         function++;
4881         *function = (ULONG_PTR) controlCode;
4882 
4883     } else {
4884         srb = NULL;
4885     }
4886 
4887     //
4888     // Change the device type to storage for the switch statement, but only
4889     // if from a legacy device type
4890     //
4891 
4892     if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE  << 16)) ||
4893         ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE  << 16)) ||
4894         ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
4895         ) {
4896 
4897         modifiedIoControlCode = (controlCode & ~0xffff0000);
4898         modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
4899 
4900     } else {
4901 
4902         modifiedIoControlCode = controlCode;
4903 
4904     }
4905 
4906     DBGTRACE(ClassDebugTrace, ("> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
4907 
4908     switch (modifiedIoControlCode) {
4909 
4910     case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
4911 
4912         if (srb) {
4913             ExFreePool(srb);
4914             srb = NULL;
4915         }
4916 
4917         if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
4918            sizeof(STORAGE_HOTPLUG_INFO)) {
4919 
4920             //
4921             // Indicate unsuccessful status and no data transferred.
4922             //
4923 
4924             Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
4925             Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
4926 
4927             ClassReleaseRemoveLock(DeviceObject, Irp);
4928             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4929             status = STATUS_BUFFER_TOO_SMALL;
4930 
4931         } else if(!commonExtension->IsFdo) {
4932 
4933             //
4934             // Just forward this down and return
4935             //
4936 
4937             IoCopyCurrentIrpStackLocationToNext(Irp);
4938 
4939             ClassReleaseRemoveLock(DeviceObject, Irp);
4940             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4941 
4942         } else {
4943 
4944             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
4945             PSTORAGE_HOTPLUG_INFO info;
4946 
4947             fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
4948             info = Irp->AssociatedIrp.SystemBuffer;
4949 
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;
4956 
4957         }
4958         break;
4959     }
4960 
4961     case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
4962 
4963         if (srb)
4964         {
4965             ExFreePool(srb);
4966             srb = NULL;
4967         }
4968 
4969         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
4970             sizeof(STORAGE_HOTPLUG_INFO)) {
4971 
4972             //
4973             // Indicate unsuccessful status and no data transferred.
4974             //
4975 
4976             Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
4977 
4978             ClassReleaseRemoveLock(DeviceObject, Irp);
4979             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
4980             status = STATUS_INFO_LENGTH_MISMATCH;
4981             goto SetStatusAndReturn;
4982 
4983         }
4984 
4985         if(!commonExtension->IsFdo) {
4986 
4987             //
4988             // Just forward this down and return
4989             //
4990 
4991             IoCopyCurrentIrpStackLocationToNext(Irp);
4992 
4993             ClassReleaseRemoveLock(DeviceObject, Irp);
4994             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
4995 
4996         } else {
4997 
4998             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
4999             PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
5000 
5001             status = STATUS_SUCCESS;
5002 
5003             if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
5004             {
5005                 status = STATUS_INVALID_PARAMETER_1;
5006             }
5007 
5008             if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
5009             {
5010                 status = STATUS_INVALID_PARAMETER_2;
5011             }
5012 
5013             if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
5014             {
5015                 status = STATUS_INVALID_PARAMETER_3;
5016             }
5017 
5018             if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
5019             {
5020                 status = STATUS_INVALID_PARAMETER_5;
5021             }
5022 
5023             if (NT_SUCCESS(status))
5024             {
5025                 fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
5026 
5027                 //
5028                 // Store the user-defined override in the registry
5029                 //
5030 
5031                 ClassSetDeviceParameter(fdoExtension,
5032                                         CLASSP_REG_SUBKEY_NAME,
5033                                         CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
5034                                         (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
5035             }
5036 
5037             Irp->IoStatus.Status = status;
5038 
5039             ClassReleaseRemoveLock(DeviceObject, Irp);
5040             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5041         }
5042 
5043         break;
5044     }
5045 
5046     case IOCTL_STORAGE_CHECK_VERIFY:
5047     case IOCTL_STORAGE_CHECK_VERIFY2: {
5048 
5049         PIRP irp2 = NULL;
5050         PIO_STACK_LOCATION newStack;
5051 
5052         PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5053 
5054         DebugPrint((1,"DeviceIoControl: Check verify\n"));
5055 
5056         //
5057         // If a buffer for a media change count was provided, make sure it's
5058         // big enough to hold the result
5059         //
5060 
5061         if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
5062 
5063             //
5064             // If the buffer is too small to hold the media change count
5065             // then return an error to the caller
5066             //
5067 
5068             if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
5069                sizeof(ULONG)) {
5070 
5071                 DebugPrint((3,"DeviceIoControl: media count "
5072                               "buffer too small\n"));
5073 
5074                 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
5075                 Irp->IoStatus.Information = sizeof(ULONG);
5076 
5077                 if(srb != NULL) {
5078                     ExFreePool(srb);
5079                 }
5080 
5081                 ClassReleaseRemoveLock(DeviceObject, Irp);
5082                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5083 
5084                 status = STATUS_BUFFER_TOO_SMALL;
5085                 goto SetStatusAndReturn;
5086 
5087             }
5088         }
5089 
5090         if(!commonExtension->IsFdo) {
5091 
5092             //
5093             // If this is a PDO then we should just forward the request down
5094             //
5095             ASSERT(!srb);
5096 
5097             IoCopyCurrentIrpStackLocationToNext(Irp);
5098 
5099             ClassReleaseRemoveLock(DeviceObject, Irp);
5100 
5101             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5102 
5103             goto SetStatusAndReturn;
5104 
5105         } else {
5106 
5107             fdoExtension = DeviceObject->DeviceExtension;
5108 
5109         }
5110 
5111         if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
5112 
5113             //
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
5117             // caller sent in
5118             //
5119 
5120             DebugPrint((2,"DeviceIoControl: Check verify wants "
5121                           "media count\n"));
5122 
5123             //
5124             // Allocate a new irp to send the TestUnitReady to the port driver
5125             //
5126 
5127             irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
5128 
5129             if(irp2 == NULL) {
5130                 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
5131                 Irp->IoStatus.Information = 0;
5132                 ASSERT(srb);
5133                 ExFreePool(srb);
5134                 ClassReleaseRemoveLock(DeviceObject, Irp);
5135                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5136                 status = STATUS_INSUFFICIENT_RESOURCES;
5137                 goto SetStatusAndReturn;
5138 
5139                 break;
5140             }
5141 
5142             //
5143             // Make sure to acquire the lock for the new irp.
5144             //
5145 
5146             ClassAcquireRemoveLock(DeviceObject, irp2);
5147 
5148             irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
5149             IoSetNextIrpStackLocation(irp2);
5150 
5151             //
5152             // Set the top stack location and shove the master Irp into the
5153             // top location
5154             //
5155 
5156             newStack = IoGetCurrentIrpStackLocation(irp2);
5157             newStack->Parameters.Others.Argument1 = Irp;
5158             newStack->DeviceObject = DeviceObject;
5159 
5160             //
5161             // Stick the check verify completion routine onto the stack
5162             // and prepare the irp for the port driver
5163             //
5164 
5165             IoSetCompletionRoutine(irp2,
5166                                    ClassCheckVerifyComplete,
5167                                    NULL,
5168                                    TRUE,
5169                                    TRUE,
5170                                    TRUE);
5171 
5172             IoSetNextIrpStackLocation(irp2);
5173             newStack = IoGetCurrentIrpStackLocation(irp2);
5174             newStack->DeviceObject = DeviceObject;
5175             newStack->MajorFunction = irpStack->MajorFunction;
5176             newStack->MinorFunction = irpStack->MinorFunction;
5177 
5178             //
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.
5182             //
5183 
5184             IoMarkIrpPending(Irp);
5185 
5186             Irp = irp2;
5187 
5188         }
5189 
5190         //
5191         // Test Unit Ready
5192         //
5193 
5194         srb->CdbLength = 6;
5195         cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
5196 
5197         //
5198         // Set timeout value.
5199         //
5200 
5201         srb->TimeOutValue = fdoExtension->TimeOutValue;
5202 
5203         //
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.
5206         //
5207 
5208         if(controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
5209             SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
5210         }
5211 
5212         //
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
5216         // on an error
5217         //
5218 
5219         //
5220         // This routine uses a completion routine so we don't want to release
5221         // the remove lock until then.
5222         //
5223 
5224         status = ClassSendSrbAsynchronous(DeviceObject,
5225                                           srb,
5226                                           Irp,
5227                                           NULL,
5228                                           0,
5229                                           FALSE);
5230 
5231         break;
5232     }
5233 
5234     case IOCTL_STORAGE_MEDIA_REMOVAL:
5235     case IOCTL_STORAGE_EJECTION_CONTROL: {
5236 
5237         PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
5238 
5239         DebugPrint((3, "DiskIoControl: ejection control\n"));
5240 
5241         if(srb) {
5242             ExFreePool(srb);
5243         }
5244 
5245         if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
5246            sizeof(PREVENT_MEDIA_REMOVAL)) {
5247 
5248             //
5249             // Indicate unsuccessful status and no data transferred.
5250             //
5251 
5252             Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
5253 
5254             ClassReleaseRemoveLock(DeviceObject, Irp);
5255             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5256             status = STATUS_INFO_LENGTH_MISMATCH;
5257             goto SetStatusAndReturn;
5258         }
5259 
5260         if(!commonExtension->IsFdo) {
5261 
5262             //
5263             // Just forward this down and return
5264             //
5265 
5266             IoCopyCurrentIrpStackLocationToNext(Irp);
5267 
5268             ClassReleaseRemoveLock(DeviceObject, Irp);
5269             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5270         }
5271         else {
5272 
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(
5278                         DeviceObject,
5279                         Irp,
5280                         ((modifiedIoControlCode ==
5281                         IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
5282                                                           SimpleMediaLock),
5283                         mediaRemoval->PreventMediaRemoval);
5284 
5285             Irp->IoStatus.Status = status;
5286             ClassReleaseRemoveLock(DeviceObject, Irp);
5287             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5288         }
5289 
5290         break;
5291     }
5292 
5293     case IOCTL_STORAGE_MCN_CONTROL: {
5294 
5295         DebugPrint((3, "DiskIoControl: MCN control\n"));
5296 
5297         if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
5298            sizeof(PREVENT_MEDIA_REMOVAL)) {
5299 
5300             //
5301             // Indicate unsuccessful status and no data transferred.
5302             //
5303 
5304             Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
5305             Irp->IoStatus.Information = 0;
5306 
5307             if(srb) {
5308                 ExFreePool(srb);
5309             }
5310 
5311             ClassReleaseRemoveLock(DeviceObject, Irp);
5312             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5313             status = STATUS_INFO_LENGTH_MISMATCH;
5314             goto SetStatusAndReturn;
5315         }
5316 
5317         if(!commonExtension->IsFdo) {
5318 
5319             //
5320             // Just forward this down and return
5321             //
5322 
5323             if(srb) {
5324                 ExFreePool(srb);
5325             }
5326 
5327             IoCopyCurrentIrpStackLocationToNext(Irp);
5328 
5329             ClassReleaseRemoveLock(DeviceObject, Irp);
5330             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5331 
5332         } else {
5333 
5334             //
5335             // Call to the FDO - handle the ejection control.
5336             //
5337 
5338             status = ClasspMcnControl(DeviceObject->DeviceExtension,
5339                                       Irp,
5340                                       srb);
5341         }
5342         goto SetStatusAndReturn;
5343     }
5344 
5345     case IOCTL_STORAGE_RESERVE:
5346     case IOCTL_STORAGE_RELEASE: {
5347 
5348         //
5349         // Reserve logical unit.
5350         //
5351 
5352         PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5353 
5354         if(!commonExtension->IsFdo) {
5355 
5356             IoCopyCurrentIrpStackLocationToNext(Irp);
5357 
5358             ClassReleaseRemoveLock(DeviceObject, Irp);
5359             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5360             goto SetStatusAndReturn;
5361         } else {
5362             fdoExtension = DeviceObject->DeviceExtension;
5363         }
5364 
5365         srb->CdbLength = 6;
5366 
5367         if(modifiedIoControlCode == IOCTL_STORAGE_RESERVE) {
5368             cdb->CDB6GENERIC.OperationCode = SCSIOP_RESERVE_UNIT;
5369         } else {
5370             cdb->CDB6GENERIC.OperationCode = SCSIOP_RELEASE_UNIT;
5371         }
5372 
5373         //
5374         // Set timeout value.
5375         //
5376 
5377         srb->TimeOutValue = fdoExtension->TimeOutValue;
5378 
5379         status = ClassSendSrbAsynchronous(DeviceObject,
5380                                           srb,
5381                                           Irp,
5382                                           NULL,
5383                                           0,
5384                                           FALSE);
5385 
5386         break;
5387     }
5388 
5389     case IOCTL_STORAGE_EJECT_MEDIA:
5390     case IOCTL_STORAGE_LOAD_MEDIA:
5391     case IOCTL_STORAGE_LOAD_MEDIA2:{
5392 
5393         //
5394         // Eject media.
5395         //
5396 
5397         PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
5398 
5399         if(!commonExtension->IsFdo) {
5400 
5401             IoCopyCurrentIrpStackLocationToNext(Irp);
5402 
5403             ClassReleaseRemoveLock(DeviceObject, Irp);
5404 
5405             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5406             goto SetStatusAndReturn;
5407         } else {
5408             fdoExtension = DeviceObject->DeviceExtension;
5409         }
5410 
5411         if(commonExtension->PagingPathCount != 0) {
5412 
5413             DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
5414                            "failure\n"));
5415 
5416             status = STATUS_FILES_OPEN;
5417             Irp->IoStatus.Status = status;
5418 
5419             Irp->IoStatus.Information = 0;
5420 
5421             if(srb) {
5422                 ExFreePool(srb);
5423             }
5424 
5425             ClassReleaseRemoveLock(DeviceObject, Irp);
5426             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5427             goto SetStatusAndReturn;
5428         }
5429 
5430         //
5431         // Synchronize with ejection control and ejection cleanup code as
5432         // well as other eject/load requests.
5433         //
5434 
5435         KeEnterCriticalRegion();
5436         KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
5437                               UserRequest,
5438                               UserMode,
5439                               FALSE,
5440                               NULL);
5441 
5442         if(fdoExtension->ProtectedLockCount != 0) {
5443 
5444             DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
5445                            "device - failure\n"));
5446 
5447             status = STATUS_DEVICE_BUSY;
5448             Irp->IoStatus.Status = status;
5449             Irp->IoStatus.Information = 0;
5450 
5451             if(srb) {
5452                 ExFreePool(srb);
5453             }
5454 
5455             ClassReleaseRemoveLock(DeviceObject, Irp);
5456             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5457 
5458             KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
5459                        IO_NO_INCREMENT,
5460                        FALSE);
5461             KeLeaveCriticalRegion();
5462 
5463             goto SetStatusAndReturn;
5464         }
5465 
5466         srb->CdbLength = 6;
5467 
5468         cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
5469         cdb->START_STOP.LoadEject = 1;
5470 
5471         if(modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
5472             cdb->START_STOP.Start = 0;
5473         } else {
5474             cdb->START_STOP.Start = 1;
5475         }
5476 
5477         //
5478         // Set timeout value.
5479         //
5480 
5481         srb->TimeOutValue = fdoExtension->TimeOutValue;
5482         status = ClassSendSrbAsynchronous(DeviceObject,
5483                                               srb,
5484                                               Irp,
5485                                               NULL,
5486                                               0,
5487                                               FALSE);
5488 
5489         KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
5490         KeLeaveCriticalRegion();
5491 
5492         break;
5493     }
5494 
5495     case IOCTL_STORAGE_FIND_NEW_DEVICES: {
5496 
5497         if(srb) {
5498             ExFreePool(srb);
5499         }
5500 
5501         if(commonExtension->IsFdo) {
5502 
5503             IoInvalidateDeviceRelations(
5504                 ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
5505                 BusRelations);
5506 
5507             status = STATUS_SUCCESS;
5508             Irp->IoStatus.Status = status;
5509 
5510             ClassReleaseRemoveLock(DeviceObject, Irp);
5511             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5512         }
5513         else {
5514 
5515             IoCopyCurrentIrpStackLocationToNext(Irp);
5516 
5517             ClassReleaseRemoveLock(DeviceObject, Irp);
5518             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5519         }
5520         break;
5521     }
5522 
5523     case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
5524 
5525         if(srb) {
5526             ExFreePool(srb);
5527         }
5528 
5529         if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
5530            sizeof(STORAGE_DEVICE_NUMBER)) {
5531 
5532             PSTORAGE_DEVICE_NUMBER deviceNumber =
5533                 Irp->AssociatedIrp.SystemBuffer;
5534             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
5535                 commonExtension->PartitionZeroExtension;
5536 
5537             deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
5538             deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
5539             deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
5540 
5541             status = STATUS_SUCCESS;
5542             Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
5543 
5544         } else {
5545             status = STATUS_BUFFER_TOO_SMALL;
5546             Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
5547         }
5548 
5549         Irp->IoStatus.Status = status;
5550         ClassReleaseRemoveLock(DeviceObject, Irp);
5551         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5552 
5553         break;
5554     }
5555 
5556     default: {
5557 
5558         DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
5559                     controlCode, DeviceObject));
5560 
5561         //
5562         // Pass the device control to the next driver.
5563         //
5564 
5565         if(srb) {
5566             ExFreePool(srb);
5567         }
5568 
5569         //
5570         // Copy the Irp stack parameters to the next stack location.
5571         //
5572 
5573         IoCopyCurrentIrpStackLocationToNext(Irp);
5574 
5575         ClassReleaseRemoveLock(DeviceObject, Irp);
5576         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
5577         break;
5578     }
5579 
5580     } // end switch( ...
5581 
5582 SetStatusAndReturn:
5583 
5584     DBGTRACE(ClassDebugTrace, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
5585 
5586     return status;
5587 } // end ClassDeviceControl()
5588 
5589 /*++////////////////////////////////////////////////////////////////////////////
5590 
5591 ClassShutdownFlush()
5592 
5593 Routine Description:
5594 
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.
5599 
5600 Arguments:
5601 
5602     DriverObject - Pointer to device object to being shutdown by system.
5603 
5604     Irp - IRP involved.
5605 
5606 Return Value:
5607 
5608     NT Status
5609 
5610 --*/
5611 NTSTATUS
5612 NTAPI
5613 ClassShutdownFlush(
5614     IN PDEVICE_OBJECT DeviceObject,
5615     IN PIRP Irp
5616     )
5617 {
5618     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
5619 
5620     ULONG isRemoved;
5621 
5622     //NTSTATUS status;
5623 
5624     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
5625 
5626     if(isRemoved) {
5627 
5628         ClassReleaseRemoveLock(DeviceObject, Irp);
5629 
5630         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
5631 
5632         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5633 
5634         return STATUS_DEVICE_DOES_NOT_EXIST;
5635     }
5636 
5637     if (commonExtension->DevInfo->ClassShutdownFlush) {
5638 
5639         //
5640         // Call the device-specific driver's routine.
5641         //
5642 
5643         return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
5644     }
5645 
5646     //
5647     // Device-specific driver doesn't support this.
5648     //
5649 
5650     Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
5651 
5652     ClassReleaseRemoveLock(DeviceObject, Irp);
5653     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
5654 
5655     return STATUS_INVALID_DEVICE_REQUEST;
5656 } // end ClassShutdownFlush()
5657 
5658 /*++////////////////////////////////////////////////////////////////////////////
5659 
5660 ClassCreateDeviceObject()
5661 
5662 Routine Description:
5663 
5664     This routine creates an object for the physical device specified and
5665     sets up the deviceExtension's function pointers for each entry point
5666     in the device-specific driver.
5667 
5668 Arguments:
5669 
5670     DriverObject - Pointer to driver object created by system.
5671 
5672     ObjectNameBuffer - Dir. name of the object to create.
5673 
5674     LowerDeviceObject - Pointer to the lower device object
5675 
5676     IsFdo - should this be an fdo or a pdo
5677 
5678     DeviceObject - Pointer to the device object pointer we will return.
5679 
5680 Return Value:
5681 
5682     NTSTATUS
5683 
5684 --*/
5685 NTSTATUS
5686 NTAPI
5687 ClassCreateDeviceObject(
5688     IN PDRIVER_OBJECT          DriverObject,
5689     IN PCCHAR                  ObjectNameBuffer,
5690     IN PDEVICE_OBJECT          LowerDevice,
5691     IN BOOLEAN                 IsFdo,
5692     IN OUT PDEVICE_OBJECT      *DeviceObject
5693     )
5694 {
5695     BOOLEAN        isPartitionable;
5696     STRING         ntNameString;
5697     UNICODE_STRING ntUnicodeString;
5698     NTSTATUS       status;
5699     PDEVICE_OBJECT deviceObject = NULL;
5700 
5701     ULONG          characteristics;
5702 
5703     PCLASS_DRIVER_EXTENSION
5704         driverExtension = IoGetDriverObjectExtension(DriverObject,
5705                                                      CLASS_DRIVER_EXTENSION_KEY);
5706 
5707     PCLASS_DEV_INFO devInfo;
5708 
5709     PAGED_CODE();
5710 
5711     *DeviceObject = NULL;
5712     RtlInitUnicodeString(&ntUnicodeString, NULL);
5713 
5714     DebugPrint((2, "ClassCreateFdo: Create device object\n"));
5715 
5716     ASSERT(LowerDevice);
5717 
5718     //
5719     // Make sure that if we're making PDO's we have an enumeration routine
5720     //
5721 
5722     isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
5723 
5724     ASSERT(IsFdo || isPartitionable);
5725 
5726     //
5727     // Grab the correct dev-info structure out of the init data
5728     //
5729 
5730     if(IsFdo) {
5731         devInfo = &(driverExtension->InitData.FdoData);
5732     } else {
5733         devInfo = &(driverExtension->InitData.PdoData);
5734     }
5735 
5736     characteristics = devInfo->DeviceCharacteristics;
5737 
5738     if(ARGUMENT_PRESENT(ObjectNameBuffer)) {
5739         DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
5740 
5741         RtlInitString(&ntNameString, ObjectNameBuffer);
5742 
5743         status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
5744 
5745         if (!NT_SUCCESS(status)) {
5746 
5747             DebugPrint((1,
5748                         "ClassCreateFdo: Cannot convert string %s\n",
5749                         ObjectNameBuffer));
5750 
5751             ntUnicodeString.Buffer = NULL;
5752             return status;
5753         }
5754     } else {
5755         DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
5756 
5757         if(IsFdo == FALSE) {
5758 
5759             //
5760             // PDO's have to have some sort of name.
5761             //
5762 
5763             SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
5764         }
5765 
5766         RtlInitUnicodeString(&ntUnicodeString, NULL);
5767     }
5768 
5769     status = IoCreateDevice(DriverObject,
5770                             devInfo->DeviceExtensionSize,
5771                             &ntUnicodeString,
5772                             devInfo->DeviceType,
5773                             devInfo->DeviceCharacteristics,
5774                             FALSE,
5775                             &deviceObject);
5776 
5777     if (!NT_SUCCESS(status)) {
5778 
5779         DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
5780                     status));
5781         ASSERT(deviceObject == NULL);
5782 
5783         //
5784         // buffer is not used any longer here.
5785         //
5786 
5787         if (ntUnicodeString.Buffer != NULL) {
5788             DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5789             ExFreePool(ntUnicodeString.Buffer);
5790             RtlInitUnicodeString(&ntUnicodeString, NULL);
5791         }
5792 
5793     } else {
5794 
5795         PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
5796 
5797         RtlZeroMemory(
5798             deviceObject->DeviceExtension,
5799             devInfo->DeviceExtensionSize);
5800 
5801         //
5802         // Setup version code
5803         //
5804 
5805         commonExtension->Version = 0x03;
5806 
5807         //
5808         // Setup the remove lock and event
5809         //
5810 
5811         commonExtension->IsRemoved = NO_REMOVE;
5812         commonExtension->RemoveLock = 0;
5813         KeInitializeEvent(&commonExtension->RemoveEvent,
5814                           SynchronizationEvent,
5815                           FALSE);
5816 
5817         #if DBG
5818             KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
5819             commonExtension->RemoveTrackingList = NULL;
5820         #else
5821             commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
5822             commonExtension->RemoveTrackingList = (PVOID) -1;
5823         #endif
5824 
5825         //
5826         // Acquire the lock once.  This reference will be released when the
5827         // remove IRP has been received.
5828         //
5829 
5830         ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
5831 
5832         //
5833         // Store a pointer to the driver extension so we don't have to do
5834         // lookups to get it.
5835         //
5836 
5837         commonExtension->DriverExtension = driverExtension;
5838 
5839         //
5840         // Fill in entry points
5841         //
5842 
5843         commonExtension->DevInfo = devInfo;
5844 
5845         //
5846         // Initialize some of the common values in the structure
5847         //
5848 
5849         commonExtension->DeviceObject = deviceObject;
5850 
5851         commonExtension->LowerDeviceObject = NULL;
5852 
5853         if(IsFdo) {
5854 
5855             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
5856 
5857             commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
5858 
5859             //
5860             // Set the initial device object flags.
5861             //
5862 
5863             SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
5864 
5865             //
5866             // Clear the PDO list
5867             //
5868 
5869             commonExtension->ChildList = NULL;
5870 
5871             commonExtension->DriverData =
5872                 ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
5873 
5874             if(isPartitionable) {
5875 
5876                 commonExtension->PartitionNumber = 0;
5877             } else {
5878                 commonExtension->PartitionNumber = (ULONG) (-1L);
5879             }
5880 
5881             fdoExtension->DevicePowerState = PowerDeviceD0;
5882 
5883             KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
5884                               SynchronizationEvent,
5885                               TRUE);
5886 
5887             KeInitializeEvent(&fdoExtension->ChildLock,
5888                               SynchronizationEvent,
5889                               TRUE);
5890 
5891             status = ClasspAllocateReleaseRequest(deviceObject);
5892 
5893             if(!NT_SUCCESS(status)) {
5894                 IoDeleteDevice(deviceObject);
5895                 *DeviceObject = NULL;
5896 
5897                 if (ntUnicodeString.Buffer != NULL) {
5898                     DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
5899                     ExFreePool(ntUnicodeString.Buffer);
5900                     RtlInitUnicodeString(&ntUnicodeString, NULL);
5901                 }
5902 
5903                 return status;
5904             }
5905 
5906         } else {
5907 
5908             PPHYSICAL_DEVICE_EXTENSION pdoExtension =
5909                 deviceObject->DeviceExtension;
5910 
5911             PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
5912                 LowerDevice->DeviceExtension;
5913 
5914             SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
5915 
5916             commonExtension->PartitionZeroExtension = p0Extension;
5917 
5918             //
5919             // Stick this onto the PDO list
5920             //
5921 
5922             ClassAddChild(p0Extension, pdoExtension, TRUE);
5923 
5924             commonExtension->DriverData = (PVOID) (pdoExtension + 1);
5925 
5926             //
5927             // Get the top of stack for the lower device - this allows
5928             // filters to get stuck in between the partitions and the
5929             // physical disk.
5930             //
5931 
5932             commonExtension->LowerDeviceObject =
5933                 IoGetAttachedDeviceReference(LowerDevice);
5934 
5935             //
5936             // Pnp will keep a reference to the lower device object long
5937             // after this partition has been deleted.  Dereference now so
5938             // we don't have to deal with it later.
5939             //
5940 
5941             ObDereferenceObject(commonExtension->LowerDeviceObject);
5942         }
5943 
5944         KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
5945 
5946         commonExtension->IsFdo = IsFdo;
5947 
5948         commonExtension->DeviceName = ntUnicodeString;
5949 
5950         commonExtension->PreviousState = 0xff;
5951 
5952         InitializeDictionary(&(commonExtension->FileObjectDictionary));
5953 
5954         commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
5955     }
5956 
5957     *DeviceObject = deviceObject;
5958 
5959     return status;
5960 } // end ClassCreateDeviceObject()
5961 
5962 /*++////////////////////////////////////////////////////////////////////////////
5963 
5964 ClassClaimDevice()
5965 
5966 Routine Description:
5967 
5968     This function claims a device in the port driver.  The port driver object
5969     is updated with the correct driver object if the device is successfully
5970     claimed.
5971 
5972 Arguments:
5973 
5974     LowerDeviceObject - Supplies the base port device object.
5975 
5976     Release - Indicates the logical unit should be released rather than claimed.
5977 
5978 Return Value:
5979 
5980     Returns a status indicating success or failure of the operation.
5981 
5982 --*/
5983 NTSTATUS
5984 NTAPI
5985 ClassClaimDevice(
5986     IN PDEVICE_OBJECT LowerDeviceObject,
5987     IN BOOLEAN Release
5988     )
5989 {
5990     IO_STATUS_BLOCK    ioStatus;
5991     PIRP               irp;
5992     PIO_STACK_LOCATION irpStack;
5993     KEVENT             event;
5994     NTSTATUS           status;
5995     SCSI_REQUEST_BLOCK srb;
5996 
5997     PAGED_CODE();
5998 
5999     //
6000     // Clear the SRB fields.
6001     //
6002 
6003     RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
6004 
6005     //
6006     // Write length to SRB.
6007     //
6008 
6009     srb.Length = sizeof(SCSI_REQUEST_BLOCK);
6010 
6011     srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
6012         SRB_FUNCTION_CLAIM_DEVICE;
6013 
6014     //
6015     // Set the event object to the unsignaled state.
6016     // It will be used to signal request completion
6017     //
6018 
6019     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
6020 
6021     //
6022     // Build synchronous request with no transfer.
6023     //
6024 
6025     irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
6026                                         LowerDeviceObject,
6027                                         NULL,
6028                                         0,
6029                                         NULL,
6030                                         0,
6031                                         TRUE,
6032                                         &event,
6033                                         &ioStatus);
6034 
6035     if (irp == NULL) {
6036         DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
6037         return STATUS_INSUFFICIENT_RESOURCES;
6038     }
6039 
6040     irpStack = IoGetNextIrpStackLocation(irp);
6041 
6042     //
6043     // Save SRB address in next stack for port driver.
6044     //
6045 
6046     irpStack->Parameters.Scsi.Srb = &srb;
6047 
6048     //
6049     // Set up IRP Address.
6050     //
6051 
6052     srb.OriginalRequest = irp;
6053 
6054     //
6055     // Call the port driver with the request and wait for it to complete.
6056     //
6057 
6058     status = IoCallDriver(LowerDeviceObject, irp);
6059     if (status == STATUS_PENDING) {
6060 
6061         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
6062         status = ioStatus.Status;
6063     }
6064 
6065     //
6066     // If this is a release request, then just decrement the reference count
6067     // and return.  The status does not matter.
6068     //
6069 
6070     if (Release) {
6071 
6072         // ObDereferenceObject(LowerDeviceObject);
6073         return STATUS_SUCCESS;
6074     }
6075 
6076     if (!NT_SUCCESS(status)) {
6077         return status;
6078     }
6079 
6080     ASSERT(srb.DataBuffer != NULL);
6081     ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
6082 
6083     return status;
6084 } // end ClassClaimDevice()
6085 
6086 /*++////////////////////////////////////////////////////////////////////////////
6087 
6088 ClassInternalIoControl()
6089 
6090 Routine Description:
6091 
6092     This routine passes internal device controls to the port driver.
6093     Internal device controls are used by higher level drivers both for ioctls
6094     and to pass through scsi requests.
6095 
6096     If the IoControlCode does not match any of the handled ioctls and is
6097     a valid system address then the request will be treated as an SRB and
6098     passed down to the lower driver.  If the IoControlCode is not a valid
6099     system address the ioctl will be failed.
6100 
6101     Callers must therefore be extremely cautious to pass correct, initialized
6102     values to this function.
6103 
6104 Arguments:
6105 
6106     DeviceObject - Supplies a pointer to the device object for this request.
6107 
6108     Irp - Supplies the Irp making the request.
6109 
6110 Return Value:
6111 
6112    Returns back a STATUS_PENDING or a completion status.
6113 
6114 --*/
6115 NTSTATUS
6116 NTAPI
6117 ClassInternalIoControl(
6118     IN PDEVICE_OBJECT DeviceObject,
6119     IN PIRP Irp
6120     )
6121 {
6122     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6123 
6124     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
6125     PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
6126 
6127     ULONG isRemoved;
6128 
6129     PSCSI_REQUEST_BLOCK srb;
6130 
6131     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
6132 
6133     if(isRemoved) {
6134 
6135         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
6136 
6137         ClassReleaseRemoveLock(DeviceObject, Irp);
6138 
6139         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
6140 
6141         return STATUS_DEVICE_DOES_NOT_EXIST;
6142     }
6143 
6144     //
6145     // Get a pointer to the SRB.
6146     //
6147 
6148     srb = irpStack->Parameters.Scsi.Srb;
6149 
6150     //
6151     // Set the parameters in the next stack location.
6152     //
6153 
6154     if(commonExtension->IsFdo) {
6155         nextStack->Parameters.Scsi.Srb = srb;
6156         nextStack->MajorFunction = IRP_MJ_SCSI;
6157         nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
6158 
6159     } else {
6160 
6161         IoCopyCurrentIrpStackLocationToNext(Irp);
6162     }
6163 
6164     ClassReleaseRemoveLock(DeviceObject, Irp);
6165 
6166     return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
6167 } // end ClassInternalIoControl()
6168 
6169 /*++////////////////////////////////////////////////////////////////////////////
6170 
6171 ClassQueryTimeOutRegistryValue()
6172 
6173 Routine Description:
6174 
6175     This routine determines whether a reg key for a user-specified timeout
6176     value exists.  This should be called at initialization time.
6177 
6178 Arguments:
6179 
6180     DeviceObject - Pointer to the device object we are retrieving the timeout
6181                    value for
6182 
6183 Return Value:
6184 
6185     None, but it sets a new default timeout for a class of devices.
6186 
6187 --*/
6188 ULONG
6189 NTAPI
6190 ClassQueryTimeOutRegistryValue(
6191     IN PDEVICE_OBJECT DeviceObject
6192     )
6193 {
6194     //
6195     // Find the appropriate reg. key
6196     //
6197 
6198     PCLASS_DRIVER_EXTENSION
6199         driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
6200                                                      CLASS_DRIVER_EXTENSION_KEY);
6201 
6202     PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
6203 
6204     PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
6205     PWSTR path;
6206     NTSTATUS status;
6207     LONG     timeOut = 0;
6208     ULONG    zero = 0;
6209     ULONG    size;
6210 
6211     PAGED_CODE();
6212 
6213     if (!registryPath) {
6214         return 0;
6215     }
6216 
6217     parameters = ExAllocatePoolWithTag(NonPagedPool,
6218                                 sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
6219                                 '1BcS');
6220 
6221     if (!parameters) {
6222         return 0;
6223     }
6224 
6225     size = registryPath->MaximumLength + sizeof(WCHAR);
6226     path = ExAllocatePoolWithTag(NonPagedPool, size, '2BcS');
6227 
6228     if (!path) {
6229         ExFreePool(parameters);
6230         return 0;
6231     }
6232 
6233     RtlZeroMemory(path,size);
6234     RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
6235 
6236 
6237     //
6238     // Check for the Timeout value.
6239     //
6240 
6241     RtlZeroMemory(parameters,
6242                   (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
6243 
6244     parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
6245     parameters[0].Name          = L"TimeOutValue";
6246     parameters[0].EntryContext  = &timeOut;
6247     parameters[0].DefaultType   = REG_DWORD;
6248     parameters[0].DefaultData   = &zero;
6249     parameters[0].DefaultLength = sizeof(ULONG);
6250 
6251     status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
6252                                     path,
6253                                     parameters,
6254                                     NULL,
6255                                     NULL);
6256 
6257     if (!(NT_SUCCESS(status))) {
6258         timeOut = 0;
6259     }
6260 
6261     ExFreePool(parameters);
6262     ExFreePool(path);
6263 
6264     DebugPrint((2,
6265                 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
6266                 timeOut));
6267 
6268 
6269     return timeOut;
6270 
6271 } // end ClassQueryTimeOutRegistryValue()
6272 
6273 /*++////////////////////////////////////////////////////////////////////////////
6274 
6275 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
6276 
6277 Routine Description:
6278 
6279     This routine executes when the port driver has completed a check verify
6280     ioctl.  It will set the status of the master Irp, copy the media change
6281     count and complete the request.
6282 
6283 Arguments:
6284 
6285     Fdo - Supplies the functional device object which represents the logical unit.
6286 
6287     Irp - Supplies the Irp which has completed.
6288 
6289     Context - NULL
6290 
6291 Return Value:
6292 
6293     NT status
6294 
6295 --*/
6296 NTSTATUS
6297 NTAPI
6298 ClassCheckVerifyComplete(
6299     IN PDEVICE_OBJECT Fdo,
6300     IN PIRP Irp,
6301     IN PVOID Context
6302     )
6303 {
6304     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
6305     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6306 
6307     PIRP originalIrp;
6308 
6309     ASSERT_FDO(Fdo);
6310 
6311     originalIrp = irpStack->Parameters.Others.Argument1;
6312 
6313     //
6314     // Copy the media change count and status
6315     //
6316 
6317     *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
6318         fdoExtension->MediaChangeCount;
6319 
6320     DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
6321                    "device %d is %lx - saved as %lx\n",
6322                 fdoExtension->DeviceNumber,
6323                 fdoExtension->MediaChangeCount,
6324                 *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
6325 
6326     originalIrp->IoStatus.Status = Irp->IoStatus.Status;
6327     originalIrp->IoStatus.Information = sizeof(ULONG);
6328 
6329     ClassReleaseRemoveLock(Fdo, originalIrp);
6330     ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
6331 
6332     IoFreeIrp(Irp);
6333 
6334     return STATUS_MORE_PROCESSING_REQUIRED;
6335 
6336 } // end ClassCheckVerifyComplete()
6337 
6338 /*++////////////////////////////////////////////////////////////////////////////
6339 
6340 ClassGetDescriptor()
6341 
6342 Routine Description:
6343 
6344     This routine will perform a query for the specified property id and will
6345     allocate a non-paged buffer to store the data in.  It is the responsibility
6346     of the caller to ensure that this buffer is freed.
6347 
6348     This routine must be run at IRQL_PASSIVE_LEVEL
6349 
6350 Arguments:
6351 
6352     DeviceObject - the device to query
6353     DeviceInfo - a location to store a pointer to the buffer we allocate
6354 
6355 Return Value:
6356 
6357     status
6358     if status is unsuccessful *DeviceInfo will be set to NULL, else the
6359     buffer allocated on behalf of the caller.
6360 
6361 --*/
6362 NTSTATUS
6363 NTAPI
6364 ClassGetDescriptor(
6365     IN PDEVICE_OBJECT DeviceObject,
6366     IN PSTORAGE_PROPERTY_ID PropertyId,
6367     OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
6368     )
6369 {
6370     STORAGE_PROPERTY_QUERY query;
6371     IO_STATUS_BLOCK ioStatus;
6372 
6373     PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
6374     ULONG length;
6375 
6376     //UCHAR pass = 0;
6377 
6378     PAGED_CODE();
6379 
6380     //
6381     // Set the passed-in descriptor pointer to NULL as default
6382     //
6383 
6384     *Descriptor = NULL;
6385 
6386 
6387     RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
6388     query.PropertyId = *PropertyId;
6389     query.QueryType = PropertyStandardQuery;
6390 
6391     //
6392     // On the first pass we just want to get the first few
6393     // bytes of the descriptor so we can read it's size
6394     //
6395 
6396     descriptor = (PVOID)&query;
6397 
6398     ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
6399 
6400     ClassSendDeviceIoControlSynchronous(
6401         IOCTL_STORAGE_QUERY_PROPERTY,
6402         DeviceObject,
6403         &query,
6404         sizeof(STORAGE_PROPERTY_QUERY),
6405         sizeof(ULONG) * 2,
6406         FALSE,
6407         &ioStatus
6408         );
6409 
6410     if(!NT_SUCCESS(ioStatus.Status)) {
6411 
6412         DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6413                        "query properties #1\n", ioStatus.Status));
6414         return ioStatus.Status;
6415     }
6416 
6417     if (descriptor->Size == 0) {
6418 
6419         //
6420         // This DebugPrint is to help third-party driver writers
6421         //
6422 
6423         DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
6424                     "%x\n", ioStatus.Status));
6425         return STATUS_UNSUCCESSFUL;
6426 
6427     }
6428 
6429     //
6430     // This time we know how much data there is so we can
6431     // allocate a buffer of the correct size
6432     //
6433 
6434     length = descriptor->Size;
6435 
6436     descriptor = ExAllocatePoolWithTag(NonPagedPool, length, '4BcS');
6437 
6438     if(descriptor == NULL) {
6439 
6440         DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
6441                     "(%d bytes)\n", length));
6442         return STATUS_INSUFFICIENT_RESOURCES;
6443     }
6444 
6445     //
6446     // setup the query again, as it was overwritten above
6447     //
6448 
6449     RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
6450     query.PropertyId = *PropertyId;
6451     query.QueryType = PropertyStandardQuery;
6452 
6453     //
6454     // copy the input to the new outputbuffer
6455     //
6456 
6457     RtlCopyMemory(descriptor,
6458                   &query,
6459                   sizeof(STORAGE_PROPERTY_QUERY)
6460                   );
6461 
6462     ClassSendDeviceIoControlSynchronous(
6463         IOCTL_STORAGE_QUERY_PROPERTY,
6464         DeviceObject,
6465         descriptor,
6466         sizeof(STORAGE_PROPERTY_QUERY),
6467         length,
6468         FALSE,
6469         &ioStatus
6470         );
6471 
6472     if(!NT_SUCCESS(ioStatus.Status)) {
6473 
6474         DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
6475                        "query properties #1\n", ioStatus.Status));
6476         ExFreePool(descriptor);
6477         return ioStatus.Status;
6478     }
6479 
6480     //
6481     // return the memory we've allocated to the caller
6482     //
6483 
6484     *Descriptor = descriptor;
6485     return ioStatus.Status;
6486 } // end ClassGetDescriptor()
6487 
6488 /*++////////////////////////////////////////////////////////////////////////////
6489 
6490 ClassSignalCompletion()
6491 
6492 Routine Description:
6493 
6494     This completion routine will signal the event given as context and then
6495     return STATUS_MORE_PROCESSING_REQUIRED to stop event completion.  It is
6496     the responsibility of the routine waiting on the event to complete the
6497     request and free the event.
6498 
6499 Arguments:
6500 
6501     DeviceObject - a pointer to the device object
6502 
6503     Irp - a pointer to the irp
6504 
6505     Event - a pointer to the event to signal
6506 
6507 Return Value:
6508 
6509     STATUS_MORE_PROCESSING_REQUIRED
6510 
6511 --*/
6512 NTSTATUS
6513 NTAPI
6514 ClassSignalCompletion(
6515     IN PDEVICE_OBJECT DeviceObject,
6516     IN PIRP Irp,
6517     IN PVOID Context
6518     )
6519 {
6520     PKEVENT Event = Context;
6521 
6522     KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
6523 
6524     return STATUS_MORE_PROCESSING_REQUIRED;
6525 } // end ClassSignalCompletion()
6526 
6527 /*++////////////////////////////////////////////////////////////////////////////
6528 
6529 ClassPnpQueryFdoRelations()
6530 
6531 Routine Description:
6532 
6533     This routine will call the driver's enumeration routine to update the
6534     list of PDO's.  It will then build a response to the
6535     IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
6536     the irp.
6537 
6538 Arguments:
6539 
6540     Fdo - a pointer to the functional device object we are enumerating
6541 
6542     Irp - a pointer to the enumeration request
6543 
6544 Return Value:
6545 
6546     status
6547 
6548 --*/
6549 NTSTATUS
6550 NTAPI
6551 ClassPnpQueryFdoRelations(
6552     IN PDEVICE_OBJECT Fdo,
6553     IN PIRP Irp
6554     )
6555 {
6556     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6557     PCLASS_DRIVER_EXTENSION
6558         driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
6559                                                      CLASS_DRIVER_EXTENSION_KEY);
6560 
6561     PAGED_CODE();
6562 
6563     //
6564     // If there's already an enumeration in progress then don't start another
6565     // one.
6566     //
6567 
6568     if(InterlockedIncrement((PLONG)&fdoExtension->EnumerationInterlock) == 1) {
6569         driverExtension->InitData.ClassEnumerateDevice(Fdo);
6570     }
6571 
6572     Irp->IoStatus.Information = (ULONG_PTR) NULL;
6573 
6574     Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
6575                                 Fdo,
6576                                 BusRelations,
6577                                 (PDEVICE_RELATIONS*)&Irp->IoStatus.Information);
6578     InterlockedDecrement((PLONG)&fdoExtension->EnumerationInterlock);
6579 
6580     return Irp->IoStatus.Status;
6581 } // end ClassPnpQueryFdoRelations()
6582 
6583 /*++////////////////////////////////////////////////////////////////////////////
6584 
6585 ClassMarkChildrenMissing()
6586 
6587 Routine Description:
6588 
6589     This routine will call ClassMarkChildMissing() for all children.
6590     It acquires the ChildLock before calling ClassMarkChildMissing().
6591 
6592 Arguments:
6593 
6594     Fdo - the "bus's" device object, such as the disk FDO for non-removable
6595         disks with multiple partitions.
6596 
6597 Return Value:
6598 
6599     None
6600 
6601 --*/
6602 VOID
6603 NTAPI
6604 ClassMarkChildrenMissing(
6605     IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
6606     )
6607 {
6608     PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
6609     PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
6610 
6611     PAGED_CODE();
6612 
6613     ClassAcquireChildLock(Fdo);
6614 
6615     while (nextChild){
6616         PPHYSICAL_DEVICE_EXTENSION tmpChild;
6617 
6618         /*
6619          *  ClassMarkChildMissing will also dequeue the child extension.
6620          *  So get the next pointer before calling ClassMarkChildMissing.
6621          */
6622         tmpChild = nextChild;
6623         nextChild = tmpChild->CommonExtension.ChildList;
6624         ClassMarkChildMissing(tmpChild, FALSE);
6625     }
6626     ClassReleaseChildLock(Fdo);
6627     return;
6628 } // end ClassMarkChildrenMissing()
6629 
6630 /*++////////////////////////////////////////////////////////////////////////////
6631 
6632 ClassMarkChildMissing()
6633 
6634 Routine Description:
6635 
6636     This routine will make an active child "missing."  If the device has never
6637     been enumerated then it will be deleted on the spot.  If the device has
6638     not been enumerated then it will be marked as missing so that we can
6639     not report it in the next device enumeration.
6640 
6641 Arguments:
6642 
6643     Child - the child device to be marked as missing.
6644 
6645     AcquireChildLock - TRUE if the child lock should be acquired before removing
6646                        the missing child.  FALSE if the child lock is already
6647                        acquired by this thread.
6648 
6649 Return Value:
6650 
6651     returns whether or not the child device object has previously been reported
6652     to PNP.
6653 
6654 --*/
6655 BOOLEAN
6656 NTAPI
6657 ClassMarkChildMissing(
6658     IN PPHYSICAL_DEVICE_EXTENSION Child,
6659     IN BOOLEAN AcquireChildLock
6660     )
6661 {
6662     BOOLEAN returnValue = Child->IsEnumerated;
6663 
6664     PAGED_CODE();
6665     ASSERT_PDO(Child->DeviceObject);
6666 
6667     Child->IsMissing = TRUE;
6668 
6669     //
6670     // Make sure this child is not in the active list.
6671     //
6672 
6673     ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
6674                      Child,
6675                      AcquireChildLock);
6676 
6677     if(Child->IsEnumerated == FALSE) {
6678         ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
6679     }
6680 
6681     return returnValue;
6682 } // end ClassMarkChildMissing()
6683 
6684 /*++////////////////////////////////////////////////////////////////////////////
6685 
6686 ClassRetrieveDeviceRelations()
6687 
6688 Routine Description:
6689 
6690     This routine will allocate a buffer to hold the specified list of
6691     relations.  It will then fill in the list with referenced device pointers
6692     and will return the request.
6693 
6694 Arguments:
6695 
6696     Fdo - pointer to the FDO being queried
6697 
6698     RelationType - what type of relations are being queried
6699 
6700     DeviceRelations - a location to store a pointer to the response
6701 
6702 Return Value:
6703 
6704     status
6705 
6706 --*/
6707 NTSTATUS
6708 NTAPI
6709 ClassRetrieveDeviceRelations(
6710     IN PDEVICE_OBJECT Fdo,
6711     IN DEVICE_RELATION_TYPE RelationType,
6712     OUT PDEVICE_RELATIONS *DeviceRelations
6713     )
6714 {
6715     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6716 
6717     ULONG count = 0;
6718     ULONG i;
6719 
6720     PPHYSICAL_DEVICE_EXTENSION nextChild;
6721 
6722     ULONG relationsSize;
6723     PDEVICE_RELATIONS deviceRelations = NULL;
6724 
6725     NTSTATUS status;
6726 
6727     PAGED_CODE();
6728 
6729     ClassAcquireChildLock(fdoExtension);
6730 
6731     nextChild = fdoExtension->CommonExtension.ChildList;
6732 
6733     //
6734     // Count the number of PDO's attached to this disk
6735     //
6736 
6737     while(nextChild != NULL) {
6738         PCOMMON_DEVICE_EXTENSION commonExtension;
6739 
6740         commonExtension = &(nextChild->CommonExtension);
6741 
6742         ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6743                   (nextChild->IsMissing == FALSE));
6744 
6745         nextChild = commonExtension->ChildList;
6746 
6747         count++;
6748     };
6749 
6750     relationsSize = (sizeof(DEVICE_RELATIONS) +
6751                      (count * sizeof(PDEVICE_OBJECT)));
6752 
6753     deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
6754 
6755     if(deviceRelations == NULL) {
6756 
6757         DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
6758                        "%d bytes for device relations\n", relationsSize));
6759 
6760         ClassReleaseChildLock(fdoExtension);
6761 
6762         return STATUS_INSUFFICIENT_RESOURCES;
6763     }
6764 
6765     RtlZeroMemory(deviceRelations, relationsSize);
6766 
6767     nextChild = fdoExtension->CommonExtension.ChildList;
6768     i = count - 1;
6769 
6770     while(nextChild != NULL) {
6771         PCOMMON_DEVICE_EXTENSION commonExtension;
6772 
6773         commonExtension = &(nextChild->CommonExtension);
6774 
6775         ASSERTMSG("ClassPnp internal error: missing child on active list\n",
6776                   (nextChild->IsMissing == FALSE));
6777 
6778         deviceRelations->Objects[i--] = nextChild->DeviceObject;
6779 
6780         status = ObReferenceObjectByPointer(
6781                     nextChild->DeviceObject,
6782                     0,
6783                     NULL,
6784                     KernelMode);
6785         ASSERT(NT_SUCCESS(status));
6786 
6787         nextChild->IsEnumerated = TRUE;
6788         nextChild = commonExtension->ChildList;
6789     }
6790 
6791     ASSERTMSG("Child list has changed: ", i == -1);
6792 
6793     deviceRelations->Count = count;
6794     *DeviceRelations = deviceRelations;
6795     ClassReleaseChildLock(fdoExtension);
6796     return STATUS_SUCCESS;
6797 } // end ClassRetrieveDeviceRelations()
6798 
6799 /*++////////////////////////////////////////////////////////////////////////////
6800 
6801 ClassGetPdoId()
6802 
6803 Routine Description:
6804 
6805     This routine will call into the driver to retrieve a copy of one of it's
6806     id strings.
6807 
6808 Arguments:
6809 
6810     Pdo - a pointer to the pdo being queried
6811 
6812     IdType - which type of id string is being queried
6813 
6814     IdString - an allocated unicode string structure which the driver
6815                can fill in.
6816 
6817 Return Value:
6818 
6819     status
6820 
6821 --*/
6822 NTSTATUS
6823 NTAPI
6824 ClassGetPdoId(
6825     IN PDEVICE_OBJECT Pdo,
6826     IN BUS_QUERY_ID_TYPE IdType,
6827     IN PUNICODE_STRING IdString
6828     )
6829 {
6830     PCLASS_DRIVER_EXTENSION
6831         driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject,
6832                                                      CLASS_DRIVER_EXTENSION_KEY);
6833 
6834     ASSERT_PDO(Pdo);
6835     ASSERT(driverExtension->InitData.ClassQueryId);
6836 
6837     PAGED_CODE();
6838 
6839     return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
6840 } // end ClassGetPdoId()
6841 
6842 /*++////////////////////////////////////////////////////////////////////////////
6843 
6844 ClassQueryPnpCapabilities()
6845 
6846 Routine Description:
6847 
6848     This routine will call into the class driver to retrieve it's pnp
6849     capabilities.
6850 
6851 Arguments:
6852 
6853     PhysicalDeviceObject - The physical device object to retrieve properties
6854                            for.
6855 
6856 Return Value:
6857 
6858     status
6859 
6860 --*/
6861 NTSTATUS
6862 NTAPI
6863 ClassQueryPnpCapabilities(
6864     IN PDEVICE_OBJECT DeviceObject,
6865     IN PDEVICE_CAPABILITIES Capabilities
6866     )
6867 {
6868     PCLASS_DRIVER_EXTENSION driverExtension =
6869         ClassGetDriverExtension(DeviceObject->DriverObject);
6870     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6871 
6872     PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
6873 
6874     PAGED_CODE();
6875 
6876     ASSERT(DeviceObject);
6877     ASSERT(Capabilities);
6878 
6879     if(commonExtension->IsFdo) {
6880         queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
6881     } else {
6882         queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
6883     }
6884 
6885     if(queryRoutine) {
6886         return queryRoutine(DeviceObject,
6887                             Capabilities);
6888     } else {
6889         return STATUS_NOT_IMPLEMENTED;
6890     }
6891 } // end ClassQueryPnpCapabilities()
6892 
6893 /*++////////////////////////////////////////////////////////////////////////////
6894 
6895 ClassInvalidateBusRelations()
6896 
6897 Routine Description:
6898 
6899     This routine re-enumerates the devices on the "bus".  It will call into
6900     the driver's ClassEnumerate routine to update the device objects
6901     immediately.  It will then schedule a bus re-enumeration for pnp by calling
6902     IoInvalidateDeviceRelations.
6903 
6904 Arguments:
6905 
6906     Fdo - a pointer to the functional device object for this bus
6907 
6908 Return Value:
6909 
6910     none
6911 
6912 --*/
6913 VOID
6914 NTAPI
6915 ClassInvalidateBusRelations(
6916     IN PDEVICE_OBJECT Fdo
6917     )
6918 {
6919     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
6920     PCLASS_DRIVER_EXTENSION
6921         driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
6922                                                      CLASS_DRIVER_EXTENSION_KEY);
6923 
6924     NTSTATUS status = STATUS_SUCCESS;
6925 
6926     PAGED_CODE();
6927 
6928     ASSERT_FDO(Fdo);
6929     ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
6930 
6931     if(InterlockedIncrement((PLONG)&fdoExtension->EnumerationInterlock) == 1) {
6932         status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
6933     }
6934     InterlockedDecrement((PLONG)&fdoExtension->EnumerationInterlock);
6935 
6936     if(!NT_SUCCESS(status)) {
6937 
6938         DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
6939                        "returned %lx\n", status));
6940     }
6941 
6942     IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
6943 
6944     return;
6945 } // end ClassInvalidateBusRelations()
6946 
6947 /*++////////////////////////////////////////////////////////////////////////////
6948 
6949 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
6950 
6951 Routine Description:
6952 
6953     This routine is called to handle the "removal" of a device.  It will
6954     forward the request downwards if necessary, call into the driver
6955     to release any necessary resources (memory, events, etc) and then
6956     will delete the device object.
6957 
6958 Arguments:
6959 
6960     DeviceObject - a pointer to the device object being removed
6961 
6962     RemoveType - indicates what type of remove this is (regular or surprise).
6963 
6964 Return Value:
6965 
6966     status
6967 
6968 --*/
6969 NTSTATUS
6970 NTAPI
6971 ClassRemoveDevice(
6972     IN PDEVICE_OBJECT DeviceObject,
6973     IN UCHAR RemoveType
6974     )
6975 {
6976     PCLASS_DRIVER_EXTENSION
6977         driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
6978                                                      CLASS_DRIVER_EXTENSION_KEY);
6979     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
6980     PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
6981     PCLASS_WMI_INFO classWmiInfo;
6982     BOOLEAN proceedWithRemove = TRUE;
6983     NTSTATUS status;
6984 
6985     PAGED_CODE();
6986 
6987     commonExtension->IsRemoved = REMOVE_PENDING;
6988 
6989     /*
6990      *  Deregister from WMI.
6991      */
6992     classWmiInfo = commonExtension->IsFdo ?
6993                             &driverExtension->InitData.FdoData.ClassWmiInfo :
6994                             &driverExtension->InitData.PdoData.ClassWmiInfo;
6995     if (classWmiInfo->GuidRegInfo){
6996         status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
6997         DBGTRACE(ClassDebugInfo, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
6998     }
6999 
7000     /*
7001      *  If we exposed a "shingle" (a named device interface openable by CreateFile)
7002      *  then delete it now.
7003      */
7004     if (commonExtension->MountedDeviceInterfaceName.Buffer){
7005         IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
7006         RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
7007         RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
7008     }
7009 
7010     //
7011     // If this is a surprise removal we leave the device around - which means
7012     // we don't have to (or want to) drop the remove lock and wait for pending
7013     // requests to complete.
7014     //
7015 
7016     if (RemoveType == IRP_MN_REMOVE_DEVICE){
7017 
7018         //
7019         // Release the lock we acquired when the device object was created.
7020         //
7021 
7022         ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
7023 
7024         DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
7025                     commonExtension->RemoveLock));
7026 
7027         KeWaitForSingleObject(&commonExtension->RemoveEvent,
7028                               Executive,
7029                               KernelMode,
7030                               FALSE,
7031                               NULL);
7032 
7033         DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject));
7034 
7035         if(commonExtension->IsFdo) {
7036 
7037             DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
7038                            "remove request.\n", DeviceObject));
7039 
7040         }
7041         else {
7042             PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
7043 
7044             if (pdoExtension->IsMissing){
7045                 /*
7046                  *  The child partition PDO is missing, so we are going to go ahead
7047                  *  and delete it for the remove.
7048                  */
7049                 DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
7050             }
7051             else {
7052                 /*
7053                  *  We got a remove for a child partition PDO which is not actually missing.
7054                  *  So we will NOT actually delete it.
7055                  */
7056                 DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
7057 
7058                 //
7059                 // Reacquire the remove lock for the next time this comes around.
7060                 //
7061 
7062                 ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
7063 
7064                 //
7065                 // the device wasn't missing so it's not really been removed.
7066                 //
7067 
7068                 commonExtension->IsRemoved = NO_REMOVE;
7069 
7070                 IoInvalidateDeviceRelations(
7071                     commonExtension->PartitionZeroExtension->LowerPdo,
7072                     BusRelations);
7073 
7074                 proceedWithRemove = FALSE;
7075             }
7076         }
7077     }
7078 
7079 
7080     if (proceedWithRemove){
7081 
7082         /*
7083          *  Call the class driver's remove handler.
7084          *  All this is supposed to do is clean up its data and device interfaces.
7085          */
7086         ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
7087         status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
7088         ASSERT(NT_SUCCESS(status));
7089         status = STATUS_SUCCESS;
7090 
7091         if (commonExtension->IsFdo){
7092             //PDEVICE_OBJECT pdo;
7093             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
7094 
7095             ClasspDisableTimer(fdoExtension->DeviceObject);
7096 
7097             if (RemoveType == IRP_MN_REMOVE_DEVICE){
7098 
7099                 PPHYSICAL_DEVICE_EXTENSION child;
7100 
7101                 //
7102                 // Cleanup the media detection resources now that the class driver
7103                 // has stopped it's timer (if any) and we can be sure they won't
7104                 // call us to do detection again.
7105                 //
7106 
7107                 ClassCleanupMediaChangeDetection(fdoExtension);
7108 
7109                 //
7110                 // Cleanup any Failure Prediction stuff
7111                 //
7112                 if (fdoExtension->FailurePredictionInfo) {
7113                     ExFreePool(fdoExtension->FailurePredictionInfo);
7114                     fdoExtension->FailurePredictionInfo = NULL;
7115                 }
7116 
7117                 /*
7118                  *  Ordinarily all child PDOs will be removed by the time
7119                  *  that the parent gets the REMOVE_DEVICE.
7120                  *  However, if a child PDO has been created but has not
7121                  *  been announced in a QueryDeviceRelations, then it is
7122                  *  just a private data structure unknown to pnp, and we have
7123                  *  to delete it ourselves.
7124                  */
7125                 ClassAcquireChildLock(fdoExtension);
7126                 while ((child = ClassRemoveChild(fdoExtension, NULL, FALSE))){
7127 
7128                     //
7129                     // Yank the pdo.  This routine will unlink the device from the
7130                     // pdo list so NextPdo will point to the next one when it's
7131                     // complete.
7132                     //
7133                     child->IsMissing = TRUE;
7134                     ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
7135                 }
7136                 ClassReleaseChildLock(fdoExtension);
7137             }
7138             else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
7139                 /*
7140                  *  This is a surprise-remove on the parent FDO.
7141                  *  We will mark the child PDOs as missing so that they
7142                  *  will actually get deleted when they get a REMOVE_DEVICE.
7143                  */
7144                 ClassMarkChildrenMissing(fdoExtension);
7145             }
7146 
7147             ClasspFreeReleaseRequest(DeviceObject);
7148 
7149             if (RemoveType == IRP_MN_REMOVE_DEVICE){
7150 
7151                 //
7152                 // Free FDO-specific data structs
7153                 //
7154                 if (fdoExtension->PrivateFdoData){
7155 
7156                     DestroyAllTransferPackets(DeviceObject);
7157 
7158                     ExFreePool(fdoExtension->PrivateFdoData);
7159                     fdoExtension->PrivateFdoData = NULL;
7160                 }
7161 
7162                 if (commonExtension->DeviceName.Buffer) {
7163                     ExFreePool(commonExtension->DeviceName.Buffer);
7164                     RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
7165                 }
7166 
7167                 if (fdoExtension->AdapterDescriptor) {
7168                     ExFreePool(fdoExtension->AdapterDescriptor);
7169                     fdoExtension->AdapterDescriptor = NULL;
7170                 }
7171 
7172                 if (fdoExtension->DeviceDescriptor) {
7173                     ExFreePool(fdoExtension->DeviceDescriptor);
7174                     fdoExtension->DeviceDescriptor = NULL;
7175                 }
7176 
7177                 //
7178                 // Detach our device object from the stack - there's no reason
7179                 // to hold off our cleanup any longer.
7180                 //
7181 
7182                 IoDetachDevice(lowerDeviceObject);
7183             }
7184         }
7185         else {
7186             /*
7187              *  This is a child partition PDO.
7188              *  We have already determined that it was previously marked
7189              *  as missing.  So if this is a REMOVE_DEVICE, we will actually
7190              *  delete it.
7191              */
7192             if (RemoveType == IRP_MN_REMOVE_DEVICE){
7193                 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
7194                     commonExtension->PartitionZeroExtension;
7195                 PPHYSICAL_DEVICE_EXTENSION pdoExtension =
7196                     (PPHYSICAL_DEVICE_EXTENSION) commonExtension;
7197 
7198                 //
7199                 // See if this device is in the child list (if this was a surprise
7200                 // removal it might be) and remove it.
7201                 //
7202 
7203                 ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
7204             }
7205         }
7206 
7207         commonExtension->PartitionLength.QuadPart = 0;
7208 
7209         if (RemoveType == IRP_MN_REMOVE_DEVICE){
7210             IoDeleteDevice(DeviceObject);
7211         }
7212     }
7213 
7214     return STATUS_SUCCESS;
7215 } // end ClassRemoveDevice()
7216 
7217 /*++////////////////////////////////////////////////////////////////////////////
7218 
7219 ClassGetDriverExtension()
7220 
7221 Routine Description:
7222 
7223     This routine will return the classpnp's driver extension.
7224 
7225 Arguments:
7226 
7227     DriverObject - the driver object for which to get classpnp's extension
7228 
7229 Return Value:
7230 
7231     Either NULL if none, or a pointer to the driver extension
7232 
7233 --*/
7234 PCLASS_DRIVER_EXTENSION
7235 NTAPI
7236 ClassGetDriverExtension(
7237     IN PDRIVER_OBJECT DriverObject
7238     )
7239 {
7240     return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
7241 } // end ClassGetDriverExtension()
7242 
7243 /*++////////////////////////////////////////////////////////////////////////////
7244 
7245 ClasspStartIo()
7246 
7247 Routine Description:
7248 
7249     This routine wraps the class driver's start io routine.  If the device
7250     is being removed it will complete any requests with
7251     STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
7252 
7253 Arguments:
7254 
7255 Return Value:
7256 
7257     none
7258 
7259 --*/
7260 VOID
7261 NTAPI
7262 ClasspStartIo(
7263     IN PDEVICE_OBJECT DeviceObject,
7264     IN PIRP Irp
7265     )
7266 {
7267     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7268 
7269     //
7270     // We're already holding the remove lock so just check the variable and
7271     // see what's going on.
7272     //
7273 
7274     if(commonExtension->IsRemoved) {
7275 
7276         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
7277 
7278         ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
7279         ClassReleaseRemoveLock(DeviceObject, Irp);
7280         ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
7281         IoStartNextPacket(DeviceObject, FALSE);
7282         ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
7283         return;
7284     }
7285 
7286     commonExtension->DriverExtension->InitData.ClassStartIo(
7287         DeviceObject,
7288         Irp);
7289 
7290     return;
7291 } // ClasspStartIo()
7292 
7293 /*++////////////////////////////////////////////////////////////////////////////
7294 
7295 ClassUpdateInformationInRegistry()
7296 
7297 Routine Description:
7298 
7299     This routine has knowledge about the layout of the device map information
7300     in the registry.  It will update this information to include a value
7301     entry specifying the dos device name that is assumed to get assigned
7302     to this NT device name.  For more information on this assigning of the
7303     dos device name look in the drive support routine in the hal that assigns
7304     all dos names.
7305 
7306     Since some versions of some device's firmware did not work and some
7307     vendors did not bother to follow the specification, the entire inquiry
7308     information must also be stored in the registry so than someone can
7309     figure out the firmware version.
7310 
7311 Arguments:
7312 
7313     DeviceObject - A pointer to the device object for the tape device.
7314 
7315 Return Value:
7316 
7317     None
7318 
7319 --*/
7320 VOID
7321 NTAPI
7322 ClassUpdateInformationInRegistry(
7323     IN PDEVICE_OBJECT     Fdo,
7324     IN PCHAR              DeviceName,
7325     IN ULONG              DeviceNumber,
7326     IN PINQUIRYDATA       InquiryData,
7327     IN ULONG              InquiryDataLength
7328     )
7329 {
7330     NTSTATUS          status;
7331     SCSI_ADDRESS      scsiAddress;
7332     OBJECT_ATTRIBUTES objectAttributes;
7333     PSTR              buffer;
7334     STRING            string;
7335     UNICODE_STRING    unicodeName;
7336     UNICODE_STRING    unicodeRegistryPath;
7337     UNICODE_STRING    unicodeData;
7338     HANDLE            targetKey;
7339     IO_STATUS_BLOCK   ioStatus;
7340 
7341 
7342     PAGED_CODE();
7343 
7344     ASSERT(DeviceName);
7345     buffer = NULL;
7346     targetKey = NULL;
7347     RtlZeroMemory(&unicodeName,         sizeof(UNICODE_STRING));
7348     RtlZeroMemory(&unicodeData,         sizeof(UNICODE_STRING));
7349     RtlZeroMemory(&unicodeRegistryPath, sizeof(UNICODE_STRING));
7350 
7351     TRY {
7352 
7353         //
7354         // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
7355         //
7356 
7357         ClassSendDeviceIoControlSynchronous(
7358             IOCTL_SCSI_GET_ADDRESS,
7359             Fdo,
7360             &scsiAddress,
7361             0,
7362             sizeof(SCSI_ADDRESS),
7363             FALSE,
7364             &ioStatus
7365             );
7366 
7367         if (!NT_SUCCESS(ioStatus.Status)) {
7368 
7369             status = ioStatus.Status;
7370             DebugPrint((1,
7371                         "UpdateInformationInRegistry: Get Address failed %lx\n",
7372                         status));
7373             LEAVE;
7374 
7375         } else {
7376 
7377             DebugPrint((1,
7378                         "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
7379                         scsiAddress.PortNumber,
7380                         scsiAddress.PathId,
7381                         scsiAddress.TargetId,
7382                         scsiAddress.Lun));
7383 
7384         }
7385 
7386         //
7387         // Allocate a buffer for the reg. spooge.
7388         //
7389 
7390         buffer = ExAllocatePoolWithTag(PagedPool, 1024, '6BcS');
7391 
7392         if (buffer == NULL) {
7393 
7394             //
7395             // There is not return value for this.  Since this is done at
7396             // claim device time (currently only system initialization) getting
7397             // the registry information correct will be the least of the worries.
7398             //
7399 
7400             LEAVE;
7401         }
7402 
7403         sprintf(buffer,
7404                 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
7405                 scsiAddress.PortNumber,
7406                 scsiAddress.PathId,
7407                 scsiAddress.TargetId,
7408                 scsiAddress.Lun);
7409 
7410         RtlInitString(&string, buffer);
7411 
7412         status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
7413                                               &string,
7414                                               TRUE);
7415 
7416         if (!NT_SUCCESS(status)) {
7417             LEAVE;
7418         }
7419 
7420         //
7421         // Open the registry key for the scsi information for this
7422         // scsibus, target, lun.
7423         //
7424 
7425         InitializeObjectAttributes(&objectAttributes,
7426                                    &unicodeRegistryPath,
7427                                    OBJ_CASE_INSENSITIVE,
7428                                    NULL,
7429                                    NULL);
7430 
7431         status = ZwOpenKey(&targetKey,
7432                            KEY_READ | KEY_WRITE,
7433                            &objectAttributes);
7434 
7435         if (!NT_SUCCESS(status)) {
7436             LEAVE;
7437         }
7438 
7439         //
7440         // Now construct and attempt to create the registry value
7441         // specifying the device name in the appropriate place in the
7442         // device map.
7443         //
7444 
7445         RtlInitUnicodeString(&unicodeName, L"DeviceName");
7446 
7447         sprintf(buffer, "%s%lu", DeviceName, DeviceNumber);
7448         RtlInitString(&string, buffer);
7449         status = RtlAnsiStringToUnicodeString(&unicodeData,
7450                                               &string,
7451                                               TRUE);
7452         if (NT_SUCCESS(status)) {
7453             status = ZwSetValueKey(targetKey,
7454                                    &unicodeName,
7455                                    0,
7456                                    REG_SZ,
7457                                    unicodeData.Buffer,
7458                                    unicodeData.Length);
7459         }
7460 
7461         //
7462         // if they sent in data, update the registry
7463         //
7464 
7465         if (InquiryDataLength) {
7466 
7467             ASSERT(InquiryData);
7468 
7469             RtlInitUnicodeString(&unicodeName, L"InquiryData");
7470             status = ZwSetValueKey(targetKey,
7471                                    &unicodeName,
7472                                    0,
7473                                    REG_BINARY,
7474                                    InquiryData,
7475                                    InquiryDataLength);
7476         }
7477 
7478         // that's all, except to clean up.
7479 
7480     } FINALLY {
7481 
7482         if (unicodeData.Buffer) {
7483             RtlFreeUnicodeString(&unicodeData);
7484         }
7485         if (unicodeRegistryPath.Buffer) {
7486             RtlFreeUnicodeString(&unicodeRegistryPath);
7487         }
7488         if (targetKey) {
7489             ZwClose(targetKey);
7490         }
7491         if (buffer) {
7492             ExFreePool(buffer);
7493         }
7494 
7495     }
7496 
7497 } // end ClassUpdateInformationInRegistry()
7498 
7499 /*++////////////////////////////////////////////////////////////////////////////
7500 
7501 ClasspSendSynchronousCompletion()
7502 
7503 Routine Description:
7504 
7505     This completion routine will set the user event in the irp after
7506     freeing the irp and the associated MDL (if any).
7507 
7508 Arguments:
7509 
7510     DeviceObject - the device object which requested the completion routine
7511 
7512     Irp - the irp being completed
7513 
7514     Context - unused
7515 
7516 Return Value:
7517 
7518     STATUS_MORE_PROCESSING_REQUIRED
7519 
7520 --*/
7521 NTSTATUS
7522 NTAPI
7523 ClasspSendSynchronousCompletion(
7524     IN PDEVICE_OBJECT DeviceObject,
7525     IN PIRP Irp,
7526     IN PVOID Context
7527     )
7528 {
7529     DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
7530                    DeviceObject, Irp, Context));
7531     //
7532     // First set the status and information fields in the io status block
7533     // provided by the caller.
7534     //
7535 
7536     *(Irp->UserIosb) = Irp->IoStatus;
7537 
7538     //
7539     // Unlock the pages for the data buffer.
7540     //
7541 
7542     if(Irp->MdlAddress) {
7543         MmUnlockPages(Irp->MdlAddress);
7544         IoFreeMdl(Irp->MdlAddress);
7545     }
7546 
7547     //
7548     // Signal the caller's event.
7549     //
7550 
7551     KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
7552 
7553     //
7554     // Free the MDL and the IRP.
7555     //
7556 
7557     IoFreeIrp(Irp);
7558 
7559     return STATUS_MORE_PROCESSING_REQUIRED;
7560 } // end ClasspSendSynchronousCompletion()
7561 
7562 /*++
7563 
7564     ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
7565 
7566 --*/
7567 VOID
7568 NTAPI
7569 ClasspRegisterMountedDeviceInterface(
7570     IN PDEVICE_OBJECT DeviceObject
7571     )
7572 {
7573 
7574     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7575     BOOLEAN isFdo = commonExtension->IsFdo;
7576 
7577     PDEVICE_OBJECT pdo;
7578     UNICODE_STRING interfaceName;
7579 
7580     NTSTATUS status;
7581 
7582     if(isFdo) {
7583 
7584         PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
7585 
7586         functionalExtension =
7587             (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
7588         pdo = functionalExtension->LowerPdo;
7589     } else {
7590         pdo = DeviceObject;
7591     }
7592 
7593     status = IoRegisterDeviceInterface(
7594                 pdo,
7595                 &MOUNTDEV_MOUNTED_DEVICE_GUID,
7596                 NULL,
7597                 &interfaceName
7598                 );
7599 
7600     if(NT_SUCCESS(status)) {
7601 
7602         //
7603         // Copy the interface name before setting the interface state - the
7604         // name is needed by the components we notify.
7605         //
7606 
7607         commonExtension->MountedDeviceInterfaceName = interfaceName;
7608         status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
7609 
7610         if(!NT_SUCCESS(status)) {
7611             RtlFreeUnicodeString(&interfaceName);
7612         }
7613     }
7614 
7615     if(!NT_SUCCESS(status)) {
7616         RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
7617                              NULL);
7618     }
7619     return;
7620 } // end ClasspRegisterMountedDeviceInterface()
7621 
7622 /*++////////////////////////////////////////////////////////////////////////////
7623 
7624 ClassSendDeviceIoControlSynchronous()
7625 
7626 Routine Description:
7627 
7628     This routine is based upon IoBuildDeviceIoControlRequest().  It has been
7629     modified to reduce code and memory by not double-buffering the io, using
7630     the same buffer for both input and output, allocating and deallocating
7631     the mdl on behalf of the caller, and waiting for the io to complete.
7632 
7633     This routine also works around the rare cases in which APC's are disabled.
7634     Since IoBuildDeviceIoControl() used APC's to signal completion, this had
7635     led to a number of difficult-to-detect hangs, where the irp was completed,
7636     but the event passed to IoBuild..() was still being waited upon by the
7637     caller.
7638 
7639 Arguments:
7640 
7641     IoControlCode - the IOCTL to send
7642 
7643     TargetDeviceObject - the device object that should handle the ioctl
7644 
7645     Buffer - the input and output buffer, or NULL if no input/output
7646 
7647     InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
7648 
7649     OutputBufferLength - the number of bytes to be filled in upon success
7650 
7651     InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
7652 
7653     IoStatus - the status block that contains the results of the operation
7654 
7655 Return Value:
7656 
7657 --*/
7658 VOID
7659 NTAPI
7660 ClassSendDeviceIoControlSynchronous(
7661     IN ULONG IoControlCode,
7662     IN PDEVICE_OBJECT TargetDeviceObject,
7663     IN OUT PVOID Buffer OPTIONAL,
7664     IN ULONG InputBufferLength,
7665     IN ULONG OutputBufferLength,
7666     IN BOOLEAN InternalDeviceIoControl,
7667     OUT PIO_STATUS_BLOCK IoStatus
7668     )
7669 {
7670     PIRP irp;
7671     PIO_STACK_LOCATION irpSp;
7672     ULONG method;
7673 
7674     PAGED_CODE();
7675 
7676     irp = NULL;
7677     method = IoControlCode & 3;
7678 
7679 
7680     #if DBG // Begin Argument Checking (nop in fre version)
7681 
7682         ASSERT(ARGUMENT_PRESENT(IoStatus));
7683 
7684         if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
7685             ASSERT(ARGUMENT_PRESENT(Buffer));
7686         }
7687         else {
7688             ASSERT(!ARGUMENT_PRESENT(Buffer));
7689         }
7690     #endif
7691 
7692     //
7693     // Begin by allocating the IRP for this request.  Do not charge quota to
7694     // the current process for this IRP.
7695     //
7696 
7697     irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
7698     if (!irp) {
7699         (*IoStatus).Information = 0;
7700         (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7701         return;
7702     }
7703 
7704     //
7705     // Get a pointer to the stack location of the first driver which will be
7706     // invoked.  This is where the function codes and the parameters are set.
7707     //
7708 
7709     irpSp = IoGetNextIrpStackLocation(irp);
7710 
7711     //
7712     // Set the major function code based on the type of device I/O control
7713     // function the caller has specified.
7714     //
7715 
7716     if (InternalDeviceIoControl) {
7717         irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
7718     } else {
7719         irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
7720     }
7721 
7722     //
7723     // Copy the caller's parameters to the service-specific portion of the
7724     // IRP for those parameters that are the same for all four methods.
7725     //
7726 
7727     irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
7728     irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
7729     irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
7730 
7731     //
7732     // Get the method bits from the I/O control code to determine how the
7733     // buffers are to be passed to the driver.
7734     //
7735 
7736     switch (method) {
7737         // case 0
7738         case METHOD_BUFFERED: {
7739             if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
7740 
7741                 irp->AssociatedIrp.SystemBuffer =
7742                     ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
7743                                           max(InputBufferLength, OutputBufferLength),
7744                                           CLASS_TAG_DEVICE_CONTROL
7745                                           );
7746 
7747                 if (irp->AssociatedIrp.SystemBuffer == NULL) {
7748                     IoFreeIrp(irp);
7749                     (*IoStatus).Information = 0;
7750                     (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7751                     return;
7752                 }
7753 
7754                 if (InputBufferLength != 0) {
7755                     RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,
7756                                   Buffer,
7757                                   InputBufferLength);
7758                 }
7759             } // end of buffering
7760 
7761             irp->UserBuffer = Buffer;
7762             break;
7763         }
7764 
7765         // case 1, case 2
7766         case METHOD_IN_DIRECT:
7767         case METHOD_OUT_DIRECT: {
7768 
7769 
7770             if (InputBufferLength != 0) {
7771                 irp->AssociatedIrp.SystemBuffer = Buffer;
7772             }
7773 
7774             if (OutputBufferLength != 0) {
7775 
7776                 irp->MdlAddress = IoAllocateMdl(Buffer,
7777                                                 OutputBufferLength,
7778                                                 FALSE, FALSE,
7779                                                 (PIRP) NULL);
7780 
7781                 if (irp->MdlAddress == NULL) {
7782                     IoFreeIrp(irp);
7783                     (*IoStatus).Information = 0;
7784                     (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
7785                     return;
7786                 }
7787 
7788                 if (method == METHOD_IN_DIRECT) {
7789                     MmProbeAndLockPages(irp->MdlAddress,
7790                                         KernelMode,
7791                                         IoReadAccess);
7792                 } else if (method == METHOD_OUT_DIRECT) {
7793                     MmProbeAndLockPages(irp->MdlAddress,
7794                                         KernelMode,
7795                                         IoWriteAccess);
7796                 } else {
7797                     ASSERT(!"If other methods reach here, code is out of date");
7798                 }
7799             }
7800             break;
7801         }
7802 
7803         // case 3
7804         case METHOD_NEITHER: {
7805 
7806             ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
7807             IoStatus->Information = 0;
7808             IoStatus->Status = STATUS_NOT_SUPPORTED;
7809             return;
7810             break;
7811         }
7812     } // end of switch(method)
7813 
7814     irp->Tail.Overlay.Thread = PsGetCurrentThread();
7815 
7816     //
7817     // send the irp synchronously
7818     //
7819 
7820     ClassSendIrpSynchronous(TargetDeviceObject, irp);
7821 
7822     //
7823     // copy the iostatus block for the caller
7824     //
7825 
7826     *IoStatus = irp->IoStatus;
7827 
7828     //
7829     // free any allocated resources
7830     //
7831 
7832     switch (method) {
7833         case METHOD_BUFFERED: {
7834 
7835             ASSERT(irp->UserBuffer == Buffer);
7836 
7837             //
7838             // first copy the buffered result, if any
7839             // Note that there are no security implications in
7840             // not checking for success since only drivers can
7841             // call into this routine anyways...
7842             //
7843 
7844             if (OutputBufferLength != 0) {
7845                 RtlCopyMemory(Buffer, // irp->UserBuffer
7846                               irp->AssociatedIrp.SystemBuffer,
7847                               OutputBufferLength
7848                               );
7849             }
7850 
7851             //
7852             // then free the memory allocated to buffer the io
7853             //
7854 
7855             if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
7856                 ExFreePool(irp->AssociatedIrp.SystemBuffer);
7857                 irp->AssociatedIrp.SystemBuffer = NULL;
7858             }
7859             break;
7860         }
7861 
7862         case METHOD_IN_DIRECT:
7863         case METHOD_OUT_DIRECT: {
7864 
7865             //
7866             // we alloc a mdl if there is an output buffer specified
7867             // free it here after unlocking the pages
7868             //
7869 
7870             if (OutputBufferLength != 0) {
7871                 ASSERT(irp->MdlAddress != NULL);
7872                 MmUnlockPages(irp->MdlAddress);
7873                 IoFreeMdl(irp->MdlAddress);
7874                 irp->MdlAddress = (PMDL) NULL;
7875             }
7876             break;
7877         }
7878 
7879         case METHOD_NEITHER: {
7880             ASSERT(!"Code is out of date");
7881             break;
7882         }
7883     }
7884 
7885     //
7886     // we always have allocated an irp.  free it here.
7887     //
7888 
7889     IoFreeIrp(irp);
7890     irp = (PIRP) NULL;
7891 
7892     //
7893     // return the io status block's status to the caller
7894     //
7895 
7896     return;
7897 } // end ClassSendDeviceIoControlSynchronous()
7898 
7899 /*++////////////////////////////////////////////////////////////////////////////
7900 
7901 ClassForwardIrpSynchronous()
7902 
7903 Routine Description:
7904 
7905     Forwards a given irp to the next lower device object.
7906 
7907 Arguments:
7908 
7909     CommonExtension - the common class extension
7910 
7911     Irp - the request to forward down the stack
7912 
7913 Return Value:
7914 
7915 --*/
7916 NTSTATUS
7917 NTAPI
7918 ClassForwardIrpSynchronous(
7919     IN PCOMMON_DEVICE_EXTENSION CommonExtension,
7920     IN PIRP Irp
7921     )
7922 {
7923     IoCopyCurrentIrpStackLocationToNext(Irp);
7924     return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
7925 } // end ClassForwardIrpSynchronous()
7926 
7927 /*++////////////////////////////////////////////////////////////////////////////
7928 
7929 ClassSendIrpSynchronous()
7930 
7931 Routine Description:
7932 
7933     This routine sends the given irp to the given device object, and waits for
7934     it to complete.  On debug versions, will print out a debug message and
7935     optionally assert for "lost" irps based upon classpnp's globals
7936 
7937 Arguments:
7938 
7939     TargetDeviceObject - the device object to handle this irp
7940 
7941     Irp - the request to be sent
7942 
7943 Return Value:
7944 
7945 --*/
7946 NTSTATUS
7947 NTAPI
7948 ClassSendIrpSynchronous(
7949     IN PDEVICE_OBJECT TargetDeviceObject,
7950     IN PIRP Irp
7951     )
7952 {
7953     KEVENT event;
7954     NTSTATUS status;
7955 
7956     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
7957     ASSERT(TargetDeviceObject != NULL);
7958     ASSERT(Irp != NULL);
7959     ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
7960 
7961     //
7962     // ISSUE-2000/02/20-henrygab   What if APCs are disabled?
7963     //    May need to enter critical section before IoCallDriver()
7964     //    until the event is hit?
7965     //
7966 
7967     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
7968     IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
7969                            TRUE, TRUE, TRUE);
7970 
7971     status = IoCallDriver(TargetDeviceObject, Irp);
7972 
7973     if (status == STATUS_PENDING) {
7974 
7975         #if DBG
7976             LARGE_INTEGER timeout;
7977 
7978             timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
7979                                           ClasspnpGlobals.SecondsToWaitForIrps);
7980 
7981             do {
7982                 status = KeWaitForSingleObject(&event,
7983                                                Executive,
7984                                                KernelMode,
7985                                                FALSE,
7986                                                &timeout);
7987 
7988 
7989                 if (status == STATUS_TIMEOUT) {
7990 
7991                     //
7992                     // This DebugPrint should almost always be investigated by the
7993                     // party who sent the irp and/or the current owner of the irp.
7994                     // Synchronous Irps should not take this long (currently 30
7995                     // seconds) without good reason.  This points to a potentially
7996                     // serious problem in the underlying device stack.
7997                     //
7998 
7999                     DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
8000                                 "complete within %x seconds\n",
8001                                 TargetDeviceObject, Irp,
8002                                 ClasspnpGlobals.SecondsToWaitForIrps
8003                                 ));
8004 
8005                     if (ClasspnpGlobals.BreakOnLostIrps != 0) {
8006                         ASSERT(!" - Irp failed to complete within 30 seconds - ");
8007                     }
8008                 }
8009 
8010 
8011             } while (status==STATUS_TIMEOUT);
8012         #else
8013             KeWaitForSingleObject(&event,
8014                                   Executive,
8015                                   KernelMode,
8016                                   FALSE,
8017                                   NULL);
8018         #endif
8019 
8020         status = Irp->IoStatus.Status;
8021     }
8022 
8023     return status;
8024 } // end ClassSendIrpSynchronous()
8025 
8026 /*++////////////////////////////////////////////////////////////////////////////
8027 
8028 ClassGetVpb()
8029 
8030 Routine Description:
8031 
8032     This routine returns the current VPB (Volume Parameter Block) for the
8033     given device object.
8034     The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
8035     of DEVICE_OBJECT; hence this exported function.
8036 
8037 Arguments:
8038 
8039     DeviceObject - the device to get the VPB for
8040 
8041 Return Value:
8042 
8043     the VPB, or NULL if none.
8044 
8045 --*/
8046 PVPB
8047 NTAPI
8048 ClassGetVpb(
8049     IN PDEVICE_OBJECT DeviceObject
8050     )
8051 {
8052     return DeviceObject->Vpb;
8053 } // end ClassGetVpb()
8054 
8055 /*++
8056 
8057     ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
8058 
8059 --*/
8060 NTSTATUS
8061 NTAPI
8062 ClasspAllocateReleaseRequest(
8063     IN PDEVICE_OBJECT Fdo
8064     )
8065 {
8066     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8067     //PIO_STACK_LOCATION irpStack;
8068 
8069     KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
8070 
8071     fdoExtension->ReleaseQueueNeeded = FALSE;
8072     fdoExtension->ReleaseQueueInProgress = FALSE;
8073     fdoExtension->ReleaseQueueIrpFromPool = FALSE;
8074 
8075     //
8076     // The class driver is responsible for allocating a properly sized irp,
8077     // or ClassReleaseQueue will attempt to do it on the first error.
8078     //
8079 
8080     fdoExtension->ReleaseQueueIrp = NULL;
8081 
8082     //
8083     // Write length to SRB.
8084     //
8085 
8086     fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
8087 
8088     return STATUS_SUCCESS;
8089 } // end ClasspAllocateReleaseRequest()
8090 
8091 /*++
8092 
8093     ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
8094 
8095 --*/
8096 VOID
8097 NTAPI
8098 ClasspFreeReleaseRequest(
8099     IN PDEVICE_OBJECT Fdo
8100     )
8101 {
8102     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8103     //KIRQL oldIrql;
8104 
8105     ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
8106 
8107     //
8108     // free anything the driver allocated
8109     //
8110 
8111     if (fdoExtension->ReleaseQueueIrp) {
8112         if (fdoExtension->ReleaseQueueIrpFromPool) {
8113             ExFreePool(fdoExtension->ReleaseQueueIrp);
8114         } else {
8115             IoFreeIrp(fdoExtension->ReleaseQueueIrp);
8116         }
8117         fdoExtension->ReleaseQueueIrp = NULL;
8118     }
8119 
8120     //
8121     // free anything that we allocated
8122     //
8123 
8124     if ((fdoExtension->PrivateFdoData) &&
8125         (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
8126 
8127         ExFreePool(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
8128         fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
8129         fdoExtension->PrivateFdoData->ReleaseQueueIrp = NULL;
8130     }
8131 
8132     return;
8133 } // end ClasspFreeReleaseRequest()
8134 
8135 /*++////////////////////////////////////////////////////////////////////////////
8136 
8137 ClassReleaseQueue()
8138 
8139 Routine Description:
8140 
8141     This routine issues an internal device control command
8142     to the port driver to release a frozen queue. The call
8143     is issued asynchronously as ClassReleaseQueue will be invoked
8144     from the IO completion DPC (and will have no context to
8145     wait for a synchronous call to complete).
8146 
8147     This routine must be called with the remove lock held.
8148 
8149 Arguments:
8150 
8151     Fdo - The functional device object for the device with the frozen queue.
8152 
8153 Return Value:
8154 
8155     None.
8156 
8157 --*/
8158 VOID
8159 NTAPI
8160 ClassReleaseQueue(
8161     IN PDEVICE_OBJECT Fdo
8162     )
8163 {
8164     ClasspReleaseQueue(Fdo, NULL);
8165     return;
8166 } // end ClassReleaseQueue()
8167 
8168 /*++////////////////////////////////////////////////////////////////////////////
8169 
8170 ClasspAllocateReleaseQueueIrp()
8171 
8172 Routine Description:
8173 
8174     This routine allocates the release queue irp held in classpnp's private
8175     extension.  This was added to allow no-memory conditions to be more
8176     survivable.
8177 
8178 Return Value:
8179 
8180     NT_SUCCESS value.
8181 
8182 Notes:
8183 
8184     Does not grab the spinlock.  Should only be called from StartDevice()
8185     routine.  May be called elsewhere for poorly-behaved drivers that cause
8186     the queue to lockup before the device is started.  This should *never*
8187     occur, since it's illegal to send a request to a non-started PDO.  This
8188     condition is checked for in ClasspReleaseQueue().
8189 
8190 --*/
8191 NTSTATUS
8192 NTAPI
8193 ClasspAllocateReleaseQueueIrp(
8194     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8195     )
8196 {
8197     //KIRQL oldIrql;
8198     UCHAR lowerStackSize;
8199 
8200     //
8201     // do an initial check w/o the spinlock
8202     //
8203 
8204     if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8205         return STATUS_SUCCESS;
8206     }
8207 
8208 
8209     lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
8210 
8211     //
8212     // don't allocate one if one is in progress!  this means whoever called
8213     // this routine didn't check if one was in progress.
8214     //
8215 
8216     ASSERT(!(FdoExtension->ReleaseQueueInProgress));
8217 
8218     FdoExtension->PrivateFdoData->ReleaseQueueIrp =
8219         ExAllocatePoolWithTag(NonPagedPool,
8220                               IoSizeOfIrp(lowerStackSize),
8221                               CLASS_TAG_RELEASE_QUEUE
8222                               );
8223 
8224     if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
8225         DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
8226                     "release queue irp\n"));
8227         return STATUS_INSUFFICIENT_RESOURCES;
8228     }
8229     IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
8230                     IoSizeOfIrp(lowerStackSize),
8231                     lowerStackSize);
8232     FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
8233 
8234     return STATUS_SUCCESS;
8235 }
8236 
8237 /*++////////////////////////////////////////////////////////////////////////////
8238 
8239 ClasspReleaseQueue()
8240 
8241 Routine Description:
8242 
8243     This routine issues an internal device control command
8244     to the port driver to release a frozen queue. The call
8245     is issued asynchronously as ClassReleaseQueue will be invoked
8246     from the IO completion DPC (and will have no context to
8247     wait for a synchronous call to complete).
8248 
8249     This routine must be called with the remove lock held.
8250 
8251 Arguments:
8252 
8253     Fdo - The functional device object for the device with the frozen queue.
8254 
8255     ReleaseQueueIrp - If this irp is supplied then the test to determine whether
8256                       a release queue request is in progress will be ignored.
8257                       The irp provided must be the IRP originally allocated
8258                       for release queue requests (so this parameter can only
8259                       really be provided by the release queue completion
8260                       routine.)
8261 
8262 Return Value:
8263 
8264     None.
8265 
8266 --*/
8267 VOID
8268 NTAPI
8269 ClasspReleaseQueue(
8270     IN PDEVICE_OBJECT Fdo,
8271     IN PIRP ReleaseQueueIrp OPTIONAL
8272     )
8273 {
8274     PIO_STACK_LOCATION irpStack;
8275     PIRP irp;
8276     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
8277     PDEVICE_OBJECT lowerDevice;
8278     PSCSI_REQUEST_BLOCK srb;
8279     KIRQL currentIrql;
8280 
8281     lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
8282 
8283     //
8284     // we raise irql separately so we're not swapped out or suspended
8285     // while holding the release queue irp in this routine.  this lets
8286     // us release the spin lock before lowering irql.
8287     //
8288 
8289     KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
8290 
8291     KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8292 
8293     //
8294     // make sure that if they passed us an irp, it matches our allocated irp.
8295     //
8296 
8297     ASSERT((ReleaseQueueIrp == NULL) ||
8298            (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
8299 
8300     //
8301     // ASSERT that we've already allocated this. (should not occur)
8302     // try to allocate it anyways, then finally bugcheck if
8303     // there's still no memory...
8304     //
8305 
8306     ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
8307     if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8308         ClasspAllocateReleaseQueueIrp(fdoExtension);
8309     }
8310     if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
8311         KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
8312     }
8313 
8314     if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
8315 
8316         //
8317         // Someone is already using the irp - just set the flag to indicate that
8318         // we need to release the queue again.
8319         //
8320 
8321         fdoExtension->ReleaseQueueNeeded = TRUE;
8322         KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8323         KeLowerIrql(currentIrql);
8324         return;
8325 
8326     }
8327 
8328     //
8329     // Mark that there is a release queue in progress and drop the spinlock.
8330     //
8331 
8332     fdoExtension->ReleaseQueueInProgress = TRUE;
8333     if (ReleaseQueueIrp) {
8334         irp = ReleaseQueueIrp;
8335     } else {
8336         irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
8337     }
8338     srb = &(fdoExtension->ReleaseQueueSrb);
8339 
8340     KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
8341 
8342     ASSERT(irp != NULL);
8343 
8344     irpStack = IoGetNextIrpStackLocation(irp);
8345 
8346     irpStack->MajorFunction = IRP_MJ_SCSI;
8347 
8348     srb->OriginalRequest = irp;
8349 
8350     //
8351     // Store the SRB address in next stack for port driver.
8352     //
8353 
8354     irpStack->Parameters.Scsi.Srb = srb;
8355 
8356     //
8357     // If this device is removable then flush the queue.  This will also
8358     // release it.
8359     //
8360 
8361     if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
8362        srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
8363     }
8364     else {
8365        srb->Function = SRB_FUNCTION_RELEASE_QUEUE;
8366     }
8367 
8368     ClassAcquireRemoveLock(Fdo, irp);
8369 
8370     IoSetCompletionRoutine(irp,
8371                            ClassReleaseQueueCompletion,
8372                            Fdo,
8373                            TRUE,
8374                            TRUE,
8375                            TRUE);
8376 
8377     IoCallDriver(lowerDevice, irp);
8378 
8379     KeLowerIrql(currentIrql);
8380 
8381     return;
8382 
8383 } // end ClassReleaseQueue()
8384 
8385 /*++////////////////////////////////////////////////////////////////////////////
8386 
8387 ClassReleaseQueueCompletion()
8388 
8389 Routine Description:
8390 
8391     This routine is called when an asynchronous I/O request
8392     which was issued by the class driver completes.  Examples of such requests
8393     are release queue or START UNIT. This routine releases the queue if
8394     necessary.  It then frees the context and the IRP.
8395 
8396 Arguments:
8397 
8398     DeviceObject - The device object for the logical unit; however since this
8399         is the top stack location the value is NULL.
8400 
8401     Irp - Supplies a pointer to the Irp to be processed.
8402 
8403     Context - Supplies the context to be used to process this request.
8404 
8405 Return Value:
8406 
8407     None.
8408 
8409 --*/
8410 NTSTATUS
8411 NTAPI
8412 ClassReleaseQueueCompletion(
8413     PDEVICE_OBJECT DeviceObject,
8414     PIRP Irp,
8415     PVOID Context
8416     )
8417 {
8418     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8419     KIRQL oldIrql;
8420 
8421     BOOLEAN releaseQueueNeeded;
8422 
8423     DeviceObject = Context;
8424 
8425     fdoExtension = DeviceObject->DeviceExtension;
8426 
8427     ClassReleaseRemoveLock(DeviceObject, Irp);
8428 
8429     //
8430     // Grab the spinlock and clear the release queue in progress flag so others
8431     // can run.  Save (and clear) the state of the release queue needed flag
8432     // so that we can issue a new release queue outside the spinlock.
8433     //
8434 
8435     KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
8436 
8437     releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
8438 
8439     fdoExtension->ReleaseQueueNeeded = FALSE;
8440     fdoExtension->ReleaseQueueInProgress = FALSE;
8441 
8442     KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
8443 
8444     //
8445     // If we need a release queue then issue one now.  Another processor may
8446     // have already started one in which case we'll try to issue this one after
8447     // it is done - but we should never recurse more than one deep.
8448     //
8449 
8450     if(releaseQueueNeeded) {
8451         ClasspReleaseQueue(DeviceObject, Irp);
8452     }
8453 
8454     //
8455     // Indicate the I/O system should stop processing the Irp completion.
8456     //
8457 
8458     return STATUS_MORE_PROCESSING_REQUIRED;
8459 
8460 } // ClassAsynchronousCompletion()
8461 
8462 /*++////////////////////////////////////////////////////////////////////////////
8463 
8464 ClassAcquireChildLock()
8465 
8466 Routine Description:
8467 
8468     This routine acquires the lock protecting children PDOs.  It may be
8469     acquired recursively by the same thread, but must be release by the
8470     thread once for each acquisition.
8471 
8472 Arguments:
8473 
8474     FdoExtension - the device whose child list is protected.
8475 
8476 Return Value:
8477 
8478     None
8479 
8480 --*/
8481 VOID
8482 NTAPI
8483 ClassAcquireChildLock(
8484     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8485     )
8486 {
8487     PAGED_CODE();
8488 
8489     if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
8490         KeWaitForSingleObject(&FdoExtension->ChildLock,
8491                               Executive, KernelMode,
8492                               FALSE, NULL);
8493 
8494         ASSERT(FdoExtension->ChildLockOwner == NULL);
8495         ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
8496 
8497         FdoExtension->ChildLockOwner = KeGetCurrentThread();
8498     } else {
8499         ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
8500     }
8501 
8502     FdoExtension->ChildLockAcquisitionCount++;
8503     return;
8504 }
8505 
8506 /*++////////////////////////////////////////////////////////////////////////////
8507 
8508 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
8509 
8510 Routine Description:
8511 
8512     This routine releases the lock protecting children PDOs.  It must be
8513     called once for each time ClassAcquireChildLock was called.
8514 
8515 Arguments:
8516 
8517     FdoExtension - the device whose child list is protected
8518 
8519 Return Value:
8520 
8521     None.
8522 
8523 --*/
8524 VOID
8525 NTAPI
8526 ClassReleaseChildLock(
8527     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8528     )
8529 {
8530     ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
8531     ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
8532 
8533     FdoExtension->ChildLockAcquisitionCount -= 1;
8534 
8535     if(FdoExtension->ChildLockAcquisitionCount == 0) {
8536         FdoExtension->ChildLockOwner = NULL;
8537         KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
8538     }
8539 
8540     return;
8541 } // end ClassReleaseChildLock(
8542 
8543 /*++////////////////////////////////////////////////////////////////////////////
8544 
8545 ClassAddChild()
8546 
8547 Routine Description:
8548 
8549     This routine will insert a new child into the head of the child list.
8550 
8551 Arguments:
8552 
8553     Parent - the child's parent (contains the head of the list)
8554     Child - the child to be inserted.
8555     AcquireLock - whether the child lock should be acquired (TRUE) or whether
8556                   it's already been acquired by or on behalf of the caller
8557                   (FALSE).
8558 
8559 Return Value:
8560 
8561     None.
8562 
8563 --*/
8564 VOID
8565 NTAPI
8566 ClassAddChild(
8567     IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
8568     IN PPHYSICAL_DEVICE_EXTENSION Child,
8569     IN BOOLEAN AcquireLock
8570     )
8571 {
8572     if(AcquireLock) {
8573         ClassAcquireChildLock(Parent);
8574     }
8575 
8576     #if DBG
8577         //
8578         // Make sure this child's not already in the list.
8579         //
8580         {
8581             PPHYSICAL_DEVICE_EXTENSION testChild;
8582 
8583             for (testChild = Parent->CommonExtension.ChildList;
8584                  testChild != NULL;
8585                  testChild = testChild->CommonExtension.ChildList) {
8586 
8587                 ASSERT(testChild != Child);
8588             }
8589         }
8590     #endif
8591 
8592     Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
8593     Parent->CommonExtension.ChildList = Child;
8594 
8595     if(AcquireLock) {
8596         ClassReleaseChildLock(Parent);
8597     }
8598     return;
8599 } // end ClassAddChild()
8600 
8601 /*++////////////////////////////////////////////////////////////////////////////
8602 
8603 ClassRemoveChild()
8604 
8605 Routine Description:
8606 
8607     This routine will remove a child from the child list.
8608 
8609 Arguments:
8610 
8611     Parent - the parent to be removed from.
8612 
8613     Child - the child to be removed or NULL if the first child should be
8614             removed.
8615 
8616     AcquireLock - whether the child lock should be acquired (TRUE) or whether
8617                   it's already been acquired by or on behalf of the caller
8618                   (FALSE).
8619 
8620 Return Value:
8621 
8622     A pointer to the child which was removed or NULL if no such child could
8623     be found in the list (or if Child was NULL but the list is empty).
8624 
8625 --*/
8626 PPHYSICAL_DEVICE_EXTENSION
8627 NTAPI
8628 ClassRemoveChild(
8629     IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
8630     IN PPHYSICAL_DEVICE_EXTENSION Child,
8631     IN BOOLEAN AcquireLock
8632     )
8633 {
8634     if(AcquireLock) {
8635         ClassAcquireChildLock(Parent);
8636     }
8637 
8638     TRY {
8639         PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
8640 
8641         //
8642         // If the list is empty then bail out now.
8643         //
8644 
8645         if(Parent->CommonExtension.ChildList == NULL) {
8646             Child = NULL;
8647             LEAVE;
8648         }
8649 
8650         //
8651         // If the caller specified a child then find the child object before
8652         // it.  If none was specified then the FDO is the child object before
8653         // the one we want to remove.
8654         //
8655 
8656         if(Child != NULL) {
8657 
8658             //
8659             // Scan through the child list to find the entry which points to
8660             // this one.
8661             //
8662 
8663             do {
8664                 ASSERT(previousChild != &Child->CommonExtension);
8665 
8666                 if(previousChild->ChildList == Child) {
8667                     break;
8668                 }
8669 
8670                 previousChild = &previousChild->ChildList->CommonExtension;
8671             } while(previousChild != NULL);
8672 
8673             if(previousChild == NULL) {
8674                 Child = NULL;
8675                 LEAVE;
8676             }
8677         }
8678 
8679         //
8680         // Save the next child away then unlink it from the list.
8681         //
8682 
8683         Child = previousChild->ChildList;
8684         previousChild->ChildList = Child->CommonExtension.ChildList;
8685         Child->CommonExtension.ChildList = NULL;
8686 
8687     } FINALLY {
8688         if(AcquireLock) {
8689             ClassReleaseChildLock(Parent);
8690         }
8691     }
8692     return Child;
8693 } // end ClassRemoveChild()
8694 
8695 /*++
8696 
8697     ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
8698 
8699 --*/
8700 VOID
8701 NTAPI
8702 ClasspRetryRequestDpc(
8703     IN PKDPC Dpc,
8704     IN PVOID Context,
8705     IN PVOID Arg1,
8706     IN PVOID Arg2
8707     )
8708 {
8709     PDEVICE_OBJECT deviceObject = Context;
8710     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8711     PCOMMON_DEVICE_EXTENSION commonExtension;
8712     PCLASS_PRIVATE_FDO_DATA fdoData;
8713     PCLASS_RETRY_INFO retryList;
8714     KIRQL irql;
8715 
8716 
8717     commonExtension = deviceObject->DeviceExtension;
8718     ASSERT(commonExtension->IsFdo);
8719     fdoExtension = deviceObject->DeviceExtension;
8720     fdoData = fdoExtension->PrivateFdoData;
8721 
8722 
8723     KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
8724     {
8725         LARGE_INTEGER now;
8726         KeQueryTickCount(&now);
8727 
8728         //
8729         // if CurrentTick is less than now
8730         //      fire another DPC
8731         // else
8732         //      retry entire list
8733         // endif
8734         //
8735 
8736         if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
8737 
8738             ClasspRetryDpcTimer(fdoData);
8739             retryList = NULL;
8740 
8741         } else {
8742 
8743             retryList = fdoData->Retry.ListHead;
8744             fdoData->Retry.ListHead = NULL;
8745             fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
8746             fdoData->Retry.Tick.QuadPart  = (LONGLONG)0;
8747 
8748         }
8749     }
8750     KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
8751 
8752     while (retryList != NULL) {
8753 
8754         PIRP irp;
8755 
8756         irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
8757         DebugPrint((ClassDebugDelayedRetry, "ClassRetry:  -- %p\n", irp));
8758         retryList = retryList->Next;
8759         #if DBG
8760             irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
8761             irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
8762             irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
8763             irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
8764         #endif
8765 
8766         IoCallDriver(commonExtension->LowerDeviceObject, irp);
8767 
8768     }
8769     return;
8770 
8771 } // end ClasspRetryRequestDpc()
8772 
8773 /*++
8774 
8775     ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
8776 
8777 --*/
8778 VOID
8779 NTAPI
8780 ClassRetryRequest(
8781     IN PDEVICE_OBJECT SelfDeviceObject,
8782     IN PIRP           Irp,
8783     IN LARGE_INTEGER  TimeDelta100ns // in 100ns units
8784     )
8785 {
8786     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
8787     PCLASS_PRIVATE_FDO_DATA fdoData;
8788     PCLASS_RETRY_INFO  retryInfo;
8789     //PCLASS_RETRY_INFO *previousNext;
8790     LARGE_INTEGER      delta;
8791     KIRQL irql;
8792 
8793     //
8794     // this checks we aren't destroying irps
8795     //
8796     ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
8797 
8798     fdoExtension = SelfDeviceObject->DeviceExtension;
8799     fdoData = fdoExtension->PrivateFdoData;
8800 
8801     if (!fdoExtension->CommonExtension.IsFdo) {
8802 
8803         //
8804         // this debug print/assertion should ALWAYS be investigated.
8805         // ClassRetryRequest can currently only be used by FDO's
8806         //
8807 
8808         DebugPrint((ClassDebugError, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
8809         ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
8810         return;
8811 
8812     }
8813 
8814     if (TimeDelta100ns.QuadPart < 0) {
8815         ASSERT(!"ClassRetryRequest - must use positive delay");
8816         TimeDelta100ns.QuadPart *= -1;
8817     }
8818 
8819     //
8820     // prepare what we can out of the loop
8821     //
8822 
8823     retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
8824     RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
8825 
8826     delta.QuadPart = (TimeDelta100ns.QuadPart / fdoData->Retry.Granularity);
8827     if (TimeDelta100ns.QuadPart % fdoData->Retry.Granularity) {
8828         delta.QuadPart ++; // round up to next tick
8829     }
8830     if (delta.QuadPart == (LONGLONG)0) {
8831         delta.QuadPart = MINIMUM_RETRY_UNITS;
8832     }
8833 
8834     //
8835     // now determine if we should fire another DPC or not
8836     //
8837 
8838     KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
8839 
8840     //
8841     // always add request to the list
8842     //
8843 
8844     retryInfo->Next = fdoData->Retry.ListHead;
8845     fdoData->Retry.ListHead = retryInfo;
8846 
8847     if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
8848 
8849         DebugPrint((ClassDebugDelayedRetry, "ClassRetry: +++ %p\n", Irp));
8850 
8851         //
8852         // must be exactly one item on list
8853         //
8854 
8855         ASSERT(fdoData->Retry.ListHead       != NULL);
8856         ASSERT(fdoData->Retry.ListHead->Next == NULL);
8857 
8858         //
8859         // if currentDelta is zero, always fire a DPC
8860         //
8861 
8862         KeQueryTickCount(&fdoData->Retry.Tick);
8863         fdoData->Retry.Tick.QuadPart  += delta.QuadPart;
8864         fdoData->Retry.Delta.QuadPart  = delta.QuadPart;
8865         ClasspRetryDpcTimer(fdoData);
8866 
8867     } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
8868 
8869         //
8870         // if delta is greater than the list's current delta,
8871         // increase the DPC handling time by difference
8872         // and update the delta to new larger value
8873         // allow the DPC to re-fire itself if needed
8874         //
8875 
8876         DebugPrint((ClassDebugDelayedRetry, "ClassRetry:  ++ %p\n", Irp));
8877 
8878         //
8879         // must be at least two items on list
8880         //
8881 
8882         ASSERT(fdoData->Retry.ListHead       != NULL);
8883         ASSERT(fdoData->Retry.ListHead->Next != NULL);
8884 
8885         fdoData->Retry.Tick.QuadPart  -= fdoData->Retry.Delta.QuadPart;
8886         fdoData->Retry.Tick.QuadPart  += delta.QuadPart;
8887 
8888         fdoData->Retry.Delta.QuadPart  = delta.QuadPart;
8889 
8890     } else {
8891 
8892         //
8893         // just inserting it on the list was enough
8894         //
8895 
8896         DebugPrint((ClassDebugDelayedRetry, "ClassRetry:  ++ %p\n", Irp));
8897 
8898     }
8899 
8900 
8901     KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
8902 
8903 
8904 } // end ClassRetryRequest()
8905 
8906 /*++
8907 
8908     ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
8909 
8910 --*/
8911 VOID
8912 NTAPI
8913 ClasspRetryDpcTimer(
8914     IN PCLASS_PRIVATE_FDO_DATA FdoData
8915     )
8916 {
8917     LARGE_INTEGER fire;
8918 
8919     ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
8920     ASSERT(FdoData->Retry.ListHead      != NULL);  // never fire an empty list
8921 
8922     //
8923     // fire == (CurrentTick - now) * (100ns per tick)
8924     //
8925     // NOTE: Overflow is nearly impossible and is ignored here
8926     //
8927 
8928     KeQueryTickCount(&fire);
8929     fire.QuadPart =  FdoData->Retry.Tick.QuadPart - fire.QuadPart;
8930     fire.QuadPart *= FdoData->Retry.Granularity;
8931 
8932     //
8933     // fire is now multiples of 100ns until should fire the timer.
8934     // if timer should already have expired, or would fire too quickly,
8935     // fire it in some arbitrary number of ticks to prevent infinitely
8936     // recursing.
8937     //
8938 
8939     if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
8940         fire.QuadPart = MINIMUM_RETRY_UNITS;
8941     }
8942 
8943     DebugPrint((ClassDebugDelayedRetry,
8944                 "ClassRetry: ======= %I64x ticks\n",
8945                 fire.QuadPart));
8946 
8947     //
8948     // must use negative to specify relative time to fire
8949     //
8950 
8951     fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
8952 
8953     //
8954     // set the timer, since this is the first addition
8955     //
8956 
8957     KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
8958 
8959     return;
8960 } // end ClasspRetryDpcTimer()
8961 
8962 NTSTATUS
8963 NTAPI
8964 ClasspInitializeHotplugInfo(
8965     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
8966     )
8967 {
8968     PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
8969     DEVICE_REMOVAL_POLICY deviceRemovalPolicy;
8970     NTSTATUS status;
8971     ULONG resultLength = 0;
8972     ULONG writeCacheOverride;
8973 
8974     PAGED_CODE();
8975 
8976     //
8977     // start with some default settings
8978     //
8979     RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
8980 
8981     //
8982     // set the size (aka version)
8983     //
8984 
8985     fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
8986 
8987     //
8988     // set if the device has removable media
8989     //
8990 
8991     if (FdoExtension->DeviceDescriptor->RemovableMedia) {
8992         fdoData->HotplugInfo.MediaRemovable = TRUE;
8993     } else {
8994         fdoData->HotplugInfo.MediaRemovable = FALSE;
8995     }
8996 
8997     //
8998     // this refers to devices which, for reasons not yet understood,
8999     // do not fail PREVENT_MEDIA_REMOVAL requests even though they
9000     // have no way to lock the media into the drive.  this allows
9001     // the filesystems to turn off delayed-write caching for these
9002     // devices as well.
9003     //
9004 
9005     if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
9006                   FDO_HACK_CANNOT_LOCK_MEDIA)) {
9007         fdoData->HotplugInfo.MediaHotplug = TRUE;
9008     } else {
9009         fdoData->HotplugInfo.MediaHotplug = FALSE;
9010     }
9011 
9012 
9013     //
9014     // Look into the registry to  see if the user has  chosen
9015     // to override the default setting for the removal policy
9016     //
9017 
9018     RtlZeroMemory(&deviceRemovalPolicy, sizeof(DEVICE_REMOVAL_POLICY));
9019 
9020     ClassGetDeviceParameter(FdoExtension,
9021                             CLASSP_REG_SUBKEY_NAME,
9022                             CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
9023                             (PULONG)&deviceRemovalPolicy);
9024 
9025     if (deviceRemovalPolicy == 0)
9026     {
9027         //
9028         // Query the default removal policy from the kernel
9029         //
9030 
9031         status = IoGetDeviceProperty(FdoExtension->LowerPdo,
9032                                      DevicePropertyRemovalPolicy,
9033                                      sizeof(DEVICE_REMOVAL_POLICY),
9034                                      (PVOID)&deviceRemovalPolicy,
9035                                      &resultLength);
9036         if (!NT_SUCCESS(status))
9037         {
9038             return status;
9039         }
9040 
9041         if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
9042         {
9043             return STATUS_UNSUCCESSFUL;
9044         }
9045     }
9046 
9047     //
9048     // use this info to set the DeviceHotplug setting
9049     // don't rely on DeviceCapabilities, since it can't properly
9050     // determine device relations, etc.  let the kernel figure this
9051     // stuff out instead.
9052     //
9053 
9054     if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
9055         fdoData->HotplugInfo.DeviceHotplug = TRUE;
9056     } else {
9057         fdoData->HotplugInfo.DeviceHotplug = FALSE;
9058     }
9059 
9060     //
9061     // this refers to the *filesystem* caching, but has to be included
9062     // here since it's a per-device setting.  this may change to be
9063     // stored by the system in the future.
9064     //
9065 
9066     writeCacheOverride = FALSE;
9067     ClassGetDeviceParameter(FdoExtension,
9068                             CLASSP_REG_SUBKEY_NAME,
9069                             CLASSP_REG_WRITE_CACHE_VALUE_NAME,
9070                             &writeCacheOverride);
9071 
9072     if (writeCacheOverride) {
9073         fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
9074     } else {
9075         fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
9076     }
9077 
9078     return STATUS_SUCCESS;
9079 }
9080 
9081 VOID
9082 NTAPI
9083 ClasspScanForClassHacks(
9084     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
9085     IN ULONG_PTR Data
9086     )
9087 {
9088     PAGED_CODE();
9089 
9090     //
9091     // remove invalid flags and save
9092     //
9093 
9094     CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
9095     SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
9096     return;
9097 }
9098 
9099 VOID
9100 NTAPI
9101 ClasspScanForSpecialInRegistry(
9102     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
9103     )
9104 {
9105     HANDLE             deviceParameterHandle; // device instance key
9106     HANDLE             classParameterHandle; // classpnp subkey
9107     OBJECT_ATTRIBUTES  objectAttributes;
9108     UNICODE_STRING     subkeyName;
9109     NTSTATUS           status;
9110 
9111     //
9112     // seeded in the ENUM tree by ClassInstaller
9113     //
9114     ULONG deviceHacks;
9115     RTL_QUERY_REGISTRY_TABLE queryTable[2]; // null terminated array
9116 
9117     PAGED_CODE();
9118 
9119     deviceParameterHandle = NULL;
9120     classParameterHandle = NULL;
9121     deviceHacks = 0;
9122 
9123     status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
9124                                      PLUGPLAY_REGKEY_DEVICE,
9125                                      KEY_WRITE,
9126                                      &deviceParameterHandle
9127                                      );
9128 
9129     if (!NT_SUCCESS(status)) {
9130         goto cleanupScanForSpecial;
9131     }
9132 
9133     RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
9134     InitializeObjectAttributes(&objectAttributes,
9135                                &subkeyName,
9136                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
9137                                deviceParameterHandle,
9138                                NULL
9139                                );
9140 
9141     status = ZwOpenKey( &classParameterHandle,
9142                         KEY_READ,
9143                         &objectAttributes
9144                         );
9145 
9146     if (!NT_SUCCESS(status)) {
9147         goto cleanupScanForSpecial;
9148     }
9149 
9150     //
9151     // Zero out the memory
9152     //
9153 
9154     RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
9155 
9156     //
9157     // Setup the structure to read
9158     //
9159 
9160     queryTable[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
9161     queryTable[0].Name          = CLASSP_REG_HACK_VALUE_NAME;
9162     queryTable[0].EntryContext  = &deviceHacks;
9163     queryTable[0].DefaultType   = REG_DWORD;
9164     queryTable[0].DefaultData   = &deviceHacks;
9165     queryTable[0].DefaultLength = 0;
9166 
9167     //
9168     // read values
9169     //
9170 
9171     status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
9172                                     (PWSTR)classParameterHandle,
9173                                     &queryTable[0],
9174                                     NULL,
9175                                     NULL
9176                                     );
9177     if (!NT_SUCCESS(status)) {
9178         goto cleanupScanForSpecial;
9179     }
9180 
9181     //
9182     // remove unknown values and save...
9183     //
9184 
9185     KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
9186                "Classpnp => ScanForSpecial: HackFlags %#08x\n",
9187                deviceHacks));
9188 
9189     CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
9190     SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
9191 
9192 
9193 cleanupScanForSpecial:
9194 
9195     if (deviceParameterHandle) {
9196         ZwClose(deviceParameterHandle);
9197     }
9198 
9199     if (classParameterHandle) {
9200         ZwClose(classParameterHandle);
9201     }
9202 
9203     //
9204     // we should modify the system hive to include another key for us to grab
9205     // settings from.  in this case:  Classpnp\HackFlags
9206     //
9207     // the use of a DWORD value for the HackFlags allows 32 hacks w/o
9208     // significant use of the registry, and also reduces OEM exposure.
9209     //
9210     // definition of bit flags:
9211     //   0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
9212     //                 cannot actually prevent removal.
9213     //   0x00000002 -- Device hard-hangs or times out for GESN requests.
9214     //   0xfffffffc -- Currently reserved, may be used later.
9215     //
9216 
9217     return;
9218 }
9219