[ATAPI] Add ATA storage driver

CORE-17256
CORE-17191
CORE-17716
CORE-17977
CORE-13976
CORE-16216
This commit is contained in:
Dmitry Borisov
2026-04-21 15:01:22 -05:00
committed by Carl J. Bialorucki
parent ae2827f481
commit ac33647888
23 changed files with 13586 additions and 0 deletions
+1
View File
@@ -1,4 +1,5 @@
add_subdirectory(atapi)
add_subdirectory(pciide)
add_subdirectory(pciidex)
add_subdirectory(uniata)
+36
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)
+765
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 <[email protected]>
*/
/* 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
+5
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>
+8
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
+95
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 <[email protected]>
*/
#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
+396
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 <[email protected]>
*/
/* 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;
}
+657
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 <[email protected]>
*/
/* 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;
}
+367
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 <[email protected]>
*/
/* 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;
}
+531
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 <[email protected]>
*/
/* 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);
}
+354
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 <[email protected]>
*
* 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);
}
}
+658
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 <[email protected]>
*/
/* 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;
}
+758
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 <[email protected]>
*/
/* INCLUDES *******************************************************************/
#include "atapi.h"
/* GLOBALS ********************************************************************/
DECLARE_PAGED_WSTRING(AtapDevSymLinkFormat, L"\\Device\\ScsiPort%lu");
DECLARE_PAGED_WSTRING(AtapDosSymLinkFormat, L"\\DosDevices\\Scsi%lu:");
/* FUNCTIONS ******************************************************************/
static
CODE_SEG("PAGE")
NTSTATUS
AtaFdoCreateSymLinks(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
{
UNICODE_STRING ChannelName;
ULONG ScsiAdapter, ScsiPortCount;
WCHAR ChannelNameBuffer[sizeof("\\Device\\Ide\\IdePort99999")];
NTSTATUS Status;
DECLARE_PAGED_WSTRING(FdoFormat, L"\\Device\\Ide\\IdePort%lu");
PAGED_CODE();
if (ChanExt->PortData.PortFlags & PORT_FLAG_SYMLINK_CREATED)
return STATUS_SUCCESS;
Status = RtlStringCbPrintfW(ChannelNameBuffer,
sizeof(ChannelNameBuffer),
FdoFormat,
ChanExt->DeviceObjectNumber);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&ChannelName, ChannelNameBuffer);
ScsiPortCount = IoGetConfigurationInformation()->ScsiPortCount;
/* Search for a free SCSI port adapter in the system */
for (ScsiAdapter = 0; ScsiAdapter <= ScsiPortCount; ++ScsiAdapter)
{
WCHAR SymLinkNameBuffer[sizeof("\\DosDevices\\Scsi99999:")];
UNICODE_STRING SymLinkName;
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
sizeof(SymLinkNameBuffer),
AtapDevSymLinkFormat,
ScsiAdapter);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
/* Create a symbolic link '\Device\ScsiPortX' -> '\Device\Ide\IdePortN' */
Status = IoCreateSymbolicLink(&SymLinkName, &ChannelName);
if (!NT_SUCCESS(Status))
continue;
INFO("Symlink created '%wZ' -> '%wZ'\n", &SymLinkName, &ChannelName);
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
sizeof(SymLinkNameBuffer),
AtapDosSymLinkFormat,
ScsiAdapter);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
/* Create a symbolic link '\DosDevices\ScsiX:' -> '\Device\Ide\IdePortN' */
Status = IoCreateSymbolicLink(&SymLinkName, &ChannelName);
if (NT_SUCCESS(Status))
{
INFO("Symlink created '%wZ' -> '%wZ'\n", &SymLinkName, &ChannelName);
}
/* Register ourselves (ATA channel) as a SCSI port adapter */
IoGetConfigurationInformation()->ScsiPortCount++;
ChanExt->ScsiPortNumber = ScsiAdapter;
ChanExt->PortData.PortFlags |= PORT_FLAG_SYMLINK_CREATED;
break;
}
return Status;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaFdoAllocateLocalBuffer(
_In_ PATAPORT_PORT_DATA PortData)
{
PHYSICAL_ADDRESS PhysicalAddress;
PHYSICAL_ADDRESS HighestAcceptableAddress;
PHYSICAL_ADDRESS LowestAcceptableAddress;
PHYSICAL_ADDRESS BoundaryAddressMultiple;
PAGED_CODE();
if (PortData->LocalBuffer)
return STATUS_SUCCESS;
LowestAcceptableAddress.QuadPart = 0;
HighestAcceptableAddress.QuadPart = 0xFFFFFFFF; // 32-bit DMA
BoundaryAddressMultiple.QuadPart = 0x10000; // 64k, for PATA compability
PortData->LocalBuffer = MmAllocateContiguousMemorySpecifyCache(ATA_LOCAL_BUFFER_SIZE,
LowestAcceptableAddress,
HighestAcceptableAddress,
BoundaryAddressMultiple,
MmNonCached);
if (!PortData->LocalBuffer)
return STATUS_INSUFFICIENT_RESOURCES;
PhysicalAddress = MmGetPhysicalAddress(PortData->LocalBuffer);
PortData->LocalSgList.NumberOfElements = 1;
PortData->LocalSgList.Elements[0].Length = ATA_LOCAL_BUFFER_SIZE;
PortData->LocalSgList.Elements[0].Address.QuadPart = PhysicalAddress.QuadPart;
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaFdoCreatePortThread(
_In_ PATAPORT_PORT_DATA PortData)
{
OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, OBJ_KERNEL_HANDLE);
HANDLE ThreadHandle;
NTSTATUS Status;
PAGED_CODE();
if (PortData->Worker.Thread)
return STATUS_SUCCESS;
KeInitializeEvent(&PortData->Worker.ThreadEvent, NotificationEvent, FALSE);
Status = PsCreateSystemThread(&ThreadHandle,
THREAD_ALL_ACCESS,
&ObjectAttributes,
NULL,
NULL,
AtaPortWorkerThread,
PortData);
if (!NT_SUCCESS(Status))
return Status;
Status = ObReferenceObjectByHandle(ThreadHandle,
THREAD_ALL_ACCESS,
NULL,
KernelMode,
(PVOID*)&PortData->Worker.Thread,
NULL);
if (!NT_SUCCESS(Status))
return Status;
ZwClose(ThreadHandle);
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
VOID
AtaFdoDestroyPortThread(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
{
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
PAGED_CODE();
if (!PortData->Worker.Thread)
return;
PortData->PortFlags |= PORT_FLAG_EXIT_THREAD;
AtaPortSignalWorkerThread(PortData);
KeWaitForSingleObject(PortData->Worker.Thread, Executive, KernelMode, FALSE, NULL);
ObDereferenceObject(PortData->Worker.Thread);
PortData->Worker.Thread = NULL;
}
static
CODE_SEG("PAGE")
VOID
AtaFdoRemoveSymLinks(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
{
NTSTATUS Status;
UNICODE_STRING SymLinkName;
WCHAR SymLinkNameBuffer[sizeof("\\DosDevices\\Scsi99999:")];
PAGED_CODE();
if (!(ChanExt->PortData.PortFlags & PORT_FLAG_SYMLINK_CREATED))
return;
/* Delete the '\DosDevices\\ScsiX:' symbolic link */
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
sizeof(SymLinkNameBuffer),
AtapDevSymLinkFormat,
ChanExt->ScsiPortNumber);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
(VOID)IoDeleteSymbolicLink(&SymLinkName);
/* Delete the '\Device\\ScsiPortX' symbolic link */
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
sizeof(SymLinkNameBuffer),
AtapDosSymLinkFormat,
ChanExt->ScsiPortNumber);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
(VOID)IoDeleteSymbolicLink(&SymLinkName);
/* Unregister the SCSI port adapter */
IoGetConfigurationInformation()->ScsiPortCount--;
}
CODE_SEG("PAGE")
NTSTATUS
AtaFdoStartDevice(
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
_In_ PCM_RESOURCE_LIST ResourcesTranslated)
{
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
PCIIDEX_CHANNEL_INTERFACE ChannelInterface = { 0 };
NTSTATUS Status;
PAGED_CODE();
INFO("Starting channel %lu\n", ChanExt->DeviceObjectNumber);
/* Get the interface of ATA channel */
Status = AtaPnpQueryInterface(&ChanExt->Common,
&GUID_PCIIDE_INTERFACE_ROS,
&ChannelInterface,
PCIIDEX_INTERFACE_VERSION,
sizeof(ChannelInterface));
if (!NT_SUCCESS(Status))
{
ERR("Failed to query channel interface %lx\n", Status);
return Status;
}
PortData->ChannelContext = ChannelInterface.ChannelContext;
PortData->AttachChannel = ChannelInterface.AttachChannel;
PortData->SetDeviceData = ChannelInterface.SetDeviceData;
PortData->GetInitTaskFile = ChannelInterface.GetInitTaskFile;
PortData->DowngradeInterfaceSpeed = ChannelInterface.DowngradeInterfaceSpeed;
PortData->InterruptObject = ChannelInterface.InterruptObject;
PortData->AbortChannel = ChannelInterface.AbortChannel;
PortData->ResetChannel = ChannelInterface.ResetChannel;
PortData->EnumerateChannel = ChannelInterface.EnumerateChannel;
PortData->IdentifyDevice = ChannelInterface.IdentifyDevice;
PortData->SetTransferMode = ChannelInterface.SetTransferMode;
PortData->AllocateSlot = ChannelInterface.AllocateSlot;
PortData->PreparePrdTable = ChannelInterface.PreparePrdTable;
PortData->PrepareIo = ChannelInterface.PrepareIo;
PortData->StartIo = ChannelInterface.StartIo;
PortData->MaxTargetId = ChannelInterface.MaxTargetId;
PortData->MaximumTransferLength = ChannelInterface.MaximumTransferLength;
PortData->MaximumPhysicalPages = ChannelInterface.MaximumPhysicalPages;
PortData->QueueDepth = ChannelInterface.QueueDepth;
PortData->PortNumber = ChannelInterface.Channel;
PortData->DmaAdapter = ChannelInterface.DmaAdapter;
PortData->ChannelObject = ChannelInterface.ChannelObject;
PortData->InterruptFlags = PORT_INT_FLAG_IS_IO_ACTIVE;
PortData->FreeSlotsBitmap =
PortData->MaxSlotsBitmap = NUM_TO_BITMAP(PortData->QueueDepth);
/* We need the slot numbers to start from zero */
PortData->LastUsedSlot = RTL_BITS_OF(ULONG) - 1;
if (!(ChannelInterface.TransferModeSupported & ~PIO_ALL))
PortData->PortFlags |= PORT_FLAG_PIO_ONLY;
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_VIA_DMA)
PortData->PortFlags |= PORT_FLAG_PIO_VIA_DMA;
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_NCQ)
PortData->PortFlags |= PORT_FLAG_NCQ;
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_IS_AHCI)
PortData->PortFlags |= PORT_FLAG_IS_AHCI;
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_IS_EXTERNAL)
PortData->PortFlags |= PORT_FLAG_IS_EXTERNAL;
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_FOR_LBA48_XFER)
PortData->PortFlags |= PORT_FLAG_PIO_FOR_LBA48_XFER;
if (ChannelInterface.HwSyncObject)
{
PortData->PortFlags |= PORT_FLAG_IS_SIMPLEX;
PortData->HwSyncObject = ChannelInterface.HwSyncObject;
}
/* Reserve PIO memory resources early. Storage drivers should not fail paging I/O operations */
if (!(ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_VIA_DMA) && !PortData->ReservedVaSpace)
{
PortData->ReservedVaSpace = MmAllocateMappingAddress(ATA_RESERVED_PAGES * PAGE_SIZE,
ATAPORT_TAG);
}
Status = AtaFdoAllocateLocalBuffer(PortData);
if (!NT_SUCCESS(Status))
{
ERR("CH %lu: Failed to allocate local buffer 0x%lx\n", PortData->PortNumber, Status);
return Status;
}
InitializeListHead(&PortData->PortQueueList);
KeInitializeEvent(&PortData->QueueStoppedEvent, NotificationEvent, FALSE);
KeInitializeDpc(&PortData->Worker.Dpc, AtaPortWorkerSignalDpc, PortData);
KeInitializeDpc(&PortData->Worker.NotificationDpc, AtaStorageNotificationlDpc, PortData);
KeInitializeSpinLock(&PortData->Worker.Lock);
KeInitializeEvent(&PortData->Worker.EnumerationEvent, NotificationEvent, FALSE);
KeInitializeEvent(&PortData->Worker.CompletionEvent, NotificationEvent, FALSE);
PortData->Worker.InternalRequest.Complete = AtaPortCompleteInternalRequest;
#if DBG
PortData->Worker.InternalRequest.Signature = ATA_DEVICE_REQUEST_SIGNATURE;
#endif
Status = AtaFdoCreatePortThread(PortData);
if (!NT_SUCCESS(Status))
{
ERR("CH %lu: Failed to create port thread 0x%lx\n", PortData->PortNumber, Status);
return Status;
}
Status = AtaFdoCreateSymLinks(ChanExt);
if (!NT_SUCCESS(Status))
{
ERR("CH %lu: Failed to create symbolic links 0x%lx\n", PortData->PortNumber, Status);
return Status;
}
AtaSetPortRegistryKey(ChanExt, DD_ATA_REG_MAX_TARGET_ID, PortData->MaxTargetId);
Status = IoRegisterDeviceInterface(ChanExt->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);
}
+940
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 <[email protected]>
*/
/* 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);
}
+798
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 <[email protected]>
*/
/* 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
+210
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 <[email protected]>
*/
#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);
}
+298
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 <[email protected]>
*/
/* 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;
}
+213
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 <[email protected]>
*/
/* 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);
}
@@ -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 <[email protected]>
*/
#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;
}