ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

class.c
Go to the documentation of this file.
00001 /*++
00002 
00003 Copyright (C) Microsoft Corporation, 1991 - 1999
00004 
00005 Module Name:
00006 
00007     class.c
00008 
00009 Abstract:
00010 
00011     SCSI class driver routines
00012 
00013 Environment:
00014 
00015     kernel mode only
00016 
00017 Notes:
00018 
00019 
00020 Revision History:
00021 
00022 --*/
00023 
00024 #define CLASS_INIT_GUID 1
00025 #include "classp.h"
00026 #include "debug.h"
00027 
00028 #ifdef ALLOC_PRAGMA
00029     #pragma alloc_text(INIT, DriverEntry)
00030     #pragma alloc_text(PAGE, ClassAddDevice)
00031     #pragma alloc_text(PAGE, ClassClaimDevice)
00032     #pragma alloc_text(PAGE, ClassCreateDeviceObject)
00033     #pragma alloc_text(PAGE, ClassDispatchPnp)
00034     #pragma alloc_text(PAGE, ClassGetDescriptor)
00035     #pragma alloc_text(PAGE, ClassGetPdoId)
00036     #pragma alloc_text(PAGE, ClassInitialize)
00037     #pragma alloc_text(PAGE, ClassInitializeEx)
00038     #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
00039     #pragma alloc_text(PAGE, ClassMarkChildMissing)
00040     #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
00041     #pragma alloc_text(PAGE, ClassModeSense)
00042     #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
00043     #pragma alloc_text(PAGE, ClassPnpStartDevice)
00044     #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
00045     #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
00046     #pragma alloc_text(PAGE, ClassRemoveDevice)
00047     #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
00048     #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
00049     #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
00050     #pragma alloc_text(PAGE, ClassUnload)
00051     #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
00052     #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
00053     #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
00054     #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
00055     #pragma alloc_text(PAGE, ClasspScanForClassHacks)
00056     #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
00057 #endif
00058 
00059 ULONG ClassPnpAllowUnload = TRUE;
00060 
00061 
00062 #define FirstDriveLetter 'C'
00063 #define LastDriveLetter  'Z'
00064 
00065 
00066 
00067 /*++////////////////////////////////////////////////////////////////////////////
00068 
00069 DriverEntry()
00070 
00071 Routine Description:
00072 
00073     Temporary entry point needed to initialize the class system dll.
00074     It doesn't do anything.
00075 
00076 Arguments:
00077 
00078     DriverObject - Pointer to the driver object created by the system.
00079 
00080 Return Value:
00081 
00082    STATUS_SUCCESS
00083 
00084 --*/
00085 NTSTATUS
00086 NTAPI
00087 DriverEntry(
00088     IN PDRIVER_OBJECT DriverObject,
00089     IN PUNICODE_STRING RegistryPath
00090     )
00091 {
00092     return STATUS_SUCCESS;
00093 }
00094 
00095 
00096 
00097 
00098 /*++////////////////////////////////////////////////////////////////////////////
00099 
00100 ClassInitialize()
00101 
00102 Routine Description:
00103 
00104     This routine is called by a class driver during its
00105     DriverEntry routine to initialize the driver.
00106 
00107 Arguments:
00108 
00109     Argument1          - Driver Object.
00110     Argument2          - Registry Path.
00111     InitializationData - Device-specific driver's initialization data.
00112 
00113 Return Value:
00114 
00115     A valid return code for a DriverEntry routine.
00116 
00117 --*/
00118 ULONG
00119 ClassInitialize(
00120     IN  PVOID            Argument1,
00121     IN  PVOID            Argument2,
00122     IN  PCLASS_INIT_DATA InitializationData
00123     )
00124 {
00125     PDRIVER_OBJECT  DriverObject = Argument1;
00126     PUNICODE_STRING RegistryPath = Argument2;
00127 
00128     PCLASS_DRIVER_EXTENSION driverExtension;
00129 
00130     NTSTATUS        status;
00131 
00132     PAGED_CODE();
00133     
00134     DebugPrint((3,"\n\nSCSI Class Driver\n"));
00135 
00136     ClasspInitializeDebugGlobals();
00137 
00138     //
00139     // Validate the length of this structure. This is effectively a
00140     // version check.
00141     //
00142 
00143     if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
00144 
00145         //
00146         // This DebugPrint is to help third-party driver writers
00147         //
00148 
00149         DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
00150         return (ULONG) STATUS_REVISION_MISMATCH;
00151     }
00152 
00153     //
00154     // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
00155     // are not required entry points.
00156     //
00157 
00158     if ((!InitializationData->FdoData.ClassDeviceControl) ||
00159         (!((InitializationData->FdoData.ClassReadWriteVerification) ||
00160            (InitializationData->ClassStartIo))) ||
00161         (!InitializationData->ClassAddDevice) ||
00162         (!InitializationData->FdoData.ClassStartDevice)) {
00163 
00164         //
00165         // This DebugPrint is to help third-party driver writers
00166         //
00167 
00168         DebugPrint((0,
00169             "ClassInitialize: Class device-specific driver missing required "
00170             "FDO entry\n"));
00171 
00172         return (ULONG) STATUS_REVISION_MISMATCH;
00173     }
00174 
00175     if ((InitializationData->ClassEnumerateDevice) &&
00176         ((!InitializationData->PdoData.ClassDeviceControl) ||
00177          (!InitializationData->PdoData.ClassStartDevice) ||
00178          (!((InitializationData->PdoData.ClassReadWriteVerification) ||
00179             (InitializationData->ClassStartIo))))) {
00180 
00181         //
00182         // This DebugPrint is to help third-party driver writers
00183         //
00184 
00185         DebugPrint((0, "ClassInitialize: Class device-specific missing "
00186                        "required PDO entry\n"));
00187 
00188         return (ULONG) STATUS_REVISION_MISMATCH;
00189     }
00190 
00191     if((InitializationData->FdoData.ClassStopDevice == NULL) ||
00192         ((InitializationData->ClassEnumerateDevice != NULL) &&
00193          (InitializationData->PdoData.ClassStopDevice == NULL))) {
00194 
00195         //
00196         // This DebugPrint is to help third-party driver writers
00197         //
00198 
00199         DebugPrint((0, "ClassInitialize: Class device-specific missing "
00200                        "required PDO entry\n"));
00201         ASSERT(FALSE);
00202         return (ULONG) STATUS_REVISION_MISMATCH;
00203     }
00204 
00205     //
00206     // Setup the default power handlers if the class driver didn't provide
00207     // any.
00208     //
00209 
00210     if(InitializationData->FdoData.ClassPowerDevice == NULL) {
00211         InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
00212     }
00213 
00214     if((InitializationData->ClassEnumerateDevice != NULL) &&
00215        (InitializationData->PdoData.ClassPowerDevice == NULL)) {
00216         InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
00217     }
00218 
00219     //
00220     // warn that unload is not supported
00221     //
00222     // ISSUE-2000/02/03-peterwie
00223     // We should think about making this a fatal error.
00224     //
00225 
00226     if(InitializationData->ClassUnload == NULL) {
00227 
00228         //
00229         // This DebugPrint is to help third-party driver writers
00230         //
00231 
00232         DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
00233                     RegistryPath));
00234     }
00235 
00236     //
00237     // Create an extension for the driver object
00238     //
00239 
00240     status = IoAllocateDriverObjectExtension(DriverObject,
00241                                              CLASS_DRIVER_EXTENSION_KEY,
00242                                              sizeof(CLASS_DRIVER_EXTENSION),
00243                                              &driverExtension);
00244 
00245     if(NT_SUCCESS(status)) {
00246 
00247         //
00248         // Copy the registry path into the driver extension so we can use it later
00249         //
00250 
00251         driverExtension->RegistryPath.Length = RegistryPath->Length;
00252         driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
00253 
00254         driverExtension->RegistryPath.Buffer =
00255             ExAllocatePoolWithTag(PagedPool,
00256                                   RegistryPath->MaximumLength,
00257                                   '1CcS');
00258 
00259         if(driverExtension->RegistryPath.Buffer == NULL) {
00260 
00261             status = STATUS_INSUFFICIENT_RESOURCES;
00262             return status;
00263         }
00264 
00265         RtlCopyUnicodeString(
00266             &(driverExtension->RegistryPath),
00267             RegistryPath);
00268 
00269         //
00270         // Copy the initialization data into the driver extension so we can reuse
00271         // it during our add device routine
00272         //
00273 
00274         RtlCopyMemory(
00275             &(driverExtension->InitData),
00276             InitializationData,
00277             sizeof(CLASS_INIT_DATA));
00278 
00279         driverExtension->DeviceCount = 0;
00280 
00281     } else if (status == STATUS_OBJECT_NAME_COLLISION) {
00282 
00283         //
00284         // The extension already exists - get a pointer to it
00285         //
00286 
00287         driverExtension = IoGetDriverObjectExtension(DriverObject,
00288                                                      CLASS_DRIVER_EXTENSION_KEY);
00289 
00290         ASSERT(driverExtension != NULL);
00291 
00292     } else {
00293 
00294         DebugPrint((1, "ClassInitialize: Class driver extension could not be "
00295                        "allocated %lx\n", status));
00296         return status;
00297     }
00298 
00299     //
00300     // Update driver object with entry points.
00301     //
00302 
00303     DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreateClose;
00304     DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassCreateClose;
00305     DriverObject->MajorFunction[IRP_MJ_READ] = ClassReadWrite;
00306     DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassReadWrite;
00307     DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassInternalIoControl;
00308     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch;
00309     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassShutdownFlush;
00310     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush;
00311     DriverObject->MajorFunction[IRP_MJ_PNP] = ClassDispatchPnp;
00312     DriverObject->MajorFunction[IRP_MJ_POWER] = ClassDispatchPower;
00313     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl;
00314 
00315     if (InitializationData->ClassStartIo) {
00316         DriverObject->DriverStartIo = ClasspStartIo;
00317     }
00318 
00319     if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) {
00320         DriverObject->DriverUnload = ClassUnload;
00321     } else {
00322         DriverObject->DriverUnload = NULL;
00323     }
00324 
00325     DriverObject->DriverExtension->AddDevice = ClassAddDevice;
00326 
00327     DbgPrint("Driver is ready to go\n");
00328     status = STATUS_SUCCESS;
00329     return status;
00330 } // end ClassInitialize()
00331 
00332 /*++////////////////////////////////////////////////////////////////////////////
00333 
00334 ClassInitializeEx()
00335 
00336 Routine Description:
00337 
00338     This routine is allows the caller to do any extra initialization or
00339     setup that is not done in ClassInitialize. The operation is
00340     controlled by the GUID that is passed and the contents of the Data
00341     parameter is dependent upon the GUID.
00342 
00343     This is the list of supported operations:
00344 
00345     Guid - GUID_CLASSPNP_QUERY_REGINFOEX
00346     Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
00347 
00348         Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
00349         callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
00350         former callback allows the driver to specify the name of the
00351         mof resource.
00352 
00353 Arguments:
00354 
00355     DriverObject
00356     Guid
00357     Data
00358 
00359 Return Value:
00360 
00361     Status Code
00362 
00363 --*/
00364 ULONG
00365 ClassInitializeEx(
00366     IN  PDRIVER_OBJECT   DriverObject,
00367     IN  LPGUID           Guid,
00368     IN  PVOID            Data
00369     )
00370 {
00371     PCLASS_DRIVER_EXTENSION driverExtension;
00372 
00373     NTSTATUS        status;
00374 
00375     PAGED_CODE();
00376 
00377     driverExtension = IoGetDriverObjectExtension( DriverObject,
00378                                                   CLASS_DRIVER_EXTENSION_KEY
00379                                                   );
00380     if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
00381     {
00382         PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
00383 
00384         //
00385         // Indicate the device supports PCLASS_QUERY_REGINFO_EX
00386         // callback instead of PCLASS_QUERY_REGINFO callback.
00387         //
00388         List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
00389 
00390         if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
00391         {
00392             driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
00393             driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
00394             status = STATUS_SUCCESS;
00395         } else {
00396             status = STATUS_INVALID_PARAMETER;
00397         }
00398     } else {
00399         status = STATUS_NOT_SUPPORTED;
00400     }
00401 
00402     return(status);
00403 
00404 } // end ClassInitializeEx()
00405 
00406 /*++////////////////////////////////////////////////////////////////////////////
00407 
00408 ClassUnload()
00409 
00410 Routine Description:
00411 
00412     called when there are no more references to the driver.  this allows
00413     drivers to be updated without rebooting.
00414 
00415 Arguments:
00416 
00417     DriverObject - a pointer to the driver object that is being unloaded
00418 
00419 Status:
00420 
00421 --*/
00422 VOID
00423 ClassUnload(
00424     IN PDRIVER_OBJECT DriverObject
00425     )
00426 {
00427     PCLASS_DRIVER_EXTENSION driverExtension;
00428     NTSTATUS status;
00429 
00430     PAGED_CODE();
00431 
00432     ASSERT( DriverObject->DeviceObject == NULL );
00433 
00434     driverExtension = IoGetDriverObjectExtension( DriverObject,
00435                                                   CLASS_DRIVER_EXTENSION_KEY
00436                                                   );
00437 
00438     ASSERT(driverExtension != NULL);
00439     ASSERT(driverExtension->RegistryPath.Buffer != NULL);
00440     ASSERT(driverExtension->InitData.ClassUnload != NULL);
00441 
00442     DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
00443                 &driverExtension->RegistryPath));
00444 
00445     //
00446     // attempt to process the driver's unload routine first.
00447     //
00448 
00449     driverExtension->InitData.ClassUnload(DriverObject);
00450 
00451     //
00452     // free own allocated resources and return
00453     //
00454 
00455     ExFreePool( driverExtension->RegistryPath.Buffer );
00456     driverExtension->RegistryPath.Buffer = NULL;
00457     driverExtension->RegistryPath.Length = 0;
00458     driverExtension->RegistryPath.MaximumLength = 0;
00459 
00460     return;
00461 } // end ClassUnload()
00462 
00463 /*++////////////////////////////////////////////////////////////////////////////
00464 
00465 ClassAddDevice()
00466 
00467 Routine Description:
00468 
00469     SCSI class driver add device routine.  This is called by pnp when a new
00470     physical device come into being.
00471 
00472     This routine will call out to the class driver to verify that it should
00473     own this device then will create and attach a device object and then hand
00474     it to the driver to initialize and create symbolic links
00475 
00476 Arguments:
00477 
00478     DriverObject - a pointer to the driver object that this is being created for
00479     PhysicalDeviceObject - a pointer to the physical device object
00480 
00481 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
00482     STATUS_SUCCESS if the creation and attachment was successful
00483     status of device creation and initialization
00484 
00485 --*/
00486 NTSTATUS
00487 ClassAddDevice(
00488     IN PDRIVER_OBJECT DriverObject,
00489     IN PDEVICE_OBJECT PhysicalDeviceObject
00490     )
00491 {
00492     PCLASS_DRIVER_EXTENSION driverExtension =
00493         IoGetDriverObjectExtension(DriverObject,
00494                                    CLASS_DRIVER_EXTENSION_KEY);
00495 
00496     NTSTATUS status;
00497 
00498     PAGED_CODE();
00499 
00500     DbgPrint("got a device\n");
00501     status = driverExtension->InitData.ClassAddDevice(DriverObject,
00502                                                       PhysicalDeviceObject);
00503     return status;
00504 } // end ClassAddDevice()
00505 
00506 /*++////////////////////////////////////////////////////////////////////////////
00507 
00508 ClassDispatchPnp()
00509 
00510 Routine Description:
00511 
00512     Storage class driver pnp routine.  This is called by the io system when
00513     a PNP request is sent to the device.
00514 
00515 Arguments:
00516 
00517     DeviceObject - pointer to the device object
00518 
00519     Irp - pointer to the io request packet
00520 
00521 Return Value:
00522 
00523     status
00524 
00525 --*/
00526 NTSTATUS
00527 ClassDispatchPnp(
00528     IN PDEVICE_OBJECT DeviceObject,
00529     IN PIRP Irp
00530     )
00531 {
00532     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
00533     BOOLEAN isFdo = commonExtension->IsFdo;
00534 
00535     PCLASS_DRIVER_EXTENSION driverExtension;
00536     PCLASS_INIT_DATA initData;
00537     PCLASS_DEV_INFO devInfo;
00538 
00539     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
00540     PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
00541 
00542     NTSTATUS status = Irp->IoStatus.Status;
00543     BOOLEAN completeRequest = TRUE;
00544     BOOLEAN lockReleased = FALSE;
00545 
00546     ULONG isRemoved;
00547 
00548     PAGED_CODE();
00549 
00550     //
00551     // Extract all the useful information out of the driver object
00552     // extension
00553     //
00554 
00555     driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
00556                                                  CLASS_DRIVER_EXTENSION_KEY);
00557     if (driverExtension){
00558 
00559         initData = &(driverExtension->InitData);
00560 
00561         if(isFdo) {
00562             devInfo = &(initData->FdoData);
00563         } else {
00564             devInfo = &(initData->PdoData);
00565         }
00566 
00567         isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
00568 
00569         DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
00570                        DeviceObject, Irp,
00571                        irpStack->MinorFunction,
00572                        isFdo ? "fdo" : "pdo",
00573                        DeviceObject));
00574         DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
00575                        DeviceObject, Irp,
00576                        commonExtension->PreviousState,
00577                        commonExtension->CurrentState));
00578 
00579         switch(irpStack->MinorFunction) {
00580 
00581             case IRP_MN_START_DEVICE: {
00582 
00583                 //
00584                 // if this is sent to the FDO we should forward it down the
00585                 // attachment chain before we start the FDO.
00586                 //
00587 
00588                 if (isFdo) {
00589                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
00590                 }
00591                 else {
00592                     status = STATUS_SUCCESS;
00593                 }
00594 
00595                 if (NT_SUCCESS(status)){
00596                     status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
00597                 }
00598 
00599                 break;
00600             }
00601 
00602 
00603             case IRP_MN_QUERY_DEVICE_RELATIONS: {
00604 
00605                 DEVICE_RELATION_TYPE type =
00606                     irpStack->Parameters.QueryDeviceRelations.Type;
00607 
00608                 PDEVICE_RELATIONS deviceRelations = NULL;
00609 
00610                 if(!isFdo) {
00611 
00612                     if(type == TargetDeviceRelation) {
00613 
00614                         //
00615                         // Device relations has one entry built in to it's size.
00616                         //
00617 
00618                         status = STATUS_INSUFFICIENT_RESOURCES;
00619 
00620                         deviceRelations = ExAllocatePoolWithTag(PagedPool,
00621                                                          sizeof(DEVICE_RELATIONS),
00622                                                          '2CcS');
00623 
00624                         if(deviceRelations != NULL) {
00625 
00626                             RtlZeroMemory(deviceRelations,
00627                                           sizeof(DEVICE_RELATIONS));
00628 
00629                             Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
00630 
00631                             deviceRelations->Count = 1;
00632                             deviceRelations->Objects[0] = DeviceObject;
00633                             ObReferenceObject(deviceRelations->Objects[0]);
00634 
00635                             status = STATUS_SUCCESS;
00636                         }
00637 
00638                     } else {
00639                         //
00640                         // PDO's just complete enumeration requests without altering
00641                         // the status.
00642                         //
00643 
00644                         status = Irp->IoStatus.Status;
00645                     }
00646 
00647                     break;
00648 
00649                 } else if (type == BusRelations) {
00650 
00651                     ASSERT(commonExtension->IsInitialized);
00652 
00653                     //
00654                     // Make sure we support enumeration
00655                     //
00656 
00657                     if(initData->ClassEnumerateDevice == NULL) {
00658 
00659                         //
00660                         // Just send the request down to the lower driver.  Perhaps
00661                         // It can enumerate children.
00662                         //
00663 
00664                     } else {
00665 
00666                         //
00667                         // Re-enumerate the device
00668                         //
00669 
00670                         status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
00671 
00672                         if(!NT_SUCCESS(status)) {
00673                             completeRequest = TRUE;
00674                             break;
00675                         }
00676                     }
00677                 }
00678 
00679                 IoCopyCurrentIrpStackLocationToNext(Irp);
00680                 ClassReleaseRemoveLock(DeviceObject, Irp);
00681                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
00682                 completeRequest = FALSE;
00683 
00684                 break;
00685             }
00686 
00687             case IRP_MN_QUERY_ID: {
00688 
00689                 BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
00690                 UNICODE_STRING unicodeString;
00691 
00692                 if(isFdo) {
00693 
00694                     //
00695                     // FDO's should just forward the query down to the lower
00696                     // device objects
00697                     //
00698 
00699                     IoCopyCurrentIrpStackLocationToNext(Irp);
00700                     ClassReleaseRemoveLock(DeviceObject, Irp);
00701 
00702                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
00703                     completeRequest = FALSE;
00704                     break;
00705                 }
00706 
00707                 //
00708                 // PDO's need to give an answer - this is easy for now
00709                 //
00710 
00711                 RtlInitUnicodeString(&unicodeString, NULL);
00712 
00713                 status = ClassGetPdoId(DeviceObject,
00714                                        idType,
00715                                        &unicodeString);
00716 
00717                 if(status == STATUS_NOT_IMPLEMENTED) {
00718                     //
00719                     // The driver doesn't implement this ID (whatever it is).
00720                     // Use the status out of the IRP so that we don't mangle a
00721                     // response from someone else.
00722                     //
00723 
00724                     status = Irp->IoStatus.Status;
00725                 } else if(NT_SUCCESS(status)) {
00726                     Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
00727                 } else {
00728                     Irp->IoStatus.Information = (ULONG_PTR) NULL;
00729                 }
00730 
00731                 break;
00732             }
00733 
00734             case IRP_MN_QUERY_STOP_DEVICE:
00735             case IRP_MN_QUERY_REMOVE_DEVICE: {
00736 
00737                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
00738                             DeviceObject, Irp,
00739                             ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
00740                              "STOP" : "REMOVE")));
00741 
00742                 //
00743                 // If this device is in use for some reason (paging, etc...)
00744                 // then we need to fail the request.
00745                 //
00746 
00747                 if(commonExtension->PagingPathCount != 0) {
00748 
00749                     DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
00750                                 "path and cannot be removed\n",
00751                                 DeviceObject, Irp));
00752                     status = STATUS_DEVICE_BUSY;
00753                     break;
00754                 }
00755 
00756                 //
00757                 // Check with the class driver to see if the query operation
00758                 // can succeed.
00759                 //
00760 
00761                 if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
00762                     status = devInfo->ClassStopDevice(DeviceObject,
00763                                                       irpStack->MinorFunction);
00764                 } else {
00765                     status = devInfo->ClassRemoveDevice(DeviceObject,
00766                                                         irpStack->MinorFunction);
00767                 }
00768 
00769                 if(NT_SUCCESS(status)) {
00770 
00771                     //
00772                     // ASSERT that we never get two queries in a row, as
00773                     // this will severly mess up the state machine
00774                     //
00775                     ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
00776                     commonExtension->PreviousState = commonExtension->CurrentState;
00777                     commonExtension->CurrentState = irpStack->MinorFunction;
00778 
00779                     if(isFdo) {
00780                         DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
00781                                     "%s irp\n", DeviceObject, Irp,
00782                                     ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
00783                                      "STOP" : "REMOVE")));
00784                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
00785                     }
00786                 }
00787                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
00788                             DeviceObject, Irp, status));
00789 
00790                 break;
00791             }
00792 
00793             case IRP_MN_CANCEL_STOP_DEVICE:
00794             case IRP_MN_CANCEL_REMOVE_DEVICE: {
00795 
00796                 //
00797                 // Check with the class driver to see if the query or cancel
00798                 // operation can succeed.
00799                 //
00800 
00801                 if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
00802                     status = devInfo->ClassStopDevice(DeviceObject,
00803                                                       irpStack->MinorFunction);
00804                     ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
00805                               "never be failed\n", NT_SUCCESS(status));
00806                 } else {
00807                     status = devInfo->ClassRemoveDevice(DeviceObject,
00808                                                         irpStack->MinorFunction);
00809                     ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
00810                               "never be failed\n", NT_SUCCESS(status));
00811                 }
00812 
00813                 Irp->IoStatus.Status = status;
00814 
00815                 //
00816                 // We got a CANCEL - roll back to the previous state only
00817                 // if the current state is the respective QUERY state.
00818                 //
00819 
00820                 if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
00821                     (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
00822                     ) ||
00823                    ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
00824                     (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
00825                     )
00826                    ) {
00827 
00828                     commonExtension->CurrentState =
00829                         commonExtension->PreviousState;
00830                     commonExtension->PreviousState = 0xff;
00831 
00832                 }
00833 
00834                 if(isFdo) {
00835                     IoCopyCurrentIrpStackLocationToNext(Irp);
00836                     ClassReleaseRemoveLock(DeviceObject, Irp);
00837                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
00838                     completeRequest = FALSE;
00839                 } else {
00840                     status = STATUS_SUCCESS;
00841                 }
00842 
00843                 break;
00844             }
00845 
00846             case IRP_MN_STOP_DEVICE: {
00847 
00848                 //
00849                 // These all mean nothing to the class driver currently.  The
00850                 // port driver will handle all queueing when necessary.
00851                 //
00852 
00853                 DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
00854                             DeviceObject, Irp,
00855                             (isFdo ? "fdo" : "pdo")
00856                             ));
00857 
00858                 ASSERT(commonExtension->PagingPathCount == 0);
00859 
00860                 //
00861                 // ISSUE-2000/02/03-peterwie
00862                 // if we stop the timer here then it means no class driver can
00863                 // do i/o in its ClassStopDevice routine.  This is because the
00864                 // retry (among other things) is tied into the tick handler
00865                 // and disabling retries could cause the class driver to deadlock.
00866                 // Currently no class driver we're aware of issues i/o in its
00867                 // Stop routine but this is a case we may want to defend ourself
00868                 // against.
00869                 //
00870 
00871                 if (DeviceObject->Timer) {
00872                     IoStopTimer(DeviceObject);
00873                 }
00874 
00875                 status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
00876 
00877                 ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
00878                           "never be failed\n", NT_SUCCESS(status));
00879 
00880                 if(isFdo) {
00881                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
00882                 }
00883 
00884                 if(NT_SUCCESS(status)) {
00885                     commonExtension->CurrentState = irpStack->MinorFunction;
00886                     commonExtension->PreviousState = 0xff;
00887                 }
00888 
00889                 break;
00890             }
00891 
00892             case IRP_MN_REMOVE_DEVICE:
00893             case IRP_MN_SURPRISE_REMOVAL: {
00894 
00895                 PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
00896                 UCHAR removeType = irpStack->MinorFunction;
00897 
00898                 if (commonExtension->PagingPathCount != 0) {
00899                     DBGTRACE(ClassDebugWarning, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
00900                 }
00901 
00902                 //
00903                 // Release the lock for this IRP before calling in.
00904                 //
00905                 ClassReleaseRemoveLock(DeviceObject, Irp);
00906                 lockReleased = TRUE;
00907 
00908                 /*
00909                  *  If a timer was started on the device, stop it.
00910                  */
00911                 if (DeviceObject->Timer) {
00912                     IoStopTimer(DeviceObject);
00913                 }
00914 
00915                 /*
00916                  *  "Fire-and-forget" the remove irp to the lower stack.
00917                  *  Don't touch the irp (or the irp stack!) after this.
00918                  */
00919                 if (isFdo) {
00920                     IoCopyCurrentIrpStackLocationToNext(Irp);
00921                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
00922                     ASSERT(NT_SUCCESS(status));
00923                     completeRequest = FALSE;
00924                 }
00925                 else {
00926                     status = STATUS_SUCCESS;
00927                 }
00928 
00929                 /*
00930                  *  Do our own cleanup and call the class driver's remove
00931                  *  cleanup routine.
00932                  *  For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
00933                  *  so don't touch the extension after this.
00934                  */
00935                 commonExtension->PreviousState = commonExtension->CurrentState;
00936                 commonExtension->CurrentState = removeType;
00937                 ClassRemoveDevice(DeviceObject, removeType);
00938 
00939                 break;
00940             }
00941 
00942             case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
00943 
00944                 switch(irpStack->Parameters.UsageNotification.Type) {
00945 
00946                     case DeviceUsageTypePaging: {
00947 
00948                         BOOLEAN setPagable;
00949 
00950                         if((irpStack->Parameters.UsageNotification.InPath) &&
00951                            (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
00952 
00953                             //
00954                             // Device isn't started.  Don't allow adding a
00955                             // paging file, but allow a removal of one.
00956                             //
00957 
00958                             status = STATUS_DEVICE_NOT_READY;
00959                             break;
00960                         }
00961 
00962                         ASSERT(commonExtension->IsInitialized);
00963 
00964                         //
00965                         // need to synchronize this now...
00966                         //
00967 
00968                         KeEnterCriticalRegion();
00969                         status = KeWaitForSingleObject(&commonExtension->PathCountEvent,
00970                                                        Executive, KernelMode,
00971                                                        FALSE, NULL);
00972                         ASSERT(NT_SUCCESS(status));
00973                         status = STATUS_SUCCESS;
00974 
00975                         //
00976                         // If the volume is removable we should try to lock it in
00977                         // place or unlock it once per paging path count
00978                         //
00979 
00980                         if (commonExtension->IsFdo){
00981                             status = ClasspEjectionControl(
00982                                             DeviceObject,
00983                                             Irp,
00984                                             InternalMediaLock,
00985                                             (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
00986                         }
00987 
00988                         if (!NT_SUCCESS(status)){
00989                             KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
00990                             KeLeaveCriticalRegion();
00991                             break;
00992                         }
00993 
00994                         //
00995                         // if removing last paging device, need to set DO_POWER_PAGABLE
00996                         // bit here, and possible re-set it below on failure.
00997                         //
00998 
00999                         setPagable = FALSE;
01000 
01001                         if (!irpStack->Parameters.UsageNotification.InPath &&
01002                             commonExtension->PagingPathCount == 1
01003                             ) {
01004 
01005                             //
01006                             // removing last paging file
01007                             // must have DO_POWER_PAGABLE bits set, but only
01008                             // if noone set the DO_POWER_INRUSH bit
01009                             //
01010 
01011 
01012                             if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
01013                                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
01014                                             "paging file removed, but "
01015                                             "DO_POWER_INRUSH was set, so NOT "
01016                                             "setting DO_POWER_PAGABLE\n",
01017                                             DeviceObject, Irp));
01018                             } else {
01019                                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
01020                                             "paging file removed, "
01021                                             "setting DO_POWER_PAGABLE\n",
01022                                             DeviceObject, Irp));
01023                                 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
01024                                 setPagable = TRUE;
01025                             }
01026 
01027                         }
01028 
01029                         //
01030                         // forward the irp before finishing handling the
01031                         // special cases
01032                         //
01033 
01034                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
01035 
01036                         //
01037                         // now deal with the failure and success cases.
01038                         // note that we are not allowed to fail the irp
01039                         // once it is sent to the lower drivers.
01040                         //
01041 
01042                         if (NT_SUCCESS(status)) {
01043 
01044                             IoAdjustPagingPathCount(
01045                                 &commonExtension->PagingPathCount,
01046                                 irpStack->Parameters.UsageNotification.InPath);
01047 
01048                             if (irpStack->Parameters.UsageNotification.InPath) {
01049                                 if (commonExtension->PagingPathCount == 1) {
01050                                     DebugPrint((2, "ClassDispatchPnp (%p,%p): "
01051                                                 "Clearing PAGABLE bit\n",
01052                                                 DeviceObject, Irp));
01053                                     CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
01054                                 }
01055                             }
01056 
01057                         } else {
01058 
01059                             //
01060                             // cleanup the changes done above
01061                             //
01062 
01063                             if (setPagable == TRUE) {
01064                                 DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
01065                                             "PAGABLE bit due to irp failure\n",
01066                                             DeviceObject, Irp));
01067                                 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
01068                                 setPagable = FALSE;
01069                             }
01070 
01071                             //
01072                             // relock or unlock the media if needed.
01073                             //
01074 
01075                             if (commonExtension->IsFdo) {
01076 
01077                                 ClasspEjectionControl(
01078                                         DeviceObject,
01079                                         Irp,
01080                                         InternalMediaLock,
01081                                         (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
01082                             }
01083                         }
01084 
01085                         //
01086                         // set the event so the next one can occur.
01087                         //
01088 
01089                         KeSetEvent(&commonExtension->PathCountEvent,
01090                                    IO_NO_INCREMENT, FALSE);
01091                         KeLeaveCriticalRegion();
01092                         break;
01093                     }
01094 
01095                     case DeviceUsageTypeHibernation: {
01096 
01097                         IoAdjustPagingPathCount(
01098                             &commonExtension->HibernationPathCount,
01099                             irpStack->Parameters.UsageNotification.InPath
01100                             );
01101                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
01102                         if (!NT_SUCCESS(status)) {
01103                             IoAdjustPagingPathCount(
01104                                 &commonExtension->HibernationPathCount,
01105                                 !irpStack->Parameters.UsageNotification.InPath
01106                                 );
01107                         }
01108 
01109                         break;
01110                     }
01111 
01112                     case DeviceUsageTypeDumpFile: {
01113                         IoAdjustPagingPathCount(
01114                             &commonExtension->DumpPathCount,
01115                             irpStack->Parameters.UsageNotification.InPath
01116                             );
01117                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
01118                         if (!NT_SUCCESS(status)) {
01119                             IoAdjustPagingPathCount(
01120                                 &commonExtension->DumpPathCount,
01121                                 !irpStack->Parameters.UsageNotification.InPath
01122                                 );
01123                         }
01124 
01125                         break;
01126                     }
01127 
01128                     default: {
01129                         status = STATUS_INVALID_PARAMETER;
01130                         break;
01131                     }
01132                 }
01133                 break;
01134             }
01135 
01136             case IRP_MN_QUERY_CAPABILITIES: {
01137 
01138                 DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
01139                             DeviceObject, Irp));
01140 
01141                 if(!isFdo) {
01142 
01143                     status = ClassQueryPnpCapabilities(
01144                                 DeviceObject,
01145                                 irpStack->Parameters.DeviceCapabilities.Capabilities
01146                                 );
01147 
01148                     break;
01149 
01150                 } else {
01151 
01152                     PDEVICE_CAPABILITIES deviceCapabilities;
01153                     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
01154                     PCLASS_PRIVATE_FDO_DATA fdoData;
01155 
01156                     fdoExtension = DeviceObject->DeviceExtension;
01157                     fdoData = fdoExtension->PrivateFdoData;
01158                     deviceCapabilities =
01159                         irpStack->Parameters.DeviceCapabilities.Capabilities;
01160 
01161                     //
01162                     // forward the irp before handling the special cases
01163                     //
01164 
01165                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
01166                     if (!NT_SUCCESS(status)) {
01167                         break;
01168                     }
01169 
01170                     //
01171                     // we generally want to remove the device from the hotplug
01172                     // applet, which requires the SR-OK bit to be set.
01173                     // only when the user specifies that they are capable of
01174                     // safely removing things do we want to clear this bit
01175                     // (saved in WriteCacheEnableOverride)
01176                     //
01177                     // setting of this bit is done either above, or by the
01178                     // lower driver.
01179                     //
01180                     // note: may not be started, so check we have FDO data first.
01181                     //
01182 
01183                     if (fdoData &&
01184                         fdoData->HotplugInfo.WriteCacheEnableOverride) {
01185                         if (deviceCapabilities->SurpriseRemovalOK) {
01186                             DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
01187                                         "device capabilities due to hotplug "
01188                                         "device or media\n"));
01189                         }
01190                         deviceCapabilities->SurpriseRemovalOK = FALSE;
01191                     }
01192                     break;
01193 
01194                 } // end QUERY_CAPABILITIES for FDOs
01195 
01196                 ASSERT(FALSE);
01197                 break;
01198 
01199 
01200             } // end QUERY_CAPABILITIES
01201 
01202             default: {
01203 
01204                 if (isFdo){
01205                     IoCopyCurrentIrpStackLocationToNext(Irp);
01206 
01207                     ClassReleaseRemoveLock(DeviceObject, Irp);
01208                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
01209 
01210                     completeRequest = FALSE;
01211                 }
01212 
01213                 break;
01214             }
01215         }
01216     }
01217     else {
01218         ASSERT(driverExtension);
01219         status = STATUS_INTERNAL_ERROR;
01220     }
01221 
01222     if (completeRequest){
01223         Irp->IoStatus.Status = status;
01224 
01225         if (!lockReleased){
01226             ClassReleaseRemoveLock(DeviceObject, Irp);
01227         }
01228 
01229         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
01230 
01231         DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
01232     }
01233     else {
01234         /*
01235          *  The irp is already completed so don't touch it.
01236          *  This may be a remove so don't touch the device extension.
01237          */
01238         DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
01239     }
01240 
01241     return status;
01242 } // end ClassDispatchPnp()
01243 
01244 /*++////////////////////////////////////////////////////////////////////////////
01245 
01246 ClassPnpStartDevice()
01247 
01248 Routine Description:
01249 
01250     Storage class driver routine for IRP_MN_START_DEVICE requests.
01251     This routine kicks off any device specific initialization
01252 
01253 Arguments:
01254 
01255     DeviceObject - a pointer to the device object
01256 
01257     Irp - a pointer to the io request packet
01258 
01259 Return Value:
01260 
01261     none
01262 
01263 --*/
01264 NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
01265 {
01266     PCLASS_DRIVER_EXTENSION driverExtension;
01267     PCLASS_INIT_DATA initData;
01268 
01269     PCLASS_DEV_INFO devInfo;
01270 
01271     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
01272     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
01273     BOOLEAN isFdo = commonExtension->IsFdo;
01274 
01275     BOOLEAN isMountedDevice = TRUE;
01276     UNICODE_STRING  interfaceName;
01277 
01278     BOOLEAN timerStarted;
01279 
01280     NTSTATUS status = STATUS_SUCCESS;
01281 
01282     PAGED_CODE();
01283 
01284     driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
01285                                                  CLASS_DRIVER_EXTENSION_KEY);
01286 
01287     initData = &(driverExtension->InitData);
01288     if(isFdo) {
01289         devInfo = &(initData->FdoData);
01290     } else {
01291         devInfo = &(initData->PdoData);
01292     }
01293 
01294     ASSERT(devInfo->ClassInitDevice != NULL);
01295     ASSERT(devInfo->ClassStartDevice != NULL);
01296 
01297     if (!commonExtension->IsInitialized){
01298 
01299         //
01300         // perform FDO/PDO specific initialization
01301         //
01302 
01303         if (isFdo){
01304             STORAGE_PROPERTY_ID propertyId;
01305 
01306             //
01307             // allocate a private extension for class data
01308             //
01309 
01310             if (fdoExtension->PrivateFdoData == NULL) {
01311                 fdoExtension->PrivateFdoData =
01312                     ExAllocatePoolWithTag(NonPagedPool,
01313                                           sizeof(CLASS_PRIVATE_FDO_DATA),
01314                                           CLASS_TAG_PRIVATE_DATA
01315                                           );
01316             }
01317 
01318             if (fdoExtension->PrivateFdoData == NULL) {
01319                 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
01320                             "private fdo data\n"));
01321                 return STATUS_INSUFFICIENT_RESOURCES;
01322             }
01323 
01324             //
01325             // initialize the struct's various fields.
01326             //
01327 
01328             RtlZeroMemory(fdoExtension->PrivateFdoData,
01329                           sizeof(CLASS_PRIVATE_FDO_DATA)
01330                           );
01331             KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
01332             KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
01333                             ClasspRetryRequestDpc,
01334                             DeviceObject);
01335             KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
01336             fdoExtension->PrivateFdoData->Retry.Granularity =
01337                 KeQueryTimeIncrement();
01338             commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
01339 
01340             //
01341             // NOTE: the old interface allowed the class driver to allocate
01342             // this.  this was unsafe for low-memory conditions. allocate one
01343             // unconditionally now, and modify our internal functions to use
01344             // our own exclusively as it is the only safe way to do this.
01345             //
01346 
01347             status = ClasspAllocateReleaseQueueIrp(fdoExtension);
01348             if (!NT_SUCCESS(status)) {
01349                 DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
01350                             "private release queue irp\n"));
01351                 return status;
01352             }
01353 
01354             //
01355             // Call port driver to get adapter capabilities.
01356             //
01357 
01358             propertyId = StorageAdapterProperty;
01359 
01360             status = ClassGetDescriptor(
01361                         commonExtension->LowerDeviceObject,
01362                         &propertyId,
01363                         &fdoExtension->AdapterDescriptor);
01364 
01365             if(!NT_SUCCESS(status)) {
01366 
01367                 //
01368                 // This DebugPrint is to help third-party driver writers
01369                 //
01370 
01371                 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
01372                                "[ADAPTER] failed %lx\n", status));
01373                 return status;
01374             }
01375 
01376             //
01377             // Call port driver to get device descriptor.
01378             //
01379 
01380             propertyId = StorageDeviceProperty;
01381 
01382             status = ClassGetDescriptor(
01383                         commonExtension->LowerDeviceObject,
01384                         &propertyId,
01385                         &fdoExtension->DeviceDescriptor);
01386 
01387             if(!NT_SUCCESS(status)) {
01388 
01389                 //
01390                 // This DebugPrint is to help third-party driver writers
01391                 //
01392 
01393                 DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
01394                                "[DEVICE] failed %lx\n", status));
01395                 return status;
01396             }
01397 
01398             ClasspScanForSpecialInRegistry(fdoExtension);
01399             ClassScanForSpecial(fdoExtension,
01400                                 ClassBadItems,
01401                                 ClasspScanForClassHacks);
01402 
01403             //
01404             // allow perf to be re-enabled after a given number of failed IOs
01405             // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
01406             //
01407 
01408             {
01409                 ULONG t = 0;
01410                 ClassGetDeviceParameter(fdoExtension,
01411                                         CLASSP_REG_SUBKEY_NAME,
01412                                         CLASSP_REG_PERF_RESTORE_VALUE_NAME,
01413                                         &t);
01414                 if (t >= CLASS_PERF_RESTORE_MINIMUM) {
01415                     fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
01416                 }
01417             }
01418 
01419 
01420             //
01421             // compatibility comes first.  writable cd media will not
01422             // get a SYNCH_CACHE on power down.
01423             //
01424 
01425             if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
01426                 SET_FLAG(fdoExtension->PrivateFdoData->HackFlags,
01427                          FDO_HACK_NO_SYNC_CACHE);
01428             }
01429 
01430             //
01431             // initialize the hotplug information only after the ScanForSpecial
01432             // routines, as it relies upon the hack flags.
01433             //
01434 
01435             status = ClasspInitializeHotplugInfo(fdoExtension);
01436 
01437             if (!NT_SUCCESS(status)) {
01438                 DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
01439                             "hotplug information %lx\n", status));
01440                 return status;
01441             }
01442 
01443             /*
01444              *  Allocate/initialize TRANSFER_PACKETs and related resources.
01445              */
01446             status = InitializeTransferPackets(DeviceObject);
01447         }
01448 
01449         //
01450         // ISSUE - drivers need to disable write caching on the media
01451         //         if hotplug and !useroverride.  perhaps we should
01452         //         allow registration of a callback to enable/disable
01453         //         write cache instead.
01454         //
01455 
01456         if (NT_SUCCESS(status)){
01457             status = devInfo->ClassInitDevice(DeviceObject);
01458         }
01459 
01460     }
01461 
01462     if (!NT_SUCCESS(status)){
01463 
01464         //
01465         // Just bail out - the remove that comes down will clean up the
01466         // initialized scraps.
01467         //
01468 
01469         return status;
01470     } else {
01471         commonExtension->IsInitialized = TRUE;
01472 
01473         if (commonExtension->IsFdo) {
01474             fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
01475         }
01476 
01477     }
01478 
01479     //
01480     // If device requests autorun functionality or a once a second callback
01481     // then enable the once per second timer.
01482     //
01483     // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
01484     //       called in the context of the ClassInitDevice callback. If called
01485     //       after then this check will have already been made and the
01486     //       once a second timer will not have been enabled.
01487     //
01488     if ((isFdo) &&
01489         ((initData->ClassTick != NULL) ||
01490          (fdoExtension->MediaChangeDetectionInfo != NULL) ||
01491          ((fdoExtension->FailurePredictionInfo != NULL) &&
01492           (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
01493     {
01494         ClasspEnableTimer(DeviceObject);
01495         timerStarted = TRUE;
01496     } else {
01497         timerStarted = FALSE;
01498     }
01499 
01500     //
01501     // NOTE: the timer looks at commonExtension->CurrentState now
01502     //       to prevent Media Change Notification code from running
01503     //       until the device is started, but allows the device
01504     //       specific tick handler to run.  therefore it is imperative
01505     //       that commonExtension->CurrentState not be updated until
01506     //       the device specific startdevice handler has finished.
01507     //
01508 
01509     status = devInfo->ClassStartDevice(DeviceObject);
01510 
01511     if(NT_SUCCESS(status)) {
01512         commonExtension->CurrentState = IRP_MN_START_DEVICE;
01513 
01514         if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
01515             isMountedDevice = FALSE;
01516         }
01517 
01518         if((DeviceObject->DeviceType != FILE_DEVICE_DISK) &&
01519            (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM)) {
01520 
01521             isMountedDevice = FALSE;
01522         }
01523 
01524 
01525         if(isMountedDevice) {
01526             ClasspRegisterMountedDeviceInterface(DeviceObject);
01527         }
01528 
01529         if((commonExtension->IsFdo) &&
01530            (devInfo->ClassWmiInfo.GuidRegInfo != NULL)) {
01531 
01532             IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
01533         }
01534     } else {
01535 
01536         if (timerStarted) {
01537             ClasspDisableTimer(DeviceObject);
01538         }
01539     }
01540 
01541     return status;
01542 }
01543 
01544 
01545 /*++////////////////////////////////////////////////////////////////////////////
01546 
01547 ClassReadWrite()
01548 
01549 Routine Description:
01550 
01551     This is the system entry point for read and write requests. The
01552     device-specific handler is invoked to perform any validation necessary.
01553 
01554     If the device object is a PDO (partition object) then the request will
01555     simply be adjusted for Partition0 and issued to the lower device driver.
01556 
01557     IF the device object is an FDO (paritition 0 object), the number of bytes
01558     in the request are checked against the maximum byte counts that the adapter
01559     supports and requests are broken up into
01560     smaller sizes if necessary.
01561 
01562 Arguments:
01563 
01564     DeviceObject - a pointer to the device object for this request
01565 
01566     Irp - IO request
01567 
01568 Return Value:
01569 
01570     NT Status
01571 
01572 --*/
01573 NTSTATUS ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
01574 {
01575     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
01576     PDEVICE_OBJECT      lowerDeviceObject = commonExtension->LowerDeviceObject;
01577     PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
01578     LARGE_INTEGER       startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
01579     ULONG               transferByteCount = currentIrpStack->Parameters.Read.Length;
01580     ULONG               isRemoved;
01581     NTSTATUS            status;
01582 
01583     /*
01584      *  Grab the remove lock.  If we can't acquire it, bail out.
01585      */
01586     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
01587     if (isRemoved) {
01588         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
01589         ClassReleaseRemoveLock(DeviceObject, Irp);
01590         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
01591         status = STATUS_DEVICE_DOES_NOT_EXIST;
01592     }
01593     else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
01594              (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
01595              !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
01596 
01597         /*
01598          *  DO_VERIFY_VOLUME is set for the device object,
01599          *  but this request is not itself a verify request.
01600          *  So fail this request.
01601          */
01602         IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
01603         Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
01604         Irp->IoStatus.Information = 0;
01605         ClassReleaseRemoveLock(DeviceObject, Irp);
01606         ClassCompleteRequest(DeviceObject, Irp, 0);
01607         status = STATUS_VERIFY_REQUIRED;
01608     }
01609     else {
01610 
01611         /*
01612          *  Since we've bypassed the verify-required tests we don't need to repeat
01613          *  them with this IRP - in particular we don't want to worry about
01614          *  hitting them at the partition 0 level if the request has come through
01615          *  a non-zero partition.
01616          */
01617         currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
01618 
01619         /*
01620          *  Call the miniport driver's pre-pass filter to check if we
01621          *  should continue with this transfer.
01622          */
01623         ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
01624         status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
01625         if (!NT_SUCCESS(status)){
01626             ASSERT(Irp->IoStatus.Status == status);
01627             ClassReleaseRemoveLock(DeviceObject, Irp);
01628             ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
01629         }
01630         else if (status == STATUS_PENDING){
01631             /*
01632              *  ClassReadWriteVerification queued this request.
01633              *  So don't touch the irp anymore.
01634              */
01635         }
01636         else {
01637 
01638             if (transferByteCount == 0) {
01639                 /*
01640                  *  Several parts of the code turn 0 into 0xffffffff,
01641                  *  so don't process a zero-length request any further.
01642                  */
01643                 Irp->IoStatus.Status = STATUS_SUCCESS;
01644                 Irp->IoStatus.Information = 0;
01645                 ClassReleaseRemoveLock(DeviceObject, Irp);
01646                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
01647                 status = STATUS_SUCCESS;
01648             }
01649             else {
01650                 /*
01651                  *  If the driver has its own StartIo routine, call it.
01652                  */
01653                 if (commonExtension->DriverExtension->InitData.ClassStartIo) {
01654                     IoMarkIrpPending(Irp);
01655                     IoStartPacket(DeviceObject, Irp, NULL, NULL);
01656                     status = STATUS_PENDING;
01657                 }
01658                 else {
01659                     /*
01660                      *  The driver does not have its own StartIo routine.
01661                      *  So process this request ourselves.
01662                      */
01663 
01664                     /*
01665                      *  Add partition byte offset to make starting byte relative to
01666                      *  beginning of disk.
01667                      */
01668                     currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
01669                         commonExtension->StartingOffset.QuadPart;
01670 
01671                     if (commonExtension->IsFdo){
01672 
01673                         /*
01674                          *  Add in any skew for the disk manager software.
01675                          */
01676                         currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
01677                              commonExtension->PartitionZeroExtension->DMByteSkew;
01678 
01679                         /*
01680                          *  Perform the actual transfer(s) on the hardware
01681                          *  to service this request.
01682                          */
01683                         ServiceTransferRequest(DeviceObject, Irp);
01684                         status = STATUS_PENDING;
01685                     }
01686                     else {
01687                         /*
01688                          *  This is a child PDO enumerated for our FDO by e.g. disk.sys
01689                          *  and owned by e.g. partmgr.  Send it down to the next device
01690                          *  and the same irp will come back to us for the FDO.
01691                          */
01692                         IoCopyCurrentIrpStackLocationToNext(Irp);
01693                         ClassReleaseRemoveLock(DeviceObject, Irp);
01694                         status = IoCallDriver(lowerDeviceObject, Irp);
01695                     }
01696                 }
01697             }
01698         }
01699     }
01700 
01701     return status;
01702 }
01703 
01704 
01705 /*++////////////////////////////////////////////////////////////////////////////
01706 
01707 ClassReadDriveCapacity()
01708 
01709 Routine Description:
01710 
01711     This routine sends a READ CAPACITY to the requested device, updates
01712     the geometry information in the device object and returns
01713     when it is complete.  This routine is synchronous.
01714 
01715     This routine must be called with the remove lock held or some other
01716     assurance that the Fdo will not be removed while processing.
01717 
01718 Arguments:
01719 
01720     DeviceObject - Supplies a pointer to the device object that represents
01721         the device whose capacity is to be read.
01722 
01723 Return Value:
01724 
01725     Status is returned.
01726 
01727 --*/
01728 NTSTATUS ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo)
01729 {
01730     READ_CAPACITY_DATA readCapacityBuffer = {0};
01731     NTSTATUS status;
01732     PMDL driveCapMdl;
01733 
01734     driveCapMdl = BuildDeviceInputMdl(&readCapacityBuffer, sizeof(READ_CAPACITY_DATA));
01735     if (driveCapMdl){
01736 
01737         TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
01738         if (pkt){
01739             PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
01740             KEVENT event;
01741             NTSTATUS pktStatus;
01742             IRP pseudoIrp = {0};
01743 
01744             /*
01745              *  Our engine needs an "original irp" to write the status back to
01746              *  and to count down packets (one in this case).
01747              *  Just use a pretend irp for this.
01748              */
01749             pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
01750             pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
01751             pseudoIrp.IoStatus.Information = 0;
01752             pseudoIrp.MdlAddress = driveCapMdl;
01753 
01754             /*
01755              *  Set this up as a SYNCHRONOUS transfer, submit it,
01756              *  and wait for the packet to complete.  The result
01757              *  status will be written to the original irp.
01758              */
01759             KeInitializeEvent(&event, SynchronizationEvent, FALSE);
01760             SetupDriveCapacityTransferPacket(   pkt,
01761                                             &readCapacityBuffer,
01762                                             sizeof(READ_CAPACITY_DATA),
01763                                             &event,
01764                                             &pseudoIrp);
01765             SubmitTransferPacket(pkt);
01766             KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
01767 
01768             status = pseudoIrp.IoStatus.Status;
01769 
01770             /*
01771              *  If we got an UNDERRUN, retry exactly once.
01772              *  (The transfer_packet engine didn't retry because the result
01773              *   status was success).
01774              */
01775             if (NT_SUCCESS(status) &&
01776                (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA))){
01777                 DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG)pseudoIrp.IoStatus.Information, sizeof(READ_CAPACITY_DATA)));
01778 
01779                 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
01780                 if (pkt){
01781                     pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
01782                     pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
01783                     pseudoIrp.IoStatus.Information = 0;
01784                     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
01785                     SetupDriveCapacityTransferPacket(   pkt,
01786                                                     &readCapacityBuffer,
01787                                                     sizeof(READ_CAPACITY_DATA),
01788                                                     &event,
01789                                                     &pseudoIrp);
01790                     SubmitTransferPacket(pkt);
01791                     KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
01792                     status = pseudoIrp.IoStatus.Status;
01793                     if (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA)){
01794                         status = STATUS_DEVICE_BUSY;
01795                     }
01796                 }
01797                 else {
01798                     status = STATUS_INSUFFICIENT_RESOURCES;
01799                 }
01800             }
01801 
01802 
01803             if (NT_SUCCESS(status)){
01804                 /*
01805                  *  The request succeeded.
01806                  *  Read out and store the drive information.
01807                  */
01808                 ULONG cylinderSize;
01809                 ULONG bytesPerSector;
01810                 ULONG tmp;
01811                 ULONG lastSector;
01812 
01813                 /*
01814                  *  Read the bytesPerSector value,
01815                  *  which is big-endian in the returned buffer.
01816                  *  Default to the standard 512 bytes.
01817                  */
01818                 tmp = readCapacityBuffer.BytesPerBlock;
01819                 ((PFOUR_BYTE)&bytesPerSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
01820                 ((PFOUR_BYTE)&bytesPerSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
01821                 ((PFOUR_BYTE)&bytesPerSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
01822                 ((PFOUR_BYTE)&bytesPerSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
01823                 if (bytesPerSector == 0) {
01824                     bytesPerSector = 512;
01825                 }
01826                 else {
01827                     /*
01828                      *  Clear all but the highest set bit.
01829                      *  That will give us a bytesPerSector value that is a power of 2.
01830                      */
01831                     while (bytesPerSector & (bytesPerSector-1)) {
01832                         bytesPerSector &= bytesPerSector-1;
01833                     }
01834                 }
01835                 fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
01836 
01837                 //
01838                 // Copy last sector in reverse byte order.
01839                 //
01840 
01841                 tmp = readCapacityBuffer.LogicalBlockAddress;
01842                 ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
01843                 ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
01844                 ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
01845                 ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
01846 
01847                 //
01848                 // Calculate sector to byte shift.
01849                 //
01850 
01851                 WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
01852 
01853                 DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
01854                     fdoExt->DiskGeometry.BytesPerSector));
01855 
01856                 DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
01857                     lastSector + 1));
01858 
01859                 if (fdoExt->DMActive){
01860                     DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
01861                                 fdoExt->DMSkew));
01862                     lastSector -= fdoExt->DMSkew;
01863                 }
01864 
01865                 /*
01866                  *  Check to see if we have a geometry we should be using already.
01867                  */
01868                 cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
01869                                 fdoExt->DiskGeometry.SectorsPerTrack);
01870                 if (cylinderSize == 0){
01871                     DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
01872                                    "values from %#x/%#x to %#x/%#x\n",
01873                                 fdoExt->DiskGeometry.TracksPerCylinder,
01874                                 fdoExt->DiskGeometry.SectorsPerTrack,
01875                                 0xff,
01876                                 0x3f));
01877 
01878                     fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
01879                     fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
01880 
01881 
01882                     cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
01883                                     fdoExt->DiskGeometry.SectorsPerTrack);
01884                 }
01885 
01886                 //
01887                 // Calculate number of cylinders.
01888                 //
01889 
01890                 fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/cylinderSize);
01891 
01892                 //
01893                 // if there are zero cylinders, then the device lied AND it's
01894                 // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
01895                 // this can fit into a single LONGLONG, so create another usable
01896                 // geometry, even if it's unusual looking.  This allows small,
01897                 // non-standard devices, such as Sony's Memory Stick, to show
01898                 // up as having a partition.
01899                 //
01900 
01901                 if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
01902                     fdoExt->DiskGeometry.SectorsPerTrack    = 1;
01903                     fdoExt->DiskGeometry.TracksPerCylinder  = 1;
01904                     fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector;
01905                 }
01906 
01907 
01908                 //
01909                 // Calculate media capacity in bytes.
01910                 //
01911 
01912                 fdoExt->CommonExtension.PartitionLength.QuadPart =
01913                     ((LONGLONG)(lastSector + 1)) << fdoExt->SectorShift;
01914 
01915                 /*
01916                  *  Is this removable or fixed media
01917                  */
01918                 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
01919                     fdoExt->DiskGeometry.MediaType = RemovableMedia;
01920                 }
01921                 else {
01922                     fdoExt->DiskGeometry.MediaType = FixedMedia;
01923                 }
01924             }
01925             else {
01926                 /*
01927                  *  The request failed.
01928                  */
01929 
01930                 //
01931                 // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
01932                 //    what happens when the disk's sector size is bigger than
01933                 //    512 bytes and we hit this code path?  this is untested.
01934                 //
01935                 // If the read capacity fails, set the geometry to reasonable parameter
01936                 // so things don't fail at unexpected places.  Zero the geometry
01937                 // except for the bytes per sector and sector shift.
01938                 //
01939 
01940                 /*
01941                  *  This request can sometimes fail legitimately
01942                  *  (e.g. when a SCSI device is attached but turned off)
01943                  *  so this is not necessarily a device/driver bug.
01944                  */
01945                 DBGTRACE(ClassDebugWarning, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo, status));
01946 
01947                 /*
01948                  *  Write in a default disk geometry which we HOPE is right (??).
01949                  *      BUGBUG !!
01950                  */
01951                 RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
01952                 fdoExt->DiskGeometry.BytesPerSector = 512;
01953                 fdoExt->SectorShift = 9;
01954                 fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
01955 
01956                 /*
01957                  *  Is this removable or fixed media
01958                  */
01959                 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
01960                     fdoExt->DiskGeometry.MediaType = RemovableMedia;
01961                 }
01962                 else {
01963                     fdoExt->DiskGeometry.MediaType = FixedMedia;
01964                 }
01965             }
01966 
01967         }
01968         else {
01969             status = STATUS_INSUFFICIENT_RESOURCES;
01970         }
01971 
01972         FreeDeviceInputMdl(driveCapMdl);
01973     }
01974     else {
01975         status = STATUS_INSUFFICIENT_RESOURCES;
01976     }
01977 
01978     return status;
01979 }
01980 
01981 
01982 /*++////////////////////////////////////////////////////////////////////////////
01983 
01984 ClassSendStartUnit()
01985 
01986 Routine Description:
01987 
01988     Send command to SCSI unit to start or power up.
01989     Because this command is issued asynchronounsly, that is, without
01990     waiting on it to complete, the IMMEDIATE flag is not set. This
01991     means that the CDB will not return until the drive has powered up.
01992     This should keep subsequent requests from being submitted to the
01993     device before it has completely spun up.
01994 
01995     This routine is called from the InterpretSense routine, when a
01996     request sense returns data indicating that a drive must be
01997     powered up.
01998 
01999     This routine may also be called from a class driver's error handler,
02000     or anytime a non-critical start device should be sent to the device.
02001 
02002 Arguments:
02003 
02004     Fdo - The functional device object for the stopped device.
02005 
02006 Return Value:
02007 
02008     None.
02009 
02010 --*/
02011 VOID
02012 ClassSendStartUnit(
02013     IN PDEVICE_OBJECT Fdo
02014     )
02015 {
02016     PIO_STACK_LOCATION irpStack;
02017     PIRP irp;
02018     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
02019     PSCSI_REQUEST_BLOCK srb;
02020     PCOMPLETION_CONTEXT context;
02021     PCDB cdb;
02022 
02023     //
02024     // Allocate Srb from nonpaged pool.
02025     //
02026 
02027     context = ExAllocatePoolWithTag(NonPagedPool,
02028                              sizeof(COMPLETION_CONTEXT),
02029                              '6CcS');
02030 
02031     if(context == NULL) {
02032 
02033         //
02034         // ISSUE-2000/02/03-peterwie
02035         // This code path was inheritted from the NT 4.0 class2.sys driver.
02036         // It needs to be changed to survive low-memory conditions.
02037         //
02038 
02039         KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
02040     }
02041 
02042     //
02043     // Save the device object in the context for use by the completion
02044     // routine.
02045     //
02046 
02047     context->DeviceObject = Fdo;
02048     srb = &context->Srb;
02049 
02050     //
02051     // Zero out srb.
02052     //
02053 
02054     RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
02055 
02056     //
02057     // Write length to SRB.
02058     //
02059 
02060     srb->Length = sizeof(SCSI_REQUEST_BLOCK);
02061 
02062     srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
02063 
02064     //
02065     // Set timeout value large enough for drive to spin up.
02066     //
02067 
02068     srb->TimeOutValue = START_UNIT_TIMEOUT;
02069 
02070     //
02071     // Set the transfer length.
02072     //
02073 
02074     srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
02075                     SRB_FLAGS_DISABLE_AUTOSENSE |
02076                     SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
02077 
02078     //
02079     // Build the start unit CDB.
02080     //
02081 
02082     srb->CdbLength = 6;
02083     cdb = (PCDB)srb->Cdb;
02084 
02085     cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
02086     cdb->START_STOP.Start = 1;
02087     cdb->START_STOP.Immediate = 0;
02088     cdb->START_STOP.LogicalUnitNumber = srb->Lun;
02089 
02090     //
02091     // Build the asynchronous request to be sent to the port driver.
02092     // Since this routine is called from a DPC the IRP should always be
02093     // available.
02094     //
02095 
02096     irp = IoAllocateIrp(Fdo->StackSize, FALSE);
02097 
02098     if(irp == NULL) {
02099 
02100         //
02101         // ISSUE-2000/02/03-peterwie
02102         // This code path was inheritted from the NT 4.0 class2.sys driver.
02103         // It needs to be changed to survive low-memory conditions.
02104         //
02105 
02106         KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
02107 
02108     }
02109 
02110     ClassAcquireRemoveLock(Fdo, irp);
02111 
02112     IoSetCompletionRoutine(irp,
02113                            (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
02114                            context,
02115                            TRUE,
02116                            TRUE,
02117                            TRUE);
02118 
02119     irpStack = IoGetNextIrpStackLocation(irp);
02120     irpStack->MajorFunction = IRP_MJ_SCSI;
02121     srb->OriginalRequest = irp;
02122 
02123     //
02124     // Store the SRB address in next stack for port driver.
02125     //
02126 
02127     irpStack->Parameters.Scsi.Srb = srb;
02128 
02129     //
02130     // Call the port driver with the IRP.
02131     //
02132 
02133     IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
02134 
02135     return;
02136 
02137 } // end StartUnit()
02138 
02139 /*++////////////////////////////////////////////////////////////////////////////
02140 
02141 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
02142 
02143 Routine Description:
02144 
02145     This routine is called when an asynchronous I/O request
02146     which was issused by the class driver completes.  Examples of such requests
02147     are release queue or START UNIT. This routine releases the queue if
02148     necessary.  It then frees the context and the IRP.
02149 
02150 Arguments:
02151 
02152     DeviceObject - The device object for the logical unit; however since this
02153         is the top stack location the value is NULL.
02154 
02155     Irp - Supplies a pointer to the Irp to be processed.
02156 
02157     Context - Supplies the context to be used to process this request.
02158 
02159 Return Value:
02160 
02161     None.
02162 
02163 --*/
02164 NTSTATUS
02165 ClassAsynchronousCompletion(
02166     PDEVICE_OBJECT DeviceObject,
02167     PIRP Irp,
02168     PVOID Context
02169     )
02170 {
02171     PCOMPLETION_CONTEXT context = Context;
02172     PSCSI_REQUEST_BLOCK srb;
02173 
02174     if(DeviceObject == NULL) {
02175 
02176         DeviceObject = context->DeviceObject;
02177     }
02178 
02179     srb = &context->Srb;
02180 
02181     //
02182     // If this is an execute srb, then check the return status and make sure.
02183     // the queue is not frozen.
02184     //
02185 
02186     if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
02187 
02188         //
02189         // Check for a frozen queue.
02190         //
02191 
02192         if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
02193 
02194             //
02195             // Unfreeze the queue getting the device object from the context.
02196             //
02197 
02198             ClassReleaseQueue(context->DeviceObject);
02199         }
02200     }
02201 
02202     { // free port-allocated sense buffer if we can detect
02203 
02204         if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
02205 
02206             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
02207             if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
02208                 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
02209             }
02210 
02211         } else {
02212 
02213             ASSERT(!TEST_FLAG(srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
02214 
02215         }
02216     }
02217 
02218 
02219     //
02220     // Free the context and the Irp.
02221     //
02222 
02223     if (Irp->MdlAddress != NULL) {
02224         MmUnlockPages(Irp->MdlAddress);
02225         IoFreeMdl(Irp->MdlAddress);
02226 
02227         Irp->MdlAddress = NULL;
02228     }
02229 
02230     ClassReleaseRemoveLock(DeviceObject, Irp);
02231 
02232     ExFreePool(context);
02233     IoFreeIrp(Irp);
02234 
02235     //
02236     // Indicate the I/O system should stop processing the Irp completion.
02237     //
02238 
02239     return STATUS_MORE_PROCESSING_REQUIRED;
02240 
02241 } // end ClassAsynchronousCompletion()
02242 
02243 
02244 
02245 VOID ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp)
02246 {
02247     PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
02248     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
02249     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
02250     PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
02251     PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
02252     ULONG entireXferLen = currentSp->Parameters.Read.Length;
02253     PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
02254     LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
02255     PTRANSFER_PACKET pkt;
02256     SINGLE_LIST_ENTRY pktList;
02257     PSINGLE_LIST_ENTRY slistEntry;
02258     ULONG numPackets;
02259     KIRQL oldIrql;
02260     ULONG i;
02261 
02262     /*
02263      *  Compute the number of hw xfers we'll have to do.
02264      *  Calculate this without allowing for an overflow condition.
02265      */
02266     ASSERT(fdoData->HwMaxXferLen >= PAGE_SIZE);
02267     numPackets = entireXferLen/fdoData->HwMaxXferLen;
02268     if (entireXferLen % fdoData->HwMaxXferLen){
02269         numPackets++;
02270     }
02271 
02272     /*
02273      *  First get all the TRANSFER_PACKETs that we'll need at once.
02274      *  Use our 'simple' slist functions since we don't need interlocked.
02275      */
02276     SimpleInitSlistHdr(&pktList);
02277     for (i = 0; i < numPackets; i++){
02278         pkt = DequeueFreeTransferPacket(Fdo, TRUE);
02279         if (pkt){
02280             SimplePushSlist(&pktList, &pkt->SlistEntry);
02281         }
02282         else {
02283             break;
02284         }
02285     }
02286 
02287     if (i == numPackets){
02288         /*
02289          *  Initialize the original IRP's status to success.
02290          *  If any of the packets fail, they will set it to an error status.
02291          *  The IoStatus.Information field will be incremented to the
02292          *  transfer length as the pieces complete.
02293          */
02294         Irp->IoStatus.Status = STATUS_SUCCESS;
02295         Irp->IoStatus.Information = 0;
02296 
02297         /*
02298          *  Store the number of transfer pieces inside the original IRP.
02299          *  It will be used to count down the pieces as they complete.
02300          */
02301         Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
02302 
02303         /*
02304          *  We are proceeding with the transfer.
02305          *  Mark the client IRP pending since it may complete on a different thread.
02306          */
02307         IoMarkIrpPending(Irp);
02308 
02309         /*
02310          *  Transmit the pieces of the transfer.
02311          */
02312         while (entireXferLen > 0){
02313             ULONG thisPieceLen = MIN(fdoData->HwMaxXferLen, entireXferLen);
02314 
02315             /*
02316              *  Set up a TRANSFER_PACKET for this piece and send it.
02317              */
02318             slistEntry = SimplePopSlist(&pktList);
02319             ASSERT(slistEntry);
02320             pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
02321             SetupReadWriteTransferPacket(   pkt,
02322                                         bufPtr,
02323                                         thisPieceLen,
02324                                         targetLocation,
02325                                         Irp);
02326             SubmitTransferPacket(pkt);
02327 
02328             entireXferLen -= thisPieceLen;
02329             bufPtr += thisPieceLen;
02330             targetLocation.QuadPart += thisPieceLen;
02331         }
02332         ASSERT(SimpleIsSlistEmpty(&pktList));
02333     }
02334     else if (i >= 1){
02335         /*
02336          *  We were unable to get all the TRANSFER_PACKETs we need,
02337          *  but we did get at least one.
02338          *  That means that we are in extreme low-memory stress.
02339          *  We'll try doing this transfer using a single packet.
02340          *  The port driver is certainly also in stress, so use one-page
02341          *  transfers.
02342          */
02343 
02344         /*
02345          *  Free all but one of the TRANSFER_PACKETs.
02346          */
02347         while (i-- > 1){
02348             slistEntry = SimplePopSlist(&pktList);
02349             ASSERT(slistEntry);
02350             pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
02351             EnqueueFreeTransferPacket(Fdo, pkt);
02352         }
02353 
02354         /*
02355          *  Get the single TRANSFER_PACKET that we'll be using.
02356          */
02357         slistEntry = SimplePopSlist(&pktList);
02358         ASSERT(slistEntry);
02359         ASSERT(SimpleIsSlistEmpty(&pktList));
02360         pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
02361         DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt));
02362 
02363         /*
02364          *  Set default status and the number of transfer packets (one)
02365          *  inside the original irp.
02366          */
02367         Irp->IoStatus.Status = STATUS_SUCCESS;
02368         Irp->IoStatus.Information = 0;
02369         Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
02370 
02371         /*
02372          *  Mark the client irp pending since it may complete on
02373          *  another thread.
02374          */
02375         IoMarkIrpPending(Irp);
02376 
02377         /*
02378          *  Set up the TRANSFER_PACKET for a lowMem transfer and launch.
02379          */
02380         SetupReadWriteTransferPacket(  pkt,
02381                                     bufPtr,
02382                                     entireXferLen,
02383                                     targetLocation,
02384                                     Irp);
02385         InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
02386         StepLowMemRetry(pkt);
02387     }
02388     else {
02389         /*
02390          *  We were unable to get ANY TRANSFER_PACKETs.
02391          *  Defer this client irp until some TRANSFER_PACKETs free up.
02392          */
02393         DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp));
02394         IoMarkIrpPending(Irp);
02395         EnqueueDeferredClientIrp(fdoData, Irp);
02396     }
02397 
02398 }
02399 
02400 
02401 /*++////////////////////////////////////////////////////////////////////////////
02402 
02403 ClassIoComplete()
02404 
02405 Routine Description:
02406 
02407     This routine executes when the port driver has completed a request.
02408     It looks at the SRB status in the completing SRB and if not success
02409     it checks for valid request sense buffer information. If valid, the
02410     info is used to update status with more precise message of type of
02411     error. This routine deallocates the SRB.
02412 
02413     This routine should only be placed on the stack location for a class
02414     driver FDO.
02415 
02416 Arguments:
02417 
02418     Fdo - Supplies the device object which represents the logical
02419         unit.
02420 
02421     Irp - Supplies the Irp which has completed.
02422 
02423     Context - Supplies a pointer to the SRB.
02424 
02425 Return Value:
02426 
02427     NT status
02428 
02429 --*/
02430 NTSTATUS
02431 ClassIoComplete(
02432     IN PDEVICE_OBJECT Fdo,
02433     IN PIRP Irp,
02434     IN PVOID Context
02435     )
02436 {
02437     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
02438     PSCSI_REQUEST_BLOCK srb = Context;
02439     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
02440     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
02441     NTSTATUS status;
02442     BOOLEAN retry;
02443     BOOLEAN callStartNextPacket;
02444 
02445     ASSERT(fdoExtension->CommonExtension.IsFdo);
02446 
02447     //
02448     // Check SRB status for success of completing request.
02449     //
02450 
02451     if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
02452         ULONG retryInterval;
02453 
02454         DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
02455 
02456         //
02457         // Release the queue if it is frozen.
02458         //
02459 
02460         if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
02461             ClassReleaseQueue(Fdo);
02462         }
02463 
02464         retry = ClassInterpretSenseInfo(
02465                     Fdo,
02466                     srb,
02467                     irpStack->MajorFunction,
02468                     irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
02469                      irpStack->Parameters.DeviceIoControl.IoControlCode :
02470                      0,
02471                     MAXIMUM_RETRIES -
02472                         ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
02473                     &status,
02474                     &retryInterval);
02475 
02476         //
02477         // If the status is verified required and the this request
02478         // should bypass verify required then retry the request.
02479         //
02480 
02481         if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
02482             status == STATUS_VERIFY_REQUIRED) {
02483 
02484             status = STATUS_IO_DEVICE_ERROR;
02485             retry = TRUE;
02486         }
02487 
02488         if (retry && ((*(PCHAR*)&irpStack->Parameters.Others.Argument4)--)) {
02489 
02490             //
02491             // Retry request.
02492             //
02493 
02494             DebugPrint((1, "Retry request %p\n", Irp));
02495 
02496             if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
02497                 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
02498             }
02499 
02500             RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
02501             return STATUS_MORE_PROCESSING_REQUIRED;
02502         }
02503 
02504     } else {
02505 
02506         //
02507         // Set status for successful request
02508         //
02509         fdoData->LoggedTURFailureSinceLastIO = FALSE;
02510         ClasspPerfIncrementSuccessfulIo(fdoExtension);
02511         status = STATUS_SUCCESS;
02512     } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
02513 
02514 
02515     //
02516     // ensure we have returned some info, and it matches what the
02517     // original request wanted for PAGING operations only
02518     //
02519 
02520     if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
02521         ASSERT(Irp->IoStatus.Information != 0);
02522         ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
02523     }
02524 
02525     //
02526     // remember if the caller wanted to skip calling IoStartNextPacket.
02527     // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
02528     // calls.  this setting only affects device objects with StartIo routines.
02529     //
02530 
02531     callStartNextPacket = !TEST_FLAG(srb->SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
02532     if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
02533         callStartNextPacket = FALSE;
02534     }
02535 
02536     //
02537     // Free the srb
02538     //
02539 
02540     if(!TEST_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
02541 
02542         if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
02543             FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
02544         }
02545 
02546         if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
02547             ClassFreeOrReuseSrb(fdoExtension, srb);
02548         }
02549         else {
02550             DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
02551             ExFreePool(srb);
02552         }
02553 
02554     } else {
02555 
02556         DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
02557                     "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
02558         if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
02559             DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
02560                         " because SRB_CLASS_FLAGS_PERSISTANT set\n",
02561                         srb->SenseInfoBuffer));
02562         }
02563 
02564     }
02565 
02566     //
02567     // Set status in completing IRP.
02568     //
02569 
02570     Irp->IoStatus.Status = status;
02571 
02572     //
02573     // Set the hard error if necessary.
02574     //
02575 
02576     if (!NT_SUCCESS(status) &&
02577         IoIsErrorUserInduced(status) &&
02578         (Irp->Tail.Overlay.Thread != NULL)
02579         ) {
02580 
02581         //
02582         // Store DeviceObject for filesystem, and clear
02583         // in IoStatus.Information field.
02584         //
02585 
02586         IoSetHardErrorOrVerifyDevice(Irp, Fdo);
02587         Irp->IoStatus.Information = 0;
02588     }
02589 
02590     //
02591     // If pending has be returned for this irp then mark the current stack as
02592     // pending.
02593     //
02594 
02595     if (Irp->PendingReturned) {
02596         IoMarkIrpPending(Irp);
02597     }
02598 
02599     if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
02600         if (callStartNextPacket) {
02601             KIRQL oldIrql;
02602             KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
02603             IoStartNextPacket(Fdo, FALSE);
02604             KeLowerIrql(oldIrql);
02605         }
02606     }
02607 
02608     ClassReleaseRemoveLock(Fdo, Irp);
02609 
02610     return status;
02611 
02612 } // end ClassIoComplete()
02613 
02614 
02615 /*++////////////////////////////////////////////////////////////////////////////
02616 
02617 ClassSendSrbSynchronous()
02618 
02619 Routine Description:
02620 
02621     This routine is called by SCSI device controls to complete an
02622     SRB and send it to the port driver synchronously (ie wait for
02623     completion). The CDB is already completed along with the SRB CDB
02624     size and request timeout value.
02625 
02626 Arguments:
02627 
02628     Fdo - Supplies the functional device object which represents the target.
02629 
02630     Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
02631 
02632     BufferAddress - Supplies the address of the buffer.
02633 
02634     BufferLength - Supplies the length in bytes of the buffer.
02635 
02636     WriteToDevice - Indicates the data should be transfer to the device.
02637 
02638 Return Value:
02639 
02640     NTSTATUS indicating the final results of the operation.
02641 
02642     If NT_SUCCESS(), then the amount of usable data is contained in the field
02643        Srb->DataTransferLength
02644 
02645 --*/
02646 NTSTATUS
02647 ClassSendSrbSynchronous(
02648     PDEVICE_OBJECT Fdo,
02649     PSCSI_REQUEST_BLOCK Srb,
02650     PVOID BufferAddress,
02651     ULONG BufferLength,
02652     BOOLEAN WriteToDevice
02653     )
02654 {
02655 
02656     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
02657     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
02658     IO_STATUS_BLOCK ioStatus;
02659     ULONG controlType;
02660     PIRP irp;
02661     PIO_STACK_LOCATION irpStack;
02662     KEVENT event;
02663     PUCHAR senseInfoBuffer;
02664     ULONG retryCount = MAXIMUM_RETRIES;
02665     NTSTATUS status;
02666     BOOLEAN retry;
02667 
02668     //
02669     // NOTE: This code is only pagable because we are not freezing
02670     //       the queue.  Allowing the queue to be frozen from a pagable
02671     //       routine could leave the queue frozen as we try to page in
02672     //       the code to unfreeze the queue.  The result would be a nice
02673     //       case of deadlock.  Therefore, since we are unfreezing the
02674     //       queue regardless of the result, just set the NO_FREEZE_QUEUE
02675     //       flag in the SRB.
02676     //
02677 
02678     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
02679     ASSERT(fdoExtension->CommonExtension.IsFdo);
02680 
02681     //
02682     // Write length to SRB.
02683     //
02684 
02685     Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
02686 
02687     //
02688     // Set SCSI bus address.
02689     //
02690 
02691     Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
02692 
02693     //
02694     // Enable auto request sense.
02695     //
02696 
02697     Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
02698 
02699     //
02700     // Sense buffer is in aligned nonpaged pool.
02701     //
02702         //
02703     senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
02704                                      SENSE_BUFFER_SIZE,
02705                                      '7CcS');
02706 
02707     if (senseInfoBuffer == NULL) {
02708 
02709         DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
02710                        "buffer\n"));
02711         return(STATUS_INSUFFICIENT_RESOURCES);
02712     }
02713 
02714     Srb->SenseInfoBuffer = senseInfoBuffer;
02715     Srb->DataBuffer = BufferAddress;
02716 
02717     //
02718     // Start retries here.
02719     //
02720 
02721 retry:
02722 
02723     //
02724     // use fdoextension's flags by default.
02725     // do not move out of loop, as the flag may change due to errors
02726     // sending this command.
02727     //
02728 
02729     Srb->SrbFlags = fdoExtension->SrbFlags;
02730 
02731     if(BufferAddress != NULL) {
02732         if(WriteToDevice) {
02733             SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
02734         } else {
02735             SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
02736         }
02737     }
02738 
02739     //
02740     // Initialize the QueueAction field.
02741     //
02742 
02743     Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
02744 
02745     //
02746     // Disable synchronous transfer for these requests.
02747     // Disable freezing the queue, since all we do is unfreeze it anyways.
02748     //
02749 
02750     SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
02751     SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
02752 
02753     //
02754     // Set the event object to the unsignaled state.
02755     // It will be used to signal request completion.
02756     //
02757 
02758     KeInitializeEvent(&event, NotificationEvent, FALSE);
02759 
02760     //
02761     // Build device I/O control request with METHOD_NEITHER data transfer.
02762     // We'll queue a completion routine to cleanup the MDL's and such ourself.
02763     //
02764 
02765     irp = IoAllocateIrp(
02766             (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
02767             FALSE);
02768 
02769     if(irp == NULL) {
02770         ExFreePool(senseInfoBuffer);
02771         DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
02772         return(STATUS_INSUFFICIENT_RESOURCES);
02773     }
02774 
02775     //
02776     // Get next stack location.
02777     //
02778 
02779     irpStack = IoGetNextIrpStackLocation(irp);
02780 
02781     //
02782     // Set up SRB for execute scsi request. Save SRB address in next stack
02783     // for the port driver.
02784     //
02785 
02786     irpStack->MajorFunction = IRP_MJ_SCSI;
02787     irpStack->Parameters.Scsi.Srb = Srb;
02788 
02789     IoSetCompletionRoutine(irp,
02790                            ClasspSendSynchronousCompletion,
02791                            Srb,
02792                            TRUE,
02793                            TRUE,
02794                            TRUE);
02795 
02796     irp->UserIosb = &ioStatus;
02797     irp->UserEvent = &event;
02798 
02799     if(BufferAddress) {
02800         //
02801         // Build an MDL for the data buffer and stick it into the irp.  The
02802         // completion routine will unlock the pages and free the MDL.
02803         //
02804 
02805         irp->MdlAddress = IoAllocateMdl( BufferAddress,
02806                                          BufferLength,
02807                                          FALSE,
02808                                          FALSE,
02809                                          irp );
02810         if (irp->MdlAddress == NULL) {
02811             ExFreePool(senseInfoBuffer);
02812             Srb->SenseInfoBuffer = NULL;
02813             IoFreeIrp( irp );
02814             DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
02815             return STATUS_INSUFFICIENT_RESOURCES;
02816         }
02817 
02818         _SEH2_TRY {
02819 
02820             //
02821             // the io manager unlocks these pages upon completion
02822             //
02823 
02824             MmProbeAndLockPages( irp->MdlAddress,
02825                                  KernelMode,
02826                                  (WriteToDevice ? IoReadAccess :
02827                                                   IoWriteAccess));
02828 
02829         } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
02830             status = _SEH2_GetExceptionCode();
02831 
02832             ExFreePool(senseInfoBuffer);
02833             Srb->SenseInfoBuffer = NULL;
02834             IoFreeMdl(irp->MdlAddress);
02835             IoFreeIrp(irp);
02836 
02837             DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
02838                            "locking buffer\n", status));
02839             return status;
02840         } _SEH2_END;
02841     }
02842 
02843     //
02844     // Set the transfer length.
02845     //
02846 
02847     Srb->DataTransferLength = BufferLength;
02848 
02849     //
02850     // Zero out status.
02851     //
02852 
02853     Srb->ScsiStatus = Srb->SrbStatus = 0;
02854     Srb->NextSrb = 0;
02855 
02856     //
02857     // Set up IRP Address.
02858     //
02859 
02860     Srb->OriginalRequest = irp;
02861 
02862     //
02863     // Call the port driver with the request and wait for it to complete.
02864     //
02865 
02866     status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
02867 
02868     if (status == STATUS_PENDING) {
02869         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
02870         status = ioStatus.Status;
02871     }
02872 
02873     //
02874     // Check that request completed without error.
02875     //
02876 
02877     if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
02878 
02879         ULONG retryInterval;
02880 
02881         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)));
02882 
02883         //
02884         // assert that the queue is not frozen
02885         //
02886 
02887         ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
02888 
02889         //
02890         // Update status and determine if request should be retried.
02891         //
02892 
02893         retry = ClassInterpretSenseInfo(Fdo,
02894                                         Srb,
02895                                         IRP_MJ_SCSI,
02896                                         0,
02897                                         MAXIMUM_RETRIES  - retryCount,
02898                                         &status,
02899                                         &retryInterval);
02900 
02901 
02902         if (retry) {
02903 
02904             if ((status == STATUS_DEVICE_NOT_READY &&
02905                  ((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode ==
02906                                 SCSI_ADSENSE_LUN_NOT_READY) ||
02907                 (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
02908 
02909                 LARGE_INTEGER delay;
02910 
02911                 //
02912                 // Delay for at least 2 seconds.
02913                 //
02914 
02915                 if(retryInterval < 2) {
02916                     retryInterval = 2;
02917                 }
02918 
02919                 delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval);
02920 
02921                 //
02922                 // Stall for a while to let the device become ready
02923                 //
02924 
02925                 KeDelayExecutionThread(KernelMode, FALSE, &delay);
02926 
02927             }
02928 
02929             //
02930             // If retries are not exhausted then retry this operation.
02931             //
02932 
02933             if (retryCount--) {
02934 
02935                 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
02936                     FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
02937                 }
02938 
02939                 goto retry;
02940             }
02941         }
02942 
02943     } else {
02944         fdoData->LoggedTURFailureSinceLastIO = FALSE;
02945         status = STATUS_SUCCESS;
02946     }
02947 
02948     //
02949     // required even though we allocated our own, since the port driver may
02950     // have allocated one also
02951     //
02952 
02953     if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
02954         FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
02955     }
02956 
02957     Srb->SenseInfoBuffer = NULL;
02958     ExFreePool(senseInfoBuffer);
02959 
02960     return status;
02961 }
02962 
02963 
02964 /*++////////////////////////////////////////////////////////////////////////////
02965 
02966 ClassInterpretSenseInfo()
02967 
02968 Routine Description:
02969 
02970     This routine interprets the data returned from the SCSI
02971     request sense. It determines the status to return in the
02972     IRP and whether this request can be retried.
02973 
02974 Arguments:
02975 
02976     DeviceObject - Supplies the device object associated with this request.
02977 
02978     Srb - Supplies the scsi request block which failed.
02979 
02980     MajorFunctionCode - Supplies the function code to be used for logging.
02981 
02982     IoDeviceCode - Supplies the device code to be used for logging.
02983 
02984     Status - Returns the status for the request.
02985 
02986 Return Value:
02987 
02988     BOOLEAN TRUE: Drivers should retry this request.
02989             FALSE: Drivers should not retry this request.
02990 
02991 --*/
02992 BOOLEAN
02993 ClassInterpretSenseInfo(
02994     IN PDEVICE_OBJECT Fdo,
02995     IN PSCSI_REQUEST_BLOCK Srb,
02996     IN UCHAR MajorFunctionCode,
02997     IN ULONG IoDeviceCode,
02998     IN ULONG RetryCount,
02999     OUT NTSTATUS *Status,
03000     OUT OPTIONAL ULONG *RetryInterval
03001     )
03002 {
03003     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
03004     PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
03005     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
03006 
03007     PSENSE_DATA       senseBuffer = Srb->SenseInfoBuffer;
03008 
03009     BOOLEAN           retry = TRUE;
03010     BOOLEAN           logError = FALSE;
03011     BOOLEAN           unhandledError = FALSE;
03012     BOOLEAN           incrementErrorCount = FALSE;
03013 
03014     ULONG             badSector = 0;
03015     ULONG             uniqueId = 0;
03016 
03017     NTSTATUS          logStatus;
03018 
03019     ULONG             readSector;
03020     ULONG             index;
03021 
03022     ULONG             retryInterval = 0;
03023     KIRQL oldIrql;
03024 
03025 
03026     logStatus = -1;
03027 
03028     if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
03029 
03030         //
03031         // Log anything remotely incorrect about paging i/o
03032         //
03033 
03034         logError = TRUE;
03035         uniqueId = 301;
03036         logStatus = IO_WARNING_PAGING_FAILURE;
03037     }
03038 
03039     //
03040     // Check that request sense buffer is valid.
03041     //
03042 
03043     ASSERT(fdoExtension->CommonExtension.IsFdo);
03044 
03045 
03046     //
03047     // must handle the SRB_STATUS_INTERNAL_ERROR case first,
03048     // as it has  all the flags set.
03049     //
03050 
03051     if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
03052 
03053         DebugPrint((ClassDebugSenseInfo,
03054                     "ClassInterpretSenseInfo: Internal Error code is %x\n",
03055                     Srb->InternalStatus));
03056 
03057         retry = FALSE;
03058         *Status = Srb->InternalStatus;
03059 
03060     } else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
03061         (Srb->SenseInfoBufferLength >=
03062             offsetof(SENSE_DATA, CommandSpecificInformation))) {
03063 
03064         //
03065         // Zero the additional sense code and additional sense code qualifier
03066         // if they were not returned by the device.
03067         //
03068 
03069         readSector = senseBuffer->AdditionalSenseLength +
03070             offsetof(SENSE_DATA, AdditionalSenseLength);
03071 
03072         if (readSector > Srb->SenseInfoBufferLength) {
03073             readSector = Srb->SenseInfoBufferLength;
03074         }
03075 
03076         if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCode)) {
03077             senseBuffer->AdditionalSenseCode = 0;
03078         }
03079 
03080         if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCodeQualifier)) {
03081             senseBuffer->AdditionalSenseCodeQualifier = 0;
03082         }
03083 
03084         DebugPrint((ClassDebugSenseInfo,
03085                     "ClassInterpretSenseInfo: Error code is %x\n",
03086                     senseBuffer->ErrorCode));
03087         DebugPrint((ClassDebugSenseInfo,
03088                     "ClassInterpretSenseInfo: Sense key is %x\n",
03089                     senseBuffer->SenseKey));
03090         DebugPrint((ClassDebugSenseInfo,
03091                     "ClassInterpretSenseInfo: Additional sense code is %x\n",
03092                     senseBuffer->AdditionalSenseCode));
03093         DebugPrint((ClassDebugSenseInfo,
03094                     "ClassInterpretSenseInfo: Additional sense code qualifier "
03095                     "is %x\n",
03096                     senseBuffer->AdditionalSenseCodeQualifier));
03097 
03098 
03099         switch (senseBuffer->SenseKey & 0xf) {
03100 
03101         case SCSI_SENSE_NOT_READY: {
03102 
03103             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03104                         "Device not ready\n"));
03105             *Status = STATUS_DEVICE_NOT_READY;
03106 
03107             switch (senseBuffer->AdditionalSenseCode) {
03108 
03109             case SCSI_ADSENSE_LUN_NOT_READY: {
03110 
03111                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03112                             "Lun not ready\n"));
03113 
03114                 switch (senseBuffer->AdditionalSenseCodeQualifier) {
03115 
03116                 case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
03117                     DEVICE_EVENT_BECOMING_READY notReady;
03118 
03119                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03120                                 "Operation In Progress\n"));
03121                     retryInterval = NOT_READY_RETRY_INTERVAL;
03122 
03123                     RtlZeroMemory(&notReady, sizeof(DEVICE_EVENT_BECOMING_READY));
03124                     notReady.Version = 1;
03125                     notReady.Reason = 2;
03126                     notReady.Estimated100msToReady = retryInterval * 10;
03127                     ClasspSendNotification(fdoExtension,
03128                                            &GUID_IO_DEVICE_BECOMING_READY,
03129                                            sizeof(DEVICE_EVENT_BECOMING_READY),
03130                                            &notReady);
03131 
03132                     break;
03133                 }
03134 
03135                 case SCSI_SENSEQ_BECOMING_READY: {
03136                     DEVICE_EVENT_BECOMING_READY notReady;
03137 
03138                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03139                                 "In process of becoming ready\n"));
03140                     retryInterval = NOT_READY_RETRY_INTERVAL;
03141 
03142                     RtlZeroMemory(&notReady, sizeof(DEVICE_EVENT_BECOMING_READY));
03143                     notReady.Version = 1;
03144                     notReady.Reason = 1;
03145                     notReady.Estimated100msToReady = retryInterval * 10;
03146                     ClasspSendNotification(fdoExtension,
03147                                            &GUID_IO_DEVICE_BECOMING_READY,
03148                                            sizeof(DEVICE_EVENT_BECOMING_READY),
03149                                            &notReady);
03150                     break;
03151                 }
03152 
03153                 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
03154                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03155                                 "Long write in progress\n"));
03156                     retry = FALSE;
03157                     break;
03158                 }
03159 
03160                 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
03161                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03162                                 "Manual intervention required\n"));
03163                     *Status = STATUS_NO_MEDIA_IN_DEVICE;
03164                     retry = FALSE;
03165                     break;
03166                 }
03167 
03168                 case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
03169                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03170                                 "Format in progress\n"));
03171                     retry = FALSE;
03172                     break;
03173                 }
03174 
03175                 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
03176 
03177                     if(!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
03178                                  CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
03179 
03180                         DebugPrint((ClassDebugSenseInfo,
03181                                     "ClassInterpretSenseInfo: "
03182                                     "not ready, cause unknown\n"));
03183                         /*
03184                         Many non-WHQL certified drives (mostly CD-RW) return
03185                         this when they have no media instead of the obvious
03186                         choice of:
03187 
03188                         SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
03189 
03190                         These drives should not pass WHQL certification due
03191                         to this discrepency.
03192 
03193                         */
03194                         retry = FALSE;
03195                         break;
03196 
03197                     } else {
03198 
03199                         //
03200                         // Treat this as init command required and fall through.
03201                         //
03202                     }
03203                 }
03204 
03205                 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
03206                 default: {
03207                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03208                                 "Initializing command required\n"));
03209 
03210                     //
03211                     // This sense code/additional sense code
03212                     // combination may indicate that the device
03213                     // needs to be started.  Send an start unit if this
03214                     // is a disk device.
03215                     //
03216 
03217                     if(TEST_FLAG(fdoExtension->DeviceFlags,
03218                                  DEV_SAFE_START_UNIT) &&
03219                         !TEST_FLAG(Srb->SrbFlags,
03220                                    SRB_CLASS_FLAGS_LOW_PRIORITY)) {
03221                         ClassSendStartUnit(Fdo);
03222                     }
03223                     break;
03224                 }
03225 
03226 
03227                 } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
03228                 break;
03229             }
03230 
03231             case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
03232                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03233                             "No Media in device.\n"));
03234                 *Status = STATUS_NO_MEDIA_IN_DEVICE;
03235                 retry = FALSE;
03236 
03237                 //
03238                 // signal MCN that there isn't any media in the device
03239                 //
03240                 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
03241                     DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
03242                                 "No Media in a non-removable device %p\n",
03243                                 Fdo));
03244                 }
03245                 ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
03246 
03247                 break;
03248             }
03249             } // end switch (senseBuffer->AdditionalSenseCode)
03250 
03251             break;
03252         } // end SCSI_SENSE_NOT_READY
03253 
03254         case SCSI_SENSE_DATA_PROTECT: {
03255             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03256                         "Media write protected\n"));
03257             *Status = STATUS_MEDIA_WRITE_PROTECTED;
03258             retry = FALSE;
03259             break;
03260         } // end SCSI_SENSE_DATA_PROTECT
03261 
03262         case SCSI_SENSE_MEDIUM_ERROR: {
03263             DebugPrint((ClassDebugSenseInfo,"ClassInterpretSenseInfo: "
03264                         "Medium Error (bad block)\n"));
03265             *Status = STATUS_DEVICE_DATA_ERROR;
03266 
03267             retry = FALSE;
03268             logError = TRUE;
03269             uniqueId = 256;
03270             logStatus = IO_ERR_BAD_BLOCK;
03271 
03272             //
03273             // Check if this error is due to unknown format
03274             //
03275             if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA){
03276 
03277                 switch (senseBuffer->AdditionalSenseCodeQualifier) {
03278 
03279                 case SCSI_SENSEQ_UNKNOWN_FORMAT: {
03280 
03281                     *Status = STATUS_UNRECOGNIZED_MEDIA;
03282 
03283                     //
03284                     // Log error only if this is a paging request
03285                     //
03286                     if(!TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
03287                         logError = FALSE;
03288                     }
03289                     break;
03290                 }
03291 
03292                 case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
03293 
03294                     *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
03295                     logError = FALSE;
03296                     break;
03297 
03298                 }
03299                 default: {
03300                     break;
03301                 }
03302                 } // end switch AdditionalSenseCodeQualifier
03303 
03304             } // end SCSI_ADSENSE_INVALID_MEDIA
03305 
03306             break;
03307 
03308         } // end SCSI_SENSE_MEDIUM_ERROR
03309 
03310         case SCSI_SENSE_HARDWARE_ERROR: {
03311             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03312                         "Hardware error\n"));
03313             *Status = STATUS_IO_DEVICE_ERROR;
03314             logError = TRUE;
03315             uniqueId = 257;
03316             logStatus = IO_ERR_CONTROLLER_ERROR;
03317             break;
03318         } // end SCSI_SENSE_HARDWARE_ERROR
03319 
03320         case SCSI_SENSE_ILLEGAL_REQUEST: {
03321 
03322             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03323                         "Illegal SCSI request\n"));
03324             *Status = STATUS_INVALID_DEVICE_REQUEST;
03325             retry = FALSE;
03326 
03327             switch (senseBuffer->AdditionalSenseCode) {
03328 
03329             case SCSI_ADSENSE_ILLEGAL_COMMAND: {
03330                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03331                             "Illegal command\n"));
03332                 break;
03333             }
03334 
03335             case SCSI_ADSENSE_ILLEGAL_BLOCK: {
03336                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03337                             "Illegal block address\n"));
03338                 *Status = STATUS_NONEXISTENT_SECTOR;
03339                 break;
03340             }
03341 
03342             case SCSI_ADSENSE_INVALID_LUN: {
03343                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03344                             "Invalid LUN\n"));
03345                 *Status = STATUS_NO_SUCH_DEVICE;
03346                 break;
03347             }
03348 
03349             case SCSI_ADSENSE_MUSIC_AREA: {
03350                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03351                             "Music area\n"));
03352                 break;
03353             }
03354 
03355             case SCSI_ADSENSE_DATA_AREA: {
03356                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03357                             "Data area\n"));
03358                 break;
03359             }
03360 
03361             case SCSI_ADSENSE_VOLUME_OVERFLOW: {
03362                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03363                             "Volume overflow\n"));
03364                 break;
03365             }
03366 
03367             case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
03368                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03369                             "Copy protection failure\n"));
03370 
03371                 *Status = STATUS_COPY_PROTECTION_FAILURE;
03372 
03373                 switch (senseBuffer->AdditionalSenseCodeQualifier) {
03374                     case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
03375                         DebugPrint((ClassDebugSenseInfo,
03376                                     "ClassInterpretSenseInfo: "
03377                                     "Authentication failure\n"));
03378                         *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
03379                         break;
03380                     case SCSI_SENSEQ_KEY_NOT_PRESENT:
03381                         DebugPrint((ClassDebugSenseInfo,
03382                                     "ClassInterpretSenseInfo: "
03383                                     "Key not present\n"));
03384                         *Status = STATUS_CSS_KEY_NOT_PRESENT;
03385                         break;
03386                     case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
03387                         DebugPrint((ClassDebugSenseInfo,
03388                                     "ClassInterpretSenseInfo: "
03389                                     "Key not established\n"));
03390                         *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
03391                         break;
03392                     case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
03393                         DebugPrint((ClassDebugSenseInfo,
03394                                     "ClassInterpretSenseInfo: "
03395                                     "Read of scrambled sector w/o "
03396                                     "authentication\n"));
03397                         *Status = STATUS_CSS_SCRAMBLED_SECTOR;
03398                         break;
03399                     case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
03400                         DebugPrint((ClassDebugSenseInfo,
03401                                     "ClassInterpretSenseInfo: "
03402                                     "Media region does not logical unit "
03403                                     "region\n"));
03404                         *Status = STATUS_CSS_REGION_MISMATCH;
03405                         break;
03406                     case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
03407                         DebugPrint((ClassDebugSenseInfo,
03408                                     "ClassInterpretSenseInfo: "
03409                                     "Region set error -- region may "
03410                                     "be permanent\n"));
03411                         *Status = STATUS_CSS_RESETS_EXHAUSTED;
03412                         break;
03413                 } // end switch of ASCQ for COPY_PROTECTION_FAILURE
03414 
03415                 break;
03416             }
03417 
03418 
03419             case SCSI_ADSENSE_INVALID_CDB: {
03420                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03421                             "Invalid CDB\n"));
03422 
03423                 //
03424                 // Note: the retry interval is not typically used.
03425                 // it is set here only because a ClassErrorHandler
03426                 // cannot set the retryInterval, and the error may
03427                 // require a few commands to be sent to clear whatever
03428                 // caused this condition (i.e. disk clears the write
03429                 // cache, requiring at least two commands)
03430                 //
03431                 // hopefully, this shortcoming can be changed for
03432                 // blackcomb.
03433                 //
03434 
03435                 retryInterval = 3;
03436                 break;
03437             }
03438 
03439             } // end switch (senseBuffer->AdditionalSenseCode)
03440 
03441             break;
03442         } // end SCSI_SENSE_ILLEGAL_REQUEST
03443 
03444         case SCSI_SENSE_UNIT_ATTENTION: {
03445 
03446             PVPB vpb;
03447             ULONG count;
03448 
03449             //
03450             // A media change may have occured so increment the change
03451             // count for the physical device
03452             //
03453 
03454             count = InterlockedIncrement(&fdoExtension->MediaChangeCount);
03455             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03456                         "Media change count for device %d incremented to %#lx\n",
03457                         fdoExtension->DeviceNumber, count));
03458 
03459 
03460             switch (senseBuffer->AdditionalSenseCode) {
03461             case SCSI_ADSENSE_MEDIUM_CHANGED: {
03462                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03463                             "Media changed\n"));
03464 
03465                 if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
03466                     DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
03467                                 "Media Changed on non-removable device %p\n",
03468                                 Fdo));
03469                 }
03470                 ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
03471                 break;
03472             }
03473 
03474             case SCSI_ADSENSE_BUS_RESET: {
03475                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03476                             "Bus reset\n"));
03477                 break;
03478             }
03479 
03480             case SCSI_ADSENSE_OPERATOR_REQUEST: {
03481                 switch (senseBuffer->AdditionalSenseCodeQualifier) {
03482 
03483                 case SCSI_SENSEQ_MEDIUM_REMOVAL: {
03484                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03485                                 "Ejection request received!\n"));
03486                     ClassSendEjectionNotification(fdoExtension);
03487                     break;
03488                 }
03489 
03490                 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
03491                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03492                                 "Operator selected write permit?! "
03493                                 "(unsupported!)\n"));
03494                     break;
03495                 }
03496 
03497                 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
03498                     DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03499                                 "Operator selected write protect?! "
03500                                 "(unsupported!)\n"));
03501                     break;
03502                 }
03503 
03504                 }
03505                 break;
03506             }
03507 
03508             default: {
03509                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03510                             "Unit attention\n"));
03511                 break;
03512             }
03513 
03514             } // end  switch (senseBuffer->AdditionalSenseCode)
03515 
03516             if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
03517             {
03518                 //
03519                 // TODO : Is the media lockable?
03520                 //
03521 
03522                 if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
03523                 {
03524                     //
03525                     // Set bit to indicate that media may have changed
03526                     // and volume needs verification.
03527                     //
03528 
03529                     SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
03530 
03531                     *Status = STATUS_VERIFY_REQUIRED;
03532                     retry = FALSE;
03533                 }
03534             }
03535             else
03536             {
03537                 *Status = STATUS_IO_DEVICE_ERROR;
03538             }
03539 
03540             break;
03541 
03542         } // end SCSI_SENSE_UNIT_ATTENTION
03543 
03544         case SCSI_SENSE_ABORTED_COMMAND: {
03545             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03546                         "Command aborted\n"));
03547             *Status = STATUS_IO_DEVICE_ERROR;
03548             retryInterval = 1;
03549             break;
03550         } // end SCSI_SENSE_ABORTED_COMMAND
03551 
03552         case SCSI_SENSE_BLANK_CHECK: {
03553             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03554                         "Media blank check\n"));
03555             retry = FALSE;
03556             *Status = STATUS_NO_DATA_DETECTED;
03557             break;
03558         } // end SCSI_SENSE_BLANK_CHECK
03559 
03560         case SCSI_SENSE_RECOVERED_ERROR: {
03561 
03562             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03563                         "Recovered error\n"));
03564             *Status = STATUS_SUCCESS;
03565             retry = FALSE;
03566             logError = TRUE;
03567             uniqueId = 258;
03568 
03569             switch(senseBuffer->AdditionalSenseCode) {
03570             case SCSI_ADSENSE_SEEK_ERROR:
03571             case SCSI_ADSENSE_TRACK_ERROR: {
03572                 logStatus = IO_ERR_SEEK_ERROR;
03573                 break;
03574             }
03575 
03576             case SCSI_ADSENSE_REC_DATA_NOECC:
03577             case SCSI_ADSENSE_REC_DATA_ECC: {
03578                 logStatus = IO_RECOVERED_VIA_ECC;
03579                 break;
03580             }
03581 
03582             case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
03583                 UCHAR wmiEventData[5];
03584 
03585                 *((PULONG)wmiEventData) = sizeof(UCHAR);
03586                 wmiEventData[sizeof(ULONG)] = senseBuffer->AdditionalSenseCodeQualifier;
03587 
03588                 //
03589                 // Don't log another eventlog if we have already logged once
03590                 // NOTE: this should have been interlocked, but the structure
03591                 //       was publicly defined to use a BOOLEAN (char).  Since
03592                 //       media only reports these errors once per X minutes,
03593                 //       the potential race condition is nearly non-existant.
03594                 //       the worst case is duplicate log entries, so ignore.
03595                 //
03596 
03597                 if (fdoExtension->FailurePredicted == 0) {
03598                     logError = TRUE;
03599                 }
03600                 fdoExtension->FailurePredicted = TRUE;
03601                 fdoExtension->FailureReason = senseBuffer->AdditionalSenseCodeQualifier;
03602                 logStatus = IO_WRN_FAILURE_PREDICTED;
03603 
03604                 ClassNotifyFailurePredicted(fdoExtension,
03605                                             (PUCHAR)&wmiEventData,
03606                                             sizeof(wmiEventData),
03607                                             0,
03608                                             4,
03609                                             Srb->PathId,
03610                                             Srb->TargetId,
03611                                             Srb->Lun);
03612                 break;
03613             }
03614 
03615             default: {
03616                 logStatus = IO_ERR_CONTROLLER_ERROR;
03617                 break;
03618             }
03619 
03620             } // end switch(senseBuffer->AdditionalSenseCode)
03621 
03622             if (senseBuffer->IncorrectLength) {
03623 
03624                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03625                             "Incorrect length detected.\n"));
03626                 *Status = STATUS_INVALID_BLOCK_LENGTH ;
03627             }
03628 
03629             break;
03630         } // end SCSI_SENSE_RECOVERED_ERROR
03631 
03632         case SCSI_SENSE_NO_SENSE: {
03633 
03634             //
03635             // Check other indicators.
03636             //
03637 
03638             if (senseBuffer->IncorrectLength) {
03639 
03640                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03641                             "Incorrect length detected.\n"));
03642                 *Status = STATUS_INVALID_BLOCK_LENGTH ;
03643                 retry   = FALSE;
03644 
03645             } else {
03646 
03647                 DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03648                             "No specific sense key\n"));
03649                 *Status = STATUS_IO_DEVICE_ERROR;
03650                 retry   = TRUE;
03651             }
03652 
03653             break;
03654         } // end SCSI_SENSE_NO_SENSE
03655 
03656         default: {
03657             DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03658                         "Unrecognized sense code\n"));
03659             *Status = STATUS_IO_DEVICE_ERROR;
03660             break;
03661         }
03662 
03663         } // end switch (senseBuffer->SenseKey & 0xf)
03664 
03665         //
03666         // Try to determine the bad sector from the inquiry data.
03667         //
03668 
03669         if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_READ ||
03670             ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY ||
03671             ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_WRITE)) {
03672 
03673             for (index = 0; index < 4; index++) {
03674                 badSector = (badSector << 8) | senseBuffer->Information[index];
03675             }
03676 
03677             readSector = 0;
03678             for (index = 0; index < 4; index++) {
03679                 readSector = (readSector << 8) | Srb->Cdb[index+2];
03680             }
03681 
03682             index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
03683                 ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
03684 
03685             //
03686             // Make sure the bad sector is within the read sectors.
03687             //
03688 
03689             if (!(badSector >= readSector && badSector < readSector + index)) {
03690                 badSector = readSector;
03691             }
03692         }
03693 
03694     } else {
03695 
03696         //
03697         // Request sense buffer not valid. No sense information
03698         // to pinpoint the error. Return general request fail.
03699         //
03700 
03701         DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
03702                     "Request sense info not valid. SrbStatus %2x\n",
03703                     SRB_STATUS(Srb->SrbStatus)));
03704         retry = TRUE;
03705 
03706         switch (SRB_STATUS(Srb->SrbStatus)) {
03707         case SRB_STATUS_INVALID_LUN:
03708         case SRB_STATUS_INVALID_TARGET_ID:
03709         case SRB_STATUS_NO_DEVICE:
03710         case SRB_STATUS_NO_HBA:
03711         case SRB_STATUS_INVALID_PATH_ID: {
03712             *Status = STATUS_NO_SUCH_DEVICE;
03713             retry = FALSE;
03714             break;
03715         }
03716 
03717         case SRB_STATUS_COMMAND_TIMEOUT:
03718         case SRB_STATUS_TIMEOUT: {
03719 
03720             //
03721             // Update the error count for the device.
03722             //
03723 
03724             incrementErrorCount = TRUE;
03725             *Status = STATUS_IO_TIMEOUT;
03726             break;
03727         }
03728 
03729         case SRB_STATUS_ABORTED: {
03730 
03731             //
03732             // Update the error count for the device.
03733             //
03734 
03735             incrementErrorCount = TRUE;
03736             *Status = STATUS_IO_TIMEOUT;
03737             retryInterval = 1;
03738             break;
03739         }
03740 
03741 
03742         case SRB_STATUS_SELECTION_TIMEOUT: {
03743             logError = TRUE;
03744             logStatus = IO_ERR_NOT_READY;
03745             uniqueId = 260;
03746             *Status = STATUS_DEVICE_NOT_CONNECTED;
03747             retry = FALSE;
03748             break;
03749         }
03750 
03751         case SRB_STATUS_DATA_OVERRUN: {
03752             *Status = STATUS_DATA_OVERRUN;
03753             retry = FALSE;
03754             break;
03755         }
03756 
03757         case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
03758 
03759             //
03760             // Update the error count for the device.
03761             //
03762 
03763             incrementErrorCount = TRUE;
03764             *Status = STATUS_IO_DEVICE_ERROR;
03765 
03766             //
03767             // If there was  phase sequence error then limit the number of
03768             // retries.
03769             //
03770 
03771             if (RetryCount > 1 ) {
03772                 retry = FALSE;
03773             }
03774 
03775             break;
03776         }
03777 
03778         case SRB_STATUS_REQUEST_FLUSHED: {
03779 
03780             //
03781             // If the status needs verification bit is set.  Then set
03782             // the status to need verification and no retry; otherwise,
03783             // just retry the request.
03784             //
03785 
03786             if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
03787 
03788                 *Status = STATUS_VERIFY_REQUIRED;
03789                 retry = FALSE;
03790 
03791             } else {
03792                 *Status = STATUS_IO_DEVICE_ERROR;
03793             }
03794 
03795             break;
03796         }
03797 
03798         case SRB_STATUS_INVALID_REQUEST: {
03799             *Status = STATUS_INVALID_DEVICE_REQUEST;
03800             retry = FALSE;
03801             break;
03802         }
03803 
03804         case SRB_STATUS_UNEXPECTED_BUS_FREE:
03805         case SRB_STATUS_PARITY_ERROR:
03806 
03807             //
03808             // Update the error count for the device
03809             // and fall through to below
03810             //
03811 
03812             incrementErrorCount = TRUE;
03813 
03814         case SRB_STATUS_BUS_RESET: {
03815             *Status = STATUS_IO_DEVICE_ERROR;
03816             break;
03817         }
03818 
03819         case SRB_STATUS_ERROR: {
03820 
03821             *Status = STATUS_IO_DEVICE_ERROR;
03822             if (Srb->ScsiStatus == 0) {
03823 
03824                 //
03825                 // This is some strange return code.  Update the error
03826                 // count for the device.
03827                 //
03828 
03829                 incrementErrorCount = TRUE;
03830 
03831             } if (Srb->ScsiStatus == SCSISTAT_BUSY) {
03832 
03833                 *Status = STATUS_DEVICE_NOT_READY;
03834 
03835             } if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) {
03836 
03837                 *Status = STATUS_DEVICE_BUSY;
03838                 retry = FALSE;
03839                 logError = FALSE;
03840 
03841             }
03842 
03843             break;
03844         }
03845 
03846         default: {
03847             logError = TRUE;
03848             logStatus = IO_ERR_CONTROLLER_ERROR;
03849             uniqueId = 259;
03850             *Status = STATUS_IO_DEVICE_ERROR;
03851             unhandledError = TRUE;
03852             break;
03853         }
03854 
03855         }
03856 
03857         //
03858         // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
03859         // we know from a previous poll when the device will be ready (ETA)
03860         // we should delay the retry more appropriately than just guessing.
03861         //
03862         /*
03863         if (fdoExtension->MediaChangeDetectionInfo &&
03864             fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
03865             TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
03866                       NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
03867             ) {
03868             // check if Gesn.ReadyTime if greater than current tick count
03869             // if so, delay that long (from 1 to 30 seconds max?)
03870             // else, leave the guess of time alone.
03871         }
03872         */
03873 
03874     }
03875 
03876     if (incrementErrorCount) {
03877 
03878         //
03879         // if any error count occurred, delay the retry of this io by
03880         // at least one second, if caller supports it.
03881         //
03882 
03883         if (retryInterval == 0) {
03884             retryInterval = 1;
03885         }
03886         ClasspPerfIncrementErrorCount(fdoExtension);
03887     }
03888 
03889     //
03890     // If there is a class specific error handler call it.
03891     //
03892 
03893     if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
03894 
03895         fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
03896                                                           Srb,
03897                                                           Status,
03898                                                           &retry);
03899     }
03900 
03901     //
03902     // If the caller wants to know the suggested retry interval tell them.
03903     //
03904 
03905     if(ARGUMENT_PRESENT(RetryInterval)) {
03906         *RetryInterval = retryInterval;
03907     }
03908 
03909 
03910     /*
03911      *  LOG the error:
03912      *      Always log the error in our internal log.
03913      *      If logError is set, also log the error in the system log.
03914      */
03915     {
03916         ULONG totalSize;
03917         ULONG senseBufferSize = 0;
03918         IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
03919         CLASS_ERROR_LOG_DATA staticErrLogData = {0};
03920 
03921         //
03922         // Calculate the total size of the error log entry.
03923         // add to totalSize in the order that they are used.
03924         // the advantage to calculating all the sizes here is
03925         // that we don't have to do a bunch of extraneous checks
03926         // later on in this code path.
03927         //
03928         totalSize = sizeof(IO_ERROR_LOG_PACKET)  // required
03929                   - sizeof(ULONG)                // struct includes one ULONG
03930                   + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
03931 
03932         //
03933         // also save any available extra sense data, up to the maximum errlog
03934         // packet size .  WMI should be used for real-time analysis.
03935         // the event log should only be used for post-mortem debugging.
03936         //
03937         if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
03938             ULONG validSenseBytes;
03939             BOOLEAN validSense;
03940 
03941             //
03942             // make sure we can at least access the AdditionalSenseLength field
03943             //
03944             validSense = RTL_CONTAINS_FIELD(senseBuffer,
03945                                             Srb->SenseInfoBufferLength,
03946                                             AdditionalSenseLength);
03947             if (validSense) {
03948 
03949                 //
03950                 // if extra info exists, copy the maximum amount of available
03951                 // sense data that is safe into the the errlog.
03952                 //
03953                 validSenseBytes = senseBuffer->AdditionalSenseLength
03954                                 + offsetof(SENSE_DATA, AdditionalSenseLength);
03955 
03956                 //
03957                 // this is invalid because it causes overflow!
03958                 // whoever sent this type of request would cause
03959                 // a system crash.
03960                 //
03961                 ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
03962 
03963                 //
03964                 // set to save the most sense buffer possible
03965                 //
03966                 senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
03967                 senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
03968             } else {
03969                 //
03970                 // it's smaller than required to read the total number of
03971                 // valid bytes, so just use the SenseInfoBufferLength field.
03972                 //
03973                 senseBufferSize = Srb->SenseInfoBufferLength;
03974             }
03975 
03976             /*
03977              *  Bump totalSize by the number of extra senseBuffer bytes
03978              *  (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
03979              *  Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
03980              */
03981             if (senseBufferSize > sizeof(SENSE_DATA)){
03982                 totalSize += senseBufferSize-sizeof(SENSE_DATA);
03983                 if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
03984                     senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
03985                     totalSize = ERROR_LOG_MAXIMUM_SIZE;
03986                 }
03987             }
03988         }
03989 
03990         //
03991         // If we've used up all of our retry attempts, set the final status to
03992         // reflect the appropriate result.
03993         //
03994         if (retry && RetryCount < MAXIMUM_RETRIES) {
03995             staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
03996             staticErrLogData.ErrorRetried = TRUE;
03997         } else {
03998             staticErrLogEntry.FinalStatus = *Status;
03999         }
04000         if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
04001             staticErrLogData.ErrorPaging = TRUE;
04002         }
04003         if (unhandledError) {
04004             staticErrLogData.ErrorUnhandled = TRUE;
04005         }
04006 
04007         //
04008         // Calculate the device offset if there is a geometry.
04009         //
04010         staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
04011         staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
04012         if (logStatus == -1){
04013             staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
04014         } else {
04015             staticErrLogEntry.ErrorCode = logStatus;
04016         }
04017 
04018         /*
04019          *  The dump data follows the IO_ERROR_LOG_PACKET,
04020          *  with the first ULONG of dump data inside the packet.
04021          */
04022         staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG);
04023 
04024         staticErrLogEntry.SequenceNumber = 0;
04025         staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
04026         staticErrLogEntry.IoControlCode = IoDeviceCode;
04027         staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
04028         staticErrLogEntry.UniqueErrorValue = uniqueId;
04029 
04030         KeQueryTickCount(&staticErrLogData.TickCount);
04031         staticErrLogData.PortNumber = (ULONG)-1;
04032 
04033         /*
04034          *  Save the entire contents of the SRB.
04035          */
04036         staticErrLogData.Srb = *Srb;
04037 
04038         /*
04039          *  For our private log, save just the default length of the SENSE_DATA.
04040          */
04041         if (senseBufferSize != 0){
04042             RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
04043         }
04044 
04045         /*
04046          *  Save the error log in our context.
04047          *  We only save the default sense buffer length.
04048          */
04049         KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
04050         fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
04051         fdoData->ErrorLogNextIndex++;
04052         fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
04053         KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
04054 
04055         /*
04056          *  If logError is set, also save this log in the system's error log.
04057          *  But make sure we don't log TUR failures over and over 
04058          *  (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
04059          */
04060         if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_TEST_UNIT_READY) && logError){
04061             if (fdoData->LoggedTURFailureSinceLastIO){
04062                 logError = FALSE;
04063             }
04064             else {
04065                 fdoData->LoggedTURFailureSinceLastIO = TRUE;    
04066             }
04067         }
04068         if (logError){
04069             PIO_ERROR_LOG_PACKET errorLogEntry;
04070             PCLASS_ERROR_LOG_DATA errlogData;
04071 
04072             errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
04073             if (errorLogEntry){
04074                 errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
04075 
04076                 *errorLogEntry = staticErrLogEntry;
04077                 *errlogData = staticErrLogData;
04078 
04079                 /*
04080                  *  For the system log, copy as much of the sense buffer as possible.
04081                  */
04082                 if (senseBufferSize != 0) {
04083                     RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
04084                 }
04085 
04086                 /*
04087                  *  Write the error log packet to the system error logging thread.
04088                  */
04089                 IoWriteErrorLogEntry(errorLogEntry);
04090             }
04091         }
04092     }
04093 
04094     return retry;
04095 
04096 } // end ClassInterpretSenseInfo()
04097 
04098 
04099 
04100 /*++////////////////////////////////////////////////////////////////////////////
04101 
04102 ClassModeSense()
04103 
04104 Routine Description:
04105 
04106     This routine sends a mode sense command to a target ID and returns
04107     when it is complete.
04108 
04109 Arguments:
04110 
04111     Fdo - Supplies the functional device object associated with this request.
04112 
04113     ModeSenseBuffer - Supplies a buffer to store the sense data.
04114 
04115     Length - Supplies the length in bytes of the mode sense buffer.
04116 
04117     PageMode - Supplies the page or pages of mode sense data to be retrived.
04118 
04119 Return Value:
04120 
04121     Length of the transferred data is returned.
04122 
04123 --*/
04124 ULONG ClassModeSense(   IN PDEVICE_OBJECT Fdo,
04125                         IN PCHAR ModeSenseBuffer,
04126                         IN ULONG Length,
04127                         IN UCHAR PageMode)
04128 {
04129     ULONG lengthTransferred = 0;
04130     PMDL senseBufferMdl;
04131 
04132     PAGED_CODE();
04133 
04134     senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
04135     if (senseBufferMdl){
04136 
04137         TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
04138         if (pkt){
04139             KEVENT event;
04140             NTSTATUS pktStatus;
04141             IRP pseudoIrp = {0};
04142 
04143             /*
04144              *  Store the number of packets servicing the irp (one)
04145              *  inside the original IRP.  It will be used to counted down
04146              *  to zero when the packet completes.
04147              *  Initialize the original IRP's status to success.
04148              *  If the packet fails, we will set it to the error status.
04149              */
04150             pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
04151             pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
04152             pseudoIrp.IoStatus.Information = 0;
04153             pseudoIrp.MdlAddress = senseBufferMdl;
04154 
04155             /*
04156              *  Set this up as a SYNCHRONOUS transfer, submit it,
04157              *  and wait for the packet to complete.  The result
04158              *  status will be written to the original irp.
04159              */
04160             ASSERT(Length <= 0x0ff);
04161             KeInitializeEvent(&event, SynchronizationEvent, FALSE);
04162             SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, &pseudoIrp);
04163             SubmitTransferPacket(pkt);
04164             KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
04165 
04166             if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
04167                 lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
04168             }
04169             else {
04170                 /*
04171                  *  This request can sometimes fail legitimately
04172                  *  (e.g. when a SCSI device is attached but turned off)
04173                  *  so this is not necessarily a device/driver bug.
04174                  */
04175                 DBGTRACE(ClassDebugWarning, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
04176             }
04177         }
04178 
04179         FreeDeviceInputMdl(senseBufferMdl);
04180     }
04181 
04182     return lengthTransferred;
04183 }
04184 
04185 
04186 /*++////////////////////////////////////////////////////////////////////////////
04187 
04188 ClassFindModePage()
04189 
04190 Routine Description:
04191 
04192     This routine scans through the mode sense data and finds the requested
04193     mode sense page code.
04194 
04195 Arguments:
04196     ModeSenseBuffer - Supplies a pointer to the mode sense data.
04197 
04198     Length - Indicates the length of valid data.
04199 
04200     PageMode - Supplies the page mode to be searched for.
04201 
04202     Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
04203 
04204 Return Value:
04205 
04206     A pointer to the the requested mode page.  If the mode page was not found
04207     then NULL is return.
04208 
04209 --*/
04210 PVOID
04211 ClassFindModePage(
04212     IN PCHAR ModeSenseBuffer,
04213     IN ULONG Length,
04214     IN UCHAR PageMode,
04215     IN BOOLEAN Use6Byte
04216     )
04217 {
04218     PUCHAR limit;
04219     ULONG  parameterHeaderLength;
04220     PVOID result = NULL;
04221 
04222     limit = ModeSenseBuffer + Length;
04223     parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
04224 
04225     if (Length >= parameterHeaderLength) {
04226 
04227         PMODE_PARAMETER_HEADER10 modeParam10;
04228         ULONG blockDescriptorLength;
04229 
04230         /*
04231          *  Skip the mode select header and block descriptors.
04232          */
04233         if (Use6Byte){
04234             blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
04235         }
04236         else {
04237             modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
04238             blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
04239         }
04240 
04241         ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
04242 
04243         //
04244         // ModeSenseBuffer now points at pages.  Walk the pages looking for the
04245         // requested page until the limit is reached.
04246         //
04247 
04248         while (ModeSenseBuffer +
04249                RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit) {
04250 
04251             if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
04252 
04253                 /*
04254                  * found the mode page.  make sure it's safe to touch it all
04255                  * before returning the pointer to caller
04256                  */
04257 
04258                 if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit) {
04259                     /*
04260                      *  Return NULL since the page is not safe to access in full
04261                      */
04262                     result = NULL;
04263                 }
04264                 else {
04265                     result = ModeSenseBuffer;
04266                 }
04267                 break;
04268             }
04269 
04270             //
04271             // Advance to the next page which is 4-byte-aligned offset after this page.
04272             //
04273             ModeSenseBuffer +=
04274                 ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
04275                 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
04276 
04277         }
04278     }
04279 
04280     return result;
04281 } // end ClassFindModePage()
04282 
04283 /*++////////////////////////////////////////////////////////////////////////////
04284 
04285 ClassSendSrbAsynchronous()
04286 
04287 Routine Description:
04288 
04289     This routine takes a partially built Srb and an Irp and sends it down to
04290     the port driver.
04291 
04292     This routine must be called with the remove lock held for the specified
04293     Irp.
04294 
04295 Arguments:
04296 
04297     Fdo - Supplies the functional device object for the orginal request.
04298 
04299     Srb - Supplies a paritally build ScsiRequestBlock.  In particular, the
04300         CDB and the SRB timeout value must be filled in.  The SRB must not be
04301         allocated from zone.
04302 
04303     Irp - Supplies the requesting Irp.
04304 
04305     BufferAddress - Supplies a pointer to the buffer to be transfered.
04306 
04307     BufferLength - Supplies the length of data transfer.
04308 
04309     WriteToDevice - Indicates the data transfer will be from system memory to
04310         device.
04311 
04312 Return Value:
04313 
04314     Returns STATUS_PENDING if the request is dispatched (since the
04315     completion routine may change the irp's status value we cannot simply
04316     return the value of the dispatch)
04317 
04318     or returns a status value to indicate why it failed.
04319 
04320 --*/
04321 NTSTATUS
04322 ClassSendSrbAsynchronous(
04323     PDEVICE_OBJECT Fdo,
04324     PSCSI_REQUEST_BLOCK Srb,
04325     PIRP Irp,
04326     PVOID BufferAddress,
04327     ULONG BufferLength,
04328     BOOLEAN WriteToDevice
04329     )
04330 {
04331 
04332     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
04333     PIO_STACK_LOCATION irpStack;
04334 
04335     ULONG savedFlags;
04336 
04337     //
04338     // Write length to SRB.
04339     //
04340 
04341     Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
04342 
04343     //
04344     // Set SCSI bus address.
04345     //
04346 
04347     Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
04348 
04349     //
04350     // This is a violation of the SCSI spec but it is required for
04351     // some targets.
04352     //
04353 
04354     // Srb->Cdb[1] |= deviceExtension->Lun << 5;
04355 
04356     //
04357     // Indicate auto request sense by specifying buffer and size.
04358     //
04359 
04360     Srb->SenseInfoBuffer = fdoExtension->SenseData;
04361     Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
04362     Srb->DataBuffer = BufferAddress;
04363 
04364     //
04365     // Save the class driver specific flags away.
04366     //
04367 
04368     savedFlags = Srb->SrbFlags & SRB_FLAGS_CLASS_DRIVER_RESERVED;
04369 
04370     //
04371     // Allow the caller to specify that they do not wish
04372     // IoStartNextPacket() to be called in the completion routine.
04373     //
04374 
04375     SET_FLAG(savedFlags, (Srb->SrbFlags & SRB_FLAGS_DONT_START_NEXT_PACKET));
04376 
04377     if (BufferAddress != NULL) {
04378 
04379         //
04380         // Build Mdl if necessary.
04381         //
04382 
04383         if (Irp->MdlAddress == NULL) {
04384 
04385             if (IoAllocateMdl(BufferAddress,
04386                               BufferLength,
04387                               FALSE,
04388                               FALSE,
04389                               Irp) == NULL) {
04390 
04391                 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
04392 
04393                 //
04394                 // ClassIoComplete() would have free'd the srb
04395                 //
04396 
04397                 if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
04398                     FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
04399                 }
04400                 ClassFreeOrReuseSrb(fdoExtension, Srb);
04401                 ClassReleaseRemoveLock(Fdo, Irp);
04402                 ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
04403 
04404                 return STATUS_INSUFFICIENT_RESOURCES;
04405             }
04406 
04407             MmBuildMdlForNonPagedPool(Irp->MdlAddress);
04408 
04409         } else {
04410 
04411             //
04412             // Make sure the buffer requested matches the MDL.
04413             //
04414 
04415             ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
04416         }
04417 
04418         //
04419         // Set read flag.
04420         //
04421 
04422         Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN;
04423 
04424     } else {
04425 
04426         //
04427         // Clear flags.
04428         //
04429 
04430         Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
04431     }
04432 
04433     //
04434     // Restore saved flags.
04435     //
04436 
04437     SET_FLAG(Srb->SrbFlags, savedFlags);
04438 
04439     //
04440     // Disable synchronous transfer for these requests.
04441     //
04442 
04443     SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
04444 
04445     //
04446     // Set the transfer length.
04447     //
04448 
04449     Srb->DataTransferLength = BufferLength;
04450 
04451     //
04452     // Zero out status.
04453     //
04454 
04455     Srb->ScsiStatus = Srb->SrbStatus = 0;
04456 
04457     Srb->NextSrb = 0;
04458 
04459     //
04460     // Save a few parameters in the current stack location.
04461     //
04462 
04463     irpStack = IoGetCurrentIrpStackLocation(Irp);
04464 
04465     //
04466     // Save retry count in current Irp stack.
04467     //
04468 
04469     irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
04470 
04471     //
04472     // Set up IoCompletion routine address.
04473     //
04474 
04475     IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
04476 
04477     //
04478     // Get next stack location and
04479     // set major function code.
04480     //
04481 
04482     irpStack = IoGetNextIrpStackLocation(Irp);
04483 
04484     irpStack->MajorFunction = IRP_MJ_SCSI;
04485 
04486     //
04487     // Save SRB address in next stack for port driver.
04488     //
04489 
04490     irpStack->Parameters.Scsi.Srb = Srb;
04491 
04492     //
04493     // Set up Irp Address.
04494     //
04495 
04496     Srb->OriginalRequest = Irp;
04497 
04498     //
04499     // Call the port driver to process the request.
04500     //
04501 
04502     IoMarkIrpPending(Irp);
04503 
04504     IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
04505 
04506     return STATUS_PENDING;
04507 
04508 } // end ClassSendSrbAsynchronous()
04509 
04510 /*++////////////////////////////////////////////////////////////////////////////
04511 
04512 ClassDeviceControlDispatch()
04513 
04514 Routine Description:
04515 
04516     The routine is the common class driver device control dispatch entry point.
04517     This routine is invokes the device-specific drivers DeviceControl routine,
04518     (which may call the Class driver's common DeviceControl routine).
04519 
04520 Arguments:
04521 
04522     DeviceObject - Supplies a pointer to the device object for this request.
04523 
04524     Irp - Supplies the Irp making the request.
04525 
04526 Return Value:
04527 
04528    Returns the status returned from the device-specific driver.
04529 
04530 --*/
04531 NTSTATUS
04532 ClassDeviceControlDispatch(
04533     PDEVICE_OBJECT DeviceObject,
04534     PIRP Irp
04535     )
04536 {
04537 
04538     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
04539     ULONG isRemoved;
04540 
04541     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
04542 
04543     if(isRemoved) {
04544 
04545         ClassReleaseRemoveLock(DeviceObject, Irp);
04546 
04547         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
04548         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
04549         return STATUS_DEVICE_DOES_NOT_EXIST;
04550     }
04551 
04552     //
04553     // Call the class specific driver DeviceControl routine.
04554     // If it doesn't handle it, it will call back into ClassDeviceControl.
04555     //
04556 
04557     ASSERT(commonExtension->DevInfo->ClassDeviceControl);
04558 
04559     return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
04560 } // end ClassDeviceControlDispatch()
04561 
04562 
04563 /*++////////////////////////////////////////////////////////////////////////////
04564 
04565 ClassDeviceControl()
04566 
04567 Routine Description:
04568 
04569     The routine is the common class driver device control dispatch function.
04570     This routine is called by a class driver when it get an unrecognized
04571     device control request.  This routine will perform the correct action for
04572     common requests such as lock media.  If the device request is unknown it
04573     passed down to the next level.
04574 
04575     This routine must be called with the remove lock held for the specified
04576     irp.
04577 
04578 Arguments:
04579 
04580     DeviceObject - Supplies a pointer to the device object for this request.
04581 
04582     Irp - Supplies the Irp making the request.
04583 
04584 Return Value:
04585 
04586    Returns back a STATUS_PENDING or a completion status.
04587 
04588 --*/
04589 NTSTATUS
04590 ClassDeviceControl(
04591     PDEVICE_OBJECT DeviceObject,
04592     PIRP Irp
04593     )
04594 {
04595     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
04596 
04597     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
04598     PIO_STACK_LOCATION nextStack = NULL;
04599 
04600     ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
04601 
04602     PSCSI_REQUEST_BLOCK srb = NULL;
04603     PCDB cdb = NULL;
04604 
04605     NTSTATUS status;
04606     ULONG modifiedIoControlCode;
04607 
04608     //
04609     // If this is a pass through I/O control, set the minor function code
04610     // and device address and pass it to the port driver.
04611     //
04612 
04613     if ((controlCode == IOCTL_SCSI_PASS_THROUGH) ||
04614         (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT)) {
04615 
04616         PSCSI_PASS_THROUGH scsiPass;
04617 
04618         //
04619         // Validiate the user buffer.
04620         //
04621         #if defined (_WIN64)
04622 
04623             if (IoIs32bitProcess(Irp)) {
04624 
04625                 if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
04626 
04627                     Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
04628 
04629                     ClassReleaseRemoveLock(DeviceObject, Irp);
04630                     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
04631 
04632                     status = STATUS_INVALID_PARAMETER;
04633                     goto SetStatusAndReturn;
04634                 }
04635             }
04636             else
04637         #endif
04638             {
04639                 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
04640                     sizeof(SCSI_PASS_THROUGH)) {
04641 
04642                     Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
04643 
04644                     ClassReleaseRemoveLock(DeviceObject, Irp);
04645                     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
04646 
04647                     status = STATUS_INVALID_PARAMETER;
04648                     goto SetStatusAndReturn;
04649                 }
04650             }
04651 
04652         IoCopyCurrentIrpStackLocationToNext(Irp);
04653 
04654         nextStack = IoGetNextIrpStackLocation(Irp);
04655         nextStack->MinorFunction = 1;
04656 
04657         ClassReleaseRemoveLock(DeviceObject, Irp);
04658 
04659         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
04660         goto SetStatusAndReturn;
04661     }
04662 
04663     Irp->IoStatus.Information = 0;
04664 
04665     switch (controlCode) {
04666 
04667         case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
04668 
04669             PMOUNTDEV_UNIQUE_ID uniqueId;
04670 
04671             if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
04672                 status = STATUS_INVALID_PARAMETER;
04673                 break;
04674             }
04675 
04676             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
04677                 sizeof(MOUNTDEV_UNIQUE_ID)) {
04678 
04679                 status = STATUS_BUFFER_TOO_SMALL;
04680                 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
04681                 break;
04682             }
04683 
04684             uniqueId = Irp->AssociatedIrp.SystemBuffer;
04685             uniqueId->UniqueIdLength =
04686                     commonExtension->MountedDeviceInterfaceName.Length;
04687 
04688             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
04689                 sizeof(USHORT) + uniqueId->UniqueIdLength) {
04690 
04691                 status = STATUS_BUFFER_OVERFLOW;
04692                 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
04693                 break;
04694             }
04695 
04696             RtlCopyMemory(uniqueId->UniqueId,
04697                           commonExtension->MountedDeviceInterfaceName.Buffer,
04698                           uniqueId->UniqueIdLength);
04699 
04700             status = STATUS_SUCCESS;
04701             Irp->IoStatus.Information = sizeof(USHORT) +
04702                                         uniqueId->UniqueIdLength;
04703             break;
04704         }
04705 
04706         case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
04707 
04708             PMOUNTDEV_NAME name;
04709 
04710             ASSERT(commonExtension->DeviceName.Buffer);
04711 
04712             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
04713                 sizeof(MOUNTDEV_NAME)) {
04714 
04715                 status = STATUS_BUFFER_TOO_SMALL;
04716                 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
04717                 break;
04718             }
04719 
04720             name = Irp->AssociatedIrp.SystemBuffer;
04721             name->NameLength = commonExtension->DeviceName.Length;
04722 
04723             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
04724                 sizeof(USHORT) + name->NameLength) {
04725 
04726                 status = STATUS_BUFFER_OVERFLOW;
04727                 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
04728                 break;
04729             }
04730 
04731             RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
04732                           name->NameLength);
04733 
04734             status = STATUS_SUCCESS;
04735             Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
04736             break;
04737         }
04738 
04739         case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
04740 
04741             PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
04742             WCHAR driveLetterNameBuffer[10];
04743             RTL_QUERY_REGISTRY_TABLE queryTable[2];
04744             PWSTR valueName;
04745             UNICODE_STRING driveLetterName;
04746 
04747             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
04748                 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
04749 
04750                 status = STATUS_BUFFER_TOO_SMALL;
04751                 Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
04752                 break;
04753             }
04754 
04755             valueName = ExAllocatePoolWithTag(
04756                             PagedPool,
04757                             commonExtension->DeviceName.Length + sizeof(WCHAR),
04758                             '8CcS');
04759 
04760             if (!valueName) {
04761                 status = STATUS_INSUFFICIENT_RESOURCES;
04762                 break;
04763             }
04764 
04765             RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
04766                           commonExtension->DeviceName.Length);
04767             valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
04768 
04769             driveLetterName.Buffer = driveLetterNameBuffer;
04770             driveLetterName.MaximumLength = 20;
04771             driveLetterName.Length = 0;
04772 
04773             RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
04774             queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
04775                                   RTL_QUERY_REGISTRY_DIRECT;
04776             queryTable[0].Name = valueName;
04777             queryTable[0].EntryContext = &driveLetterName;
04778 
04779             status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
04780                                             L"\\Registry\\Machine\\System\\DISK",
04781                                             queryTable, NULL, NULL);
04782 
04783             if (!NT_SUCCESS(status)) {
04784                 ExFreePool(valueName);
04785                 break;
04786             }
04787 
04788             if (driveLetterName.Length == 4 &&
04789                 driveLetterName.Buffer[0] == '%' &&
04790                 driveLetterName.Buffer[1] == ':') {
04791 
04792                 driveLetterName.Buffer[0] = 0xFF;
04793 
04794             } else if (driveLetterName.Length != 4 ||
04795                 driveLetterName.Buffer[0] < FirstDriveLetter ||
04796                 driveLetterName.Buffer[0] > LastDriveLetter ||
04797                 driveLetterName.Buffer[1] != ':') {
04798 
04799                 status = STATUS_NOT_FOUND;
04800                 ExFreePool(valueName);
04801                 break;
04802             }
04803 
04804             suggestedName = Irp->AssociatedIrp.SystemBuffer;
04805             suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
04806             suggestedName->NameLength = 28;
04807 
04808             Irp->IoStatus.Information =
04809                     FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
04810 
04811             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
04812                 Irp->IoStatus.Information) {
04813 
04814                 Irp->IoStatus.Information =
04815                         sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
04816                 status = STATUS_BUFFER_OVERFLOW;
04817                 ExFreePool(valueName);
04818                 break;
04819             }
04820 
04821             RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
04822                                    L"\\Registry\\Machine\\System\\DISK",
04823                                    valueName);
04824 
04825             ExFreePool(valueName);
04826 
04827             RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
04828             suggestedName->Name[12] = driveLetterName.Buffer[0];
04829             suggestedName->Name[13] = ':';
04830 
04831             //
04832             // NT_SUCCESS(status) based on RtlQueryRegistryValues
04833             //
04834             status = STATUS_SUCCESS;
04835 
04836             break;
04837         }
04838 
04839         default:
04840             status = STATUS_PENDING;
04841             break;
04842     }
04843 
04844     if (status != STATUS_PENDING) {
04845         ClassReleaseRemoveLock(DeviceObject, Irp);
04846         Irp->IoStatus.Status = status;
04847         IoCompleteRequest(Irp, IO_NO_INCREMENT);
04848         return status;
04849     }
04850 
04851     if (commonExtension->IsFdo){
04852 
04853         PULONG_PTR function;
04854 
04855         srb = ExAllocatePoolWithTag(NonPagedPool,
04856                              sizeof(SCSI_REQUEST_BLOCK) +
04857                              (sizeof(ULONG_PTR) * 2),
04858                              '9CcS');
04859 
04860         if (srb == NULL) {
04861 
04862             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
04863             ClassReleaseRemoveLock(DeviceObject, Irp);
04864             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
04865             status = STATUS_INSUFFICIENT_RESOURCES;
04866             goto SetStatusAndReturn;
04867         }
04868 
04869         RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
04870 
04871         cdb = (PCDB)srb->Cdb;
04872 
04873         //
04874         // Save the function code and the device object in the memory after
04875         // the SRB.
04876         //
04877 
04878         function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
04879         *function = (ULONG_PTR) DeviceObject;
04880         function++;
04881         *function = (ULONG_PTR) controlCode;
04882 
04883     } else {
04884         srb = NULL;
04885     }
04886 
04887     //
04888     // Change the device type to storage for the switch statement, but only
04889     // if from a legacy device type
04890     //
04891 
04892     if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE  << 16)) ||
04893         ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE  << 16)) ||
04894         ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
04895         ) {
04896 
04897         modifiedIoControlCode = (controlCode & ~0xffff0000);
04898         modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
04899 
04900     } else {
04901 
04902         modifiedIoControlCode = controlCode;
04903 
04904     }
04905 
04906     DBGTRACE(ClassDebugTrace, ("> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
04907 
04908     switch (modifiedIoControlCode) {
04909 
04910     case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
04911 
04912         if (srb) {
04913             ExFreePool(srb);
04914             srb = NULL;
04915         }
04916 
04917         if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
04918            sizeof(STORAGE_HOTPLUG_INFO)) {
04919 
04920             //
04921             // Indicate unsuccessful status and no data transferred.
04922             //
04923 
04924             Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
04925             Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
04926 
04927             ClassReleaseRemoveLock(DeviceObject, Irp);
04928             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
04929             status = STATUS_BUFFER_TOO_SMALL;
04930 
04931         } else if(!commonExtension->IsFdo) {
04932 
04933             //
04934             // Just forward this down and return
04935             //
04936 
04937             IoCopyCurrentIrpStackLocationToNext(Irp);
04938 
04939             ClassReleaseRemoveLock(DeviceObject, Irp);
04940             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
04941 
04942         } else {
04943 
04944             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
04945             PSTORAGE_HOTPLUG_INFO info;
04946 
04947             fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
04948             info = Irp->AssociatedIrp.SystemBuffer;
04949 
04950             *info = fdoExtension->PrivateFdoData->HotplugInfo;
04951             Irp->IoStatus.Status = STATUS_SUCCESS;
04952             Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
04953             ClassReleaseRemoveLock(DeviceObject, Irp);
04954             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
04955             status = STATUS_SUCCESS;
04956 
04957         }
04958         break;
04959     }
04960 
04961     case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
04962 
04963         if (srb)
04964         {
04965             ExFreePool(srb);
04966             srb = NULL;
04967         }
04968 
04969         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
04970             sizeof(STORAGE_HOTPLUG_INFO)) {
04971 
04972             //
04973             // Indicate unsuccessful status and no data transferred.
04974             //
04975 
04976             Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
04977 
04978             ClassReleaseRemoveLock(DeviceObject, Irp);
04979             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
04980             status = STATUS_INFO_LENGTH_MISMATCH;
04981             goto SetStatusAndReturn;
04982 
04983         }
04984 
04985         if(!commonExtension->IsFdo) {
04986 
04987             //
04988             // Just forward this down and return
04989             //
04990 
04991             IoCopyCurrentIrpStackLocationToNext(Irp);
04992 
04993             ClassReleaseRemoveLock(DeviceObject, Irp);
04994             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
04995 
04996         } else {
04997 
04998             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
04999             PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
05000 
05001             status = STATUS_SUCCESS;
05002 
05003             if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
05004             {
05005                 status = STATUS_INVALID_PARAMETER_1;
05006             }
05007 
05008             if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
05009             {
05010                 status = STATUS_INVALID_PARAMETER_2;
05011             }
05012 
05013             if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
05014             {
05015                 status = STATUS_INVALID_PARAMETER_3;
05016             }
05017 
05018             if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
05019             {
05020                 status = STATUS_INVALID_PARAMETER_5;
05021             }
05022 
05023             if (NT_SUCCESS(status))
05024             {
05025                 fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
05026 
05027                 //
05028                 // Store the user-defined override in the registry
05029                 //
05030 
05031                 ClassSetDeviceParameter(fdoExtension,
05032                                         CLASSP_REG_SUBKEY_NAME,
05033                                         CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
05034                                         (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
05035             }
05036 
05037             Irp->IoStatus.Status = status;
05038 
05039             ClassReleaseRemoveLock(DeviceObject, Irp);
05040             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05041         }
05042 
05043         break;
05044     }
05045 
05046     case IOCTL_STORAGE_CHECK_VERIFY:
05047     case IOCTL_STORAGE_CHECK_VERIFY2: {
05048 
05049         PIRP irp2 = NULL;
05050         PIO_STACK_LOCATION newStack;
05051 
05052         PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
05053 
05054         DebugPrint((1,"DeviceIoControl: Check verify\n"));
05055 
05056         //
05057         // If a buffer for a media change count was provided, make sure it's
05058         // big enough to hold the result
05059         //
05060 
05061         if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
05062 
05063             //
05064             // If the buffer is too small to hold the media change count
05065             // then return an error to the caller
05066             //
05067 
05068             if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
05069                sizeof(ULONG)) {
05070 
05071                 DebugPrint((3,"DeviceIoControl: media count "
05072                               "buffer too small\n"));
05073 
05074                 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
05075                 Irp->IoStatus.Information = sizeof(ULONG);
05076 
05077                 if(srb != NULL) {
05078                     ExFreePool(srb);
05079                 }
05080 
05081                 ClassReleaseRemoveLock(DeviceObject, Irp);
05082                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05083 
05084                 status = STATUS_BUFFER_TOO_SMALL;
05085                 goto SetStatusAndReturn;
05086 
05087             }
05088         }
05089 
05090         if(!commonExtension->IsFdo) {
05091 
05092             //
05093             // If this is a PDO then we should just forward the request down
05094             //
05095             ASSERT(!srb);
05096 
05097             IoCopyCurrentIrpStackLocationToNext(Irp);
05098 
05099             ClassReleaseRemoveLock(DeviceObject, Irp);
05100 
05101             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
05102 
05103             goto SetStatusAndReturn;
05104 
05105         } else {
05106 
05107             fdoExtension = DeviceObject->DeviceExtension;
05108 
05109         }
05110 
05111         if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
05112 
05113             //
05114             // The caller has provided a valid buffer.  Allocate an additional
05115             // irp and stick the CheckVerify completion routine on it.  We will
05116             // then send this down to the port driver instead of the irp the
05117             // caller sent in
05118             //
05119 
05120             DebugPrint((2,"DeviceIoControl: Check verify wants "
05121                           "media count\n"));
05122 
05123             //
05124             // Allocate a new irp to send the TestUnitReady to the port driver
05125             //
05126 
05127             irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
05128 
05129             if(irp2 == NULL) {
05130                 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
05131                 Irp->IoStatus.Information = 0;
05132                 ASSERT(srb);
05133                 ExFreePool(srb);
05134                 ClassReleaseRemoveLock(DeviceObject, Irp);
05135                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05136                 status = STATUS_INSUFFICIENT_RESOURCES;
05137                 goto SetStatusAndReturn;
05138 
05139                 break;
05140             }
05141 
05142             //
05143             // Make sure to acquire the lock for the new irp.
05144             //
05145 
05146             ClassAcquireRemoveLock(DeviceObject, irp2);
05147 
05148             irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
05149             IoSetNextIrpStackLocation(irp2);
05150 
05151             //
05152             // Set the top stack location and shove the master Irp into the
05153             // top location
05154             //
05155 
05156             newStack = IoGetCurrentIrpStackLocation(irp2);
05157             newStack->Parameters.Others.Argument1 = Irp;
05158             newStack->DeviceObject = DeviceObject;
05159 
05160             //
05161             // Stick the check verify completion routine onto the stack
05162             // and prepare the irp for the port driver
05163             //
05164 
05165             IoSetCompletionRoutine(irp2,
05166                                    ClassCheckVerifyComplete,
05167                                    NULL,
05168                                    TRUE,
05169                                    TRUE,
05170                                    TRUE);
05171 
05172             IoSetNextIrpStackLocation(irp2);
05173             newStack = IoGetCurrentIrpStackLocation(irp2);
05174             newStack->DeviceObject = DeviceObject;
05175             newStack->MajorFunction = irpStack->MajorFunction;
05176             newStack->MinorFunction = irpStack->MinorFunction;
05177 
05178             //
05179             // Mark the master irp as pending - whether the lower level
05180             // driver completes it immediately or not this should allow it
05181             // to go all the way back up.
05182             //
05183 
05184             IoMarkIrpPending(Irp);
05185 
05186             Irp = irp2;
05187 
05188         }
05189 
05190         //
05191         // Test Unit Ready
05192         //
05193 
05194         srb->CdbLength = 6;
05195         cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
05196 
05197         //
05198         // Set timeout value.
05199         //
05200 
05201         srb->TimeOutValue = fdoExtension->TimeOutValue;
05202 
05203         //
05204         // If this was a CV2 then mark the request as low-priority so we don't
05205         // spin up the drive just to satisfy it.
05206         //
05207 
05208         if(controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
05209             SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
05210         }
05211 
05212         //
05213         // Since this routine will always hand the request to the
05214         // port driver if there isn't a data transfer to be done
05215         // we don't have to worry about completing the request here
05216         // on an error
05217         //
05218 
05219         //
05220         // This routine uses a completion routine so we don't want to release
05221         // the remove lock until then.
05222         //
05223 
05224         status = ClassSendSrbAsynchronous(DeviceObject,
05225                                           srb,
05226                                           Irp,
05227                                           NULL,
05228                                           0,
05229                                           FALSE);
05230 
05231         break;
05232     }
05233 
05234     case IOCTL_STORAGE_MEDIA_REMOVAL:
05235     case IOCTL_STORAGE_EJECTION_CONTROL: {
05236 
05237         PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
05238 
05239         DebugPrint((3, "DiskIoControl: ejection control\n"));
05240 
05241         if(srb) {
05242             ExFreePool(srb);
05243         }
05244 
05245         if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
05246            sizeof(PREVENT_MEDIA_REMOVAL)) {
05247 
05248             //
05249             // Indicate unsuccessful status and no data transferred.
05250             //
05251 
05252             Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
05253 
05254             ClassReleaseRemoveLock(DeviceObject, Irp);
05255             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05256             status = STATUS_INFO_LENGTH_MISMATCH;
05257             goto SetStatusAndReturn;
05258         }
05259 
05260         if(!commonExtension->IsFdo) {
05261 
05262             //
05263             // Just forward this down and return
05264             //
05265 
05266             IoCopyCurrentIrpStackLocationToNext(Irp);
05267 
05268             ClassReleaseRemoveLock(DeviceObject, Irp);
05269             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
05270         }
05271         else {
05272 
05273             // i don't believe this assertion is valid.  this is a request
05274             // from user-mode, so they could request this for any device
05275             // they want?  also, we handle it properly.
05276             // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
05277             status = ClasspEjectionControl(
05278                         DeviceObject,
05279                         Irp,
05280                         ((modifiedIoControlCode ==
05281                         IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
05282                                                           SimpleMediaLock),
05283                         mediaRemoval->PreventMediaRemoval);
05284 
05285             Irp->IoStatus.Status = status;
05286             ClassReleaseRemoveLock(DeviceObject, Irp);
05287             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05288         }
05289 
05290         break;
05291     }
05292 
05293     case IOCTL_STORAGE_MCN_CONTROL: {
05294 
05295         DebugPrint((3, "DiskIoControl: MCN control\n"));
05296 
05297         if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
05298            sizeof(PREVENT_MEDIA_REMOVAL)) {
05299 
05300             //
05301             // Indicate unsuccessful status and no data transferred.
05302             //
05303 
05304             Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
05305             Irp->IoStatus.Information = 0;
05306 
05307             if(srb) {
05308                 ExFreePool(srb);
05309             }
05310 
05311             ClassReleaseRemoveLock(DeviceObject, Irp);
05312             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05313             status = STATUS_INFO_LENGTH_MISMATCH;
05314             goto SetStatusAndReturn;
05315         }
05316 
05317         if(!commonExtension->IsFdo) {
05318 
05319             //
05320             // Just forward this down and return
05321             //
05322 
05323             if(srb) {
05324                 ExFreePool(srb);
05325             }
05326 
05327             IoCopyCurrentIrpStackLocationToNext(Irp);
05328 
05329             ClassReleaseRemoveLock(DeviceObject, Irp);
05330             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
05331 
05332         } else {
05333 
05334             //
05335             // Call to the FDO - handle the ejection control.
05336             //
05337 
05338             status = ClasspMcnControl(DeviceObject->DeviceExtension,
05339                                       Irp,
05340                                       srb);
05341         }
05342         goto SetStatusAndReturn;
05343     }
05344 
05345     case IOCTL_STORAGE_RESERVE:
05346     case IOCTL_STORAGE_RELEASE: {
05347 
05348         //
05349         // Reserve logical unit.
05350         //
05351 
05352         PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
05353 
05354         if(!commonExtension->IsFdo) {
05355 
05356             IoCopyCurrentIrpStackLocationToNext(Irp);
05357 
05358             ClassReleaseRemoveLock(DeviceObject, Irp);
05359             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
05360             goto SetStatusAndReturn;
05361         } else {
05362             fdoExtension = DeviceObject->DeviceExtension;
05363         }
05364 
05365         srb->CdbLength = 6;
05366 
05367         if(modifiedIoControlCode == IOCTL_STORAGE_RESERVE) {
05368             cdb->CDB6GENERIC.OperationCode = SCSIOP_RESERVE_UNIT;
05369         } else {
05370             cdb->CDB6GENERIC.OperationCode = SCSIOP_RELEASE_UNIT;
05371         }
05372 
05373         //
05374         // Set timeout value.
05375         //
05376 
05377         srb->TimeOutValue = fdoExtension->TimeOutValue;
05378 
05379         status = ClassSendSrbAsynchronous(DeviceObject,
05380                                           srb,
05381                                           Irp,
05382                                           NULL,
05383                                           0,
05384                                           FALSE);
05385 
05386         break;
05387     }
05388 
05389     case IOCTL_STORAGE_EJECT_MEDIA:
05390     case IOCTL_STORAGE_LOAD_MEDIA:
05391     case IOCTL_STORAGE_LOAD_MEDIA2:{
05392 
05393         //
05394         // Eject media.
05395         //
05396 
05397         PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
05398 
05399         if(!commonExtension->IsFdo) {
05400 
05401             IoCopyCurrentIrpStackLocationToNext(Irp);
05402 
05403             ClassReleaseRemoveLock(DeviceObject, Irp);
05404 
05405             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
05406             goto SetStatusAndReturn;
05407         } else {
05408             fdoExtension = DeviceObject->DeviceExtension;
05409         }
05410 
05411         if(commonExtension->PagingPathCount != 0) {
05412 
05413             DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
05414                            "failure\n"));
05415 
05416             status = STATUS_FILES_OPEN;
05417             Irp->IoStatus.Status = status;
05418 
05419             Irp->IoStatus.Information = 0;
05420 
05421             if(srb) {
05422                 ExFreePool(srb);
05423             }
05424 
05425             ClassReleaseRemoveLock(DeviceObject, Irp);
05426             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05427             goto SetStatusAndReturn;
05428         }
05429 
05430         //
05431         // Synchronize with ejection control and ejection cleanup code as
05432         // well as other eject/load requests.
05433         //
05434 
05435         KeEnterCriticalRegion();
05436         KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
05437                               UserRequest,
05438                               UserMode,
05439                               FALSE,
05440                               NULL);
05441 
05442         if(fdoExtension->ProtectedLockCount != 0) {
05443 
05444             DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
05445                            "device - failure\n"));
05446 
05447             status = STATUS_DEVICE_BUSY;
05448             Irp->IoStatus.Status = status;
05449             Irp->IoStatus.Information = 0;
05450 
05451             if(srb) {
05452                 ExFreePool(srb);
05453             }
05454 
05455             ClassReleaseRemoveLock(DeviceObject, Irp);
05456             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05457 
05458             KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
05459                        IO_NO_INCREMENT,
05460                        FALSE);
05461             KeLeaveCriticalRegion();
05462 
05463             goto SetStatusAndReturn;
05464         }
05465 
05466         srb->CdbLength = 6;
05467 
05468         cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
05469         cdb->START_STOP.LoadEject = 1;
05470 
05471         if(modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
05472             cdb->START_STOP.Start = 0;
05473         } else {
05474             cdb->START_STOP.Start = 1;
05475         }
05476 
05477         //
05478         // Set timeout value.
05479         //
05480 
05481         srb->TimeOutValue = fdoExtension->TimeOutValue;
05482         status = ClassSendSrbAsynchronous(DeviceObject,
05483                                               srb,
05484                                               Irp,
05485                                               NULL,
05486                                               0,
05487                                               FALSE);
05488 
05489         KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
05490         KeLeaveCriticalRegion();
05491 
05492         break;
05493     }
05494 
05495     case IOCTL_STORAGE_FIND_NEW_DEVICES: {
05496 
05497         if(srb) {
05498             ExFreePool(srb);
05499         }
05500 
05501         if(commonExtension->IsFdo) {
05502 
05503             IoInvalidateDeviceRelations(
05504                 ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
05505                 BusRelations);
05506 
05507             status = STATUS_SUCCESS;
05508             Irp->IoStatus.Status = status;
05509 
05510             ClassReleaseRemoveLock(DeviceObject, Irp);
05511             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05512         }
05513         else {
05514 
05515             IoCopyCurrentIrpStackLocationToNext(Irp);
05516 
05517             ClassReleaseRemoveLock(DeviceObject, Irp);
05518             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
05519         }
05520         break;
05521     }
05522 
05523     case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
05524 
05525         if(srb) {
05526             ExFreePool(srb);
05527         }
05528 
05529         if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
05530            sizeof(STORAGE_DEVICE_NUMBER)) {
05531 
05532             PSTORAGE_DEVICE_NUMBER deviceNumber =
05533                 Irp->AssociatedIrp.SystemBuffer;
05534             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
05535                 commonExtension->PartitionZeroExtension;
05536 
05537             deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
05538             deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
05539             deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
05540 
05541             status = STATUS_SUCCESS;
05542             Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
05543 
05544         } else {
05545             status = STATUS_BUFFER_TOO_SMALL;
05546             Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
05547         }
05548 
05549         Irp->IoStatus.Status = status;
05550         ClassReleaseRemoveLock(DeviceObject, Irp);
05551         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05552 
05553         break;
05554     }
05555 
05556     default: {
05557 
05558         DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
05559                     controlCode, DeviceObject));
05560 
05561         //
05562         // Pass the device control to the next driver.
05563         //
05564 
05565         if(srb) {
05566             ExFreePool(srb);
05567         }
05568 
05569         //
05570         // Copy the Irp stack parameters to the next stack location.
05571         //
05572 
05573         IoCopyCurrentIrpStackLocationToNext(Irp);
05574 
05575         ClassReleaseRemoveLock(DeviceObject, Irp);
05576         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
05577         break;
05578     }
05579 
05580     } // end switch( ...
05581 
05582 SetStatusAndReturn:
05583 
05584     DBGTRACE(ClassDebugTrace, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
05585 
05586     return status;
05587 } // end ClassDeviceControl()
05588 
05589 /*++////////////////////////////////////////////////////////////////////////////
05590 
05591 ClassShutdownFlush()
05592 
05593 Routine Description:
05594 
05595     This routine is called for a shutdown and flush IRPs.  These are sent by the
05596     system before it actually shuts down or when the file system does a flush.
05597     If it exists, the device-specific driver's routine will be invoked. If there
05598     wasn't one specified, the Irp will be completed with an Invalid device request.
05599 
05600 Arguments:
05601 
05602     DriverObject - Pointer to device object to being shutdown by system.
05603 
05604     Irp - IRP involved.
05605 
05606 Return Value:
05607 
05608     NT Status
05609 
05610 --*/
05611 NTSTATUS
05612 ClassShutdownFlush(
05613     IN PDEVICE_OBJECT DeviceObject,
05614     IN PIRP Irp
05615     )
05616 {
05617     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
05618 
05619     ULONG isRemoved;
05620 
05621     NTSTATUS status;
05622 
05623     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
05624 
05625     if(isRemoved) {
05626 
05627         ClassReleaseRemoveLock(DeviceObject, Irp);
05628 
05629         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
05630 
05631         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05632 
05633         return STATUS_DEVICE_DOES_NOT_EXIST;
05634     }
05635 
05636     if (commonExtension->DevInfo->ClassShutdownFlush) {
05637 
05638         //
05639         // Call the device-specific driver's routine.
05640         //
05641 
05642         return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
05643     }
05644 
05645     //
05646     // Device-specific driver doesn't support this.
05647     //
05648 
05649     Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
05650 
05651     ClassReleaseRemoveLock(DeviceObject, Irp);
05652     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
05653 
05654     return STATUS_INVALID_DEVICE_REQUEST;
05655 } // end ClassShutdownFlush()
05656 
05657 /*++////////////////////////////////////////////////////////////////////////////
05658 
05659 ClassCreateDeviceObject()
05660 
05661 Routine Description:
05662 
05663     This routine creates an object for the physical device specified and
05664     sets up the deviceExtension's function pointers for each entry point
05665     in the device-specific driver.
05666 
05667 Arguments:
05668 
05669     DriverObject - Pointer to driver object created by system.
05670 
05671     ObjectNameBuffer - Dir. name of the object to create.
05672 
05673     LowerDeviceObject - Pointer to the lower device object
05674 
05675     IsFdo - should this be an fdo or a pdo
05676 
05677     DeviceObject - Pointer to the device object pointer we will return.
05678 
05679 Return Value:
05680 
05681     NTSTATUS
05682 
05683 --*/
05684 NTSTATUS
05685 ClassCreateDeviceObject(
05686     IN PDRIVER_OBJECT          DriverObject,
05687     IN PCCHAR                  ObjectNameBuffer,
05688     IN PDEVICE_OBJECT          LowerDevice,
05689     IN BOOLEAN                 IsFdo,
05690     IN OUT PDEVICE_OBJECT      *DeviceObject
05691     )
05692 {
05693     BOOLEAN        isPartitionable;
05694     STRING         ntNameString;
05695     UNICODE_STRING ntUnicodeString;
05696     NTSTATUS       status, status2;
05697     PDEVICE_OBJECT deviceObject = NULL;
05698 
05699     ULONG          characteristics;
05700 
05701     PCLASS_DRIVER_EXTENSION
05702         driverExtension = IoGetDriverObjectExtension(DriverObject,
05703                                                      CLASS_DRIVER_EXTENSION_KEY);
05704 
05705     PCLASS_DEV_INFO devInfo;
05706 
05707     PAGED_CODE();
05708 
05709     *DeviceObject = NULL;
05710     RtlInitUnicodeString(&ntUnicodeString, NULL);
05711 
05712     DebugPrint((2, "ClassCreateFdo: Create device object\n"));
05713 
05714     ASSERT(LowerDevice);
05715 
05716     //
05717     // Make sure that if we're making PDO's we have an enumeration routine
05718     //
05719 
05720     isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
05721 
05722     ASSERT(IsFdo || isPartitionable);
05723 
05724     //
05725     // Grab the correct dev-info structure out of the init data
05726     //
05727 
05728     if(IsFdo) {
05729         devInfo = &(driverExtension->InitData.FdoData);
05730     } else {
05731         devInfo = &(driverExtension->InitData.PdoData);
05732     }
05733 
05734     characteristics = devInfo->DeviceCharacteristics;
05735 
05736     if(ARGUMENT_PRESENT(ObjectNameBuffer)) {
05737         DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
05738 
05739         RtlInitString(&ntNameString, ObjectNameBuffer);
05740 
05741         status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
05742 
05743         if (!NT_SUCCESS(status)) {
05744 
05745             DebugPrint((1,
05746                         "ClassCreateFdo: Cannot convert string %s\n",
05747                         ObjectNameBuffer));
05748 
05749             ntUnicodeString.Buffer = NULL;
05750             return status;
05751         }
05752     } else {
05753         DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
05754 
05755         if(IsFdo == FALSE) {
05756 
05757             //
05758             // PDO's have to have some sort of name.
05759             //
05760 
05761             SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
05762         }
05763 
05764         RtlInitUnicodeString(&ntUnicodeString, NULL);
05765     }
05766 
05767     status = IoCreateDevice(DriverObject,
05768                             devInfo->DeviceExtensionSize,
05769                             &ntUnicodeString,
05770                             devInfo->DeviceType,
05771                             devInfo->DeviceCharacteristics,
05772                             FALSE,
05773                             &deviceObject);
05774 
05775     if (!NT_SUCCESS(status)) {
05776 
05777         DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
05778                     status));
05779         ASSERT(deviceObject == NULL);
05780 
05781         //
05782         // buffer is not used any longer here.
05783         //
05784 
05785         if (ntUnicodeString.Buffer != NULL) {
05786             DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
05787             ExFreePool(ntUnicodeString.Buffer);
05788             RtlInitUnicodeString(&ntUnicodeString, NULL);
05789         }
05790 
05791     } else {
05792 
05793         PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
05794 
05795         RtlZeroMemory(
05796             deviceObject->DeviceExtension,
05797             devInfo->DeviceExtensionSize);
05798 
05799         //
05800         // Setup version code
05801         //
05802 
05803         commonExtension->Version = 0x03;
05804 
05805         //
05806         // Setup the remove lock and event
05807         //
05808 
05809         commonExtension->IsRemoved = NO_REMOVE;
05810         commonExtension->RemoveLock = 0;
05811         KeInitializeEvent(&commonExtension->RemoveEvent,
05812                           SynchronizationEvent,
05813                           FALSE);
05814 
05815         #if DBG
05816             KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
05817             commonExtension->RemoveTrackingList = NULL;
05818         #else
05819             commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
05820             commonExtension->RemoveTrackingList = (PVOID) -1;
05821         #endif
05822 
05823         //
05824         // Acquire the lock once.  This reference will be released when the
05825         // remove IRP has been received.
05826         //
05827 
05828         ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
05829 
05830         //
05831         // Store a pointer to the driver extension so we don't have to do
05832         // lookups to get it.
05833         //
05834 
05835         commonExtension->DriverExtension = driverExtension;
05836 
05837         //
05838         // Fill in entry points
05839         //
05840 
05841         commonExtension->DevInfo = devInfo;
05842 
05843         //
05844         // Initialize some of the common values in the structure
05845         //
05846 
05847         commonExtension->DeviceObject = deviceObject;
05848 
05849         commonExtension->LowerDeviceObject = NULL;
05850 
05851         if(IsFdo) {
05852 
05853             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
05854 
05855             commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
05856 
05857             //
05858             // Set the initial device object flags.
05859             //
05860 
05861             SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
05862 
05863             //
05864             // Clear the PDO list
05865             //
05866 
05867             commonExtension->ChildList = NULL;
05868 
05869             commonExtension->DriverData =
05870                 ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
05871 
05872             if(isPartitionable) {
05873 
05874                 commonExtension->PartitionNumber = 0;
05875             } else {
05876                 commonExtension->PartitionNumber = (ULONG) (-1L);
05877             }
05878 
05879             fdoExtension->DevicePowerState = PowerDeviceD0;
05880 
05881             KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
05882                               SynchronizationEvent,
05883                               TRUE);
05884 
05885             KeInitializeEvent(&fdoExtension->ChildLock,
05886                               SynchronizationEvent,
05887                               TRUE);
05888 
05889             status = ClasspAllocateReleaseRequest(deviceObject);
05890 
05891             if(!NT_SUCCESS(status)) {
05892                 IoDeleteDevice(deviceObject);
05893                 *DeviceObject = NULL;
05894 
05895                 if (ntUnicodeString.Buffer != NULL) {
05896                     DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
05897                     ExFreePool(ntUnicodeString.Buffer);
05898                     RtlInitUnicodeString(&ntUnicodeString, NULL);
05899                 }
05900 
05901                 return status;
05902             }
05903 
05904         } else {
05905 
05906             PPHYSICAL_DEVICE_EXTENSION pdoExtension =
05907                 deviceObject->DeviceExtension;
05908 
05909             PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
05910                 LowerDevice->DeviceExtension;
05911 
05912             SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
05913 
05914             commonExtension->PartitionZeroExtension = p0Extension;
05915 
05916             //
05917             // Stick this onto the PDO list
05918             //
05919 
05920             ClassAddChild(p0Extension, pdoExtension, TRUE);
05921 
05922             commonExtension->DriverData = (PVOID) (pdoExtension + 1);
05923 
05924             //
05925             // Get the top of stack for the lower device - this allows
05926             // filters to get stuck in between the partitions and the
05927             // physical disk.
05928             //
05929 
05930             commonExtension->LowerDeviceObject =
05931                 IoGetAttachedDeviceReference(LowerDevice);
05932 
05933             //
05934             // Pnp will keep a reference to the lower device object long
05935             // after this partition has been deleted.  Dereference now so
05936             // we don't have to deal with it later.
05937             //
05938 
05939             ObDereferenceObject(commonExtension->LowerDeviceObject);
05940         }
05941 
05942         KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
05943 
05944         commonExtension->IsFdo = IsFdo;
05945 
05946         commonExtension->DeviceName = ntUnicodeString;
05947 
05948         commonExtension->PreviousState = 0xff;
05949 
05950         InitializeDictionary(&(commonExtension->FileObjectDictionary));
05951 
05952         commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
05953     }
05954 
05955     *DeviceObject = deviceObject;
05956 
05957     return status;
05958 } // end ClassCreateDeviceObject()
05959 
05960 /*++////////////////////////////////////////////////////////////////////////////
05961 
05962 ClassClaimDevice()
05963 
05964 Routine Description:
05965 
05966     This function claims a device in the port driver.  The port driver object
05967     is updated with the correct driver object if the device is successfully
05968     claimed.
05969 
05970 Arguments:
05971 
05972     LowerDeviceObject - Supplies the base port device object.
05973 
05974     Release - Indicates the logical unit should be released rather than claimed.
05975 
05976 Return Value:
05977 
05978     Returns a status indicating success or failure of the operation.
05979 
05980 --*/
05981 NTSTATUS
05982 ClassClaimDevice(
05983     IN PDEVICE_OBJECT LowerDeviceObject,
05984     IN BOOLEAN Release
05985     )
05986 {
05987     IO_STATUS_BLOCK    ioStatus;
05988     PIRP               irp;
05989     PIO_STACK_LOCATION irpStack;
05990     KEVENT             event;
05991     NTSTATUS           status;
05992     SCSI_REQUEST_BLOCK srb;
05993 
05994     PAGED_CODE();
05995 
05996     //
05997     // Clear the SRB fields.
05998     //
05999 
06000     RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
06001 
06002     //
06003     // Write length to SRB.
06004     //
06005 
06006     srb.Length = sizeof(SCSI_REQUEST_BLOCK);
06007 
06008     srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
06009         SRB_FUNCTION_CLAIM_DEVICE;
06010 
06011     //
06012     // Set the event object to the unsignaled state.
06013     // It will be used to signal request completion
06014     //
06015 
06016     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
06017 
06018     //
06019     // Build synchronous request with no transfer.
06020     //
06021 
06022     irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
06023                                         LowerDeviceObject,
06024                                         NULL,
06025                                         0,
06026                                         NULL,
06027                                         0,
06028                                         TRUE,
06029                                         &event,
06030                                         &ioStatus);
06031 
06032     if (irp == NULL) {
06033         DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
06034         return STATUS_INSUFFICIENT_RESOURCES;
06035     }
06036 
06037     irpStack = IoGetNextIrpStackLocation(irp);
06038 
06039     //
06040     // Save SRB address in next stack for port driver.
06041     //
06042 
06043     irpStack->Parameters.Scsi.Srb = &srb;
06044 
06045     //
06046     // Set up IRP Address.
06047     //
06048 
06049     srb.OriginalRequest = irp;
06050 
06051     //
06052     // Call the port driver with the request and wait for it to complete.
06053     //
06054 
06055     status = IoCallDriver(LowerDeviceObject, irp);
06056     if (status == STATUS_PENDING) {
06057 
06058         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
06059         status = ioStatus.Status;
06060     }
06061 
06062     //
06063     // If this is a release request, then just decrement the reference count
06064     // and return.  The status does not matter.
06065     //
06066 
06067     if (Release) {
06068 
06069         // ObDereferenceObject(LowerDeviceObject);
06070         return STATUS_SUCCESS;
06071     }
06072 
06073     if (!NT_SUCCESS(status)) {
06074         return status;
06075     }
06076 
06077     ASSERT(srb.DataBuffer != NULL);
06078     ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
06079 
06080     return status;
06081 } // end ClassClaimDevice()
06082 
06083 /*++////////////////////////////////////////////////////////////////////////////
06084 
06085 ClassInternalIoControl()
06086 
06087 Routine Description:
06088 
06089     This routine passes internal device controls to the port driver.
06090     Internal device controls are used by higher level drivers both for ioctls
06091     and to pass through scsi requests.
06092 
06093     If the IoControlCode does not match any of the handled ioctls and is
06094     a valid system address then the request will be treated as an SRB and
06095     passed down to the lower driver.  If the IoControlCode is not a valid
06096     system address the ioctl will be failed.
06097 
06098     Callers must therefore be extremely cautious to pass correct, initialized
06099     values to this function.
06100 
06101 Arguments:
06102 
06103     DeviceObject - Supplies a pointer to the device object for this request.
06104 
06105     Irp - Supplies the Irp making the request.
06106 
06107 Return Value:
06108 
06109    Returns back a STATUS_PENDING or a completion status.
06110 
06111 --*/
06112 NTSTATUS
06113 ClassInternalIoControl(
06114     IN PDEVICE_OBJECT DeviceObject,
06115     IN PIRP Irp
06116     )
06117 {
06118     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
06119 
06120     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
06121     PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
06122 
06123     ULONG isRemoved;
06124 
06125     PSCSI_REQUEST_BLOCK srb;
06126 
06127     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
06128 
06129     if(isRemoved) {
06130 
06131         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
06132 
06133         ClassReleaseRemoveLock(DeviceObject, Irp);
06134 
06135         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
06136 
06137         return STATUS_DEVICE_DOES_NOT_EXIST;
06138     }
06139 
06140     //
06141     // Get a pointer to the SRB.
06142     //
06143 
06144     srb = irpStack->Parameters.Scsi.Srb;
06145 
06146     //
06147     // Set the parameters in the next stack location.
06148     //
06149 
06150     if(commonExtension->IsFdo) {
06151         nextStack->Parameters.Scsi.Srb = srb;
06152         nextStack->MajorFunction = IRP_MJ_SCSI;
06153         nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
06154 
06155     } else {
06156 
06157         IoCopyCurrentIrpStackLocationToNext(Irp);
06158     }
06159 
06160     ClassReleaseRemoveLock(DeviceObject, Irp);
06161 
06162     return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
06163 } // end ClassInternalIoControl()
06164 
06165 /*++////////////////////////////////////////////////////////////////////////////
06166 
06167 ClassQueryTimeOutRegistryValue()
06168 
06169 Routine Description:
06170 
06171     This routine determines whether a reg key for a user-specified timeout
06172     value exists.  This should be called at initialization time.
06173 
06174 Arguments:
06175 
06176     DeviceObject - Pointer to the device object we are retrieving the timeout
06177                    value for
06178 
06179 Return Value:
06180 
06181     None, but it sets a new default timeout for a class of devices.
06182 
06183 --*/
06184 ULONG
06185 ClassQueryTimeOutRegistryValue(
06186     IN PDEVICE_OBJECT DeviceObject
06187     )
06188 {
06189     //
06190     // Find the appropriate reg. key
06191     //
06192 
06193     PCLASS_DRIVER_EXTENSION
06194         driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
06195                                                      CLASS_DRIVER_EXTENSION_KEY);
06196 
06197     PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
06198 
06199     PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
06200     PWSTR path;
06201     NTSTATUS status;
06202     LONG     timeOut = 0;
06203     ULONG    zero = 0;
06204     ULONG    size;
06205 
06206     PAGED_CODE();
06207 
06208     if (!registryPath) {
06209         return 0;
06210     }
06211 
06212     parameters = ExAllocatePoolWithTag(NonPagedPool,
06213                                 sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
06214                                 '1BcS');
06215 
06216     if (!parameters) {
06217         return 0;
06218     }
06219 
06220     size = registryPath->MaximumLength + sizeof(WCHAR);
06221     path = ExAllocatePoolWithTag(NonPagedPool, size, '2BcS');
06222 
06223     if (!path) {
06224         ExFreePool(parameters);
06225         return 0;
06226     }
06227 
06228     RtlZeroMemory(path,size);
06229     RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
06230 
06231 
06232     //
06233     // Check for the Timeout value.
06234     //
06235 
06236     RtlZeroMemory(parameters,
06237                   (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
06238 
06239     parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
06240     parameters[0].Name          = L"TimeOutValue";
06241     parameters[0].EntryContext  = &timeOut;
06242     parameters[0].DefaultType   = REG_DWORD;
06243     parameters[0].DefaultData   = &zero;
06244     parameters[0].DefaultLength = sizeof(ULONG);
06245 
06246     status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
06247                                     path,
06248                                     parameters,
06249                                     NULL,
06250                                     NULL);
06251 
06252     if (!(NT_SUCCESS(status))) {
06253         timeOut = 0;
06254     }
06255 
06256     ExFreePool(parameters);
06257     ExFreePool(path);
06258 
06259     DebugPrint((2,
06260                 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
06261                 timeOut));
06262 
06263 
06264     return timeOut;
06265 
06266 } // end ClassQueryTimeOutRegistryValue()
06267 
06268 /*++////////////////////////////////////////////////////////////////////////////
06269 
06270 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
06271 
06272 Routine Description:
06273 
06274     This routine executes when the port driver has completed a check verify
06275     ioctl.  It will set the status of the master Irp, copy the media change
06276     count and complete the request.
06277 
06278 Arguments:
06279 
06280     Fdo - Supplies the functional device object which represents the logical unit.
06281 
06282     Irp - Supplies the Irp which has completed.
06283 
06284     Context - NULL
06285 
06286 Return Value:
06287 
06288     NT status
06289 
06290 --*/
06291 NTSTATUS
06292 ClassCheckVerifyComplete(
06293     IN PDEVICE_OBJECT Fdo,
06294     IN PIRP Irp,
06295     IN PVOID Context
06296     )
06297 {
06298     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
06299     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
06300 
06301     PIRP originalIrp;
06302 
06303     ASSERT_FDO(Fdo);
06304 
06305     originalIrp = irpStack->Parameters.Others.Argument1;
06306 
06307     //
06308     // Copy the media change count and status
06309     //
06310 
06311     *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
06312         fdoExtension->MediaChangeCount;
06313 
06314     DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
06315                    "device %d is %lx - saved as %lx\n",
06316                 fdoExtension->DeviceNumber,
06317                 fdoExtension->MediaChangeCount,
06318                 *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
06319 
06320     originalIrp->IoStatus.Status = Irp->IoStatus.Status;
06321     originalIrp->IoStatus.Information = sizeof(ULONG);
06322 
06323     ClassReleaseRemoveLock(Fdo, originalIrp);
06324     ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
06325 
06326     IoFreeIrp(Irp);
06327 
06328     return STATUS_MORE_PROCESSING_REQUIRED;
06329 
06330 } // end ClassCheckVerifyComplete()
06331 
06332 /*++////////////////////////////////////////////////////////////////////////////
06333 
06334 ClassGetDescriptor()
06335 
06336 Routine Description:
06337 
06338     This routine will perform a query for the specified property id and will
06339     allocate a non-paged buffer to store the data in.  It is the responsibility
06340     of the caller to ensure that this buffer is freed.
06341 
06342     This routine must be run at IRQL_PASSIVE_LEVEL
06343 
06344 Arguments:
06345 
06346     DeviceObject - the device to query
06347     DeviceInfo - a location to store a pointer to the buffer we allocate
06348 
06349 Return Value:
06350 
06351     status
06352     if status is unsuccessful *DeviceInfo will be set to NULL, else the
06353     buffer allocated on behalf of the caller.
06354 
06355 --*/
06356 NTSTATUS
06357 ClassGetDescriptor(
06358     IN PDEVICE_OBJECT DeviceObject,
06359     IN PSTORAGE_PROPERTY_ID PropertyId,
06360     OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
06361     )
06362 {
06363     STORAGE_PROPERTY_QUERY query;
06364     IO_STATUS_BLOCK ioStatus;
06365 
06366     PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
06367     ULONG length;
06368 
06369     UCHAR pass = 0;
06370 
06371     PAGED_CODE();
06372 
06373     //
06374     // Set the passed-in descriptor pointer to NULL as default
06375     //
06376 
06377     *Descriptor = NULL;
06378 
06379 
06380     RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
06381     query.PropertyId = *PropertyId;
06382     query.QueryType = PropertyStandardQuery;
06383 
06384     //
06385     // On the first pass we just want to get the first few
06386     // bytes of the descriptor so we can read it's size
06387     //
06388 
06389     descriptor = (PVOID)&query;
06390 
06391     ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
06392 
06393     ClassSendDeviceIoControlSynchronous(
06394         IOCTL_STORAGE_QUERY_PROPERTY,
06395         DeviceObject,
06396         &query,
06397         sizeof(STORAGE_PROPERTY_QUERY),
06398         sizeof(ULONG) * 2,
06399         FALSE,
06400         &ioStatus
06401         );
06402 
06403     if(!NT_SUCCESS(ioStatus.Status)) {
06404 
06405         DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
06406                        "query properties #1\n", ioStatus.Status));
06407         return ioStatus.Status;
06408     }
06409 
06410     if (descriptor->Size == 0) {
06411 
06412         //
06413         // This DebugPrint is to help third-party driver writers
06414         //
06415 
06416         DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
06417                     "%x\n", ioStatus.Status));
06418         return STATUS_UNSUCCESSFUL;
06419 
06420     }
06421 
06422     //
06423     // This time we know how much data there is so we can
06424     // allocate a buffer of the correct size
06425     //
06426 
06427     length = descriptor->Size;
06428 
06429     descriptor = ExAllocatePoolWithTag(NonPagedPool, length, '4BcS');
06430 
06431     if(descriptor == NULL) {
06432 
06433         DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
06434                     "(%d bytes)\n", length));
06435         return STATUS_INSUFFICIENT_RESOURCES;
06436     }
06437 
06438     //
06439     // setup the query again, as it was overwritten above
06440     //
06441 
06442     RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
06443     query.PropertyId = *PropertyId;
06444     query.QueryType = PropertyStandardQuery;
06445 
06446     //
06447     // copy the input to the new outputbuffer
06448     //
06449 
06450     RtlCopyMemory(descriptor,
06451                   &query,
06452                   sizeof(STORAGE_PROPERTY_QUERY)
06453                   );
06454 
06455     ClassSendDeviceIoControlSynchronous(
06456         IOCTL_STORAGE_QUERY_PROPERTY,
06457         DeviceObject,
06458         descriptor,
06459         sizeof(STORAGE_PROPERTY_QUERY),
06460         length,
06461         FALSE,
06462         &ioStatus
06463         );
06464 
06465     if(!NT_SUCCESS(ioStatus.Status)) {
06466 
06467         DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
06468                        "query properties #1\n", ioStatus.Status));
06469         ExFreePool(descriptor);
06470         return ioStatus.Status;
06471     }
06472 
06473     //
06474     // return the memory we've allocated to the caller
06475     //
06476 
06477     *Descriptor = descriptor;
06478     return ioStatus.Status;
06479 } // end ClassGetDescriptor()
06480 
06481 /*++////////////////////////////////////////////////////////////////////////////
06482 
06483 ClassSignalCompletion()
06484 
06485 Routine Description:
06486 
06487     This completion routine will signal the event given as context and then
06488     return STATUS_MORE_PROCESSING_REQUIRED to stop event completion.  It is
06489     the responsibility of the routine waiting on the event to complete the
06490     request and free the event.
06491 
06492 Arguments:
06493 
06494     DeviceObject - a pointer to the device object
06495 
06496     Irp - a pointer to the irp
06497 
06498     Event - a pointer to the event to signal
06499 
06500 Return Value:
06501 
06502     STATUS_MORE_PROCESSING_REQUIRED
06503 
06504 --*/
06505 NTSTATUS
06506 ClassSignalCompletion(
06507     IN PDEVICE_OBJECT DeviceObject,
06508     IN PIRP Irp,
06509     IN PKEVENT Event
06510     )
06511 {
06512     KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
06513 
06514     return STATUS_MORE_PROCESSING_REQUIRED;
06515 } // end ClassSignalCompletion()
06516 
06517 /*++////////////////////////////////////////////////////////////////////////////
06518 
06519 ClassPnpQueryFdoRelations()
06520 
06521 Routine Description:
06522 
06523     This routine will call the driver's enumeration routine to update the
06524     list of PDO's.  It will then build a response to the
06525     IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
06526     the irp.
06527 
06528 Arguments:
06529 
06530     Fdo - a pointer to the functional device object we are enumerating
06531 
06532     Irp - a pointer to the enumeration request
06533 
06534 Return Value:
06535 
06536     status
06537 
06538 --*/
06539 NTSTATUS
06540 ClassPnpQueryFdoRelations(
06541     IN PDEVICE_OBJECT Fdo,
06542     IN PIRP Irp
06543     )
06544 {
06545     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
06546     PCLASS_DRIVER_EXTENSION
06547         driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
06548                                                      CLASS_DRIVER_EXTENSION_KEY);
06549     NTSTATUS status;
06550 
06551     PAGED_CODE();
06552 
06553     //
06554     // If there's already an enumeration in progress then don't start another
06555     // one.
06556     //
06557 
06558     if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
06559         status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
06560     }
06561 
06562     Irp->IoStatus.Information = (ULONG_PTR) NULL;
06563 
06564     Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
06565                                 Fdo,
06566                                 BusRelations,
06567                                 (PDEVICE_RELATIONS*)&Irp->IoStatus.Information);
06568     InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
06569 
06570     return Irp->IoStatus.Status;
06571 } // end ClassPnpQueryFdoRelations()
06572 
06573 /*++////////////////////////////////////////////////////////////////////////////
06574 
06575 ClassMarkChildrenMissing()
06576 
06577 Routine Description:
06578 
06579     This routine will call ClassMarkChildMissing() for all children.
06580     It acquires the ChildLock before calling ClassMarkChildMissing().
06581 
06582 Arguments:
06583 
06584     Fdo - the "bus's" device object, such as the disk FDO for non-removable
06585         disks with multiple partitions.
06586 
06587 Return Value:
06588 
06589     None
06590 
06591 --*/
06592 VOID
06593 ClassMarkChildrenMissing(
06594     IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
06595     )
06596 {
06597     PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
06598     PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
06599 
06600     PAGED_CODE();
06601 
06602     ClassAcquireChildLock(Fdo);
06603 
06604     while (nextChild){
06605         PPHYSICAL_DEVICE_EXTENSION tmpChild;
06606 
06607         /*
06608          *  ClassMarkChildMissing will also dequeue the child extension.
06609          *  So get the next pointer before calling ClassMarkChildMissing.
06610          */
06611         tmpChild = nextChild;
06612         nextChild = tmpChild->CommonExtension.ChildList;
06613         ClassMarkChildMissing(tmpChild, FALSE);
06614     }
06615     ClassReleaseChildLock(Fdo);
06616     return;
06617 } // end ClassMarkChildrenMissing()
06618 
06619 /*++////////////////////////////////////////////////////////////////////////////
06620 
06621 ClassMarkChildMissing()
06622 
06623 Routine Description:
06624 
06625     This routine will make an active child "missing."  If the device has never
06626     been enumerated then it will be deleted on the spot.  If the device has
06627     not been enumerated then it will be marked as missing so that we can
06628     not report it in the next device enumeration.
06629 
06630 Arguments:
06631 
06632     Child - the child device to be marked as missing.
06633 
06634     AcquireChildLock - TRUE if the child lock should be acquired before removing
06635                        the missing child.  FALSE if the child lock is already
06636                        acquired by this thread.
06637 
06638 Return Value:
06639 
06640     returns whether or not the child device object has previously been reported
06641     to PNP.
06642 
06643 --*/
06644 BOOLEAN
06645 ClassMarkChildMissing(
06646     IN PPHYSICAL_DEVICE_EXTENSION Child,
06647     IN BOOLEAN AcquireChildLock
06648     )
06649 {
06650     BOOLEAN returnValue = Child->IsEnumerated;
06651 
06652     PAGED_CODE();
06653     ASSERT_PDO(Child->DeviceObject);
06654 
06655     Child->IsMissing = TRUE;
06656 
06657     //
06658     // Make sure this child is not in the active list.
06659     //
06660 
06661     ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
06662                      Child,
06663                      AcquireChildLock);
06664 
06665     if(Child->IsEnumerated == FALSE) {
06666         ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
06667     }
06668 
06669     return returnValue;
06670 } // end ClassMarkChildMissing()
06671 
06672 /*++////////////////////////////////////////////////////////////////////////////
06673 
06674 ClassRetrieveDeviceRelations()
06675 
06676 Routine Description:
06677 
06678     This routine will allocate a buffer to hold the specified list of
06679     relations.  It will then fill in the list with referenced device pointers
06680     and will return the request.
06681 
06682 Arguments:
06683 
06684     Fdo - pointer to the FDO being queried
06685 
06686     RelationType - what type of relations are being queried
06687 
06688     DeviceRelations - a location to store a pointer to the response
06689 
06690 Return Value:
06691 
06692     status
06693 
06694 --*/
06695 NTSTATUS
06696 ClassRetrieveDeviceRelations(
06697     IN PDEVICE_OBJECT Fdo,
06698     IN DEVICE_RELATION_TYPE RelationType,
06699     OUT PDEVICE_RELATIONS *DeviceRelations
06700     )
06701 {
06702     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
06703 
06704     ULONG count = 0;
06705     ULONG i;
06706 
06707     PPHYSICAL_DEVICE_EXTENSION nextChild;
06708 
06709     ULONG relationsSize;
06710     PDEVICE_RELATIONS deviceRelations = NULL;
06711 
06712     NTSTATUS status;
06713 
06714     PAGED_CODE();
06715 
06716     ClassAcquireChildLock(fdoExtension);
06717 
06718     nextChild = fdoExtension->CommonExtension.ChildList;
06719 
06720     //
06721     // Count the number of PDO's attached to this disk
06722     //
06723 
06724     while(nextChild != NULL) {
06725         PCOMMON_DEVICE_EXTENSION commonExtension;
06726 
06727         commonExtension = &(nextChild->CommonExtension);
06728 
06729         ASSERTMSG("ClassPnp internal error: missing child on active list\n",
06730                   (nextChild->IsMissing == FALSE));
06731 
06732         nextChild = commonExtension->ChildList;
06733 
06734         count++;
06735     };
06736 
06737     relationsSize = (sizeof(DEVICE_RELATIONS) +
06738                      (count * sizeof(PDEVICE_OBJECT)));
06739 
06740     deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
06741 
06742     if(deviceRelations == NULL) {
06743 
06744         DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
06745                        "%d bytes for device relations\n", relationsSize));
06746 
06747         ClassReleaseChildLock(fdoExtension);
06748 
06749         return STATUS_INSUFFICIENT_RESOURCES;
06750     }
06751 
06752     RtlZeroMemory(deviceRelations, relationsSize);
06753 
06754     nextChild = fdoExtension->CommonExtension.ChildList;
06755     i = count - 1;
06756 
06757     while(nextChild != NULL) {
06758         PCOMMON_DEVICE_EXTENSION commonExtension;
06759 
06760         commonExtension = &(nextChild->CommonExtension);
06761 
06762         ASSERTMSG("ClassPnp internal error: missing child on active list\n",
06763                   (nextChild->IsMissing == FALSE));
06764 
06765         deviceRelations->Objects[i--] = nextChild->DeviceObject;
06766 
06767         status = ObReferenceObjectByPointer(
06768                     nextChild->DeviceObject,
06769                     0,
06770                     NULL,
06771                     KernelMode);
06772         ASSERT(NT_SUCCESS(status));
06773 
06774         nextChild->IsEnumerated = TRUE;
06775         nextChild = commonExtension->ChildList;
06776     }
06777 
06778     ASSERTMSG("Child list has changed: ", i == -1);
06779 
06780     deviceRelations->Count = count;
06781     *DeviceRelations = deviceRelations;
06782     ClassReleaseChildLock(fdoExtension);
06783     return STATUS_SUCCESS;
06784 } // end ClassRetrieveDeviceRelations()
06785 
06786 /*++////////////////////////////////////////////////////////////////////////////
06787 
06788 ClassGetPdoId()
06789 
06790 Routine Description:
06791 
06792     This routine will call into the driver to retrieve a copy of one of it's
06793     id strings.
06794 
06795 Arguments:
06796 
06797     Pdo - a pointer to the pdo being queried
06798 
06799     IdType - which type of id string is being queried
06800 
06801     IdString - an allocated unicode string structure which the driver
06802                can fill in.
06803 
06804 Return Value:
06805 
06806     status
06807 
06808 --*/
06809 NTSTATUS
06810 ClassGetPdoId(
06811     IN PDEVICE_OBJECT Pdo,
06812     IN BUS_QUERY_ID_TYPE IdType,
06813     IN PUNICODE_STRING IdString
06814     )
06815 {
06816     PCLASS_DRIVER_EXTENSION
06817         driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject,
06818                                                      CLASS_DRIVER_EXTENSION_KEY);
06819 
06820     ASSERT_PDO(Pdo);
06821     ASSERT(driverExtension->InitData.ClassQueryId);
06822 
06823     PAGED_CODE();
06824 
06825     return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
06826 } // end ClassGetPdoId()
06827 
06828 /*++////////////////////////////////////////////////////////////////////////////
06829 
06830 ClassQueryPnpCapabilities()
06831 
06832 Routine Description:
06833 
06834     This routine will call into the class driver to retrieve it's pnp
06835     capabilities.
06836 
06837 Arguments:
06838 
06839     PhysicalDeviceObject - The physical device object to retrieve properties
06840                            for.
06841 
06842 Return Value:
06843 
06844     status
06845 
06846 --*/
06847 NTSTATUS
06848 ClassQueryPnpCapabilities(
06849     IN PDEVICE_OBJECT DeviceObject,
06850     IN PDEVICE_CAPABILITIES Capabilities
06851     )
06852 {
06853     PCLASS_DRIVER_EXTENSION driverExtension =
06854         ClassGetDriverExtension(DeviceObject->DriverObject);
06855     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
06856 
06857     PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
06858 
06859     PAGED_CODE();
06860 
06861     ASSERT(DeviceObject);
06862     ASSERT(Capabilities);
06863 
06864     if(commonExtension->IsFdo) {
06865         queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
06866     } else {
06867         queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
06868     }
06869 
06870     if(queryRoutine) {
06871         return queryRoutine(DeviceObject,
06872                             Capabilities);
06873     } else {
06874         return STATUS_NOT_IMPLEMENTED;
06875     }
06876 } // end ClassQueryPnpCapabilities()
06877 
06878 /*++////////////////////////////////////////////////////////////////////////////
06879 
06880 ClassInvalidateBusRelations()
06881 
06882 Routine Description:
06883 
06884     This routine re-enumerates the devices on the "bus".  It will call into
06885     the driver's ClassEnumerate routine to update the device objects
06886     immediately.  It will then schedule a bus re-enumeration for pnp by calling
06887     IoInvalidateDeviceRelations.
06888 
06889 Arguments:
06890 
06891     Fdo - a pointer to the functional device object for this bus
06892 
06893 Return Value:
06894 
06895     none
06896 
06897 --*/
06898 VOID
06899 ClassInvalidateBusRelations(
06900     IN PDEVICE_OBJECT Fdo
06901     )
06902 {
06903     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
06904     PCLASS_DRIVER_EXTENSION
06905         driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
06906                                                      CLASS_DRIVER_EXTENSION_KEY);
06907 
06908     NTSTATUS status = STATUS_SUCCESS;
06909 
06910     PAGED_CODE();
06911 
06912     ASSERT_FDO(Fdo);
06913     ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
06914 
06915     if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
06916         status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
06917     }
06918     InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
06919 
06920     if(!NT_SUCCESS(status)) {
06921 
06922         DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
06923                        "returned %lx\n", status));
06924     }
06925 
06926     IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
06927 
06928     return;
06929 } // end ClassInvalidateBusRelations()
06930 
06931 /*++////////////////////////////////////////////////////////////////////////////
06932 
06933 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
06934 
06935 Routine Description:
06936 
06937     This routine is called to handle the "removal" of a device.  It will
06938     forward the request downwards if necesssary, call into the driver
06939     to release any necessary resources (memory, events, etc) and then
06940     will delete the device object.
06941 
06942 Arguments:
06943 
06944     DeviceObject - a pointer to the device object being removed
06945 
06946     RemoveType - indicates what type of remove this is (regular or surprise).
06947 
06948 Return Value:
06949 
06950     status
06951 
06952 --*/
06953 NTSTATUS
06954 ClassRemoveDevice(
06955     IN PDEVICE_OBJECT DeviceObject,
06956     IN UCHAR RemoveType
06957     )
06958 {
06959     PCLASS_DRIVER_EXTENSION
06960         driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
06961                                                      CLASS_DRIVER_EXTENSION_KEY);
06962     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
06963     PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
06964     PCLASS_WMI_INFO classWmiInfo;
06965     BOOLEAN proceedWithRemove = TRUE;
06966     NTSTATUS status;
06967 
06968     PAGED_CODE();
06969 
06970     commonExtension->IsRemoved = REMOVE_PENDING;
06971 
06972     /*
06973      *  Deregister from WMI.
06974      */
06975     classWmiInfo = commonExtension->IsFdo ?
06976                             &driverExtension->InitData.FdoData.ClassWmiInfo :
06977                             &driverExtension->InitData.PdoData.ClassWmiInfo;
06978     if (classWmiInfo->GuidRegInfo){
06979         status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
06980         DBGTRACE(ClassDebugInfo, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
06981     }
06982 
06983     /*
06984      *  If we exposed a "shingle" (a named device interface openable by CreateFile)
06985      *  then delete it now.
06986      */
06987     if (commonExtension->MountedDeviceInterfaceName.Buffer){
06988         IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
06989         RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
06990         RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
06991     }
06992 
06993     //
06994     // If this is a surprise removal we leave the device around - which means
06995     // we don't have to (or want to) drop the remove lock and wait for pending
06996     // requests to complete.
06997     //
06998 
06999     if (RemoveType == IRP_MN_REMOVE_DEVICE){
07000 
07001         //
07002         // Release the lock we acquired when the device object was created.
07003         //
07004 
07005         ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
07006 
07007         DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
07008                     commonExtension->RemoveLock));
07009 
07010         KeWaitForSingleObject(&commonExtension->RemoveEvent,
07011                               Executive,
07012                               KernelMode,
07013                               FALSE,
07014                               NULL);
07015 
07016         DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject));
07017 
07018         if(commonExtension->IsFdo) {
07019 
07020             DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
07021                            "remove request.\n", DeviceObject));
07022 
07023         }
07024         else {
07025             PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
07026 
07027             if (pdoExtension->IsMissing){
07028                 /*
07029                  *  The child partition PDO is missing, so we are going to go ahead
07030                  *  and delete it for the remove.
07031                  */
07032                 DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
07033             }
07034             else {
07035                 /*
07036                  *  We got a remove for a child partition PDO which is not actually missing.
07037                  *  So we will NOT actually delete it.
07038                  */
07039                 DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
07040 
07041                 //
07042                 // Reacquire the remove lock for the next time this comes around.
07043                 //
07044 
07045                 ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
07046 
07047                 //
07048                 // the device wasn't missing so it's not really been removed.
07049                 //
07050 
07051                 commonExtension->IsRemoved = NO_REMOVE;
07052 
07053                 IoInvalidateDeviceRelations(
07054                     commonExtension->PartitionZeroExtension->LowerPdo,
07055                     BusRelations);
07056 
07057                 proceedWithRemove = FALSE;
07058             }
07059         }
07060     }
07061 
07062 
07063     if (proceedWithRemove){
07064 
07065         /*
07066          *  Call the class driver's remove handler.
07067          *  All this is supposed to do is clean up its data and device interfaces.
07068          */
07069         ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
07070         status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
07071         ASSERT(NT_SUCCESS(status));
07072         status = STATUS_SUCCESS;
07073 
07074         if (commonExtension->IsFdo){
07075             PDEVICE_OBJECT pdo;
07076             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
07077 
07078             ClasspDisableTimer(fdoExtension->DeviceObject);
07079 
07080             if (RemoveType == IRP_MN_REMOVE_DEVICE){
07081 
07082                 PPHYSICAL_DEVICE_EXTENSION child;
07083 
07084                 //
07085                 // Cleanup the media detection resources now that the class driver
07086                 // has stopped it's timer (if any) and we can be sure they won't
07087                 // call us to do detection again.
07088                 //
07089 
07090                 ClassCleanupMediaChangeDetection(fdoExtension);
07091 
07092                 //
07093                 // Cleanup any Failure Prediction stuff
07094                 //
07095                 if (fdoExtension->FailurePredictionInfo) {
07096                     ExFreePool(fdoExtension->FailurePredictionInfo);
07097                     fdoExtension->FailurePredictionInfo = NULL;
07098                 }
07099 
07100                 /*
07101                  *  Ordinarily all child PDOs will be removed by the time
07102                  *  that the parent gets the REMOVE_DEVICE.
07103                  *  However, if a child PDO has been created but has not
07104                  *  been announced in a QueryDeviceRelations, then it is
07105                  *  just a private data structure unknown to pnp, and we have
07106                  *  to delete it ourselves.
07107                  */
07108                 ClassAcquireChildLock(fdoExtension);
07109                 while (child = ClassRemoveChild(fdoExtension, NULL, FALSE)){
07110 
07111                     //
07112                     // Yank the pdo.  This routine will unlink the device from the
07113                     // pdo list so NextPdo will point to the next one when it's
07114                     // complete.
07115                     //
07116                     child->IsMissing = TRUE;
07117                     ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
07118                 }
07119                 ClassReleaseChildLock(fdoExtension);
07120             }
07121             else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
07122                 /*
07123                  *  This is a surprise-remove on the parent FDO.
07124                  *  We will mark the child PDOs as missing so that they
07125                  *  will actually get deleted when they get a REMOVE_DEVICE.
07126                  */
07127                 ClassMarkChildrenMissing(fdoExtension);
07128             }
07129 
07130             ClasspFreeReleaseRequest(DeviceObject);
07131 
07132             if (RemoveType == IRP_MN_REMOVE_DEVICE){
07133 
07134                 //
07135                 // Free FDO-specific data structs
07136                 //
07137                 if (fdoExtension->PrivateFdoData){
07138 
07139                     DestroyAllTransferPackets(DeviceObject);
07140 
07141                     ExFreePool(fdoExtension->PrivateFdoData);
07142                     fdoExtension->PrivateFdoData = NULL;
07143                 }
07144 
07145                 if (commonExtension->DeviceName.Buffer) {
07146                     ExFreePool(commonExtension->DeviceName.Buffer);
07147                     RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
07148                 }
07149 
07150                 if (fdoExtension->AdapterDescriptor) {
07151                     ExFreePool(fdoExtension->AdapterDescriptor);
07152                     fdoExtension->AdapterDescriptor = NULL;
07153                 }
07154 
07155                 if (fdoExtension->DeviceDescriptor) {
07156                     ExFreePool(fdoExtension->DeviceDescriptor);
07157                     fdoExtension->DeviceDescriptor = NULL;
07158                 }
07159 
07160                 //
07161                 // Detach our device object from the stack - there's no reason
07162                 // to hold off our cleanup any longer.
07163                 //
07164 
07165                 IoDetachDevice(lowerDeviceObject);
07166             }
07167         }
07168         else {
07169             /*
07170              *  This is a child partition PDO.
07171              *  We have already determined that it was previously marked
07172              *  as missing.  So if this is a REMOVE_DEVICE, we will actually
07173              *  delete it.
07174              */
07175             if (RemoveType == IRP_MN_REMOVE_DEVICE){
07176                 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
07177                     commonExtension->PartitionZeroExtension;
07178                 PPHYSICAL_DEVICE_EXTENSION pdoExtension =
07179                     (PPHYSICAL_DEVICE_EXTENSION) commonExtension;
07180 
07181                 //
07182                 // See if this device is in the child list (if this was a suprise
07183                 // removal it might be) and remove it.
07184                 //
07185 
07186                 ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
07187             }
07188         }
07189 
07190         commonExtension->PartitionLength.QuadPart = 0;
07191 
07192         if (RemoveType == IRP_MN_REMOVE_DEVICE){
07193             IoDeleteDevice(DeviceObject);
07194         }
07195     }
07196 
07197     return STATUS_SUCCESS;
07198 } // end ClassRemoveDevice()
07199 
07200 /*++////////////////////////////////////////////////////////////////////////////
07201 
07202 ClassGetDriverExtension()
07203 
07204 Routine Description:
07205 
07206     This routine will return the classpnp's driver extension.
07207 
07208 Arguments:
07209 
07210     DriverObject - the driver object for which to get classpnp's extension
07211 
07212 Return Value:
07213 
07214     Either NULL if none, or a pointer to the driver extension
07215 
07216 --*/
07217 PCLASS_DRIVER_EXTENSION
07218 ClassGetDriverExtension(
07219     IN PDRIVER_OBJECT DriverObject
07220     )
07221 {
07222     return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
07223 } // end ClassGetDriverExtension()
07224 
07225 /*++////////////////////////////////////////////////////////////////////////////
07226 
07227 ClasspStartIo()
07228 
07229 Routine Description:
07230 
07231     This routine wraps the class driver's start io routine.  If the device
07232     is being removed it will complete any requests with
07233     STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
07234 
07235 Arguments:
07236 
07237 Return Value:
07238 
07239     none
07240 
07241 --*/
07242 VOID
07243 ClasspStartIo(
07244     IN PDEVICE_OBJECT DeviceObject,
07245     IN PIRP Irp
07246     )
07247 {
07248     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
07249 
07250     //
07251     // We're already holding the remove lock so just check the variable and
07252     // see what's going on.
07253     //
07254 
07255     if(commonExtension->IsRemoved) {
07256 
07257         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
07258 
07259         ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
07260         ClassReleaseRemoveLock(DeviceObject, Irp);
07261         ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
07262         IoStartNextPacket(DeviceObject, FALSE);
07263         ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
07264         return;
07265     }
07266 
07267     commonExtension->DriverExtension->InitData.ClassStartIo(
07268         DeviceObject,
07269         Irp);
07270 
07271     return;
07272 } // ClasspStartIo()
07273 
07274 /*++////////////////////////////////////////////////////////////////////////////
07275 
07276 ClassUpdateInformationInRegistry()
07277 
07278 Routine Description:
07279 
07280     This routine has knowledge about the layout of the device map information
07281     in the registry.  It will update this information to include a value
07282     entry specifying the dos device name that is assumed to get assigned
07283     to this NT device name.  For more information on this assigning of the
07284     dos device name look in the drive support routine in the hal that assigns
07285     all dos names.
07286 
07287     Since some versions of some device's firmware did not work and some
07288     vendors did not bother to follow the specification, the entire inquiry
07289     information must also be stored in the registry so than someone can
07290     figure out the firmware version.
07291 
07292 Arguments:
07293 
07294     DeviceObject - A pointer to the device object for the tape device.
07295 
07296 Return Value:
07297 
07298     None
07299 
07300 --*/
07301 VOID
07302 ClassUpdateInformationInRegistry(
07303     IN PDEVICE_OBJECT     Fdo,
07304     IN PCHAR              DeviceName,
07305     IN ULONG              DeviceNumber,
07306     IN PINQUIRYDATA       InquiryData,
07307     IN ULONG              InquiryDataLength
07308     )
07309 {
07310     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
07311     NTSTATUS          status;
07312     SCSI_ADDRESS      scsiAddress;
07313     OBJECT_ATTRIBUTES objectAttributes;
07314     PUCHAR            buffer;
07315     STRING            string;
07316     UNICODE_STRING    unicodeName;
07317     UNICODE_STRING    unicodeRegistryPath;
07318     UNICODE_STRING    unicodeData;
07319     HANDLE            targetKey;
07320     IO_STATUS_BLOCK   ioStatus;
07321 
07322 
07323     PAGED_CODE();
07324 
07325     ASSERT(DeviceName);
07326     fdoExtension = Fdo->DeviceExtension;
07327     buffer = NULL;
07328     targetKey = NULL;
07329     RtlZeroMemory(&unicodeName,         sizeof(UNICODE_STRING));
07330     RtlZeroMemory(&unicodeData,         sizeof(UNICODE_STRING));
07331     RtlZeroMemory(&unicodeRegistryPath, sizeof(UNICODE_STRING));
07332 
07333     TRY {
07334 
07335         //
07336         // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
07337         //
07338 
07339         ClassSendDeviceIoControlSynchronous(
07340             IOCTL_SCSI_GET_ADDRESS,
07341             Fdo,
07342             &scsiAddress,
07343             0,
07344             sizeof(SCSI_ADDRESS),
07345             FALSE,
07346             &ioStatus
07347             );
07348 
07349         if (!NT_SUCCESS(ioStatus.Status)) {
07350 
07351             status = ioStatus.Status;
07352             DebugPrint((1,
07353                         "UpdateInformationInRegistry: Get Address failed %lx\n",
07354                         status));
07355             LEAVE;
07356 
07357         } else {
07358 
07359             DebugPrint((1,
07360                         "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
07361                         scsiAddress.PortNumber,
07362                         scsiAddress.PathId,
07363                         scsiAddress.TargetId,
07364                         scsiAddress.Lun));
07365 
07366         }
07367 
07368         //
07369         // Allocate a buffer for the reg. spooge.
07370         //
07371 
07372         buffer = ExAllocatePoolWithTag(PagedPool, 1024, '6BcS');
07373 
07374         if (buffer == NULL) {
07375 
07376             //
07377             // There is not return value for this.  Since this is done at
07378             // claim device time (currently only system initialization) getting
07379             // the registry information correct will be the least of the worries.
07380             //
07381 
07382             LEAVE;
07383         }
07384 
07385         sprintf(buffer,
07386                 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
07387                 scsiAddress.PortNumber,
07388                 scsiAddress.PathId,
07389                 scsiAddress.TargetId,
07390                 scsiAddress.Lun);
07391 
07392         RtlInitString(&string, buffer);
07393 
07394         status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
07395                                               &string,
07396                                               TRUE);
07397 
07398         if (!NT_SUCCESS(status)) {
07399             LEAVE;
07400         }
07401 
07402         //
07403         // Open the registry key for the scsi information for this
07404         // scsibus, target, lun.
07405         //
07406 
07407         InitializeObjectAttributes(&objectAttributes,
07408                                    &unicodeRegistryPath,
07409                                    OBJ_CASE_INSENSITIVE,
07410                                    NULL,
07411                                    NULL);
07412 
07413         status = ZwOpenKey(&targetKey,
07414                            KEY_READ | KEY_WRITE,
07415                            &objectAttributes);
07416 
07417         if (!NT_SUCCESS(status)) {
07418             LEAVE;
07419         }
07420 
07421         //
07422         // Now construct and attempt to create the registry value
07423         // specifying the device name in the appropriate place in the
07424         // device map.
07425         //
07426 
07427         RtlInitUnicodeString(&unicodeName, L"DeviceName");
07428 
07429         sprintf(buffer, "%s%d", DeviceName, DeviceNumber);
07430         RtlInitString(&string, buffer);
07431         status = RtlAnsiStringToUnicodeString(&unicodeData,
07432                                               &string,
07433                                               TRUE);
07434         if (NT_SUCCESS(status)) {
07435             status = ZwSetValueKey(targetKey,
07436                                    &unicodeName,
07437                                    0,
07438                                    REG_SZ,
07439                                    unicodeData.Buffer,
07440                                    unicodeData.Length);
07441         }
07442 
07443         //
07444         // if they sent in data, update the registry
07445         //
07446 
07447         if (InquiryDataLength) {
07448 
07449             ASSERT(InquiryData);
07450 
07451             RtlInitUnicodeString(&unicodeName, L"InquiryData");
07452             status = ZwSetValueKey(targetKey,
07453                                    &unicodeName,
07454                                    0,
07455                                    REG_BINARY,
07456                                    InquiryData,
07457                                    InquiryDataLength);
07458         }
07459 
07460         // that's all, except to clean up.
07461 
07462     } FINALLY {
07463 
07464         if (unicodeData.Buffer) {
07465             RtlFreeUnicodeString(&unicodeData);
07466         }
07467         if (unicodeRegistryPath.Buffer) {
07468             RtlFreeUnicodeString(&unicodeRegistryPath);
07469         }
07470         if (targetKey) {
07471             ZwClose(targetKey);
07472         }
07473         if (buffer) {
07474             ExFreePool(buffer);
07475         }
07476 
07477     }
07478 
07479 } // end ClassUpdateInformationInRegistry()
07480 
07481 /*++////////////////////////////////////////////////////////////////////////////
07482 
07483 ClasspSendSynchronousCompletion()
07484 
07485 Routine Description:
07486 
07487     This completion routine will set the user event in the irp after
07488     freeing the irp and the associated MDL (if any).
07489 
07490 Arguments:
07491 
07492     DeviceObject - the device object which requested the completion routine
07493 
07494     Irp - the irp being completed
07495 
07496     Context - unused
07497 
07498 Return Value:
07499 
07500     STATUS_MORE_PROCESSING_REQUIRED
07501 
07502 --*/
07503 NTSTATUS
07504 ClasspSendSynchronousCompletion(
07505     IN PDEVICE_OBJECT DeviceObject,
07506     IN PIRP Irp,
07507     IN PVOID Context
07508     )
07509 {
07510     DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
07511                    DeviceObject, Irp, Context));
07512     //
07513     // First set the status and information fields in the io status block
07514     // provided by the caller.
07515     //
07516 
07517     *(Irp->UserIosb) = Irp->IoStatus;
07518 
07519     //
07520     // Unlock the pages for the data buffer.
07521     //
07522 
07523     if(Irp->MdlAddress) {
07524         MmUnlockPages(Irp->MdlAddress);
07525         IoFreeMdl(Irp->MdlAddress);
07526     }
07527 
07528     //
07529     // Signal the caller's event.
07530     //
07531 
07532     KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
07533 
07534     //
07535     // Free the MDL and the IRP.
07536     //
07537 
07538     IoFreeIrp(Irp);
07539 
07540     return STATUS_MORE_PROCESSING_REQUIRED;
07541 } // end ClasspSendSynchronousCompletion()
07542 
07543 /*++
07544 
07545     ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
07546 
07547 --*/
07548 VOID
07549 ClasspRegisterMountedDeviceInterface(
07550     IN PDEVICE_OBJECT DeviceObject
07551     )
07552 {
07553 
07554     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
07555     BOOLEAN isFdo = commonExtension->IsFdo;
07556 
07557     PDEVICE_OBJECT pdo;
07558     UNICODE_STRING interfaceName;
07559 
07560     NTSTATUS status;
07561 
07562     if(isFdo) {
07563 
07564         PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
07565 
07566         functionalExtension =
07567             (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
07568         pdo = functionalExtension->LowerPdo;
07569     } else {
07570         pdo = DeviceObject;
07571     }
07572 
07573     status = IoRegisterDeviceInterface(
07574                 pdo,
07575                 &MOUNTDEV_MOUNTED_DEVICE_GUID,
07576                 NULL,
07577                 &interfaceName
07578                 );
07579 
07580     if(NT_SUCCESS(status)) {
07581 
07582         //
07583         // Copy the interface name before setting the interface state - the
07584         // name is needed by the components we notify.
07585         //
07586 
07587         commonExtension->MountedDeviceInterfaceName = interfaceName;
07588         status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
07589 
07590         if(!NT_SUCCESS(status)) {
07591             RtlFreeUnicodeString(&interfaceName);
07592         }
07593     }
07594 
07595     if(!NT_SUCCESS(status)) {
07596         RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
07597                              NULL);
07598     }
07599     return;
07600 } // end ClasspRegisterMountedDeviceInterface()
07601 
07602 /*++////////////////////////////////////////////////////////////////////////////
07603 
07604 ClassSendDeviceIoControlSynchronous()
07605 
07606 Routine Description:
07607 
07608     This routine is based upon IoBuildDeviceIoControlRequest().  It has been
07609     modified to reduce code and memory by not double-buffering the io, using
07610     the same buffer for both input and output, allocating and deallocating
07611     the mdl on behalf of the caller, and waiting for the io to complete.
07612 
07613     This routine also works around the rare cases in which APC's are disabled.
07614     Since IoBuildDeviceIoControl() used APC's to signal completion, this had
07615     led to a number of difficult-to-detect hangs, where the irp was completed,
07616     but the event passed to IoBuild..() was still being waited upon by the
07617     caller.
07618 
07619 Arguments:
07620 
07621     IoControlCode - the IOCTL to send
07622 
07623     TargetDeviceObject - the device object that should handle the ioctl
07624 
07625     Buffer - the input and output buffer, or NULL if no input/output
07626 
07627     InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
07628 
07629     OutputBufferLength - the number of bytes to be filled in upon success
07630 
07631     InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
07632 
07633     IoStatus - the status block that contains the results of the operation
07634 
07635 Return Value:
07636 
07637 --*/
07638 VOID
07639 ClassSendDeviceIoControlSynchronous(
07640     IN ULONG IoControlCode,
07641     IN PDEVICE_OBJECT TargetDeviceObject,
07642     IN OUT PVOID Buffer OPTIONAL,
07643     IN ULONG InputBufferLength,
07644     IN ULONG OutputBufferLength,
07645     IN BOOLEAN InternalDeviceIoControl,
07646     OUT PIO_STATUS_BLOCK IoStatus
07647     )
07648 {
07649     PIRP irp;
07650     PIO_STACK_LOCATION irpSp;
07651     ULONG method;
07652 
07653     PAGED_CODE();
07654 
07655     irp = NULL;
07656     method = IoControlCode & 3;
07657 
07658 
07659     #if DBG // Begin Argument Checking (nop in fre version)
07660 
07661         ASSERT(ARGUMENT_PRESENT(IoStatus));
07662 
07663         if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
07664             ASSERT(ARGUMENT_PRESENT(Buffer));
07665         }
07666         else {
07667             ASSERT(!ARGUMENT_PRESENT(Buffer));
07668         }
07669     #endif
07670 
07671     //
07672     // Begin by allocating the IRP for this request.  Do not charge quota to
07673     // the current process for this IRP.
07674     //
07675 
07676     irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
07677     if (!irp) {
07678         (*IoStatus).Information = 0;
07679         (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
07680         return;
07681     }
07682 
07683     //
07684     // Get a pointer to the stack location of the first driver which will be
07685     // invoked.  This is where the function codes and the parameters are set.
07686     //
07687 
07688     irpSp = IoGetNextIrpStackLocation(irp);
07689 
07690     //
07691     // Set the major function code based on the type of device I/O control
07692     // function the caller has specified.
07693     //
07694 
07695     if (InternalDeviceIoControl) {
07696         irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
07697     } else {
07698         irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
07699     }
07700 
07701     //
07702     // Copy the caller's parameters to the service-specific portion of the
07703     // IRP for those parameters that are the same for all four methods.
07704     //
07705 
07706     irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
07707     irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
07708     irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
07709 
07710     //
07711     // Get the method bits from the I/O control code to determine how the
07712     // buffers are to be passed to the driver.
07713     //
07714 
07715     switch (method) {
07716         // case 0
07717         case METHOD_BUFFERED: {
07718             if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
07719 
07720                 irp->AssociatedIrp.SystemBuffer =
07721                     ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
07722                                           max(InputBufferLength, OutputBufferLength),
07723                                           CLASS_TAG_DEVICE_CONTROL
07724                                           );
07725 
07726                 if (irp->AssociatedIrp.SystemBuffer == NULL) {
07727                     IoFreeIrp(irp);
07728                     (*IoStatus).Information = 0;
07729                     (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
07730                     return;
07731                 }
07732 
07733                 if (InputBufferLength != 0) {
07734                     RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,
07735                                   Buffer,
07736                                   InputBufferLength);
07737                 }
07738             } // end of buffering
07739 
07740             irp->UserBuffer = Buffer;
07741             break;
07742         }
07743 
07744         // case 1, case 2
07745         case METHOD_IN_DIRECT:
07746         case METHOD_OUT_DIRECT: {
07747 
07748 
07749             if (InputBufferLength != 0) {
07750                 irp->AssociatedIrp.SystemBuffer = Buffer;
07751             }
07752 
07753             if (OutputBufferLength != 0) {
07754 
07755                 irp->MdlAddress = IoAllocateMdl(Buffer,
07756                                                 OutputBufferLength,
07757                                                 FALSE, FALSE,
07758                                                 (PIRP) NULL);
07759 
07760                 if (irp->MdlAddress == NULL) {
07761                     IoFreeIrp(irp);
07762                     (*IoStatus).Information = 0;
07763                     (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
07764                     return;
07765                 }
07766 
07767                 if (method == METHOD_IN_DIRECT) {
07768                     MmProbeAndLockPages(irp->MdlAddress,
07769                                         KernelMode,
07770                                         IoReadAccess);
07771                 } else if (method == METHOD_OUT_DIRECT) {
07772                     MmProbeAndLockPages(irp->MdlAddress,
07773                                         KernelMode,
07774                                         IoWriteAccess);
07775                 } else {
07776                     ASSERT(!"If other methods reach here, code is out of date");
07777                 }
07778             }
07779             break;
07780         }
07781 
07782         // case 3
07783         case METHOD_NEITHER: {
07784 
07785             ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
07786             IoStatus->Information = 0;
07787             IoStatus->Status = STATUS_NOT_SUPPORTED;
07788             return;
07789             break;
07790         }
07791     } // end of switch(method)
07792 
07793     irp->Tail.Overlay.Thread = PsGetCurrentThread();
07794 
07795     //
07796     // send the irp synchronously
07797     //
07798 
07799     ClassSendIrpSynchronous(TargetDeviceObject, irp);
07800 
07801     //
07802     // copy the iostatus block for the caller
07803     //
07804 
07805     *IoStatus = irp->IoStatus;
07806 
07807     //
07808     // free any allocated resources
07809     //
07810 
07811     switch (method) {
07812         case METHOD_BUFFERED: {
07813 
07814             ASSERT(irp->UserBuffer == Buffer);
07815 
07816             //
07817             // first copy the buffered result, if any
07818             // Note that there are no security implications in
07819             // not checking for success since only drivers can
07820             // call into this routine anyways...
07821             //
07822 
07823             if (OutputBufferLength != 0) {
07824                 RtlCopyMemory(Buffer, // irp->UserBuffer
07825                               irp->AssociatedIrp.SystemBuffer,
07826                               OutputBufferLength
07827                               );
07828             }
07829 
07830             //
07831             // then free the memory allocated to buffer the io
07832             //
07833 
07834             if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
07835                 ExFreePool(irp->AssociatedIrp.SystemBuffer);
07836                 irp->AssociatedIrp.SystemBuffer = NULL;
07837             }
07838             break;
07839         }
07840 
07841         case METHOD_IN_DIRECT:
07842         case METHOD_OUT_DIRECT: {
07843 
07844             //
07845             // we alloc a mdl if there is an output buffer specified
07846             // free it here after unlocking the pages
07847             //
07848 
07849             if (OutputBufferLength != 0) {
07850                 ASSERT(irp->MdlAddress != NULL);
07851                 MmUnlockPages(irp->MdlAddress);
07852                 IoFreeMdl(irp->MdlAddress);
07853                 irp->MdlAddress = (PMDL) NULL;
07854             }
07855             break;
07856         }
07857 
07858         case METHOD_NEITHER: {
07859             ASSERT(!"Code is out of date");
07860             break;
07861         }
07862     }
07863 
07864     //
07865     // we always have allocated an irp.  free it here.
07866     //
07867 
07868     IoFreeIrp(irp);
07869     irp = (PIRP) NULL;
07870 
07871     //
07872     // return the io status block's status to the caller
07873     //
07874 
07875     return;
07876 } // end ClassSendDeviceIoControlSynchronous()
07877 
07878 /*++////////////////////////////////////////////////////////////////////////////
07879 
07880 ClassForwardIrpSynchronous()
07881 
07882 Routine Description:
07883 
07884     Forwards a given irp to the next lower device object.
07885 
07886 Arguments:
07887 
07888     CommonExtension - the common class extension
07889 
07890     Irp - the request to forward down the stack
07891 
07892 Return Value:
07893 
07894 --*/
07895 NTSTATUS
07896 ClassForwardIrpSynchronous(
07897     IN PCOMMON_DEVICE_EXTENSION CommonExtension,
07898     IN PIRP Irp
07899     )
07900 {
07901     IoCopyCurrentIrpStackLocationToNext(Irp);
07902     return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
07903 } // end ClassForwardIrpSynchronous()
07904 
07905 /*++////////////////////////////////////////////////////////////////////////////
07906 
07907 ClassSendIrpSynchronous()
07908 
07909 Routine Description:
07910 
07911     This routine sends the given irp to the given device object, and waits for
07912     it to complete.  On debug versions, will print out a debug message and
07913     optionally assert for "lost" irps based upon classpnp's globals
07914 
07915 Arguments:
07916 
07917     TargetDeviceObject - the device object to handle this irp
07918 
07919     Irp - the request to be sent
07920 
07921 Return Value:
07922 
07923 --*/
07924 NTSTATUS
07925 ClassSendIrpSynchronous(
07926     IN PDEVICE_OBJECT TargetDeviceObject,
07927     IN PIRP Irp
07928     )
07929 {
07930     KEVENT event;
07931     NTSTATUS status;
07932 
07933     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
07934     ASSERT(TargetDeviceObject != NULL);
07935     ASSERT(Irp != NULL);
07936     ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
07937 
07938     //
07939     // ISSUE-2000/02/20-henrygab   What if APCs are disabled?
07940     //    May need to enter critical section before IoCallDriver()
07941     //    until the event is hit?
07942     //
07943 
07944     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
07945     IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
07946                            TRUE, TRUE, TRUE);
07947 
07948     status = IoCallDriver(TargetDeviceObject, Irp);
07949 
07950     if (status == STATUS_PENDING) {
07951 
07952         #if DBG
07953             LARGE_INTEGER timeout;
07954 
07955             timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
07956                                           ClasspnpGlobals.SecondsToWaitForIrps);
07957 
07958             do {
07959                 status = KeWaitForSingleObject(&event,
07960                                                Executive,
07961                                                KernelMode,
07962                                                FALSE,
07963                                                &timeout);
07964 
07965 
07966                 if (status == STATUS_TIMEOUT) {
07967 
07968                     //
07969                     // This DebugPrint should almost always be investigated by the
07970                     // party who sent the irp and/or the current owner of the irp.
07971                     // Synchronous Irps should not take this long (currently 30
07972                     // seconds) without good reason.  This points to a potentially
07973                     // serious problem in the underlying device stack.
07974                     //
07975 
07976                     DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
07977                                 "complete within %x seconds\n",
07978                                 TargetDeviceObject, Irp,
07979                                 ClasspnpGlobals.SecondsToWaitForIrps
07980                                 ));
07981 
07982                     if (ClasspnpGlobals.BreakOnLostIrps != 0) {
07983                         ASSERT(!" - Irp failed to complete within 30 seconds - ");
07984                     }
07985                 }
07986 
07987 
07988             } while (status==STATUS_TIMEOUT);
07989         #else
07990             KeWaitForSingleObject(&event,
07991                                   Executive,
07992                                   KernelMode,
07993                                   FALSE,
07994                                   NULL);
07995         #endif
07996 
07997         status = Irp->IoStatus.Status;
07998     }
07999 
08000     return status;
08001 } // end ClassSendIrpSynchronous()
08002 
08003 /*++////////////////////////////////////////////////////////////////////////////
08004 
08005 ClassGetVpb()
08006 
08007 Routine Description:
08008 
08009     This routine returns the current VPB (Volume Parameter Block) for the
08010     given device object.
08011     The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
08012     of DEVICE_OBJECT; hence this exported function.
08013 
08014 Arguments:
08015 
08016     DeviceObject - the device to get the VPB for
08017 
08018 Return Value:
08019 
08020     the VPB, or NULL if none.
08021 
08022 --*/
08023 PVPB
08024 ClassGetVpb(
08025     IN PDEVICE_OBJECT DeviceObject
08026     )
08027 {
08028     return DeviceObject->Vpb;
08029 } // end ClassGetVpb()
08030 
08031 /*++
08032 
08033     ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
08034 
08035 --*/
08036 NTSTATUS
08037 ClasspAllocateReleaseRequest(
08038     IN PDEVICE_OBJECT Fdo
08039     )
08040 {
08041     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
08042     PIO_STACK_LOCATION irpStack;
08043 
08044     KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
08045 
08046     fdoExtension->ReleaseQueueNeeded = FALSE;
08047     fdoExtension->ReleaseQueueInProgress = FALSE;
08048     fdoExtension->ReleaseQueueIrpFromPool = FALSE;
08049 
08050     //
08051     // The class driver is responsible for allocating a properly sized irp,
08052     // or ClassReleaseQueue will attempt to do it on the first error.
08053     //
08054 
08055     fdoExtension->ReleaseQueueIrp = NULL;
08056 
08057     //
08058     // Write length to SRB.
08059     //
08060 
08061     fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
08062 
08063     return STATUS_SUCCESS;
08064 } // end ClasspAllocateReleaseRequest()
08065 
08066 /*++
08067 
08068     ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
08069 
08070 --*/
08071 VOID
08072 ClasspFreeReleaseRequest(
08073     IN PDEVICE_OBJECT Fdo
08074     )
08075 {
08076     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
08077     //KIRQL oldIrql;
08078 
08079     ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
08080 
08081     //
08082     // free anything the driver allocated
08083     //
08084 
08085     if (fdoExtension->ReleaseQueueIrp) {
08086         if (fdoExtension->ReleaseQueueIrpFromPool) {
08087             ExFreePool(fdoExtension->ReleaseQueueIrp);
08088         } else {
08089             IoFreeIrp(fdoExtension->ReleaseQueueIrp);
08090         }
08091         fdoExtension->ReleaseQueueIrp = NULL;
08092     }
08093 
08094     //
08095     // free anything that we allocated
08096     //
08097 
08098     if ((fdoExtension->PrivateFdoData) &&
08099         (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
08100 
08101         ExFreePool(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
08102         fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
08103         fdoExtension->PrivateFdoData->ReleaseQueueIrp = NULL;
08104     }
08105 
08106     return;
08107 } // end ClasspFreeReleaseRequest()
08108 
08109 /*++////////////////////////////////////////////////////////////////////////////
08110 
08111 ClassReleaseQueue()
08112 
08113 Routine Description:
08114 
08115     This routine issues an internal device control command
08116     to the port driver to release a frozen queue. The call
08117     is issued asynchronously as ClassReleaseQueue will be invoked
08118     from the IO completion DPC (and will have no context to
08119     wait for a synchronous call to complete).
08120 
08121     This routine must be called with the remove lock held.
08122 
08123 Arguments:
08124 
08125     Fdo - The functional device object for the device with the frozen queue.
08126 
08127 Return Value:
08128 
08129     None.
08130 
08131 --*/
08132 VOID
08133 ClassReleaseQueue(
08134     IN PDEVICE_OBJECT Fdo
08135     )
08136 {
08137     ClasspReleaseQueue(Fdo, NULL);
08138     return;
08139 } // end ClassReleaseQueue()
08140 
08141 /*++////////////////////////////////////////////////////////////////////////////
08142 
08143 ClasspAllocateReleaseQueueIrp()
08144 
08145 Routine Description:
08146 
08147     This routine allocates the release queue irp held in classpnp's private
08148     extension.  This was added to allow no-memory conditions to be more
08149     survivable.
08150 
08151 Return Value:
08152 
08153     NT_SUCCESS value.
08154 
08155 Notes:
08156 
08157     Does not grab the spinlock.  Should only be called from StartDevice()
08158     routine.  May be called elsewhere for poorly-behaved drivers that cause
08159     the queue to lockup before the device is started.  This should *never*
08160     occur, since it's illegal to send a request to a non-started PDO.  This
08161     condition is checked for in ClasspReleaseQueue().
08162 
08163 --*/
08164 NTSTATUS
08165 ClasspAllocateReleaseQueueIrp(
08166     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
08167     )
08168 {
08169     KIRQL oldIrql;
08170     UCHAR lowerStackSize;
08171 
08172     //
08173     // do an initial check w/o the spinlock
08174     //
08175 
08176     if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
08177         return STATUS_SUCCESS;
08178     }
08179 
08180 
08181     lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
08182 
08183     //
08184     // don't allocate one if one is in progress!  this means whoever called
08185     // this routine didn't check if one was in progress.
08186     //
08187 
08188     ASSERT(!(FdoExtension->ReleaseQueueInProgress));
08189 
08190     FdoExtension->PrivateFdoData->ReleaseQueueIrp =
08191         ExAllocatePoolWithTag(NonPagedPool,
08192                               IoSizeOfIrp(lowerStackSize),
08193                               CLASS_TAG_RELEASE_QUEUE
08194                               );
08195 
08196     if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
08197         DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
08198                     "release queue irp\n"));
08199         return STATUS_INSUFFICIENT_RESOURCES;
08200     }
08201     IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
08202                     IoSizeOfIrp(lowerStackSize),
08203                     lowerStackSize);
08204     FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
08205 
08206     return STATUS_SUCCESS;
08207 }
08208 
08209 
08210 /*++////////////////////////////////////////////////////////////////////////////
08211 
08212 ClasspReleaseQueue()
08213 
08214 Routine Description:
08215 
08216     This routine issues an internal device control command
08217     to the port driver to release a frozen queue. The call
08218     is issued asynchronously as ClassReleaseQueue will be invoked
08219     from the IO completion DPC (and will have no context to
08220     wait for a synchronous call to complete).
08221 
08222     This routine must be called with the remove lock held.
08223 
08224 Arguments:
08225 
08226     Fdo - The functional device object for the device with the frozen queue.
08227 
08228     ReleaseQueueIrp - If this irp is supplied then the test to determine whether
08229                       a release queue request is in progress will be ignored.
08230                       The irp provided must be the IRP originally allocated
08231                       for release queue requests (so this parameter can only
08232                       really be provided by the release queue completion
08233                       routine.)
08234 
08235 Return Value:
08236 
08237     None.
08238 
08239 --*/
08240 VOID
08241 ClasspReleaseQueue(
08242     IN PDEVICE_OBJECT Fdo,
08243     IN PIRP ReleaseQueueIrp OPTIONAL
08244     )
08245 {
08246     PIO_STACK_LOCATION irpStack;
08247     PIRP irp;
08248     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
08249     PDEVICE_OBJECT lowerDevice;
08250     PSCSI_REQUEST_BLOCK srb;
08251     KIRQL currentIrql;
08252 
08253     lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
08254 
08255     //
08256     // we raise irql seperately so we're not swapped out or suspended
08257     // while holding the release queue irp in this routine.  this lets
08258     // us release the spin lock before lowering irql.
08259     //
08260 
08261     KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
08262 
08263     KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
08264 
08265     //
08266     // make sure that if they passed us an irp, it matches our allocated irp.
08267     //
08268 
08269     ASSERT((ReleaseQueueIrp == NULL) ||
08270            (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
08271 
08272     //
08273     // ASSERT that we've already allocated this. (should not occur)
08274     // try to allocate it anyways, then finally bugcheck if
08275     // there's still no memory...
08276     //
08277 
08278     ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
08279     if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
08280         ClasspAllocateReleaseQueueIrp(fdoExtension);
08281     }
08282     if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
08283         KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
08284     }
08285 
08286     if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
08287 
08288         //
08289         // Someone is already using the irp - just set the flag to indicate that
08290         // we need to release the queue again.
08291         //
08292 
08293         fdoExtension->ReleaseQueueNeeded = TRUE;
08294         KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
08295         KeLowerIrql(currentIrql);
08296         return;
08297 
08298     }
08299 
08300     //
08301     // Mark that there is a release queue in progress and drop the spinlock.
08302     //
08303 
08304     fdoExtension->ReleaseQueueInProgress = TRUE;
08305     if (ReleaseQueueIrp) {
08306         irp = ReleaseQueueIrp;
08307     } else {
08308         irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
08309     }
08310     srb = &(fdoExtension->ReleaseQueueSrb);
08311 
08312     KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
08313 
08314     ASSERT(irp != NULL);
08315 
08316     irpStack = IoGetNextIrpStackLocation(irp);
08317 
08318     irpStack->MajorFunction = IRP_MJ_SCSI;
08319 
08320     srb->OriginalRequest = irp;
08321 
08322     //
08323     // Store the SRB address in next stack for port driver.
08324     //
08325 
08326     irpStack->Parameters.Scsi.Srb = srb;
08327 
08328     //
08329     // If this device is removable then flush the queue.  This will also
08330     // release it.
08331     //
08332 
08333     if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
08334        srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
08335     }
08336     else {
08337        srb->Function = SRB_FUNCTION_RELEASE_QUEUE;
08338     }
08339 
08340     ClassAcquireRemoveLock(Fdo, irp);
08341 
08342     IoSetCompletionRoutine(irp,
08343                            ClassReleaseQueueCompletion,
08344                            Fdo,
08345                            TRUE,
08346                            TRUE,
08347                            TRUE);
08348 
08349     IoCallDriver(lowerDevice, irp);
08350 
08351     KeLowerIrql(currentIrql);
08352 
08353     return;
08354 
08355 } // end ClassReleaseQueue()
08356 
08357 /*++////////////////////////////////////////////////////////////////////////////
08358 
08359 ClassReleaseQueueCompletion()
08360 
08361 Routine Description:
08362 
08363     This routine is called when an asynchronous I/O request
08364     which was issused by the class driver completes.  Examples of such requests
08365     are release queue or START UNIT. This routine releases the queue if
08366     necessary.  It then frees the context and the IRP.
08367 
08368 Arguments:
08369 
08370     DeviceObject - The device object for the logical unit; however since this
08371         is the top stack location the value is NULL.
08372 
08373     Irp - Supplies a pointer to the Irp to be processed.
08374 
08375     Context - Supplies the context to be used to process this request.
08376 
08377 Return Value:
08378 
08379     None.
08380 
08381 --*/
08382 NTSTATUS
08383 ClassReleaseQueueCompletion(
08384     PDEVICE_OBJECT DeviceObject,
08385     PIRP Irp,
08386     PVOID Context
08387     )
08388 {
08389     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
08390     KIRQL oldIrql;
08391 
08392     BOOLEAN releaseQueueNeeded;
08393 
08394     DeviceObject = Context;
08395 
08396     fdoExtension = DeviceObject->DeviceExtension;
08397 
08398     ClassReleaseRemoveLock(DeviceObject, Irp);
08399 
08400     //
08401     // Grab the spinlock and clear the release queue in progress flag so others
08402     // can run.  Save (and clear) the state of the release queue needed flag
08403     // so that we can issue a new release queue outside the spinlock.
08404     //
08405 
08406     KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
08407 
08408     releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
08409 
08410     fdoExtension->ReleaseQueueNeeded = FALSE;
08411     fdoExtension->ReleaseQueueInProgress = FALSE;
08412 
08413     KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
08414 
08415     //
08416     // If we need a release queue then issue one now.  Another processor may
08417     // have already started one in which case we'll try to issue this one after
08418     // it is done - but we should never recurse more than one deep.
08419     //
08420 
08421     if(releaseQueueNeeded) {
08422         ClasspReleaseQueue(DeviceObject, Irp);
08423     }
08424 
08425     //
08426     // Indicate the I/O system should stop processing the Irp completion.
08427     //
08428 
08429     return STATUS_MORE_PROCESSING_REQUIRED;
08430 
08431 } // ClassAsynchronousCompletion()
08432 
08433 /*++////////////////////////////////////////////////////////////////////////////
08434 
08435 ClassAcquireChildLock()
08436 
08437 Routine Description:
08438 
08439     This routine acquires the lock protecting children PDOs.  It may be
08440     acquired recursively by the same thread, but must be release by the
08441     thread once for each acquisition.
08442 
08443 Arguments:
08444 
08445     FdoExtension - the device whose child list is protected.
08446 
08447 Return Value:
08448 
08449     None
08450 
08451 --*/
08452 VOID
08453 ClassAcquireChildLock(
08454     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
08455     )
08456 {
08457     PAGED_CODE();
08458 
08459     if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
08460         KeWaitForSingleObject(&FdoExtension->ChildLock,
08461                               Executive, KernelMode,
08462                               FALSE, NULL);
08463 
08464         ASSERT(FdoExtension->ChildLockOwner == NULL);
08465         ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
08466 
08467         FdoExtension->ChildLockOwner = KeGetCurrentThread();
08468     } else {
08469         ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
08470     }
08471 
08472     FdoExtension->ChildLockAcquisitionCount++;
08473     return;
08474 }
08475 
08476 /*++////////////////////////////////////////////////////////////////////////////
08477 
08478 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
08479 
08480 Routine Description:
08481 
08482     This routine releases the lock protecting children PDOs.  It must be
08483     called once for each time ClassAcquireChildLock was called.
08484 
08485 Arguments:
08486 
08487     FdoExtension - the device whose child list is protected
08488 
08489 Return Value:
08490 
08491     None.
08492 
08493 --*/
08494 VOID
08495 ClassReleaseChildLock(
08496     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
08497     )
08498 {
08499     ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
08500     ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
08501 
08502     FdoExtension->ChildLockAcquisitionCount -= 1;
08503 
08504     if(FdoExtension->ChildLockAcquisitionCount == 0) {
08505         FdoExtension->ChildLockOwner = NULL;
08506         KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
08507     }
08508 
08509     return;
08510 } // end ClassReleaseChildLock(
08511 
08512 /*++////////////////////////////////////////////////////////////////////////////
08513 
08514 ClassAddChild()
08515 
08516 Routine Description:
08517 
08518     This routine will insert a new child into the head of the child list.
08519 
08520 Arguments:
08521 
08522     Parent - the child's parent (contains the head of the list)
08523     Child - the child to be inserted.
08524     AcquireLock - whether the child lock should be acquired (TRUE) or whether
08525                   it's already been acquired by or on behalf of the caller
08526                   (FALSE).
08527 
08528 Return Value:
08529 
08530     None.
08531 
08532 --*/
08533 VOID
08534 ClassAddChild(
08535     IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
08536     IN PPHYSICAL_DEVICE_EXTENSION Child,
08537     IN BOOLEAN AcquireLock
08538     )
08539 {
08540     if(AcquireLock) {
08541         ClassAcquireChildLock(Parent);
08542     }
08543 
08544     #if DBG
08545         //
08546         // Make sure this child's not already in the list.
08547         //
08548         {
08549             PPHYSICAL_DEVICE_EXTENSION testChild;
08550 
08551             for (testChild = Parent->CommonExtension.ChildList;
08552                  testChild != NULL;
08553                  testChild = testChild->CommonExtension.ChildList) {
08554 
08555                 ASSERT(testChild != Child);
08556             }
08557         }
08558     #endif
08559 
08560     Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
08561     Parent->CommonExtension.ChildList = Child;
08562 
08563     if(AcquireLock) {
08564         ClassReleaseChildLock(Parent);
08565     }
08566     return;
08567 } // end ClassAddChild()
08568 
08569 /*++////////////////////////////////////////////////////////////////////////////
08570 
08571 ClassRemoveChild()
08572 
08573 Routine Description:
08574 
08575     This routine will remove a child from the child list.
08576 
08577 Arguments:
08578 
08579     Parent - the parent to be removed from.
08580 
08581     Child - the child to be removed or NULL if the first child should be
08582             removed.
08583 
08584     AcquireLock - whether the child lock should be acquired (TRUE) or whether
08585                   it's already been acquired by or on behalf of the caller
08586                   (FALSE).
08587 
08588 Return Value:
08589 
08590     A pointer to the child which was removed or NULL if no such child could
08591     be found in the list (or if Child was NULL but the list is empty).
08592 
08593 --*/
08594 PPHYSICAL_DEVICE_EXTENSION
08595 ClassRemoveChild(
08596     IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
08597     IN PPHYSICAL_DEVICE_EXTENSION Child,
08598     IN BOOLEAN AcquireLock
08599     )
08600 {
08601     if(AcquireLock) {
08602         ClassAcquireChildLock(Parent);
08603     }
08604 
08605     TRY {
08606         PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
08607 
08608         //
08609         // If the list is empty then bail out now.
08610         //
08611 
08612         if(Parent->CommonExtension.ChildList == NULL) {
08613             Child = NULL;
08614             LEAVE;
08615         }
08616 
08617         //
08618         // If the caller specified a child then find the child object before
08619         // it.  If none was specified then the FDO is the child object before
08620         // the one we want to remove.
08621         //
08622 
08623         if(Child != NULL) {
08624 
08625             //
08626             // Scan through the child list to find the entry which points to
08627             // this one.
08628             //
08629 
08630             do {
08631                 ASSERT(previousChild != &Child->CommonExtension);
08632 
08633                 if(previousChild->ChildList == Child) {
08634                     break;
08635                 }
08636 
08637                 previousChild = &previousChild->ChildList->CommonExtension;
08638             } while(previousChild != NULL);
08639 
08640             if(previousChild == NULL) {
08641                 Child = NULL;
08642                 LEAVE;
08643             }
08644         }
08645 
08646         //
08647         // Save the next child away then unlink it from the list.
08648         //
08649 
08650         Child = previousChild->ChildList;
08651         previousChild->ChildList = Child->CommonExtension.ChildList;
08652         Child->CommonExtension.ChildList = NULL;
08653 
08654     } FINALLY {
08655         if(AcquireLock) {
08656             ClassReleaseChildLock(Parent);
08657         }
08658     }
08659     return Child;
08660 } // end ClassRemoveChild()
08661 
08662 
08663 /*++
08664 
08665     ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
08666 
08667 --*/
08668 VOID
08669 ClasspRetryRequestDpc(
08670     IN PKDPC Dpc,
08671     IN PDEVICE_OBJECT DeviceObject,
08672     IN PVOID Arg1,
08673     IN PVOID Arg2
08674     )
08675 {
08676     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
08677     PCOMMON_DEVICE_EXTENSION commonExtension;
08678     PCLASS_PRIVATE_FDO_DATA fdoData;
08679     PCLASS_RETRY_INFO retryList;
08680     KIRQL irql;
08681 
08682 
08683     commonExtension = DeviceObject->DeviceExtension;
08684     ASSERT(commonExtension->IsFdo);
08685     fdoExtension = DeviceObject->DeviceExtension;
08686     fdoData = fdoExtension->PrivateFdoData;
08687 
08688 
08689     KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
08690     {
08691         LARGE_INTEGER now;
08692         KeQueryTickCount(&now);
08693 
08694         //
08695         // if CurrentTick is less than now
08696         //      fire another DPC
08697         // else
08698         //      retry entire list
08699         // endif
08700         //
08701 
08702         if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
08703 
08704             ClasspRetryDpcTimer(fdoData);
08705             retryList = NULL;
08706 
08707         } else {
08708 
08709             retryList = fdoData->Retry.ListHead;
08710             fdoData->Retry.ListHead = NULL;
08711             fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
08712             fdoData->Retry.Tick.QuadPart  = (LONGLONG)0;
08713 
08714         }
08715     }
08716     KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
08717 
08718     while (retryList != NULL) {
08719 
08720         PIRP irp;
08721 
08722         irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
08723         DebugPrint((ClassDebugDelayedRetry, "ClassRetry:  -- %p\n", irp));
08724         retryList = retryList->Next;
08725         #if DBG
08726             irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
08727             irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
08728             irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
08729             irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
08730         #endif
08731 
08732         IoCallDriver(commonExtension->LowerDeviceObject, irp);
08733 
08734     }
08735     return;
08736 
08737 } // end ClasspRetryRequestDpc()
08738 
08739 /*++
08740 
08741     ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
08742 
08743 --*/
08744 VOID
08745 ClassRetryRequest(
08746     IN PDEVICE_OBJECT SelfDeviceObject,
08747     IN PIRP           Irp,
08748     IN LARGE_INTEGER  TimeDelta100ns // in 100ns units
08749     )
08750 {
08751     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
08752     PCLASS_PRIVATE_FDO_DATA fdoData;
08753     PCLASS_RETRY_INFO  retryInfo;
08754     PCLASS_RETRY_INFO *previousNext;
08755     LARGE_INTEGER      delta;
08756     KIRQL irql;
08757 
08758     //
08759     // this checks we aren't destroying irps
08760     //
08761     ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
08762 
08763     fdoExtension = SelfDeviceObject->DeviceExtension;
08764     fdoData = fdoExtension->PrivateFdoData;
08765 
08766     if (!fdoExtension->CommonExtension.IsFdo) {
08767 
08768         //
08769         // this debug print/assertion should ALWAYS be investigated.
08770         // ClassRetryRequest can currently only be used by FDO's
08771         //
08772 
08773         DebugPrint((ClassDebugError, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
08774         ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
08775         return;
08776 
08777     }
08778 
08779     if (TimeDelta100ns.QuadPart < 0) {
08780         ASSERT(!"ClassRetryRequest - must use positive delay");
08781         TimeDelta100ns.QuadPart *= -1;
08782     }
08783 
08784     //
08785     // prepare what we can out of the loop
08786     //
08787 
08788     retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
08789     RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
08790 
08791     delta.QuadPart = (TimeDelta100ns.QuadPart / fdoData->Retry.Granularity);
08792     if (TimeDelta100ns.QuadPart % fdoData->Retry.Granularity) {
08793         delta.QuadPart ++; // round up to next tick
08794     }
08795     if (delta.QuadPart == (LONGLONG)0) {
08796         delta.QuadPart = MINIMUM_RETRY_UNITS;
08797     }
08798 
08799     //
08800     // now determine if we should fire another DPC or not
08801     //
08802 
08803     KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
08804 
08805     //
08806     // always add request to the list
08807     //
08808 
08809     retryInfo->Next = fdoData->Retry.ListHead;
08810     fdoData->Retry.ListHead = retryInfo;
08811 
08812     if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
08813 
08814         DebugPrint((ClassDebugDelayedRetry, "ClassRetry: +++ %p\n", Irp));
08815 
08816         //
08817         // must be exactly one item on list
08818         //
08819 
08820         ASSERT(fdoData->Retry.ListHead       != NULL);
08821         ASSERT(fdoData->Retry.ListHead->Next == NULL);
08822 
08823         //
08824         // if currentDelta is zero, always fire a DPC
08825         //
08826 
08827         KeQueryTickCount(&fdoData->Retry.Tick);
08828         fdoData->Retry.Tick.QuadPart  += delta.QuadPart;
08829         fdoData->Retry.Delta.QuadPart  = delta.QuadPart;
08830         ClasspRetryDpcTimer(fdoData);
08831 
08832     } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
08833 
08834         //
08835         // if delta is greater than the list's current delta,
08836         // increase the DPC handling time by difference
08837         // and update the delta to new larger value
08838         // allow the DPC to re-fire itself if needed
08839         //
08840 
08841         DebugPrint((ClassDebugDelayedRetry, "ClassRetry:  ++ %p\n", Irp));
08842 
08843         //
08844         // must be at least two items on list
08845         //
08846 
08847         ASSERT(fdoData->Retry.ListHead       != NULL);
08848         ASSERT(fdoData->Retry.ListHead->Next != NULL);
08849 
08850         fdoData->Retry.Tick.QuadPart  -= fdoData->Retry.Delta.QuadPart;
08851         fdoData->Retry.Tick.QuadPart  += delta.QuadPart;
08852 
08853         fdoData->Retry.Delta.QuadPart  = delta.QuadPart;
08854 
08855     } else {
08856 
08857         //
08858         // just inserting it on the list was enough
08859         //
08860 
08861         DebugPrint((ClassDebugDelayedRetry, "ClassRetry:  ++ %p\n", Irp));
08862 
08863     }
08864 
08865 
08866     KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
08867 
08868 
08869 } // end ClassRetryRequest()
08870 
08871 /*++
08872 
08873     ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
08874 
08875 --*/
08876 VOID
08877 ClasspRetryDpcTimer(
08878     IN PCLASS_PRIVATE_FDO_DATA FdoData
08879     )
08880 {
08881     LARGE_INTEGER fire;
08882 
08883     ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
08884     ASSERT(FdoData->Retry.ListHead      != NULL);  // never fire an empty list
08885 
08886     //
08887     // fire == (CurrentTick - now) * (100ns per tick)
08888     //
08889     // NOTE: Overflow is nearly impossible and is ignored here
08890     //
08891 
08892     KeQueryTickCount(&fire);
08893     fire.QuadPart =  FdoData->Retry.Tick.QuadPart - fire.QuadPart;
08894     fire.QuadPart *= FdoData->Retry.Granularity;
08895 
08896     //
08897     // fire is now multiples of 100ns until should fire the timer.
08898     // if timer should already have expired, or would fire too quickly,
08899     // fire it in some arbitrary number of ticks to prevent infinitely
08900     // recursing.
08901     //
08902 
08903     if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
08904         fire.QuadPart = MINIMUM_RETRY_UNITS;
08905     }
08906 
08907     DebugPrint((ClassDebugDelayedRetry,
08908                 "ClassRetry: ======= %I64x ticks\n",
08909                 fire.QuadPart));
08910 
08911     //
08912     // must use negative to specify relative time to fire
08913     //
08914 
08915     fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
08916 
08917     //
08918     // set the timer, since this is the first addition
08919     //
08920 
08921     KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
08922 
08923     return;
08924 } // end ClasspRetryDpcTimer()
08925 
08926 NTSTATUS
08927 ClasspInitializeHotplugInfo(
08928     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
08929     )
08930 {
08931     PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
08932     DEVICE_REMOVAL_POLICY deviceRemovalPolicy;
08933     NTSTATUS status;
08934     ULONG resultLength = 0;
08935     ULONG writeCacheOverride;
08936 
08937     PAGED_CODE();
08938 
08939     //
08940     // start with some default settings
08941     //
08942     RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
08943 
08944     //
08945     // set the size (aka version)
08946     //
08947 
08948     fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
08949 
08950     //
08951     // set if the device has removable media
08952     //
08953 
08954     if (FdoExtension->DeviceDescriptor->RemovableMedia) {
08955         fdoData->HotplugInfo.MediaRemovable = TRUE;
08956     } else {
08957         fdoData->HotplugInfo.MediaRemovable = FALSE;
08958     }
08959 
08960     //
08961     // this refers to devices which, for reasons not yet understood,
08962     // do not fail PREVENT_MEDIA_REMOVAL requests even though they
08963     // have no way to lock the media into the drive.  this allows
08964     // the filesystems to turn off delayed-write caching for these
08965     // devices as well.
08966     //
08967 
08968     if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
08969                   FDO_HACK_CANNOT_LOCK_MEDIA)) {
08970         fdoData->HotplugInfo.MediaHotplug = TRUE;
08971     } else {
08972         fdoData->HotplugInfo.MediaHotplug = FALSE;
08973     }
08974 
08975 
08976     //
08977     // Look into the registry to  see if the user has  chosen
08978     // to override the default setting for the removal policy
08979     //
08980 
08981     RtlZeroMemory(&deviceRemovalPolicy, sizeof(DEVICE_REMOVAL_POLICY));
08982 
08983     ClassGetDeviceParameter(FdoExtension,
08984                             CLASSP_REG_SUBKEY_NAME,
08985                             CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
08986                             (PULONG)&deviceRemovalPolicy);
08987 
08988     if (deviceRemovalPolicy == 0)
08989     {
08990         //
08991         // Query the default removal policy from the kernel
08992         //
08993 
08994         status = IoGetDeviceProperty(FdoExtension->LowerPdo,
08995                                      DevicePropertyRemovalPolicy,
08996                                      sizeof(DEVICE_REMOVAL_POLICY),
08997                                      (PVOID)&deviceRemovalPolicy,
08998                                      &resultLength);
08999         if (!NT_SUCCESS(status))
09000         {
09001             return status;
09002         }
09003 
09004         if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
09005         {
09006             return STATUS_UNSUCCESSFUL;
09007         }
09008     }
09009 
09010     //
09011     // use this info to set the DeviceHotplug setting
09012     // don't rely on DeviceCapabilities, since it can't properly
09013     // determine device relations, etc.  let the kernel figure this
09014     // stuff out instead.
09015     //
09016 
09017     if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
09018         fdoData->HotplugInfo.DeviceHotplug = TRUE;
09019     } else {
09020         fdoData->HotplugInfo.DeviceHotplug = FALSE;
09021     }
09022 
09023     //
09024     // this refers to the *filesystem* caching, but has to be included
09025     // here since it's a per-device setting.  this may change to be
09026     // stored by the system in the future.
09027     //
09028 
09029     writeCacheOverride = FALSE;
09030     ClassGetDeviceParameter(FdoExtension,
09031                             CLASSP_REG_SUBKEY_NAME,
09032                             CLASSP_REG_WRITE_CACHE_VALUE_NAME,
09033                             &writeCacheOverride);
09034 
09035     if (writeCacheOverride) {
09036         fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
09037     } else {
09038         fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
09039     }
09040 
09041     return STATUS_SUCCESS;
09042 }
09043 
09044 VOID
09045 ClasspScanForClassHacks(
09046     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
09047     IN ULONG_PTR Data
09048     )
09049 {
09050     PAGED_CODE();
09051 
09052     //
09053     // remove invalid flags and save
09054     //
09055 
09056     CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
09057     SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
09058     return;
09059 }
09060 
09061 VOID
09062 ClasspScanForSpecialInRegistry(
09063     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
09064     )
09065 {
09066     HANDLE             deviceParameterHandle; // device instance key
09067     HANDLE             classParameterHandle; // classpnp subkey
09068     OBJECT_ATTRIBUTES  objectAttributes;
09069     UNICODE_STRING     subkeyName;
09070     NTSTATUS           status;
09071 
09072     //
09073     // seeded in the ENUM tree by ClassInstaller
09074     //
09075     ULONG deviceHacks;
09076     RTL_QUERY_REGISTRY_TABLE queryTable[2]; // null terminated array
09077 
09078     PAGED_CODE();
09079 
09080     deviceParameterHandle = NULL;
09081     classParameterHandle = NULL;
09082     deviceHacks = 0;
09083 
09084     status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
09085                                      PLUGPLAY_REGKEY_DEVICE,
09086                                      KEY_WRITE,
09087                                      &deviceParameterHandle
09088                                      );
09089 
09090     if (!NT_SUCCESS(status)) {
09091         goto cleanupScanForSpecial;
09092     }
09093 
09094     RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
09095     InitializeObjectAttributes(&objectAttributes,
09096                                &subkeyName,
09097                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
09098                                deviceParameterHandle,
09099                                NULL
09100                                );
09101 
09102     status = ZwOpenKey( &classParameterHandle,
09103                         KEY_READ,
09104                         &objectAttributes
09105                         );
09106 
09107     if (!NT_SUCCESS(status)) {
09108         goto cleanupScanForSpecial;
09109     }
09110 
09111     //
09112     // Zero out the memory
09113     //
09114 
09115     RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
09116 
09117     //
09118     // Setup the structure to read
09119     //
09120 
09121     queryTable[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
09122     queryTable[0].Name          = CLASSP_REG_HACK_VALUE_NAME;
09123     queryTable[0].EntryContext  = &deviceHacks;
09124     queryTable[0].DefaultType   = REG_DWORD;
09125     queryTable[0].DefaultData   = &deviceHacks;
09126     queryTable[0].DefaultLength = 0;
09127 
09128     //
09129     // read values
09130     //
09131 
09132     status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
09133                                     (PWSTR)classParameterHandle,
09134                                     &queryTable[0],
09135                                     NULL,
09136                                     NULL
09137                                     );
09138     if (!NT_SUCCESS(status)) {
09139         goto cleanupScanForSpecial;
09140     }
09141 
09142     //
09143     // remove unknown values and save...
09144     //
09145 
09146     KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
09147                "Classpnp => ScanForSpecial: HackFlags %#08x\n",
09148                deviceHacks));
09149 
09150     CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
09151     SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
09152 
09153 
09154 cleanupScanForSpecial:
09155 
09156     if (deviceParameterHandle) {
09157         ZwClose(deviceParameterHandle);
09158     }
09159 
09160     if (classParameterHandle) {
09161         ZwClose(classParameterHandle);
09162     }
09163 
09164     //
09165     // we should modify the system hive to include another key for us to grab
09166     // settings from.  in this case:  Classpnp\HackFlags
09167     //
09168     // the use of a DWORD value for the HackFlags allows 32 hacks w/o
09169     // significant use of the registry, and also reduces OEM exposure.
09170     //
09171     // definition of bit flags:
09172     //   0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
09173     //                 cannot actually prevent removal.
09174     //   0x00000002 -- Device hard-hangs or times out for GESN requests.
09175     //   0xfffffffc -- Currently reserved, may be used later.
09176     //
09177 
09178     return;
09179 }
09180 
09181 
09182 
09183 

Generated on Fri May 25 2012 04:26:36 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.