VOID ClasspCleanupDisableMcn( IN PFILE_OBJECT_EXTENSION FsContext ) { PCOMMON_DEVICE_EXTENSION commonExtension = FsContext->DeviceObject->DeviceExtension; PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension; PAGED_CODE(); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspCleanupDisableMcn called for %p\n", FsContext->DeviceObject)); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspCleanupDisableMcn - FsContext %p is disabled " "%d times\n", FsContext, FsContext->McnDisableCount)); // // For each secure lock on this handle decrement the secured lock count // for the FDO. Keep track of the new value. // while(FsContext->McnDisableCount != 0) { FsContext->McnDisableCount--; ClassEnableMediaChangeDetection(fdoExtension); } return; }
/*++//////////////////////////////////////////////////////////////////////////// ClassCompleteRequest() Routine Description: This routine is a wrapper around (and should be used instead of) IoCompleteRequest. It is used primarily for debugging purposes. The routine will assert if the Irp being completed is still holding the release lock. Arguments: DeviceObject - the device object that was handling this request Irp - the irp to be completed by IoCompleteRequest PriorityBoost - the priority boost to pass to IoCompleteRequest Return Value: none --*/ VOID ClassCompleteRequest( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp, _In_ CCHAR PriorityBoost ) { #if DBG PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; PRTL_GENERIC_TABLE removeTrackingList = NULL; REMOVE_TRACKING_BLOCK searchDataBlock; PREMOVE_TRACKING_BLOCK foundTrackingBlock; KIRQL oldIrql; KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql); removeTrackingList = commonExtension->RemoveTrackingList; if (removeTrackingList != NULL) { searchDataBlock.Tag = Irp; foundTrackingBlock = RtlLookupElementGenericTable(removeTrackingList, &searchDataBlock); if(foundTrackingBlock != NULL) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: " "Irp %p completed while still holding the remove lock\n", Irp)); TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: " "Lock acquired in file %s on line %d\n", foundTrackingBlock->File, foundTrackingBlock->Line)); NT_ASSERT(FALSE); } } KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql); #endif UNREFERENCED_PARAMETER(DeviceObject); IoCompleteRequest(Irp, PriorityBoost); return; } // end ClassCompleteRequest()
/*++ ClasspIdleTimerDpc Routine Description: Timer dpc function. This function will be called once every IdleInterval. This will increment the IdleTicks and if it goes above 1 (i.e., disk is in idle state) then it will service an idle request. This function will increment IdleTimerTicks if the IdleTicks does not go above 1 (i.e., disk is not in idle state). When it reaches the starvation idle count (1 second) it will process one idle request. Arguments: Dpc - Pointer to DPC object Context - Pointer to the fdo device extension SystemArgument1 - Not used SystemArgument2 - Not used Return Value: None --*/ VOID ClasspIdleTimerDpc( IN PKDPC Dpc, IN PVOID Context, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Context; PCLASS_PRIVATE_FDO_DATA fdoData; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument1); UNREFERENCED_PARAMETER(SystemArgument2); if (fdoExtension == NULL) { NT_ASSERT(fdoExtension != NULL); return; } fdoData = fdoExtension->PrivateFdoData; if ((fdoData->ActiveIoCount <= 0) && (++fdoData->IdleTicks >= CLASS_IDLE_TIMER_TICKS)) { // // If there are max active idle request, do not issue another one here. // if (fdoData->ActiveIdleIoCount >= fdoData->IdleActiveIoMax) { return; } // // Check whether enough idle time has passed since the last non-idle // request has completed. // if (ClasspIdleTicksSufficient(fdoData)) { // // We are going to issue an idle request so reset the anti-starvation // timer counter. // fdoData->IdleTimerTicks = 0; ClasspServiceIdleRequest(fdoExtension, FALSE); } return; } // // If the timer is running then there must be at least one idle priority I/O pending // if (++fdoData->IdleTimerTicks >= fdoData->StarvationCount) { fdoData->IdleTimerTicks = 0; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspIdleTimerDpc: Starvation timer. Send one idle request\n")); ClasspServiceIdleRequest(fdoExtension, FALSE); } return; }
/* * ClassSplitRequest * * This is a legacy exported function. * It is called by storage miniport driver that have their own * StartIo routine when the transfer size is too large for the hardware. * We map it to our new read/write handler. */ VOID ClassSplitRequest(__in PDEVICE_OBJECT Fdo, __in PIRP Irp, __in ULONG MaximumBytes) { PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; if (MaximumBytes > fdoData->HwMaxXferLen) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "ClassSplitRequest - driver requesting split to size that " "hardware is unable to handle!\n")); } if (MaximumBytes < fdoData->HwMaxXferLen){ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "ClassSplitRequest - driver requesting smaller HwMaxXferLen " "than required")); fdoData->HwMaxXferLen = MAX(MaximumBytes, PAGE_SIZE); } ServiceTransferRequest(Fdo, Irp); }
VOID ClassFreeOrReuseSrb( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN PSCSI_REQUEST_BLOCK Srb) /*++ Routine Description: This routine will attempt to reuse the provided SRB to start a blocked read/write request. If there is no need to reuse the request it will be returned to the SRB lookaside list. Arguments: Fdo - the device extension Srb - the SRB which is to be reused or freed. Return Value: none. --*/ { PCOMMON_DEVICE_EXTENSION commonExt = &FdoExtension->CommonExtension; KIRQL oldIrql; PIRP blockedIrp; // This function is obsolete, but still called by DISK.SYS . // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassFreeOrReuseSrb is OBSOLETE !")); // // safety net. this should never occur. if it does, it's a potential // memory leak. // ASSERT(!TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); if (commonExt->IsSrbLookasideListInitialized){ /* * Put the SRB back in our lookaside list. * * Note: Some class drivers use ClassIoComplete * to complete SRBs that they themselves allocated. * So we may be putting a "foreign" SRB * (e.g. with a different pool tag) into our lookaside list. */ ClasspFreeSrb(FdoExtension, Srb); } else { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,"ClassFreeOrReuseSrb: someone is trying to use an uninitialized SrbLookasideList !!!")); FREE_POOL(Srb); } }
Vision::Vision() : VisionBase() { TracePrint(TRACE_VISION, "Initializing Camera Settings."); // Get an instance of the camera AxisCamera &camera = AxisCamera::GetInstance(CAMERA_IP); // Write some configuration to the camera camera.WriteResolution(CAMERA_LOW_RESOLUTION); camera.WriteCompression(CAMERA_COMPRESSION); camera.WriteBrightness(CAMERA_BRIGHTNESS); camera.WriteMaxFPS(CAMERA_FPS); camera.WriteColorLevel(CAMERA_COLOR_LEVEL); TracePrint(TRACE_VISION, "Done Configuring Camera."); particles = NULL; lightRing = new Relay(DEFAULT_DIGITAL_MODULE, LIGHT_RING_PORT, Relay::kForwardOnly); lightRing->Set(Relay::kOn); }
VOID ClassInsertCScanList(IN PCSCAN_LIST List, IN PIRP Irp, IN ULONGLONG BlockNumber, IN BOOLEAN LowPriority) /*++ Routine Description: This routine inserts an entry into the CScan list based on it's block number and priority. It is assumed that the caller is providing synchronization to the access of the list. Low priority requests are always scheduled to run on the next sweep across the disk. Normal priority requests will be inserted into the current or next sweep based on the standard C-SCAN algorithm. Arguments: List - the list to insert into Irp - the irp to be inserted. BlockNumber - the block number for this request. LowPriority - indicates that the request is lower priority and should be done on the next sweep across the disk. Return Value: none --*/ { PCSCAN_LIST_ENTRY entry = (PCSCAN_LIST_ENTRY)Irp->Tail.Overlay.DriverContext; TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInsertCScanList is OBSOLETE !")); // // Set the block number in the entry. We need this to keep the list sorted. // entry->BlockNumber = BlockNumber; // // If it's a normal priority request and further down the disk than our // current position then insert this entry into the current sweep. // if((LowPriority != TRUE) && (BlockNumber > List->BlockNumber)) { ClasspInsertCScanList(&(List->CurrentSweep), entry); } else { ClasspInsertCScanList(&(List->NextSweep), entry); } return; }
VOID ClassDeleteSrbLookasideList(__inout PCOMMON_DEVICE_EXTENSION CommonExtension) { PAGED_CODE(); // This function is obsolete, but is still called by some of our code. // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassDeleteSrbLookasideList is OBSOLETE !")); if (CommonExtension->IsSrbLookasideListInitialized){ CommonExtension->IsSrbLookasideListInitialized = FALSE; ExDeleteNPagedLookasideList(&CommonExtension->SrbLookasideList); } else { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassDeleteSrbLookasideList: attempt to delete uninitialized or freed srblookasidelist")); } }
/*++ ClasspStopIdleTimer Routine Description: Stop the idle timer if running. Also reset the timer counters. Arguments: FdoData - Pointer to the private fdo data Return Value: None --*/ VOID ClasspStopIdleTimer( PCLASS_PRIVATE_FDO_DATA FdoData ) { LONG timerStarted; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspStopIdleTimer: Stop idle timer\n")); timerStarted = InterlockedCompareExchange(&FdoData->IdleTimerStarted, 0, 1); if (timerStarted) { (VOID)KeCancelTimer(&FdoData->IdleTimer); } return; }
/*++ ClasspStartIdleTimer Routine Description: Start the idle timer if not already running. Reset the timer counters before starting the timer. Use the IdleInterval in the private fdo data to setup the timer. Arguments: FdoData - Pointer to the private fdo data IdleInterval - Amount of time since the completion of the last non-idle request Return Value: None --*/ VOID ClasspStartIdleTimer( IN PCLASS_PRIVATE_FDO_DATA FdoData, IN ULONGLONG IdleInterval ) { LARGE_INTEGER dueTime; LONG mstotimer; LONG timerStarted; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspStartIdleTimer: Start idle timer\n")); timerStarted = InterlockedCompareExchange(&FdoData->IdleTimerStarted, 1, 0); if (!timerStarted) { // // Reset the anti-starvation timer tick counter and set the idle tick // counter according to the actual amount of idle time. The latter is // important to do to ensure that if the idle queue drains and the timer // has to be stopped and started on the arrival of the next idle request, // those requests don't get delayed unnecessarily due to IdleTicks not // reflecting actual idle time. // FdoData->IdleTimerTicks = 0; FdoData->IdleTicks = (ULONG)(IdleInterval / FdoData->IdleTimerInterval); // // convert milliseconds to a relative 100ns // mstotimer = (-10) * 1000; // // multiply the period // dueTime.QuadPart = Int32x32To64(FdoData->IdleTimerInterval, mstotimer); KeSetTimerEx(&FdoData->IdleTimer, dueTime, FdoData->IdleTimerInterval, &FdoData->IdleDpc); } return; }
VOID TransferPacketRetryTimerDpc( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PTRANSFER_PACKET pkt; PDEVICE_OBJECT fdo; PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; _Analysis_assume_(DeferredContext != NULL); pkt = (PTRANSFER_PACKET)DeferredContext; fdo = pkt->Fdo; fdoExtension = fdo->DeviceExtension; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument1); UNREFERENCED_PARAMETER(SystemArgument2); /* * Sometimes the port driver can allocates a new 'sense' buffer * to report transfer errors, e.g. when the default sense buffer * is too small. If so, it is up to us to free it. * Now that we're done using the sense info, free it if appropriate. * Then clear the sense buffer so it doesn't pollute future errors returned in this packet. */ if (PORT_ALLOCATED_SENSE_EX(fdoExtension, pkt->Srb)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt)); FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, pkt->Srb); SrbSetSenseInfoBuffer(pkt->Srb, &pkt->SrbErrorSenseData); SrbSetSenseInfoBufferLength(pkt->Srb, sizeof(pkt->SrbErrorSenseData)); } else { NT_ASSERT(SrbGetSenseInfoBuffer(pkt->Srb) == &pkt->SrbErrorSenseData); NT_ASSERT(SrbGetSenseInfoBufferLength(pkt->Srb) <= sizeof(pkt->SrbErrorSenseData)); } RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(pkt->SrbErrorSenseData)); SubmitTransferPacket(pkt); }
VOID ClasspInsertCScanList(IN PLIST_ENTRY ListHead, IN PCSCAN_LIST_ENTRY Entry) { PCSCAN_LIST_ENTRY t; TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClasspInsertCScanList is OBSOLETE !")); // // Iterate through the list. Insert this entry in the sorted list in // order (after other requests for the same block). At each stop if // blockNumber(Entry) >= blockNumber(t) then move on. // for(t = (PCSCAN_LIST_ENTRY) ListHead->Flink; t != (PCSCAN_LIST_ENTRY) ListHead; t = (PCSCAN_LIST_ENTRY) t->Entry.Flink) { if(Entry->BlockNumber < t->BlockNumber) { // // Set the pointers in entry to the right location. // Entry->Entry.Flink = &(t->Entry); Entry->Entry.Blink = t->Entry.Blink; // // Set the pointers in the surrounding elements to refer to us. // t->Entry.Blink->Flink = &(Entry->Entry); t->Entry.Blink = &(Entry->Entry); return; } } // // Insert this entry at the tail of the list. If the list was empty this // will also be the head of the list. // InsertTailList(ListHead, &(Entry->Entry)); }
PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded) { PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; PTRANSFER_PACKET pkt; PSLIST_ENTRY slistEntry; slistEntry = InterlockedPopEntrySList(&fdoData->FreeTransferPacketsList); if (slistEntry){ slistEntry->Next = NULL; pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry); InterlockedDecrement(&fdoData->NumFreeTransferPackets); // when dequeue'ing the packet, also reset the history data HISTORYINITIALIZERETRYLOGS(pkt); } else { if (AllocIfNeeded){ /* * We are in stress and have run out of lookaside packets. * In order to service the current transfer, * allocate an extra packet. * We will free it lazily when we are out of stress. */ pkt = NewTransferPacket(Fdo); if (pkt){ InterlockedIncrement(&fdoData->NumTotalTransferPackets); fdoData->DbgPeakNumTransferPackets = max(fdoData->DbgPeakNumTransferPackets, fdoData->NumTotalTransferPackets); } else { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "DequeueFreeTransferPacket: packet allocation failed")); } } else { pkt = NULL; } } return pkt; }
/*++ ClasspCompleteIdleRequest Routine Description: This function will be called every time an idle request is completed. This will call ClasspServiceIdleRequest to process any other pending idle requests. Arguments: FdoExtension - Pointer to the device extension Return Value: None --*/ VOID ClasspCompleteIdleRequest( PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ) { PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; // // Issue the next idle request if there are any left in the queue, there are // no non-idle requests outstanding, there are less than max idle requests // outstanding, and it has been long enough since the completion of the last // non-idle request. // if ((fdoData->IdleIoCount > 0) && (fdoData->ActiveIdleIoCount < fdoData->IdleActiveIoMax) && (fdoData->ActiveIoCount <= 0) && (ClasspIdleTicksSufficient(fdoData))) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspCompleteIdleRequest: Service next idle reqeusts\n")); ClasspServiceIdleRequest(FdoExtension, TRUE); } return; }
NTSTATUS DiskInitFdo( IN PDEVICE_OBJECT Fdo ) /*++ Routine Description: This routine is called to do one-time initialization of new device objects Arguments: Fdo - a pointer to the functional device object for this device Return Value: status --*/ { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData; ULONG srbFlags = 0; ULONG timeOut = 0; ULONG bytesPerSector; PULONG dmSkew; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); // // Build the lookaside list for srb's for the physical disk. Should only // need a couple. If this fails then we don't have an emergency SRB so // fail the call to initialize. // ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) fdoExtension, PARTITION0_LIST_SIZE); if (fdoExtension->DeviceDescriptor->RemovableMedia) { SET_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA); } // // Initialize the srb flags. // // // Because all requests share a common sense buffer, it is possible // for the buffer to be overwritten if the port driver completes // multiple failed requests that require a request sense before the // class driver's completion routine can consume the data in the buffer. // To prevent this, we allow the port driver to allocate a unique sense // buffer each time it needs one. We are responsible for freeing this // buffer. This also allows the adapter to be configured to support // additional sense data beyond the minimum 18 bytes. // SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE); if (fdoExtension->DeviceDescriptor->CommandQueueing && fdoExtension->AdapterDescriptor->CommandQueueing) { SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); } // // Look for controllers that require special flags. // ClassScanForSpecial(fdoExtension, DiskBadControllers, DiskSetSpecialHacks); // // Clear buffer for drive geometry. // RtlZeroMemory(&(fdoExtension->DiskGeometry), sizeof(DISK_GEOMETRY)); // // Allocate request sense buffer. // fdoExtension->SenseData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, SENSE_BUFFER_SIZE_EX, DISK_TAG_START); if (fdoExtension->SenseData == NULL) { // // The buffer can not be allocated. // TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskInitFdo: Can not allocate request sense buffer\n")); status = STATUS_INSUFFICIENT_RESOURCES; return status; } // // Set the buffer size of SenseData // fdoExtension->SenseDataLength = SENSE_BUFFER_SIZE_EX; // // Physical device object will describe the entire // device, starting at byte offset 0. // fdoExtension->CommonExtension.StartingOffset.QuadPart = (LONGLONG)(0); // // Set timeout value in seconds. // if ( (fdoExtension->MiniportDescriptor != NULL) && (fdoExtension->MiniportDescriptor->IoTimeoutValue > 0) ) { // // use the value set by Storport miniport driver // fdoExtension->TimeOutValue = fdoExtension->MiniportDescriptor->IoTimeoutValue; } else { // // get timeout value from registry // timeOut = ClassQueryTimeOutRegistryValue(Fdo); if (timeOut) { fdoExtension->TimeOutValue = timeOut; } else { fdoExtension->TimeOutValue = SCSI_DISK_TIMEOUT; } } // // If this is a removable drive, build an entry in devicemap\scsi // indicating it's physicaldriveN name, set up the appropriate // update partitions routine and set the flags correctly. // note: only do this after the timeout value is set, above. // if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { ClassUpdateInformationInRegistry( Fdo, "PhysicalDrive", fdoExtension->DeviceNumber, NULL, 0); // // Enable media change notification for removable disks // ClassInitializeMediaChangeDetection(fdoExtension, (PUCHAR)"Disk"); } else { SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT); SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); } // // The commands we send during the init could cause the flags to change // in case of any error. Save the SRB flags locally and restore it at // the end of this function, so that the class driver can get it. // srbFlags = fdoExtension->SrbFlags; // // Read the drive capacity. Don't use the disk version of the routine here // since we don't know the disk signature yet - the disk version will // attempt to determine the BIOS reported geometry. // (VOID)ClassReadDriveCapacity(Fdo); // // Set up sector size fields. // // Stack variables will be used to update // the partition device extensions. // // The device extension field SectorShift is // used to calculate sectors in I/O transfers. // // The DiskGeometry structure is used to service // IOCTls used by the format utility. // bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; // // Make sure sector size is not zero. // if (bytesPerSector == 0) { // // Default sector size for disk is 512. // bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector = 512; fdoExtension->SectorShift = 9; } // // Determine is DM Driver is loaded on an IDE drive that is // under control of Atapi - this could be either a crashdump or // an Atapi device is sharing the controller with an IDE disk. // HalExamineMBR(fdoExtension->CommonExtension.DeviceObject, fdoExtension->DiskGeometry.BytesPerSector, (ULONG)0x54, &dmSkew); if (dmSkew) { // // Update the device extension, so that the call to IoReadPartitionTable // will get the correct information. Any I/O to this disk will have // to be skewed by *dmSkew sectors aka DMByteSkew. // fdoExtension->DMSkew = *dmSkew; fdoExtension->DMActive = TRUE; fdoExtension->DMByteSkew = fdoExtension->DMSkew * bytesPerSector; FREE_POOL(dmSkew); } #if defined(_X86_) || defined(_AMD64_) // // Try to read the signature off the disk and determine the correct drive // geometry based on that. This requires rereading the disk size to get // the cylinder count updated correctly. // if(!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { DiskReadSignature(Fdo); DiskReadDriveCapacity(Fdo); if (diskData->GeometrySource == DiskGeometryUnknown) { // // Neither the BIOS nor the port driver could provide us with a reliable // geometry. Before we use the default, look to see if it was partitioned // under Windows NT4 [or earlier] and apply the one that was used back then // if (DiskIsNT4Geometry(fdoExtension)) { diskData->RealGeometry = fdoExtension->DiskGeometry; diskData->RealGeometry.SectorsPerTrack = 0x20; diskData->RealGeometry.TracksPerCylinder = 0x40; fdoExtension->DiskGeometry = diskData->RealGeometry; diskData->GeometrySource = DiskGeometryFromNT4; } } } #endif DiskCreateSymbolicLinks(Fdo); // // Get the SCSI address if it's available for use with SMART ioctls. // SMART ioctls are used for failure prediction, so we need to get // the SCSI address before initializing failure prediction. // { PIRP irp; KEVENT event; IO_STATUS_BLOCK statusBlock = { 0 }; KeInitializeEvent(&event, SynchronizationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS, fdoExtension->CommonExtension.LowerDeviceObject, NULL, 0L, &(diskData->ScsiAddress), sizeof(SCSI_ADDRESS), FALSE, &event, &statusBlock); status = STATUS_UNSUCCESSFUL; if(irp != NULL) { status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp); if(status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = statusBlock.Status; } } } // // Determine the type of disk and enable failure prediction in the hardware // and enable failure prediction polling. // if (*InitSafeBootMode == 0) { DiskDetectFailurePrediction(fdoExtension, &diskData->FailurePredictionCapability, NT_SUCCESS(status)); if (diskData->FailurePredictionCapability != FailurePredictionNone) { // // Cool, we've got some sort of failure prediction, enable it // at the hardware and then enable polling for it // // // By default we allow performance to be degradeded if failure // prediction is enabled. // diskData->AllowFPPerfHit = TRUE; // // Enable polling only after Atapi and SBP2 add support for the new // SRB flag that indicates that the request should not reset the // drive spin down idle timer. // status = DiskEnableDisableFailurePredictPolling(fdoExtension, TRUE, DISK_DEFAULT_FAILURE_POLLING_PERIOD); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskInitFdo: Failure Prediction Poll enabled as " "%d for device %p, Status = %lx\n", diskData->FailurePredictionCapability, Fdo, status)); } } else { // // In safe boot mode we do not enable failure prediction, as perhaps // it is the reason why normal boot does not work // diskData->FailurePredictionCapability = FailurePredictionNone; } // // Initialize the verify mutex // KeInitializeMutex(&diskData->VerifyMutex, MAX_SECTORS_PER_VERIFY); // // Initialize the flush group context // RtlZeroMemory(&diskData->FlushContext, sizeof(DISK_GROUP_CONTEXT)); InitializeListHead(&diskData->FlushContext.CurrList); InitializeListHead(&diskData->FlushContext.NextList); KeInitializeSpinLock(&diskData->FlushContext.Spinlock); KeInitializeEvent(&diskData->FlushContext.Event, SynchronizationEvent, FALSE); // // Restore the saved value // fdoExtension->SrbFlags = srbFlags; return STATUS_SUCCESS; } // end DiskInitFdo()
/* * RetryTransferPacket * * Retry sending a TRANSFER_PACKET. * * Return TRUE iff the packet is complete. * (if so the status in pkt->irp is the final status). */ BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt) { BOOLEAN packetDone; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(Pkt->Srb))); NT_ASSERT(Pkt->NumRetries > 0 || Pkt->RetryHistory); Pkt->NumRetries--; // // If this is the last retry, then turn off disconnect, sync transfer, // and tagged queuing. On all other retries, leave the original settings. // if ( 0 == Pkt->NumRetries ) { /* * Tone down performance on the retry. * This increases the chance for success on the retry. * We've seen instances of drives that fail consistently but then start working * once this scale-down is applied. */ SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_DISCONNECT); SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); SrbClearSrbFlags(Pkt->Srb, SRB_FLAGS_QUEUE_ACTION_ENABLE); SrbSetRequestTag(Pkt->Srb, SP_UNTAGGED); } if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) { PCDB pCdb = SrbGetCdb(Pkt->Srb); UCHAR cdbOpcode = 0; BOOLEAN isReadWrite = FALSE; PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; if (pCdb) { cdbOpcode = pCdb->CDB10.OperationCode; isReadWrite = IS_SCSIOP_READWRITE(cdbOpcode); } if ((Pkt->DriverUsesStartIO) && ( (cdbOpcode == SCSIOP_WRITE6 ) || (cdbOpcode == SCSIOP_WRITE ) || (cdbOpcode == SCSIOP_WRITE12) || (cdbOpcode == SCSIOP_WRITE16) )) { /* don't retry writes in super-low-memory conditions if the * driver must serialize against StartIO. This is because * some write methods used in such drivers cannot accept * random-sized writes. (i.e CD-RW in packet writing mode) * Reads, however, are always safe to split up. */ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH1); packetDone = TRUE; } else if (Pkt->InLowMemRetry || !isReadWrite){ /* * This should never happen under normal circumstances. * The memory manager guarantees that at least four pages will * be available to allow forward progress in the port driver. * So a one-page transfer should never fail with insufficient resources. * * However, it is possible to get in here with virtual storage * or thin provisioned storage for example. * A single sector write can trigger an allocation request and * presently a forward progress guarantee is not provided. * VHD also may have some limitations in forward progress guarantee. * And USB too might also fall into this category. */ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH2); packetDone = TRUE; } else { /* * We are in low-memory stress. * Start the low-memory retry state machine, which tries to * resend the packet in little one-page chunks. */ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH3); InitLowMemRetry(Pkt, Pkt->BufPtrCopy, Pkt->BufLenCopy, Pkt->TargetLocationCopy); StepLowMemRetry(Pkt); packetDone = FALSE; } } else { /* * Retry the packet by simply resending it after a delay. * Put the packet back in the pending queue and * schedule a timer to retry the transfer. * * Do not call SetupReadWriteTransferPacket again because: * (1) The minidriver may have set some bits * in the SRB that it needs again and * (2) doing so would reset numRetries. * * BECAUSE we do not call SetupReadWriteTransferPacket again, * we have to reset a couple fields in the SRB that * some miniports overwrite when they fail an SRB. */ SrbSetDataBuffer(Pkt->Srb, Pkt->BufPtrCopy); SrbSetDataTransferLength(Pkt->Srb, Pkt->BufLenCopy); TransferPacketQueueRetryDpc(Pkt); packetDone = FALSE; } return packetDone; }
/* * InterpretTransferPacketError * * Interpret the SRB error into a meaningful IRP status. * ClassInterpretSenseInfo also may modify the SRB for the retry. * * Return TRUE iff packet should be retried. */ BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; ULONG timesAlreadyRetried; BOOLEAN shouldRetry = FALSE; PCDB pCdb = ClasspTransferPacketGetCdb(Pkt); /* * Interpret the error using the returned sense info first. */ Pkt->RetryIn100nsUnits = 0; /* * Pre-calculate the number of times the IO has already been * retried, so that all InterpretSenseInfo routines get the right value. */ if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL) { timesAlreadyRetried = NUM_LOCKMEDIAREMOVAL_RETRIES - Pkt->NumRetries; } else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) || (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)) { timesAlreadyRetried = NUM_MODESENSE_RETRIES - Pkt->NumRetries; } else if ((pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) || (pCdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16)) { timesAlreadyRetried = NUM_DRIVECAPACITY_RETRIES - Pkt->NumRetries; } else if (IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) { timesAlreadyRetried = NUM_IO_RETRIES - Pkt->NumRetries; } else if (pCdb->TOKEN_OPERATION.OperationCode == SCSIOP_POPULATE_TOKEN && pCdb->TOKEN_OPERATION.ServiceAction == SERVICE_ACTION_POPULATE_TOKEN) { timesAlreadyRetried = NUM_POPULATE_TOKEN_RETRIES - Pkt->NumRetries; } else if (pCdb->TOKEN_OPERATION.OperationCode == SCSIOP_WRITE_USING_TOKEN && pCdb->TOKEN_OPERATION.ServiceAction == SERVICE_ACTION_WRITE_USING_TOKEN) { timesAlreadyRetried = NUM_WRITE_USING_TOKEN_RETRIES - Pkt->NumRetries; } else if (ClasspIsReceiveTokenInformation(pCdb)) { timesAlreadyRetried = NUM_RECEIVE_TOKEN_INFORMATION_RETRIES - Pkt->NumRetries; } else { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt)); timesAlreadyRetried = 0; } if (fdoData->InterpretSenseInfo != NULL) { SCSI_REQUEST_BLOCK tempSrb = {0}; PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Pkt->Srb; // SAL annotation and ClassInitializeEx() both validate this NT_ASSERT(fdoData->InterpretSenseInfo->Interpret != NULL); // // If class driver does not support extended SRB and this is // an extended SRB, convert to legacy SRB and pass to class // driver. // if ((Pkt->Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) && ((fdoExtension->CommonExtension.DriverExtension->SrbSupport & CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) { ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Pkt->Srb); srbPtr = &tempSrb; } shouldRetry = fdoData->InterpretSenseInfo->Interpret(Pkt->Fdo, Pkt->OriginalIrp, srbPtr, IRP_MJ_SCSI, 0, timesAlreadyRetried, Pkt->RetryHistory, &Pkt->Irp->IoStatus.Status, &Pkt->RetryIn100nsUnits); } else if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL){ ULONG retryIntervalSeconds = 0; /* * This is an Ejection Control SRB. Interpret its sense info specially. */ shouldRetry = ClassInterpretSenseInfo( Pkt->Fdo, (PSCSI_REQUEST_BLOCK)Pkt->Srb, IRP_MJ_SCSI, 0, timesAlreadyRetried, &Pkt->Irp->IoStatus.Status, &retryIntervalSeconds); if (shouldRetry){ /* * If the device is not ready, wait at least 2 seconds before retrying. */ PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt); UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt); BOOLEAN validSense = FALSE; UCHAR additionalSenseCode = 0; BOOLEAN setRetryIntervalSeconds = FALSE; validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer, senseInfoBufferLength, SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, NULL, &additionalSenseCode, NULL); if (validSense) { if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) && (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) { setRetryIntervalSeconds = TRUE; } } if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) { setRetryIntervalSeconds = TRUE; } if (setRetryIntervalSeconds) { retryIntervalSeconds = MAX(retryIntervalSeconds, 2); } } if (shouldRetry) { Pkt->RetryIn100nsUnits = retryIntervalSeconds; Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10; } } else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) || (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)){ ULONG retryIntervalSeconds = 0; /* * This is an Mode Sense SRB. Interpret its sense info specially. */ shouldRetry = ClassInterpretSenseInfo( Pkt->Fdo, (PSCSI_REQUEST_BLOCK)Pkt->Srb, IRP_MJ_SCSI, 0, timesAlreadyRetried, &Pkt->Irp->IoStatus.Status, &retryIntervalSeconds); if (shouldRetry){ /* * If the device is not ready, wait at least 2 seconds before retrying. */ PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt); UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt); BOOLEAN validSense = FALSE; UCHAR additionalSenseCode = 0; BOOLEAN setRetryIntervalSeconds = FALSE; NT_ASSERT(senseInfoBuffer); validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer, senseInfoBufferLength, SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, NULL, &additionalSenseCode, NULL); if (validSense) { if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) && (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) { setRetryIntervalSeconds = TRUE; } } if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) { setRetryIntervalSeconds = TRUE; } if (setRetryIntervalSeconds) { retryIntervalSeconds = MAX(retryIntervalSeconds, 2); } } /* * Some special cases for mode sense. */ if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){ shouldRetry = TRUE; } else if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN){ /* * This is a HACK. * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means * underrun (i.e. success, and the buffer is longer than needed). * So treat this as a success. * When the caller of this function sees that the status was changed to success, * it will add the transferred length to the original irp. */ Pkt->Irp->IoStatus.Status = STATUS_SUCCESS; shouldRetry = FALSE; } if (shouldRetry) { Pkt->RetryIn100nsUnits = retryIntervalSeconds; Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10; } } else if ((pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) || (pCdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16)) { ULONG retryIntervalSeconds = 0; /* * This is a Drive Capacity SRB. Interpret its sense info specially. */ shouldRetry = ClassInterpretSenseInfo( Pkt->Fdo, (PSCSI_REQUEST_BLOCK)Pkt->Srb, IRP_MJ_SCSI, 0, timesAlreadyRetried, &Pkt->Irp->IoStatus.Status, &retryIntervalSeconds); if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){ shouldRetry = TRUE; } if (shouldRetry) { Pkt->RetryIn100nsUnits = retryIntervalSeconds; Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10; } } else if (IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) { ULONG retryIntervalSeconds = 0; /* * This is a Read/Write Data packet. */ PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp); shouldRetry = ClassInterpretSenseInfo( Pkt->Fdo, (PSCSI_REQUEST_BLOCK)Pkt->Srb, origCurrentSp->MajorFunction, 0, timesAlreadyRetried, &Pkt->Irp->IoStatus.Status, &retryIntervalSeconds); /* * Deal with some special cases. */ if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){ /* * We are in extreme low-memory stress. * We will retry in smaller chunks. */ shouldRetry = TRUE; } else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) && (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)){ /* * We are still verifying a (possibly) reloaded disk/cdrom. * So retry the request. */ Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; shouldRetry = TRUE; } if (shouldRetry) { Pkt->RetryIn100nsUnits = retryIntervalSeconds; Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10; } } else if (ClasspIsOffloadDataTransferCommand(pCdb)) { ULONG retryIntervalSeconds = 0; Pkt->TransferCount = 0; shouldRetry = ClassInterpretSenseInfo( Pkt->Fdo, (PSCSI_REQUEST_BLOCK)Pkt->Srb, IRP_MJ_SCSI, 0, timesAlreadyRetried, &Pkt->Irp->IoStatus.Status, &retryIntervalSeconds); if (shouldRetry) { Pkt->RetryIn100nsUnits = retryIntervalSeconds; Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10; } else { if (ClasspIsTokenOperation(pCdb)) { PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt); UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt); BOOLEAN validSense = FALSE; UCHAR senseKey = 0; UCHAR additionalSenseCode = 0; UCHAR additionalSenseQual = 0; BOOLEAN isInformationValid = FALSE; ULONGLONG information = 0; NT_ASSERT(senseInfoBuffer); validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer, senseInfoBufferLength, SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, &senseKey, &additionalSenseCode, &additionalSenseQual); if (validSense) { // // If this is a data underrun condition (i.e. target truncated the offload data transfer), // the SenseData's Information field may have the TransferCount. // if ((senseKey == SCSI_SENSE_COPY_ABORTED || senseKey == SCSI_SENSE_ABORTED_COMMAND) && (additionalSenseCode == SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR && additionalSenseQual == SCSI_SENSEQ_DATA_UNDERRUN)) { // // Sense data in Descriptor format // if (IsDescriptorSenseDataFormat(senseInfoBuffer)) { PVOID startBuffer = NULL; UCHAR startBufferLength = 0; if (ScsiGetSenseDescriptor(senseInfoBuffer, SrbGetSenseInfoBufferLength(Pkt->Srb), &startBuffer, &startBufferLength)) { UCHAR outType; PVOID outBuffer = NULL; UCHAR outBufferLength = 0; UCHAR typeList[1] = {SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION}; if (ScsiGetNextSenseDescriptorByType(startBuffer, startBufferLength, typeList, ARRAYSIZE(typeList), &outType, &outBuffer, &outBufferLength)) { if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) { if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) { REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information)); isInformationValid = TRUE; } } else { // // ScsiGetNextDescriptorByType should only return a type that is specified by us. // NT_ASSERT(FALSE); } } } } else { // // Sense data in Fixed format // REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseInfoBuffer)->Information)); isInformationValid = TRUE; } if (isInformationValid) { Pkt->TransferCount = information; } } } } } } else { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt)); } return shouldRetry; }
/*++ ClasspEnqueueIdleRequest Routine Description: This function will insert the idle request into the list. If the inserted reqeust is the first request then it will start the timer. Arguments: DeviceObject - Pointer to device object Irp - Pointer to the idle I/O request packet Return Value: NT status code. --*/ NTSTATUS ClasspEnqueueIdleRequest( PDEVICE_OBJECT DeviceObject, PIRP Irp ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; KIRQL oldIrql; BOOLEAN issueRequest = TRUE; ULONGLONG idleInterval; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspEnqueueIdleRequest: Queue idle request %p\n", Irp)); IoMarkIrpPending(Irp); // // Get the time difference between current time and last non-idle request // complete time. If the there has been enough idle time, then issue the // request (unless other factors prevent us from doing so below) and set the // idle time such that we starting the timer below, it would start off with // enough idle ticks. // idleInterval = ClasspGetIdleTime(fdoData); if (idleInterval >= fdoData->IdleInterval) { idleInterval = fdoData->IdleTimerInterval * CLASS_IDLE_TIMER_TICKS; } else { issueRequest = FALSE; } // // If there are already max active idle requests in the port driver, then // queue this idle request. // if (fdoData->ActiveIdleIoCount >= fdoData->IdleActiveIoMax) { issueRequest = FALSE; } TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_TIMER, "ClasspEnqueueIdleRequest: Diff time %I64d\n", idleInterval)); KeAcquireSpinLock(&fdoData->IdleListLock, &oldIrql); if (IsListEmpty(&fdoData->IdleIrpList)) { NT_ASSERT(fdoData->IdleIoCount == 0); } InsertTailList(&fdoData->IdleIrpList, &Irp->Tail.Overlay.ListEntry); fdoData->IdleIoCount++; if (!fdoData->IdleTimerStarted) { ClasspStartIdleTimer(fdoData, idleInterval); } if (fdoData->IdleIoCount != 1) { issueRequest = FALSE; } KeReleaseSpinLock(&fdoData->IdleListLock, oldIrql); if (issueRequest) { ClasspServiceIdleRequest(fdoExtension, FALSE); } return STATUS_PENDING; }
VOID DiskUpdateRemovablePartitions( IN PDEVICE_OBJECT Fdo, IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList ) /*++ Routine Description: This routine is called by the class DLL to update the PDO list off of this FDO. The disk driver also calls it internally to re-create device objects. This routine will read the partition table and update the size of the single partition device object which always exists for removable devices. Arguments: Fdo - a pointer to the FDO being reenumerated. Return Value: status --*/ { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; ULONG partitionCount; ULONG partitionNumber; ULONG partitionOrdinal = 0; ULONG newPartitionNumber; PDISK_DATA pdoData; NTSTATUS status; PPARTITION_INFORMATION_EX partitionEntry; PARTITION_STYLE partitionStyle; PAGED_CODE(); ASSERT(Fdo->Characteristics & FILE_REMOVABLE_MEDIA); partitionStyle = PartitionList->PartitionStyle; partitionCount = PartitionList->PartitionCount; for(partitionNumber = 0; partitionNumber < partitionCount; partitionNumber++) { partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); partitionEntry->PartitionNumber = 0; } // // Get exclusive access to the child list while repartitioning. // ClassAcquireChildLock(fdoExtension); // // Removable media should never have more than one PDO. // pdoExtension = fdoExtension->CommonExtension.ChildList; if(pdoExtension == NULL) { PARTITION_INFORMATION_EX tmpPartitionEntry = { 0 }; PDEVICE_OBJECT pdo; // // There is no PDO currently. Create one and pre-initialize it with // a zero length. // tmpPartitionEntry.PartitionNumber = 1; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdateRemovablePartitions: Creating RM partition\n")); status = DiskCreatePdo(Fdo, 0, &tmpPartitionEntry, partitionStyle, &pdo); if(!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM, "DiskUpdateRemovablePartitions: error %lx creating " "new PDO for RM partition\n", status)); goto DiskUpdateRemovablePartitionsExit; } // // mark the new device as enumerated // pdoExtension = pdo->DeviceExtension; pdoExtension->IsMissing = FALSE; } pdoData = pdoExtension->CommonExtension.DriverData; // // Search the partition list for a valid entry. We're looking for a // primary partition since we only support the one. // for(partitionNumber = 0; partitionNumber < partitionCount; partitionNumber++) { partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); // // Is this partition interesting? // if (partitionStyle == PARTITION_STYLE_MBR) { if(partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || IsContainerPartition(partitionEntry->Mbr.PartitionType)) { continue; } } partitionOrdinal++; // // We have found the first and thus only partition allowed on // this disk. Update the information in the PDO to match the new // partition. // TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdateRemovablePartitions: Matched %wZ to #%d, " "ordinal %d\n", &pdoExtension->CommonExtension.DeviceName, partitionEntry->PartitionNumber, partitionOrdinal)); partitionEntry->PartitionNumber = 1; pdoData->PartitionStyle = partitionStyle; pdoData->PartitionOrdinal = partitionOrdinal; ASSERT(partitionEntry->PartitionLength.LowPart != 0x23456789); pdoExtension->CommonExtension.StartingOffset = partitionEntry->StartingOffset; pdoExtension->CommonExtension.PartitionLength = partitionEntry->PartitionLength; if (partitionStyle == PARTITION_STYLE_MBR) { pdoData->Mbr.HiddenSectors = partitionEntry->Mbr.HiddenSectors; pdoData->Mbr.BootIndicator = partitionEntry->Mbr.BootIndicator; // // If this partition is being re-written then update the type // information as well // if (partitionEntry->RewritePartition) { pdoData->Mbr.PartitionType = partitionEntry->Mbr.PartitionType; } } else { pdoData->Efi.PartitionType = partitionEntry->Gpt.PartitionType; pdoData->Efi.PartitionId = partitionEntry->Gpt.PartitionId; pdoData->Efi.Attributes = partitionEntry->Gpt.Attributes; RtlCopyMemory( pdoData->Efi.PartitionName, partitionEntry->Gpt.Name, sizeof (pdoData->Efi.PartitionName) ); } // // Mark this one as found // pdoExtension->IsMissing = FALSE; goto DiskUpdateRemovablePartitionsExit; } // // No interesting partition was found. // if (partitionStyle == PARTITION_STYLE_MBR) { pdoData->Mbr.HiddenSectors = 0; pdoData->Mbr.PartitionType = PARTITION_ENTRY_UNUSED; } else { RtlZeroMemory (&pdoData->Efi, sizeof (pdoData->Efi) ); } pdoExtension->CommonExtension.StartingOffset.QuadPart = 0; pdoExtension->CommonExtension.PartitionLength.QuadPart = 0; DiskUpdateRemovablePartitionsExit: // // Update the parent device object // { PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); if (partitionStyle == PARTITION_STYLE_MBR) { diskData->PartitionStyle = PARTITION_STYLE_MBR; diskData->Mbr.Signature = PartitionList->Mbr.Signature; } else { diskData->PartitionStyle = PARTITION_STYLE_GPT; diskData->Efi.DiskId = PartitionList->Gpt.DiskId; } } ClassReleaseChildLock(fdoExtension); return; }
NTSTATUS DiskCreatePdo( IN PDEVICE_OBJECT Fdo, IN ULONG PartitionOrdinal, IN PPARTITION_INFORMATION_EX PartitionEntry, IN PARTITION_STYLE PartitionStyle, OUT PDEVICE_OBJECT *Pdo ) /*++ Routine Description: This routine will create and initialize a new partition device object (PDO) and insert it into the FDO partition list. Arguments: Fdo - a pointer to the functional device object this PDO will be a child of PartitionOrdinal - the partition ordinal for this PDO PartitionEntry - the partition information for this device object PartitionStyle - what style of partition table entry PartitionEntry is; currently either MBR or EFI Pdo - a location to store the pdo pointer upon successful completion Return Value: status --*/ { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PDEVICE_OBJECT pdo = NULL; PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; PUCHAR deviceName = NULL; PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData; ULONG numberListElements; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); // // Create partition object and set up partition parameters. // status = DiskGenerateDeviceName(FALSE, fdoExtension->DeviceNumber, PartitionEntry->PartitionNumber, &PartitionEntry->StartingOffset, &PartitionEntry->PartitionLength, &deviceName); if(!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM, "DiskCreatePdo - Can't generate name %lx\n", status)); return status; } TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskCreatePdo: Create device object %s\n", deviceName)); status = ClassCreateDeviceObject(Fdo->DriverObject, deviceName, Fdo, FALSE, &pdo); if (!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM, "DiskEnumerateDevice: Can't create device object for %s\n", deviceName)); FREE_POOL(deviceName); return status; } FREE_POOL(deviceName); // // Set up device extension fields. // pdoExtension = pdo->DeviceExtension; // // Set up device object fields. // SET_FLAG(pdo->Flags, DO_DIRECT_IO); pdo->StackSize = (CCHAR) pdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1; // // Get pointer to new disk data. // diskData = (PDISK_DATA) pdoExtension->CommonExtension.DriverData; // // Set the alignment requirements for the device based on the // host adapter requirements // if (Fdo->AlignmentRequirement > pdo->AlignmentRequirement) { pdo->AlignmentRequirement = Fdo->AlignmentRequirement; } if (fdoExtension->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) { numberListElements = 30; } else { numberListElements = 8; } // // Build the lookaside list for srb's for this partition based on // whether the adapter and disk can do tagged queueing. Don't bother to // check the status - this can't fail when called for a PDO. // ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) pdoExtension, numberListElements); // // Set the sense-data pointer in the device extension. // diskData->PartitionOrdinal = PartitionOrdinal; pdoExtension->CommonExtension.PartitionNumber = PartitionEntry->PartitionNumber; // // Initialize relevant data. // diskData->PartitionStyle = PartitionStyle; if (PartitionStyle == PARTITION_STYLE_MBR) { diskData->Mbr.PartitionType = PartitionEntry->Mbr.PartitionType; diskData->Mbr.BootIndicator = PartitionEntry->Mbr.BootIndicator; diskData->Mbr.HiddenSectors = PartitionEntry->Mbr.HiddenSectors; } else { diskData->Efi.PartitionType = PartitionEntry->Gpt.PartitionType; diskData->Efi.PartitionId = PartitionEntry->Gpt.PartitionId; diskData->Efi.Attributes = PartitionEntry->Gpt.Attributes; RtlCopyMemory (diskData->Efi.PartitionName, PartitionEntry->Gpt.Name, sizeof (diskData->Efi.PartitionName) ); } TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskEnumerateDevice: Partition type is %x\n", diskData->Mbr.PartitionType)); pdoExtension->CommonExtension.StartingOffset = PartitionEntry->StartingOffset; pdoExtension->CommonExtension.PartitionLength = PartitionEntry->PartitionLength; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskCreatePdo: hidden sectors value for pdo %#p set to %#x\n", pdo, diskData->Mbr.HiddenSectors)); // // Check for removable media support. // if (fdoExtension->DeviceDescriptor->RemovableMedia) { SET_FLAG(pdo->Characteristics, FILE_REMOVABLE_MEDIA); } pdoExtension->CommonExtension.DeviceObject = pdo; CLEAR_FLAG(pdo->Flags, DO_DEVICE_INITIALIZING); *Pdo = pdo; return status; }
VOID DiskCreateSymbolicLinks( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This routine will generate a symbolic link for the specified device object using the well known form \\Device\HarddiskX\PartitionY, where X and Y is always 0 which represents the entire disk object. This routine will not try to delete any previous symbolic link for the same generated name - the caller must make sure the symbolic link has been broken before calling this routine. Arguments: DeviceObject - the device object to make a well known name for Return Value: STATUS --*/ { PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; PDISK_DATA diskData = commonExtension->DriverData; WCHAR wideSourceName[64] = { 0 }; UNICODE_STRING unicodeSourceName; NTSTATUS status; PAGED_CODE(); // // Build the destination for the link first using the device name // stored in the device object // NT_ASSERT(commonExtension->DeviceName.Buffer); if(!diskData->LinkStatus.WellKnownNameCreated) { // // Put together the source name using the partition and device number // in the device extension and disk data segment // status = RtlStringCchPrintfW(wideSourceName, sizeof(wideSourceName) / sizeof(wideSourceName[0]) - 1, L"\\Device\\Harddisk%d\\Partition0", commonExtension->PartitionZeroExtension->DeviceNumber); if (NT_SUCCESS(status)) { RtlInitUnicodeString(&unicodeSourceName, wideSourceName); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n", &unicodeSourceName, &commonExtension->DeviceName)); status = IoCreateSymbolicLink(&unicodeSourceName, &commonExtension->DeviceName); if(NT_SUCCESS(status)){ diskData->LinkStatus.WellKnownNameCreated = TRUE; } } } if (!diskData->LinkStatus.PhysicalDriveLinkCreated) { // // Create a physical drive N link using the device number we saved // away during AddDevice. // status = RtlStringCchPrintfW(wideSourceName, sizeof(wideSourceName) / sizeof(wideSourceName[0]) - 1, L"\\DosDevices\\PhysicalDrive%d", commonExtension->PartitionZeroExtension->DeviceNumber); if (NT_SUCCESS(status)) { RtlInitUnicodeString(&unicodeSourceName, wideSourceName); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n", &unicodeSourceName, &(commonExtension->DeviceName))); status = IoCreateSymbolicLink(&unicodeSourceName, &(commonExtension->DeviceName)); if(NT_SUCCESS(status)) { diskData->LinkStatus.PhysicalDriveLinkCreated = TRUE; } } } return; }
NTSTATUS DiskAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: This routine gets a port drivers capabilities, obtains the inquiry data, searches the SCSI bus for the port driver and creates the device objects for the disks found. Arguments: DriverObject - Pointer to driver object created by system. Pdo - Device object use to send requests to port driver. Return Value: True is returned if one disk was found and successfully created. --*/ { ULONG rootPartitionMountable = FALSE; PCONFIGURATION_INFORMATION configurationInformation; ULONG diskCount; NTSTATUS status; PAGED_CODE(); // // See if we should be allowing file systems to mount on partition zero. // TRY { HANDLE deviceKey = NULL; UNICODE_STRING diskKeyName; OBJECT_ATTRIBUTES objectAttributes = {0}; HANDLE diskKey; RTL_QUERY_REGISTRY_TABLE queryTable[2] = { 0 }; status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &deviceKey); if(!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx opening device key " "for pdo %p\n", status, PhysicalDeviceObject)); LEAVE; } RtlInitUnicodeString(&diskKeyName, L"Disk"); InitializeObjectAttributes(&objectAttributes, &diskKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, deviceKey, NULL); status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes); ZwClose(deviceKey); if(!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx opening disk key " "for pdo %p device key %p\n", status, PhysicalDeviceObject, deviceKey)); LEAVE; } queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[0].Name = L"RootPartitionMountable"; queryTable[0].EntryContext = &(rootPartitionMountable); #pragma prefast(suppress:6309, "We don't have QueryRoutine so Context doesn't make any sense") status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, diskKey, queryTable, NULL, NULL); if(!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx reading value from " "disk key %p for pdo %p\n", status, diskKey, PhysicalDeviceObject)); } ZwClose(diskKey); } FINALLY { // // Do nothing. // if(!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskAddDevice: Will %sallow file system to mount on " "partition zero of disk %p\n", (rootPartitionMountable ? "" : "not "), PhysicalDeviceObject)); } } // // Create device objects for disk // diskCount = 0; status = DiskCreateFdo( DriverObject, PhysicalDeviceObject, &diskCount, (BOOLEAN) !rootPartitionMountable ); // // Get the number of disks already initialized. // configurationInformation = IoGetConfigurationInformation(); if (NT_SUCCESS(status)) { // // Increment system disk device count. // configurationInformation->DiskCount++; } return status; } // end DiskAddDevice()
VOID DiskUpdatePartitions( IN PDEVICE_OBJECT Fdo, IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList ) /*++ Routine Description: This routine will synchronize the information held in the partition list with the device objects hanging off this Fdo. Any new partition objects will be created, any non-existant ones will be marked as un-enumerated. This will be done in several stages: * Clear state (partition number) from every entry in the partition list * Set IsMissing flag on every child of this FDO * For each child of the FDO: if a matching partition exists in the partition list, update the partition number in the table, update the ordinal in the object and mark the object as enumerated * For each un-enumerated device object zero out the partition information to invalidate the device delete the symbolic link if any * For each un-matched entry in the partition list: create a new partition object update the partition number in the list entry create a new symbolic link if necessary Arguments: Fdo - a pointer to the functional device object this partition list is for PartitionList - a pointer to the partition list being updated Return Value: none --*/ { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PPHYSICAL_DEVICE_EXTENSION oldChildList = NULL; PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; ULONG partitionCount; ULONG partitionNumber; ULONG partitionOrdinal; ULONG newPartitionNumber; PPARTITION_INFORMATION_EX partitionEntry; PDISK_DATA pdoData; PARTITION_STYLE partitionStyle; NTSTATUS status; PAGED_CODE(); // // Get exclusive access to the child list. // ClassAcquireChildLock(fdoExtension); partitionStyle = PartitionList->PartitionStyle; partitionCount = PartitionList->PartitionCount; // // Pull all the child device objects off the children list. We'll // add them back later. // oldChildList = fdoExtension->CommonExtension.ChildList; fdoExtension->CommonExtension.ChildList = NULL; // // Clear the partition numbers from the list entries // for(partitionNumber = 0; partitionNumber < partitionCount; partitionNumber++) { partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); partitionEntry->PartitionNumber = 0; } // // Now match each child partition to it's entry (if any) in the partition // list. // while(oldChildList != NULL) { pdoExtension = oldChildList; pdoData = pdoExtension->CommonExtension.DriverData; // // Check all partition entries for a match on offset and length // partitionOrdinal = 0; for(partitionNumber = 0; partitionNumber < partitionCount; partitionNumber++) { partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); // // Is this an interesting partition entry? // if (partitionStyle == PARTITION_STYLE_MBR) { if((partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) || (IsContainerPartition(partitionEntry->Mbr.PartitionType))) { continue; } } partitionOrdinal++; if(partitionEntry->PartitionNumber) { // // This partition has already been found - skip it // continue; } // // Let's see if the partition information matches // if(partitionEntry->StartingOffset.QuadPart != pdoExtension->CommonExtension.StartingOffset.QuadPart) { continue; } if(partitionEntry->PartitionLength.QuadPart != pdoExtension->CommonExtension.PartitionLength.QuadPart) { continue; } // // Yep - it matches. Update the information in the entry // partitionEntry->PartitionNumber = pdoExtension->CommonExtension.PartitionNumber; if (partitionStyle == PARTITION_STYLE_MBR) { pdoData->Mbr.HiddenSectors = partitionEntry->Mbr.HiddenSectors; } break; } if(partitionNumber != partitionCount) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: Matched %wZ to #%d, ordinal " "%d\n", &pdoExtension->CommonExtension.DeviceName, partitionEntry->PartitionNumber, partitionOrdinal)); ASSERT(partitionEntry->PartitionLength.LowPart != 0x23456789); // ASSERT(pdoExtension->CommonExtension.PartitionLength.QuadPart != 0); pdoData->PartitionStyle = partitionStyle; // // we found a match - update the information in the device object // extension and driverdata // pdoData->PartitionOrdinal = partitionOrdinal; // // If this partition is being re-written then update the type // information as well // if (partitionStyle == PARTITION_STYLE_MBR) { if(partitionEntry->RewritePartition) { pdoData->Mbr.PartitionType = partitionEntry->Mbr.PartitionType; } } else { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: EFI Partition %ws\n", pdoData->Efi.PartitionName )); pdoData->Efi.PartitionType = partitionEntry->Gpt.PartitionType; pdoData->Efi.PartitionId = partitionEntry->Gpt.PartitionId; pdoData->Efi.Attributes = partitionEntry->Gpt.Attributes; RtlCopyMemory( pdoData->Efi.PartitionName, partitionEntry->Gpt.Name, sizeof (pdoData->Efi.PartitionName) ); } // // Mark this one as found. // pdoExtension->IsMissing = FALSE; // // Pull it out of the old child list and add it into the // real one. // oldChildList = pdoExtension->CommonExtension.ChildList; pdoExtension->CommonExtension.ChildList = fdoExtension->CommonExtension.ChildList; fdoExtension->CommonExtension.ChildList = pdoExtension; } else { PDEVICE_OBJECT nextPdo; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: Deleting %wZ\n", &pdoExtension->CommonExtension.DeviceName)); if (partitionStyle == PARTITION_STYLE_GPT) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: EFI Partition %ws\n", pdoData->Efi.PartitionName )); } // // no matching entry in the partition list - throw this partition // object away // pdoExtension->CommonExtension.PartitionLength.QuadPart = 0; // // grab a pointer to the next child before we mark this one as // missing since missing devices could vanish at any time. // oldChildList = pdoExtension->CommonExtension.ChildList; pdoExtension->CommonExtension.ChildList = (PVOID) -1; // // Now tell the class driver that this child is "missing" - this // will cause it to be deleted. // ClassMarkChildMissing(pdoExtension, FALSE); } } // // At this point the old child list had best be empty. // ASSERT(oldChildList == NULL); // // Iterate through the partition entries and create any partition // objects that don't already exist // partitionOrdinal = 0; newPartitionNumber = 0; for(partitionNumber = 0; partitionNumber < partitionCount; partitionNumber++) { PDEVICE_OBJECT pdo; partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); // // Is this partition interesting // if (partitionStyle == PARTITION_STYLE_MBR) { if((partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) || (IsContainerPartition(partitionEntry->Mbr.PartitionType))) { continue; } } // // Increment the count of interesting partitions // partitionOrdinal++; newPartitionNumber++; // // Has this already been matched // if(partitionEntry->PartitionNumber == 0) { LONG i; // // find the first safe partition number for this device // for(i = 0; i < (LONG) partitionCount; i++) { PPARTITION_INFORMATION_EX tmp = &(PartitionList->PartitionEntry[i]); if (partitionStyle == PARTITION_STYLE_MBR) { if (tmp->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || IsContainerPartition(tmp->Mbr.PartitionType)) { continue; } } if(tmp->PartitionNumber == newPartitionNumber) { // // Found a matching partition number - increment the count // and restart the scan. // newPartitionNumber++; i = -1; continue; } } // // Assign this partition a partition number // partitionEntry->PartitionNumber = newPartitionNumber; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: Found new partition #%d, ord %d " "starting at %#016I64x and running for %#016I64x\n", partitionEntry->PartitionNumber, partitionOrdinal, partitionEntry->StartingOffset.QuadPart, partitionEntry->PartitionLength.QuadPart)); ClassReleaseChildLock(fdoExtension); status = DiskCreatePdo(Fdo, partitionOrdinal, partitionEntry, partitionStyle, &pdo); ClassAcquireChildLock(fdoExtension); if(!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM, "DiskUpdatePartitions: error %lx creating " "new PDO for partition ordinal %d, number %d\n", status, partitionOrdinal, partitionEntry->PartitionNumber)); // // don't increment the partition number - we'll try to reuse // it for the next child. // partitionEntry->PartitionNumber = 0; newPartitionNumber--; continue; } // // mark the new device as enumerated // pdoExtension = pdo->DeviceExtension; pdoExtension->IsMissing = FALSE; // // This number's taken already - try to scanning the partition // table more than once for a new number. // } } // // Update the parent device object // { PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); if (partitionStyle == PARTITION_STYLE_MBR) { diskData->PartitionStyle = PARTITION_STYLE_MBR; diskData->Mbr.Signature = PartitionList->Mbr.Signature; } else { diskData->PartitionStyle = PARTITION_STYLE_GPT; diskData->Efi.DiskId = PartitionList->Gpt.DiskId; } } ClassReleaseChildLock(fdoExtension); return; }
/*++//////////////////////////////////////////////////////////////////////////// ClassSystemControl() Routine Description: Dispatch routine for IRP_MJ_SYSTEM_CONTROL. This routine will process all wmi requests received, forwarding them if they are not for this driver or determining if the guid is valid and if so passing it to the driver specific function for handing wmi requests. Arguments: DeviceObject - Supplies a pointer to the device object for this request. Irp - Supplies the Irp making the request. Return Value: status --*/ NTSTATUS ClassSystemControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; PCLASS_DRIVER_EXTENSION driverExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); ULONG isRemoved; ULONG bufferSize; PUCHAR buffer; NTSTATUS status; UCHAR minorFunction; ULONG guidIndex = (ULONG)-1; PCLASS_WMI_INFO classWmiInfo; BOOLEAN isInternalGuid = FALSE; PAGED_CODE(); // // Make sure device has not been removed isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp); if(isRemoved) { Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; ClassReleaseRemoveLock(DeviceObject, Irp); ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); return STATUS_DEVICE_DOES_NOT_EXIST; } // // If the irp is not a WMI irp or it is not targetted at this device // or this device has not regstered with WMI then just forward it on. minorFunction = irpStack->MinorFunction; if ((minorFunction > IRP_MN_EXECUTE_METHOD) || (irpStack->Parameters.WMI.ProviderId != (ULONG_PTR)DeviceObject) || ((minorFunction != IRP_MN_REGINFO) && (commonExtension->GuidCount == 0))) { // // CONSIDER: Do I need to hang onto lock until IoCallDriver returns ? IoSkipCurrentIrpStackLocation(Irp); ClassReleaseRemoveLock(DeviceObject, Irp); return(IoCallDriver(commonExtension->LowerDeviceObject, Irp)); } buffer = (PUCHAR)irpStack->Parameters.WMI.Buffer; bufferSize = irpStack->Parameters.WMI.BufferSize; if (minorFunction != IRP_MN_REGINFO) { // // For all requests other than query registration info we are passed // a guid. Determine if the guid is one that is supported by the // device. if (commonExtension->GuidRegInfo != NULL && ClassFindGuid(commonExtension->GuidRegInfo, commonExtension->GuidCount, (LPGUID)irpStack->Parameters.WMI.DataPath, &guidIndex)) { isInternalGuid = FALSE; status = STATUS_SUCCESS; } else if (ClassFindInternalGuid((LPGUID)irpStack->Parameters.WMI.DataPath, &guidIndex)) { isInternalGuid = TRUE; status = STATUS_SUCCESS; } else { status = STATUS_WMI_GUID_NOT_FOUND; TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "WMI GUID not found!")); } TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_WMI, "WMI Find Guid = %x, isInternalGuid = %x", status, isInternalGuid)); if (NT_SUCCESS(status) && ((minorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) || (minorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE) || (minorFunction == IRP_MN_CHANGE_SINGLE_ITEM) || (minorFunction == IRP_MN_EXECUTE_METHOD))) { if ( (((PWNODE_HEADER)buffer)->Flags) & WNODE_FLAG_STATIC_INSTANCE_NAMES) { if ( ((PWNODE_SINGLE_INSTANCE)buffer)->InstanceIndex != 0 ) { status = STATUS_WMI_INSTANCE_NOT_FOUND; } } else { status = STATUS_WMI_INSTANCE_NOT_FOUND; TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "WMI Instance not found!")); } } if (! NT_SUCCESS(status)) { Irp->IoStatus.Status = status; ClassReleaseRemoveLock(DeviceObject, Irp); ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); return(status); } } driverExtension = commonExtension->DriverExtension; classWmiInfo = commonExtension->IsFdo ? &driverExtension->InitData.FdoData.ClassWmiInfo : &driverExtension->InitData.PdoData.ClassWmiInfo; switch(minorFunction) { case IRP_MN_REGINFO: { ULONG guidCount; PGUIDREGINFO guidList; PWMIREGINFOW wmiRegInfo; PWMIREGGUIDW wmiRegGuid; PUNICODE_STRING regPath; PWCHAR stringPtr; ULONG retSize; ULONG registryPathOffset; ULONG mofResourceOffset; ULONG bufferNeeded; ULONG i; ULONG_PTR nameInfo; ULONG nameSize, nameOffset, nameFlags; UNICODE_STRING name, mofName; PCLASS_QUERY_WMI_REGINFO_EX ClassQueryWmiRegInfoEx; name.Buffer = NULL; name.Length = 0; name.MaximumLength = 0; nameFlags = 0; ClassQueryWmiRegInfoEx = commonExtension->IsFdo ? driverExtension->ClassFdoQueryWmiRegInfoEx : driverExtension->ClassPdoQueryWmiRegInfoEx; if ((classWmiInfo->GuidRegInfo != NULL) && (classWmiInfo->ClassQueryWmiRegInfo != NULL) && (ClassQueryWmiRegInfoEx == NULL)) { status = classWmiInfo->ClassQueryWmiRegInfo( DeviceObject, &nameFlags, &name); RtlInitUnicodeString(&mofName, MOFRESOURCENAME); } else if ((classWmiInfo->GuidRegInfo != NULL) && (ClassQueryWmiRegInfoEx != NULL)) { RtlInitUnicodeString(&mofName, L""); status = (*ClassQueryWmiRegInfoEx)( DeviceObject, &nameFlags, &name, &mofName); } else { RtlInitUnicodeString(&mofName, L""); nameFlags = WMIREG_FLAG_INSTANCE_PDO; status = STATUS_SUCCESS; } if (NT_SUCCESS(status) && (! (nameFlags & WMIREG_FLAG_INSTANCE_PDO) && (name.Buffer == NULL))) { // // if PDO flag not specified then an instance name must be status = STATUS_INVALID_DEVICE_REQUEST; TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "Invalid Device Request!")); } if (NT_SUCCESS(status)) { guidList = classWmiInfo->GuidRegInfo; guidCount = (classWmiInfo->GuidRegInfo == NULL ? 0 : classWmiInfo->GuidCount) + NUM_CLASS_WMI_GUIDS; nameOffset = sizeof(WMIREGINFO) + guidCount * sizeof(WMIREGGUIDW); if (nameFlags & WMIREG_FLAG_INSTANCE_PDO) { nameSize = 0; nameInfo = commonExtension->IsFdo ? (ULONG_PTR)((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension)->LowerPdo : (ULONG_PTR)DeviceObject; } else { nameFlags |= WMIREG_FLAG_INSTANCE_LIST; nameSize = name.Length + sizeof(USHORT); nameInfo = nameOffset; } mofResourceOffset = nameOffset + nameSize; registryPathOffset = mofResourceOffset + mofName.Length + sizeof(USHORT); regPath = &driverExtension->RegistryPath; bufferNeeded = registryPathOffset + regPath->Length; bufferNeeded += sizeof(USHORT); if (bufferNeeded <= bufferSize) { retSize = bufferNeeded; commonExtension->GuidCount = guidCount; commonExtension->GuidRegInfo = guidList; wmiRegInfo = (PWMIREGINFO)buffer; wmiRegInfo->BufferSize = bufferNeeded; wmiRegInfo->NextWmiRegInfo = 0; wmiRegInfo->MofResourceName = mofResourceOffset; wmiRegInfo->RegistryPath = registryPathOffset; wmiRegInfo->GuidCount = guidCount; for (i = 0; i < classWmiInfo->GuidCount; i++) { wmiRegGuid = &wmiRegInfo->WmiRegGuid[i]; wmiRegGuid->Guid = guidList[i].Guid; wmiRegGuid->Flags = guidList[i].Flags | nameFlags; wmiRegGuid->InstanceInfo = nameInfo; wmiRegGuid->InstanceCount = 1; } for (i = 0; i < NUM_CLASS_WMI_GUIDS; i++) { wmiRegGuid = &wmiRegInfo->WmiRegGuid[i + classWmiInfo->GuidCount]; wmiRegGuid->Guid = wmiClassGuids[i].Guid; wmiRegGuid->Flags = wmiClassGuids[i].Flags | nameFlags; wmiRegGuid->InstanceInfo = nameInfo; wmiRegGuid->InstanceCount = 1; } if ( nameFlags & WMIREG_FLAG_INSTANCE_LIST) { bufferNeeded = nameOffset + sizeof(WCHAR); bufferNeeded += name.Length; if (bufferSize >= bufferNeeded){ stringPtr = (PWCHAR)((PUCHAR)buffer + nameOffset); *stringPtr++ = name.Length; RtlCopyMemory(stringPtr, name.Buffer, name.Length); } else { NT_ASSERT(bufferSize >= bufferNeeded); status = STATUS_INVALID_BUFFER_SIZE; } } bufferNeeded = mofResourceOffset + sizeof(WCHAR); bufferNeeded += mofName.Length; if (bufferSize >= bufferNeeded){ stringPtr = (PWCHAR)((PUCHAR)buffer + mofResourceOffset); *stringPtr++ = mofName.Length; RtlCopyMemory(stringPtr, mofName.Buffer, mofName.Length); } else { NT_ASSERT(bufferSize >= bufferNeeded); status = STATUS_INVALID_BUFFER_SIZE; } bufferNeeded = registryPathOffset + sizeof(WCHAR); bufferNeeded += regPath->Length; if (bufferSize >= bufferNeeded){ stringPtr = (PWCHAR)((PUCHAR)buffer + registryPathOffset); *stringPtr++ = regPath->Length; RtlCopyMemory(stringPtr, regPath->Buffer, regPath->Length); } else { NT_ASSERT(bufferSize >= bufferNeeded); TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "Invalid Buffer Size!")); status = STATUS_INVALID_BUFFER_SIZE; } } else { *((PULONG)buffer) = bufferNeeded; retSize = sizeof(ULONG); } } else { retSize = 0; } FREE_POOL(name.Buffer); Irp->IoStatus.Status = status; Irp->IoStatus.Information = retSize; ClassReleaseRemoveLock(DeviceObject, Irp); ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); return(status); } case IRP_MN_QUERY_ALL_DATA: { PWNODE_ALL_DATA wnode; ULONG bufferAvail; wnode = (PWNODE_ALL_DATA)buffer; if (bufferSize < sizeof(WNODE_ALL_DATA)) { bufferAvail = 0; } else { bufferAvail = bufferSize - sizeof(WNODE_ALL_DATA); } wnode->DataBlockOffset = sizeof(WNODE_ALL_DATA); NT_ASSERT(guidIndex != (ULONG)-1); _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassQueryInternalDataBlock( DeviceObject, Irp, guidIndex, bufferAvail, buffer + sizeof(WNODE_ALL_DATA)); } else { status = classWmiInfo->ClassQueryWmiDataBlock( DeviceObject, Irp, guidIndex, bufferAvail, buffer + sizeof(WNODE_ALL_DATA)); } break; } case IRP_MN_QUERY_SINGLE_INSTANCE: { PWNODE_SINGLE_INSTANCE wnode; ULONG dataBlockOffset; wnode = (PWNODE_SINGLE_INSTANCE)buffer; dataBlockOffset = wnode->DataBlockOffset; NT_ASSERT(guidIndex != (ULONG)-1); _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassQueryInternalDataBlock( DeviceObject, Irp, guidIndex, bufferSize - dataBlockOffset, (PUCHAR)wnode + dataBlockOffset); } else { status = classWmiInfo->ClassQueryWmiDataBlock( DeviceObject, Irp, guidIndex, bufferSize - dataBlockOffset, (PUCHAR)wnode + dataBlockOffset); } break; } case IRP_MN_CHANGE_SINGLE_INSTANCE: { PWNODE_SINGLE_INSTANCE wnode; wnode = (PWNODE_SINGLE_INSTANCE)buffer; _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassWmiCompleteRequest(DeviceObject, Irp, STATUS_WMI_GUID_NOT_FOUND, 0, IO_NO_INCREMENT); } else { NT_ASSERT(guidIndex != (ULONG)-1); status = classWmiInfo->ClassSetWmiDataBlock( DeviceObject, Irp, guidIndex, wnode->SizeDataBlock, (PUCHAR)wnode + wnode->DataBlockOffset); } break; } case IRP_MN_CHANGE_SINGLE_ITEM: { PWNODE_SINGLE_ITEM wnode; wnode = (PWNODE_SINGLE_ITEM)buffer; NT_ASSERT(guidIndex != (ULONG)-1); _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassWmiCompleteRequest(DeviceObject, Irp, STATUS_WMI_GUID_NOT_FOUND, 0, IO_NO_INCREMENT); } else { NT_ASSERT(guidIndex != (ULONG)-1); status = classWmiInfo->ClassSetWmiDataItem( DeviceObject, Irp, guidIndex, wnode->ItemId, wnode->SizeDataItem, (PUCHAR)wnode + wnode->DataBlockOffset); } break; } case IRP_MN_EXECUTE_METHOD: { PWNODE_METHOD_ITEM wnode; wnode = (PWNODE_METHOD_ITEM)buffer; _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassWmiCompleteRequest(DeviceObject, Irp, STATUS_WMI_GUID_NOT_FOUND, 0, IO_NO_INCREMENT); } else { NT_ASSERT(guidIndex != (ULONG)-1); status = classWmiInfo->ClassExecuteWmiMethod( DeviceObject, Irp, guidIndex, wnode->MethodId, wnode->SizeDataBlock, bufferSize - wnode->DataBlockOffset, buffer + wnode->DataBlockOffset); } break; } case IRP_MN_ENABLE_EVENTS: { _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassWmiCompleteRequest(DeviceObject, Irp, STATUS_WMI_GUID_NOT_FOUND, 0, IO_NO_INCREMENT); } else { NT_ASSERT(guidIndex != (ULONG)-1); status = classWmiInfo->ClassWmiFunctionControl( DeviceObject, Irp, guidIndex, EventGeneration, TRUE); } break; } case IRP_MN_DISABLE_EVENTS: { _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassWmiCompleteRequest(DeviceObject, Irp, STATUS_WMI_GUID_NOT_FOUND, 0, IO_NO_INCREMENT); } else { NT_ASSERT(guidIndex != (ULONG)-1); status = classWmiInfo->ClassWmiFunctionControl( DeviceObject, Irp, guidIndex, EventGeneration, FALSE); } break; } case IRP_MN_ENABLE_COLLECTION: { _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassWmiCompleteRequest(DeviceObject, Irp, STATUS_WMI_GUID_NOT_FOUND, 0, IO_NO_INCREMENT); } else { NT_ASSERT(guidIndex != (ULONG)-1); status = classWmiInfo->ClassWmiFunctionControl( DeviceObject, Irp, guidIndex, DataBlockCollection, TRUE); } break; } case IRP_MN_DISABLE_COLLECTION: { _Analysis_assume_(isInternalGuid); if (isInternalGuid) { status = ClassWmiCompleteRequest(DeviceObject, Irp, STATUS_WMI_GUID_NOT_FOUND, 0, IO_NO_INCREMENT); } else { NT_ASSERT(guidIndex != (ULONG)-1); status = classWmiInfo->ClassWmiFunctionControl( DeviceObject, Irp, guidIndex, DataBlockCollection, FALSE); } break; } default: { status = STATUS_INVALID_DEVICE_REQUEST; break; } } return(status); } // end ClassSystemControl()
VOID ClasspBuildRequestEx( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN PIRP Irp, IN PSCSI_REQUEST_BLOCK Srb ) /*++ ClasspBuildRequestEx() Routine Description: This routine allocates and builds an Srb for a read or write request. The block address and length are supplied by the Irp. The retry count is stored in the current stack for use by ClassIoComplete which processes these requests when they complete. The Irp is ready to be passed to the port driver when this routine returns. Arguments: FdoExtension - Supplies the device extension associated with this request. Irp - Supplies the request to be issued. Srb - Supplies an SRB to be used for the request. Note: If the IRP is for a disk transfer, the byteoffset field will already have been adjusted to make it relative to the beginning of the disk. Return Value: NT Status --*/ { PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; PCDB cdb; ULONG logicalBlockAddress; USHORT transferBlocks; // This function is obsolete, but still called by CDROM.SYS . // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClasspBuildRequestEx is OBSOLETE !")); // // Prepare the SRB. // RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK)); // // Calculate relative sector address. // logicalBlockAddress = (ULONG)(Int64ShrlMod32(startingOffset.QuadPart, FdoExtension->SectorShift)); // // Write length to SRB. // Srb->Length = sizeof(SCSI_REQUEST_BLOCK); // // Set up IRP Address. // Srb->OriginalRequest = Irp; // // Set up target ID and logical unit number. // Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; Srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); // // Save byte count of transfer in SRB Extension. // Srb->DataTransferLength = currentIrpStack->Parameters.Read.Length; // // Initialize the queue actions field. // Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; // // Queue sort key is Relative Block Address. // Srb->QueueSortKey = logicalBlockAddress; // // Indicate auto request sense by specifying buffer and size. // Srb->SenseInfoBuffer = FdoExtension->SenseData; Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; // // Set timeout value of one unit per 64k bytes of data. // Srb->TimeOutValue = ((Srb->DataTransferLength + 0xFFFF) >> 16) * FdoExtension->TimeOutValue; // // Zero statuses. // Srb->SrbStatus = Srb->ScsiStatus = 0; Srb->NextSrb = 0; // // Indicate that 10-byte CDB's will be used. // Srb->CdbLength = 10; // // Fill in CDB fields. // cdb = (PCDB)Srb->Cdb; transferBlocks = (USHORT)(currentIrpStack->Parameters.Read.Length >> FdoExtension->SectorShift); // // Move little endian values into CDB in big endian format. // cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3; cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2; cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1; cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0; cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&transferBlocks)->Byte1; cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&transferBlocks)->Byte0; // // Set transfer direction flag and Cdb command. // if (currentIrpStack->MajorFunction == IRP_MJ_READ) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "ClassBuildRequest: Read Command\n")); SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN); cdb->CDB10.OperationCode = SCSIOP_READ; } else { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "ClassBuildRequest: Write Command\n")); SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT); cdb->CDB10.OperationCode = SCSIOP_WRITE; } // // If this is not a write-through request, then allow caching. // if (!(currentIrpStack->Flags & SL_WRITE_THROUGH)) { SET_FLAG(Srb->SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE); } else { // // If write caching is enable then force media access in the // cdb. // cdb->CDB10.ForceUnitAccess = FdoExtension->CdbForceUnitAccess; } if(TEST_FLAG(Irp->Flags, (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO))) { SET_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING); } // // OR in the default flags from the device object. // SET_FLAG(Srb->SrbFlags, FdoExtension->SrbFlags); // // Set up major SCSI function. // nextIrpStack->MajorFunction = IRP_MJ_SCSI; // // Save SRB address in next stack for port driver. // nextIrpStack->Parameters.Scsi.Srb = Srb; // // Save retry count in current IRP stack. // currentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; // // Set up IoCompletion routine address. // IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE); }
/*++//////////////////////////////////////////////////////////////////////////// ClassIoCompleteAssociated() Routine Description: This routine executes when the port driver has completed a request. It looks at the SRB status in the completing SRB and if not success it checks for valid request sense buffer information. If valid, the info is used to update status with more precise message of type of error. This routine deallocates the SRB. This routine is used for requests which were build by split request. After it has processed the request it decrements the Irp count in the master Irp. If the count goes to zero then the master Irp is completed. Arguments: Fdo - Supplies the functional device object which represents the target. Irp - Supplies the Irp which has completed. Context - Supplies a pointer to the SRB. Return Value: NT status --*/ NTSTATUS ClassIoCompleteAssociated( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN PVOID Context ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb = Context; PIRP originalIrp = Irp->AssociatedIrp.MasterIrp; LONG irpCount; NTSTATUS status; BOOLEAN retry; TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated is OBSOLETE !")); // // Check SRB status for success of completing request. // if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { LONGLONG retryInterval; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated: IRP %p, SRB %p", Irp, srb)); // // Release the queue if it is frozen. // if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { ClassReleaseQueue(Fdo); } retry = InterpretSenseInfoWithoutHistory( Fdo, Irp, srb, irpStack->MajorFunction, irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0, MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4), &status, &retryInterval); // // If the status is verified required and the this request // should bypass verify required then retry the request. // if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && status == STATUS_VERIFY_REQUIRED) { status = STATUS_IO_DEVICE_ERROR; retry = TRUE; } if (retry && ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4)--) { // // Retry request. If the class driver has supplied a StartIo, // call it directly for retries. // TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "Retry request %p\n", Irp)); if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); } RetryRequest(Fdo, Irp, srb, TRUE, retryInterval); return STATUS_MORE_PROCESSING_REQUIRED; } } else { // // Set status for successful request. // status = STATUS_SUCCESS; } // end if (SRB_STATUS(srb->SrbStatus) ... // // Return SRB to list. // if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); } ClassFreeOrReuseSrb(fdoExtension, srb); // // Set status in completing IRP. // Irp->IoStatus.Status = status; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated: Partial xfer IRP %p\n", Irp)); // // Get next stack location. This original request is unused // except to keep track of the completing partial IRPs so the // stack location is valid. // irpStack = IoGetNextIrpStackLocation(originalIrp); // // Update status only if error so that if any partial transfer // completes with error, then the original IRP will return with // error. If any of the asynchronous partial transfer IRPs fail, // with an error then the original IRP will return 0 bytes transfered. // This is an optimization for successful transfers. // if (!NT_SUCCESS(status)) { originalIrp->IoStatus.Status = status; originalIrp->IoStatus.Information = 0; // // Set the hard error if necessary. // if (IoIsErrorUserInduced(status) && (originalIrp->Tail.Overlay.Thread != NULL)) { // // Store DeviceObject for filesystem. // IoSetHardErrorOrVerifyDevice(originalIrp, Fdo); } } // // Decrement and get the count of remaining IRPs. // irpCount = InterlockedDecrement( (PLONG)&irpStack->Parameters.Others.Argument1); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated: Partial IRPs left %d\n", irpCount)); // // Ensure that the irpCount doesn't go negative. This was happening once // because classpnp would get confused if it ran out of resources when // splitting the request. // ASSERT(irpCount >= 0); if (irpCount == 0) { // // All partial IRPs have completed. // TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated: All partial IRPs complete %p\n", originalIrp)); if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) { // // Acquire a separate copy of the remove lock so the debugging code // works okay and we don't have to hold up the completion of this // irp until after we start the next packet(s). // KIRQL oldIrql; UCHAR uniqueAddress; ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddress); ClassReleaseRemoveLock(Fdo, originalIrp); ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT); KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); IoStartNextPacket(Fdo, TRUE); // yes, some IO is now cancellable KeLowerIrql(oldIrql); ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddress); } else { // // just complete this request // ClassReleaseRemoveLock(Fdo, originalIrp); ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT); } } // // Deallocate IRP and indicate the I/O system should not attempt any more // processing. // IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } // end ClassIoCompleteAssociated()
NTSTATUS DiskGenerateDeviceName( IN ULONG DeviceNumber, OUT PCCHAR *RawName ) /*++ Routine Description: This routine will allocate a unicode string buffer and then fill it in with a generated name for the specified device object. It is the responsibility of the user to allocate a UNICODE_STRING structure to pass in and to free UnicodeName->Buffer when done with it. Arguments: DeviceObject - a pointer to the device object UnicodeName - a unicode string to put the name buffer into Return Value: status --*/ #define FDO_NAME_FORMAT "\\Device\\Harddisk%d\\DR%d" { CHAR rawName[64] = { 0 }; NTSTATUS status; PAGED_CODE(); status = RtlStringCchPrintfA(rawName, sizeof(rawName) - 1, FDO_NAME_FORMAT, DeviceNumber, diskDeviceSequenceNumber++); if (!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskGenerateDeviceName: Format FDO name failed with error: 0x%X\n", status)); return status; } *RawName = ExAllocatePoolWithTag(PagedPool, strlen(rawName) + 1, DISK_TAG_NAME); if(*RawName == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } status = RtlStringCchCopyA(*RawName, strlen(rawName) + 1, rawName); if (!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskGenerateDeviceName: Device name copy failed with error: 0x%X\n", status)); FREE_POOL(*RawName); return status; } TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskGenerateDeviceName: generated \"%s\"\n", rawName)); return STATUS_SUCCESS; }
/*++ ClasspInitializeIdleTimer Routine Description: Initialize the idle timer for the given device. Arguments: FdoExtension - Pointer to the device extension IdleInterval - Timer interval Return Value: None --*/ VOID ClasspInitializeIdleTimer( PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ) { PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; ULONG idleInterval = CLASS_IDLE_INTERVAL; ULONG idlePrioritySupported = TRUE; ULONG activeIdleIoMax = 1; ClassGetDeviceParameter(FdoExtension, CLASSP_REG_SUBKEY_NAME, CLASSP_REG_IDLE_PRIORITY_SUPPORTED, &idlePrioritySupported); if (idlePrioritySupported == FALSE) { // // User has set the registry to disable idle priority for this disk. // No need to initialize any further. // Always ensure that none of the other fields used for idle priority // io are ever used without checking if it is supported. // TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspInitializeIdleTimer: Idle priority not supported for disk %p\n", FdoExtension)); fdoData->IdlePrioritySupported = FALSE; fdoData->IdleIoCount = 0; fdoData->ActiveIoCount = 0; return; } ClassGetDeviceParameter(FdoExtension, CLASSP_REG_SUBKEY_NAME, CLASSP_REG_IDLE_INTERVAL_NAME, &idleInterval); if ((idleInterval < CLASS_IDLE_TIMER_TICKS) || (idleInterval > USHORT_MAX)) { // // If the interval is too low or too high, reset it to the default value. // idleInterval = CLASS_IDLE_INTERVAL; } fdoData->IdlePrioritySupported = TRUE; KeInitializeSpinLock(&fdoData->IdleListLock); KeInitializeTimer(&fdoData->IdleTimer); KeInitializeDpc(&fdoData->IdleDpc, ClasspIdleTimerDpc, FdoExtension); InitializeListHead(&fdoData->IdleIrpList); fdoData->IdleTimerStarted = FALSE; fdoData->IdleTimerInterval = (USHORT) (idleInterval / CLASS_IDLE_TIMER_TICKS); fdoData->StarvationCount = CLASS_STARVATION_INTERVAL / fdoData->IdleTimerInterval; // // Due to the coarseness of the idle timer frequency, some variability in // the idle interval will be tolerated such that it is the desired idle // interval on average. fdoData->IdleInterval = (USHORT)(idleInterval - (fdoData->IdleTimerInterval / 2)); fdoData->IdleTimerTicks = 0; fdoData->IdleTicks = 0; fdoData->IdleIoCount = 0; fdoData->ActiveIoCount = 0; fdoData->ActiveIdleIoCount = 0; ClassGetDeviceParameter(FdoExtension, CLASSP_REG_SUBKEY_NAME, CLASSP_REG_IDLE_ACTIVE_MAX, &activeIdleIoMax); activeIdleIoMax = max(activeIdleIoMax, 1); activeIdleIoMax = min(activeIdleIoMax, USHORT_MAX); fdoData->IdleActiveIoMax = (USHORT)activeIdleIoMax; return; }
NTSTATUS DiskStartFdo( IN PDEVICE_OBJECT Fdo ) /*++ Routine Description: This routine will query the underlying device for any information necessary to complete initialization of the device. This will include physical disk geometry, mode sense information and such. This routine does not perform partition enumeration - that is left to the re-enumeration routine If this routine fails it will return an error value. It does not clean up any resources - that is left for the Stop/Remove routine. Arguments: Fdo - a pointer to the functional device object for this device Return Value: status --*/ { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PCOMMON_DEVICE_EXTENSION commonExtension = &(fdoExtension->CommonExtension); PDISK_DATA diskData = commonExtension->DriverData; STORAGE_HOTPLUG_INFO hotplugInfo = { 0 }; DISK_CACHE_INFORMATION cacheInfo = { 0 }; ULONG isPowerProtected = 0; NTSTATUS status; PAGED_CODE(); // // Get the hotplug information, so we can turn off write cache if needed // // NOTE: Capabilities info is not good enough to determine hotplugedness // as we cannot determine device relations information and other // dependencies. Get the hotplug info instead // { PIRP irp; KEVENT event; IO_STATUS_BLOCK statusBlock = { 0 }; KeInitializeEvent(&event, SynchronizationEvent, FALSE); irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_HOTPLUG_INFO, Fdo, NULL, 0L, &hotplugInfo, sizeof(STORAGE_HOTPLUG_INFO), FALSE, &event, &statusBlock); if (irp != NULL) { // send to self -- classpnp handles this status = IoCallDriver(Fdo, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = statusBlock.Status; } NT_ASSERT(NT_SUCCESS(status)); } } // // Clear the DEV_WRITE_CACHE flag now and set // it below only if we read that from the disk // CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); ADJUST_FUA_FLAG(fdoExtension); diskData->WriteCacheOverride = DiskWriteCacheDefault; // // Look into the registry to see if the user // has chosen to override the default setting // ClassGetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey, DiskDeviceUserWriteCacheSetting, (PULONG)&diskData->WriteCacheOverride); if (diskData->WriteCacheOverride == DiskWriteCacheDefault) { // // The user has not overridden the default settings // if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE)) { // // This flag indicates that we have faulty firmware and this // may cause the filesystem to refuse to mount on this media // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to a firmware issue\n", Fdo)); diskData->WriteCacheOverride = DiskWriteCacheDisable; } else if (hotplugInfo.DeviceHotplug && !hotplugInfo.WriteCacheEnableOverride) { // // This flag indicates that the device is hotpluggable making it unsafe to enable caching // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to hotpluggable device\n", Fdo)); diskData->WriteCacheOverride = DiskWriteCacheDisable; } else if (hotplugInfo.MediaHotplug) { // // This flag indicates that the media in the device cannot be reliably locked // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to unlockable media\n", Fdo)); diskData->WriteCacheOverride = DiskWriteCacheDisable; } else { // // Even though the device does not seem to have any obvious problems // we leave it to the user to modify the previous write cache setting // } } // // Query the disk to see if write cache is enabled // and set the DEV_WRITE_CACHE flag appropriately // status = DiskGetCacheInformation(fdoExtension, &cacheInfo); if (NT_SUCCESS(status)) { if (cacheInfo.WriteCacheEnabled == TRUE) { SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); ADJUST_FUA_FLAG(fdoExtension); if (diskData->WriteCacheOverride == DiskWriteCacheDisable) { // // Write cache is currently enabled on this // device, but we would like to turn it off // cacheInfo.WriteCacheEnabled = FALSE; DiskSetCacheInformation(fdoExtension, &cacheInfo); } } else { if (diskData->WriteCacheOverride == DiskWriteCacheEnable) { // // Write cache is currently disabled on this // device, but we would like to turn it on // cacheInfo.WriteCacheEnabled = TRUE; DiskSetCacheInformation(fdoExtension, &cacheInfo); } } } // // Query the registry to see if this disk is power-protected or not // CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED); ClassGetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey, DiskDeviceCacheIsPowerProtected, &isPowerProtected); if (isPowerProtected == 1) { SET_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED); } ADJUST_FUA_FLAG(fdoExtension); return STATUS_SUCCESS; } // end DiskStartFdo()
NTSTATUS PowerContextReuseRequest( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: reset fields for the request. Arguments: DeviceExtension - device context Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; WDF_REQUEST_REUSE_PARAMS reuseParams; PIRP irp = NULL; RtlZeroMemory(&(DeviceExtension->PowerContext.SenseData), sizeof(DeviceExtension->PowerContext.SenseData)); RtlZeroMemory(&(DeviceExtension->PowerContext.Srb), sizeof(DeviceExtension->PowerContext.Srb)); irp = WdfRequestWdmGetIrp(DeviceExtension->PowerContext.PowerRequest); // Re-use the previously created PowerRequest object and format it WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED); status = WdfRequestReuse(DeviceExtension->PowerContext.PowerRequest, &reuseParams); if (NT_SUCCESS(status)) { // This request was preformated during initialization so this call should never fail. status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, DeviceExtension->PowerContext.PowerRequest, IOCTL_SCSI_EXECUTE_IN, NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "PowerContextReuseRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n", status)); } } // Do some basic initialization of the PowerRequest, the rest will be done by the caller // of this function if (NT_SUCCESS(status)) { PIO_STACK_LOCATION nextStack = NULL; nextStack = IoGetNextIrpStackLocation(irp); nextStack->MajorFunction = IRP_MJ_SCSI; nextStack->Parameters.Scsi.Srb = &(DeviceExtension->PowerContext.Srb); DeviceExtension->PowerContext.Srb.Length = sizeof(SCSI_REQUEST_BLOCK); DeviceExtension->PowerContext.Srb.OriginalRequest = irp; DeviceExtension->PowerContext.Srb.SenseInfoBuffer = &(DeviceExtension->PowerContext.SenseData); DeviceExtension->PowerContext.Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE; } return status; }