//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: luext.c // //-------------------------------------------------------------------------- #include "ideport.h" static ULONG IdeDeviceUniqueId = 0; PPDO_EXTENSION RefPdo( PDEVICE_OBJECT PhysicalDeviceObject, BOOLEAN RemovedOk DECLARE_EXTRA_DEBUG_PARAMETER(PVOID, Tag) ) { PPDO_EXTENSION pdoExtension; PPDO_EXTENSION pdoExtension2Return; KIRQL currentIrql; pdoExtension = PhysicalDeviceObject->DeviceExtension; KeAcquireSpinLock(&pdoExtension->PdoSpinLock, ¤tIrql); pdoExtension2Return = RefPdoWithSpinLockHeldWithTag( PhysicalDeviceObject, RemovedOk, Tag ); if (pdoExtension2Return) { ASSERT(pdoExtension2Return == pdoExtension); } KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql); return pdoExtension2Return; } // RefPdo() PPDO_EXTENSION RefPdoWithSpinLockHeld( PDEVICE_OBJECT PhysicalDeviceObject, BOOLEAN RemovedOk DECLARE_EXTRA_DEBUG_PARAMETER(PVOID, Tag) ) { PPDO_EXTENSION pdoExtension; KIRQL currentIrql; pdoExtension = PhysicalDeviceObject->DeviceExtension; if (!(pdoExtension->PdoState & (PDOS_REMOVED | PDOS_DEADMEAT | PDOS_SURPRISE_REMOVED)) || RemovedOk) { IdeInterlockedIncrement ( pdoExtension, &pdoExtension->ReferenceCount, Tag ); } else { pdoExtension = NULL; } return pdoExtension; } // RefPdoWithSpinLockHeld() VOID UnrefPdo( PPDO_EXTENSION PdoExtension DECLARE_EXTRA_DEBUG_PARAMETER(PVOID, Tag) ) { UnrefLogicalUnitExtensionWithTag( PdoExtension->ParentDeviceExtension, PdoExtension, Tag ); } PPDO_EXTENSION RefLogicalUnitExtension( PFDO_EXTENSION DeviceExtension, UCHAR PathId, UCHAR TargetId, UCHAR Lun, BOOLEAN RemovedOk DECLARE_EXTRA_DEBUG_PARAMETER(PVOID, Tag) ) /*++ Routine Description: Walk logical unit extension list looking for extension with matching target id. Arguments: deviceExtension TargetId Return Value: Requested logical unit extension if found, else NULL. --*/ { PPDO_EXTENSION pdoExtension; PPDO_EXTENSION pdoExtension2Return = NULL; KIRQL currentIrql; if (TargetId >= DeviceExtension->HwDeviceExtension->MaxIdeTargetId) { return NULL; } KeAcquireSpinLock(&DeviceExtension->LogicalUnitListSpinLock, ¤tIrql); pdoExtension = DeviceExtension->LogicalUnitList[(TargetId + Lun) % NUMBER_LOGICAL_UNIT_BINS]; while (pdoExtension && !(pdoExtension->TargetId == TargetId && pdoExtension->Lun == Lun && pdoExtension->PathId == PathId)) { pdoExtension = pdoExtension->NextLogicalUnit; } if (pdoExtension) { pdoExtension2Return = RefPdoWithTag( pdoExtension->DeviceObject, RemovedOk, Tag ); } KeReleaseSpinLock(&DeviceExtension->LogicalUnitListSpinLock, currentIrql); return pdoExtension2Return; } // end RefLogicalUnitExtension() VOID UnrefLogicalUnitExtension( PFDO_EXTENSION FdoExtension, PPDO_EXTENSION PdoExtension DECLARE_EXTRA_DEBUG_PARAMETER(PVOID, Tag) ) { KIRQL currentIrql; LONG refCount; BOOLEAN deletePdo = FALSE; ULONG lockCount; ASSERT (PdoExtension); if (PdoExtension) { KeAcquireSpinLock(&PdoExtension->PdoSpinLock, ¤tIrql); ASSERT(PdoExtension->ReferenceCount > 0); lockCount = IdeInterlockedDecrement ( PdoExtension, &PdoExtension->ReferenceCount, Tag ); // ASSERT(lockCount >= 0); if (lockCount <= 0) { if ((PdoExtension->PdoState & PDOS_DEADMEAT) && (PdoExtension->PdoState & PDOS_REMOVED)) { deletePdo = TRUE; } } KeReleaseSpinLock(&PdoExtension->PdoSpinLock, currentIrql); if (deletePdo) { // IoDeleteDevice (PdoExtension->DeviceObject); KeSetEvent (&PdoExtension->RemoveEvent, 0, FALSE); } } } // UnrefLogicalUnitExtension(); PPDO_EXTENSION AllocatePdo( IN PFDO_EXTENSION FdoExtension, IN IDE_PATH_ID PathId DECLARE_EXTRA_DEBUG_PARAMETER(PVOID, Tag) ) /*++ Routine Description: Create logical unit extension. Arguments: DeviceExtension PathId Return Value: Logical unit extension --*/ { PDEVICE_OBJECT physicalDeviceObject; KIRQL currentIrql; PPDO_EXTENSION pdoExtension; ULONG size; ULONG bin; ULONG uniqueId; NTSTATUS status; UNICODE_STRING deviceName; WCHAR deviceNameBuffer[64]; PAGED_CODE(); uniqueId = InterlockedIncrement (&IdeDeviceUniqueId) - 1; swprintf(deviceNameBuffer, DEVICE_OJBECT_BASE_NAME L"\\IdeDeviceP%dT%dL%d-%x", FdoExtension->IdePortNumber, PathId.b.TargetId, PathId.b.Lun, uniqueId ); RtlInitUnicodeString(&deviceName, deviceNameBuffer); physicalDeviceObject = DeviceCreatePhysicalDeviceObject ( FdoExtension->DriverObject, FdoExtension, &deviceName ); if (physicalDeviceObject == NULL) { DebugPrint ((DBG_ALWAYS, "ATAPI: Unable to create device object\n", deviceNameBuffer)); return NULL; } pdoExtension = physicalDeviceObject->DeviceExtension; pdoExtension->AttacherDeviceObject = physicalDeviceObject; pdoExtension->PathId = (UCHAR) PathId.b.Path; pdoExtension->TargetId = (UCHAR) PathId.b.TargetId; pdoExtension->Lun = (UCHAR) PathId.b.Lun; // // Set timer counters in LogicalUnits to -1 to indicate no // outstanding requests. // pdoExtension->RequestTimeoutCounter = -1; // // This logical unit is be initialized // pdoExtension->LuFlags |= PD_RESCAN_ACTIVE; // // Allocate spin lock for critical sections. // KeInitializeSpinLock(&pdoExtension->PdoSpinLock); // // Initialize the request list. // InitializeListHead(&pdoExtension->SrbData.RequestList); // // Initialize a event // KeInitializeEvent ( &pdoExtension->RemoveEvent, NotificationEvent, FALSE ); // // Link logical unit extension on list. // bin = (PathId.b.TargetId + PathId.b.Lun) % NUMBER_LOGICAL_UNIT_BINS; // // get spinlock for accessing the logical unit extension bin // KeAcquireSpinLock(&FdoExtension->LogicalUnitListSpinLock, ¤tIrql); pdoExtension->NextLogicalUnit = FdoExtension->LogicalUnitList[bin]; // // Open the Command Log // IdeLogOpenCommandLog(&pdoExtension->SrbData); FdoExtension->LogicalUnitList[bin] = pdoExtension; FdoExtension->NumberOfLogicalUnits++; FdoExtension->NumberOfLogicalUnitsPowerUp++; IdeInterlockedIncrement ( pdoExtension, &pdoExtension->ReferenceCount, Tag ); KeReleaseSpinLock(&FdoExtension->LogicalUnitListSpinLock, currentIrql); return pdoExtension; } // end CreateLogicalUnitExtension() NTSTATUS FreePdo( IN PPDO_EXTENSION PdoExtension, IN BOOLEAN Sync, IN BOOLEAN CallIoDeleteDevice DECLARE_EXTRA_DEBUG_PARAMETER(PVOID, Tag) ) { PFDO_EXTENSION fdoExtension; PPDO_EXTENSION pdoExtension; KIRQL currentIrql; PLOGICAL_UNIT_EXTENSION lastPdoExtension; ULONG targetId; ULONG lun; LONG refCount; NTSTATUS status; targetId = PdoExtension->TargetId; lun = PdoExtension->Lun; fdoExtension = PdoExtension->ParentDeviceExtension; lastPdoExtension = NULL; // // get spinlock for accessing the logical unit extension bin // KeAcquireSpinLock(&fdoExtension->LogicalUnitListSpinLock, ¤tIrql); pdoExtension = fdoExtension->LogicalUnitList[(targetId + lun) % NUMBER_LOGICAL_UNIT_BINS]; while (pdoExtension != NULL) { if (pdoExtension == PdoExtension) { if (lastPdoExtension == NULL) { // // Remove from head of list. // fdoExtension->LogicalUnitList[(targetId + lun) % NUMBER_LOGICAL_UNIT_BINS] = pdoExtension->NextLogicalUnit; } else { lastPdoExtension->NextLogicalUnit = pdoExtension->NextLogicalUnit; } ASSERT (!(pdoExtension->PdoState & PDOS_LEGACY_ATTACHER)); if (pdoExtension->ReferenceCount > 1) { DebugPrint ((0, "IdePort FreePdo: pdoe 0x%x ReferenceCount is 0x%x\n", pdoExtension, pdoExtension->ReferenceCount)); } fdoExtension->NumberOfLogicalUnits--; // // only if pdo is freed while it is powered up // if (pdoExtension->DevicePowerState <= PowerDeviceD0) { fdoExtension->NumberOfLogicalUnitsPowerUp--; } KeReleaseSpinLock(&fdoExtension->LogicalUnitListSpinLock, currentIrql); break; } lastPdoExtension = pdoExtension; pdoExtension = pdoExtension->NextLogicalUnit; } if (pdoExtension) { ASSERT (pdoExtension == PdoExtension); KeAcquireSpinLock(&pdoExtension->PdoSpinLock, ¤tIrql); // // better not attached by a legacy device // ASSERT (!(pdoExtension->PdoState & PDOS_LEGACY_ATTACHER)); // // lower the refer count for the caller // and save the new refCount // ASSERT(pdoExtension->ReferenceCount > 0); refCount = IdeInterlockedDecrement ( pdoExtension, &pdoExtension->ReferenceCount, Tag ); // // no more new request // pdoExtension->PdoState |= PDOS_DEADMEAT | PDOS_REMOVED; KeReleaseSpinLock(&pdoExtension->PdoSpinLock, currentIrql); // // remove idle detection timer if any // DeviceUnregisterIdleDetection (PdoExtension); // // free acpi data // if (PdoExtension->AcpiDeviceSettings) { ExFreePool(PdoExtension->AcpiDeviceSettings); PdoExtension->AcpiDeviceSettings = NULL; } // // flush the requests in the queue // IdePortFlushLogicalUnit ( fdoExtension, PdoExtension, TRUE ); if (refCount) { if (Sync) { status = KeWaitForSingleObject(&pdoExtension->RemoveEvent, Executive, KernelMode, FALSE, NULL); } } if (CallIoDeleteDevice) { // // Free command log if it was allocated // IdeLogFreeCommandLog(&PdoExtension->SrbData); IoDeleteDevice (pdoExtension->DeviceObject); } return STATUS_SUCCESS; } else { KeReleaseSpinLock(&fdoExtension->LogicalUnitListSpinLock, currentIrql); if (CallIoDeleteDevice) { DebugPrint (( DBG_PNP, "ideport: deleting device 0x%x that was PROBABLY surprise removed\n", PdoExtension->DeviceObject )); //ASSERT (PdoExtension->PdoState & PDOS_SURPRISE_REMOVED); // // delete the device if it wasn't removed before. // PDOS_REMOVED could be set, if the device was surprise // removed. In that case remove the device // if (!(PdoExtension->PdoState & PDOS_REMOVED) || PdoExtension->PdoState & PDOS_SURPRISE_REMOVED) { // // Free command log if it was allocated // IdeLogFreeCommandLog(&PdoExtension->SrbData); IoDeleteDevice (PdoExtension->DeviceObject); } } return STATUS_SUCCESS; } } // end FreeLogicalUnitExtension() PLOGICAL_UNIT_EXTENSION NextLogUnitExtension( IN PFDO_EXTENSION FdoExtension, IN OUT PIDE_PATH_ID PathId, IN BOOLEAN RemovedOk DECLARE_EXTRA_DEBUG_PARAMETER(PVOID, Tag) ) { PLOGICAL_UNIT_EXTENSION logUnitExtension; logUnitExtension = NULL; for (; !logUnitExtension && (PathId->b.Path < MAX_IDE_PATH); PathId->b.Path++, PathId->b.TargetId = 0) { for (; !logUnitExtension && (PathId->b.TargetId < FdoExtension->HwDeviceExtension->MaxIdeTargetId); PathId->b.TargetId++, PathId->b.Lun = 0) { logUnitExtension = RefLogicalUnitExtensionWithTag ( FdoExtension, (UCHAR) PathId->b.Path, (UCHAR) PathId->b.TargetId, (UCHAR) PathId->b.Lun, RemovedOk, Tag ); if (logUnitExtension) { // // increment lun for the next time around // PathId->b.Lun++; return logUnitExtension; } // // Assume Lun number never skips. // If we can't find the logical unit extension for a lun, // will go to the next target ID with lun 0 // } } return NULL; } // end NextLogicalUnitExtension() VOID KillPdo( IN PPDO_EXTENSION PdoExtension ) { KIRQL currentIrql; ASSERT (PdoExtension); KeAcquireSpinLock(&PdoExtension->PdoSpinLock, ¤tIrql); ASSERT (!(PdoExtension->PdoState & PDOS_DEADMEAT)); SETMASK (PdoExtension->PdoState, PDOS_DEADMEAT); IdeLogDeadMeatReason( PdoExtension->DeadmeatRecord.Reason, byKilledPdo ); KeReleaseSpinLock(&PdoExtension->PdoSpinLock, currentIrql); } #if DBG PVOID IdePortInterestedLockTag=NULL; LONG IdeInterlockedIncrement ( IN PPDO_EXTENSION PdoExtension, IN PLONG Addend, IN PVOID Tag ) { ULONG i; KIRQL currentIrql; DebugPrint (( DBG_PDO_LOCKTAG, ">>>>>>>>>>>>>>>>>>>> Acquire PdoLock with tag = 0x%x\n", Tag )); if (IdePortInterestedLockTag == Tag) { DebugPrint ((DBG_ALWAYS, "Found the interested lock tag 0x%x\n", Tag)); DbgBreakPoint(); } KeAcquireSpinLock(&PdoExtension->RefCountSpinLock, ¤tIrql); if (PdoExtension->NumTagUsed >= TAG_TABLE_SIZE) { DebugPrint ((DBG_ALWAYS, "Used up all %d tag\n", TAG_TABLE_SIZE)); DbgBreakPoint(); } for (i=0; iNumTagUsed; i++) { if (PdoExtension->TagTable[i] == Tag) { DebugPrint ((DBG_ALWAYS, "Tag 0x%x already in used\n", Tag)); DbgBreakPoint(); } } PdoExtension->TagTable[PdoExtension->NumTagUsed] = Tag; PdoExtension->NumTagUsed++; KeReleaseSpinLock(&PdoExtension->RefCountSpinLock, currentIrql); return InterlockedIncrement (Addend); } LONG IdeInterlockedDecrement ( IN PPDO_EXTENSION PdoExtension, IN PLONG Addend, IN PVOID Tag ) { ULONG i; KIRQL currentIrql; BOOLEAN foundTag; DebugPrint (( DBG_PDO_LOCKTAG, ">>>>>>>>>>>>>>>>>>>> Release PdoLock with tag = 0x%x\n", Tag )); KeAcquireSpinLock(&PdoExtension->RefCountSpinLock, ¤tIrql); for (i=0, foundTag=FALSE; iNumTagUsed; i++) { if (PdoExtension->TagTable[i] == Tag) { if (PdoExtension->NumTagUsed > 1) { PdoExtension->TagTable[i] = PdoExtension->TagTable[PdoExtension->NumTagUsed - 1]; } PdoExtension->NumTagUsed--; foundTag = TRUE; break; } } if (!foundTag) { DebugPrint ((DBG_ALWAYS, "Unable to find tag 0x%x\n", Tag)); DbgBreakPoint(); } KeReleaseSpinLock(&PdoExtension->RefCountSpinLock, currentIrql); return InterlockedDecrement (Addend); } #endif //DBG