[ATAPI] Add ATA storage driver

CORE-17256
CORE-17191
CORE-17716
CORE-17977
CORE-13976
CORE-16216
This commit is contained in:
Dmitry Borisov
2024-03-03 19:15:55 +06:00
committed by Carl J. Bialorucki
parent ae2827f481
commit ac33647888
23 changed files with 13586 additions and 0 deletions

View File

@@ -1,4 +1,5 @@
add_subdirectory(atapi)
add_subdirectory(pciide)
add_subdirectory(pciidex)
add_subdirectory(uniata)

View File

@@ -0,0 +1,36 @@
include_directories(
${REACTOS_SOURCE_DIR}/sdk/lib/drivers/sptilib)
list(APPEND SOURCE
atapi.c
atapi.h
debug.h
dev_config.c
dev_error.c
dev_identify.c
dev_power.c
dev_timings.c
enum.c
fdo.c
ioctl.c
pdo.c
portstate.c
satl.c
scsi.c
scsiex.h
smart.c
wmi.c)
add_library(atapi MODULE ${SOURCE} atapi.rc)
if (STACK_PROTECTOR)
target_sources(atapi PRIVATE $<TARGET_OBJECTS:gcc_ssp_nt>)
endif()
set_module_type(atapi kernelmodedriver)
target_link_libraries(atapi memcmp sptilib ${PSEH_LIB})
add_pch(atapi atapi.h SOURCE)
add_importlibs(atapi ntoskrnl hal wmilib)
add_cd_file(TARGET atapi DESTINATION reactos/system32/drivers NO_CAB FOR all)
add_registry_inf(atapi_reg.inf)

View File

@@ -0,0 +1,765 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Driver entrypoint and utility functions
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* GLOBALS ********************************************************************/
UNICODE_STRING AtapDriverRegistryPath;
BOOLEAN AtapInPEMode;
ATAPORT_PAGED_DATA
static const struct
{
PCSTR DeviceType;
PCSTR GenericType;
PCSTR PeripheralId;
} AtapGenericDeviceNames[] =
{
{"Disk", "GenDisk", "DiskPeripheral" }, // DIRECT_ACCESS_DEVICE
{"Sequential", "GenSequential", "TapePeripheral" }, // SEQUENTIAL_ACCESS_DEVICE
{"Printer", "GenPrinter", "PrinterPeripheral" }, // PRINTER_DEVICE
{"Processor", "GenProcessor", "ProcessorPeripheral" }, // PROCESSOR_DEVICE
{"Worm", "GenWorm", "WormPeripheral" }, // WRITE_ONCE_READ_MULTIPLE_DEVICE
{"CdRom", "GenCdRom", "CdRomPeripheral" }, // READ_ONLY_DIRECT_ACCESS_DEVICE
{"Scanner", "GenScanner", "ScannerPeripheral" }, // SCANNER_DEVICE
{"Optical", "GenOptical", "OpticalDiskPeripheral" }, // OPTICAL_DEVICE
{"Changer", "GenChanger", "MediumChangerPeripheral" }, // MEDIUM_CHANGER
{"Net", "GenNet", "CommunicationsPeripheral"}, // COMMUNICATION_DEVICE
{"Other", "IdeOther", "OtherPeripheral" }, // 10
};
/* FUNCTIONS ******************************************************************/
CODE_SEG("PAGE")
PCSTR
AtaTypeCodeToName(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ DEVICE_TYPE_NAME Type)
{
ULONG DeviceType;
PAGED_CODE();
DeviceType = DevExt->InquiryData.DeviceType;
DeviceType = min(DeviceType, RTL_NUMBER_OF(AtapGenericDeviceNames) - 1);
switch (Type)
{
case GetDeviceType:
return AtapGenericDeviceNames[DeviceType].DeviceType;
case GetPeripheralId:
return AtapGenericDeviceNames[DeviceType].PeripheralId;
case GetGenericType:
if (DevExt->Device.DeviceFlags & DEVICE_IS_SUPER_FLOPPY)
return "GenSFloppy"; // Install the Super Floppy storage class driver
else
return AtapGenericDeviceNames[DeviceType].GenericType;
default:
ASSERT(FALSE);
UNREACHABLE;
}
}
CODE_SEG("PAGE")
NTSTATUS
AtaOpenRegistryKey(
_Out_ PHANDLE KeyHandle,
_In_ HANDLE RootKey,
_In_ PUNICODE_STRING KeyName,
_In_ BOOLEAN Create)
{
NTSTATUS Status;
ULONG Disposition;
OBJECT_ATTRIBUTES ObjectAttributes;
PAGED_CODE();
InitializeObjectAttributes(&ObjectAttributes,
KeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
RootKey,
NULL);
if (Create)
{
Status = ZwCreateKey(KeyHandle,
KEY_ALL_ACCESS,
&ObjectAttributes,
0,
NULL,
REG_OPTION_VOLATILE,
&Disposition);
}
else
{
Status = ZwOpenKey(KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
}
return Status;
}
CODE_SEG("PAGE")
VOID
AtaGetRegistryKey(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ UCHAR TargetId,
_In_ PCWSTR KeyName,
_Out_ PULONG KeyValue,
_In_ ULONG DefaultValue)
{
UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(*KeyValue)];
HANDLE HwKeyHandle, TargetKeyHandle;
UNICODE_STRING ValueName, TargetKeyName;
NTSTATUS Status;
WCHAR TargetKeyBuffer[sizeof("Target99")];
ULONG ResultLength;
PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
PAGED_CODE();
Status = IoOpenDeviceRegistryKey(ChanExt->Pdo,
PLUGPLAY_REGKEY_DEVICE,
KEY_ALL_ACCESS,
&HwKeyHandle);
if (!NT_SUCCESS(Status))
{
TRACE("Failed to open device hardware key, status 0x%lx\n", Status);
return;
}
/* Open or create the 'TargetX' key */
Status = RtlStringCbPrintfW(TargetKeyBuffer,
sizeof(TargetKeyBuffer),
L"Target%u",
TargetId);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&TargetKeyName, TargetKeyBuffer);
Status = AtaOpenRegistryKey(&TargetKeyHandle, HwKeyHandle, &TargetKeyName, TRUE);
if (!NT_SUCCESS(Status))
{
TRACE("Failed to create '%wZ' key, status 0x%lx\n", &TargetKeyName, Status);
goto Cleanup;
}
RtlInitUnicodeString(&ValueName, KeyName);
Status = ZwQueryValueKey(TargetKeyHandle,
&ValueName,
KeyValuePartialInformation,
PartialInfo,
sizeof(Buffer),
&ResultLength);
ZwClose(TargetKeyHandle);
if (!NT_SUCCESS(Status) ||
(PartialInfo->Type != REG_DWORD) ||
(PartialInfo->DataLength != sizeof(*KeyValue)))
{
TRACE("Failed to read '%wZ' key, status 0x%lx\n", &ValueName, Status);
*KeyValue = DefaultValue;
goto Cleanup;
}
*KeyValue = *(PULONG)&PartialInfo->Data;
Cleanup:
ZwClose(HwKeyHandle);
}
CODE_SEG("PAGE")
VOID
AtaSetRegistryKey(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ UCHAR TargetId,
_In_ PCWSTR KeyName,
_In_ ULONG KeyValue)
{
HANDLE HwKeyHandle, TargetKeyHandle;
UNICODE_STRING ValueName, TargetKeyName;
NTSTATUS Status;
WCHAR TargetKeyBuffer[sizeof("Target99")];
PAGED_CODE();
Status = IoOpenDeviceRegistryKey(ChanExt->Pdo,
PLUGPLAY_REGKEY_DEVICE,
KEY_ALL_ACCESS,
&HwKeyHandle);
if (!NT_SUCCESS(Status))
{
TRACE("Failed to open device hardware key, status 0x%lx\n", Status);
return;
}
/* Open or create the 'TargetX' key */
Status = RtlStringCbPrintfW(TargetKeyBuffer,
sizeof(TargetKeyBuffer),
L"Target%u",
TargetId);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&TargetKeyName, TargetKeyBuffer);
Status = AtaOpenRegistryKey(&TargetKeyHandle, HwKeyHandle, &TargetKeyName, TRUE);
if (!NT_SUCCESS(Status))
{
TRACE("Failed to create '%wZ' key, status 0x%lx\n", &TargetKeyName, Status);
goto Cleanup;
}
RtlInitUnicodeString(&ValueName, KeyName);
Status = ZwSetValueKey(TargetKeyHandle,
&ValueName,
0,
REG_DWORD,
&KeyValue,
sizeof(KeyValue));
ZwClose(TargetKeyHandle);
if (!NT_SUCCESS(Status))
{
TRACE("Failed to set '%wZ' key, status 0x%lx\n", &ValueName, Status);
}
Cleanup:
ZwClose(HwKeyHandle);
}
CODE_SEG("PAGE")
VOID
AtaSetPortRegistryKey(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ PCWSTR KeyName,
_In_ ULONG KeyValue)
{
HANDLE HwKeyHandle;
UNICODE_STRING ValueName;
NTSTATUS Status;
PAGED_CODE();
Status = IoOpenDeviceRegistryKey(ChanExt->Pdo,
PLUGPLAY_REGKEY_DEVICE,
KEY_ALL_ACCESS,
&HwKeyHandle);
if (!NT_SUCCESS(Status))
{
TRACE("Failed to open device hardware key, status 0x%lx\n", Status);
return;
}
RtlInitUnicodeString(&ValueName, KeyName);
Status = ZwSetValueKey(HwKeyHandle,
&ValueName,
0,
REG_DWORD,
&KeyValue,
sizeof(KeyValue));
ZwClose(HwKeyHandle);
if (!NT_SUCCESS(Status))
{
TRACE("Failed to set '%wZ' key, status 0x%lx\n", &ValueName, Status);
}
}
CODE_SEG("PAGE")
NTSTATUS
NTAPI
AtaDispatchCreateClose(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
PAGED_CODE();
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
CODE_SEG("PAGE")
VOID
NTAPI
AtaUnload(
_In_ PDRIVER_OBJECT DriverObject)
{
PAGED_CODE();
RtlFreeUnicodeString(&AtapDriverRegistryPath);
}
VOID
NTAPI
AtaStorageNotificationWorker(
_In_ PDEVICE_OBJECT DeviceObject,
_In_opt_ PVOID Context)
{
PIO_WORKITEM WorkItem = Context;
PDEVICE_OBJECT TopDeviceObject;
PIRP Irp;
KEVENT Event;
IO_STATUS_BLOCK IoStatus;
STORAGE_EVENT_NOTIFICATION EventObject;
NTSTATUS Status;
TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
KeInitializeEvent(&Event, NotificationEvent, FALSE);
EventObject.Version = STORAGE_EVENT_NOTIFICATION_VERSION_V1;
EventObject.Size = sizeof(EventObject);
EventObject.Events = STORAGE_EVENT_ALL;
Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_EVENT_NOTIFICATION,
TopDeviceObject,
&EventObject,
sizeof(EventObject),
NULL,
0,
FALSE,
&Event,
&IoStatus);
if (!Irp)
{
ERR("IoBuildDeviceIoControlRequest() failed\n");
goto Exit;
}
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
Status = IoStatus.Status;
}
TRACE("Notification result %08lx\n", Status);
Exit:
ObDereferenceObject(TopDeviceObject);
IoFreeWorkItem(WorkItem);
}
VOID
NTAPI
AtaStorageNotificationlDpc(
_In_ PKDPC Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2)
{
PATAPORT_PORT_DATA PortData = DeferredContext;
ULONG DeviceBitmap = PtrToUlong(SystemArgument1);
PATAPORT_CHANNEL_EXTENSION ChanExt;
ULONG i;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument2);
ChanExt = CONTAINING_RECORD(PortData, ATAPORT_CHANNEL_EXTENSION, PortData);
for (i = 0; i < ATA_MAX_DEVICE; ++i)
{
PATAPORT_DEVICE_EXTENSION DevExt;
PIO_WORKITEM WorkItem;
if (!(DeviceBitmap & (1 << i)))
continue;
DevExt = AtaFdoFindDeviceByPath(ChanExt,
AtaMarshallScsiAddress(PortData->PortNumber, i, 0),
AtaStorageNotificationlDpc);
if (!DevExt)
continue;
WorkItem = IoAllocateWorkItem(DevExt->Common.Self);
if (!WorkItem)
{
ERR("Failed to allocate workitem");
}
else
{
IoQueueWorkItem(WorkItem, AtaStorageNotificationWorker, CriticalWorkQueue, WorkItem);
}
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, AtaStorageNotificationlDpc);
}
}
CODE_SEG("PAGE")
NTSTATUS
AtaPnpQueryInterface(
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
_In_ const GUID* Guid,
_Out_ PVOID Interface,
_In_ ULONG Version,
_In_ ULONG Size)
{
KEVENT Event;
PIRP Irp;
IO_STATUS_BLOCK IoStatusBlock;
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
PAGED_CODE();
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
CommonExt->LowerDeviceObject,
NULL,
0,
NULL,
&Event,
&IoStatusBlock);
if (!Irp)
return STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IoStack = IoGetNextIrpStackLocation(Irp);
IoStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
IoStack->Parameters.QueryInterface.InterfaceType = Guid;
IoStack->Parameters.QueryInterface.Size = Size;
IoStack->Parameters.QueryInterface.Version = Version;
IoStack->Parameters.QueryInterface.Interface = Interface;
IoStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
Status = IoCallDriver(CommonExt->LowerDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
return Status;
}
CODE_SEG("PAGE")
NTSTATUS
AtaPnpRepeatRequest(
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
_In_ PIRP Irp,
_In_opt_ PDEVICE_CAPABILITIES DeviceCapabilities)
{
PATAPORT_COMMON_EXTENSION FdoExt = CommonExt->FdoExt;
PDEVICE_OBJECT TopDeviceObject;
PIO_STACK_LOCATION IoStack, SubStack;
PIRP SubIrp;
KEVENT Event;
NTSTATUS Status;
PAGED_CODE();
ASSERT(!IS_FDO(CommonExt));
TopDeviceObject = IoGetAttachedDeviceReference(FdoExt->Self);
SubIrp = IoAllocateIrp(TopDeviceObject->StackSize, FALSE);
if (!SubIrp)
{
ObDereferenceObject(TopDeviceObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeEvent(&Event, NotificationEvent, FALSE);
IoStack = IoGetCurrentIrpStackLocation(Irp);
SubStack = IoGetNextIrpStackLocation(SubIrp);
RtlCopyMemory(SubStack, IoStack, sizeof(*SubStack));
if (DeviceCapabilities)
SubStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
IoSetCompletionRoutine(SubIrp,
AtaPdoCompletionRoutine,
&Event,
TRUE,
TRUE,
TRUE);
SubIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
Status = IoCallDriver(TopDeviceObject, SubIrp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
}
ObDereferenceObject(TopDeviceObject);
Status = SubIrp->IoStatus.Status;
IoFreeIrp(SubIrp);
return Status;
}
CODE_SEG("PAGE")
NTSTATUS
AtaPnpQueryDeviceUsageNotification(
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
_In_ PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
volatile LONG* Counter;
PAGED_CODE();
if (!IS_FDO(CommonExt))
{
Status = AtaPnpRepeatRequest(CommonExt, Irp, NULL);
}
else
{
if (!NT_VERIFY(IoForwardIrpSynchronously(CommonExt->LowerDeviceObject, Irp)))
return STATUS_UNSUCCESSFUL;
Status = Irp->IoStatus.Status;
}
if (!NT_SUCCESS(Status))
return Status;
IoStack = IoGetCurrentIrpStackLocation(Irp);
switch (IoStack->Parameters.UsageNotification.Type)
{
case DeviceUsageTypePaging:
Counter = &CommonExt->PageFiles;
break;
case DeviceUsageTypeHibernation:
Counter = &CommonExt->HibernateFiles;
break;
case DeviceUsageTypeDumpFile:
Counter = &CommonExt->DumpFiles;
break;
default:
return Status;
}
IoAdjustPagingPathCount(Counter, IoStack->Parameters.UsageNotification.InPath);
if (!IS_FDO(CommonExt))
IoInvalidateDeviceState(CommonExt->Self);
return STATUS_SUCCESS;
}
CODE_SEG("PAGE")
NTSTATUS
AtaPnpQueryPnpDeviceState(
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
_In_ PIRP Irp)
{
PAGED_CODE();
if (CommonExt->PageFiles || CommonExt->HibernateFiles || CommonExt->DumpFiles)
Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
if (IS_FDO(CommonExt))
Irp->IoStatus.Status = STATUS_SUCCESS;
return STATUS_SUCCESS;
}
CODE_SEG("PAGE")
VOID
AtaPnpInitializeCommonExtension(
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
_In_ PDEVICE_OBJECT SelfDeviceObject,
_In_ ULONG Flags)
{
PAGED_CODE();
CommonExt->Flags = Flags;
CommonExt->Self = SelfDeviceObject;
CommonExt->DevicePowerState = PowerDeviceD0;
CommonExt->SystemPowerState = PowerSystemWorking;
IoInitializeRemoveLock(&CommonExt->RemoveLock, ATAPORT_TAG, 0, 0);
}
CODE_SEG("PAGE")
NTSTATUS
NTAPI
AtaAddChannel(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PDEVICE_OBJECT PhysicalDeviceObject)
{
NTSTATUS Status;
PDEVICE_OBJECT Fdo;
UNICODE_STRING DeviceName;
PATAPORT_CHANNEL_EXTENSION ChanExt;
WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\IdePort99999")];
DECLARE_PAGED_WSTRING(FdoFormat, L"\\Device\\Ide\\IdePort%lu");
static ULONG AtapFdoNumber = 0;
PAGED_CODE();
Status = RtlStringCbPrintfW(DeviceNameBuffer,
sizeof(DeviceNameBuffer),
FdoFormat,
AtapFdoNumber);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
INFO("%s(%p, %p) '%wZ'\n", __FUNCTION__, DriverObject, PhysicalDeviceObject, &DeviceName);
Status = IoCreateDevice(DriverObject,
sizeof(*ChanExt),
&DeviceName,
FILE_DEVICE_CONTROLLER,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&Fdo);
if (!NT_SUCCESS(Status))
{
ERR("Failed to create the FDO with status 0x%lx\n", Status);
return Status;
}
ChanExt = Fdo->DeviceExtension;
RtlZeroMemory(ChanExt, sizeof(*ChanExt));
AtaPnpInitializeCommonExtension(&ChanExt->Common, Fdo, DO_IS_FDO);
ChanExt->Common.LowerDeviceObject = IoAttachDeviceToDeviceStack(Fdo, PhysicalDeviceObject);
if (!ChanExt->Common.LowerDeviceObject)
{
ERR("Failed to attach the FDO\n");
Status = STATUS_DEVICE_REMOVED;
goto Failure;
}
ChanExt->DeviceObjectNumber = AtapFdoNumber++;
ChanExt->Pdo = PhysicalDeviceObject;
/* DMA buffers alignment */
Fdo->AlignmentRequirement = ChanExt->Common.LowerDeviceObject->AlignmentRequirement;
Fdo->AlignmentRequirement = max(Fdo->AlignmentRequirement, ATA_MIN_BUFFER_ALIGNMENT);
KeInitializeSpinLock(&ChanExt->PdoListLock);
Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
Failure:
if (ChanExt->Common.LowerDeviceObject)
IoDetachDevice(ChanExt->Common.LowerDeviceObject);
IoDeleteDevice(Fdo);
return Status;
}
static
CODE_SEG("INIT")
BOOLEAN
AtaInPEMode(VOID)
{
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE KeyHandle;
NTSTATUS Status;
DECLARE_PAGED_UNICODE_STRING(
KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\MiniNT");
PAGED_CODE();
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(Status))
{
ZwClose(KeyHandle);
return TRUE;
}
return FALSE;
}
static
CODE_SEG("INIT")
VOID
AtaCreateIdeDirectory(VOID)
{
HANDLE Handle;
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
DECLARE_PAGED_UNICODE_STRING(DirectoryName, L"\\Device\\Ide");
PAGED_CODE();
InitializeObjectAttributes(&ObjectAttributes,
&DirectoryName,
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes);
if (NT_SUCCESS(Status))
{
/* We don't need a handle for a permanent object */
ZwClose(Handle);
}
/*
* Ignore directory creation failures (don't report them as a driver initialization error)
* as the directory may have already been created by another driver.
* We will handle fatal errors later via IoCreateDevice() call.
*/
}
CODE_SEG("INIT")
NTSTATUS
NTAPI
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath)
{
INFO("ATAPI driver entry\n");
// FIXME: No crashdump/hibernation support
if (!DriverObject)
return STATUS_NOT_IMPLEMENTED;
/* Make a copy of the registry path */
AtapDriverRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
AtapDriverRegistryPath.Buffer =
ExAllocatePoolUninitialized(NonPagedPool,
AtapDriverRegistryPath.MaximumLength,
ATAPORT_TAG);
if (!AtapDriverRegistryPath.Buffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyUnicodeString(&AtapDriverRegistryPath, RegistryPath);
AtapDriverRegistryPath.Buffer[RegistryPath->Length / sizeof(WCHAR)] = UNICODE_NULL;
DriverObject->MajorFunction[IRP_MJ_CREATE] = AtaDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = AtaDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AtaDispatchDeviceControl;
DriverObject->MajorFunction[IRP_MJ_SCSI] = AtaDispatchScsi;
DriverObject->MajorFunction[IRP_MJ_POWER] = AtaDispatchPower;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = AtaDispatchWmi;
DriverObject->MajorFunction[IRP_MJ_PNP] = AtaDispatchPnp;
DriverObject->DriverExtension->AddDevice = AtaAddChannel;
DriverObject->DriverUnload = AtaUnload;
KeInitializeDpc(&AtapCompletionDpc, AtaReqCompletionDpc, NULL);
InitializeSListHead(&AtapCompletionQueueList);
/* Create a directory to hold the driver's device objects */
AtaCreateIdeDirectory();
AtapInPEMode = AtaInPEMode();
return STATUS_SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
#define REACTOS_VERSION_DLL
#define REACTOS_STR_FILE_DESCRIPTION "ATA Port Driver"
#define REACTOS_STR_INTERNAL_NAME "atapi"
#define REACTOS_STR_ORIGINAL_FILENAME "atapi.sys"
#include <reactos/version.rc>

View File

@@ -0,0 +1,8 @@
; ATA Port Driver
[AddReg]
HKLM,"SYSTEM\CurrentControlSet\Services\atapi","ErrorControl",0x00010001,0x00000003
HKLM,"SYSTEM\CurrentControlSet\Services\atapi","Group",0x00000000,"SCSI Miniport"
HKLM,"SYSTEM\CurrentControlSet\Services\atapi","ImagePath",0x00020000,"system32\drivers\atapi.sys"
HKLM,"SYSTEM\CurrentControlSet\Services\atapi","Start",0x00010001,0x00000000
HKLM,"SYSTEM\CurrentControlSet\Services\atapi","Type",0x00010001,0x00000001
HKLM,"SYSTEM\CurrentControlSet\Services\atapi","Tag",0x00010001,0x00000021

View File

@@ -0,0 +1,95 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Debug support header file
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
#pragma once
#ifndef __RELFILE__
#define __RELFILE__ __FILE__
#endif
#if DBG
// #define DEBUG_TRACE
// #define DEBUG_INFO
#define DEBUG_WARN
#define DEBUG_ERR
#ifdef DEBUG_TRACE
#define TRACE(fmt, ...) \
do { \
if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \
DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \
} while (0)
#else
#if defined(_MSC_VER)
#define TRACE __noop
#else
#define TRACE(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
#endif
#endif
#ifdef DEBUG_INFO
#define INFO(fmt, ...) \
do { \
if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \
DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \
} while (0)
#else
#if defined(_MSC_VER)
#define INFO __noop
#else
#define INFO(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
#endif
#endif
#ifdef DEBUG_WARN
#define WARN(fmt, ...) \
do { \
if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \
DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \
} while (0)
#else
#if defined(_MSC_VER)
#define WARN __noop
#else
#define WARN(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
#endif
#endif
#ifdef DEBUG_ERR
#define ERR(fmt, ...) \
do { \
if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \
DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \
} while (0)
#else
#if defined(_MSC_VER)
#define ERR __noop
#else
#define ERR(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
#endif
#endif
#else
#if defined(_MSC_VER)
#define TRACE __noop
#define INFO __noop
#define WARN __noop
#define ERR __noop
#else
#define TRACE(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
#define INFO(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
#define WARN(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
#define ERR(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
#endif
#endif

View File

@@ -0,0 +1,396 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Device initialization and configuration code
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
#include <acpiioct.h>
/* FUNCTIONS ******************************************************************/
static
NTSTATUS
AtaDeviceSetGeometry(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
NTSTATUS Status;
if (IS_ATAPI(&DevExt->Device) || (DevExt->Device.DeviceFlags & DEVICE_LBA_MODE))
return STATUS_SUCCESS;
Request->Flags = REQUEST_FLAG_SET_DEVICE_REGISTER;
Request->TimeOut = 5;
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = IDE_COMMAND_SET_DRIVE_PARAMETERS;
Request->TaskFile.SectorCount = (UCHAR)DevExt->Device.SectorsPerTrack;
Request->TaskFile.DriveSelect = (UCHAR)(DevExt->Device.Heads - 1) |
DevExt->Device.DeviceSelect;
Status = AtaPortSendRequest(PortData, DevExt);
if (!NT_SUCCESS(Status))
{
WARN("CH %lu: Failed to set geometry for '%s'\n",
PortData->PortNumber,
DevExt->TransferModeSelectedBitmap,
DevExt->FriendlyName);
}
return Status;
}
static
VOID
AtaDeviceBuildSetTransferModeTaskFile(
_In_ PATA_DEVICE_REQUEST Request,
_In_ UCHAR Mode)
{
Request->Flags = 0;
Request->TimeOut = 3;
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = IDE_COMMAND_SET_FEATURE;
Request->TaskFile.Feature = IDE_FEATURE_SET_TRANSFER_MODE;
Request->TaskFile.SectorCount = Mode;
}
static
NTSTATUS
AtaDeviceSetPioTransferMode(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
NTSTATUS Status;
ULONG Mode;
NT_VERIFY(_BitScanReverse(&Mode, DevExt->TransferModeSelectedBitmap & PIO_ALL) != 0);
Mode = IDE_SET_ADVANCE_PIO_MODE(Mode);
AtaDeviceBuildSetTransferModeTaskFile(Request, Mode);
Status = AtaPortSendRequest(PortData, DevExt);
if (!NT_SUCCESS(Status))
{
WARN("CH %lu: Failed to set %lu PIO settings for '%s'\n",
PortData->PortNumber,
Mode,
DevExt->FriendlyName);
}
return Status;
}
static
NTSTATUS
AtaDeviceSetDmaTransferMode(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
NTSTATUS Status;
ULONG Mode;
if (!_BitScanReverse(&Mode, DevExt->TransferModeSelectedBitmap & ~PIO_ALL))
return STATUS_SUCCESS;
if (Mode >= UDMA_MODE(0))
Mode = IDE_SET_UDMA_MODE(Mode - UDMA_MODE(0));
else if (Mode >= MWDMA_MODE(0))
Mode = IDE_SET_MWDMA_MODE(Mode - MWDMA_MODE(0));
else
Mode = IDE_SET_SWDMA_MODE(Mode - SWDMA_MODE(0));
AtaDeviceBuildSetTransferModeTaskFile(Request, Mode);
Status = AtaPortSendRequest(PortData, DevExt);
if (NT_SUCCESS(Status))
{
DevExt->Device.DeviceFlags &= ~DEVICE_PIO_ONLY;
}
else
{
WARN("CH %lu: Failed to set 0x%lx DMA settings for '%s'\n",
PortData->PortNumber,
Mode,
DevExt->FriendlyName);
DevExt->Device.DeviceFlags &= ~DEVICE_NCQ;
DevExt->TransferModeSelectedBitmap &= PIO_ALL;
}
return Status;
}
static
NTSTATUS
AtaDeviceSetMultipleMode(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
NTSTATUS Status;
if (IS_ATAPI(&DevExt->Device))
return STATUS_SUCCESS;
DevExt->Device.MultiSectorCount = AtaDevMaximumSectorsPerDrq(&DevExt->IdentifyDeviceData);
if (DevExt->Device.MultiSectorCount == 0)
return STATUS_SUCCESS;
Request->Flags = 0;
Request->TimeOut = 3;
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = IDE_COMMAND_SET_MULTIPLE;
Request->TaskFile.SectorCount = DevExt->Device.MultiSectorCount;
Status = AtaPortSendRequest(PortData, DevExt);
if (!NT_SUCCESS(Status))
{
WARN("CH %lu: Failed to set %u multiple mode for '%s'\n",
PortData->PortNumber,
DevExt->Device.MultiSectorCount,
DevExt->FriendlyName);
DevExt->Device.MultiSectorCount = 0;
}
return Status;
}
static
BOOLEAN
AtaDeviceFilterAcpiTaskFile(
_In_ PATA_ACPI_TASK_FILE AcpiTaskFile)
{
switch (AcpiTaskFile->Command)
{
/* These features are managed by the driver */
case IDE_COMMAND_SET_FEATURE:
{
if (AcpiTaskFile->Feature == IDE_FEATURE_SET_TRANSFER_MODE)
return FALSE;
break;
}
case IDE_COMMAND_SET_DRIVE_PARAMETERS:
case IDE_COMMAND_SET_MULTIPLE:
return FALSE;
// TODO: Anything else to check?
default:
break;
}
return TRUE;
}
static
VOID
AtaDeviceAcpiTaskFileToInternalTaskFile(
_In_ ATA_ACPI_TASK_FILE* __restrict AcpiTaskFile,
_Out_ ATA_DEVICE_REQUEST* __restrict Request)
{
Request->Flags = REQUEST_FLAG_SET_DEVICE_REGISTER;
Request->TimeOut = 6;
Request->TaskFile.Feature = AcpiTaskFile->Feature;
Request->TaskFile.SectorCount = AcpiTaskFile->SectorCount;
Request->TaskFile.LowLba = AcpiTaskFile->LowLba;
Request->TaskFile.MidLba = AcpiTaskFile->MidLba;
Request->TaskFile.HighLba = AcpiTaskFile->HighLba;
Request->TaskFile.DriveSelect = AcpiTaskFile->DriveSelect;
Request->TaskFile.Command = AcpiTaskFile->Command;
}
static
NTSTATUS
AtaDeviceExecuteAcpiTaskFile(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PACPI_EVAL_OUTPUT_BUFFER GtfDataBuffer = DevExt->GtfDataBuffer;
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
PATA_ACPI_TASK_FILE AcpiTaskFile;
NTSTATUS Status;
ULONG i;
if (!GtfDataBuffer)
return STATUS_SUCCESS;
ASSERT(GtfDataBuffer->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE);
AcpiTaskFile = (PATA_ACPI_TASK_FILE)GtfDataBuffer->Argument[0].Data;
for (i = 0; i < GtfDataBuffer->Argument[0].DataLength / sizeof(*AcpiTaskFile); ++i)
{
PCSTR Result;
if (!AtaDeviceFilterAcpiTaskFile(AcpiTaskFile))
{
Result = "filtered out";
}
else
{
AtaDeviceAcpiTaskFileToInternalTaskFile(AcpiTaskFile, Request);
Status = AtaPortSendRequest(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
Result = NT_SUCCESS(Status) ? "completed" : "aborted";
}
INFO("CH %lu: GTF[%lu]: %02x:%02x:%02x:%02x:%02x:%02x:%02x -- %s\n",
PortData->PortNumber,
i,
AcpiTaskFile->Feature,
AcpiTaskFile->SectorCount,
AcpiTaskFile->LowLba,
AcpiTaskFile->MidLba,
AcpiTaskFile->HighLba,
AcpiTaskFile->DriveSelect,
AcpiTaskFile->Command,
Result);
++AcpiTaskFile;
}
return STATUS_SUCCESS;
}
/*
* See MSDN note:
* https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/security-group-commands
*/
static
NTSTATUS
AtaDeviceLockSecurityModeFeatureCommands(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
if (IS_ATAPI(&DevExt->Device) ||
AtapInPEMode ||
!AtaDevHasSecurityModeFeature(&DevExt->IdentifyDeviceData))
{
return STATUS_SUCCESS;
}
Request->Flags = 0;
Request->TimeOut = 3;
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = IDE_COMMAND_SECURITY_FREEZE_LOCK;
return AtaPortSendRequest(PortData, DevExt);
}
static
NTSTATUS
AtaDeviceLockDeviceParameters(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
Request->Flags = 0;
Request->TimeOut = 3;
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = IDE_COMMAND_SET_FEATURE;
Request->TaskFile.Feature = IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON;
return AtaPortSendRequest(PortData, DevExt);
}
static
NTSTATUS
AtaDeviceEnableMsnFeature(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
NTSTATUS Status;
if (!AtaDevHasRemovableMediaStatusNotification(&DevExt->IdentifyDeviceData))
return STATUS_SUCCESS;
Request->Flags = 0;
Request->TimeOut = 3;
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = IDE_COMMAND_SET_FEATURE;
Request->TaskFile.Feature = IDE_FEATURE_ENABLE_MSN;
Status = AtaPortSendRequest(PortData, DevExt);
if (NT_SUCCESS(Status))
{
DevExt->Device.DeviceFlags |= DEVICE_HAS_MEDIA_STATUS;
}
return Status;
}
NTSTATUS
AtaPortDeviceProcessConfig(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
NTSTATUS Status;
DevExt->Device.DeviceFlags |= DEVICE_PIO_ONLY;
DevExt->Device.DeviceFlags &= ~(DEVICE_HAS_MEDIA_STATUS | DEVICE_SENSE_DATA_REPORTING);
DevExt->Device.MultiSectorCount = 0;
Status = AtaDeviceSetGeometry(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
Status = AtaDeviceSetPioTransferMode(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
Status = AtaDeviceSetDmaTransferMode(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
Status = AtaDeviceSetMultipleMode(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
Status = AtaDeviceExecuteAcpiTaskFile(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
Status = AtaDeviceLockSecurityModeFeatureCommands(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
Status = AtaDeviceLockDeviceParameters(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
Status = AtaDeviceEnableMsnFeature(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
/* Identify data might have changed during the configuration, so update it here */
Status = AtaDeviceSendIdentify(PortData,
DevExt,
IS_ATAPI(&DevExt->Device) ?
IDE_COMMAND_ATAPI_IDENTIFY : IDE_COMMAND_IDENTIFY);
if (NT_SUCCESS(Status))
{
RtlCopyMemory(&DevExt->IdentifyDeviceData,
DevExt->Device.LocalBuffer,
sizeof(DevExt->IdentifyDeviceData));
}
return Status;
}

View File

@@ -0,0 +1,657 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Device I/O error handling
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* FUNCTIONS ******************************************************************/
static
BOOLEAN
AtaDeviceRequestSenseNeeded(
_In_ PATA_DEVICE_REQUEST Request)
{
return ((Request->Flags & REQUEST_FLAG_PACKET_COMMAND) &&
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
(Request->Srb->SenseInfoBuffer != NULL) &&
(Request->Srb->SenseInfoBufferLength != 0));
}
static
BOOLEAN
AtaDeviceRequestSenseNeededExt(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PATA_DEVICE_REQUEST Request)
{
/* NOTE: The ERROR bit and the SENSE DATA AVAILABLE bit may both be set to one */
return ((DevExt->Device.DeviceFlags & DEVICE_SENSE_DATA_REPORTING) &&
(Request->Output.Status & IDE_STATUS_INDEX) &&
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
(Request->Srb->SenseInfoBuffer != NULL) &&
(Request->Srb->SenseInfoBufferLength != 0));
}
static
BOOLEAN
AtaDeviceFixedErrorNeeded(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PATA_DEVICE_REQUEST Request)
{
return (!(Request->Flags & REQUEST_FLAG_PACKET_COMMAND) &&
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
(Request->Srb->SenseInfoBuffer != NULL) &&
(Request->Srb->SenseInfoBufferLength != 0));
}
static
BOOLEAN
AtaDeviceIsDmaCrcError(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PATA_DEVICE_REQUEST Request)
{
// TODO: Check also for SATA SErr or AHCI interrupt status bits
return (Request->Output.Error & IDE_ERROR_CRC_ERROR) &&
(Request->Flags & REQUEST_FLAG_DMA);
}
static
VOID
AtaDeviceHandleRequestSense(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
PSCSI_REQUEST_BLOCK Srb = FailedRequest->Srb;
PSENSE_DATA SenseData;
BOOLEAN Success;
if (IS_ATAPI(&DevExt->Device))
{
Success = (Request->SrbStatus == SRB_STATUS_SUCCESS) ||
(Request->SrbStatus == SRB_STATUS_DATA_OVERRUN);
}
else
{
Success = (Request->SrbStatus == SRB_STATUS_SUCCESS) &&
(Request->Flags & REQUEST_FLAG_HAS_TASK_FILE) &&
(Request->TaskFile.HighLba != 0);
}
if (!Success)
{
ERR("Request sense failed\n");
if (IS_ATAPI(&DevExt->Device))
{
Srb->ScsiStatus = SCSISTAT_GOOD;
FailedRequest->SrbStatus = SRB_STATUS_REQUEST_SENSE_FAILED;
}
else
{
/* Request sense failed, translate the original ATA device error */
FailedRequest->SrbStatus = AtaReqSetFixedAtaSenseData(FailedRequest);
}
return;
}
SenseData = Srb->SenseInfoBuffer;
if (IS_ATAPI(&DevExt->Device))
{
/* Copy the sense data from the local buffer */
RtlCopyMemory(Srb->SenseInfoBuffer,
DevExt->Device.LocalBuffer,
min(Srb->SenseInfoBufferLength, ATA_LOCAL_BUFFER_SIZE));
Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
FailedRequest->SrbStatus = SRB_STATUS_ERROR | SRB_STATUS_AUTOSENSE_VALID;
}
else
{
SCSI_SENSE_CODE SenseCode;
/* Copy the sense code from the task file registers */
SenseCode.SrbStatus = SRB_STATUS_ERROR;
SenseCode.SenseKey = Request->TaskFile.HighLba;
SenseCode.AdditionalSenseCode = Request->TaskFile.MidLba;
SenseCode.AdditionalSenseCodeQualifier = Request->TaskFile.LowLba;
FailedRequest->SrbStatus = AtaReqSetFixedSenseData(Srb, SenseCode);
}
if (RTL_CONTAINS_FIELD(SenseData,
Srb->SenseInfoBufferLength,
AdditionalSenseCodeQualifier))
{
/* INFO("0x%02X: SK 0x%02X, ASC 0x%02X, ASCQ 0x%02X\n", */
/* Srb->Cdb[0], */
/* SenseData->SenseKey, */
/* SenseData->AdditionalSenseCode, */
/* SenseData->AdditionalSenseCodeQualifier); */
}
}
static
NTSTATUS
AtaDeviceSendRequestSense(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
NTSTATUS Status;
PCDB Cdb;
ASSERT_REQUEST(FailedRequest);
ASSERT(FailedRequest->Srb->SenseInfoBufferLength > 0);
Request->Flags = REQUEST_FLAG_DATA_IN |
REQUEST_FLAG_PACKET_COMMAND |
REQUEST_FLAG_HAS_LOCAL_BUFFER;
Request->TimeOut = 3;
Request->DataTransferLength = FailedRequest->Srb->SenseInfoBufferLength;
Cdb = (PCDB)Request->Cdb;
Cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
Cdb->CDB6INQUIRY.LogicalUnitNumber = 0;
Cdb->CDB6INQUIRY.Reserved1 = 0;
Cdb->CDB6INQUIRY.PageCode = 0;
Cdb->CDB6INQUIRY.IReserved = 0;
Cdb->CDB6INQUIRY.AllocationLength = (UCHAR)Request->DataTransferLength;
Cdb->CDB6INQUIRY.Control = 0;
Status = AtaPortSendRequest(PortData, DevExt);
AtaDeviceHandleRequestSense(PortData, DevExt);
return Status;
}
static
NTSTATUS
AtaDeviceSendRequestSenseExt(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
NTSTATUS Status;
Request->Flags = REQUEST_FLAG_SAVE_TASK_FILE | REQUEST_FLAG_LBA48;
Request->TimeOut = 3;
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = IDE_COMMAND_REQUEST_SENSE_DATA_EXT;
Status = AtaPortSendRequest(PortData, DevExt);
AtaDeviceHandleRequestSense(PortData, DevExt);
return Status;
}
static
NTSTATUS
AtaDeviceSendReadNcqCommandErrorLog(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
AtaReqBuildReadLogTaskFile(Request,
IDE_GP_LOG_NCQ_COMMAND_ERROR_ADDRESS,
0,
1);
Request->Flags |= REQUEST_FLAG_HAS_LOCAL_BUFFER;
Request->TimeOut = 3;
return AtaPortSendRequest(PortData, DevExt);
}
static
VOID
AtaDeviceSaveLogPageTaskFile(
_In_ GP_LOG_NCQ_COMMAND_ERROR* __restrict LogPage,
_Inout_ ATA_DEVICE_REQUEST* __restrict Request)
{
PATA_TASKFILE TaskFile = &Request->Output;
TaskFile->SectorCount = LogPage->Count7_0;
TaskFile->LowLba = LogPage->LBA7_0;
TaskFile->MidLba = LogPage->LBA15_8;
TaskFile->HighLba = LogPage->LBA23_16;
TaskFile->DriveSelect = LogPage->Device;
if (Request->Flags & REQUEST_FLAG_LBA48)
{
TaskFile->FeatureEx = 0; // Reserved
TaskFile->SectorCountEx = LogPage->Count15_8;
TaskFile->LowLbaEx = LogPage->LBA31_24;
TaskFile->MidLbaEx = LogPage->LBA39_32;
TaskFile->HighLbaEx = LogPage->LBA47_40;
}
}
static
VOID
AtaDeviceLogEvent(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ ATA_ERROR_LOG_VALUE ErrorValue)
{
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
ULONG ErrorCode, FinalStatus;
PIO_ERROR_LOG_PACKET LogEntry;
UCHAR Size;
Size = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
LogEntry = IoAllocateErrorLogEntry(ChanExt->Common.Self, Size);
if (!LogEntry)
return;
switch (ErrorValue)
{
case EVENT_CODE_TIMEOUT:
ErrorCode = IO_ERR_TIMEOUT;
FinalStatus = STATUS_IO_TIMEOUT;
break;
case EVENT_CODE_CRC_ERROR:
ErrorCode = IO_ERR_PARITY;
FinalStatus = STATUS_IO_DEVICE_ERROR;
break;
default:
ErrorCode = IO_ERR_CONTROLLER_ERROR;
FinalStatus = STATUS_IO_DEVICE_ERROR;
break;
}
RtlZeroMemory(LogEntry, Size);
LogEntry->FinalStatus = FinalStatus;
LogEntry->ErrorCode = ErrorCode;
LogEntry->MajorFunctionCode = IRP_MJ_SCSI;
LogEntry->UniqueErrorValue = ErrorValue;
IoWriteErrorLogEntry(LogEntry);
}
static
VOID
AtaDeviceDowngradeTransferSpeed(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
ULONG Mode, AllowedModesMask;
/* Already in PIO mode */
if (!_BitScanReverse(&Mode, DevExt->TransferModeSelectedBitmap & ~PIO_ALL))
return;
/* Clear the current mode and the upper bits */
AllowedModesMask = ((1 << Mode) - 1);
if ((DevExt->TransferModeSupportedBitmap & UDMA_ALL) && !(AllowedModesMask & UDMA_ALL))
{
/*
* It makes no sense to disable DMA for AHCI hard drives
* because that would otherwise cause READ/WRITE commands to fail.
*/
if ((PortData->PortFlags & PORT_FLAG_PIO_VIA_DMA) && !IS_ATAPI(&DevExt->Device))
return;
/* From UDMA to PIO (intentionally skip MWDMA and SWDMA) */
AllowedModesMask &= PIO_ALL;
}
else if (((DevExt->TransferModeSupportedBitmap & MWDMA_ALL) &&
!(AllowedModesMask & MWDMA_ALL)) ||
((DevExt->TransferModeSupportedBitmap & SWDMA_ALL) &&
!(AllowedModesMask & SWDMA_ALL)))
{
/* From MWDMA to PIO (intentionally skip SWDMA) or from SWDMA to PIO */
AllowedModesMask &= PIO_ALL;
}
/* We are about to disable DMA, log the change */
if ((DevExt->TransferModeSupportedBitmap & ~PIO_ALL) && !(AllowedModesMask & ~PIO_ALL))
{
WARN("Too many DMA failures, disabling DMA for '%s'\n", DevExt->FriendlyName);
AtaDeviceLogEvent(DevExt, EVENT_CODE_DMA_DISABLE);
}
WARN("Downgrading DMA speed from %lu for '%s'\n", Mode, DevExt->FriendlyName);
DevExt->TransferModeAllowedMask &= AllowedModesMask;
/* Program the new timings */
_InterlockedOr(&PortData->Worker.EventsPending, ACTION_PORT_TIMING | ACTION_DEVICE_CONFIG);
_InterlockedOr(&DevExt->Worker.EventsPending, ACTION_DEVICE_CONFIG);
/* Request a QBR to ensure that storprop.dll updates its data from the registry */
PortData->Worker.Flags |= WORKER_FLAG_NEED_RESCAN;
}
static
VOID
AtaDeviceAnalyzeDmaError(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PATA_DEVICE_REQUEST Request,
_In_ ATA_ERROR_LOG_VALUE ErrorValue)
{
LARGE_INTEGER CurrentTime, TimeDifferenceMs;
WARN("DMA error %lu on '%s'\n", ErrorValue, DevExt->FriendlyName);
/* We silently ignore device errors caused by PASSTHROUGH commands came from user mode */
if (Request->Flags & REQUEST_FLAG_PASSTHROUGH)
return;
KeQuerySystemTime(&CurrentTime);
TimeDifferenceMs.QuadPart = (CurrentTime.QuadPart - DevExt->LastDmaErrorTime.QuadPart) / 10000;
DevExt->LastDmaErrorTime.QuadPart = CurrentTime.QuadPart;
AtaDeviceLogEvent(DevExt, ErrorValue);
/* Ignore all occasional DMA errors we encounter */
if (TimeDifferenceMs.QuadPart >= (10LL * 60000LL)) // 10 min
return;
/* DMA timeouts and DMA CRC errors usually indicate a bad connection (a bad cable) */
/* Try to disable NCQ */
if (DevExt->Device.DeviceFlags & DEVICE_NCQ)
{
DevExt->Device.DeviceFlags &= ~DEVICE_NCQ;
ERR("NCQ disabled for '%s'\n", DevExt->FriendlyName);
AtaDeviceLogEvent(DevExt, EVENT_CODE_NCQ_DISABLE);
return;
}
/* Try to reduce the interface speed */
if (PortData->DowngradeInterfaceSpeed(PortData->ChannelContext))
{
WARN("Downgrading interface for '%s'\n", DevExt->FriendlyName);
AtaDeviceLogEvent(DevExt, EVENT_CODE_DOWNSHIFT);
return;
}
/* Try to reduce the transfer speed */
AtaDeviceDowngradeTransferSpeed(PortData, DevExt);
}
static
VOID
AtaDeviceCompleteFailedRequest(
_In_ PATAPORT_PORT_DATA PortData)
{
PATA_DEVICE_REQUEST Request = PortData->Worker.FailedRequest;
UCHAR SrbStatus;
SIZE_T RetryCount;
KIRQL OldIrql;
ASSERT_REQUEST(Request);
ASSERT(Request != &PortData->Worker.InternalRequest);
SrbStatus = SRB_STATUS(Request->SrbStatus);
RetryCount = SRB_GET_FLAGS(Request->Srb) & SRB_FLAG_RETRY_COUNT_MASK;
RetryCount++;
SRB_CLEAR_FLAGS(Request->Srb, SRB_FLAG_RETRY_COUNT_MASK);
SRB_SET_FLAGS(Request->Srb, RetryCount);
if (RetryCount > 3)
{
ASSERT(SrbStatus != SRB_STATUS_BUSY);
}
else
{
/*
* We usually do not want to retry a request, because the upper class driver
* is intended to effectively analyze errors and sense data.
*/
switch (SrbStatus)
{
case SRB_STATUS_BUS_RESET:
case SRB_STATUS_TIMEOUT:
{
/* DMA timeout, attempt to retry in PIO mode */
if (!(PortData->PortFlags & PORT_FLAG_PIO_VIA_DMA))
{
SRB_SET_FLAGS(Request->Srb, SRB_FLAG_PIO_RETRY);
}
SrbStatus = SRB_STATUS_BUSY;
break;
}
case SRB_STATUS_REQUEST_SENSE_FAILED:
{
SrbStatus = SRB_STATUS_BUSY;
break;
}
default:
{
/* Retry paging I/O operations only */
if (Request->Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_PAGING_IO))
SrbStatus = SRB_STATUS_BUSY;
break;
}
}
}
SrbStatus |= Request->SrbStatus & (SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN);
Request->SrbStatus = SrbStatus;
if (SRB_STATUS(SrbStatus) == SRB_STATUS_BUSY)
return;
ASSERT(SRB_STATUS(SrbStatus) != SRB_STATUS_SUCCESS);
/* Fail the command with an error */
Request->InternalState = REQUEST_STATE_FREEZE_QUEUE;
OldIrql = KeAcquireInterruptSpinLock(PortData->InterruptObject);
ASSERT(PortData->Worker.PausedSlotsBitmap & (1 << Request->Slot));
PortData->Worker.PausedSlotsBitmap &= ~(1 << Request->Slot);
KeReleaseInterruptSpinLock(PortData->InterruptObject, OldIrql);
}
static
NTSTATUS
AtaDeviceNcqRecovery(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST FailedRequest;
PGP_LOG_NCQ_COMMAND_ERROR LogPage;
NTSTATUS Status;
ULONG i;
UCHAR Crc;
Status = AtaDeviceSendReadNcqCommandErrorLog(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
/* We failed at retrieving the log page */
if (!NT_SUCCESS(Status))
{
ERR("READ LOG EXT failure\n");
goto Failure;
}
LogPage = DevExt->Device.LocalBuffer;
/* It's not ours */
if (LogPage->NonQueuedCmd)
{
ERR("Unexpected NQ bit in the log page 0x10 structure\n");
goto Failure;
}
/* Verify the checksum */
Crc = 0;
for (i = 0; i < IDE_GP_LOG_SECTOR_SIZE; ++i)
{
Crc += ((PUCHAR)LogPage)[i];
}
if (Crc != 0)
{
ERR("CRC error in the log page 0x10 structure\n");
goto Failure;
}
/* Find the failed queued command */
if (!(PortData->Worker.PausedSlotsBitmap & (1 << LogPage->NcqTag)))
{
ERR("Failed command %08lx not found in %08lx\n",
1 << LogPage->NcqTag, PortData->Worker.PausedSlotsBitmap);
goto Failure;
}
FailedRequest = PortData->Slots[LogPage->NcqTag];
ASSERT_REQUEST(FailedRequest);
PortData->Worker.FailedRequest = FailedRequest;
/* Fail the command with an error */
FailedRequest->SrbStatus = SRB_STATUS_ERROR;
FailedRequest->Output.Status = LogPage->Status;
FailedRequest->Output.Error = LogPage->Error;
if (FailedRequest->Flags & REQUEST_FLAG_SAVE_TASK_FILE)
{
AtaDeviceSaveLogPageTaskFile(LogPage, FailedRequest);
}
if ((LogPage->SenseKey != 0) && AtaDevHasNcqAutosense(&DevExt->IdentifyDeviceData))
{
SCSI_SENSE_CODE SenseCode;
/* Set the sense data returned by the device */
SenseCode.SrbStatus = FailedRequest->SrbStatus;
SenseCode.SenseKey = LogPage->SenseKey;
SenseCode.AdditionalSenseCode = LogPage->ASC;
SenseCode.AdditionalSenseCodeQualifier = LogPage->ASCQ;
FailedRequest->SrbStatus = AtaReqSetFixedSenseData(FailedRequest->Srb, SenseCode);
/* Set the "Final LBA in Error" field */
AtaReqSetLbaInformation(FailedRequest->Srb,
((ULONG64)(((PUCHAR)LogPage)[17]) << 0) |
((ULONG64)(((PUCHAR)LogPage)[18]) << 8) |
((ULONG64)(((PUCHAR)LogPage)[19]) << 16) |
((ULONG64)(((PUCHAR)LogPage)[20]) << 24) |
((ULONG64)(((PUCHAR)LogPage)[21]) << 32) |
((ULONG64)(((PUCHAR)LogPage)[22]) << 48));
}
else if (AtaDeviceRequestSenseNeededExt(DevExt, FailedRequest))
{
/* Get sense data */
Status = AtaDeviceSendRequestSenseExt(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
}
else
{
FailedRequest->SrbStatus = AtaReqSetFixedAtaSenseData(FailedRequest);
}
AtaDeviceCompleteFailedRequest(PortData);
return STATUS_SUCCESS;
Failure:
/* A port reset is required to abort all outstanding queued commands */
_InterlockedOr(&PortData->Worker.EventsPending, ACTION_PORT_RESET);
return STATUS_SUCCESS;
}
static
NTSTATUS
AtaDeviceGenericRecovery(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = PortData->Worker.FailedRequest;
NTSTATUS Status;
switch (Request->SrbStatus)
{
/* Unexpected channel/device state */
case SRB_STATUS_BUS_RESET:
case SRB_STATUS_TIMEOUT:
{
if (Request->Flags & REQUEST_FLAG_DMA)
{
ATA_ERROR_LOG_VALUE ErrorValue;
if (Request->SrbStatus == SRB_STATUS_TIMEOUT)
ErrorValue = EVENT_CODE_TIMEOUT;
else
ErrorValue = EVENT_CODE_BAD_STATE;
AtaDeviceAnalyzeDmaError(PortData, DevExt, Request, ErrorValue);
}
break;
}
/* General errors */
case SRB_STATUS_ERROR:
{
if (AtaDeviceIsDmaCrcError(DevExt, Request))
{
AtaDeviceAnalyzeDmaError(PortData, DevExt, Request, EVENT_CODE_CRC_ERROR);
break;
}
/* Send the recovery command to figure out why the current command failed */
if (AtaDeviceRequestSenseNeeded(Request))
{
/* Handle failed ATAPI commands */
Status = AtaDeviceSendRequestSense(PortData, DevExt);
break;
}
else if (AtaDeviceRequestSenseNeededExt(DevExt, Request))
{
/* Handle failed non-queued commands */
Status = AtaDeviceSendRequestSenseExt(PortData, DevExt);
break;
}
else if (AtaDeviceFixedErrorNeeded(DevExt, Request))
{
Request->SrbStatus = AtaReqSetFixedAtaSenseData(Request);
}
/* Recovery command is not required, just complete the request with an error */
__fallthrough;
}
default:
break;
}
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
AtaDeviceCompleteFailedRequest(PortData);
return STATUS_SUCCESS;
}
NTSTATUS
AtaPortDeviceProcessError(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
NTSTATUS Status;
ASSERT_REQUEST(FailedRequest);
if (FailedRequest->Flags & REQUEST_FLAG_NCQ)
Status = AtaDeviceNcqRecovery(PortData, DevExt);
else
Status = AtaDeviceGenericRecovery(PortData, DevExt);
return Status;
}

View File

@@ -0,0 +1,367 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Device identification
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* FUNCTIONS ******************************************************************/
static
BOOLEAN
AtaDeviceIdentifyDataEqual(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData1,
_In_ PIDENTIFY_DEVICE_DATA IdentifyData2)
{
if (!RtlEqualMemory(IdentifyData1->SerialNumber,
IdentifyData2->SerialNumber,
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, SerialNumber)))
{
return FALSE;
}
if (!RtlEqualMemory(IdentifyData1->FirmwareRevision,
IdentifyData2->FirmwareRevision,
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, FirmwareRevision)))
{
return FALSE;
}
if (!RtlEqualMemory(IdentifyData1->ModelNumber,
IdentifyData2->ModelNumber,
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, ModelNumber)))
{
return FALSE;
}
return TRUE;
}
NTSTATUS
AtaDeviceSendIdentify(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ UCHAR Command)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
TRACE("CH %lu: Send %s identify device %u\n",
PortData->PortNumber,
Command == IDE_COMMAND_IDENTIFY ? "ATA" : "ATAPI",
DevExt->Device.AtaScsiAddress.TargetId);
/*
* For PATA devices, disable interrupts for the identify command and use polling instead.
*
* On some single device 1 configurations or non-existent IDE channels
* the status register stuck permanently at a value of 0,
* and we incorrectly assume that the device is present.
* This will result in a taskfile timeout
* which must be avoided as it would cause hangs at boot time.
*/
Request->Flags = REQUEST_FLAG_POLL;
Request->Flags |= REQUEST_FLAG_DATA_IN | REQUEST_FLAG_HAS_LOCAL_BUFFER;
Request->TimeOut = 10;
Request->DataTransferLength = sizeof(DevExt->IdentifyDeviceData);
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = Command;
return AtaPortSendRequest(PortData, DevExt);
}
static
NTSTATUS
AtaDeviceSpinUp(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
TRACE("CH %lu: Spin-up device %u\n",
PortData->PortNumber,
DevExt->Device.AtaScsiAddress.TargetId);
Request->Flags = 0;
Request->TimeOut = 20;
Request->TaskFile.Command = IDE_COMMAND_SET_FEATURE;
Request->TaskFile.Feature = IDE_FEATURE_PUIS_SPIN_UP;
return AtaPortSendRequest(PortData, DevExt);
}
static
BOOLEAN
AtaDeviceIsXboxDrive(
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
static const PCSTR AtapBrokenInquiryDrive[] =
{
"THOMSON-DVD",
"PHILIPS XBOX DVD DRIVE",
"PHILIPS J5 3235C",
"SAMSUNG DVD-ROM SDG-605B"
// TODO: Xbox 360 drives are also affected.
};
CHAR ModelNumber[26];
PUCHAR End;
ULONG i;
End = AtaCopyIdStringUnsafe((PUCHAR)ModelNumber,
DevExt->IdentifyDeviceData.ModelNumber,
sizeof(ModelNumber));
*End = ANSI_NULL;
for (i = 0; i < RTL_NUMBER_OF(AtapBrokenInquiryDrive); ++i)
{
if (strcmp(ModelNumber, AtapBrokenInquiryDrive[i]) == 0)
return TRUE;
}
return FALSE;
}
static
VOID
AtaCreateAtapiStandardInquiryData(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
_Out_ PINQUIRYDATA InquiryData)
{
InquiryData->DeviceType = READ_ONLY_DIRECT_ACCESS_DEVICE;
InquiryData->RemovableMedia = AtaDevIsRemovable(IdentifyData);
InquiryData->Versions = 0x04; // SPC-2
InquiryData->ResponseDataFormat = 2; // This means "Complies to this standard"
InquiryData->AdditionalLength =
INQUIRYDATABUFFERSIZE - RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
/* Copy ModelNumber from a byte-swapped ATA string */
AtaCopyIdStringSafe((PCHAR)InquiryData->VendorId,
IdentifyData->ModelNumber,
sizeof(IdentifyData->ModelNumber),
' ');
AtaSwapIdString(InquiryData->VendorId, sizeof(IdentifyData->ModelNumber) / 2);
/* Copy FirmwareRevision from a byte-swapped ATA string */
AtaCopyIdStringSafe((PCHAR)InquiryData->ProductRevisionLevel,
IdentifyData->FirmwareRevision,
sizeof(InquiryData->ProductRevisionLevel),
' ');
AtaSwapIdString(InquiryData->ProductRevisionLevel,
sizeof(InquiryData->ProductRevisionLevel) / 2);
}
static
NTSTATUS
AtaDeviceSendInquiry(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
NTSTATUS Status;
PCDB Cdb;
TRACE("CH %lx: Send inquiry device %u\n",
PortData->PortNumber,
DevExt->Device.AtaScsiAddress.TargetId);
Request->Flags = REQUEST_FLAG_DATA_IN |
REQUEST_FLAG_PACKET_COMMAND |
REQUEST_FLAG_HAS_LOCAL_BUFFER;
Request->TimeOut = 3;
Request->DataTransferLength = INQUIRYDATABUFFERSIZE;
RtlZeroMemory(Request->Cdb, sizeof(Request->Cdb));
Cdb = (PCDB)&Request->Cdb;
Cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
Cdb->CDB6INQUIRY.LogicalUnitNumber = DevExt->Device.AtaScsiAddress.Lun;
Cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
Status = AtaPortSendRequest(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
if (!NT_SUCCESS(Status))
{
if (DevExt->Device.AtaScsiAddress.Lun == 0 && AtaDeviceIsXboxDrive(DevExt))
{
DevExt->Device.DeviceFlags |= DEVICE_CACHE_INQUIRY;
/*
* The INQUIRY command is mandatory to be implemented by ATAPI devices,
* but some Xbox drives violate this.
*/
AtaCreateAtapiStandardInquiryData(&DevExt->IdentifyDeviceData,
DevExt->Device.LocalBuffer);
Status = DEV_STATUS_NEW_DEVICE;
}
else
{
Status = DEV_STATUS_NO_DEVICE;
}
}
return STATUS_SUCCESS;
}
ATA_DEVICE_STATUS
AtaPortIdentifyDevice(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PIDENTIFY_DEVICE_DATA IdentifyData = DevExt->Device.LocalBuffer;
ATA_CONNECTION_STATUS ConnectionStatus;
NTSTATUS Status;
BOOLEAN StartFromAtapi, IsSameDevice;
ULONG Attempt, RetryCount;
ATA_DEVICE_TYPE DeviceType;
_InterlockedOr(&PortData->InterruptFlags, PORT_INT_FLAG_IGNORE_LINK_IRQ);
ConnectionStatus = PortData->IdentifyDevice(PortData->ChannelContext,
DevExt->Device.AtaScsiAddress.TargetId);
_InterlockedAnd(&PortData->InterruptFlags, ~PORT_INT_FLAG_IGNORE_LINK_IRQ);
if (ConnectionStatus == CONN_STATUS_FAILURE)
return DEV_STATUS_FAILED;
if (ConnectionStatus == CONN_STATUS_NO_DEVICE)
return DEV_STATUS_NO_DEVICE;
/* Power up the device if needed */
Status = AtaPortCheckDevicePowerState(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return DEV_STATUS_FAILED;
/*
* Try the known device type first.
* This may speed up device detection by eliminating I/O errors.
*/
if (DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED)
StartFromAtapi = (ConnectionStatus == CONN_STATUS_DEV_ATAPI);
else
StartFromAtapi = IS_ATAPI(&DevExt->Device);
/* Look for ATA/ATAPI devices */
for (RetryCount = 0; RetryCount < 2; ++RetryCount)
{
/* Send the identify command */
for (Attempt = 0; Attempt < 2; ++Attempt)
{
if (StartFromAtapi)
DeviceType = DEV_ATAPI;
else
DeviceType = DEV_ATA;
/* Swap device types */
if (Attempt != 0)
DeviceType ^= (DEV_ATAPI ^ DEV_ATA);
Status = AtaDeviceSendIdentify(PortData,
DevExt,
DeviceType == DEV_ATAPI ?
IDE_COMMAND_ATAPI_IDENTIFY : IDE_COMMAND_IDENTIFY);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return DEV_STATUS_FAILED;
if (NT_SUCCESS(Status))
break;
}
if (!NT_SUCCESS(Status))
return DEV_STATUS_NO_DEVICE;
/*
* The drive needs to be spun up
* or the device was unable to return complete identity data.
*/
if (AtaDevInPuisState(IdentifyData) || AtaDevIsIdentifyDataIncomplete(IdentifyData))
{
Status = AtaDeviceSpinUp(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return DEV_STATUS_FAILED;
if (!NT_SUCCESS(Status))
ERR("Failed to spin-up device\n");
continue;
}
}
/* Verify the checksum */
if (!AtaDevIsIdentifyDataValid(IdentifyData))
{
ERR("Identify data CRC error\n");
return DEV_STATUS_FAILED;
}
if (DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED)
{
IsSameDevice = FALSE;
}
else
{
IsSameDevice = (DeviceType == DevExt->DeviceType) &&
(AtaDeviceIdentifyDataEqual(&DevExt->IdentifyDeviceData,
DevExt->Device.LocalBuffer));
}
/* Update identify data */
RtlCopyMemory(&DevExt->IdentifyDeviceData,
DevExt->Device.LocalBuffer,
sizeof(DevExt->IdentifyDeviceData));
DevExt->DeviceType = DeviceType;
if (IsSameDevice)
return DEV_STATUS_SAME_DEVICE;
DevExt->Device.TransportFlags &= ~(DEVICE_IS_ATAPI |
DEVICE_HAS_CDB_INTERRUPT |
DEVICE_NEED_DMA_DIRECTION |
DEVICE_IS_NEC_CDR260);
if (DeviceType == DEV_ATAPI)
{
DevExt->Device.TransportFlags |= DEVICE_IS_ATAPI;
DevExt->Device.CdbSize = AtaDevCdbSizeInWords(&DevExt->IdentifyPacketData);
TRACE("Device has CDB size of %u bytes\n", DevExt->Device.CdbSize * 2);
if (AtaDevHasCdbInterrupt(&DevExt->IdentifyPacketData))
{
TRACE("Device has CDB interrupt\n");
DevExt->Device.TransportFlags |= DEVICE_HAS_CDB_INTERRUPT;
}
if (AtaDevIsDmaDirectionRequired(&DevExt->IdentifyPacketData))
{
TRACE("Device needs DMA DIR\n");
DevExt->Device.TransportFlags |= DEVICE_NEED_DMA_DIRECTION;
}
/* The NEC CDR-260 string is not byteswapped */
if (DevExt->IdentifyPacketData.ModelNumber[0] == 'N' &&
DevExt->IdentifyPacketData.ModelNumber[1] == 'E' &&
DevExt->IdentifyPacketData.ModelNumber[2] == 'C')
{
TRACE("Device is a NEC CDR-260 drive\n");
DevExt->Device.TransportFlags |= DEVICE_IS_NEC_CDR260;
}
Status = AtaDeviceSendInquiry(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return DEV_STATUS_FAILED;
/* Update the inquiry data */
RtlCopyMemory(&DevExt->InquiryData,
DevExt->Device.LocalBuffer,
sizeof(DevExt->InquiryData));
}
return DEV_STATUS_NEW_DEVICE;
}

View File

@@ -0,0 +1,531 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Power management
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* GLOBALS ********************************************************************/
static IO_COMPLETION_ROUTINE AtaPdoPowerUpCompletionRoutine;
static REQUEST_POWER_COMPLETE AtaPdoPowerSetSysStateWithDevStateComplete;
/* FUNCTIONS ******************************************************************/
static
NTSTATUS
AtaPowerCompleteIrp(
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
_In_ PIRP Irp,
_In_ NTSTATUS Status)
{
PoStartNextPowerIrp(Irp);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
IoReleaseRemoveLock(&CommonExt->RemoveLock, Irp);
return Status;
}
VOID
AtaDeviceFlushPowerIrpQueue(
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
while (TRUE)
{
PLIST_ENTRY Entry;
PIRP PowerIrp;
KIRQL OldLevel;
KeAcquireSpinLock(&DevExt->Device.QueueLock, &OldLevel);
if (IsListEmpty(&DevExt->PowerIrpQueueList))
Entry = NULL;
else
Entry = RemoveHeadList(&DevExt->PowerIrpQueueList);
KeReleaseSpinLock(&DevExt->Device.QueueLock, OldLevel);
if (!Entry)
break;
PowerIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
ASSERT(IoGetCurrentIrpStackLocation(PowerIrp)->MinorFunction == IRP_MN_SET_POWER);
AtaPowerCompleteIrp(&DevExt->Common, PowerIrp, STATUS_NO_SUCH_DEVICE);
}
}
UCHAR
AtaDeviceGetFlushCacheCommand(
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
UCHAR Command = 0;
if (!IS_ATAPI(&DevExt->Device))
{
if ((DevExt->Device.DeviceFlags & DEVICE_LBA48) &&
AtaDevHasFlushCacheExt(&DevExt->IdentifyDeviceData))
{
Command = IDE_COMMAND_FLUSH_CACHE_EXT;
}
else if (AtaDevHasFlushCache(&DevExt->IdentifyDeviceData))
{
Command = IDE_COMMAND_FLUSH_CACHE;
}
}
return Command;
}
static
VOID
AtaDeviceCompletePowerIrp(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
POWER_STATE PowerState;
PowerState.DeviceState = DevExt->Common.DevicePowerState;
PoSetPowerState(DevExt->Common.Self, DevicePowerState, PowerState);
AtaPowerCompleteIrp(&DevExt->Common, Irp, STATUS_SUCCESS);
}
static
NTSTATUS
AtaDeviceFlushCache(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
UCHAR Command;
Command = AtaDeviceGetFlushCacheCommand(DevExt);
if (Command == 0)
return STATUS_SUCCESS;
Request->Flags = 0;
Request->TimeOut = 30;
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
Request->TaskFile.Command = Command;
return AtaPortSendRequest(PortData, DevExt);
}
static
NTSTATUS
AtaDeviceSetIdleMode(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
Request->Flags = 0;
Request->TimeOut = 20;
Request->TaskFile.Feature = 0;
Request->TaskFile.Command = IDE_COMMAND_IDLE_IMMEDIATE;
return AtaPortSendRequest(PortData, DevExt);
}
static
NTSTATUS
AtaDeviceSetStandbyMode(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
Request->Flags = 0;
Request->TimeOut = 20;
Request->TaskFile.Command = IDE_COMMAND_STANDBY_IMMEDIATE;
return AtaPortSendRequest(PortData, DevExt);
}
NTSTATUS
AtaPortDeviceProcessPowerChange(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PLIST_ENTRY Entry;
PIRP PowerIrp;
PIO_STACK_LOCATION IoStack;
DEVICE_POWER_STATE NewState;
KIRQL OldLevel;
NTSTATUS Status;
KeAcquireSpinLock(&DevExt->Device.QueueLock, &OldLevel);
if (IsListEmpty(&DevExt->PowerIrpQueueList))
Entry = NULL;
else
Entry = RemoveHeadList(&DevExt->PowerIrpQueueList);
KeReleaseSpinLock(&DevExt->Device.QueueLock, OldLevel);
if (!Entry)
return STATUS_SUCCESS;
PowerIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
IoStack = IoGetCurrentIrpStackLocation(PowerIrp);
ASSERT(IoStack->MinorFunction == IRP_MN_SET_POWER);
NewState = IoStack->Parameters.Power.State.DeviceState;
if (NewState == DevExt->Common.DevicePowerState)
{
AtaDeviceCompletePowerIrp(DevExt, PowerIrp);
return STATUS_SUCCESS;
}
if (NewState != PowerDeviceD3)
{
AtaReqThawQueue(DevExt, QUEUE_FLAG_FROZEN_POWER);
/* Power up the device */
Status = AtaDeviceSetIdleMode(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
/* Restore the software settings after power up */
_InterlockedOr(&DevExt->Worker.EventsPending, ACTION_DEVICE_CONFIG);
_InterlockedOr(&PortData->Worker.EventsPending, ACTION_DEVICE_CONFIG);
}
else
{
AtaReqFreezeQueue(DevExt, QUEUE_FLAG_FROZEN_POWER);
/* Flush device caches */
Status = AtaDeviceFlushCache(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
/* Power down the device */
Status = AtaDeviceSetStandbyMode(PortData, DevExt);
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
return Status;
}
AtaDeviceCompletePowerIrp(DevExt, PowerIrp);
DevExt->Common.DevicePowerState = NewState;
return Status;
}
NTSTATUS
AtaPortCheckDevicePowerState(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
NTSTATUS Status;
if (DevExt->Common.DevicePowerState == PowerDeviceD0)
return STATUS_SUCCESS;
INFO("CH %lu: Powering up idle device '%s'\n", PortData->PortNumber, DevExt->FriendlyName);
Status = AtaDeviceSetIdleMode(PortData, DevExt);
if (!NT_SUCCESS(Status))
{
/*
* If the power on device has failed, the next command can still work.
* The media access will result in a transition from the PM1:Idle/PM2:Standby state
* to the PM0:Active state by the ATA spec.
*/
ERR("CH %lu: Failed to power up device '%s' %lx\n",
PortData->PortNumber, DevExt->FriendlyName, Status);
}
else
{
DevExt->Common.DevicePowerState = PowerDeviceD0;
}
return Status;
}
static
VOID
AtaPdoQueuePowerIrp(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
KIRQL OldLevel;
KeAcquireSpinLock(&DevExt->Device.QueueLock, &OldLevel);
InsertTailList(&DevExt->PowerIrpQueueList, &Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLock(&DevExt->Device.QueueLock, OldLevel);
AtaDeviceQueueEvent(DevExt->Device.PortData, DevExt, ACTION_DEVICE_POWER);
}
static
NTSTATUS
NTAPI
AtaPdoPowerUpCompletionRoutine(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Context)
{
PATAPORT_DEVICE_EXTENSION DevExt = Context;
NTSTATUS Status;
UNREFERENCED_PARAMETER(DeviceObject);
Status = Irp->IoStatus.Status;
INFO("Powering up device, lower driver status %lx\n", Status);
if (!NT_SUCCESS(Status))
{
PoStartNextPowerIrp(Irp);
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
return STATUS_CONTINUE_COMPLETION;
}
AtaPdoQueuePowerIrp(DevExt, Irp);
/* Defer IRP completion */
return STATUS_MORE_PROCESSING_REQUIRED;
}
static
NTSTATUS
AtaPdoPowerSetDevicePowerState(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
DEVICE_POWER_STATE NewState;
IoStack = IoGetCurrentIrpStackLocation(Irp);
NewState = IoStack->Parameters.Power.State.DeviceState;
IoMarkIrpPending(Irp);
if (NewState < DevExt->Common.DevicePowerState)
{
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
AtaPdoPowerUpCompletionRoutine,
DevExt,
TRUE,
TRUE,
TRUE);
(VOID)PoCallDriver(ChanExt->Common.Self, Irp);
}
else
{
INFO("Powering down device\n");
AtaPdoQueuePowerIrp(DevExt, Irp);
}
return STATUS_PENDING;
}
static
VOID
NTAPI
AtaPdoPowerSetSysStateWithDevStateComplete(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ UCHAR MinorFunction,
_In_ POWER_STATE PowerState,
_In_opt_ PVOID Context,
_In_ PIO_STATUS_BLOCK IoStatus)
{
PATAPORT_DEVICE_EXTENSION DevExt = DeviceObject->DeviceExtension;
PIRP Irp = Context;
UNREFERENCED_PARAMETER(MinorFunction);
if (DevExt->Common.SystemPowerState != PowerState.SystemState)
{
DevExt->Common.SystemPowerState = PowerState.SystemState;
PoSetPowerState(DeviceObject, SystemPowerState, PowerState);
}
AtaPowerCompleteIrp(&DevExt->Common, Irp, IoStatus->Status);
}
static
VOID
AtaPdoPowerGetDevStateForSysState(
_In_ SYSTEM_POWER_STATE SystemState,
_Out_ PDEVICE_POWER_STATE DeviceState)
{
if (SystemState == PowerSystemWorking)
{
*DeviceState = PowerDeviceD0;
}
else
{
*DeviceState = PowerDeviceD3;
}
}
static
NTSTATUS
AtaPdoPowerSetSystemPowerState(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
POWER_STATE NewPowerState;
SYSTEM_POWER_STATE NewSystemState;
IoStack = IoGetCurrentIrpStackLocation(Irp);
NewSystemState = IoStack->Parameters.Power.State.SystemState;
IoMarkIrpPending(Irp);
AtaPdoPowerGetDevStateForSysState(NewSystemState, &NewPowerState.DeviceState);
Status = PoRequestPowerIrp(DevExt->Common.Self,
IRP_MN_SET_POWER,
NewPowerState,
AtaPdoPowerSetSysStateWithDevStateComplete,
Irp,
NULL);
if (!NT_SUCCESS(Status))
{
IO_STATUS_BLOCK IoStatusBlock;
ERR("Failed to change power state for device '%s' %lx\n", DevExt->FriendlyName, Status);
IoStatusBlock.Status = Status;
AtaPdoPowerSetSysStateWithDevStateComplete(DevExt->Common.Self,
IRP_MN_SET_POWER,
NewPowerState,
Irp,
&IoStatusBlock);
}
return Status;
}
static
NTSTATUS
AtaPdoPower(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_Inout_ PIRP Irp)
{
// FIXME: Investigate how to fix this kernel bug
// *** Assertion failed: KeGetCurrentThread()->SystemAffinityActive == FALSE
// *** Source File: ../ntoskrnl/ke/dpc.c, line 926
#if 1
NTSTATUS Status;
UNREFERENCED_PARAMETER(AtaPdoPowerSetSystemPowerState);
UNREFERENCED_PARAMETER(AtaPdoPowerGetDevStateForSysState);
UNREFERENCED_PARAMETER(AtaPdoPowerSetSysStateWithDevStateComplete);
UNREFERENCED_PARAMETER(AtaPdoPowerSetDevicePowerState);
UNREFERENCED_PARAMETER(AtaPdoPowerUpCompletionRoutine);
UNREFERENCED_PARAMETER(AtaPdoQueuePowerIrp);
Status = Irp->IoStatus.Status;
PoStartNextPowerIrp(Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
#else
NTSTATUS Status;
PIO_STACK_LOCATION IoStack;
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, Irp);
if (!NT_SUCCESS(Status))
{
PoStartNextPowerIrp(Irp);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
IoStack = IoGetCurrentIrpStackLocation(Irp);
switch (IoStack->MinorFunction)
{
case IRP_MN_QUERY_POWER:
{
switch (IoStack->Parameters.Power.Type)
{
case SystemPowerState:
case DevicePowerState:
Status = AtaPowerCompleteIrp(&DevExt->Common, Irp, STATUS_SUCCESS);
break;
default:
Status = AtaPowerCompleteIrp(&DevExt->Common, Irp, Irp->IoStatus.Status);
break;
}
break;
}
case IRP_MN_SET_POWER:
{
switch (IoStack->Parameters.Power.Type)
{
case SystemPowerState:
Status = AtaPdoPowerSetSystemPowerState(DevExt, Irp);
break;
case DevicePowerState:
Status = AtaPdoPowerSetDevicePowerState(DevExt, Irp);
break;
default:
Status = AtaPowerCompleteIrp(&DevExt->Common, Irp, Irp->IoStatus.Status);
break;
}
break;
}
default:
Status = AtaPowerCompleteIrp(&DevExt->Common, Irp, Irp->IoStatus.Status);
break;
}
return Status;
#endif
}
static
NTSTATUS
AtaFdoPower(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_Inout_ PIRP Irp)
{
NTSTATUS Status;
Status = IoAcquireRemoveLock(&ChanExt->Common.RemoveLock, Irp);
if (!NT_SUCCESS(Status))
{
PoStartNextPowerIrp(Irp);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
Status = PoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
return Status;
}
NTSTATUS
NTAPI
AtaDispatchPower(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
if (IS_FDO(DeviceObject->DeviceExtension))
return AtaFdoPower(DeviceObject->DeviceExtension, Irp);
else
return AtaPdoPower(DeviceObject->DeviceExtension, Irp);
}

View File

@@ -0,0 +1,354 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: ATA PIO and DMA timings support
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*
* REFERENCES: For more details refer to the
* "Intel ICH0~ICH5 Programmer's Reference Manual (PRM) 298600-004":
* "Determining a Drive's Transfer Rate Capabilities"
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* GLOBALS ********************************************************************/
static const ULONG AtapModeToCycleTime[] =
{
/* 0 1 2 3 4 PIO_MODE */
600, 383, 240, 180, 120,
/* 0 1 2 SWDMA_MODE */
960, 480, 240,
/* 0 1 2 MWDMA_MODE */
480, 150, 120,
/* 0 1 2 3 4 5 6 UDMA_MODE */
120, 80, 60, 45, 30, 20, 15
};
/* FUNCTIONS ******************************************************************/
static
VOID
AtaTimQueryPioModeSupport(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ ULONG AllowedModesMask)
{
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
ULONG SupportedMode, BestMode, CurrentMode, CycleTime;
/* Find the best possible PIO mode */
if (IdentifyData->TranslationFieldsValid & 0x2)
{
/* PIO 3-4 */
SupportedMode = IdentifyData->AdvancedPIOModes & 0x3;
SupportedMode <<= PIO_MODE(3);
/* The device is assumed to have PIO 0-2 */
SupportedMode |= PIO_MODE0 | PIO_MODE1 | PIO_MODE2;
SupportedMode &= AllowedModesMask;
if (IdentifyData->Capabilities.IordySupported)
CycleTime = IdentifyData->MinimumPIOCycleTimeIORDY;
else
CycleTime = IdentifyData->MinimumPIOCycleTime;
/*
* Any device that supports PIO 3 or above
* should support word 67 and word 68, but be defensive.
*/
if (CycleTime == 0)
{
WARN("Device '%s' returned zero cycle time\n", DevExt->FriendlyName);
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
/* Fall back to the default value */
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
CycleTime = AtapModeToCycleTime[BestMode];
}
}
else
{
/* PIO 0-4 (maximum supported mode) */
SupportedMode = IdentifyData->ObsoleteWords51[0] >> 8;
if (SupportedMode > PIO_MODE(4))
SupportedMode = PIO_MODE(0);
/* Convert the maximum mode to a mask */
SupportedMode = (1 << (SupportedMode + 1)) - 1;
SupportedMode &= AllowedModesMask;
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
CycleTime = AtapModeToCycleTime[BestMode];
}
DevExt->TransferModeSupportedBitmap |= SupportedMode;
DevExt->MinimumPioCycleTime = CycleTime;
/*
* Since there is no way to obtain the current PIO mode from identify data
* we just look for the fastest supported mode.
*/
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
CurrentMode = BestMode;
DevExt->TransferModeCurrentBitmap |= 1 << CurrentMode;
}
static
VOID
AtaTimQuerySwDmaModeSupport(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ ULONG AllowedModesMask)
{
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
ULONG SupportedMode, BestMode, CurrentMode, CycleTime;
CycleTime = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED;
/* Find the current active SWDMA mode */
CurrentMode = ((IdentifyData->ObsoleteWord62 & 0xFF00) >> 16) & 0x7;
if (CurrentMode != 0)
{
NT_VERIFY(_BitScanReverse(&CurrentMode, CurrentMode));
CurrentMode += SWDMA_MODE(0);
DevExt->TransferModeCurrentBitmap |= 1 << CurrentMode;
}
/* Find the best possible SWDMA mode */
SupportedMode = (IdentifyData->ObsoleteWord62 & 0x00FF) & 0x7;
SupportedMode <<= SWDMA_MODE(0);
SupportedMode &= AllowedModesMask;
if (SupportedMode != 0)
{
DevExt->TransferModeSupportedBitmap |= SupportedMode;
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
CycleTime = AtapModeToCycleTime[BestMode];
}
DevExt->MinimumSingleWordDmaCycleTime = CycleTime;
}
static
VOID
AtaTimQueryMwDmaModeSupport(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ ULONG AllowedModesMask)
{
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
ULONG SupportedMode, BestMode, CurrentMode, CycleTime;
CycleTime = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED;
/* Find the current active MWDMA mode */
CurrentMode = IdentifyData->MultiWordDMAActive & 0x7;
if (CurrentMode != 0)
{
NT_VERIFY(_BitScanReverse(&CurrentMode, CurrentMode));
CurrentMode += MWDMA_MODE(0);
DevExt->TransferModeCurrentBitmap |= 1 << CurrentMode;
}
/* Find the best possible MWDMA mode */
SupportedMode = IdentifyData->MultiWordDMASupport & 0x7;
SupportedMode <<= MWDMA_MODE(0);
SupportedMode &= AllowedModesMask;
if (SupportedMode != 0)
{
DevExt->TransferModeSupportedBitmap |= SupportedMode;
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
/* Prefer the minimum cycle time, if words 64-70 are valid */
if ((IdentifyData->TranslationFieldsValid & 0x2) &&
(IdentifyData->MinimumMWXferCycleTime != 0) &&
(IdentifyData->RecommendedMWXferCycleTime != 0))
{
CycleTime = IdentifyData->MinimumMWXferCycleTime;
}
else
{
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
CycleTime = AtapModeToCycleTime[BestMode];
}
}
DevExt->MinimumMultiWordDmaCycleTime = CycleTime;
}
static
VOID
AtaTimQueryUDmaModeSupport(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ ULONG AllowedModesMask)
{
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
ULONG SupportedMode, BestMode, CurrentMode, CycleTime;
CycleTime = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED;
/* Word 88 is not valid */
if (!(IdentifyData->TranslationFieldsValid & 0x4))
goto Exit;
/* Find the current active UDMA mode */
CurrentMode = IdentifyData->UltraDMAActive & 0x7F;
if (CurrentMode != 0)
{
NT_VERIFY(_BitScanReverse(&CurrentMode, CurrentMode));
CurrentMode += UDMA_MODE(0);
DevExt->TransferModeCurrentBitmap |= 1 << CurrentMode;
}
/* Find the best possible UDMA mode */
SupportedMode = IdentifyData->UltraDMASupport & 0x7F;
SupportedMode <<= UDMA_MODE(0);
SupportedMode &= AllowedModesMask;
if (SupportedMode != 0)
{
DevExt->TransferModeSupportedBitmap |= SupportedMode;
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
CycleTime = AtapModeToCycleTime[BestMode];
}
Exit:
DevExt->MinimumUltraDmaCycleTime = CycleTime;
}
static
VOID
AtaTimDumpTimingInfo(
_In_ PATAPORT_PORT_DATA PortData,
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
INFO("CH %lu: Device %u '%s'\n"
"Cycle time UDMA[%ld] MWDMA[%ld] SWDMA[%ld] PIO[%ld]\n"
"Supported modes 0x%08lX\n"
"Active modes 0x%08lX\n"
"Selected modes 0x%08lX\n"
"IOREADY %u\n",
PortData->PortNumber,
DevExt->Device.AtaScsiAddress.TargetId,
DevExt->FriendlyName,
(LONG)DevExt->MinimumUltraDmaCycleTime,
(LONG)DevExt->MinimumMultiWordDmaCycleTime,
(LONG)DevExt->MinimumSingleWordDmaCycleTime,
(LONG)DevExt->MinimumPioCycleTime,
DevExt->TransferModeSupportedBitmap,
DevExt->TransferModeCurrentBitmap,
DevExt->TransferModeSelectedBitmap,
DevExt->IdentifyDeviceData.Capabilities.IordySupported);
}
VOID
AtaPortSelectTimings(
_In_ PATAPORT_PORT_DATA PortData,
_In_ BOOLEAN ForceCompatibleTimings)
{
PATAPORT_CHANNEL_EXTENSION ChanExt;
ULONG i;
CHANNEL_DEVICE_CONFIG Config[ATA_MAX_DEVICE] = { 0 };
PCHANNEL_DEVICE_CONFIG DeviceList[ATA_MAX_DEVICE] = { 0 };
if (ForceCompatibleTimings)
{
for (i = 0; i < ATA_MAX_DEVICE; ++i)
{
/* Fake device presence */
DeviceList[i] = &Config[i];
/* Disable DMA and select compatible timings */
Config[i].SupportedModes = PIO_MODE0;
Config[i].CurrentModes = PIO_MODE0;
Config[i].MinPioCycleTime = AtapModeToCycleTime[PIO_MODE(0)];
Config[i].MinSwDmaCycleTime = AtapModeToCycleTime[SWDMA_MODE(0)];
Config[i].MinMwDmaCycleTime = AtapModeToCycleTime[MWDMA_MODE(0)];
Config[i].IsFixedDisk = TRUE;
Config[i].IoReadySupported = FALSE;
Config[i].IsNewDevice = FALSE;
Config[i].FriendlyName = "";
}
/* Call the chipset driver */
PortData->SetTransferMode(PortData->ChannelContext, DeviceList);
return;
}
ChanExt = CONTAINING_RECORD(PortData, ATAPORT_CHANNEL_EXTENSION, PortData);
for (i = 0; i < ATA_MAX_DEVICE; ++i)
{
PATAPORT_DEVICE_EXTENSION DevExt;
ULONG AllowedModesMask;
DevExt = AtaFdoFindDeviceByPath(ChanExt,
AtaMarshallScsiAddress(PortData->PortNumber, i, 0),
NULL);
if (!DevExt)
continue;
DevExt->TransferModeCurrentBitmap = 0;
DevExt->TransferModeSupportedBitmap = 0;
AllowedModesMask = DevExt->TransferModeUserAllowedMask;
AllowedModesMask &= DevExt->TransferModeAllowedMask;
/* Mode cannot be slower than PIO0 */
AllowedModesMask |= PIO_MODE0;
AtaTimQueryPioModeSupport(DevExt, AllowedModesMask);
AtaTimQuerySwDmaModeSupport(DevExt, AllowedModesMask);
AtaTimQueryMwDmaModeSupport(DevExt, AllowedModesMask);
AtaTimQueryUDmaModeSupport(DevExt, AllowedModesMask);
DeviceList[i] = &Config[i];
Config[i].CurrentModes = DevExt->TransferModeCurrentBitmap;
Config[i].SupportedModes = DevExt->TransferModeSupportedBitmap;
Config[i].MinPioCycleTime = DevExt->MinimumPioCycleTime;
Config[i].MinSwDmaCycleTime = DevExt->MinimumSingleWordDmaCycleTime;
Config[i].MinMwDmaCycleTime = DevExt->MinimumMultiWordDmaCycleTime;
Config[i].IsFixedDisk = !IS_ATAPI(&DevExt->Device);
Config[i].IoReadySupported = !!DevExt->IdentifyDeviceData.Capabilities.IordySupported;
Config[i].IsNewDevice = !(DevExt->Device.DeviceFlags & DEVICE_PNP_STARTED);
Config[i].FriendlyName = DevExt->FriendlyName;
}
/* Call the chipset driver */
PortData->SetTransferMode(PortData->ChannelContext, DeviceList);
/* Save the result */
for (i = 0; i < ATA_MAX_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG DeviceConfig = DeviceList[i];
PATAPORT_DEVICE_EXTENSION DevExt;
if (!DeviceConfig)
continue;
DevExt = AtaFdoFindDeviceByPath(ChanExt,
AtaMarshallScsiAddress(PortData->PortNumber, i, 0),
NULL);
ASSERT(DevExt);
DevExt->TransferModeSelectedBitmap = 1 << DeviceConfig->PioMode;
if (DeviceConfig->DmaMode != PIO_MODE(0))
{
DevExt->TransferModeSelectedBitmap |= 1 << DeviceConfig->DmaMode;
}
AtaTimDumpTimingInfo(PortData, DevExt);
}
}

View File

@@ -0,0 +1,658 @@
/*
* 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;
}

View File

@@ -0,0 +1,758 @@
/*
* 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 <di.sean@protonmail.com>
*/
/* 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->Common.Self,
&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);
}

View File

@@ -0,0 +1,940 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: I/O control handling
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* GLOBALS ********************************************************************/
#define AtaVerifyInBuffer(IoStack, Length) \
((IoStack)->Parameters.DeviceIoControl.InputBufferLength >= (Length))
#define AtaVerifyOutBuffer(IoStack, Length) \
((IoStack)->Parameters.DeviceIoControl.OutputBufferLength >= (Length))
/* FUNCTIONS ******************************************************************/
static
CODE_SEG("PAGE")
BOOLEAN
AtaCheckPropertyQuery(
_Inout_ PIRP Irp,
_In_ ULONG DescriptorSize,
_Out_ NTSTATUS* Status)
{
PSTORAGE_PROPERTY_QUERY PropertyQuery = Irp->AssociatedIrp.SystemBuffer;
PSTORAGE_DESCRIPTOR_HEADER DescriptorHeader;
PIO_STACK_LOCATION IoStack;
PAGED_CODE();
IoStack = IoGetCurrentIrpStackLocation(Irp);
if (!AtaVerifyInBuffer(IoStack, sizeof(*PropertyQuery)))
{
*Status = STATUS_INVALID_PARAMETER;
return FALSE;
}
/* Check the type of a property query */
if (PropertyQuery->QueryType != PropertyStandardQuery &&
PropertyQuery->QueryType != PropertyExistsQuery)
{
*Status = STATUS_NOT_SUPPORTED;
return FALSE;
}
/* The requested property is supported */
if (PropertyQuery->QueryType == PropertyExistsQuery)
{
*Status = STATUS_SUCCESS;
return FALSE;
}
if (!AtaVerifyOutBuffer(IoStack, sizeof(*DescriptorHeader)))
{
*Status = STATUS_INFO_LENGTH_MISMATCH;
return FALSE;
}
/* Caller can determine required size based upon DescriptorHeader */
if (!AtaVerifyOutBuffer(IoStack, DescriptorSize))
{
DescriptorHeader = (PSTORAGE_DESCRIPTOR_HEADER)PropertyQuery;
DescriptorHeader->Version = DescriptorSize;
DescriptorHeader->Size = DescriptorSize;
Irp->IoStatus.Information = sizeof(*DescriptorHeader);
*Status = STATUS_SUCCESS;
return FALSE;
}
return TRUE;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryStorageDeviceProperty(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PINQUIRYDATA InquiryData = &DevExt->InquiryData;
PSTORAGE_DEVICE_DESCRIPTOR DeviceDescriptor;
NTSTATUS Status;
ULONG DescriptorSize, RawPropertiesLength, Length1, Length2, Length3;
PAGED_CODE();
Length1 = (ULONG)strlen(DevExt->FriendlyName) + sizeof(ANSI_NULL);
Length2 = (ULONG)strlen(DevExt->RevisionNumber) + sizeof(ANSI_NULL);
Length3 = (ULONG)strlen(DevExt->SerialNumber) + sizeof(ANSI_NULL);
RawPropertiesLength = Length1 + Length2 + Length3;
DescriptorSize = FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR, RawDeviceProperties) +
RawPropertiesLength;
if (!AtaCheckPropertyQuery(Irp, DescriptorSize, &Status))
return Status;
DeviceDescriptor = Irp->AssociatedIrp.SystemBuffer;
DeviceDescriptor->RawPropertiesLength = RawPropertiesLength;
DeviceDescriptor->Version = sizeof(*DeviceDescriptor);
DeviceDescriptor->Size = DescriptorSize;
DeviceDescriptor->DeviceType = InquiryData->DeviceType;
DeviceDescriptor->DeviceTypeModifier = InquiryData->DeviceTypeModifier;
DeviceDescriptor->RemovableMedia = InquiryData->RemovableMedia;
DeviceDescriptor->CommandQueueing = FALSE; // Disable request tagging
if (DevExt->Device.PortData->PortFlags & PORT_FLAG_IS_AHCI)
DeviceDescriptor->BusType = BusTypeSata;
else
DeviceDescriptor->BusType = BusTypeAta;
/* Property 1: The vendor ID. We return a NULL string here */
DeviceDescriptor->VendorIdOffset = 0;
/* Property 2: The product ID */
DeviceDescriptor->ProductIdOffset =
FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR, RawDeviceProperties);
RtlCopyMemory((PUCHAR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->ProductIdOffset),
DevExt->FriendlyName,
Length1);
/* Property 3: The product revision */
DeviceDescriptor->ProductRevisionOffset = DeviceDescriptor->ProductIdOffset + Length1;
RtlCopyMemory((PUCHAR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->ProductRevisionOffset),
DevExt->RevisionNumber,
Length2);
/* Property 4: The serial number */
DeviceDescriptor->SerialNumberOffset = DeviceDescriptor->ProductRevisionOffset + Length2;
RtlCopyMemory((PUCHAR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->SerialNumberOffset),
DevExt->SerialNumber,
Length3);
Irp->IoStatus.Information = DescriptorSize;
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryStorageAccessAlignmentProperty(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR AccessAlignmentDescriptor;
ULONG LogicalSectorsPerPhysicalSector, Dummy;
NTSTATUS Status;
PAGED_CODE();
if (IS_ATAPI(&DevExt->Device))
return STATUS_NOT_SUPPORTED;
if (!AtaCheckPropertyQuery(Irp, sizeof(*AccessAlignmentDescriptor), &Status))
return Status;
AccessAlignmentDescriptor = Irp->AssociatedIrp.SystemBuffer;
AccessAlignmentDescriptor->Version = sizeof(*AccessAlignmentDescriptor);
AccessAlignmentDescriptor->Size = sizeof(*AccessAlignmentDescriptor);
AccessAlignmentDescriptor->BytesPerCacheLine = 0;
AccessAlignmentDescriptor->BytesOffsetForCacheAlignment = 0;
AccessAlignmentDescriptor->BytesPerLogicalSector = DevExt->Device.SectorSize;
LogicalSectorsPerPhysicalSector =
AtaDevLogicalSectorsPerPhysicalSector(&DevExt->IdentifyDeviceData, &Dummy);
AccessAlignmentDescriptor->BytesPerPhysicalSector =
DevExt->Device.SectorSize * LogicalSectorsPerPhysicalSector;
AccessAlignmentDescriptor->BytesOffsetForSectorAlignment =
DevExt->Device.SectorSize * AtaDevLogicalSectorAlignment(&DevExt->IdentifyDeviceData);
Irp->IoStatus.Information = sizeof(*AccessAlignmentDescriptor);
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryStorageDeviceSeekPenaltyProperty(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PDEVICE_SEEK_PENALTY_DESCRIPTOR PenaltyDescriptor;
NTSTATUS Status;
BOOLEAN IncursSeekPenalty;
PAGED_CODE();
if (!AtaCheckPropertyQuery(Irp, sizeof(*PenaltyDescriptor), &Status))
return Status;
if (AtaDevIsRotatingDevice(&DevExt->IdentifyDeviceData))
IncursSeekPenalty = TRUE;
else if (AtaDevIsSsd(&DevExt->IdentifyDeviceData))
IncursSeekPenalty = FALSE;
else
return STATUS_UNSUCCESSFUL; // Undetermined
PenaltyDescriptor = Irp->AssociatedIrp.SystemBuffer;
PenaltyDescriptor->Version = sizeof(*PenaltyDescriptor);
PenaltyDescriptor->Size = sizeof(*PenaltyDescriptor);
PenaltyDescriptor->IncursSeekPenalty = IncursSeekPenalty;
Irp->IoStatus.Information = sizeof(*PenaltyDescriptor);
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryStorageDeviceTrimProperty(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PDEVICE_TRIM_DESCRIPTOR TrimDescriptor;
NTSTATUS Status;
PAGED_CODE();
if (IS_ATAPI(&DevExt->Device))
return STATUS_NOT_SUPPORTED;
if (!AtaCheckPropertyQuery(Irp, sizeof(*TrimDescriptor), &Status))
return Status;
TrimDescriptor = Irp->AssociatedIrp.SystemBuffer;
TrimDescriptor->Version = sizeof(*TrimDescriptor);
TrimDescriptor->Size = sizeof(*TrimDescriptor);
TrimDescriptor->TrimEnabled = AtaDevHasTrimFunction(&DevExt->IdentifyDeviceData);
Irp->IoStatus.Information = sizeof(*TrimDescriptor);
return STATUS_SUCCESS;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaPdoHandleStorageQueryProperty(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack,
_Out_ PBOOLEAN ForwardToFdo)
{
PSTORAGE_PROPERTY_QUERY PropertyQuery;
NTSTATUS Status;
PAGED_CODE();
if (!AtaVerifyInBuffer(IoStack, sizeof(*PropertyQuery)))
{
ERR("Buffer too small\n");
return STATUS_BUFFER_TOO_SMALL;
}
PropertyQuery = Irp->AssociatedIrp.SystemBuffer;
switch (PropertyQuery->PropertyId)
{
case StorageDeviceProperty:
Status = AtaPdoQueryStorageDeviceProperty(DevExt, Irp);
break;
case StorageAccessAlignmentProperty:
Status = AtaPdoQueryStorageAccessAlignmentProperty(DevExt, Irp);
break;
case StorageDeviceSeekPenaltyProperty:
Status = AtaPdoQueryStorageDeviceSeekPenaltyProperty(DevExt, Irp);
break;
case StorageDeviceTrimProperty:
Status = AtaPdoQueryStorageDeviceTrimProperty(DevExt, Irp);
break;
default:
*ForwardToFdo = TRUE;
return STATUS_MORE_PROCESSING_REQUIRED;
}
*ForwardToFdo = FALSE;
return Status;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaPdoHandleGetScsiAddress(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PATAPORT_CHANNEL_EXTENSION ChanExt;
PSCSI_ADDRESS ScsiAddress;
ATA_SCSI_ADDRESS AtaScsiAddress;
PAGED_CODE();
if (!AtaVerifyOutBuffer(IoStack, sizeof(*ScsiAddress)))
{
ERR("Buffer too small\n");
return STATUS_BUFFER_TOO_SMALL;
}
ChanExt = DevExt->Common.FdoExt;
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
ScsiAddress = Irp->AssociatedIrp.SystemBuffer;
ScsiAddress->Length = sizeof(*ScsiAddress);
ScsiAddress->PortNumber = ChanExt->ScsiPortNumber;
ScsiAddress->PathId = AtaScsiAddress.PathId;
ScsiAddress->TargetId = AtaScsiAddress.TargetId;
ScsiAddress->Lun = AtaScsiAddress.Lun;
Irp->IoStatus.Information = sizeof(*ScsiAddress);
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoSendHbaControl(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PSRB_IO_CONTROL SrbControl,
_In_ ULONG BufferSize)
{
PSCSI_REQUEST_BLOCK Srb;
IO_STATUS_BLOCK IoStatusBlock;
PIRP Irp;
KEVENT Event;
NTSTATUS Status;
LARGE_INTEGER LargeInt;
PIO_STACK_LOCATION IoStack;
PAGED_CODE();
Srb = ExAllocatePoolZero(NonPagedPool, sizeof(*Srb), ATAPORT_TAG);
if (!Srb)
return STATUS_INSUFFICIENT_RESOURCES;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
LargeInt.QuadPart = 1; // For compatibility only
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SCSI,
DevExt->Common.Self,
SrbControl,
BufferSize,
&LargeInt,
&Event,
&IoStatusBlock);
if (!Irp)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
Srb->OriginalRequest = Irp;
Srb->Function = SRB_FUNCTION_IO_CONTROL;
Srb->Length = sizeof(*Srb);
Srb->TargetId = DevExt->Device.AtaScsiAddress.TargetId;
Srb->Lun = DevExt->Device.AtaScsiAddress.Lun;
Srb->PathId = DevExt->Device.AtaScsiAddress.PathId;
Srb->TimeOutValue = SrbControl->Timeout;
Srb->SrbFlags = SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_DATA_IN;
Srb->DataBuffer = SrbControl;
Srb->DataTransferLength = BufferSize;
IoStack = IoGetNextIrpStackLocation(Irp);
IoStack->Parameters.Scsi.Srb = Srb;
Status = IoCallDriver(DevExt->Common.Self, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
Cleanup:
ExFreePoolWithTag(Srb, ATAPORT_TAG);
return Status;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaPdoHandleStorageManageDataSetAttributes(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PAGED_CODE();
// TODO: Implement
return Irp->IoStatus.Status;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaPdoHandleScsiMiniport(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_Inout_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PSRB_IO_CONTROL SrbControl;
NTSTATUS Status;
ULONG CmdBufferSize, BufferSize;
PAGED_CODE();
if (!AtaVerifyInBuffer(IoStack, sizeof(*SrbControl)))
{
ERR("Buffer too small\n");
return STATUS_BUFFER_TOO_SMALL;
}
SrbControl = Irp->AssociatedIrp.SystemBuffer;
if (SrbControl->HeaderLength != sizeof(*SrbControl))
{
ERR("Unknown structure size %lu\n", SrbControl->HeaderLength);
return STATUS_REVISION_MISMATCH;
}
Status = RtlULongAdd(SrbControl->Length, sizeof(*SrbControl), &BufferSize);
if (!NT_SUCCESS(Status))
{
ERR("Too large buffer 0x%lx\n", SrbControl->Length);
return Status;
}
CmdBufferSize = IoStack->Parameters.DeviceIoControl.InputBufferLength;
CmdBufferSize = max(CmdBufferSize, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
if (CmdBufferSize < BufferSize)
{
ERR("Cmd buffer too small\n");
return STATUS_BUFFER_TOO_SMALL;
}
if (RtlEqualMemory(SrbControl->Signature, "SCSIDISK", sizeof("SCSIDISK") - 1))
Status = AtaPdoSendHbaControl(DevExt, SrbControl, BufferSize);
else
Status = STATUS_INVALID_PARAMETER;
return Status;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaPdoHandleAtaPassthrough(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_Inout_ PIRP Irp)
{
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
PAGED_CODE();
return SptiHandleAtaPassthru(DevExt->Common.Self,
Irp,
ChanExt->PortData.MaximumTransferLength,
ChanExt->PortData.MaximumPhysicalPages);
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaPdoHandleScsiPassthrough(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_Inout_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PATAPORT_CHANNEL_EXTENSION ChanExt;
PAGED_CODE();
/* Skip requests that bypassed the class driver. See also cdrom!RequestHandleScsiPassThrough */
if ((IoStack->MinorFunction == 0) && DevExt->DeviceClaimed)
return STATUS_INVALID_DEVICE_REQUEST;
ChanExt = DevExt->Common.FdoExt;
return SptiHandleScsiPassthru(DevExt->Common.Self,
Irp,
ChanExt->PortData.MaximumTransferLength,
ChanExt->PortData.MaximumPhysicalPages);
}
static
NTSTATUS
AtaPdoDeviceControl(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_Inout_ PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
BOOLEAN ForwardToFdo = FALSE;
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, Irp);
if (!NT_SUCCESS(Status))
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
IoStack = IoGetCurrentIrpStackLocation(Irp);
switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_STORAGE_QUERY_PROPERTY:
Status = AtaPdoHandleStorageQueryProperty(DevExt, Irp, IoStack, &ForwardToFdo);
break;
case IOCTL_SCSI_GET_ADDRESS:
Status = AtaPdoHandleGetScsiAddress(DevExt, Irp, IoStack);
break;
case IOCTL_SCSI_MINIPORT:
Status = AtaPdoHandleScsiMiniport(DevExt, Irp, IoStack);
break;
case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES:
Status = AtaPdoHandleStorageManageDataSetAttributes(DevExt, Irp, IoStack);
break;
case IOCTL_ATA_PASS_THROUGH:
case IOCTL_ATA_PASS_THROUGH_DIRECT:
Status = AtaPdoHandleAtaPassthrough(DevExt, Irp);
break;
case IOCTL_SCSI_PASS_THROUGH:
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
Status = AtaPdoHandleScsiPassthrough(DevExt, Irp, IoStack);
break;
case IOCTL_SCSI_GET_CAPABILITIES:
case IOCTL_SCSI_GET_INQUIRY_DATA:
ForwardToFdo = TRUE;
break;
default:
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
if (ForwardToFdo)
{
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
IoSkipCurrentIrpStackLocation(Irp);
Status = IoCallDriver(ChanExt->Common.Self, Irp);
}
else
{
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
return Status;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaFdoQueryStorageAdapterProperty(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ PIRP Irp)
{
PSTORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor;
NTSTATUS Status;
PAGED_CODE();
if (!AtaCheckPropertyQuery(Irp, sizeof(*AdapterDescriptor), &Status))
return Status;
AdapterDescriptor = Irp->AssociatedIrp.SystemBuffer;
/*
* This structure has to be zeroed out first
* in order to not confuse the upper storage class drivers.
* Also the new version of the descriptor structure (NT6.2+)
* has two extra fields instead of the unnamed "padding" fields.
*/
RtlZeroMemory(AdapterDescriptor, sizeof(*AdapterDescriptor));
AdapterDescriptor->Version = sizeof(*AdapterDescriptor);
AdapterDescriptor->Size = sizeof(*AdapterDescriptor);
AdapterDescriptor->MaximumTransferLength = ChanExt->PortData.MaximumTransferLength;
AdapterDescriptor->MaximumPhysicalPages = ChanExt->PortData.MaximumPhysicalPages;
AdapterDescriptor->AlignmentMask = ChanExt->Common.Self->AlignmentRequirement;
AdapterDescriptor->AdapterUsesPio = !!(ChanExt->PortData.PortFlags && PORT_FLAG_PIO_ONLY);
AdapterDescriptor->AdapterScansDown = FALSE;
AdapterDescriptor->CommandQueueing = FALSE; // Disable request tagging
AdapterDescriptor->AcceleratedTransfer = FALSE;
if (ChanExt->PortData.PortFlags & PORT_FLAG_IS_AHCI)
AdapterDescriptor->BusType = BusTypeSata;
else
AdapterDescriptor->BusType = BusTypeAta;
AdapterDescriptor->BusMajorVersion = 1;
AdapterDescriptor->BusMinorVersion = 0;
#if (NTDDI_VERSION >= NTDDI_WIN8)
AdapterDescriptor->SrbType = SRB_TYPE_SCSI_REQUEST_BLOCK;
AdapterDescriptor->AddressType = STORAGE_ADDRESS_TYPE_BTL8;
#endif
Irp->IoStatus.Information = sizeof(*AdapterDescriptor);
return STATUS_SUCCESS;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaFdoHandleStorageQueryProperty(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PSTORAGE_PROPERTY_QUERY PropertyQuery;
NTSTATUS Status;
PAGED_CODE();
if (!AtaVerifyInBuffer(IoStack, sizeof(*PropertyQuery)))
{
ERR("Buffer too small\n");
return STATUS_BUFFER_TOO_SMALL;
}
PropertyQuery = Irp->AssociatedIrp.SystemBuffer;
switch (PropertyQuery->PropertyId)
{
case StorageAdapterProperty:
Status = AtaFdoQueryStorageAdapterProperty(ChanExt, Irp);
break;
default:
Status = STATUS_NOT_SUPPORTED;
break;
}
return Status;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaFdoHandleGetScsiCapabilities(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PIO_SCSI_CAPABILITIES Capabilities;
PAGED_CODE();
if (!AtaVerifyOutBuffer(IoStack, sizeof(*Capabilities)))
{
ERR("Buffer too small\n");
return STATUS_BUFFER_TOO_SMALL;
}
Capabilities = Irp->AssociatedIrp.SystemBuffer;
Capabilities->Length = sizeof(*Capabilities);
Capabilities->MaximumTransferLength = ChanExt->PortData.MaximumTransferLength;
Capabilities->MaximumPhysicalPages = ChanExt->PortData.MaximumPhysicalPages;
Capabilities->SupportedAsynchronousEvents = FALSE;
Capabilities->AlignmentMask = ChanExt->Common.Self->AlignmentRequirement;
Capabilities->TaggedQueuing = FALSE;
Capabilities->AdapterScansDown = FALSE;
Capabilities->AdapterUsesPio = !!(ChanExt->PortData.PortFlags && PORT_FLAG_PIO_ONLY);
Irp->IoStatus.Information = sizeof(*Capabilities);
return STATUS_SUCCESS;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaFdoHandleGetScsiInquiryData(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PSCSI_INQUIRY_DATA ScsiInquiryData;
PSCSI_ADAPTER_BUS_INFO ScsiAdapterBusInfo;
ULONG PdoCount, EntrySize, TotalSize;
ATA_SCSI_ADDRESS AtaScsiAddress;
PAGED_CODE();
PdoCount = 0;
AtaScsiAddress.AsULONG = 0;
while (TRUE)
{
PATAPORT_DEVICE_EXTENSION DevExt;
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, FALSE, NULL);
if (!DevExt)
break;
++PdoCount;
}
EntrySize = ALIGN_UP(sizeof(*ScsiInquiryData) - 1 + INQUIRYDATABUFFERSIZE, ULONG);
TotalSize = sizeof(*ScsiAdapterBusInfo) + EntrySize * PdoCount;
TRACE("Total size %lu\n", TotalSize);
if (!AtaVerifyOutBuffer(IoStack, TotalSize))
return STATUS_BUFFER_TOO_SMALL;
Irp->IoStatus.Information = TotalSize;
ScsiAdapterBusInfo = Irp->AssociatedIrp.SystemBuffer;
ScsiAdapterBusInfo->NumberOfBuses = 1;
ScsiAdapterBusInfo->BusData[0].NumberOfLogicalUnits = 0;
ScsiAdapterBusInfo->BusData[0].InitiatorBusId = 0xFF;
ScsiAdapterBusInfo->BusData[0].InquiryDataOffset = sizeof(*ScsiAdapterBusInfo);
ScsiInquiryData = (PSCSI_INQUIRY_DATA)(ScsiAdapterBusInfo + 1);
AtaScsiAddress.AsULONG = 0;
while (TRUE)
{
PATAPORT_DEVICE_EXTENSION DevExt;
PINQUIRYDATA InquiryData;
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, FALSE, Irp);
if (!DevExt)
break;
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
ScsiInquiryData->PathId = AtaScsiAddress.PathId;
ScsiInquiryData->TargetId = AtaScsiAddress.TargetId;
ScsiInquiryData->Lun = AtaScsiAddress.Lun;
ScsiInquiryData->InquiryDataLength = INQUIRYDATABUFFERSIZE;
ScsiInquiryData->DeviceClaimed = DevExt->DeviceClaimed;
ScsiInquiryData->NextInquiryDataOffset =
(ULONG)((ULONG_PTR)ScsiInquiryData + EntrySize - (ULONG_PTR)ScsiAdapterBusInfo);
RtlCopyMemory(ScsiInquiryData->InquiryData,
&DevExt->InquiryData,
INQUIRYDATABUFFERSIZE);
/* This is a legacy IOCTL, limit the standard INQUIRY data to 36 bytes */
InquiryData = (PINQUIRYDATA)ScsiInquiryData->InquiryData;
InquiryData->AdditionalLength =
INQUIRYDATABUFFERSIZE - RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
ScsiInquiryData = (PSCSI_INQUIRY_DATA)((ULONG_PTR)ScsiInquiryData + EntrySize);
if (++ScsiAdapterBusInfo->BusData[0].NumberOfLogicalUnits >= PdoCount)
break;
}
/* Terminate the last entry */
if (ScsiAdapterBusInfo->BusData[0].NumberOfLogicalUnits != 0)
{
ScsiInquiryData = ((PSCSI_INQUIRY_DATA)((ULONG_PTR)ScsiInquiryData - EntrySize));
ScsiInquiryData->NextInquiryDataOffset = 0;
}
else
{
ScsiAdapterBusInfo->BusData[0].InquiryDataOffset = 0;
}
return STATUS_SUCCESS;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaFdoHandleScsiMiniport(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PATAPORT_DEVICE_EXTENSION DevExt;
NTSTATUS Status;
PAGED_CODE();
/* FDO requests are routed directly to 0:0:0 */
DevExt = AtaFdoFindDeviceByPath(ChanExt, AtaMarshallScsiAddress(0, 0, 0), Irp);
if (!DevExt)
return STATUS_NO_SUCH_DEVICE;
Status = AtaPdoHandleScsiMiniport(DevExt, Irp, IoStack);
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
return Status;
}
static
DECLSPEC_NOINLINE_FROM_NOT_PAGED
CODE_SEG("PAGE")
NTSTATUS
AtaFdoHandleScsiPassthrough(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
PSCSI_PASS_THROUGH Spt;
PATAPORT_DEVICE_EXTENSION DevExt;
NTSTATUS Status;
PAGED_CODE();
if (!AtaVerifyInBuffer(IoStack, RTL_SIZEOF_THROUGH_FIELD(SCSI_PASS_THROUGH, Lun)))
{
ERR("Buffer too small\n");
return STATUS_BUFFER_TOO_SMALL;
}
Spt = Irp->AssociatedIrp.SystemBuffer;
DevExt = AtaFdoFindDeviceByPath(ChanExt,
AtaMarshallScsiAddress(Spt->PathId, Spt->TargetId, Spt->Lun),
Irp);
if (!DevExt)
return STATUS_NO_SUCH_DEVICE;
Status = AtaPdoHandleScsiPassthrough(DevExt, Irp, IoStack);
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
return Status;
}
static
NTSTATUS
AtaFdoDeviceControl(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_Inout_ PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
Status = IoAcquireRemoveLock(&ChanExt->Common.RemoveLock, Irp);
if (!NT_SUCCESS(Status))
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
IoStack = IoGetCurrentIrpStackLocation(Irp);
switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_STORAGE_QUERY_PROPERTY:
Status = AtaFdoHandleStorageQueryProperty(ChanExt, Irp, IoStack);
break;
case IOCTL_SCSI_GET_CAPABILITIES:
Status = AtaFdoHandleGetScsiCapabilities(ChanExt, Irp, IoStack);
break;
case IOCTL_SCSI_GET_INQUIRY_DATA:
Status = AtaFdoHandleGetScsiInquiryData(ChanExt, Irp, IoStack);
break;
case IOCTL_SCSI_MINIPORT:
Status = AtaFdoHandleScsiMiniport(ChanExt, Irp, IoStack);
break;
case IOCTL_SCSI_PASS_THROUGH:
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
Status = AtaFdoHandleScsiPassthrough(ChanExt, Irp, IoStack);
break;
case IOCTL_SCSI_RESCAN_BUS:
IoInvalidateDeviceRelations(ChanExt->Pdo, BusRelations);
Status = STATUS_SUCCESS;
break;
default:
{
IoSkipCurrentIrpStackLocation(Irp);
Status = IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
return Status;
}
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
return Status;
}
/*
* For storage drivers this dispatch function must be not paged,
* because it must be present when it is received unknown IOCTL,
* otherwise we risk locking up the whole system.
*/
NTSTATUS
NTAPI
AtaDispatchDeviceControl(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
if (IS_FDO(DeviceObject->DeviceExtension))
return AtaFdoDeviceControl(DeviceObject->DeviceExtension, Irp);
else
return AtaPdoDeviceControl(DeviceObject->DeviceExtension, Irp);
}

View File

@@ -0,0 +1,798 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Child device object (PDO) dispatch routines
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* GLOBALS ********************************************************************/
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoStartDevice(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PAGED_CODE();
if (!(DevExt->Device.DeviceFlags & DEVICE_PNP_STARTED))
{
PATAPORT_PORT_DATA PortData = DevExt->Device.PortData;
AtaPdoWmiRegistration(DevExt, TRUE);
/* Update the type of device connected to the port */
PortData->SetDeviceData(PortData->ChannelContext,
DevExt->Common.Self,
&DevExt->IdentifyDeviceData);
/* Get the ATA initialization commands to restore the boot up defaults */
if (!DevExt->GtfDataBuffer)
{
DevExt->GtfDataBuffer = PortData->GetInitTaskFile(PortData->ChannelContext,
DevExt->Common.Self);
}
/* Use the standard power policy for mass storage devices */
DevExt->Device.PowerIdleCounter = PoRegisterDeviceForIdleDetection(DevExt->Common.Self,
(ULONG)-1,
(ULONG)-1,
PowerDeviceD3);
/* Set the new ATA volatile settings */
KeClearEvent(&DevExt->Worker.ConfigureEvent);
AtaDeviceQueueEvent(DevExt->Device.PortData, DevExt, ACTION_DEVICE_CONFIG);
KeWaitForSingleObject(&DevExt->Worker.ConfigureEvent,
Executive,
KernelMode,
FALSE,
NULL);
DevExt->Device.DeviceFlags |= DEVICE_PNP_STARTED;
}
AtaReqThawQueue(DevExt, QUEUE_FLAG_FROZEN_PNP);
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoStopDevice(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PAGED_CODE();
AtaReqFreezeQueue(DevExt, QUEUE_FLAG_FROZEN_PNP);
AtaReqWaitForOutstandingIoToComplete(&DevExt->Device, NULL);
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoRemoveDevice(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_Inout_ PIRP Irp,
_In_ BOOLEAN FinalRemove)
{
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
NTSTATUS Status;
BOOLEAN WasRemoved = FALSE;
PAGED_CODE();
AtaReqFreezeQueue(DevExt, QUEUE_FLAG_FROZEN_REMOVED);
AtaReqFlushDeviceQueue(&DevExt->Device);
AtaPdoWmiRegistration(DevExt, FALSE);
if (FinalRemove && DevExt->ReportedMissing)
{
IoReleaseRemoveLockAndWait(&DevExt->Common.RemoveLock, Irp);
AtaFdoDeviceListInsert(ChanExt, DevExt, FALSE);
AtaPdoFreeDevice(DevExt);
WasRemoved = TRUE;
}
Status = STATUS_SUCCESS;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (!WasRemoved)
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
return Status;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryStopRemoveDevice(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PAGED_CODE();
if (DevExt->Common.PageFiles ||
DevExt->Common.HibernateFiles ||
DevExt->Common.DumpFiles)
{
return STATUS_DEVICE_BUSY;
}
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
AtaPdoCompletionRoutine(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_reads_opt_(_Inexpressible_("varies")) PVOID Context)
{
UNREFERENCED_PARAMETER(DeviceObject);
if (Irp->PendingReturned)
KeSetEvent(Context, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryCapabilities(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
DEVICE_CAPABILITIES ParentCapabilities;
PDEVICE_CAPABILITIES DeviceCapabilities;
NTSTATUS Status;
ATA_SCSI_ADDRESS AtaScsiAddress;
PAGED_CODE();
/* Get the capabilities of the parent device */
RtlZeroMemory(&ParentCapabilities, sizeof(ParentCapabilities));
ParentCapabilities.Size = sizeof(ParentCapabilities);
ParentCapabilities.Version = 1;
ParentCapabilities.Address = MAXULONG;
ParentCapabilities.UINumber = MAXULONG;
Status = AtaPnpRepeatRequest(&DevExt->Common, Irp, &ParentCapabilities);
if (!NT_SUCCESS(Status))
return Status;
DeviceCapabilities = IoStack->Parameters.DeviceCapabilities.Capabilities;
RtlCopyMemory(DeviceCapabilities, &ParentCapabilities, sizeof(*DeviceCapabilities));
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
/* Override some fields */
DeviceCapabilities->UINumber = AtaScsiAddress.TargetId;
DeviceCapabilities->UniqueID = FALSE;
DeviceCapabilities->SurpriseRemovalOK = FALSE;
DeviceCapabilities->Removable = !!(DevExt->Device.DeviceFlags & DEVICE_IS_PDO_REMOVABLE);
DeviceCapabilities->D1Latency =
DeviceCapabilities->D2Latency =
DeviceCapabilities->D3Latency = 31 * 10000; // 31 seconds (legacy ATA timeout)
/*
* See ACPI specification, _ADR (Address).
* The goal of the LUN field here is to hide devices with LUN>0 from the ACPI driver,
* since these devices are not mentioned in the ACPI spec at all.
*/
DeviceCapabilities->Address = (AtaScsiAddress.Lun << 4) | AtaScsiAddress.TargetId;
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryTargetDeviceRelations(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp)
{
PDEVICE_RELATIONS DeviceRelations;
PAGED_CODE();
DeviceRelations = ExAllocatePoolUninitialized(PagedPool,
sizeof(*DeviceRelations),
ATAPORT_TAG);
if (!DeviceRelations)
return STATUS_INSUFFICIENT_RESOURCES;
DeviceRelations->Count = 1;
DeviceRelations->Objects[0] = DevExt->Common.Self;
ObReferenceObject(DevExt->Common.Self);
Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryId(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
CHAR LocalBuffer[ATAPORT_FN_FIELD + ATAPORT_RN_FIELD];
NTSTATUS Status;
PWCHAR Buffer, End;
PCSTR DeviceType, GenericType;
size_t CharCount, Remaining;
PAGED_CODE();
switch (IoStack->Parameters.QueryId.IdType)
{
case BusQueryDeviceID:
{
DeviceType = AtaTypeCodeToName(DevExt, GetDeviceType);
/* 'IDE\CdRomVBOX_CD-ROM_____________________________1.0_____' */
CharCount = (sizeof("IDE\\") - 1) + strlen(DeviceType) +
ATAPORT_FN_FIELD + ATAPORT_RN_FIELD +
sizeof(ANSI_NULL);
Buffer = ExAllocatePoolUninitialized(PagedPool,
CharCount * sizeof(WCHAR),
ATAPORT_TAG);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
AtaCopyIdStringSafe(LocalBuffer,
(PUCHAR)DevExt->FriendlyName,
ATAPORT_FN_FIELD,
'_');
AtaCopyIdStringSafe(&LocalBuffer[ATAPORT_FN_FIELD],
(PUCHAR)DevExt->RevisionNumber,
ATAPORT_RN_FIELD,
'_');
Status = RtlStringCchPrintfExW(Buffer,
CharCount,
NULL,
NULL,
0,
L"IDE\\%hs%.48hs",
DeviceType,
LocalBuffer);
ASSERT(NT_SUCCESS(Status));
INFO("DeviceID: '%S'\n", Buffer);
break;
}
case BusQueryHardwareIDs:
{
PWCHAR IdStart;
DBG_UNREFERENCED_LOCAL_VARIABLE(IdStart);
DeviceType = AtaTypeCodeToName(DevExt, GetDeviceType);
GenericType = AtaTypeCodeToName(DevExt, GetGenericType);
/*
* |------------------ 40 -----------------|-- 8 --|
* v v v
* 1) 'IDE\CdRomVBOX_CD-ROM_____________________________1.0_____'
* 2) 'IDE\VBOX_CD-ROM_____________________________1.0_____'
* 3) 'IDE\CdRomVBOX_CD-ROM_____________________________'
* 4) 'VBOX_CD-ROM_____________________________1.0_____'
* 5) 'GenCdRom'
*/
CharCount = strlen(DeviceType) * 2 +
strlen(GenericType) +
(sizeof("IDE\\") - 1) * 3 +
ATAPORT_FN_FIELD * 4 +
ATAPORT_RN_FIELD * 3 +
5 * sizeof(ANSI_NULL) +
sizeof(ANSI_NULL); /* multi-string */
Buffer = ExAllocatePoolUninitialized(PagedPool,
CharCount * sizeof(WCHAR),
ATAPORT_TAG);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
AtaCopyIdStringSafe(LocalBuffer,
(PUCHAR)DevExt->FriendlyName,
ATAPORT_FN_FIELD,
'_');
AtaCopyIdStringSafe(&LocalBuffer[ATAPORT_FN_FIELD],
(PUCHAR)DevExt->RevisionNumber,
ATAPORT_RN_FIELD,
'_');
INFO("HardwareIDs:\n");
/* ID 1 */
Status = RtlStringCchPrintfExW(Buffer,
CharCount,
&End,
&Remaining,
0,
L"IDE\\%hs%.48hs",
DeviceType,
LocalBuffer);
ASSERT(NT_SUCCESS(Status));
INFO(" '%S'\n", Buffer);
++End;
--Remaining;
/* ID 2 */
IdStart = End;
Status = RtlStringCchPrintfExW(End,
Remaining,
&End,
&Remaining,
0,
L"IDE\\%.48hs",
LocalBuffer);
ASSERT(NT_SUCCESS(Status));
INFO(" '%S'\n", IdStart);
++End;
--Remaining;
/* ID 3 */
IdStart = End;
Status = RtlStringCchPrintfExW(End,
Remaining,
&End,
&Remaining,
0,
L"IDE\\%hs%.40hs",
DeviceType,
LocalBuffer);
ASSERT(NT_SUCCESS(Status));
INFO(" '%S'\n", IdStart);
++End;
--Remaining;
/* ID 4 */
IdStart = End;
Status = RtlStringCchPrintfExW(End,
Remaining,
&End,
&Remaining,
0,
L"%.48hs",
LocalBuffer);
ASSERT(NT_SUCCESS(Status));
INFO(" '%S'\n", IdStart);
++End;
--Remaining;
/* ID 5 */
IdStart = End;
Status = RtlStringCchPrintfExW(End,
Remaining,
&End,
&Remaining,
0,
L"%hs",
GenericType);
ASSERT(NT_SUCCESS(Status));
*++End = UNICODE_NULL; /* multi-string */
INFO(" '%S'\n", IdStart);
break;
}
case BusQueryCompatibleIDs:
{
GenericType = AtaTypeCodeToName(DevExt, GetGenericType);
/* 'GenCdRom' */
CharCount = strlen(GenericType) + 2 * sizeof(ANSI_NULL); /* multi-string */
Buffer = ExAllocatePoolUninitialized(PagedPool,
CharCount * sizeof(WCHAR),
ATAPORT_TAG);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
Status = RtlStringCchPrintfExW(Buffer,
CharCount,
&End,
&Remaining,
0,
L"%hs",
GenericType);
ASSERT(NT_SUCCESS(Status));
*++End = UNICODE_NULL; /* multi-string */
INFO("CompatibleIDs: '%S'\n", Buffer);
break;
}
case BusQueryInstanceID:
{
ATA_SCSI_ADDRESS AtaScsiAddress;
/* 'Path.Larget.Lun' */
CharCount = sizeof("FF.FF.FF");
Buffer = ExAllocatePoolUninitialized(PagedPool,
CharCount * sizeof(WCHAR),
ATAPORT_TAG);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
Status = RtlStringCchPrintfExW(Buffer,
CharCount,
&End,
&Remaining,
0,
L"%x.%x.%x",
AtaScsiAddress.PathId,
AtaScsiAddress.TargetId,
AtaScsiAddress.Lun);
ASSERT(NT_SUCCESS(Status));
INFO("InstanceID: '%S'\n", Buffer);
break;
}
default:
return Irp->IoStatus.Status;
}
Irp->IoStatus.Information = (ULONG_PTR)Buffer;
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoQueryDeviceText(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IoStack)
{
NTSTATUS Status;
PWCHAR Buffer;
size_t CharCount;
PAGED_CODE();
switch (IoStack->Parameters.QueryDeviceText.DeviceTextType)
{
case DeviceTextDescription:
{
CharCount = strlen(DevExt->FriendlyName) + sizeof(ANSI_NULL);
Buffer = ExAllocatePoolUninitialized(PagedPool,
CharCount * sizeof(WCHAR),
ATAPORT_TAG);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
Status = RtlStringCchPrintfExW(Buffer,
CharCount,
NULL,
NULL,
0,
L"%hs",
DevExt->FriendlyName);
ASSERT(NT_SUCCESS(Status));
INFO("TextDescription: '%S'\n", Buffer);
break;
}
case DeviceTextLocationInformation:
{
ATA_SCSI_ADDRESS AtaScsiAddress;
CharCount = sizeof("Channel 255, Target 255, Lun 255");
Buffer = ExAllocatePoolUninitialized(PagedPool,
CharCount * sizeof(WCHAR),
ATAPORT_TAG);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
Status = RtlStringCchPrintfExW(Buffer,
CharCount,
NULL,
NULL,
0,
L"Channel %u, Target %u, Lun %u",
AtaScsiAddress.PathId,
AtaScsiAddress.TargetId,
AtaScsiAddress.Lun);
ASSERT(NT_SUCCESS(Status));
INFO("TextLocationInformation: '%S'\n", Buffer);
break;
}
default:
return Irp->IoStatus.Status;
}
Irp->IoStatus.Information = (ULONG_PTR)Buffer;
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoPnp(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_Inout_ PIRP Irp)
{
NTSTATUS Status;
PIO_STACK_LOCATION IoStack;
PAGED_CODE();
INFO("(%p, %p) Tid.%lu %s\n",
DevExt->Common.Self,
Irp,
DevExt->Device.AtaScsiAddress.TargetId,
GetIRPMinorFunctionString(IoGetCurrentIrpStackLocation(Irp)->MinorFunction));
Status = IoAcquireRemoveLock(&DevExt->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:
Status = AtaPdoStartDevice(DevExt, Irp);
break;
case IRP_MN_STOP_DEVICE:
Status = AtaPdoStopDevice(DevExt, Irp);
break;
case IRP_MN_REMOVE_DEVICE:
case IRP_MN_SURPRISE_REMOVAL:
return AtaPdoRemoveDevice(DevExt,
Irp,
(IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE));
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
Status = AtaPdoQueryStopRemoveDevice(DevExt, Irp);
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
case IRP_MN_CANCEL_STOP_DEVICE:
Status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_CAPABILITIES:
Status = AtaPdoQueryCapabilities(DevExt, Irp, IoStack);
break;
case IRP_MN_QUERY_PNP_DEVICE_STATE:
Status = AtaPnpQueryPnpDeviceState(&DevExt->Common, Irp);
break;
case IRP_MN_QUERY_ID:
Status = AtaPdoQueryId(DevExt, Irp, IoStack);
break;
case IRP_MN_QUERY_DEVICE_TEXT:
Status = AtaPdoQueryDeviceText(DevExt, Irp, IoStack);
break;
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
Status = AtaPnpQueryDeviceUsageNotification(&DevExt->Common, Irp);
break;
case IRP_MN_QUERY_DEVICE_RELATIONS:
{
if (IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation)
Status = AtaPdoQueryTargetDeviceRelations(DevExt, Irp);
else
Status = Irp->IoStatus.Status;
break;
}
default:
Status = Irp->IoStatus.Status;
break;
}
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
return Status;
}
CODE_SEG("PAGE")
NTSTATUS
NTAPI
AtaDispatchPnp(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
PAGED_CODE();
if (IS_FDO(DeviceObject->DeviceExtension))
return AtaFdoPnp(DeviceObject->DeviceExtension, Irp);
else
return AtaPdoPnp(DeviceObject->DeviceExtension, Irp);
}
VOID
CODE_SEG("PAGE")
AtaPdoFreeDevice(
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
{
PAGED_CODE();
if (DevExt->Device.PowerIdleCounter)
{
PoRegisterDeviceForIdleDetection(DevExt->Common.Self,
0,
0,
PowerDeviceD3);
}
if (DevExt->GtfDataBuffer)
{
ExFreePoolWithTag(DevExt->GtfDataBuffer, ATAPORT_TAG);
DevExt->GtfDataBuffer = NULL;
}
if (DevExt->Device.Requests)
ExFreePoolWithTag(DevExt->Device.Requests, ATAPORT_TAG);
IoDeleteDevice(DevExt->Common.Self);
}
CODE_SEG("PAGE")
PATAPORT_DEVICE_EXTENSION
AtaPdoCreateDevice(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ ATA_SCSI_ADDRESS AtaScsiAddress)
{
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
NTSTATUS Status;
UNICODE_STRING DeviceName;
PATAPORT_DEVICE_EXTENSION DevExt;
PDEVICE_OBJECT Pdo;
WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\IdeDeviceP99999T9L9-FFF")];
ULONG i;
DECLARE_PAGED_WSTRING(PdoFormat, L"\\Device\\Ide\\IdeDeviceP%luT%luL%lu-%lx");
static ULONG AtapPdoNumber = 0;
PAGED_CODE();
Status = RtlStringCbPrintfW(DeviceNameBuffer,
sizeof(DeviceNameBuffer),
PdoFormat,
ChanExt->DeviceObjectNumber,
AtaScsiAddress.TargetId,
AtaScsiAddress.Lun,
AtapPdoNumber++);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
Status = IoCreateDevice(ChanExt->Common.Self->DriverObject,
sizeof(*DevExt),
&DeviceName,
FILE_DEVICE_MASS_STORAGE,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&Pdo);
if (!NT_SUCCESS(Status))
{
ERR("Failed to create PDO '%wZ' with status 0x%lx\n", &DeviceName, Status);
return NULL;
}
INFO("Created device object %p '%wZ'\n", Pdo, &DeviceName);
/* DMA buffers alignment */
Pdo->AlignmentRequirement = ChanExt->Common.Self->AlignmentRequirement;
Pdo->AlignmentRequirement = max(Pdo->AlignmentRequirement, ATA_MIN_BUFFER_ALIGNMENT);
Pdo->Flags |= DO_DIRECT_IO;
DevExt = Pdo->DeviceExtension;
RtlZeroMemory(DevExt, sizeof(*DevExt));
AtaPnpInitializeCommonExtension(&DevExt->Common, Pdo, ChanExt->Common.Flags & ~DO_IS_FDO);
DevExt->Common.FdoExt = ChanExt;
DevExt->TransferModeAllowedMask = MAXULONG;
DevExt->Device.SectorSize = ATA_MIN_SECTOR_SIZE;
DevExt->Device.PortData = PortData;
DevExt->Device.LocalBuffer = PortData->LocalBuffer;
DevExt->Device.AtaScsiAddress = AtaScsiAddress;
DevExt->Device.TransportFlags = AtaScsiAddress.TargetId;
DevExt->Device.DeviceSelect = IDE_DRIVE_SELECT | AtaScsiAddress.Lun;
if (!(PortData->PortFlags & PORT_FLAG_IS_AHCI))
{
/* Master/Slave select bit */
DevExt->Device.DeviceSelect |= ((AtaScsiAddress.TargetId & 1) << 4);
}
DevExt->Device.DeviceFlags = DEVICE_UNINITIALIZED;
if (PortData->PortFlags & PORT_FLAG_IS_EXTERNAL)
DevExt->Device.DeviceFlags |= DEVICE_IS_PDO_REMOVABLE;
if (PortData->PortFlags & PORT_FLAG_PIO_VIA_DMA)
DevExt->Device.DeviceFlags |= DEVICE_PIO_VIA_DMA;
if (PortData->PortFlags & PORT_FLAG_PIO_FOR_LBA48_XFER)
DevExt->Device.DeviceFlags |= DEVICE_PIO_FOR_LBA48_XFER;
/* Device's capability not explored, yet */
DevExt->Device.DeviceFlags |= DEVICE_PIO_ONLY;
DevExt->Device.FreeRequestsBitmap =
DevExt->Device.MaxRequestsBitmap = NUM_TO_BITMAP(PortData->QueueDepth);
DevExt->Device.Requests = ExAllocatePoolZero(NonPagedPool,
sizeof(ATA_DEVICE_REQUEST) * PortData->QueueDepth,
ATAPORT_TAG);
if (!DevExt->Device.Requests)
goto Failure;
for (i = 0; i < PortData->QueueDepth; ++i)
{
PATA_DEVICE_REQUEST Request = &DevExt->Device.Requests[i];
Request->Device = (PATA_IO_CONTEXT_COMMON)&DevExt->Device;
#if DBG
Request->Signature = ATA_DEVICE_REQUEST_SIGNATURE;
#endif
}
KeInitializeSpinLock(&DevExt->Device.QueueLock);
InitializeListHead(&DevExt->Device.DeviceQueueList);
InitializeListHead(&DevExt->PowerIrpQueueList);
KeInitializeEvent(&DevExt->Device.QueueStoppedEvent, NotificationEvent, FALSE);
KeInitializeEvent(&DevExt->Worker.EnumerationEvent, NotificationEvent, FALSE);
KeInitializeEvent(&DevExt->Worker.ConfigureEvent, NotificationEvent, FALSE);
return DevExt;
Failure:
AtaPdoFreeDevice(DevExt);
return NULL;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,210 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Extra SCSI definitions
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
#pragma once
#include <pshpack1.h>
typedef union _SCSI_SENSE_CODE
{
struct
{
UCHAR SrbStatus;
UCHAR SenseKey;
UCHAR AdditionalSenseCode;
UCHAR AdditionalSenseCodeQualifier;
};
ULONG AsULONG;
} SCSI_SENSE_CODE, *PSCSI_SENSE_CODE;
typedef struct _SCSI_SENSE_KEY_SPECIFIC_FIELD_POINTER
{
UCHAR BitPointer:3;
UCHAR BitPointerValid:1;
UCHAR Reserved:2;
UCHAR CommandData:1;
UCHAR SenseKeySpecificValid:1;
UCHAR FieldPointer[2];
} SCSI_SENSE_KEY_SPECIFIC_FIELD_POINTER, *PSCSI_SENSE_KEY_SPECIFIC_FIELD_POINTER;
// CommandData = 1
// SenseKeySpecificValid = 1
C_ASSERT(sizeof(SCSI_SENSE_KEY_SPECIFIC_FIELD_POINTER) == 3);
typedef struct _MODE_CACHING_PAGE_SPC5
{
UCHAR PageCode:6;
UCHAR Reserved:1;
UCHAR PageSavable:1;
UCHAR PageLength;
UCHAR ReadDisableCache:1;
UCHAR MultiplicationFactor:1;
UCHAR WriteCacheEnable:1;
UCHAR Reserved2:5;
UCHAR WriteRetensionPriority:4;
UCHAR ReadRetensionPriority:4;
UCHAR DisablePrefetchTransfer[2];
UCHAR MinimumPrefetch[2];
UCHAR MaximumPrefetch[2];
UCHAR MaximumPrefetchCeiling[2];
UCHAR NV_DIS:1;
UCHAR SYNC_PROG:2;
UCHAR Reserved1:2;
UCHAR DisableReadAHead:1;
UCHAR LBCSS:1;
UCHAR FSW:1;
UCHAR NumberOfCacheSegments;
UCHAR CacheSegmentSize[2];
UCHAR Reserved3;
UCHAR Obsolete[3];
} MODE_CACHING_PAGE_SPC5, *PMODE_CACHING_PAGE_SPC5;
C_ASSERT(sizeof(MODE_CACHING_PAGE_SPC5) == 20);
typedef struct _MODE_CONTROL_EXTENSION_PAGE
{
UCHAR PageCode:6;
UCHAR SubPageFormat:1;
UCHAR PageSavable:1;
UCHAR SubPageCode;
UCHAR PageLength[2];
UCHAR IALUAE:1;
UCHAR SCSIP:1;
UCHAR TCMOS:1;
UCHAR Reserved:5;
UCHAR InitialCommandPriority:4;
UCHAR Reserved1:4;
UCHAR MaximumSenseDataLength;
UCHAR Reserved2[25];
} MODE_CONTROL_EXTENSION_PAGE, *PMODE_CONTROL_EXTENSION_PAGE;
C_ASSERT(sizeof(MODE_CONTROL_EXTENSION_PAGE) == 32);
#include <poppack.h>
#define SCSI_ADSENSE_ADDRESS_MARK_NOT_FOUND_FOR_DATA_FIELD 0x13
/* SAT-6 */
C_ASSERT(sizeof(MODE_INFO_EXCEPTIONS) == 12);
C_ASSERT(sizeof(MODE_CONTROL_PAGE) == 12);
C_ASSERT(sizeof(MODE_READ_WRITE_RECOVERY_PAGE) == 12);
C_ASSERT(sizeof(POWER_CONDITION_PAGE) == 12);
C_ASSERT(sizeof(VPD_ATA_INFORMATION_PAGE) == 572);
C_ASSERT(sizeof(VPD_BLOCK_LIMITS_PAGE) == 0x3c+4);
C_ASSERT(sizeof(VPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE) == 0x3c+4);
FORCEINLINE
UCHAR
CdbGetAllocationLength6(
_In_ PCDB Cdb)
{
return Cdb->CDB6GENERIC.CommandUniqueBytes[2];
}
FORCEINLINE
USHORT
CdbGetAllocationLength10(
_In_ PCDB Cdb)
{
return (Cdb->CDB10.TransferBlocksMsb << 8) |
(Cdb->CDB10.TransferBlocksLsb << 0);
}
FORCEINLINE
ULONG
CdbGetAllocationLength16(
_In_ PCDB Cdb)
{
return (Cdb->CDB16.TransferLength[0] << 24) |
(Cdb->CDB16.TransferLength[1] << 16) |
(Cdb->CDB16.TransferLength[2] << 8) |
(Cdb->CDB16.TransferLength[3] << 0);
}
FORCEINLINE
USHORT
CdbGetTransferLength10(
_In_ PCDB Cdb)
{
/* Bytes 7:8 */
return (Cdb->CDB10.TransferBlocksMsb << 8) |
(Cdb->CDB10.TransferBlocksLsb << 0);
}
FORCEINLINE
ULONG
CdbGetTransferLength12(
_In_ PCDB Cdb)
{
/* Bytes 6:9 */
return (Cdb->CDB12.TransferLength[0] << 24) |
(Cdb->CDB12.TransferLength[1] << 16) |
(Cdb->CDB12.TransferLength[2] << 8) |
(Cdb->CDB12.TransferLength[3] << 0);
}
FORCEINLINE
ULONG
CdbGetTransferLength16(
_In_ PCDB Cdb)
{
/* Bytes 10:13 */
return (Cdb->CDB16.TransferLength[0] << 24) |
(Cdb->CDB16.TransferLength[1] << 16) |
(Cdb->CDB16.TransferLength[2] << 8) |
(Cdb->CDB16.TransferLength[3] << 0);
}
FORCEINLINE
ULONG
CdbGetLogicalBlockAddress6(
_In_ PCDB Cdb)
{
/* Bytes 2:3 */
return (Cdb->CDB6READWRITE.LogicalBlockMsb0 << 8) |
(Cdb->CDB6READWRITE.LogicalBlockLsb << 0);
}
FORCEINLINE
ULONG
CdbGetLogicalBlockAddress10(
_In_ PCDB Cdb)
{
/* Bytes 2:5 */
return (Cdb->CDB10.LogicalBlockByte0 << 24) |
(Cdb->CDB10.LogicalBlockByte1 << 16) |
(Cdb->CDB10.LogicalBlockByte2 << 8) |
(Cdb->CDB10.LogicalBlockByte3 << 0);
}
FORCEINLINE
ULONG
CdbGetLogicalBlockAddress12(
_In_ PCDB Cdb)
{
/* Bytes 2:5 */
return (Cdb->CDB12.LogicalBlock[0] << 24) |
(Cdb->CDB12.LogicalBlock[1] << 16) |
(Cdb->CDB12.LogicalBlock[2] << 8) |
(Cdb->CDB12.LogicalBlock[3] << 0);
}
FORCEINLINE
ULONG64
CdbGetLogicalBlockAddress16(
_In_ PCDB Cdb)
{
/* Bytes 2:9 */
return ((ULONG64)Cdb->CDB16.LogicalBlock[0] << 56) |
((ULONG64)Cdb->CDB16.LogicalBlock[1] << 48) |
((ULONG64)Cdb->CDB16.LogicalBlock[2] << 40) |
((ULONG64)Cdb->CDB16.LogicalBlock[3] << 32) |
((ULONG64)Cdb->CDB16.LogicalBlock[4] << 24) |
((ULONG64)Cdb->CDB16.LogicalBlock[5] << 16) |
((ULONG64)Cdb->CDB16.LogicalBlock[6] << 8) |
((ULONG64)Cdb->CDB16.LogicalBlock[7] << 0);
}

View File

@@ -0,0 +1,298 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: SMART Feature Set support
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* FUNCTIONS ******************************************************************/
static
VOID
AtaIdeRegsToTaskFile(
_In_ ATAPORT_DEVICE_EXTENSION* __restrict DevExt,
_In_ IDEREGS* __restrict IdeRegs,
_Out_ ATA_DEVICE_REQUEST* __restrict Request)
{
PATA_TASKFILE TaskFile = &Request->TaskFile;
TaskFile->Feature = IdeRegs->bFeaturesReg;
TaskFile->SectorCount = IdeRegs->bSectorCountReg;
TaskFile->LowLba = IdeRegs->bSectorNumberReg;
TaskFile->MidLba = IdeRegs->bCylLowReg;
TaskFile->HighLba = IdeRegs->bCylHighReg;
TaskFile->Command = IDE_COMMAND_SMART; // SMART_CMD
/* Set the master/slave bit to the correct value */
TaskFile->DriveSelect = IdeRegs->bDriveHeadReg & ~IDE_DRIVE_SELECT_SLAVE;
TaskFile->DriveSelect |= DevExt->Device.DeviceSelect & IDE_DRIVE_SELECT_SLAVE;
}
static
VOID
AtaTaskFileToIdeRegs(
_In_ ATA_DEVICE_REQUEST* __restrict Request,
_Out_ IDEREGS* __restrict IdeRegs)
{
PATA_TASKFILE TaskFile = &Request->Output;
IdeRegs->bFeaturesReg = TaskFile->Feature;
IdeRegs->bSectorCountReg = TaskFile->SectorCount;
IdeRegs->bSectorNumberReg = TaskFile->LowLba;
IdeRegs->bCylLowReg = TaskFile->MidLba;
IdeRegs->bCylHighReg = TaskFile->HighLba;
IdeRegs->bCommandReg = TaskFile->Command;
IdeRegs->bDriveHeadReg = TaskFile->DriveSelect;
}
static
ATA_COMPLETION_ACTION
AtaReqCompleteSmartIoControl(
_In_ PATA_DEVICE_REQUEST Request)
{
PSENDCMDOUTPARAMS CmdOut;
CmdOut = (PSENDCMDOUTPARAMS)(((PUCHAR)Request->Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
if (Request->SrbStatus == SRB_STATUS_SUCCESS)
{
CmdOut->DriverStatus.bDriverError = SMART_NO_ERROR;
CmdOut->DriverStatus.bIDEError = 0;
}
else
{
CmdOut->DriverStatus.bDriverError = SMART_IDE_ERROR;
CmdOut->DriverStatus.bIDEError = Request->Output.Error;
}
/* Return the SMART status */
if (Request->Flags & REQUEST_FLAG_SAVE_TASK_FILE)
{
if (Request->Flags & REQUEST_FLAG_HAS_TASK_FILE)
{
PIDEREGS IdeRegs = (PIDEREGS)&CmdOut->bBuffer;
AtaTaskFileToIdeRegs(Request, IdeRegs);
}
else
{
CmdOut->DriverStatus.bDriverError = SMART_IDE_ERROR;
CmdOut->DriverStatus.bIDEError = Request->Output.Error;
}
}
return COMPLETE_IRP;
}
UCHAR
AtaReqSmartIoControl(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PATA_DEVICE_REQUEST Request,
_In_ PSCSI_REQUEST_BLOCK Srb)
{
union _PARAMS
{
PSENDCMDINPARAMS CmdIn;
PSENDCMDOUTPARAMS CmdOut;
} Buffer;
if (Srb->DataTransferLength < (sizeof(SRB_IO_CONTROL) + sizeof(*Buffer.CmdIn) - 1))
return SRB_STATUS_INVALID_REQUEST;
Buffer.CmdIn = (PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
if (Buffer.CmdIn->irDriveRegs.bCommandReg != SMART_CMD)
return SRB_STATUS_INVALID_REQUEST;
switch (Buffer.CmdIn->irDriveRegs.bFeaturesReg)
{
case READ_ATTRIBUTES:
{
Request->DataTransferLength = READ_ATTRIBUTE_BUFFER_SIZE;
Request->Flags = REQUEST_FLAG_DATA_IN;
break;
}
case READ_THRESHOLDS:
{
Request->DataTransferLength = READ_THRESHOLD_BUFFER_SIZE;
Request->Flags = REQUEST_FLAG_DATA_IN;
break;
}
case SMART_READ_LOG:
{
Request->DataTransferLength =
Buffer.CmdIn->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE;
Request->Flags = REQUEST_FLAG_DATA_IN;
break;
}
case SMART_WRITE_LOG:
{
Request->DataTransferLength =
Buffer.CmdIn->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE;
Request->Flags = REQUEST_FLAG_DATA_OUT;
break;
}
case RETURN_SMART_STATUS:
{
Request->DataTransferLength = sizeof(IDEREGS);
Request->Flags = REQUEST_FLAG_SAVE_TASK_FILE;
break;
}
case EXECUTE_OFFLINE_DIAGS:
{
UCHAR Subcommand = Buffer.CmdIn->irDriveRegs.bSectorNumberReg;
if (Subcommand == SMART_SHORT_SELFTEST_CAPTIVE ||
Subcommand == SMART_EXTENDED_SELFTEST_CAPTIVE)
{
return SRB_STATUS_INVALID_REQUEST;
}
__fallthrough;
}
case ENABLE_DISABLE_AUTOSAVE:
case SAVE_ATTRIBUTE_VALUES:
case ENABLE_SMART:
case DISABLE_SMART:
case ENABLE_DISABLE_AUTO_OFFLINE:
{
Request->DataTransferLength = 0;
Request->Flags = 0;
break;
}
default:
return SRB_STATUS_INVALID_REQUEST;
}
if (Request->Flags & (REQUEST_FLAG_DATA_IN | REQUEST_FLAG_SAVE_TASK_FILE))
{
if (Srb->DataTransferLength <
(sizeof(SRB_IO_CONTROL) + sizeof(*Buffer.CmdOut) - 1 + Request->DataTransferLength))
{
return SRB_STATUS_INVALID_REQUEST;
}
Request->DataBuffer = Buffer.CmdOut->bBuffer;
}
else if (Request->Flags & REQUEST_FLAG_DATA_OUT)
{
if (Srb->DataTransferLength <
(sizeof(SRB_IO_CONTROL) + sizeof(*Buffer.CmdIn) - 1 + Request->DataTransferLength))
{
return SRB_STATUS_INVALID_REQUEST;
}
Request->DataBuffer = Buffer.CmdIn->bBuffer;
}
Request->Complete = AtaReqCompleteSmartIoControl;
AtaIdeRegsToTaskFile(DevExt, &Buffer.CmdIn->irDriveRegs, Request);
return SRB_STATUS_PENDING;
}
CODE_SEG("PAGE")
NTSTATUS
AtaPdoHandleMiniportIdentify(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PSCSI_REQUEST_BLOCK Srb)
{
union _PARAMS
{
PSENDCMDINPARAMS CmdIn;
PSENDCMDOUTPARAMS CmdOut;
} Buffer;
PAGED_CODE();
if (Srb->DataTransferLength <
(sizeof(SRB_IO_CONTROL) + sizeof(*Buffer.CmdOut) - 1 + IDENTIFY_BUFFER_SIZE))
{
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
return STATUS_BUFFER_TOO_SMALL;
}
Buffer.CmdIn = (PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
if (IS_ATAPI(&DevExt->Device) || (Buffer.CmdIn->irDriveRegs.bCommandReg != ID_CMD))
{
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
return STATUS_INVALID_DEVICE_REQUEST;
}
Buffer.CmdOut->cBufferSize = IDENTIFY_BUFFER_SIZE;
Buffer.CmdOut->DriverStatus.bDriverError = 0;
Buffer.CmdOut->DriverStatus.bIDEError = 0;
RtlCopyMemory(Buffer.CmdOut->bBuffer, &DevExt->IdentifyDeviceData, IDENTIFY_BUFFER_SIZE);
Srb->SrbStatus = SRB_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
CODE_SEG("PAGE")
NTSTATUS
AtaPdoHandleMiniportSmartVersion(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ PSCSI_REQUEST_BLOCK Srb)
{
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
PGETVERSIONINPARAMS VersionParameters;
ULONG i;
PAGED_CODE();
if (Srb->DataTransferLength < (sizeof(*VersionParameters) + sizeof(SRB_IO_CONTROL)))
{
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
return STATUS_BUFFER_TOO_SMALL;
}
VersionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
/* SMART 1.03 */
VersionParameters->bVersion = 1;
VersionParameters->bRevision = 1;
VersionParameters->bReserved = 0;
VersionParameters->fCapabilities = (CAP_ATA_ID_CMD | CAP_ATAPI_ID_CMD | CAP_SMART_CMD);
VersionParameters->bIDEDeviceMap = 0;
/* Emulate the PATA behavior */
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PATAPORT_DEVICE_EXTENSION DevExt;
ULONG DeviceFlag;
DevExt = AtaFdoFindDeviceByPath(ChanExt,
AtaMarshallScsiAddress(PortData->PortNumber, i, 0),
Srb);
if (!DevExt)
continue;
DeviceFlag = 1 << i;
if (PortData->PortNumber != 0)
DeviceFlag <<= 2;
if (IS_ATAPI(&DevExt->Device))
DeviceFlag <<= 4;
VersionParameters->bIDEDeviceMap |= DeviceFlag;
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Srb);
}
Srb->SrbStatus = SRB_STATUS_SUCCESS;
return STATUS_SUCCESS;
}

View File

@@ -0,0 +1,213 @@
/*
* PROJECT: ReactOS ATA Port Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: WMI support
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* GLOBALS ********************************************************************/
static WMIGUIDREGINFO AtapWmiGuidList[] =
{
{&MSIde_PortDeviceInfo_GUID, 1, 0}
};
/* FUNCTIONS ******************************************************************/
static
CODE_SEG("PAGE")
NTSTATUS
NTAPI
AtaQueryWmiRegInfo(
_Inout_ PDEVICE_OBJECT DeviceObject,
_Inout_ PULONG RegFlags,
_Inout_ PUNICODE_STRING InstanceName,
_Outptr_result_maybenull_ PUNICODE_STRING *RegistryPath,
_Inout_ PUNICODE_STRING MofResourceName,
_Outptr_result_maybenull_ PDEVICE_OBJECT *Pdo)
{
PATAPORT_DEVICE_EXTENSION DevExt = DeviceObject->DeviceExtension;
UNREFERENCED_PARAMETER(InstanceName);
UNREFERENCED_PARAMETER(MofResourceName);
PAGED_CODE();
*RegFlags = WMIREG_FLAG_INSTANCE_PDO;
*RegistryPath = &AtapDriverRegistryPath;
*Pdo = DevExt->Common.Self;
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
NTAPI
AtaQueryWmiDataBlock(
_Inout_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp,
_In_ ULONG GuidIndex,
_In_ ULONG InstanceIndex,
_In_ ULONG InstanceCount,
_Out_writes_opt_(InstanceCount) PULONG InstanceLengthArray,
_In_ ULONG BufferAvail,
_Out_writes_bytes_opt_(BufferAvail) PUCHAR Buffer)
{
PATAPORT_DEVICE_EXTENSION DevExt;
NTSTATUS Status;
ATA_SCSI_ADDRESS AtaScsiAddress;
PMSIde_PortDeviceInfo DeviceInfo;
PAGED_CODE();
if (GuidIndex > RTL_NUMBER_OF(AtapWmiGuidList))
{
Status = STATUS_WMI_GUID_NOT_FOUND;
goto Complete;
}
/* Only ever register 1 instance per GUID */
if (InstanceIndex != 0 || InstanceCount != 1)
{
Status = STATUS_WMI_INSTANCE_NOT_FOUND;
goto Complete;
}
if (!InstanceLengthArray || BufferAvail < sizeof(*DeviceInfo))
{
Status = STATUS_BUFFER_TOO_SMALL;
goto Complete;
}
DevExt = DeviceObject->DeviceExtension;
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
DeviceInfo = (PMSIde_PortDeviceInfo)Buffer;
DeviceInfo->Bus = AtaScsiAddress.PathId;
DeviceInfo->Target = AtaScsiAddress.TargetId;
DeviceInfo->Lun = AtaScsiAddress.Lun;
*InstanceLengthArray = sizeof(*DeviceInfo);
Status = STATUS_SUCCESS;
Complete:
return WmiCompleteRequest(DeviceObject,
Irp,
Status,
sizeof(*DeviceInfo),
IO_NO_INCREMENT);
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaPdoWmi(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_Inout_ PIRP Irp)
{
NTSTATUS Status;
SYSCTL_IRP_DISPOSITION Disposition;
PAGED_CODE();
TRACE("%s(%p, %p)\n", __FUNCTION__, DevExt, Irp);
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, Irp);
if (!NT_SUCCESS(Status))
{
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
Status = WmiSystemControl(&DevExt->WmiLibInfo,
DevExt->Common.Self,
Irp,
&Disposition);
switch (Disposition)
{
case IrpProcessed:
break;
case IrpNotCompleted:
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
default:
ASSERT(FALSE);
__fallthrough;
case IrpForward:
case IrpNotWmi:
Status = STATUS_NOT_SUPPORTED;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
}
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
return Status;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaFdoWmi(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_Inout_ PIRP Irp)
{
PAGED_CODE();
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
}
CODE_SEG("PAGE")
NTSTATUS
NTAPI
AtaDispatchWmi(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
PAGED_CODE();
if (IS_FDO(DeviceObject->DeviceExtension))
return AtaFdoWmi(DeviceObject->DeviceExtension, Irp);
else
return AtaPdoWmi(DeviceObject->DeviceExtension, Irp);
}
CODE_SEG("PAGE")
NTSTATUS
AtaPdoWmiRegistration(
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
_In_ BOOLEAN Register)
{
ULONG Action;
PAGED_CODE();
if (Register)
{
DevExt->WmiLibInfo.GuidCount = RTL_NUMBER_OF(AtapWmiGuidList);
DevExt->WmiLibInfo.GuidList = AtapWmiGuidList;
DevExt->WmiLibInfo.QueryWmiRegInfo = AtaQueryWmiRegInfo;
DevExt->WmiLibInfo.QueryWmiDataBlock = AtaQueryWmiDataBlock;
Action = WMIREG_ACTION_REGISTER;
}
else
{
Action = WMIREG_ACTION_DEREGISTER;
}
return IoWMIRegistrationControl(DevExt->Common.Self, Action);
}

View File

@@ -0,0 +1,595 @@
/*
* PROJECT: ReactOS Storage Stack
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* or MIT (https://spdx.org/licenses/MIT)
* PURPOSE: ATA IDENTIFY DEVICE and IDENTIFY PACKET DEVICE data helper functions
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
#pragma once
FORCEINLINE
BOOLEAN
AtaDevHasCdbInterrupt(
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
{
/* Bits 5:6 of word 0 */
return (IdentifyPacketData->GeneralConfiguration.DrqDelay == 1);
}
FORCEINLINE
UCHAR
AtaDevCdbSizeInWords(
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
{
/* Bits 0:2 of word 0 */
return (IdentifyPacketData->GeneralConfiguration.PacketType != 0) ? 8 : 6;
}
FORCEINLINE
BOOLEAN
AtaDevMaxLun(
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
{
/* Bits 0:2 of word 126 */
USHORT LastLunIdentifier = IdentifyPacketData->ReservedWord126 & 7;
/*
* We perform additional validation because
* most ATAPI devices ignore the LUN field in the CDB and respond to each LUN.
*/
/* Make sure this field has no value that represents all bits set */
if (LastLunIdentifier != 7)
return LastLunIdentifier + 1;
return 1;
}
FORCEINLINE
BOOLEAN
AtaDevIsDmaDirectionRequired(
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
{
/* Bit 15 of word 62 */
if (IdentifyPacketData->DMADIR.DMADIRBitRequired)
{
return !(IdentifyPacketData->MultiWordDMASupport & 0x7) && // Bits 0:2 of word 63
!IdentifyPacketData->Capabilities.DmaSupported && // Bit 8 of word 49
!IdentifyPacketData->Capabilities.InterleavedDmaSupported && // Bit 15 of word 49
!(IdentifyPacketData->UltraDMASupport & 0x7F); // Bits 0:6 of word 88
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevIsTape(
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
{
/* Bits 8:12 of word 0 (sequential-access device) */
return (IdentifyPacketData->GeneralConfiguration.CommandPacketType == 1);
}
FORCEINLINE
BOOLEAN
AtaDevIsIdentifyDataValid(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
ULONG i;
UCHAR Crc;
/* Bits 0:8 of word 255 */
if (IdentifyData->Signature != 0xA5)
{
/* The integrity word is missing, assume the data provided by the device is valid */
return TRUE;
}
/* Verify the checksum */
Crc = 0;
for (i = 0; i < sizeof(*IdentifyData); ++i)
{
Crc += ((PUCHAR)IdentifyData)[i];
}
return (Crc == 0);
}
FORCEINLINE
BOOLEAN
AtaDevHasLbaTranslation(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bit 9 of word 49 */
return IdentifyData->Capabilities.LbaSupported;
}
FORCEINLINE
ULONG
AtaDevUserAddressableSectors28Bit(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Words 60-61 */
return IdentifyData->UserAddressableSectors;
}
FORCEINLINE
ULONG64
AtaDevUserAddressableSectors48Bit(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Words 100-103 */
return ((ULONG64)IdentifyData->Max48BitLBA[1] << 32) | IdentifyData->Max48BitLBA[0];
}
FORCEINLINE
BOOLEAN
AtaDevHas48BitAddressFeature(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 83: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetSupport.WordValid83 == 1)
{
/* Bit 10 of word 83 */
return IdentifyData->CommandSetSupport.BigLba;
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevIsCurrentGeometryValid(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
return ((IdentifyData->TranslationFieldsValid & 1) &&
(IdentifyData->NumberOfCurrentCylinders != 0) &&
(IdentifyData->NumberOfCurrentCylinders <= 63) &&
(IdentifyData->NumberOfCurrentHeads != 0) &&
(IdentifyData->NumberOfCurrentHeads <= 16) &&
(IdentifyData->CurrentSectorsPerTrack != 0));
}
FORCEINLINE
VOID
AtaDevDefaultChsTranslation(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
_Out_ PUSHORT Cylinders,
_Out_ PUSHORT Heads,
_Out_ PUSHORT SectorsPerTrack)
{
/* Word 1 */
*Cylinders = IdentifyData->NumCylinders;
/* Word 3 */
*Heads = IdentifyData->NumHeads;
/* Word 6 */
*SectorsPerTrack = IdentifyData->NumSectorsPerTrack;
}
FORCEINLINE
VOID
AtaDevCurrentChsTranslation(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
_Out_ PUSHORT Cylinders,
_Out_ PUSHORT Heads,
_Out_ PUSHORT SectorsPerTrack)
{
/* Word 54 */
*Cylinders = IdentifyData->NumberOfCurrentCylinders;
/* Word 55 */
*Heads = IdentifyData->NumberOfCurrentHeads;
/* Word 55 */
*SectorsPerTrack = IdentifyData->CurrentSectorsPerTrack;
}
FORCEINLINE
UCHAR
AtaDevCurrentSectorsPerDrq(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
UCHAR MultiSectorCurrent;
/* Bit 8 of word 59 */
if (!(IdentifyData->MultiSectorSettingValid))
return 0;
/* The word 59 should be a power of 2 */
MultiSectorCurrent = IdentifyData->CurrentMultiSectorSetting;
if ((MultiSectorCurrent > 0) && ((MultiSectorCurrent & (MultiSectorCurrent - 1)) == 0))
return MultiSectorCurrent;
return 0;
}
FORCEINLINE
UCHAR
AtaDevMaximumSectorsPerDrq(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
UCHAR MultiSectorMax;
/* The word 47 should be a power of 2 */
MultiSectorMax = IdentifyData->MaximumBlockTransfer;
if ((MultiSectorMax > 0) && ((MultiSectorMax & (MultiSectorMax - 1)) == 0))
return MultiSectorMax;
return 0;
}
FORCEINLINE
ULONG
AtaDevBytesPerLogicalSector(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
ULONG WordCount;
/* Word 106: 15 = 0, 14 = 1, 12 = 1 */
if (IdentifyData->PhysicalLogicalSectorSize.Reserved1 == 1 &&
IdentifyData->PhysicalLogicalSectorSize.LogicalSectorLongerThan256Words)
{
/* Words 116-117 */
WordCount = IdentifyData->WordsPerLogicalSector[0];
WordCount |= (ULONG)IdentifyData->WordsPerLogicalSector[1] << 16;
}
else
{
/* 256 words = 512 bytes */
WordCount = 256;
}
return WordCount * sizeof(USHORT);
}
FORCEINLINE
ULONG
AtaDevLogicalSectorsPerPhysicalSector(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
_Out_ PULONG Exponent)
{
/* Word 106: 15 = 0, 14 = 1, 13 = 1 */
if (IdentifyData->PhysicalLogicalSectorSize.Reserved1 == 1 &&
IdentifyData->PhysicalLogicalSectorSize.MultipleLogicalSectorsPerPhysicalSector)
{
/* Bits 0:3 of word 106 */
*Exponent = IdentifyData->PhysicalLogicalSectorSize.LogicalSectorsPerPhysicalSector;
return 1 << *Exponent;
}
*Exponent = 0;
return 1 << 0;
}
FORCEINLINE
ULONG
AtaDevLogicalSectorAlignment(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 209: 15 = 0, 14 = 1 */
if (IdentifyData->BlockAlignment.Word209Supported &&
IdentifyData->BlockAlignment.Reserved0 == 0)
{
/* Bits 0:13 of word 209 */
return IdentifyData->BlockAlignment.AlignmentOfLogicalWithinPhysical;
}
return 0;
}
FORCEINLINE
BOOLEAN
AtaDevInPuisState(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 2 */
return (IdentifyData->SpecificConfiguration == 0x37C8) ||
(IdentifyData->SpecificConfiguration == 0x738C);
}
FORCEINLINE
BOOLEAN
AtaDevIsIdentifyDataIncomplete(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bit 2 of word 0 */
return IdentifyData->GeneralConfiguration.ResponseIncomplete;
}
FORCEINLINE
BOOLEAN
AtaDevIsRemovable(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bit 7 of word 0 */
return IdentifyData->GeneralConfiguration.RemovableMedia;
}
FORCEINLINE
BOOLEAN
AtaDevHasRemovableMediaFeature(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
if (AtaDevIsRemovable(IdentifyData))
{
/* Word 83: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetSupport.WordValid == 1)
{
/* Bit 2 of word 82 */
return IdentifyData->CommandSetSupport.RemovableMediaFeature;
}
}
return FALSE;
}
FORCEINLINE
ULONG
AtaDevQueueDepth(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bit 8 of word 76 */
if (IdentifyData->SerialAtaCapabilities.NCQ)
{
/* Bits 0:4 of word 75 */
return IdentifyData->QueueDepth + 1;
}
return 0;
}
FORCEINLINE
BOOLEAN
AtaDevHasNcqAutosense(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
USHORT Word76 = ((PUSHORT)IdentifyData)[76]; // IdentifyData->SerialAtaCapabilities
if (Word76 != 0x0000 && Word76 != 0xFFFF)
{
/* Bit 7 of word 78 */
return IdentifyData->SerialAtaFeaturesSupported.NCQAutosense;
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevHasSenseDataReporting(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
if (IdentifyData->CommandSetActive.Words119_120Valid && // Word 86: bit 15 = 1
IdentifyData->CommandSetSupportExt.WordValid == 1) // Word 119: 15 = 0, 14 = 1
{
/* Bit 6 of word 119 */
return IdentifyData->CommandSetSupportExt.SenseDataReporting;
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevHasRemovableMediaStatusNotification(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 127: bit 0 = 1, bit 1 = 0 */
return (IdentifyData->MsnSupport == 1);
}
FORCEINLINE
BOOLEAN
AtaDevHasIeee1667(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
// TODO: Verify word 48
/* Bit 0 of word 48 */
if (IdentifyData->TrustedComputing.FeatureSupported)
{
/* Bit 7 of word 69 */
return IdentifyData->AdditionalSupported.IEEE1667;
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevHasWorldWideName(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 87: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetActive.Reserved4 == 1)
{
/* Bit 8 of word 87 */
return IdentifyData->CommandSetActive.WWN64Bit;
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevIsZonedDevice(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bits 0:1 of word 69 */
return (IdentifyData->AdditionalSupported.ZonedCapabilities != 0);
}
FORCEINLINE
UCHAR
AtaDevZonedCapabilities(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bits 0:1 of word 69 */
return IdentifyData->AdditionalSupported.ZonedCapabilities;
}
FORCEINLINE
BOOLEAN
AtaDevHasSecurityModeFeature(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 83: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetSupport.WordValid == 1)
{
/* Bit 1 of word 82 */
return IdentifyData->CommandSetSupport.SecurityMode;
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevHasSmartFeature(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bit 0 of word 82 */
return IdentifyData->CommandSetSupport.SmartCommands;
}
FORCEINLINE
BOOLEAN
AtaDevIsVolatileWriteCacheEnabled(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 83: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetSupport.WordValid == 1)
{
/* Bit 5 of word 82 and bit 5 of word 85 */
return (IdentifyData->CommandSetSupport.WriteCache &&
IdentifyData->CommandSetActive.WriteCache);
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevIsReadLookAHeadEnabled(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 83: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetSupport.WordValid == 1)
{
/* Bit 6 of word 82 and bit 6 of word 85 */
return (IdentifyData->CommandSetSupport.LookAhead &&
IdentifyData->CommandSetActive.LookAhead);
}
return FALSE;
}
FORCEINLINE
UCHAR
AtaDevNominalFormFactor(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bits 0:3 of word 168 */
return IdentifyData->NominalFormFactor;
}
FORCEINLINE
USHORT
AtaDevMediumRotationRate(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 217 */
return IdentifyData->NominalMediaRotationRate;
}
FORCEINLINE
BOOLEAN
AtaDevIsRotatingDevice(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 217 */
return (IdentifyData->NominalMediaRotationRate >= 0x0401 &&
IdentifyData->NominalMediaRotationRate <= 0xFFFE);
}
FORCEINLINE
BOOLEAN
AtaDevIsSsd(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 217 */
return (IdentifyData->NominalMediaRotationRate == 1);
}
FORCEINLINE
BOOLEAN
AtaDevHasTrimFunction(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bit 0 of word 169 */
return IdentifyData->DataSetManagementFeature.SupportsTrim;
}
FORCEINLINE
BOOLEAN
AtaDevHasRzatFunction(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bit 5 of word 69 */
return IdentifyData->AdditionalSupported.ReadZeroAfterTrimSupported;
}
FORCEINLINE
BOOLEAN
AtaDevHasDratFunction(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Bit 14 of word 69 */
return IdentifyData->AdditionalSupported.DeterministicReadAfterTrimSupported;
}
FORCEINLINE
BOOLEAN
AtaDevHasForceUnitAccessCommands(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 83: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetSupport.WordValid83 == 1)
{
/* Bit 6 of word 84 */
return IdentifyData->CommandSetSupport.WriteFua;
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevHasFlushCache(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 83: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetSupport.WordValid == 1)
{
/* Bit 12 of word 83 */
return IdentifyData->CommandSetSupport.FlushCache;
}
return FALSE;
}
FORCEINLINE
BOOLEAN
AtaDevHasFlushCacheExt(
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
{
/* Word 83: 15 = 0, 14 = 1 */
if (IdentifyData->CommandSetSupport.WordValid == 1)
{
/* Bit 13 of word 83 */
return IdentifyData->CommandSetSupport.FlushCacheExt;
}
return FALSE;
}