mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-26 19:33:31 +00:00
IoRegisterDeviceInterface() is taking a PDO as argument. Otherwise, it is failing with STATUS_INVALID_DEVICE_REQUEST.
759 lines
23 KiB
C
759 lines
23 KiB
C
/*
|
|
* PROJECT: ReactOS ATA Port Driver
|
|
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
|
* PURPOSE: ATA channel device object (FDO) dispatch routines
|
|
* COPYRIGHT: Copyright 2026 Dmitry Borisov <[email protected]>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "atapi.h"
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
DECLARE_PAGED_WSTRING(AtapDevSymLinkFormat, L"\\Device\\ScsiPort%lu");
|
|
DECLARE_PAGED_WSTRING(AtapDosSymLinkFormat, L"\\DosDevices\\Scsi%lu:");
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
AtaFdoCreateSymLinks(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
|
{
|
|
UNICODE_STRING ChannelName;
|
|
ULONG ScsiAdapter, ScsiPortCount;
|
|
WCHAR ChannelNameBuffer[sizeof("\\Device\\Ide\\IdePort99999")];
|
|
NTSTATUS Status;
|
|
DECLARE_PAGED_WSTRING(FdoFormat, L"\\Device\\Ide\\IdePort%lu");
|
|
|
|
PAGED_CODE();
|
|
|
|
if (ChanExt->PortData.PortFlags & PORT_FLAG_SYMLINK_CREATED)
|
|
return STATUS_SUCCESS;
|
|
|
|
Status = RtlStringCbPrintfW(ChannelNameBuffer,
|
|
sizeof(ChannelNameBuffer),
|
|
FdoFormat,
|
|
ChanExt->DeviceObjectNumber);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
RtlInitUnicodeString(&ChannelName, ChannelNameBuffer);
|
|
|
|
ScsiPortCount = IoGetConfigurationInformation()->ScsiPortCount;
|
|
|
|
/* Search for a free SCSI port adapter in the system */
|
|
for (ScsiAdapter = 0; ScsiAdapter <= ScsiPortCount; ++ScsiAdapter)
|
|
{
|
|
WCHAR SymLinkNameBuffer[sizeof("\\DosDevices\\Scsi99999:")];
|
|
UNICODE_STRING SymLinkName;
|
|
|
|
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
|
|
sizeof(SymLinkNameBuffer),
|
|
AtapDevSymLinkFormat,
|
|
ScsiAdapter);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
|
|
|
|
/* Create a symbolic link '\Device\ScsiPortX' -> '\Device\Ide\IdePortN' */
|
|
Status = IoCreateSymbolicLink(&SymLinkName, &ChannelName);
|
|
if (!NT_SUCCESS(Status))
|
|
continue;
|
|
|
|
INFO("Symlink created '%wZ' -> '%wZ'\n", &SymLinkName, &ChannelName);
|
|
|
|
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
|
|
sizeof(SymLinkNameBuffer),
|
|
AtapDosSymLinkFormat,
|
|
ScsiAdapter);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
|
|
|
|
/* Create a symbolic link '\DosDevices\ScsiX:' -> '\Device\Ide\IdePortN' */
|
|
Status = IoCreateSymbolicLink(&SymLinkName, &ChannelName);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
INFO("Symlink created '%wZ' -> '%wZ'\n", &SymLinkName, &ChannelName);
|
|
}
|
|
|
|
/* Register ourselves (ATA channel) as a SCSI port adapter */
|
|
IoGetConfigurationInformation()->ScsiPortCount++;
|
|
|
|
ChanExt->ScsiPortNumber = ScsiAdapter;
|
|
ChanExt->PortData.PortFlags |= PORT_FLAG_SYMLINK_CREATED;
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
AtaFdoAllocateLocalBuffer(
|
|
_In_ PATAPORT_PORT_DATA PortData)
|
|
{
|
|
PHYSICAL_ADDRESS PhysicalAddress;
|
|
PHYSICAL_ADDRESS HighestAcceptableAddress;
|
|
PHYSICAL_ADDRESS LowestAcceptableAddress;
|
|
PHYSICAL_ADDRESS BoundaryAddressMultiple;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PortData->LocalBuffer)
|
|
return STATUS_SUCCESS;
|
|
|
|
LowestAcceptableAddress.QuadPart = 0;
|
|
HighestAcceptableAddress.QuadPart = 0xFFFFFFFF; // 32-bit DMA
|
|
BoundaryAddressMultiple.QuadPart = 0x10000; // 64k, for PATA compability
|
|
PortData->LocalBuffer = MmAllocateContiguousMemorySpecifyCache(ATA_LOCAL_BUFFER_SIZE,
|
|
LowestAcceptableAddress,
|
|
HighestAcceptableAddress,
|
|
BoundaryAddressMultiple,
|
|
MmNonCached);
|
|
if (!PortData->LocalBuffer)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
PhysicalAddress = MmGetPhysicalAddress(PortData->LocalBuffer);
|
|
|
|
PortData->LocalSgList.NumberOfElements = 1;
|
|
PortData->LocalSgList.Elements[0].Length = ATA_LOCAL_BUFFER_SIZE;
|
|
PortData->LocalSgList.Elements[0].Address.QuadPart = PhysicalAddress.QuadPart;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
AtaFdoCreatePortThread(
|
|
_In_ PATAPORT_PORT_DATA PortData)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, OBJ_KERNEL_HANDLE);
|
|
HANDLE ThreadHandle;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PortData->Worker.Thread)
|
|
return STATUS_SUCCESS;
|
|
|
|
KeInitializeEvent(&PortData->Worker.ThreadEvent, NotificationEvent, FALSE);
|
|
|
|
Status = PsCreateSystemThread(&ThreadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
NULL,
|
|
NULL,
|
|
AtaPortWorkerThread,
|
|
PortData);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
NULL,
|
|
KernelMode,
|
|
(PVOID*)&PortData->Worker.Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
ZwClose(ThreadHandle);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaFdoDestroyPortThread(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
|
{
|
|
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!PortData->Worker.Thread)
|
|
return;
|
|
|
|
PortData->PortFlags |= PORT_FLAG_EXIT_THREAD;
|
|
AtaPortSignalWorkerThread(PortData);
|
|
|
|
KeWaitForSingleObject(PortData->Worker.Thread, Executive, KernelMode, FALSE, NULL);
|
|
|
|
ObDereferenceObject(PortData->Worker.Thread);
|
|
|
|
PortData->Worker.Thread = NULL;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaFdoRemoveSymLinks(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING SymLinkName;
|
|
WCHAR SymLinkNameBuffer[sizeof("\\DosDevices\\Scsi99999:")];
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!(ChanExt->PortData.PortFlags & PORT_FLAG_SYMLINK_CREATED))
|
|
return;
|
|
|
|
/* Delete the '\DosDevices\\ScsiX:' symbolic link */
|
|
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
|
|
sizeof(SymLinkNameBuffer),
|
|
AtapDevSymLinkFormat,
|
|
ChanExt->ScsiPortNumber);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
|
|
(VOID)IoDeleteSymbolicLink(&SymLinkName);
|
|
|
|
/* Delete the '\Device\\ScsiPortX' symbolic link */
|
|
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
|
|
sizeof(SymLinkNameBuffer),
|
|
AtapDosSymLinkFormat,
|
|
ChanExt->ScsiPortNumber);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
|
|
(VOID)IoDeleteSymbolicLink(&SymLinkName);
|
|
|
|
/* Unregister the SCSI port adapter */
|
|
IoGetConfigurationInformation()->ScsiPortCount--;
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
AtaFdoStartDevice(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_In_ PCM_RESOURCE_LIST ResourcesTranslated)
|
|
{
|
|
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
|
PCIIDEX_CHANNEL_INTERFACE ChannelInterface = { 0 };
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
INFO("Starting channel %lu\n", ChanExt->DeviceObjectNumber);
|
|
|
|
/* Get the interface of ATA channel */
|
|
Status = AtaPnpQueryInterface(&ChanExt->Common,
|
|
&GUID_PCIIDE_INTERFACE_ROS,
|
|
&ChannelInterface,
|
|
PCIIDEX_INTERFACE_VERSION,
|
|
sizeof(ChannelInterface));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to query channel interface %lx\n", Status);
|
|
return Status;
|
|
}
|
|
PortData->ChannelContext = ChannelInterface.ChannelContext;
|
|
PortData->AttachChannel = ChannelInterface.AttachChannel;
|
|
PortData->SetDeviceData = ChannelInterface.SetDeviceData;
|
|
PortData->GetInitTaskFile = ChannelInterface.GetInitTaskFile;
|
|
PortData->DowngradeInterfaceSpeed = ChannelInterface.DowngradeInterfaceSpeed;
|
|
PortData->InterruptObject = ChannelInterface.InterruptObject;
|
|
PortData->AbortChannel = ChannelInterface.AbortChannel;
|
|
PortData->ResetChannel = ChannelInterface.ResetChannel;
|
|
PortData->EnumerateChannel = ChannelInterface.EnumerateChannel;
|
|
PortData->IdentifyDevice = ChannelInterface.IdentifyDevice;
|
|
PortData->SetTransferMode = ChannelInterface.SetTransferMode;
|
|
PortData->AllocateSlot = ChannelInterface.AllocateSlot;
|
|
PortData->PreparePrdTable = ChannelInterface.PreparePrdTable;
|
|
PortData->PrepareIo = ChannelInterface.PrepareIo;
|
|
PortData->StartIo = ChannelInterface.StartIo;
|
|
PortData->MaxTargetId = ChannelInterface.MaxTargetId;
|
|
PortData->MaximumTransferLength = ChannelInterface.MaximumTransferLength;
|
|
PortData->MaximumPhysicalPages = ChannelInterface.MaximumPhysicalPages;
|
|
PortData->QueueDepth = ChannelInterface.QueueDepth;
|
|
PortData->PortNumber = ChannelInterface.Channel;
|
|
PortData->DmaAdapter = ChannelInterface.DmaAdapter;
|
|
PortData->ChannelObject = ChannelInterface.ChannelObject;
|
|
|
|
PortData->InterruptFlags = PORT_INT_FLAG_IS_IO_ACTIVE;
|
|
PortData->FreeSlotsBitmap =
|
|
PortData->MaxSlotsBitmap = NUM_TO_BITMAP(PortData->QueueDepth);
|
|
/* We need the slot numbers to start from zero */
|
|
PortData->LastUsedSlot = RTL_BITS_OF(ULONG) - 1;
|
|
|
|
if (!(ChannelInterface.TransferModeSupported & ~PIO_ALL))
|
|
PortData->PortFlags |= PORT_FLAG_PIO_ONLY;
|
|
|
|
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_VIA_DMA)
|
|
PortData->PortFlags |= PORT_FLAG_PIO_VIA_DMA;
|
|
|
|
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_NCQ)
|
|
PortData->PortFlags |= PORT_FLAG_NCQ;
|
|
|
|
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_IS_AHCI)
|
|
PortData->PortFlags |= PORT_FLAG_IS_AHCI;
|
|
|
|
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_IS_EXTERNAL)
|
|
PortData->PortFlags |= PORT_FLAG_IS_EXTERNAL;
|
|
|
|
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_FOR_LBA48_XFER)
|
|
PortData->PortFlags |= PORT_FLAG_PIO_FOR_LBA48_XFER;
|
|
|
|
if (ChannelInterface.HwSyncObject)
|
|
{
|
|
PortData->PortFlags |= PORT_FLAG_IS_SIMPLEX;
|
|
PortData->HwSyncObject = ChannelInterface.HwSyncObject;
|
|
}
|
|
|
|
/* Reserve PIO memory resources early. Storage drivers should not fail paging I/O operations */
|
|
if (!(ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_VIA_DMA) && !PortData->ReservedVaSpace)
|
|
{
|
|
PortData->ReservedVaSpace = MmAllocateMappingAddress(ATA_RESERVED_PAGES * PAGE_SIZE,
|
|
ATAPORT_TAG);
|
|
}
|
|
|
|
Status = AtaFdoAllocateLocalBuffer(PortData);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("CH %lu: Failed to allocate local buffer 0x%lx\n", PortData->PortNumber, Status);
|
|
return Status;
|
|
}
|
|
|
|
InitializeListHead(&PortData->PortQueueList);
|
|
KeInitializeEvent(&PortData->QueueStoppedEvent, NotificationEvent, FALSE);
|
|
|
|
KeInitializeDpc(&PortData->Worker.Dpc, AtaPortWorkerSignalDpc, PortData);
|
|
KeInitializeDpc(&PortData->Worker.NotificationDpc, AtaStorageNotificationlDpc, PortData);
|
|
KeInitializeSpinLock(&PortData->Worker.Lock);
|
|
KeInitializeEvent(&PortData->Worker.EnumerationEvent, NotificationEvent, FALSE);
|
|
|
|
KeInitializeEvent(&PortData->Worker.CompletionEvent, NotificationEvent, FALSE);
|
|
PortData->Worker.InternalRequest.Complete = AtaPortCompleteInternalRequest;
|
|
#if DBG
|
|
PortData->Worker.InternalRequest.Signature = ATA_DEVICE_REQUEST_SIGNATURE;
|
|
#endif
|
|
|
|
Status = AtaFdoCreatePortThread(PortData);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("CH %lu: Failed to create port thread 0x%lx\n", PortData->PortNumber, Status);
|
|
return Status;
|
|
}
|
|
|
|
Status = AtaFdoCreateSymLinks(ChanExt);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("CH %lu: Failed to create symbolic links 0x%lx\n", PortData->PortNumber, Status);
|
|
return Status;
|
|
}
|
|
|
|
AtaSetPortRegistryKey(ChanExt, DD_ATA_REG_MAX_TARGET_ID, PortData->MaxTargetId);
|
|
|
|
Status = IoRegisterDeviceInterface(ChanExt->Pdo,
|
|
&GUID_DEVINTERFACE_STORAGEPORT,
|
|
NULL,
|
|
&ChanExt->StorageInterfaceName);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
INFO("InterfaceName: '%wZ'\n", &ChanExt->StorageInterfaceName);
|
|
|
|
Status = IoSetDeviceInterfaceState(&ChanExt->StorageInterfaceName, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RtlFreeUnicodeString(&ChanExt->StorageInterfaceName);
|
|
ChanExt->StorageInterfaceName.Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
*ChannelInterface.PortContext = PortData;
|
|
*ChannelInterface.PortNotification = AtaPortNotification;
|
|
*ChannelInterface.Slots = PortData->Slots;
|
|
|
|
Status = IoInitializeTimer(ChanExt->Common.Self, AtaPortIoTimer, PortData);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
IoStartTimer(ChanExt->Common.Self);
|
|
PortData->PortFlags |= PORT_FLAG_IO_TIMER_ACTIVE;
|
|
|
|
Status = PortData->AttachChannel(PortData->ChannelContext, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("CH %lu: Failed to attach channel %lx\n", PortData->PortNumber, Status);
|
|
return Status;
|
|
}
|
|
PortData->PortFlags |= PORT_FLAG_CHANNEL_ATTACHED;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaFdoDetachChannel(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
|
{
|
|
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PortData->AttachChannel)
|
|
{
|
|
PortData->AttachChannel(PortData->ChannelContext, FALSE);
|
|
PortData->AttachChannel = NULL;
|
|
}
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
AtaFdoStopDevice(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_In_ PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
AtaFdoDetachChannel(ChanExt);
|
|
|
|
if (ChanExt->StorageInterfaceName.Buffer)
|
|
IoSetDeviceInterfaceState(&ChanExt->StorageInterfaceName, FALSE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
AtaFdoRemoveDevice(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_In_ PIRP Irp,
|
|
_In_ BOOLEAN FinalRemove)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (ChanExt->StorageInterfaceName.Buffer)
|
|
{
|
|
IoSetDeviceInterfaceState(&ChanExt->StorageInterfaceName, FALSE);
|
|
|
|
RtlFreeUnicodeString(&ChanExt->StorageInterfaceName);
|
|
ChanExt->StorageInterfaceName.Buffer = NULL;
|
|
}
|
|
|
|
AtaFdoDestroyPortThread(ChanExt);
|
|
|
|
if (ChanExt->PortData.PortFlags & PORT_FLAG_IO_TIMER_ACTIVE)
|
|
{
|
|
IoStopTimer(ChanExt->Common.Self);
|
|
ChanExt->PortData.PortFlags &= ~PORT_FLAG_IO_TIMER_ACTIVE;
|
|
}
|
|
|
|
if (ChanExt->PortData.PortFlags & PORT_FLAG_CHANNEL_ATTACHED)
|
|
{
|
|
AtaFdoDetachChannel(ChanExt);
|
|
ChanExt->PortData.PortFlags &= ~PORT_FLAG_CHANNEL_ATTACHED;
|
|
}
|
|
|
|
if (FinalRemove)
|
|
{
|
|
ATA_SCSI_ADDRESS AtaScsiAddress;
|
|
|
|
IoReleaseRemoveLockAndWait(&ChanExt->Common.RemoveLock, Irp);
|
|
|
|
AtaScsiAddress.AsULONG = 0;
|
|
while (TRUE)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt;
|
|
|
|
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, TRUE, NULL);
|
|
if (!DevExt)
|
|
break;
|
|
|
|
AtaReqFlushDeviceQueue(&DevExt->Device);
|
|
AtaPdoFreeDevice(DevExt);
|
|
}
|
|
|
|
AtaFdoRemoveSymLinks(ChanExt);
|
|
|
|
if (ChanExt->PortData.LocalBuffer)
|
|
{
|
|
MmFreeContiguousMemorySpecifyCache(ChanExt->PortData.LocalBuffer,
|
|
ATA_LOCAL_BUFFER_SIZE,
|
|
MmNonCached);
|
|
ChanExt->PortData.LocalBuffer = NULL;
|
|
}
|
|
|
|
if (ChanExt->PortData.ReservedVaSpace)
|
|
{
|
|
MmFreeMappingAddress(ChanExt->PortData.ReservedVaSpace, ATAPORT_TAG);
|
|
ChanExt->PortData.ReservedVaSpace = NULL;
|
|
}
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
Status = IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
|
|
|
IoDetachDevice(ChanExt->Common.LowerDeviceObject);
|
|
IoDeleteDevice(ChanExt->Common.Self);
|
|
}
|
|
else
|
|
{
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
Status = IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
|
|
|
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
AtaFdoPnp(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_Inout_ PIRP Irp)
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IoStack;
|
|
|
|
PAGED_CODE();
|
|
|
|
INFO("(%p, %p) Ch.%lu %s\n",
|
|
ChanExt->Common.Self,
|
|
Irp,
|
|
ChanExt->DeviceObjectNumber,
|
|
GetIRPMinorFunctionString(IoGetCurrentIrpStackLocation(Irp)->MinorFunction));
|
|
|
|
Status = IoAcquireRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
|
}
|
|
|
|
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
|
switch (IoStack->MinorFunction)
|
|
{
|
|
case IRP_MN_START_DEVICE:
|
|
{
|
|
if (!NT_VERIFY(IoForwardIrpSynchronously(ChanExt->Common.LowerDeviceObject, Irp)))
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto CompleteIrp;
|
|
}
|
|
Status = Irp->IoStatus.Status;
|
|
if (!NT_SUCCESS(Status))
|
|
goto CompleteIrp;
|
|
|
|
Status = AtaFdoStartDevice(ChanExt,
|
|
IoStack->Parameters.
|
|
StartDevice.AllocatedResourcesTranslated);
|
|
goto CompleteIrp;
|
|
}
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
Status = AtaFdoStopDevice(ChanExt, Irp);
|
|
break;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
return AtaFdoRemoveDevice(ChanExt,
|
|
Irp,
|
|
(IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE));
|
|
|
|
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
|
Status = AtaPnpQueryPnpDeviceState(&ChanExt->Common, Irp);
|
|
break;
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
{
|
|
if (IoStack->Parameters.QueryDeviceRelations.Type != BusRelations)
|
|
break;
|
|
|
|
Status = AtaFdoQueryBusRelations(ChanExt, Irp);
|
|
if (!NT_SUCCESS(Status))
|
|
goto CompleteIrp;
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
|
|
Status = AtaPnpQueryDeviceUsageNotification(&ChanExt->Common, Irp);
|
|
break;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
IoSkipCurrentIrpStackLocation(Irp);
|
|
Status = IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
|
|
|
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
|
|
|
return Status;
|
|
|
|
CompleteIrp:
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
DECLSPEC_NOINLINE_FROM_PAGED
|
|
PATAPORT_DEVICE_EXTENSION
|
|
AtaFdoFindDeviceByPath(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_In_ ATA_SCSI_ADDRESS AtaScsiAddress,
|
|
_In_ PVOID ReferenceTag)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt, Result = NULL;
|
|
PSINGLE_LIST_ENTRY Entry;
|
|
KIRQL OldLevel;
|
|
NTSTATUS Status;
|
|
|
|
KeAcquireSpinLock(&ChanExt->PdoListLock, &OldLevel);
|
|
|
|
for (Entry = ChanExt->PdoList.Next; Entry != NULL; Entry = Entry->Next)
|
|
{
|
|
DevExt = CONTAINING_RECORD(Entry, ATAPORT_DEVICE_EXTENSION, ListEntry);
|
|
|
|
if (DevExt->Device.AtaScsiAddress.AsULONG != AtaScsiAddress.AsULONG)
|
|
continue;
|
|
|
|
if (DevExt->ReportedMissing || DevExt->RemovalPending)
|
|
continue;
|
|
|
|
if (ReferenceTag)
|
|
{
|
|
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, ReferenceTag);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
}
|
|
|
|
Result = DevExt;
|
|
break;
|
|
}
|
|
|
|
KeReleaseSpinLock(&ChanExt->PdoListLock, OldLevel);
|
|
|
|
return Result;
|
|
}
|
|
|
|
DECLSPEC_NOINLINE_FROM_PAGED
|
|
PATAPORT_DEVICE_EXTENSION
|
|
AtaFdoFindNextDeviceByPath(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_Inout_ PATA_SCSI_ADDRESS AtaScsiAddress,
|
|
_In_ BOOLEAN SearchRemoveDev,
|
|
_In_ PVOID ReferenceTag)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt, Result = NULL;
|
|
PSINGLE_LIST_ENTRY Entry;
|
|
KIRQL OldLevel;
|
|
NTSTATUS Status;
|
|
|
|
KeAcquireSpinLock(&ChanExt->PdoListLock, &OldLevel);
|
|
|
|
for (Entry = ChanExt->PdoList.Next; Entry != NULL; Entry = Entry->Next)
|
|
{
|
|
DevExt = CONTAINING_RECORD(Entry, ATAPORT_DEVICE_EXTENSION, ListEntry);
|
|
|
|
if (DevExt->Device.AtaScsiAddress.AsULONG <= AtaScsiAddress->AsULONG)
|
|
continue;
|
|
|
|
if (DevExt->ReportedMissing)
|
|
continue;
|
|
|
|
if (!SearchRemoveDev && DevExt->RemovalPending)
|
|
continue;
|
|
|
|
*AtaScsiAddress = DevExt->Device.AtaScsiAddress;
|
|
|
|
if (ReferenceTag)
|
|
{
|
|
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, ReferenceTag);
|
|
if (!NT_SUCCESS(Status))
|
|
continue;
|
|
}
|
|
Result = DevExt;
|
|
break;
|
|
}
|
|
|
|
KeReleaseSpinLock(&ChanExt->PdoListLock, OldLevel);
|
|
|
|
return Result;
|
|
}
|
|
|
|
DECLSPEC_NOINLINE_FROM_PAGED
|
|
VOID
|
|
AtaFdoDeviceListInsert(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ BOOLEAN DoInsert)
|
|
{
|
|
PSINGLE_LIST_ENTRY Entry, PrevEntry;
|
|
KIRQL OldLevel;
|
|
ULONG Address = DevExt->Device.AtaScsiAddress.AsULONG;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeAcquireSpinLock(&ChanExt->PdoListLock, &OldLevel);
|
|
|
|
for (Entry = ChanExt->PdoList.Next, PrevEntry = NULL;
|
|
Entry != NULL;
|
|
Entry = Entry->Next)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION CurrentDevExt;
|
|
|
|
CurrentDevExt = CONTAINING_RECORD(Entry, ATAPORT_DEVICE_EXTENSION, ListEntry);
|
|
|
|
if (DoInsert)
|
|
{
|
|
if (CurrentDevExt->Device.AtaScsiAddress.AsULONG > Address)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (CurrentDevExt->Device.AtaScsiAddress.AsULONG == Address)
|
|
break;
|
|
}
|
|
|
|
PrevEntry = Entry;
|
|
}
|
|
|
|
/* The device list is ordered by SCSI address (Path:Target:Lun), smallest first */
|
|
if (PrevEntry)
|
|
{
|
|
/* Before the current entry */
|
|
if (DoInsert)
|
|
{
|
|
DevExt->ListEntry.Next = PrevEntry->Next;
|
|
PrevEntry->Next = &DevExt->ListEntry;
|
|
}
|
|
else
|
|
{
|
|
PrevEntry->Next = DevExt->ListEntry.Next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* In the beginning */
|
|
if (DoInsert)
|
|
PushEntryList(&ChanExt->PdoList, &DevExt->ListEntry);
|
|
else
|
|
PopEntryList(&DevExt->ListEntry);
|
|
}
|
|
|
|
KeReleaseSpinLock(&ChanExt->PdoListLock, OldLevel);
|
|
}
|