mirror of
https://github.com/reactos/reactos.git
synced 2026-05-24 08:11:38 +08:00
659 lines
19 KiB
C
659 lines
19 KiB
C
/*
|
|
* PROJECT: ReactOS ATA Port Driver
|
|
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
|
* PURPOSE: ATA bus enumeration
|
|
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "atapi.h"
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
PUCHAR
|
|
AtaCopyIdStringUnsafe(
|
|
_Out_writes_bytes_all_(Length) PUCHAR Destination,
|
|
_In_reads_bytes_(Length) PUCHAR Source,
|
|
_In_ ULONG Length)
|
|
{
|
|
ULONG i;
|
|
|
|
ASSUME((Length >= sizeof(USHORT)) && (Length % sizeof(USHORT)) == 0);
|
|
|
|
/* Copy the ATA string and swap it */
|
|
for (i = 0; i < Length; i += sizeof(USHORT))
|
|
{
|
|
Destination[i] = Source[i + 1];
|
|
Destination[i + 1] = Source[i];
|
|
}
|
|
|
|
return &Destination[i - 1];
|
|
}
|
|
|
|
PCHAR
|
|
AtaCopyIdStringSafe(
|
|
_Out_writes_bytes_all_(MaxLength) PCHAR Destination,
|
|
_In_reads_bytes_(MaxLength) PUCHAR Source,
|
|
_In_ ULONG MaxLength,
|
|
_In_ CHAR DefaultCharacter)
|
|
{
|
|
PCHAR Dest = Destination;
|
|
|
|
PAGED_CODE();
|
|
|
|
while (MaxLength != 0)
|
|
{
|
|
const UCHAR Char = *Source;
|
|
|
|
/* Only characters from space to tilde are allowed in an ID */
|
|
if (Char > ' ' && Char <= '~' && Char != ',')
|
|
*Dest = Char;
|
|
else
|
|
*Dest = DefaultCharacter;
|
|
|
|
++Source;
|
|
++Dest;
|
|
--MaxLength;
|
|
}
|
|
|
|
return Dest;
|
|
}
|
|
|
|
VOID
|
|
AtaSwapIdString(
|
|
_Inout_updates_bytes_(WordCount * sizeof(USHORT)) PVOID Buffer,
|
|
_In_range_(>, 0) ULONG WordCount)
|
|
{
|
|
PUSHORT Word = Buffer;
|
|
|
|
/* The buffer should be USHORT aligned for ARM compatibility */
|
|
ASSERT(((ULONG_PTR)Buffer & 1) == 0);
|
|
|
|
while (WordCount--)
|
|
{
|
|
*Word = RtlUshortByteSwap(*Word);
|
|
++Word;
|
|
}
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
PCHAR
|
|
AtaTrimIdString(
|
|
_In_ _Post_z_ PCHAR Start,
|
|
_In_ PCHAR End)
|
|
{
|
|
PCHAR Current = End - 1;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Remove trailing spaces */
|
|
while (Current >= Start && *Current == ' ')
|
|
{
|
|
--Current;
|
|
}
|
|
Current[1] = ANSI_NULL;
|
|
|
|
return (Current + 1);
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaPdoFillIdentificationStrings(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
|
PCHAR End;
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
size_t Remaining;
|
|
|
|
PAGED_CODE();
|
|
|
|
/*
|
|
* For ATAPI devices inquiry data is preferred over identify data.
|
|
* ATA strings are *not* byte-swapped for early ATAPI drives (NEC CDR-260, etc.).
|
|
*/
|
|
if (IS_ATAPI(&DevExt->Device))
|
|
{
|
|
/* Combine VendorId and ProductId, separated by a space */
|
|
End = AtaCopyIdStringSafe(DevExt->FriendlyName,
|
|
DevExt->InquiryData.VendorId,
|
|
RTL_FIELD_SIZE(INQUIRYDATA, VendorId),
|
|
' ');
|
|
End = AtaTrimIdString(DevExt->FriendlyName, End);
|
|
*End++ = ' ';
|
|
End = AtaCopyIdStringSafe(End,
|
|
DevExt->InquiryData.ProductId,
|
|
RTL_FIELD_SIZE(INQUIRYDATA, ProductId),
|
|
' ');
|
|
AtaTrimIdString(DevExt->FriendlyName, End);
|
|
|
|
/* Copy ProductRevisionLevel */
|
|
End = AtaCopyIdStringSafe(DevExt->RevisionNumber,
|
|
DevExt->InquiryData.ProductRevisionLevel,
|
|
RTL_FIELD_SIZE(INQUIRYDATA, ProductRevisionLevel),
|
|
' ');
|
|
AtaTrimIdString(DevExt->RevisionNumber, End);
|
|
}
|
|
else
|
|
{
|
|
/* Copy ModelNumber from a byte-swapped ATA string */
|
|
End = AtaCopyIdStringSafe(DevExt->FriendlyName,
|
|
IdentifyData->ModelNumber,
|
|
ATAPORT_FN_FIELD,
|
|
' ');
|
|
AtaSwapIdString(DevExt->FriendlyName, ATAPORT_FN_FIELD / 2);
|
|
AtaTrimIdString(DevExt->FriendlyName, End);
|
|
|
|
/* Copy FirmwareRevision from a byte-swapped ATA string */
|
|
End = AtaCopyIdStringSafe(DevExt->RevisionNumber,
|
|
IdentifyData->FirmwareRevision,
|
|
ATAPORT_RN_FIELD,
|
|
' ');
|
|
AtaSwapIdString(DevExt->RevisionNumber, ATAPORT_RN_FIELD / 2);
|
|
AtaTrimIdString(DevExt->RevisionNumber, End);
|
|
}
|
|
|
|
End = DevExt->SerialNumber;
|
|
Remaining = sizeof(DevExt->SerialNumber);
|
|
|
|
/* Format the serial number */
|
|
for (i = 0; i < sizeof(IdentifyData->SerialNumber); ++i)
|
|
{
|
|
Status = RtlStringCchPrintfExA(End,
|
|
Remaining,
|
|
&End,
|
|
&Remaining,
|
|
0,
|
|
"%2x",
|
|
IdentifyData->SerialNumber[i]);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
|
|
INFO("FriendlyName: '%s'\n", DevExt->FriendlyName);
|
|
INFO("RevisionNumber: '%s'\n", DevExt->RevisionNumber);
|
|
INFO("SerialNumber: '%s'\n", DevExt->SerialNumber);
|
|
}
|
|
|
|
VOID
|
|
AtaDeviceSetAddressingMode(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
|
ULONG64 TotalSectors;
|
|
|
|
DevExt->Device.DeviceFlags &= ~(DEVICE_LBA_MODE | DEVICE_LBA48 | DEVICE_HAS_FUA);
|
|
|
|
/* Using LBA addressing mode */
|
|
if (AtaDevHasLbaTranslation(IdentifyData))
|
|
{
|
|
DevExt->Device.DeviceFlags |= DEVICE_LBA_MODE;
|
|
|
|
if (AtaDevHas48BitAddressFeature(IdentifyData))
|
|
{
|
|
/* Using LBA48 addressing mode */
|
|
TotalSectors = AtaDevUserAddressableSectors48Bit(IdentifyData);
|
|
ASSERT(TotalSectors <= ATA_MAX_LBA_48);
|
|
|
|
DevExt->Device.DeviceFlags |= DEVICE_LBA48;
|
|
|
|
if (AtaDevHasForceUnitAccessCommands(IdentifyData))
|
|
DevExt->Device.DeviceFlags |= DEVICE_HAS_FUA;
|
|
}
|
|
else
|
|
{
|
|
/* Using LBA28 addressing mode */
|
|
TotalSectors = AtaDevUserAddressableSectors28Bit(IdentifyData);
|
|
ASSERT(TotalSectors <= ATA_MAX_LBA_28);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
USHORT Cylinders, Heads, SectorsPerTrack;
|
|
|
|
/* Using CHS addressing mode */
|
|
if (AtaDevIsCurrentGeometryValid(IdentifyData))
|
|
{
|
|
AtaDevCurrentChsTranslation(IdentifyData, &Cylinders, &Heads, &SectorsPerTrack);
|
|
}
|
|
else
|
|
{
|
|
AtaDevDefaultChsTranslation(IdentifyData, &Cylinders, &Heads, &SectorsPerTrack);
|
|
}
|
|
DevExt->Device.Cylinders = Cylinders;
|
|
DevExt->Device.Heads = Heads;
|
|
DevExt->Device.SectorsPerTrack = SectorsPerTrack;
|
|
|
|
TotalSectors = (ULONG64)Cylinders * Heads * SectorsPerTrack;
|
|
}
|
|
|
|
/*
|
|
* The sector count can be 0 on faulty devices. It's better to keep
|
|
* them available in the system to allow
|
|
* the user to send any command to the drive through the pass-through interface.
|
|
*/
|
|
if (TotalSectors == 0)
|
|
{
|
|
ERR("Unknown geometry\n");
|
|
|
|
/* Fix up sector count for the READ CAPACITY command */
|
|
TotalSectors = 1;
|
|
|
|
/* Avoid dividing by zero in READ/WRITE commands */
|
|
DevExt->Device.SectorsPerTrack = 1;
|
|
DevExt->Device.Heads = 1;
|
|
}
|
|
|
|
DevExt->Device.TotalSectors = TotalSectors;
|
|
|
|
DevExt->Device.SectorSize = AtaDevBytesPerLogicalSector(IdentifyData);
|
|
ASSERT(DevExt->Device.SectorSize >= ATA_MIN_SECTOR_SIZE);
|
|
DevExt->Device.SectorSize = max(DevExt->Device.SectorSize, ATA_MIN_SECTOR_SIZE);
|
|
|
|
INFO("Total sectors %I64u of size %lu, CHS %u:%u:%u\n",
|
|
DevExt->Device.TotalSectors,
|
|
DevExt->Device.SectorSize,
|
|
DevExt->Device.Cylinders,
|
|
DevExt->Device.Heads,
|
|
DevExt->Device.SectorsPerTrack);
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaDeviceEnableQueuedCommands(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
ULONG DeviceQueueDepth;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceQueueDepth = AtaDevQueueDepth(&DevExt->IdentifyDeviceData);
|
|
if (DeviceQueueDepth == 0)
|
|
return;
|
|
|
|
/* Do not set the queue depth to larger than the HBA can handle */
|
|
DeviceQueueDepth = min(DeviceQueueDepth, DevExt->Device.PortData->QueueDepth);
|
|
|
|
DevExt->Device.TransportFlags |= DeviceQueueDepth << DEVICE_QUEUE_DEPTH_SHIFT;
|
|
|
|
DevExt->Device.DeviceFlags |= DEVICE_NCQ;
|
|
|
|
INFO("NCQ enabled, queue depth %lu/%lu\n",
|
|
DeviceQueueDepth,
|
|
DevExt->Device.PortData->QueueDepth);
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
BOOLEAN
|
|
AtaDeviceIsSuperFloppy(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (DevExt->InquiryData.DeviceType == DIRECT_ACCESS_DEVICE)
|
|
{
|
|
/* Look for ATAPI SuperDisk drives. For example, 'MATSHITA LS-120 COSM 03' */
|
|
return (strstr(DevExt->FriendlyName, " LS-120") ||
|
|
strstr(DevExt->FriendlyName, " LS-240"));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaPdoInit(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
AtaPdoFillIdentificationStrings(DevExt);
|
|
|
|
if (IS_ATAPI(&DevExt->Device))
|
|
{
|
|
if (AtaDeviceIsSuperFloppy(DevExt))
|
|
DevExt->Device.DeviceFlags |= DEVICE_IS_SUPER_FLOPPY;
|
|
|
|
if (AtaDevIsTape(&DevExt->IdentifyPacketData))
|
|
{
|
|
INFO("Tape drive detected '%s'\n", DevExt->FriendlyName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ChanExt->PortData.PortFlags & PORT_FLAG_NCQ)
|
|
AtaDeviceEnableQueuedCommands(DevExt);
|
|
|
|
AtaDeviceSetAddressingMode(DevExt);
|
|
AtaCreateStandardInquiryData(DevExt);
|
|
}
|
|
|
|
if (DevExt->Device.AtaScsiAddress.Lun == 0)
|
|
{
|
|
AtaSetRegistryKey(ChanExt,
|
|
DevExt->Device.AtaScsiAddress.TargetId,
|
|
DD_ATA_REG_ATA_DEVICE_TYPE,
|
|
DevExt->DeviceType);
|
|
|
|
AtaSetRegistryKey(ChanExt,
|
|
DevExt->Device.AtaScsiAddress.TargetId,
|
|
DD_ATA_REG_SCSI_DEVICE_TYPE,
|
|
DevExt->InquiryData.DeviceType);
|
|
|
|
AtaGetRegistryKey(ChanExt,
|
|
DevExt->Device.AtaScsiAddress.TargetId,
|
|
DD_ATA_REG_XFER_MODE_ALLOWED,
|
|
&DevExt->TransferModeUserAllowedMask,
|
|
MAXULONG);
|
|
}
|
|
|
|
/* Will be unlocked upon PnP START IRP */
|
|
AtaReqFreezeQueue(DevExt, QUEUE_FLAG_FROZEN_PNP);
|
|
|
|
if (!AtaDevIsSsd(&DevExt->IdentifyDeviceData))
|
|
{
|
|
/* The spindle motor requires an inrush of power at start-up */
|
|
DevExt->Common.Self->Flags |= DO_POWER_INRUSH;
|
|
}
|
|
if (DevExt->InquiryData.RemovableMedia)
|
|
{
|
|
/* Distinguish between fixed and removable drives (e.g. CFA media) */
|
|
DevExt->Common.Self->Characteristics |= FILE_REMOVABLE_MEDIA;
|
|
}
|
|
DevExt->Common.Self->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
DevExt->Device.DeviceFlags &= ~DEVICE_UNINITIALIZED;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
ULONG
|
|
AtaFdoQueryDeviceCount(
|
|
_In_ PATAPORT_PORT_DATA PortData)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
KeClearEvent(&PortData->Worker.EnumerationEvent);
|
|
AtaDeviceQueueEvent(PortData, NULL, ACTION_ENUM_PORT);
|
|
KeWaitForSingleObject(&PortData->Worker.EnumerationEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
return PortData->Worker.DeviceCount;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
ATA_DEVICE_STATUS
|
|
AtaFdoQueryDeviceStatus(
|
|
_In_ PATAPORT_PORT_DATA PortData,
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ ATA_PORT_ACTION Action)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
KeClearEvent(&DevExt->Worker.EnumerationEvent);
|
|
AtaDeviceQueueEvent(PortData, DevExt, Action);
|
|
KeWaitForSingleObject(&DevExt->Worker.EnumerationEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
return DevExt->Worker.EnumStatus;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaFdoEnumeratePort(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
|
{
|
|
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
|
ULONG i, DeviceCount;
|
|
BOOLEAN DiscoveredNewDevice = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceCount = AtaFdoQueryDeviceCount(PortData);
|
|
|
|
for (i = 0; i < DeviceCount; ++i)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt;
|
|
ATA_SCSI_ADDRESS AtaScsiAddress;
|
|
ATA_DEVICE_STATUS EnumStatus;
|
|
|
|
AtaScsiAddress = AtaMarshallScsiAddress(PortData->PortNumber, i, 0);
|
|
|
|
/* Try to find an existing device */
|
|
DevExt = AtaFdoFindDeviceByPath(ChanExt, AtaScsiAddress, NULL);
|
|
if (!DevExt)
|
|
{
|
|
DevExt = AtaPdoCreateDevice(ChanExt, AtaScsiAddress);
|
|
if (!DevExt)
|
|
{
|
|
/* We are out of memory, trying to continue process the QBR IRP anyway */
|
|
ERR("Failed to allocate PDO extension\n");
|
|
continue;
|
|
}
|
|
|
|
/* Query the last known device type */
|
|
AtaGetRegistryKey(ChanExt,
|
|
i,
|
|
DD_ATA_REG_ATA_DEVICE_TYPE,
|
|
&DevExt->DeviceType,
|
|
DEV_NONE);
|
|
|
|
PortData->Worker.EnumDevExt = DevExt;
|
|
EnumStatus = AtaFdoQueryDeviceStatus(PortData, DevExt, ACTION_ENUM_DEVICE_NEW);
|
|
}
|
|
else
|
|
{
|
|
EnumStatus = AtaFdoQueryDeviceStatus(PortData, DevExt, ACTION_ENUM_DEVICE);
|
|
}
|
|
|
|
/* Determine the type of the device */
|
|
switch (EnumStatus)
|
|
{
|
|
/* No device present at this SCSI address */
|
|
case DEV_STATUS_FAILED:
|
|
case DEV_STATUS_NO_DEVICE:
|
|
{
|
|
if (DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED)
|
|
AtaPdoFreeDevice(DevExt);
|
|
continue;
|
|
}
|
|
|
|
/* It's the same device still */
|
|
case DEV_STATUS_SAME_DEVICE:
|
|
{
|
|
ASSERT(!(DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED));
|
|
|
|
DevExt->NotPresent = FALSE;
|
|
continue;
|
|
}
|
|
|
|
/* At this point, we assume that the drive is a new device */
|
|
case DEV_STATUS_NEW_DEVICE:
|
|
{
|
|
/* Device type has changed because of a hot-plug event */
|
|
if (!(DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED))
|
|
{
|
|
/* Create a new PDO for the newly hotplugged device */
|
|
--i; // Retry
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
UNREACHABLE;
|
|
}
|
|
|
|
AtaPdoInit(ChanExt, DevExt);
|
|
AtaFdoDeviceListInsert(ChanExt, DevExt, TRUE);
|
|
|
|
DiscoveredNewDevice = TRUE;
|
|
}
|
|
|
|
/* Prepare the channel for a new device */
|
|
if (DiscoveredNewDevice)
|
|
AtaDeviceQueueEvent(PortData, NULL, ACTION_PORT_TIMING);
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaFdoInitializeDeviceRelations(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_In_ PDEVICE_RELATIONS DeviceRelations)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt;
|
|
ATA_SCSI_ADDRESS AtaScsiAddress;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceRelations->Count = 0;
|
|
|
|
AtaScsiAddress.AsULONG = 0;
|
|
while (TRUE)
|
|
{
|
|
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, TRUE, NULL);
|
|
if (!DevExt)
|
|
break;
|
|
|
|
if (DevExt->NotPresent)
|
|
{
|
|
if (AtaScsiAddress.Lun == 0)
|
|
{
|
|
AtaSetRegistryKey(ChanExt,
|
|
AtaScsiAddress.TargetId,
|
|
DD_ATA_REG_ATA_DEVICE_TYPE,
|
|
DEV_NONE);
|
|
}
|
|
|
|
DevExt->ReportedMissing = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (AtaScsiAddress.Lun == 0)
|
|
{
|
|
AtaSetRegistryKey(ChanExt, AtaScsiAddress.TargetId,
|
|
DD_ATA_REG_XFER_MODE_SUPPORTED,
|
|
DevExt->TransferModeSupportedBitmap);
|
|
|
|
AtaSetRegistryKey(ChanExt,
|
|
AtaScsiAddress.TargetId,
|
|
DD_ATA_REG_XFER_MODE_SELECTED,
|
|
DevExt->TransferModeSelectedBitmap);
|
|
}
|
|
|
|
DeviceRelations->Objects[DeviceRelations->Count++] = DevExt->Common.Self;
|
|
ObReferenceObject(DevExt->Common.Self);
|
|
}
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
AtaFdoQueryBusRelations(
|
|
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
|
_In_ PIRP Irp)
|
|
{
|
|
PDEVICE_RELATIONS DeviceRelations = NULL;
|
|
ULONG Size, PdoCount;
|
|
ATA_SCSI_ADDRESS AtaScsiAddress;
|
|
#if DBG
|
|
LARGE_INTEGER TimeStart, TimeFinish;
|
|
ULONG EnumTimeMs;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
KeQuerySystemTime(&TimeStart);
|
|
#endif
|
|
|
|
AtaScsiAddress.AsULONG = 0;
|
|
while (TRUE)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt;
|
|
|
|
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, TRUE, NULL);
|
|
if (!DevExt)
|
|
break;
|
|
|
|
DevExt->NotPresent = TRUE;
|
|
|
|
if (DevExt->Device.AtaScsiAddress.Lun == 0)
|
|
{
|
|
AtaGetRegistryKey(ChanExt,
|
|
DevExt->Device.AtaScsiAddress.TargetId,
|
|
DD_ATA_REG_XFER_MODE_ALLOWED,
|
|
&DevExt->TransferModeUserAllowedMask,
|
|
MAXULONG);
|
|
}
|
|
}
|
|
|
|
AtaFdoEnumeratePort(ChanExt);
|
|
|
|
PdoCount = 0;
|
|
AtaScsiAddress.AsULONG = 0;
|
|
while (TRUE)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt;
|
|
|
|
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, TRUE, NULL);
|
|
if (!DevExt)
|
|
break;
|
|
|
|
if (!DevExt->NotPresent)
|
|
++PdoCount;
|
|
}
|
|
|
|
Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects[PdoCount]);
|
|
DeviceRelations = ExAllocatePoolUninitialized(PagedPool, Size, ATAPORT_TAG);
|
|
if (!DeviceRelations)
|
|
{
|
|
ERR("Failed to allocate device relations\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
AtaFdoInitializeDeviceRelations(ChanExt, DeviceRelations);
|
|
|
|
#if DBG
|
|
KeQuerySystemTime(&TimeFinish);
|
|
EnumTimeMs = (TimeFinish.QuadPart - TimeStart.QuadPart) / 10000;
|
|
if (EnumTimeMs >= 5000)
|
|
{
|
|
WARN("%lu: QBR request took %lu ms, %lu devices\n",
|
|
ChanExt->ScsiPortNumber,
|
|
EnumTimeMs,
|
|
DeviceRelations->Count);
|
|
}
|
|
else
|
|
{
|
|
INFO("%lu: QBR request took %lu ms, %lu devices\n",
|
|
ChanExt->ScsiPortNumber,
|
|
EnumTimeMs,
|
|
DeviceRelations->Count);
|
|
}
|
|
#endif
|
|
|
|
Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
|
|
return STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
if (DeviceRelations)
|
|
ExFreePoolWithTag(DeviceRelations, ATAPORT_TAG);
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|