You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
698 lines
18 KiB
698 lines
18 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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; i<PdoExtension->NumTagUsed; 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; i<PdoExtension->NumTagUsed; 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
|
|
|