mirror of
https://github.com/reactos/reactos.git
synced 2026-06-03 09:51:03 +08:00
[ATAPI] Add ATA storage driver
CORE-17256 CORE-17191 CORE-17716 CORE-17977 CORE-13976 CORE-16216
This commit is contained in:
committed by
Carl J. Bialorucki
parent
ae2827f481
commit
ac33647888
@@ -1,4 +1,5 @@
|
||||
|
||||
add_subdirectory(atapi)
|
||||
add_subdirectory(pciide)
|
||||
add_subdirectory(pciidex)
|
||||
add_subdirectory(uniata)
|
||||
|
||||
36
drivers/storage/ide/atapi/CMakeLists.txt
Normal file
36
drivers/storage/ide/atapi/CMakeLists.txt
Normal 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
drivers/storage/ide/atapi/atapi.c
Normal file
765
drivers/storage/ide/atapi/atapi.c
Normal file
@@ -0,0 +1,765 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: Driver entrypoint and utility functions
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
UNICODE_STRING AtapDriverRegistryPath;
|
||||
BOOLEAN AtapInPEMode;
|
||||
|
||||
ATAPORT_PAGED_DATA
|
||||
static const struct
|
||||
{
|
||||
PCSTR DeviceType;
|
||||
PCSTR GenericType;
|
||||
PCSTR PeripheralId;
|
||||
} AtapGenericDeviceNames[] =
|
||||
{
|
||||
{"Disk", "GenDisk", "DiskPeripheral" }, // DIRECT_ACCESS_DEVICE
|
||||
{"Sequential", "GenSequential", "TapePeripheral" }, // SEQUENTIAL_ACCESS_DEVICE
|
||||
{"Printer", "GenPrinter", "PrinterPeripheral" }, // PRINTER_DEVICE
|
||||
{"Processor", "GenProcessor", "ProcessorPeripheral" }, // PROCESSOR_DEVICE
|
||||
{"Worm", "GenWorm", "WormPeripheral" }, // WRITE_ONCE_READ_MULTIPLE_DEVICE
|
||||
{"CdRom", "GenCdRom", "CdRomPeripheral" }, // READ_ONLY_DIRECT_ACCESS_DEVICE
|
||||
{"Scanner", "GenScanner", "ScannerPeripheral" }, // SCANNER_DEVICE
|
||||
{"Optical", "GenOptical", "OpticalDiskPeripheral" }, // OPTICAL_DEVICE
|
||||
{"Changer", "GenChanger", "MediumChangerPeripheral" }, // MEDIUM_CHANGER
|
||||
{"Net", "GenNet", "CommunicationsPeripheral"}, // COMMUNICATION_DEVICE
|
||||
{"Other", "IdeOther", "OtherPeripheral" }, // 10
|
||||
};
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
PCSTR
|
||||
AtaTypeCodeToName(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ DEVICE_TYPE_NAME Type)
|
||||
{
|
||||
ULONG DeviceType;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
DeviceType = DevExt->InquiryData.DeviceType;
|
||||
DeviceType = min(DeviceType, RTL_NUMBER_OF(AtapGenericDeviceNames) - 1);
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case GetDeviceType:
|
||||
return AtapGenericDeviceNames[DeviceType].DeviceType;
|
||||
|
||||
case GetPeripheralId:
|
||||
return AtapGenericDeviceNames[DeviceType].PeripheralId;
|
||||
|
||||
case GetGenericType:
|
||||
if (DevExt->Device.DeviceFlags & DEVICE_IS_SUPER_FLOPPY)
|
||||
return "GenSFloppy"; // Install the Super Floppy storage class driver
|
||||
else
|
||||
return AtapGenericDeviceNames[DeviceType].GenericType;
|
||||
|
||||
default:
|
||||
ASSERT(FALSE);
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaOpenRegistryKey(
|
||||
_Out_ PHANDLE KeyHandle,
|
||||
_In_ HANDLE RootKey,
|
||||
_In_ PUNICODE_STRING KeyName,
|
||||
_In_ BOOLEAN Create)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
ULONG Disposition;
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
KeyName,
|
||||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||||
RootKey,
|
||||
NULL);
|
||||
|
||||
if (Create)
|
||||
{
|
||||
Status = ZwCreateKey(KeyHandle,
|
||||
KEY_ALL_ACCESS,
|
||||
&ObjectAttributes,
|
||||
0,
|
||||
NULL,
|
||||
REG_OPTION_VOLATILE,
|
||||
&Disposition);
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = ZwOpenKey(KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaGetRegistryKey(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ UCHAR TargetId,
|
||||
_In_ PCWSTR KeyName,
|
||||
_Out_ PULONG KeyValue,
|
||||
_In_ ULONG DefaultValue)
|
||||
{
|
||||
UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(*KeyValue)];
|
||||
HANDLE HwKeyHandle, TargetKeyHandle;
|
||||
UNICODE_STRING ValueName, TargetKeyName;
|
||||
NTSTATUS Status;
|
||||
WCHAR TargetKeyBuffer[sizeof("Target99")];
|
||||
ULONG ResultLength;
|
||||
PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
Status = IoOpenDeviceRegistryKey(ChanExt->Pdo,
|
||||
PLUGPLAY_REGKEY_DEVICE,
|
||||
KEY_ALL_ACCESS,
|
||||
&HwKeyHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("Failed to open device hardware key, status 0x%lx\n", Status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Open or create the 'TargetX' key */
|
||||
Status = RtlStringCbPrintfW(TargetKeyBuffer,
|
||||
sizeof(TargetKeyBuffer),
|
||||
L"Target%u",
|
||||
TargetId);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&TargetKeyName, TargetKeyBuffer);
|
||||
Status = AtaOpenRegistryKey(&TargetKeyHandle, HwKeyHandle, &TargetKeyName, TRUE);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("Failed to create '%wZ' key, status 0x%lx\n", &TargetKeyName, Status);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString(&ValueName, KeyName);
|
||||
Status = ZwQueryValueKey(TargetKeyHandle,
|
||||
&ValueName,
|
||||
KeyValuePartialInformation,
|
||||
PartialInfo,
|
||||
sizeof(Buffer),
|
||||
&ResultLength);
|
||||
ZwClose(TargetKeyHandle);
|
||||
if (!NT_SUCCESS(Status) ||
|
||||
(PartialInfo->Type != REG_DWORD) ||
|
||||
(PartialInfo->DataLength != sizeof(*KeyValue)))
|
||||
{
|
||||
TRACE("Failed to read '%wZ' key, status 0x%lx\n", &ValueName, Status);
|
||||
|
||||
*KeyValue = DefaultValue;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
*KeyValue = *(PULONG)&PartialInfo->Data;
|
||||
|
||||
Cleanup:
|
||||
ZwClose(HwKeyHandle);
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaSetRegistryKey(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ UCHAR TargetId,
|
||||
_In_ PCWSTR KeyName,
|
||||
_In_ ULONG KeyValue)
|
||||
{
|
||||
HANDLE HwKeyHandle, TargetKeyHandle;
|
||||
UNICODE_STRING ValueName, TargetKeyName;
|
||||
NTSTATUS Status;
|
||||
WCHAR TargetKeyBuffer[sizeof("Target99")];
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
Status = IoOpenDeviceRegistryKey(ChanExt->Pdo,
|
||||
PLUGPLAY_REGKEY_DEVICE,
|
||||
KEY_ALL_ACCESS,
|
||||
&HwKeyHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("Failed to open device hardware key, status 0x%lx\n", Status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Open or create the 'TargetX' key */
|
||||
Status = RtlStringCbPrintfW(TargetKeyBuffer,
|
||||
sizeof(TargetKeyBuffer),
|
||||
L"Target%u",
|
||||
TargetId);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&TargetKeyName, TargetKeyBuffer);
|
||||
Status = AtaOpenRegistryKey(&TargetKeyHandle, HwKeyHandle, &TargetKeyName, TRUE);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("Failed to create '%wZ' key, status 0x%lx\n", &TargetKeyName, Status);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString(&ValueName, KeyName);
|
||||
Status = ZwSetValueKey(TargetKeyHandle,
|
||||
&ValueName,
|
||||
0,
|
||||
REG_DWORD,
|
||||
&KeyValue,
|
||||
sizeof(KeyValue));
|
||||
ZwClose(TargetKeyHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("Failed to set '%wZ' key, status 0x%lx\n", &ValueName, Status);
|
||||
}
|
||||
|
||||
Cleanup:
|
||||
ZwClose(HwKeyHandle);
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaSetPortRegistryKey(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PCWSTR KeyName,
|
||||
_In_ ULONG KeyValue)
|
||||
{
|
||||
HANDLE HwKeyHandle;
|
||||
UNICODE_STRING ValueName;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
Status = IoOpenDeviceRegistryKey(ChanExt->Pdo,
|
||||
PLUGPLAY_REGKEY_DEVICE,
|
||||
KEY_ALL_ACCESS,
|
||||
&HwKeyHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("Failed to open device hardware key, status 0x%lx\n", Status);
|
||||
return;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString(&ValueName, KeyName);
|
||||
Status = ZwSetValueKey(HwKeyHandle,
|
||||
&ValueName,
|
||||
0,
|
||||
REG_DWORD,
|
||||
&KeyValue,
|
||||
sizeof(KeyValue));
|
||||
ZwClose(HwKeyHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("Failed to set '%wZ' key, status 0x%lx\n", &ValueName, Status);
|
||||
}
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaDispatchCreateClose(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
NTAPI
|
||||
AtaUnload(
|
||||
_In_ PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
RtlFreeUnicodeString(&AtapDriverRegistryPath);
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
AtaStorageNotificationWorker(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_In_opt_ PVOID Context)
|
||||
{
|
||||
PIO_WORKITEM WorkItem = Context;
|
||||
PDEVICE_OBJECT TopDeviceObject;
|
||||
PIRP Irp;
|
||||
KEVENT Event;
|
||||
IO_STATUS_BLOCK IoStatus;
|
||||
STORAGE_EVENT_NOTIFICATION EventObject;
|
||||
NTSTATUS Status;
|
||||
|
||||
TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
|
||||
|
||||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||||
|
||||
EventObject.Version = STORAGE_EVENT_NOTIFICATION_VERSION_V1;
|
||||
EventObject.Size = sizeof(EventObject);
|
||||
EventObject.Events = STORAGE_EVENT_ALL;
|
||||
|
||||
Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_EVENT_NOTIFICATION,
|
||||
TopDeviceObject,
|
||||
&EventObject,
|
||||
sizeof(EventObject),
|
||||
NULL,
|
||||
0,
|
||||
FALSE,
|
||||
&Event,
|
||||
&IoStatus);
|
||||
if (!Irp)
|
||||
{
|
||||
ERR("IoBuildDeviceIoControlRequest() failed\n");
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Status = IoCallDriver(DeviceObject, Irp);
|
||||
if (Status == STATUS_PENDING)
|
||||
{
|
||||
KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
|
||||
Status = IoStatus.Status;
|
||||
}
|
||||
|
||||
TRACE("Notification result %08lx\n", Status);
|
||||
|
||||
Exit:
|
||||
ObDereferenceObject(TopDeviceObject);
|
||||
IoFreeWorkItem(WorkItem);
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
AtaStorageNotificationlDpc(
|
||||
_In_ PKDPC Dpc,
|
||||
_In_opt_ PVOID DeferredContext,
|
||||
_In_opt_ PVOID SystemArgument1,
|
||||
_In_opt_ PVOID SystemArgument2)
|
||||
{
|
||||
PATAPORT_PORT_DATA PortData = DeferredContext;
|
||||
ULONG DeviceBitmap = PtrToUlong(SystemArgument1);
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt;
|
||||
ULONG i;
|
||||
|
||||
UNREFERENCED_PARAMETER(Dpc);
|
||||
UNREFERENCED_PARAMETER(SystemArgument2);
|
||||
|
||||
ChanExt = CONTAINING_RECORD(PortData, ATAPORT_CHANNEL_EXTENSION, PortData);
|
||||
|
||||
for (i = 0; i < ATA_MAX_DEVICE; ++i)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
PIO_WORKITEM WorkItem;
|
||||
|
||||
if (!(DeviceBitmap & (1 << i)))
|
||||
continue;
|
||||
|
||||
DevExt = AtaFdoFindDeviceByPath(ChanExt,
|
||||
AtaMarshallScsiAddress(PortData->PortNumber, i, 0),
|
||||
AtaStorageNotificationlDpc);
|
||||
if (!DevExt)
|
||||
continue;
|
||||
|
||||
WorkItem = IoAllocateWorkItem(DevExt->Common.Self);
|
||||
if (!WorkItem)
|
||||
{
|
||||
ERR("Failed to allocate workitem");
|
||||
}
|
||||
else
|
||||
{
|
||||
IoQueueWorkItem(WorkItem, AtaStorageNotificationWorker, CriticalWorkQueue, WorkItem);
|
||||
}
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, AtaStorageNotificationlDpc);
|
||||
}
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPnpQueryInterface(
|
||||
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
|
||||
_In_ const GUID* Guid,
|
||||
_Out_ PVOID Interface,
|
||||
_In_ ULONG Version,
|
||||
_In_ ULONG Size)
|
||||
{
|
||||
KEVENT Event;
|
||||
PIRP Irp;
|
||||
IO_STATUS_BLOCK IoStatusBlock;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||||
|
||||
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
|
||||
CommonExt->LowerDeviceObject,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&Event,
|
||||
&IoStatusBlock);
|
||||
if (!Irp)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||||
|
||||
IoStack = IoGetNextIrpStackLocation(Irp);
|
||||
IoStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
|
||||
IoStack->Parameters.QueryInterface.InterfaceType = Guid;
|
||||
IoStack->Parameters.QueryInterface.Size = Size;
|
||||
IoStack->Parameters.QueryInterface.Version = Version;
|
||||
IoStack->Parameters.QueryInterface.Interface = Interface;
|
||||
IoStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
||||
|
||||
Status = IoCallDriver(CommonExt->LowerDeviceObject, Irp);
|
||||
if (Status == STATUS_PENDING)
|
||||
{
|
||||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
||||
Status = IoStatusBlock.Status;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPnpRepeatRequest(
|
||||
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_opt_ PDEVICE_CAPABILITIES DeviceCapabilities)
|
||||
{
|
||||
PATAPORT_COMMON_EXTENSION FdoExt = CommonExt->FdoExt;
|
||||
PDEVICE_OBJECT TopDeviceObject;
|
||||
PIO_STACK_LOCATION IoStack, SubStack;
|
||||
PIRP SubIrp;
|
||||
KEVENT Event;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
ASSERT(!IS_FDO(CommonExt));
|
||||
|
||||
TopDeviceObject = IoGetAttachedDeviceReference(FdoExt->Self);
|
||||
|
||||
SubIrp = IoAllocateIrp(TopDeviceObject->StackSize, FALSE);
|
||||
if (!SubIrp)
|
||||
{
|
||||
ObDereferenceObject(TopDeviceObject);
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
SubStack = IoGetNextIrpStackLocation(SubIrp);
|
||||
RtlCopyMemory(SubStack, IoStack, sizeof(*SubStack));
|
||||
|
||||
if (DeviceCapabilities)
|
||||
SubStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
|
||||
|
||||
IoSetCompletionRoutine(SubIrp,
|
||||
AtaPdoCompletionRoutine,
|
||||
&Event,
|
||||
TRUE,
|
||||
TRUE,
|
||||
TRUE);
|
||||
|
||||
SubIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||||
|
||||
Status = IoCallDriver(TopDeviceObject, SubIrp);
|
||||
if (Status == STATUS_PENDING)
|
||||
{
|
||||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
||||
}
|
||||
|
||||
ObDereferenceObject(TopDeviceObject);
|
||||
|
||||
Status = SubIrp->IoStatus.Status;
|
||||
IoFreeIrp(SubIrp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPnpQueryDeviceUsageNotification(
|
||||
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
NTSTATUS Status;
|
||||
volatile LONG* Counter;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!IS_FDO(CommonExt))
|
||||
{
|
||||
Status = AtaPnpRepeatRequest(CommonExt, Irp, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!NT_VERIFY(IoForwardIrpSynchronously(CommonExt->LowerDeviceObject, Irp)))
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
Status = Irp->IoStatus.Status;
|
||||
}
|
||||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
switch (IoStack->Parameters.UsageNotification.Type)
|
||||
{
|
||||
case DeviceUsageTypePaging:
|
||||
Counter = &CommonExt->PageFiles;
|
||||
break;
|
||||
|
||||
case DeviceUsageTypeHibernation:
|
||||
Counter = &CommonExt->HibernateFiles;
|
||||
break;
|
||||
|
||||
case DeviceUsageTypeDumpFile:
|
||||
Counter = &CommonExt->DumpFiles;
|
||||
break;
|
||||
|
||||
default:
|
||||
return Status;
|
||||
}
|
||||
|
||||
IoAdjustPagingPathCount(Counter, IoStack->Parameters.UsageNotification.InPath);
|
||||
|
||||
if (!IS_FDO(CommonExt))
|
||||
IoInvalidateDeviceState(CommonExt->Self);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPnpQueryPnpDeviceState(
|
||||
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
if (CommonExt->PageFiles || CommonExt->HibernateFiles || CommonExt->DumpFiles)
|
||||
Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
|
||||
|
||||
if (IS_FDO(CommonExt))
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaPnpInitializeCommonExtension(
|
||||
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
|
||||
_In_ PDEVICE_OBJECT SelfDeviceObject,
|
||||
_In_ ULONG Flags)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
CommonExt->Flags = Flags;
|
||||
CommonExt->Self = SelfDeviceObject;
|
||||
CommonExt->DevicePowerState = PowerDeviceD0;
|
||||
CommonExt->SystemPowerState = PowerSystemWorking;
|
||||
|
||||
IoInitializeRemoveLock(&CommonExt->RemoveLock, ATAPORT_TAG, 0, 0);
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaAddChannel(
|
||||
_In_ PDRIVER_OBJECT DriverObject,
|
||||
_In_ PDEVICE_OBJECT PhysicalDeviceObject)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
PDEVICE_OBJECT Fdo;
|
||||
UNICODE_STRING DeviceName;
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt;
|
||||
WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\IdePort99999")];
|
||||
DECLARE_PAGED_WSTRING(FdoFormat, L"\\Device\\Ide\\IdePort%lu");
|
||||
static ULONG AtapFdoNumber = 0;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
Status = RtlStringCbPrintfW(DeviceNameBuffer,
|
||||
sizeof(DeviceNameBuffer),
|
||||
FdoFormat,
|
||||
AtapFdoNumber);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
|
||||
|
||||
INFO("%s(%p, %p) '%wZ'\n", __FUNCTION__, DriverObject, PhysicalDeviceObject, &DeviceName);
|
||||
|
||||
Status = IoCreateDevice(DriverObject,
|
||||
sizeof(*ChanExt),
|
||||
&DeviceName,
|
||||
FILE_DEVICE_CONTROLLER,
|
||||
FILE_DEVICE_SECURE_OPEN,
|
||||
FALSE,
|
||||
&Fdo);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("Failed to create the FDO with status 0x%lx\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
ChanExt = Fdo->DeviceExtension;
|
||||
|
||||
RtlZeroMemory(ChanExt, sizeof(*ChanExt));
|
||||
AtaPnpInitializeCommonExtension(&ChanExt->Common, Fdo, DO_IS_FDO);
|
||||
|
||||
ChanExt->Common.LowerDeviceObject = IoAttachDeviceToDeviceStack(Fdo, PhysicalDeviceObject);
|
||||
if (!ChanExt->Common.LowerDeviceObject)
|
||||
{
|
||||
ERR("Failed to attach the FDO\n");
|
||||
Status = STATUS_DEVICE_REMOVED;
|
||||
goto Failure;
|
||||
}
|
||||
ChanExt->DeviceObjectNumber = AtapFdoNumber++;
|
||||
ChanExt->Pdo = PhysicalDeviceObject;
|
||||
|
||||
/* DMA buffers alignment */
|
||||
Fdo->AlignmentRequirement = ChanExt->Common.LowerDeviceObject->AlignmentRequirement;
|
||||
Fdo->AlignmentRequirement = max(Fdo->AlignmentRequirement, ATA_MIN_BUFFER_ALIGNMENT);
|
||||
|
||||
KeInitializeSpinLock(&ChanExt->PdoListLock);
|
||||
|
||||
Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Failure:
|
||||
if (ChanExt->Common.LowerDeviceObject)
|
||||
IoDetachDevice(ChanExt->Common.LowerDeviceObject);
|
||||
|
||||
IoDeleteDevice(Fdo);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
AtaInPEMode(VOID)
|
||||
{
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
HANDLE KeyHandle;
|
||||
NTSTATUS Status;
|
||||
DECLARE_PAGED_UNICODE_STRING(
|
||||
KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\MiniNT");
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
&KeyName,
|
||||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
ZwClose(KeyHandle);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("INIT")
|
||||
VOID
|
||||
AtaCreateIdeDirectory(VOID)
|
||||
{
|
||||
HANDLE Handle;
|
||||
NTSTATUS Status;
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
DECLARE_PAGED_UNICODE_STRING(DirectoryName, L"\\Device\\Ide");
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
&DirectoryName,
|
||||
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_KERNEL_HANDLE,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
Status = ZwCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
/* We don't need a handle for a permanent object */
|
||||
ZwClose(Handle);
|
||||
}
|
||||
/*
|
||||
* Ignore directory creation failures (don't report them as a driver initialization error)
|
||||
* as the directory may have already been created by another driver.
|
||||
* We will handle fatal errors later via IoCreateDevice() call.
|
||||
*/
|
||||
}
|
||||
|
||||
CODE_SEG("INIT")
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
DriverEntry(
|
||||
_In_ PDRIVER_OBJECT DriverObject,
|
||||
_In_ PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
INFO("ATAPI driver entry\n");
|
||||
|
||||
// FIXME: No crashdump/hibernation support
|
||||
if (!DriverObject)
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
|
||||
/* Make a copy of the registry path */
|
||||
AtapDriverRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
|
||||
AtapDriverRegistryPath.Buffer =
|
||||
ExAllocatePoolUninitialized(NonPagedPool,
|
||||
AtapDriverRegistryPath.MaximumLength,
|
||||
ATAPORT_TAG);
|
||||
if (!AtapDriverRegistryPath.Buffer)
|
||||
{
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
RtlCopyUnicodeString(&AtapDriverRegistryPath, RegistryPath);
|
||||
AtapDriverRegistryPath.Buffer[RegistryPath->Length / sizeof(WCHAR)] = UNICODE_NULL;
|
||||
|
||||
DriverObject->MajorFunction[IRP_MJ_CREATE] = AtaDispatchCreateClose;
|
||||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = AtaDispatchCreateClose;
|
||||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AtaDispatchDeviceControl;
|
||||
DriverObject->MajorFunction[IRP_MJ_SCSI] = AtaDispatchScsi;
|
||||
DriverObject->MajorFunction[IRP_MJ_POWER] = AtaDispatchPower;
|
||||
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = AtaDispatchWmi;
|
||||
DriverObject->MajorFunction[IRP_MJ_PNP] = AtaDispatchPnp;
|
||||
DriverObject->DriverExtension->AddDevice = AtaAddChannel;
|
||||
DriverObject->DriverUnload = AtaUnload;
|
||||
|
||||
KeInitializeDpc(&AtapCompletionDpc, AtaReqCompletionDpc, NULL);
|
||||
InitializeSListHead(&AtapCompletionQueueList);
|
||||
|
||||
/* Create a directory to hold the driver's device objects */
|
||||
AtaCreateIdeDirectory();
|
||||
|
||||
AtapInPEMode = AtaInPEMode();
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
1022
drivers/storage/ide/atapi/atapi.h
Normal file
1022
drivers/storage/ide/atapi/atapi.h
Normal file
File diff suppressed because it is too large
Load Diff
5
drivers/storage/ide/atapi/atapi.rc
Normal file
5
drivers/storage/ide/atapi/atapi.rc
Normal 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
drivers/storage/ide/atapi/atapi_reg.inf
Normal file
8
drivers/storage/ide/atapi/atapi_reg.inf
Normal 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
drivers/storage/ide/atapi/debug.h
Normal file
95
drivers/storage/ide/atapi/debug.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: Debug support header file
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __RELFILE__
|
||||
#define __RELFILE__ __FILE__
|
||||
#endif
|
||||
|
||||
#if DBG
|
||||
|
||||
// #define DEBUG_TRACE
|
||||
// #define DEBUG_INFO
|
||||
#define DEBUG_WARN
|
||||
#define DEBUG_ERR
|
||||
|
||||
#ifdef DEBUG_TRACE
|
||||
#define TRACE(fmt, ...) \
|
||||
do { \
|
||||
if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \
|
||||
DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#if defined(_MSC_VER)
|
||||
#define TRACE __noop
|
||||
#else
|
||||
#define TRACE(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_INFO
|
||||
#define INFO(fmt, ...) \
|
||||
do { \
|
||||
if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \
|
||||
DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#if defined(_MSC_VER)
|
||||
#define INFO __noop
|
||||
#else
|
||||
#define INFO(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_WARN
|
||||
#define WARN(fmt, ...) \
|
||||
do { \
|
||||
if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \
|
||||
DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#if defined(_MSC_VER)
|
||||
#define WARN __noop
|
||||
#else
|
||||
#define WARN(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ERR
|
||||
#define ERR(fmt, ...) \
|
||||
do { \
|
||||
if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \
|
||||
DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#if defined(_MSC_VER)
|
||||
#define ERR __noop
|
||||
#else
|
||||
#define ERR(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define TRACE __noop
|
||||
#define INFO __noop
|
||||
#define WARN __noop
|
||||
#define ERR __noop
|
||||
#else
|
||||
#define TRACE(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
|
||||
#define INFO(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
|
||||
#define WARN(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
|
||||
#define ERR(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
396
drivers/storage/ide/atapi/dev_config.c
Normal file
396
drivers/storage/ide/atapi/dev_config.c
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: Device initialization and configuration code
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
#include <acpiioct.h>
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSetGeometry(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
NTSTATUS Status;
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device) || (DevExt->Device.DeviceFlags & DEVICE_LBA_MODE))
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Request->Flags = REQUEST_FLAG_SET_DEVICE_REGISTER;
|
||||
Request->TimeOut = 5;
|
||||
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = IDE_COMMAND_SET_DRIVE_PARAMETERS;
|
||||
Request->TaskFile.SectorCount = (UCHAR)DevExt->Device.SectorsPerTrack;
|
||||
Request->TaskFile.DriveSelect = (UCHAR)(DevExt->Device.Heads - 1) |
|
||||
DevExt->Device.DeviceSelect;
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
WARN("CH %lu: Failed to set geometry for '%s'\n",
|
||||
PortData->PortNumber,
|
||||
DevExt->TransferModeSelectedBitmap,
|
||||
DevExt->FriendlyName);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceBuildSetTransferModeTaskFile(
|
||||
_In_ PATA_DEVICE_REQUEST Request,
|
||||
_In_ UCHAR Mode)
|
||||
{
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 3;
|
||||
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = IDE_COMMAND_SET_FEATURE;
|
||||
Request->TaskFile.Feature = IDE_FEATURE_SET_TRANSFER_MODE;
|
||||
Request->TaskFile.SectorCount = Mode;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSetPioTransferMode(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
NTSTATUS Status;
|
||||
ULONG Mode;
|
||||
|
||||
NT_VERIFY(_BitScanReverse(&Mode, DevExt->TransferModeSelectedBitmap & PIO_ALL) != 0);
|
||||
|
||||
Mode = IDE_SET_ADVANCE_PIO_MODE(Mode);
|
||||
AtaDeviceBuildSetTransferModeTaskFile(Request, Mode);
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
WARN("CH %lu: Failed to set %lu PIO settings for '%s'\n",
|
||||
PortData->PortNumber,
|
||||
Mode,
|
||||
DevExt->FriendlyName);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSetDmaTransferMode(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
NTSTATUS Status;
|
||||
ULONG Mode;
|
||||
|
||||
if (!_BitScanReverse(&Mode, DevExt->TransferModeSelectedBitmap & ~PIO_ALL))
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
if (Mode >= UDMA_MODE(0))
|
||||
Mode = IDE_SET_UDMA_MODE(Mode - UDMA_MODE(0));
|
||||
else if (Mode >= MWDMA_MODE(0))
|
||||
Mode = IDE_SET_MWDMA_MODE(Mode - MWDMA_MODE(0));
|
||||
else
|
||||
Mode = IDE_SET_SWDMA_MODE(Mode - SWDMA_MODE(0));
|
||||
AtaDeviceBuildSetTransferModeTaskFile(Request, Mode);
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
DevExt->Device.DeviceFlags &= ~DEVICE_PIO_ONLY;
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN("CH %lu: Failed to set 0x%lx DMA settings for '%s'\n",
|
||||
PortData->PortNumber,
|
||||
Mode,
|
||||
DevExt->FriendlyName);
|
||||
|
||||
DevExt->Device.DeviceFlags &= ~DEVICE_NCQ;
|
||||
DevExt->TransferModeSelectedBitmap &= PIO_ALL;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSetMultipleMode(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
NTSTATUS Status;
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
DevExt->Device.MultiSectorCount = AtaDevMaximumSectorsPerDrq(&DevExt->IdentifyDeviceData);
|
||||
if (DevExt->Device.MultiSectorCount == 0)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 3;
|
||||
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = IDE_COMMAND_SET_MULTIPLE;
|
||||
Request->TaskFile.SectorCount = DevExt->Device.MultiSectorCount;
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
WARN("CH %lu: Failed to set %u multiple mode for '%s'\n",
|
||||
PortData->PortNumber,
|
||||
DevExt->Device.MultiSectorCount,
|
||||
DevExt->FriendlyName);
|
||||
|
||||
DevExt->Device.MultiSectorCount = 0;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
BOOLEAN
|
||||
AtaDeviceFilterAcpiTaskFile(
|
||||
_In_ PATA_ACPI_TASK_FILE AcpiTaskFile)
|
||||
{
|
||||
switch (AcpiTaskFile->Command)
|
||||
{
|
||||
/* These features are managed by the driver */
|
||||
case IDE_COMMAND_SET_FEATURE:
|
||||
{
|
||||
if (AcpiTaskFile->Feature == IDE_FEATURE_SET_TRANSFER_MODE)
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
case IDE_COMMAND_SET_DRIVE_PARAMETERS:
|
||||
case IDE_COMMAND_SET_MULTIPLE:
|
||||
return FALSE;
|
||||
|
||||
// TODO: Anything else to check?
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceAcpiTaskFileToInternalTaskFile(
|
||||
_In_ ATA_ACPI_TASK_FILE* __restrict AcpiTaskFile,
|
||||
_Out_ ATA_DEVICE_REQUEST* __restrict Request)
|
||||
{
|
||||
Request->Flags = REQUEST_FLAG_SET_DEVICE_REGISTER;
|
||||
Request->TimeOut = 6;
|
||||
|
||||
Request->TaskFile.Feature = AcpiTaskFile->Feature;
|
||||
Request->TaskFile.SectorCount = AcpiTaskFile->SectorCount;
|
||||
Request->TaskFile.LowLba = AcpiTaskFile->LowLba;
|
||||
Request->TaskFile.MidLba = AcpiTaskFile->MidLba;
|
||||
Request->TaskFile.HighLba = AcpiTaskFile->HighLba;
|
||||
Request->TaskFile.DriveSelect = AcpiTaskFile->DriveSelect;
|
||||
Request->TaskFile.Command = AcpiTaskFile->Command;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceExecuteAcpiTaskFile(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PACPI_EVAL_OUTPUT_BUFFER GtfDataBuffer = DevExt->GtfDataBuffer;
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
PATA_ACPI_TASK_FILE AcpiTaskFile;
|
||||
NTSTATUS Status;
|
||||
ULONG i;
|
||||
|
||||
if (!GtfDataBuffer)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
ASSERT(GtfDataBuffer->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE);
|
||||
|
||||
AcpiTaskFile = (PATA_ACPI_TASK_FILE)GtfDataBuffer->Argument[0].Data;
|
||||
|
||||
for (i = 0; i < GtfDataBuffer->Argument[0].DataLength / sizeof(*AcpiTaskFile); ++i)
|
||||
{
|
||||
PCSTR Result;
|
||||
|
||||
if (!AtaDeviceFilterAcpiTaskFile(AcpiTaskFile))
|
||||
{
|
||||
Result = "filtered out";
|
||||
}
|
||||
else
|
||||
{
|
||||
AtaDeviceAcpiTaskFileToInternalTaskFile(AcpiTaskFile, Request);
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
Result = NT_SUCCESS(Status) ? "completed" : "aborted";
|
||||
}
|
||||
INFO("CH %lu: GTF[%lu]: %02x:%02x:%02x:%02x:%02x:%02x:%02x -- %s\n",
|
||||
PortData->PortNumber,
|
||||
i,
|
||||
AcpiTaskFile->Feature,
|
||||
AcpiTaskFile->SectorCount,
|
||||
AcpiTaskFile->LowLba,
|
||||
AcpiTaskFile->MidLba,
|
||||
AcpiTaskFile->HighLba,
|
||||
AcpiTaskFile->DriveSelect,
|
||||
AcpiTaskFile->Command,
|
||||
Result);
|
||||
|
||||
++AcpiTaskFile;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* See MSDN note:
|
||||
* https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/security-group-commands
|
||||
*/
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceLockSecurityModeFeatureCommands(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device) ||
|
||||
AtapInPEMode ||
|
||||
!AtaDevHasSecurityModeFeature(&DevExt->IdentifyDeviceData))
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 3;
|
||||
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = IDE_COMMAND_SECURITY_FREEZE_LOCK;
|
||||
|
||||
return AtaPortSendRequest(PortData, DevExt);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceLockDeviceParameters(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 3;
|
||||
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = IDE_COMMAND_SET_FEATURE;
|
||||
Request->TaskFile.Feature = IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON;
|
||||
|
||||
return AtaPortSendRequest(PortData, DevExt);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceEnableMsnFeature(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
NTSTATUS Status;
|
||||
|
||||
if (!AtaDevHasRemovableMediaStatusNotification(&DevExt->IdentifyDeviceData))
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 3;
|
||||
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = IDE_COMMAND_SET_FEATURE;
|
||||
Request->TaskFile.Feature = IDE_FEATURE_ENABLE_MSN;
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
DevExt->Device.DeviceFlags |= DEVICE_HAS_MEDIA_STATUS;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
AtaPortDeviceProcessConfig(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
|
||||
DevExt->Device.DeviceFlags |= DEVICE_PIO_ONLY;
|
||||
DevExt->Device.DeviceFlags &= ~(DEVICE_HAS_MEDIA_STATUS | DEVICE_SENSE_DATA_REPORTING);
|
||||
DevExt->Device.MultiSectorCount = 0;
|
||||
|
||||
Status = AtaDeviceSetGeometry(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
Status = AtaDeviceSetPioTransferMode(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
Status = AtaDeviceSetDmaTransferMode(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
Status = AtaDeviceSetMultipleMode(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
Status = AtaDeviceExecuteAcpiTaskFile(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
Status = AtaDeviceLockSecurityModeFeatureCommands(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
Status = AtaDeviceLockDeviceParameters(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
Status = AtaDeviceEnableMsnFeature(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
/* Identify data might have changed during the configuration, so update it here */
|
||||
Status = AtaDeviceSendIdentify(PortData,
|
||||
DevExt,
|
||||
IS_ATAPI(&DevExt->Device) ?
|
||||
IDE_COMMAND_ATAPI_IDENTIFY : IDE_COMMAND_IDENTIFY);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
RtlCopyMemory(&DevExt->IdentifyDeviceData,
|
||||
DevExt->Device.LocalBuffer,
|
||||
sizeof(DevExt->IdentifyDeviceData));
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
657
drivers/storage/ide/atapi/dev_error.c
Normal file
657
drivers/storage/ide/atapi/dev_error.c
Normal file
@@ -0,0 +1,657 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: Device I/O error handling
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
BOOLEAN
|
||||
AtaDeviceRequestSenseNeeded(
|
||||
_In_ PATA_DEVICE_REQUEST Request)
|
||||
{
|
||||
return ((Request->Flags & REQUEST_FLAG_PACKET_COMMAND) &&
|
||||
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
||||
(Request->Srb->SenseInfoBuffer != NULL) &&
|
||||
(Request->Srb->SenseInfoBufferLength != 0));
|
||||
}
|
||||
|
||||
static
|
||||
BOOLEAN
|
||||
AtaDeviceRequestSenseNeededExt(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PATA_DEVICE_REQUEST Request)
|
||||
{
|
||||
/* NOTE: The ERROR bit and the SENSE DATA AVAILABLE bit may both be set to one */
|
||||
return ((DevExt->Device.DeviceFlags & DEVICE_SENSE_DATA_REPORTING) &&
|
||||
(Request->Output.Status & IDE_STATUS_INDEX) &&
|
||||
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
||||
(Request->Srb->SenseInfoBuffer != NULL) &&
|
||||
(Request->Srb->SenseInfoBufferLength != 0));
|
||||
}
|
||||
|
||||
static
|
||||
BOOLEAN
|
||||
AtaDeviceFixedErrorNeeded(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PATA_DEVICE_REQUEST Request)
|
||||
{
|
||||
return (!(Request->Flags & REQUEST_FLAG_PACKET_COMMAND) &&
|
||||
!(Request->Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
|
||||
(Request->Srb->SenseInfoBuffer != NULL) &&
|
||||
(Request->Srb->SenseInfoBufferLength != 0));
|
||||
}
|
||||
|
||||
static
|
||||
BOOLEAN
|
||||
AtaDeviceIsDmaCrcError(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PATA_DEVICE_REQUEST Request)
|
||||
{
|
||||
// TODO: Check also for SATA SErr or AHCI interrupt status bits
|
||||
return (Request->Output.Error & IDE_ERROR_CRC_ERROR) &&
|
||||
(Request->Flags & REQUEST_FLAG_DMA);
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceHandleRequestSense(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
|
||||
PSCSI_REQUEST_BLOCK Srb = FailedRequest->Srb;
|
||||
PSENSE_DATA SenseData;
|
||||
BOOLEAN Success;
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
{
|
||||
Success = (Request->SrbStatus == SRB_STATUS_SUCCESS) ||
|
||||
(Request->SrbStatus == SRB_STATUS_DATA_OVERRUN);
|
||||
}
|
||||
else
|
||||
{
|
||||
Success = (Request->SrbStatus == SRB_STATUS_SUCCESS) &&
|
||||
(Request->Flags & REQUEST_FLAG_HAS_TASK_FILE) &&
|
||||
(Request->TaskFile.HighLba != 0);
|
||||
}
|
||||
|
||||
if (!Success)
|
||||
{
|
||||
ERR("Request sense failed\n");
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
{
|
||||
Srb->ScsiStatus = SCSISTAT_GOOD;
|
||||
FailedRequest->SrbStatus = SRB_STATUS_REQUEST_SENSE_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Request sense failed, translate the original ATA device error */
|
||||
FailedRequest->SrbStatus = AtaReqSetFixedAtaSenseData(FailedRequest);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SenseData = Srb->SenseInfoBuffer;
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
{
|
||||
/* Copy the sense data from the local buffer */
|
||||
RtlCopyMemory(Srb->SenseInfoBuffer,
|
||||
DevExt->Device.LocalBuffer,
|
||||
min(Srb->SenseInfoBufferLength, ATA_LOCAL_BUFFER_SIZE));
|
||||
|
||||
Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
|
||||
FailedRequest->SrbStatus = SRB_STATUS_ERROR | SRB_STATUS_AUTOSENSE_VALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
SCSI_SENSE_CODE SenseCode;
|
||||
|
||||
/* Copy the sense code from the task file registers */
|
||||
SenseCode.SrbStatus = SRB_STATUS_ERROR;
|
||||
SenseCode.SenseKey = Request->TaskFile.HighLba;
|
||||
SenseCode.AdditionalSenseCode = Request->TaskFile.MidLba;
|
||||
SenseCode.AdditionalSenseCodeQualifier = Request->TaskFile.LowLba;
|
||||
FailedRequest->SrbStatus = AtaReqSetFixedSenseData(Srb, SenseCode);
|
||||
}
|
||||
|
||||
if (RTL_CONTAINS_FIELD(SenseData,
|
||||
Srb->SenseInfoBufferLength,
|
||||
AdditionalSenseCodeQualifier))
|
||||
{
|
||||
/* INFO("0x%02X: SK 0x%02X, ASC 0x%02X, ASCQ 0x%02X\n", */
|
||||
/* Srb->Cdb[0], */
|
||||
/* SenseData->SenseKey, */
|
||||
/* SenseData->AdditionalSenseCode, */
|
||||
/* SenseData->AdditionalSenseCodeQualifier); */
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSendRequestSense(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
|
||||
NTSTATUS Status;
|
||||
PCDB Cdb;
|
||||
|
||||
ASSERT_REQUEST(FailedRequest);
|
||||
ASSERT(FailedRequest->Srb->SenseInfoBufferLength > 0);
|
||||
|
||||
Request->Flags = REQUEST_FLAG_DATA_IN |
|
||||
REQUEST_FLAG_PACKET_COMMAND |
|
||||
REQUEST_FLAG_HAS_LOCAL_BUFFER;
|
||||
Request->TimeOut = 3;
|
||||
Request->DataTransferLength = FailedRequest->Srb->SenseInfoBufferLength;
|
||||
|
||||
Cdb = (PCDB)Request->Cdb;
|
||||
Cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
|
||||
Cdb->CDB6INQUIRY.LogicalUnitNumber = 0;
|
||||
Cdb->CDB6INQUIRY.Reserved1 = 0;
|
||||
Cdb->CDB6INQUIRY.PageCode = 0;
|
||||
Cdb->CDB6INQUIRY.IReserved = 0;
|
||||
Cdb->CDB6INQUIRY.AllocationLength = (UCHAR)Request->DataTransferLength;
|
||||
Cdb->CDB6INQUIRY.Control = 0;
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
|
||||
AtaDeviceHandleRequestSense(PortData, DevExt);
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSendRequestSenseExt(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
NTSTATUS Status;
|
||||
|
||||
Request->Flags = REQUEST_FLAG_SAVE_TASK_FILE | REQUEST_FLAG_LBA48;
|
||||
Request->TimeOut = 3;
|
||||
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = IDE_COMMAND_REQUEST_SENSE_DATA_EXT;
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
|
||||
AtaDeviceHandleRequestSense(PortData, DevExt);
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSendReadNcqCommandErrorLog(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
|
||||
AtaReqBuildReadLogTaskFile(Request,
|
||||
IDE_GP_LOG_NCQ_COMMAND_ERROR_ADDRESS,
|
||||
0,
|
||||
1);
|
||||
Request->Flags |= REQUEST_FLAG_HAS_LOCAL_BUFFER;
|
||||
Request->TimeOut = 3;
|
||||
|
||||
return AtaPortSendRequest(PortData, DevExt);
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceSaveLogPageTaskFile(
|
||||
_In_ GP_LOG_NCQ_COMMAND_ERROR* __restrict LogPage,
|
||||
_Inout_ ATA_DEVICE_REQUEST* __restrict Request)
|
||||
{
|
||||
PATA_TASKFILE TaskFile = &Request->Output;
|
||||
|
||||
TaskFile->SectorCount = LogPage->Count7_0;
|
||||
TaskFile->LowLba = LogPage->LBA7_0;
|
||||
TaskFile->MidLba = LogPage->LBA15_8;
|
||||
TaskFile->HighLba = LogPage->LBA23_16;
|
||||
TaskFile->DriveSelect = LogPage->Device;
|
||||
|
||||
if (Request->Flags & REQUEST_FLAG_LBA48)
|
||||
{
|
||||
TaskFile->FeatureEx = 0; // Reserved
|
||||
TaskFile->SectorCountEx = LogPage->Count15_8;
|
||||
TaskFile->LowLbaEx = LogPage->LBA31_24;
|
||||
TaskFile->MidLbaEx = LogPage->LBA39_32;
|
||||
TaskFile->HighLbaEx = LogPage->LBA47_40;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceLogEvent(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ ATA_ERROR_LOG_VALUE ErrorValue)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
|
||||
ULONG ErrorCode, FinalStatus;
|
||||
PIO_ERROR_LOG_PACKET LogEntry;
|
||||
UCHAR Size;
|
||||
|
||||
Size = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData);
|
||||
LogEntry = IoAllocateErrorLogEntry(ChanExt->Common.Self, Size);
|
||||
if (!LogEntry)
|
||||
return;
|
||||
|
||||
switch (ErrorValue)
|
||||
{
|
||||
case EVENT_CODE_TIMEOUT:
|
||||
ErrorCode = IO_ERR_TIMEOUT;
|
||||
FinalStatus = STATUS_IO_TIMEOUT;
|
||||
break;
|
||||
case EVENT_CODE_CRC_ERROR:
|
||||
ErrorCode = IO_ERR_PARITY;
|
||||
FinalStatus = STATUS_IO_DEVICE_ERROR;
|
||||
break;
|
||||
|
||||
default:
|
||||
ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
||||
FinalStatus = STATUS_IO_DEVICE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
RtlZeroMemory(LogEntry, Size);
|
||||
LogEntry->FinalStatus = FinalStatus;
|
||||
LogEntry->ErrorCode = ErrorCode;
|
||||
LogEntry->MajorFunctionCode = IRP_MJ_SCSI;
|
||||
LogEntry->UniqueErrorValue = ErrorValue;
|
||||
|
||||
IoWriteErrorLogEntry(LogEntry);
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceDowngradeTransferSpeed(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
ULONG Mode, AllowedModesMask;
|
||||
|
||||
/* Already in PIO mode */
|
||||
if (!_BitScanReverse(&Mode, DevExt->TransferModeSelectedBitmap & ~PIO_ALL))
|
||||
return;
|
||||
|
||||
/* Clear the current mode and the upper bits */
|
||||
AllowedModesMask = ((1 << Mode) - 1);
|
||||
|
||||
if ((DevExt->TransferModeSupportedBitmap & UDMA_ALL) && !(AllowedModesMask & UDMA_ALL))
|
||||
{
|
||||
/*
|
||||
* It makes no sense to disable DMA for AHCI hard drives
|
||||
* because that would otherwise cause READ/WRITE commands to fail.
|
||||
*/
|
||||
if ((PortData->PortFlags & PORT_FLAG_PIO_VIA_DMA) && !IS_ATAPI(&DevExt->Device))
|
||||
return;
|
||||
|
||||
/* From UDMA to PIO (intentionally skip MWDMA and SWDMA) */
|
||||
AllowedModesMask &= PIO_ALL;
|
||||
}
|
||||
else if (((DevExt->TransferModeSupportedBitmap & MWDMA_ALL) &&
|
||||
!(AllowedModesMask & MWDMA_ALL)) ||
|
||||
((DevExt->TransferModeSupportedBitmap & SWDMA_ALL) &&
|
||||
!(AllowedModesMask & SWDMA_ALL)))
|
||||
{
|
||||
/* From MWDMA to PIO (intentionally skip SWDMA) or from SWDMA to PIO */
|
||||
AllowedModesMask &= PIO_ALL;
|
||||
}
|
||||
|
||||
/* We are about to disable DMA, log the change */
|
||||
if ((DevExt->TransferModeSupportedBitmap & ~PIO_ALL) && !(AllowedModesMask & ~PIO_ALL))
|
||||
{
|
||||
WARN("Too many DMA failures, disabling DMA for '%s'\n", DevExt->FriendlyName);
|
||||
|
||||
AtaDeviceLogEvent(DevExt, EVENT_CODE_DMA_DISABLE);
|
||||
}
|
||||
|
||||
WARN("Downgrading DMA speed from %lu for '%s'\n", Mode, DevExt->FriendlyName);
|
||||
|
||||
DevExt->TransferModeAllowedMask &= AllowedModesMask;
|
||||
|
||||
/* Program the new timings */
|
||||
_InterlockedOr(&PortData->Worker.EventsPending, ACTION_PORT_TIMING | ACTION_DEVICE_CONFIG);
|
||||
_InterlockedOr(&DevExt->Worker.EventsPending, ACTION_DEVICE_CONFIG);
|
||||
|
||||
/* Request a QBR to ensure that storprop.dll updates its data from the registry */
|
||||
PortData->Worker.Flags |= WORKER_FLAG_NEED_RESCAN;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceAnalyzeDmaError(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PATA_DEVICE_REQUEST Request,
|
||||
_In_ ATA_ERROR_LOG_VALUE ErrorValue)
|
||||
{
|
||||
LARGE_INTEGER CurrentTime, TimeDifferenceMs;
|
||||
|
||||
WARN("DMA error %lu on '%s'\n", ErrorValue, DevExt->FriendlyName);
|
||||
|
||||
/* We silently ignore device errors caused by PASSTHROUGH commands came from user mode */
|
||||
if (Request->Flags & REQUEST_FLAG_PASSTHROUGH)
|
||||
return;
|
||||
|
||||
KeQuerySystemTime(&CurrentTime);
|
||||
TimeDifferenceMs.QuadPart = (CurrentTime.QuadPart - DevExt->LastDmaErrorTime.QuadPart) / 10000;
|
||||
DevExt->LastDmaErrorTime.QuadPart = CurrentTime.QuadPart;
|
||||
|
||||
AtaDeviceLogEvent(DevExt, ErrorValue);
|
||||
|
||||
/* Ignore all occasional DMA errors we encounter */
|
||||
if (TimeDifferenceMs.QuadPart >= (10LL * 60000LL)) // 10 min
|
||||
return;
|
||||
|
||||
/* DMA timeouts and DMA CRC errors usually indicate a bad connection (a bad cable) */
|
||||
|
||||
/* Try to disable NCQ */
|
||||
if (DevExt->Device.DeviceFlags & DEVICE_NCQ)
|
||||
{
|
||||
DevExt->Device.DeviceFlags &= ~DEVICE_NCQ;
|
||||
|
||||
ERR("NCQ disabled for '%s'\n", DevExt->FriendlyName);
|
||||
AtaDeviceLogEvent(DevExt, EVENT_CODE_NCQ_DISABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to reduce the interface speed */
|
||||
if (PortData->DowngradeInterfaceSpeed(PortData->ChannelContext))
|
||||
{
|
||||
WARN("Downgrading interface for '%s'\n", DevExt->FriendlyName);
|
||||
AtaDeviceLogEvent(DevExt, EVENT_CODE_DOWNSHIFT);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to reduce the transfer speed */
|
||||
AtaDeviceDowngradeTransferSpeed(PortData, DevExt);
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceCompleteFailedRequest(
|
||||
_In_ PATAPORT_PORT_DATA PortData)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = PortData->Worker.FailedRequest;
|
||||
UCHAR SrbStatus;
|
||||
SIZE_T RetryCount;
|
||||
KIRQL OldIrql;
|
||||
|
||||
ASSERT_REQUEST(Request);
|
||||
ASSERT(Request != &PortData->Worker.InternalRequest);
|
||||
|
||||
SrbStatus = SRB_STATUS(Request->SrbStatus);
|
||||
|
||||
RetryCount = SRB_GET_FLAGS(Request->Srb) & SRB_FLAG_RETRY_COUNT_MASK;
|
||||
RetryCount++;
|
||||
SRB_CLEAR_FLAGS(Request->Srb, SRB_FLAG_RETRY_COUNT_MASK);
|
||||
SRB_SET_FLAGS(Request->Srb, RetryCount);
|
||||
|
||||
if (RetryCount > 3)
|
||||
{
|
||||
ASSERT(SrbStatus != SRB_STATUS_BUSY);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We usually do not want to retry a request, because the upper class driver
|
||||
* is intended to effectively analyze errors and sense data.
|
||||
*/
|
||||
switch (SrbStatus)
|
||||
{
|
||||
case SRB_STATUS_BUS_RESET:
|
||||
case SRB_STATUS_TIMEOUT:
|
||||
{
|
||||
/* DMA timeout, attempt to retry in PIO mode */
|
||||
if (!(PortData->PortFlags & PORT_FLAG_PIO_VIA_DMA))
|
||||
{
|
||||
SRB_SET_FLAGS(Request->Srb, SRB_FLAG_PIO_RETRY);
|
||||
}
|
||||
|
||||
SrbStatus = SRB_STATUS_BUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
case SRB_STATUS_REQUEST_SENSE_FAILED:
|
||||
{
|
||||
SrbStatus = SRB_STATUS_BUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
/* Retry paging I/O operations only */
|
||||
if (Request->Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_PAGING_IO))
|
||||
SrbStatus = SRB_STATUS_BUSY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SrbStatus |= Request->SrbStatus & (SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN);
|
||||
Request->SrbStatus = SrbStatus;
|
||||
|
||||
if (SRB_STATUS(SrbStatus) == SRB_STATUS_BUSY)
|
||||
return;
|
||||
|
||||
ASSERT(SRB_STATUS(SrbStatus) != SRB_STATUS_SUCCESS);
|
||||
|
||||
/* Fail the command with an error */
|
||||
Request->InternalState = REQUEST_STATE_FREEZE_QUEUE;
|
||||
|
||||
OldIrql = KeAcquireInterruptSpinLock(PortData->InterruptObject);
|
||||
|
||||
ASSERT(PortData->Worker.PausedSlotsBitmap & (1 << Request->Slot));
|
||||
PortData->Worker.PausedSlotsBitmap &= ~(1 << Request->Slot);
|
||||
|
||||
KeReleaseInterruptSpinLock(PortData->InterruptObject, OldIrql);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceNcqRecovery(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST FailedRequest;
|
||||
PGP_LOG_NCQ_COMMAND_ERROR LogPage;
|
||||
NTSTATUS Status;
|
||||
ULONG i;
|
||||
UCHAR Crc;
|
||||
|
||||
Status = AtaDeviceSendReadNcqCommandErrorLog(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
/* We failed at retrieving the log page */
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("READ LOG EXT failure\n");
|
||||
goto Failure;
|
||||
}
|
||||
|
||||
LogPage = DevExt->Device.LocalBuffer;
|
||||
|
||||
/* It's not ours */
|
||||
if (LogPage->NonQueuedCmd)
|
||||
{
|
||||
ERR("Unexpected NQ bit in the log page 0x10 structure\n");
|
||||
goto Failure;
|
||||
}
|
||||
|
||||
/* Verify the checksum */
|
||||
Crc = 0;
|
||||
for (i = 0; i < IDE_GP_LOG_SECTOR_SIZE; ++i)
|
||||
{
|
||||
Crc += ((PUCHAR)LogPage)[i];
|
||||
}
|
||||
if (Crc != 0)
|
||||
{
|
||||
ERR("CRC error in the log page 0x10 structure\n");
|
||||
goto Failure;
|
||||
}
|
||||
|
||||
/* Find the failed queued command */
|
||||
if (!(PortData->Worker.PausedSlotsBitmap & (1 << LogPage->NcqTag)))
|
||||
{
|
||||
ERR("Failed command %08lx not found in %08lx\n",
|
||||
1 << LogPage->NcqTag, PortData->Worker.PausedSlotsBitmap);
|
||||
goto Failure;
|
||||
}
|
||||
|
||||
FailedRequest = PortData->Slots[LogPage->NcqTag];
|
||||
ASSERT_REQUEST(FailedRequest);
|
||||
PortData->Worker.FailedRequest = FailedRequest;
|
||||
|
||||
/* Fail the command with an error */
|
||||
FailedRequest->SrbStatus = SRB_STATUS_ERROR;
|
||||
FailedRequest->Output.Status = LogPage->Status;
|
||||
FailedRequest->Output.Error = LogPage->Error;
|
||||
if (FailedRequest->Flags & REQUEST_FLAG_SAVE_TASK_FILE)
|
||||
{
|
||||
AtaDeviceSaveLogPageTaskFile(LogPage, FailedRequest);
|
||||
}
|
||||
|
||||
if ((LogPage->SenseKey != 0) && AtaDevHasNcqAutosense(&DevExt->IdentifyDeviceData))
|
||||
{
|
||||
SCSI_SENSE_CODE SenseCode;
|
||||
|
||||
/* Set the sense data returned by the device */
|
||||
SenseCode.SrbStatus = FailedRequest->SrbStatus;
|
||||
SenseCode.SenseKey = LogPage->SenseKey;
|
||||
SenseCode.AdditionalSenseCode = LogPage->ASC;
|
||||
SenseCode.AdditionalSenseCodeQualifier = LogPage->ASCQ;
|
||||
FailedRequest->SrbStatus = AtaReqSetFixedSenseData(FailedRequest->Srb, SenseCode);
|
||||
|
||||
/* Set the "Final LBA in Error" field */
|
||||
AtaReqSetLbaInformation(FailedRequest->Srb,
|
||||
((ULONG64)(((PUCHAR)LogPage)[17]) << 0) |
|
||||
((ULONG64)(((PUCHAR)LogPage)[18]) << 8) |
|
||||
((ULONG64)(((PUCHAR)LogPage)[19]) << 16) |
|
||||
((ULONG64)(((PUCHAR)LogPage)[20]) << 24) |
|
||||
((ULONG64)(((PUCHAR)LogPage)[21]) << 32) |
|
||||
((ULONG64)(((PUCHAR)LogPage)[22]) << 48));
|
||||
}
|
||||
else if (AtaDeviceRequestSenseNeededExt(DevExt, FailedRequest))
|
||||
{
|
||||
/* Get sense data */
|
||||
Status = AtaDeviceSendRequestSenseExt(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
}
|
||||
else
|
||||
{
|
||||
FailedRequest->SrbStatus = AtaReqSetFixedAtaSenseData(FailedRequest);
|
||||
}
|
||||
|
||||
AtaDeviceCompleteFailedRequest(PortData);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Failure:
|
||||
/* A port reset is required to abort all outstanding queued commands */
|
||||
_InterlockedOr(&PortData->Worker.EventsPending, ACTION_PORT_RESET);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceGenericRecovery(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = PortData->Worker.FailedRequest;
|
||||
NTSTATUS Status;
|
||||
|
||||
switch (Request->SrbStatus)
|
||||
{
|
||||
/* Unexpected channel/device state */
|
||||
case SRB_STATUS_BUS_RESET:
|
||||
case SRB_STATUS_TIMEOUT:
|
||||
{
|
||||
if (Request->Flags & REQUEST_FLAG_DMA)
|
||||
{
|
||||
ATA_ERROR_LOG_VALUE ErrorValue;
|
||||
|
||||
if (Request->SrbStatus == SRB_STATUS_TIMEOUT)
|
||||
ErrorValue = EVENT_CODE_TIMEOUT;
|
||||
else
|
||||
ErrorValue = EVENT_CODE_BAD_STATE;
|
||||
AtaDeviceAnalyzeDmaError(PortData, DevExt, Request, ErrorValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* General errors */
|
||||
case SRB_STATUS_ERROR:
|
||||
{
|
||||
if (AtaDeviceIsDmaCrcError(DevExt, Request))
|
||||
{
|
||||
AtaDeviceAnalyzeDmaError(PortData, DevExt, Request, EVENT_CODE_CRC_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Send the recovery command to figure out why the current command failed */
|
||||
if (AtaDeviceRequestSenseNeeded(Request))
|
||||
{
|
||||
/* Handle failed ATAPI commands */
|
||||
Status = AtaDeviceSendRequestSense(PortData, DevExt);
|
||||
break;
|
||||
}
|
||||
else if (AtaDeviceRequestSenseNeededExt(DevExt, Request))
|
||||
{
|
||||
/* Handle failed non-queued commands */
|
||||
Status = AtaDeviceSendRequestSenseExt(PortData, DevExt);
|
||||
break;
|
||||
}
|
||||
else if (AtaDeviceFixedErrorNeeded(DevExt, Request))
|
||||
{
|
||||
Request->SrbStatus = AtaReqSetFixedAtaSenseData(Request);
|
||||
}
|
||||
|
||||
/* Recovery command is not required, just complete the request with an error */
|
||||
__fallthrough;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
AtaDeviceCompleteFailedRequest(PortData);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
AtaPortDeviceProcessError(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST FailedRequest = PortData->Worker.FailedRequest;
|
||||
NTSTATUS Status;
|
||||
|
||||
ASSERT_REQUEST(FailedRequest);
|
||||
|
||||
if (FailedRequest->Flags & REQUEST_FLAG_NCQ)
|
||||
Status = AtaDeviceNcqRecovery(PortData, DevExt);
|
||||
else
|
||||
Status = AtaDeviceGenericRecovery(PortData, DevExt);
|
||||
|
||||
return Status;
|
||||
}
|
||||
367
drivers/storage/ide/atapi/dev_identify.c
Normal file
367
drivers/storage/ide/atapi/dev_identify.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: Device identification
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
BOOLEAN
|
||||
AtaDeviceIdentifyDataEqual(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData1,
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData2)
|
||||
{
|
||||
if (!RtlEqualMemory(IdentifyData1->SerialNumber,
|
||||
IdentifyData2->SerialNumber,
|
||||
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, SerialNumber)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!RtlEqualMemory(IdentifyData1->FirmwareRevision,
|
||||
IdentifyData2->FirmwareRevision,
|
||||
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, FirmwareRevision)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!RtlEqualMemory(IdentifyData1->ModelNumber,
|
||||
IdentifyData2->ModelNumber,
|
||||
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, ModelNumber)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
AtaDeviceSendIdentify(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ UCHAR Command)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
|
||||
TRACE("CH %lu: Send %s identify device %u\n",
|
||||
PortData->PortNumber,
|
||||
Command == IDE_COMMAND_IDENTIFY ? "ATA" : "ATAPI",
|
||||
DevExt->Device.AtaScsiAddress.TargetId);
|
||||
|
||||
/*
|
||||
* For PATA devices, disable interrupts for the identify command and use polling instead.
|
||||
*
|
||||
* On some single device 1 configurations or non-existent IDE channels
|
||||
* the status register stuck permanently at a value of 0,
|
||||
* and we incorrectly assume that the device is present.
|
||||
* This will result in a taskfile timeout
|
||||
* which must be avoided as it would cause hangs at boot time.
|
||||
*/
|
||||
Request->Flags = REQUEST_FLAG_POLL;
|
||||
|
||||
Request->Flags |= REQUEST_FLAG_DATA_IN | REQUEST_FLAG_HAS_LOCAL_BUFFER;
|
||||
Request->TimeOut = 10;
|
||||
Request->DataTransferLength = sizeof(DevExt->IdentifyDeviceData);
|
||||
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = Command;
|
||||
|
||||
return AtaPortSendRequest(PortData, DevExt);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSpinUp(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
|
||||
TRACE("CH %lu: Spin-up device %u\n",
|
||||
PortData->PortNumber,
|
||||
DevExt->Device.AtaScsiAddress.TargetId);
|
||||
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 20;
|
||||
|
||||
Request->TaskFile.Command = IDE_COMMAND_SET_FEATURE;
|
||||
Request->TaskFile.Feature = IDE_FEATURE_PUIS_SPIN_UP;
|
||||
|
||||
return AtaPortSendRequest(PortData, DevExt);
|
||||
}
|
||||
|
||||
static
|
||||
BOOLEAN
|
||||
AtaDeviceIsXboxDrive(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
static const PCSTR AtapBrokenInquiryDrive[] =
|
||||
{
|
||||
"THOMSON-DVD",
|
||||
"PHILIPS XBOX DVD DRIVE",
|
||||
"PHILIPS J5 3235C",
|
||||
"SAMSUNG DVD-ROM SDG-605B"
|
||||
// TODO: Xbox 360 drives are also affected.
|
||||
};
|
||||
CHAR ModelNumber[26];
|
||||
PUCHAR End;
|
||||
ULONG i;
|
||||
|
||||
End = AtaCopyIdStringUnsafe((PUCHAR)ModelNumber,
|
||||
DevExt->IdentifyDeviceData.ModelNumber,
|
||||
sizeof(ModelNumber));
|
||||
*End = ANSI_NULL;
|
||||
|
||||
for (i = 0; i < RTL_NUMBER_OF(AtapBrokenInquiryDrive); ++i)
|
||||
{
|
||||
if (strcmp(ModelNumber, AtapBrokenInquiryDrive[i]) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaCreateAtapiStandardInquiryData(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
|
||||
_Out_ PINQUIRYDATA InquiryData)
|
||||
{
|
||||
InquiryData->DeviceType = READ_ONLY_DIRECT_ACCESS_DEVICE;
|
||||
InquiryData->RemovableMedia = AtaDevIsRemovable(IdentifyData);
|
||||
InquiryData->Versions = 0x04; // SPC-2
|
||||
InquiryData->ResponseDataFormat = 2; // This means "Complies to this standard"
|
||||
InquiryData->AdditionalLength =
|
||||
INQUIRYDATABUFFERSIZE - RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
|
||||
|
||||
/* Copy ModelNumber from a byte-swapped ATA string */
|
||||
AtaCopyIdStringSafe((PCHAR)InquiryData->VendorId,
|
||||
IdentifyData->ModelNumber,
|
||||
sizeof(IdentifyData->ModelNumber),
|
||||
' ');
|
||||
AtaSwapIdString(InquiryData->VendorId, sizeof(IdentifyData->ModelNumber) / 2);
|
||||
|
||||
/* Copy FirmwareRevision from a byte-swapped ATA string */
|
||||
AtaCopyIdStringSafe((PCHAR)InquiryData->ProductRevisionLevel,
|
||||
IdentifyData->FirmwareRevision,
|
||||
sizeof(InquiryData->ProductRevisionLevel),
|
||||
' ');
|
||||
AtaSwapIdString(InquiryData->ProductRevisionLevel,
|
||||
sizeof(InquiryData->ProductRevisionLevel) / 2);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSendInquiry(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
NTSTATUS Status;
|
||||
PCDB Cdb;
|
||||
|
||||
TRACE("CH %lx: Send inquiry device %u\n",
|
||||
PortData->PortNumber,
|
||||
DevExt->Device.AtaScsiAddress.TargetId);
|
||||
|
||||
Request->Flags = REQUEST_FLAG_DATA_IN |
|
||||
REQUEST_FLAG_PACKET_COMMAND |
|
||||
REQUEST_FLAG_HAS_LOCAL_BUFFER;
|
||||
Request->TimeOut = 3;
|
||||
Request->DataTransferLength = INQUIRYDATABUFFERSIZE;
|
||||
|
||||
RtlZeroMemory(Request->Cdb, sizeof(Request->Cdb));
|
||||
Cdb = (PCDB)&Request->Cdb;
|
||||
Cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
|
||||
Cdb->CDB6INQUIRY.LogicalUnitNumber = DevExt->Device.AtaScsiAddress.Lun;
|
||||
Cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
|
||||
|
||||
Status = AtaPortSendRequest(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
if (DevExt->Device.AtaScsiAddress.Lun == 0 && AtaDeviceIsXboxDrive(DevExt))
|
||||
{
|
||||
DevExt->Device.DeviceFlags |= DEVICE_CACHE_INQUIRY;
|
||||
|
||||
/*
|
||||
* The INQUIRY command is mandatory to be implemented by ATAPI devices,
|
||||
* but some Xbox drives violate this.
|
||||
*/
|
||||
AtaCreateAtapiStandardInquiryData(&DevExt->IdentifyDeviceData,
|
||||
DevExt->Device.LocalBuffer);
|
||||
Status = DEV_STATUS_NEW_DEVICE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = DEV_STATUS_NO_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ATA_DEVICE_STATUS
|
||||
AtaPortIdentifyDevice(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PIDENTIFY_DEVICE_DATA IdentifyData = DevExt->Device.LocalBuffer;
|
||||
ATA_CONNECTION_STATUS ConnectionStatus;
|
||||
NTSTATUS Status;
|
||||
BOOLEAN StartFromAtapi, IsSameDevice;
|
||||
ULONG Attempt, RetryCount;
|
||||
ATA_DEVICE_TYPE DeviceType;
|
||||
|
||||
_InterlockedOr(&PortData->InterruptFlags, PORT_INT_FLAG_IGNORE_LINK_IRQ);
|
||||
|
||||
ConnectionStatus = PortData->IdentifyDevice(PortData->ChannelContext,
|
||||
DevExt->Device.AtaScsiAddress.TargetId);
|
||||
|
||||
_InterlockedAnd(&PortData->InterruptFlags, ~PORT_INT_FLAG_IGNORE_LINK_IRQ);
|
||||
|
||||
if (ConnectionStatus == CONN_STATUS_FAILURE)
|
||||
return DEV_STATUS_FAILED;
|
||||
|
||||
if (ConnectionStatus == CONN_STATUS_NO_DEVICE)
|
||||
return DEV_STATUS_NO_DEVICE;
|
||||
|
||||
/* Power up the device if needed */
|
||||
Status = AtaPortCheckDevicePowerState(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return DEV_STATUS_FAILED;
|
||||
|
||||
/*
|
||||
* Try the known device type first.
|
||||
* This may speed up device detection by eliminating I/O errors.
|
||||
*/
|
||||
if (DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED)
|
||||
StartFromAtapi = (ConnectionStatus == CONN_STATUS_DEV_ATAPI);
|
||||
else
|
||||
StartFromAtapi = IS_ATAPI(&DevExt->Device);
|
||||
|
||||
/* Look for ATA/ATAPI devices */
|
||||
for (RetryCount = 0; RetryCount < 2; ++RetryCount)
|
||||
{
|
||||
/* Send the identify command */
|
||||
for (Attempt = 0; Attempt < 2; ++Attempt)
|
||||
{
|
||||
if (StartFromAtapi)
|
||||
DeviceType = DEV_ATAPI;
|
||||
else
|
||||
DeviceType = DEV_ATA;
|
||||
|
||||
/* Swap device types */
|
||||
if (Attempt != 0)
|
||||
DeviceType ^= (DEV_ATAPI ^ DEV_ATA);
|
||||
|
||||
Status = AtaDeviceSendIdentify(PortData,
|
||||
DevExt,
|
||||
DeviceType == DEV_ATAPI ?
|
||||
IDE_COMMAND_ATAPI_IDENTIFY : IDE_COMMAND_IDENTIFY);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return DEV_STATUS_FAILED;
|
||||
|
||||
if (NT_SUCCESS(Status))
|
||||
break;
|
||||
}
|
||||
if (!NT_SUCCESS(Status))
|
||||
return DEV_STATUS_NO_DEVICE;
|
||||
|
||||
/*
|
||||
* The drive needs to be spun up
|
||||
* or the device was unable to return complete identity data.
|
||||
*/
|
||||
if (AtaDevInPuisState(IdentifyData) || AtaDevIsIdentifyDataIncomplete(IdentifyData))
|
||||
{
|
||||
|
||||
Status = AtaDeviceSpinUp(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return DEV_STATUS_FAILED;
|
||||
|
||||
if (!NT_SUCCESS(Status))
|
||||
ERR("Failed to spin-up device\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify the checksum */
|
||||
if (!AtaDevIsIdentifyDataValid(IdentifyData))
|
||||
{
|
||||
ERR("Identify data CRC error\n");
|
||||
return DEV_STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED)
|
||||
{
|
||||
IsSameDevice = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsSameDevice = (DeviceType == DevExt->DeviceType) &&
|
||||
(AtaDeviceIdentifyDataEqual(&DevExt->IdentifyDeviceData,
|
||||
DevExt->Device.LocalBuffer));
|
||||
}
|
||||
|
||||
/* Update identify data */
|
||||
RtlCopyMemory(&DevExt->IdentifyDeviceData,
|
||||
DevExt->Device.LocalBuffer,
|
||||
sizeof(DevExt->IdentifyDeviceData));
|
||||
DevExt->DeviceType = DeviceType;
|
||||
|
||||
if (IsSameDevice)
|
||||
return DEV_STATUS_SAME_DEVICE;
|
||||
|
||||
DevExt->Device.TransportFlags &= ~(DEVICE_IS_ATAPI |
|
||||
DEVICE_HAS_CDB_INTERRUPT |
|
||||
DEVICE_NEED_DMA_DIRECTION |
|
||||
DEVICE_IS_NEC_CDR260);
|
||||
|
||||
if (DeviceType == DEV_ATAPI)
|
||||
{
|
||||
DevExt->Device.TransportFlags |= DEVICE_IS_ATAPI;
|
||||
|
||||
DevExt->Device.CdbSize = AtaDevCdbSizeInWords(&DevExt->IdentifyPacketData);
|
||||
TRACE("Device has CDB size of %u bytes\n", DevExt->Device.CdbSize * 2);
|
||||
|
||||
if (AtaDevHasCdbInterrupt(&DevExt->IdentifyPacketData))
|
||||
{
|
||||
TRACE("Device has CDB interrupt\n");
|
||||
DevExt->Device.TransportFlags |= DEVICE_HAS_CDB_INTERRUPT;
|
||||
}
|
||||
|
||||
if (AtaDevIsDmaDirectionRequired(&DevExt->IdentifyPacketData))
|
||||
{
|
||||
TRACE("Device needs DMA DIR\n");
|
||||
DevExt->Device.TransportFlags |= DEVICE_NEED_DMA_DIRECTION;
|
||||
}
|
||||
|
||||
/* The NEC CDR-260 string is not byteswapped */
|
||||
if (DevExt->IdentifyPacketData.ModelNumber[0] == 'N' &&
|
||||
DevExt->IdentifyPacketData.ModelNumber[1] == 'E' &&
|
||||
DevExt->IdentifyPacketData.ModelNumber[2] == 'C')
|
||||
{
|
||||
TRACE("Device is a NEC CDR-260 drive\n");
|
||||
DevExt->Device.TransportFlags |= DEVICE_IS_NEC_CDR260;
|
||||
}
|
||||
|
||||
Status = AtaDeviceSendInquiry(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return DEV_STATUS_FAILED;
|
||||
|
||||
/* Update the inquiry data */
|
||||
RtlCopyMemory(&DevExt->InquiryData,
|
||||
DevExt->Device.LocalBuffer,
|
||||
sizeof(DevExt->InquiryData));
|
||||
}
|
||||
|
||||
return DEV_STATUS_NEW_DEVICE;
|
||||
}
|
||||
531
drivers/storage/ide/atapi/dev_power.c
Normal file
531
drivers/storage/ide/atapi/dev_power.c
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: Power management
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
static IO_COMPLETION_ROUTINE AtaPdoPowerUpCompletionRoutine;
|
||||
static REQUEST_POWER_COMPLETE AtaPdoPowerSetSysStateWithDevStateComplete;
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaPowerCompleteIrp(
|
||||
_In_ PATAPORT_COMMON_EXTENSION CommonExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ NTSTATUS Status)
|
||||
{
|
||||
PoStartNextPowerIrp(Irp);
|
||||
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
IoReleaseRemoveLock(&CommonExt->RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
VOID
|
||||
AtaDeviceFlushPowerIrpQueue(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
PIRP PowerIrp;
|
||||
KIRQL OldLevel;
|
||||
|
||||
KeAcquireSpinLock(&DevExt->Device.QueueLock, &OldLevel);
|
||||
if (IsListEmpty(&DevExt->PowerIrpQueueList))
|
||||
Entry = NULL;
|
||||
else
|
||||
Entry = RemoveHeadList(&DevExt->PowerIrpQueueList);
|
||||
KeReleaseSpinLock(&DevExt->Device.QueueLock, OldLevel);
|
||||
|
||||
if (!Entry)
|
||||
break;
|
||||
|
||||
PowerIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||||
ASSERT(IoGetCurrentIrpStackLocation(PowerIrp)->MinorFunction == IRP_MN_SET_POWER);
|
||||
AtaPowerCompleteIrp(&DevExt->Common, PowerIrp, STATUS_NO_SUCH_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
UCHAR
|
||||
AtaDeviceGetFlushCacheCommand(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
UCHAR Command = 0;
|
||||
|
||||
if (!IS_ATAPI(&DevExt->Device))
|
||||
{
|
||||
if ((DevExt->Device.DeviceFlags & DEVICE_LBA48) &&
|
||||
AtaDevHasFlushCacheExt(&DevExt->IdentifyDeviceData))
|
||||
{
|
||||
Command = IDE_COMMAND_FLUSH_CACHE_EXT;
|
||||
}
|
||||
else if (AtaDevHasFlushCache(&DevExt->IdentifyDeviceData))
|
||||
{
|
||||
Command = IDE_COMMAND_FLUSH_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
return Command;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaDeviceCompletePowerIrp(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
POWER_STATE PowerState;
|
||||
|
||||
PowerState.DeviceState = DevExt->Common.DevicePowerState;
|
||||
PoSetPowerState(DevExt->Common.Self, DevicePowerState, PowerState);
|
||||
|
||||
AtaPowerCompleteIrp(&DevExt->Common, Irp, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceFlushCache(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
UCHAR Command;
|
||||
|
||||
Command = AtaDeviceGetFlushCacheCommand(DevExt);
|
||||
if (Command == 0)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 30;
|
||||
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
||||
Request->TaskFile.Command = Command;
|
||||
|
||||
return AtaPortSendRequest(PortData, DevExt);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSetIdleMode(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 20;
|
||||
Request->TaskFile.Feature = 0;
|
||||
Request->TaskFile.Command = IDE_COMMAND_IDLE_IMMEDIATE;
|
||||
|
||||
return AtaPortSendRequest(PortData, DevExt);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaDeviceSetStandbyMode(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &PortData->Worker.InternalRequest;
|
||||
|
||||
Request->Flags = 0;
|
||||
Request->TimeOut = 20;
|
||||
Request->TaskFile.Command = IDE_COMMAND_STANDBY_IMMEDIATE;
|
||||
|
||||
return AtaPortSendRequest(PortData, DevExt);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
AtaPortDeviceProcessPowerChange(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
PIRP PowerIrp;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
DEVICE_POWER_STATE NewState;
|
||||
KIRQL OldLevel;
|
||||
NTSTATUS Status;
|
||||
|
||||
KeAcquireSpinLock(&DevExt->Device.QueueLock, &OldLevel);
|
||||
if (IsListEmpty(&DevExt->PowerIrpQueueList))
|
||||
Entry = NULL;
|
||||
else
|
||||
Entry = RemoveHeadList(&DevExt->PowerIrpQueueList);
|
||||
KeReleaseSpinLock(&DevExt->Device.QueueLock, OldLevel);
|
||||
if (!Entry)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
PowerIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(PowerIrp);
|
||||
ASSERT(IoStack->MinorFunction == IRP_MN_SET_POWER);
|
||||
|
||||
NewState = IoStack->Parameters.Power.State.DeviceState;
|
||||
if (NewState == DevExt->Common.DevicePowerState)
|
||||
{
|
||||
AtaDeviceCompletePowerIrp(DevExt, PowerIrp);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (NewState != PowerDeviceD3)
|
||||
{
|
||||
AtaReqThawQueue(DevExt, QUEUE_FLAG_FROZEN_POWER);
|
||||
|
||||
/* Power up the device */
|
||||
Status = AtaDeviceSetIdleMode(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
/* Restore the software settings after power up */
|
||||
_InterlockedOr(&DevExt->Worker.EventsPending, ACTION_DEVICE_CONFIG);
|
||||
_InterlockedOr(&PortData->Worker.EventsPending, ACTION_DEVICE_CONFIG);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtaReqFreezeQueue(DevExt, QUEUE_FLAG_FROZEN_POWER);
|
||||
|
||||
/* Flush device caches */
|
||||
Status = AtaDeviceFlushCache(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
|
||||
/* Power down the device */
|
||||
Status = AtaDeviceSetStandbyMode(PortData, DevExt);
|
||||
if (Status == STATUS_ADAPTER_HARDWARE_ERROR)
|
||||
return Status;
|
||||
}
|
||||
|
||||
AtaDeviceCompletePowerIrp(DevExt, PowerIrp);
|
||||
|
||||
DevExt->Common.DevicePowerState = NewState;
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
AtaPortCheckDevicePowerState(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
|
||||
if (DevExt->Common.DevicePowerState == PowerDeviceD0)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
INFO("CH %lu: Powering up idle device '%s'\n", PortData->PortNumber, DevExt->FriendlyName);
|
||||
|
||||
Status = AtaDeviceSetIdleMode(PortData, DevExt);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
/*
|
||||
* If the power on device has failed, the next command can still work.
|
||||
* The media access will result in a transition from the PM1:Idle/PM2:Standby state
|
||||
* to the PM0:Active state by the ATA spec.
|
||||
*/
|
||||
ERR("CH %lu: Failed to power up device '%s' %lx\n",
|
||||
PortData->PortNumber, DevExt->FriendlyName, Status);
|
||||
}
|
||||
else
|
||||
{
|
||||
DevExt->Common.DevicePowerState = PowerDeviceD0;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaPdoQueuePowerIrp(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
KIRQL OldLevel;
|
||||
|
||||
KeAcquireSpinLock(&DevExt->Device.QueueLock, &OldLevel);
|
||||
InsertTailList(&DevExt->PowerIrpQueueList, &Irp->Tail.Overlay.ListEntry);
|
||||
KeReleaseSpinLock(&DevExt->Device.QueueLock, OldLevel);
|
||||
|
||||
AtaDeviceQueueEvent(DevExt->Device.PortData, DevExt, ACTION_DEVICE_POWER);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaPdoPowerUpCompletionRoutine(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_In_ PIRP Irp,
|
||||
_In_reads_opt_(_Inexpressible_("varies")) PVOID Context)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt = Context;
|
||||
NTSTATUS Status;
|
||||
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
|
||||
Status = Irp->IoStatus.Status;
|
||||
|
||||
INFO("Powering up device, lower driver status %lx\n", Status);
|
||||
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
PoStartNextPowerIrp(Irp);
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
return STATUS_CONTINUE_COMPLETION;
|
||||
}
|
||||
|
||||
AtaPdoQueuePowerIrp(DevExt, Irp);
|
||||
|
||||
/* Defer IRP completion */
|
||||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaPdoPowerSetDevicePowerState(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
DEVICE_POWER_STATE NewState;
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
NewState = IoStack->Parameters.Power.State.DeviceState;
|
||||
|
||||
IoMarkIrpPending(Irp);
|
||||
|
||||
if (NewState < DevExt->Common.DevicePowerState)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
|
||||
|
||||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||||
IoSetCompletionRoutine(Irp,
|
||||
AtaPdoPowerUpCompletionRoutine,
|
||||
DevExt,
|
||||
TRUE,
|
||||
TRUE,
|
||||
TRUE);
|
||||
(VOID)PoCallDriver(ChanExt->Common.Self, Irp);
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO("Powering down device\n");
|
||||
|
||||
AtaPdoQueuePowerIrp(DevExt, Irp);
|
||||
}
|
||||
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
NTAPI
|
||||
AtaPdoPowerSetSysStateWithDevStateComplete(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_In_ UCHAR MinorFunction,
|
||||
_In_ POWER_STATE PowerState,
|
||||
_In_opt_ PVOID Context,
|
||||
_In_ PIO_STATUS_BLOCK IoStatus)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt = DeviceObject->DeviceExtension;
|
||||
PIRP Irp = Context;
|
||||
|
||||
UNREFERENCED_PARAMETER(MinorFunction);
|
||||
|
||||
if (DevExt->Common.SystemPowerState != PowerState.SystemState)
|
||||
{
|
||||
DevExt->Common.SystemPowerState = PowerState.SystemState;
|
||||
PoSetPowerState(DeviceObject, SystemPowerState, PowerState);
|
||||
}
|
||||
|
||||
AtaPowerCompleteIrp(&DevExt->Common, Irp, IoStatus->Status);
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaPdoPowerGetDevStateForSysState(
|
||||
_In_ SYSTEM_POWER_STATE SystemState,
|
||||
_Out_ PDEVICE_POWER_STATE DeviceState)
|
||||
{
|
||||
if (SystemState == PowerSystemWorking)
|
||||
{
|
||||
*DeviceState = PowerDeviceD0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*DeviceState = PowerDeviceD3;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaPdoPowerSetSystemPowerState(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
NTSTATUS Status;
|
||||
POWER_STATE NewPowerState;
|
||||
SYSTEM_POWER_STATE NewSystemState;
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
NewSystemState = IoStack->Parameters.Power.State.SystemState;
|
||||
|
||||
IoMarkIrpPending(Irp);
|
||||
|
||||
AtaPdoPowerGetDevStateForSysState(NewSystemState, &NewPowerState.DeviceState);
|
||||
Status = PoRequestPowerIrp(DevExt->Common.Self,
|
||||
IRP_MN_SET_POWER,
|
||||
NewPowerState,
|
||||
AtaPdoPowerSetSysStateWithDevStateComplete,
|
||||
Irp,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
IO_STATUS_BLOCK IoStatusBlock;
|
||||
|
||||
ERR("Failed to change power state for device '%s' %lx\n", DevExt->FriendlyName, Status);
|
||||
|
||||
IoStatusBlock.Status = Status;
|
||||
AtaPdoPowerSetSysStateWithDevStateComplete(DevExt->Common.Self,
|
||||
IRP_MN_SET_POWER,
|
||||
NewPowerState,
|
||||
Irp,
|
||||
&IoStatusBlock);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaPdoPower(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
// FIXME: Investigate how to fix this kernel bug
|
||||
// *** Assertion failed: KeGetCurrentThread()->SystemAffinityActive == FALSE
|
||||
// *** Source File: ../ntoskrnl/ke/dpc.c, line 926
|
||||
#if 1
|
||||
NTSTATUS Status;
|
||||
|
||||
UNREFERENCED_PARAMETER(AtaPdoPowerSetSystemPowerState);
|
||||
UNREFERENCED_PARAMETER(AtaPdoPowerGetDevStateForSysState);
|
||||
UNREFERENCED_PARAMETER(AtaPdoPowerSetSysStateWithDevStateComplete);
|
||||
UNREFERENCED_PARAMETER(AtaPdoPowerSetDevicePowerState);
|
||||
UNREFERENCED_PARAMETER(AtaPdoPowerUpCompletionRoutine);
|
||||
UNREFERENCED_PARAMETER(AtaPdoQueuePowerIrp);
|
||||
|
||||
Status = Irp->IoStatus.Status;
|
||||
PoStartNextPowerIrp(Irp);
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return Status;
|
||||
#else
|
||||
NTSTATUS Status;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
|
||||
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
PoStartNextPowerIrp(Irp);
|
||||
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return Status;
|
||||
}
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
switch (IoStack->MinorFunction)
|
||||
{
|
||||
case IRP_MN_QUERY_POWER:
|
||||
{
|
||||
switch (IoStack->Parameters.Power.Type)
|
||||
{
|
||||
case SystemPowerState:
|
||||
case DevicePowerState:
|
||||
Status = AtaPowerCompleteIrp(&DevExt->Common, Irp, STATUS_SUCCESS);
|
||||
break;
|
||||
|
||||
default:
|
||||
Status = AtaPowerCompleteIrp(&DevExt->Common, Irp, Irp->IoStatus.Status);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IRP_MN_SET_POWER:
|
||||
{
|
||||
switch (IoStack->Parameters.Power.Type)
|
||||
{
|
||||
case SystemPowerState:
|
||||
Status = AtaPdoPowerSetSystemPowerState(DevExt, Irp);
|
||||
break;
|
||||
case DevicePowerState:
|
||||
Status = AtaPdoPowerSetDevicePowerState(DevExt, Irp);
|
||||
break;
|
||||
|
||||
default:
|
||||
Status = AtaPowerCompleteIrp(&DevExt->Common, Irp, Irp->IoStatus.Status);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Status = AtaPowerCompleteIrp(&DevExt->Common, Irp, Irp->IoStatus.Status);
|
||||
break;
|
||||
}
|
||||
|
||||
return Status;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaFdoPower(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
|
||||
Status = IoAcquireRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
PoStartNextPowerIrp(Irp);
|
||||
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return Status;
|
||||
}
|
||||
|
||||
PoStartNextPowerIrp(Irp);
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
Status = PoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
||||
|
||||
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaDispatchPower(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
if (IS_FDO(DeviceObject->DeviceExtension))
|
||||
return AtaFdoPower(DeviceObject->DeviceExtension, Irp);
|
||||
else
|
||||
return AtaPdoPower(DeviceObject->DeviceExtension, Irp);
|
||||
}
|
||||
354
drivers/storage/ide/atapi/dev_timings.c
Normal file
354
drivers/storage/ide/atapi/dev_timings.c
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: ATA PIO and DMA timings support
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*
|
||||
* REFERENCES: For more details refer to the
|
||||
* "Intel ICH0~ICH5 Programmer's Reference Manual (PRM) 298600-004":
|
||||
* "Determining a Drive's Transfer Rate Capabilities"
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
static const ULONG AtapModeToCycleTime[] =
|
||||
{
|
||||
/* 0 1 2 3 4 PIO_MODE */
|
||||
600, 383, 240, 180, 120,
|
||||
|
||||
/* 0 1 2 SWDMA_MODE */
|
||||
960, 480, 240,
|
||||
|
||||
/* 0 1 2 MWDMA_MODE */
|
||||
480, 150, 120,
|
||||
|
||||
/* 0 1 2 3 4 5 6 UDMA_MODE */
|
||||
120, 80, 60, 45, 30, 20, 15
|
||||
};
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaTimQueryPioModeSupport(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ ULONG AllowedModesMask)
|
||||
{
|
||||
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
||||
ULONG SupportedMode, BestMode, CurrentMode, CycleTime;
|
||||
|
||||
/* Find the best possible PIO mode */
|
||||
if (IdentifyData->TranslationFieldsValid & 0x2)
|
||||
{
|
||||
/* PIO 3-4 */
|
||||
SupportedMode = IdentifyData->AdvancedPIOModes & 0x3;
|
||||
SupportedMode <<= PIO_MODE(3);
|
||||
|
||||
/* The device is assumed to have PIO 0-2 */
|
||||
SupportedMode |= PIO_MODE0 | PIO_MODE1 | PIO_MODE2;
|
||||
|
||||
SupportedMode &= AllowedModesMask;
|
||||
|
||||
if (IdentifyData->Capabilities.IordySupported)
|
||||
CycleTime = IdentifyData->MinimumPIOCycleTimeIORDY;
|
||||
else
|
||||
CycleTime = IdentifyData->MinimumPIOCycleTime;
|
||||
|
||||
/*
|
||||
* Any device that supports PIO 3 or above
|
||||
* should support word 67 and word 68, but be defensive.
|
||||
*/
|
||||
if (CycleTime == 0)
|
||||
{
|
||||
WARN("Device '%s' returned zero cycle time\n", DevExt->FriendlyName);
|
||||
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
|
||||
|
||||
/* Fall back to the default value */
|
||||
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
|
||||
CycleTime = AtapModeToCycleTime[BestMode];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* PIO 0-4 (maximum supported mode) */
|
||||
SupportedMode = IdentifyData->ObsoleteWords51[0] >> 8;
|
||||
if (SupportedMode > PIO_MODE(4))
|
||||
SupportedMode = PIO_MODE(0);
|
||||
|
||||
/* Convert the maximum mode to a mask */
|
||||
SupportedMode = (1 << (SupportedMode + 1)) - 1;
|
||||
SupportedMode &= AllowedModesMask;
|
||||
|
||||
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
|
||||
|
||||
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
|
||||
CycleTime = AtapModeToCycleTime[BestMode];
|
||||
}
|
||||
DevExt->TransferModeSupportedBitmap |= SupportedMode;
|
||||
DevExt->MinimumPioCycleTime = CycleTime;
|
||||
|
||||
/*
|
||||
* Since there is no way to obtain the current PIO mode from identify data
|
||||
* we just look for the fastest supported mode.
|
||||
*/
|
||||
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
|
||||
CurrentMode = BestMode;
|
||||
DevExt->TransferModeCurrentBitmap |= 1 << CurrentMode;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaTimQuerySwDmaModeSupport(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ ULONG AllowedModesMask)
|
||||
{
|
||||
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
||||
ULONG SupportedMode, BestMode, CurrentMode, CycleTime;
|
||||
|
||||
CycleTime = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED;
|
||||
|
||||
/* Find the current active SWDMA mode */
|
||||
CurrentMode = ((IdentifyData->ObsoleteWord62 & 0xFF00) >> 16) & 0x7;
|
||||
if (CurrentMode != 0)
|
||||
{
|
||||
NT_VERIFY(_BitScanReverse(&CurrentMode, CurrentMode));
|
||||
CurrentMode += SWDMA_MODE(0);
|
||||
|
||||
DevExt->TransferModeCurrentBitmap |= 1 << CurrentMode;
|
||||
}
|
||||
|
||||
/* Find the best possible SWDMA mode */
|
||||
SupportedMode = (IdentifyData->ObsoleteWord62 & 0x00FF) & 0x7;
|
||||
SupportedMode <<= SWDMA_MODE(0);
|
||||
SupportedMode &= AllowedModesMask;
|
||||
if (SupportedMode != 0)
|
||||
{
|
||||
DevExt->TransferModeSupportedBitmap |= SupportedMode;
|
||||
|
||||
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
|
||||
|
||||
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
|
||||
CycleTime = AtapModeToCycleTime[BestMode];
|
||||
}
|
||||
|
||||
DevExt->MinimumSingleWordDmaCycleTime = CycleTime;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaTimQueryMwDmaModeSupport(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ ULONG AllowedModesMask)
|
||||
{
|
||||
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
||||
ULONG SupportedMode, BestMode, CurrentMode, CycleTime;
|
||||
|
||||
CycleTime = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED;
|
||||
|
||||
/* Find the current active MWDMA mode */
|
||||
CurrentMode = IdentifyData->MultiWordDMAActive & 0x7;
|
||||
if (CurrentMode != 0)
|
||||
{
|
||||
NT_VERIFY(_BitScanReverse(&CurrentMode, CurrentMode));
|
||||
CurrentMode += MWDMA_MODE(0);
|
||||
|
||||
DevExt->TransferModeCurrentBitmap |= 1 << CurrentMode;
|
||||
}
|
||||
|
||||
/* Find the best possible MWDMA mode */
|
||||
SupportedMode = IdentifyData->MultiWordDMASupport & 0x7;
|
||||
SupportedMode <<= MWDMA_MODE(0);
|
||||
SupportedMode &= AllowedModesMask;
|
||||
if (SupportedMode != 0)
|
||||
{
|
||||
DevExt->TransferModeSupportedBitmap |= SupportedMode;
|
||||
|
||||
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
|
||||
|
||||
/* Prefer the minimum cycle time, if words 64-70 are valid */
|
||||
if ((IdentifyData->TranslationFieldsValid & 0x2) &&
|
||||
(IdentifyData->MinimumMWXferCycleTime != 0) &&
|
||||
(IdentifyData->RecommendedMWXferCycleTime != 0))
|
||||
{
|
||||
CycleTime = IdentifyData->MinimumMWXferCycleTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
|
||||
CycleTime = AtapModeToCycleTime[BestMode];
|
||||
}
|
||||
}
|
||||
|
||||
DevExt->MinimumMultiWordDmaCycleTime = CycleTime;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaTimQueryUDmaModeSupport(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ ULONG AllowedModesMask)
|
||||
{
|
||||
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
||||
ULONG SupportedMode, BestMode, CurrentMode, CycleTime;
|
||||
|
||||
CycleTime = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED;
|
||||
|
||||
/* Word 88 is not valid */
|
||||
if (!(IdentifyData->TranslationFieldsValid & 0x4))
|
||||
goto Exit;
|
||||
|
||||
/* Find the current active UDMA mode */
|
||||
CurrentMode = IdentifyData->UltraDMAActive & 0x7F;
|
||||
if (CurrentMode != 0)
|
||||
{
|
||||
NT_VERIFY(_BitScanReverse(&CurrentMode, CurrentMode));
|
||||
CurrentMode += UDMA_MODE(0);
|
||||
|
||||
DevExt->TransferModeCurrentBitmap |= 1 << CurrentMode;
|
||||
}
|
||||
|
||||
/* Find the best possible UDMA mode */
|
||||
SupportedMode = IdentifyData->UltraDMASupport & 0x7F;
|
||||
SupportedMode <<= UDMA_MODE(0);
|
||||
SupportedMode &= AllowedModesMask;
|
||||
if (SupportedMode != 0)
|
||||
{
|
||||
DevExt->TransferModeSupportedBitmap |= SupportedMode;
|
||||
|
||||
NT_VERIFY(_BitScanReverse(&BestMode, SupportedMode));
|
||||
|
||||
ASSERT(BestMode < RTL_NUMBER_OF(AtapModeToCycleTime));
|
||||
CycleTime = AtapModeToCycleTime[BestMode];
|
||||
}
|
||||
|
||||
Exit:
|
||||
DevExt->MinimumUltraDmaCycleTime = CycleTime;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaTimDumpTimingInfo(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
INFO("CH %lu: Device %u '%s'\n"
|
||||
"Cycle time UDMA[%ld] MWDMA[%ld] SWDMA[%ld] PIO[%ld]\n"
|
||||
"Supported modes 0x%08lX\n"
|
||||
"Active modes 0x%08lX\n"
|
||||
"Selected modes 0x%08lX\n"
|
||||
"IOREADY %u\n",
|
||||
PortData->PortNumber,
|
||||
DevExt->Device.AtaScsiAddress.TargetId,
|
||||
DevExt->FriendlyName,
|
||||
(LONG)DevExt->MinimumUltraDmaCycleTime,
|
||||
(LONG)DevExt->MinimumMultiWordDmaCycleTime,
|
||||
(LONG)DevExt->MinimumSingleWordDmaCycleTime,
|
||||
(LONG)DevExt->MinimumPioCycleTime,
|
||||
DevExt->TransferModeSupportedBitmap,
|
||||
DevExt->TransferModeCurrentBitmap,
|
||||
DevExt->TransferModeSelectedBitmap,
|
||||
DevExt->IdentifyDeviceData.Capabilities.IordySupported);
|
||||
}
|
||||
|
||||
VOID
|
||||
AtaPortSelectTimings(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ BOOLEAN ForceCompatibleTimings)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt;
|
||||
ULONG i;
|
||||
CHANNEL_DEVICE_CONFIG Config[ATA_MAX_DEVICE] = { 0 };
|
||||
PCHANNEL_DEVICE_CONFIG DeviceList[ATA_MAX_DEVICE] = { 0 };
|
||||
|
||||
if (ForceCompatibleTimings)
|
||||
{
|
||||
for (i = 0; i < ATA_MAX_DEVICE; ++i)
|
||||
{
|
||||
/* Fake device presence */
|
||||
DeviceList[i] = &Config[i];
|
||||
|
||||
/* Disable DMA and select compatible timings */
|
||||
Config[i].SupportedModes = PIO_MODE0;
|
||||
Config[i].CurrentModes = PIO_MODE0;
|
||||
Config[i].MinPioCycleTime = AtapModeToCycleTime[PIO_MODE(0)];
|
||||
Config[i].MinSwDmaCycleTime = AtapModeToCycleTime[SWDMA_MODE(0)];
|
||||
Config[i].MinMwDmaCycleTime = AtapModeToCycleTime[MWDMA_MODE(0)];
|
||||
Config[i].IsFixedDisk = TRUE;
|
||||
Config[i].IoReadySupported = FALSE;
|
||||
Config[i].IsNewDevice = FALSE;
|
||||
Config[i].FriendlyName = "";
|
||||
}
|
||||
|
||||
/* Call the chipset driver */
|
||||
PortData->SetTransferMode(PortData->ChannelContext, DeviceList);
|
||||
return;
|
||||
}
|
||||
|
||||
ChanExt = CONTAINING_RECORD(PortData, ATAPORT_CHANNEL_EXTENSION, PortData);
|
||||
|
||||
for (i = 0; i < ATA_MAX_DEVICE; ++i)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
ULONG AllowedModesMask;
|
||||
|
||||
DevExt = AtaFdoFindDeviceByPath(ChanExt,
|
||||
AtaMarshallScsiAddress(PortData->PortNumber, i, 0),
|
||||
NULL);
|
||||
if (!DevExt)
|
||||
continue;
|
||||
|
||||
DevExt->TransferModeCurrentBitmap = 0;
|
||||
DevExt->TransferModeSupportedBitmap = 0;
|
||||
|
||||
AllowedModesMask = DevExt->TransferModeUserAllowedMask;
|
||||
AllowedModesMask &= DevExt->TransferModeAllowedMask;
|
||||
/* Mode cannot be slower than PIO0 */
|
||||
AllowedModesMask |= PIO_MODE0;
|
||||
|
||||
AtaTimQueryPioModeSupport(DevExt, AllowedModesMask);
|
||||
AtaTimQuerySwDmaModeSupport(DevExt, AllowedModesMask);
|
||||
AtaTimQueryMwDmaModeSupport(DevExt, AllowedModesMask);
|
||||
AtaTimQueryUDmaModeSupport(DevExt, AllowedModesMask);
|
||||
|
||||
DeviceList[i] = &Config[i];
|
||||
Config[i].CurrentModes = DevExt->TransferModeCurrentBitmap;
|
||||
Config[i].SupportedModes = DevExt->TransferModeSupportedBitmap;
|
||||
Config[i].MinPioCycleTime = DevExt->MinimumPioCycleTime;
|
||||
Config[i].MinSwDmaCycleTime = DevExt->MinimumSingleWordDmaCycleTime;
|
||||
Config[i].MinMwDmaCycleTime = DevExt->MinimumMultiWordDmaCycleTime;
|
||||
Config[i].IsFixedDisk = !IS_ATAPI(&DevExt->Device);
|
||||
Config[i].IoReadySupported = !!DevExt->IdentifyDeviceData.Capabilities.IordySupported;
|
||||
Config[i].IsNewDevice = !(DevExt->Device.DeviceFlags & DEVICE_PNP_STARTED);
|
||||
Config[i].FriendlyName = DevExt->FriendlyName;
|
||||
}
|
||||
|
||||
/* Call the chipset driver */
|
||||
PortData->SetTransferMode(PortData->ChannelContext, DeviceList);
|
||||
|
||||
/* Save the result */
|
||||
for (i = 0; i < ATA_MAX_DEVICE; ++i)
|
||||
{
|
||||
PCHANNEL_DEVICE_CONFIG DeviceConfig = DeviceList[i];
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
|
||||
if (!DeviceConfig)
|
||||
continue;
|
||||
|
||||
DevExt = AtaFdoFindDeviceByPath(ChanExt,
|
||||
AtaMarshallScsiAddress(PortData->PortNumber, i, 0),
|
||||
NULL);
|
||||
ASSERT(DevExt);
|
||||
|
||||
DevExt->TransferModeSelectedBitmap = 1 << DeviceConfig->PioMode;
|
||||
if (DeviceConfig->DmaMode != PIO_MODE(0))
|
||||
{
|
||||
DevExt->TransferModeSelectedBitmap |= 1 << DeviceConfig->DmaMode;
|
||||
}
|
||||
|
||||
AtaTimDumpTimingInfo(PortData, DevExt);
|
||||
}
|
||||
}
|
||||
658
drivers/storage/ide/atapi/enum.c
Normal file
658
drivers/storage/ide/atapi/enum.c
Normal file
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: ATA bus enumeration
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
PUCHAR
|
||||
AtaCopyIdStringUnsafe(
|
||||
_Out_writes_bytes_all_(Length) PUCHAR Destination,
|
||||
_In_reads_bytes_(Length) PUCHAR Source,
|
||||
_In_ ULONG Length)
|
||||
{
|
||||
ULONG i;
|
||||
|
||||
ASSUME((Length >= sizeof(USHORT)) && (Length % sizeof(USHORT)) == 0);
|
||||
|
||||
/* Copy the ATA string and swap it */
|
||||
for (i = 0; i < Length; i += sizeof(USHORT))
|
||||
{
|
||||
Destination[i] = Source[i + 1];
|
||||
Destination[i + 1] = Source[i];
|
||||
}
|
||||
|
||||
return &Destination[i - 1];
|
||||
}
|
||||
|
||||
PCHAR
|
||||
AtaCopyIdStringSafe(
|
||||
_Out_writes_bytes_all_(MaxLength) PCHAR Destination,
|
||||
_In_reads_bytes_(MaxLength) PUCHAR Source,
|
||||
_In_ ULONG MaxLength,
|
||||
_In_ CHAR DefaultCharacter)
|
||||
{
|
||||
PCHAR Dest = Destination;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
while (MaxLength != 0)
|
||||
{
|
||||
const UCHAR Char = *Source;
|
||||
|
||||
/* Only characters from space to tilde are allowed in an ID */
|
||||
if (Char > ' ' && Char <= '~' && Char != ',')
|
||||
*Dest = Char;
|
||||
else
|
||||
*Dest = DefaultCharacter;
|
||||
|
||||
++Source;
|
||||
++Dest;
|
||||
--MaxLength;
|
||||
}
|
||||
|
||||
return Dest;
|
||||
}
|
||||
|
||||
VOID
|
||||
AtaSwapIdString(
|
||||
_Inout_updates_bytes_(WordCount * sizeof(USHORT)) PVOID Buffer,
|
||||
_In_range_(>, 0) ULONG WordCount)
|
||||
{
|
||||
PUSHORT Word = Buffer;
|
||||
|
||||
/* The buffer should be USHORT aligned for ARM compatibility */
|
||||
ASSERT(((ULONG_PTR)Buffer & 1) == 0);
|
||||
|
||||
while (WordCount--)
|
||||
{
|
||||
*Word = RtlUshortByteSwap(*Word);
|
||||
++Word;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
PCHAR
|
||||
AtaTrimIdString(
|
||||
_In_ _Post_z_ PCHAR Start,
|
||||
_In_ PCHAR End)
|
||||
{
|
||||
PCHAR Current = End - 1;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/* Remove trailing spaces */
|
||||
while (Current >= Start && *Current == ' ')
|
||||
{
|
||||
--Current;
|
||||
}
|
||||
Current[1] = ANSI_NULL;
|
||||
|
||||
return (Current + 1);
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaPdoFillIdentificationStrings(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
||||
PCHAR End;
|
||||
ULONG i;
|
||||
NTSTATUS Status;
|
||||
size_t Remaining;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/*
|
||||
* For ATAPI devices inquiry data is preferred over identify data.
|
||||
* ATA strings are *not* byte-swapped for early ATAPI drives (NEC CDR-260, etc.).
|
||||
*/
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
{
|
||||
/* Combine VendorId and ProductId, separated by a space */
|
||||
End = AtaCopyIdStringSafe(DevExt->FriendlyName,
|
||||
DevExt->InquiryData.VendorId,
|
||||
RTL_FIELD_SIZE(INQUIRYDATA, VendorId),
|
||||
' ');
|
||||
End = AtaTrimIdString(DevExt->FriendlyName, End);
|
||||
*End++ = ' ';
|
||||
End = AtaCopyIdStringSafe(End,
|
||||
DevExt->InquiryData.ProductId,
|
||||
RTL_FIELD_SIZE(INQUIRYDATA, ProductId),
|
||||
' ');
|
||||
AtaTrimIdString(DevExt->FriendlyName, End);
|
||||
|
||||
/* Copy ProductRevisionLevel */
|
||||
End = AtaCopyIdStringSafe(DevExt->RevisionNumber,
|
||||
DevExt->InquiryData.ProductRevisionLevel,
|
||||
RTL_FIELD_SIZE(INQUIRYDATA, ProductRevisionLevel),
|
||||
' ');
|
||||
AtaTrimIdString(DevExt->RevisionNumber, End);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Copy ModelNumber from a byte-swapped ATA string */
|
||||
End = AtaCopyIdStringSafe(DevExt->FriendlyName,
|
||||
IdentifyData->ModelNumber,
|
||||
ATAPORT_FN_FIELD,
|
||||
' ');
|
||||
AtaSwapIdString(DevExt->FriendlyName, ATAPORT_FN_FIELD / 2);
|
||||
AtaTrimIdString(DevExt->FriendlyName, End);
|
||||
|
||||
/* Copy FirmwareRevision from a byte-swapped ATA string */
|
||||
End = AtaCopyIdStringSafe(DevExt->RevisionNumber,
|
||||
IdentifyData->FirmwareRevision,
|
||||
ATAPORT_RN_FIELD,
|
||||
' ');
|
||||
AtaSwapIdString(DevExt->RevisionNumber, ATAPORT_RN_FIELD / 2);
|
||||
AtaTrimIdString(DevExt->RevisionNumber, End);
|
||||
}
|
||||
|
||||
End = DevExt->SerialNumber;
|
||||
Remaining = sizeof(DevExt->SerialNumber);
|
||||
|
||||
/* Format the serial number */
|
||||
for (i = 0; i < sizeof(IdentifyData->SerialNumber); ++i)
|
||||
{
|
||||
Status = RtlStringCchPrintfExA(End,
|
||||
Remaining,
|
||||
&End,
|
||||
&Remaining,
|
||||
0,
|
||||
"%2x",
|
||||
IdentifyData->SerialNumber[i]);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
}
|
||||
|
||||
INFO("FriendlyName: '%s'\n", DevExt->FriendlyName);
|
||||
INFO("RevisionNumber: '%s'\n", DevExt->RevisionNumber);
|
||||
INFO("SerialNumber: '%s'\n", DevExt->SerialNumber);
|
||||
}
|
||||
|
||||
VOID
|
||||
AtaDeviceSetAddressingMode(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
||||
ULONG64 TotalSectors;
|
||||
|
||||
DevExt->Device.DeviceFlags &= ~(DEVICE_LBA_MODE | DEVICE_LBA48 | DEVICE_HAS_FUA);
|
||||
|
||||
/* Using LBA addressing mode */
|
||||
if (AtaDevHasLbaTranslation(IdentifyData))
|
||||
{
|
||||
DevExt->Device.DeviceFlags |= DEVICE_LBA_MODE;
|
||||
|
||||
if (AtaDevHas48BitAddressFeature(IdentifyData))
|
||||
{
|
||||
/* Using LBA48 addressing mode */
|
||||
TotalSectors = AtaDevUserAddressableSectors48Bit(IdentifyData);
|
||||
ASSERT(TotalSectors <= ATA_MAX_LBA_48);
|
||||
|
||||
DevExt->Device.DeviceFlags |= DEVICE_LBA48;
|
||||
|
||||
if (AtaDevHasForceUnitAccessCommands(IdentifyData))
|
||||
DevExt->Device.DeviceFlags |= DEVICE_HAS_FUA;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Using LBA28 addressing mode */
|
||||
TotalSectors = AtaDevUserAddressableSectors28Bit(IdentifyData);
|
||||
ASSERT(TotalSectors <= ATA_MAX_LBA_28);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
USHORT Cylinders, Heads, SectorsPerTrack;
|
||||
|
||||
/* Using CHS addressing mode */
|
||||
if (AtaDevIsCurrentGeometryValid(IdentifyData))
|
||||
{
|
||||
AtaDevCurrentChsTranslation(IdentifyData, &Cylinders, &Heads, &SectorsPerTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtaDevDefaultChsTranslation(IdentifyData, &Cylinders, &Heads, &SectorsPerTrack);
|
||||
}
|
||||
DevExt->Device.Cylinders = Cylinders;
|
||||
DevExt->Device.Heads = Heads;
|
||||
DevExt->Device.SectorsPerTrack = SectorsPerTrack;
|
||||
|
||||
TotalSectors = (ULONG64)Cylinders * Heads * SectorsPerTrack;
|
||||
}
|
||||
|
||||
/*
|
||||
* The sector count can be 0 on faulty devices. It's better to keep
|
||||
* them available in the system to allow
|
||||
* the user to send any command to the drive through the pass-through interface.
|
||||
*/
|
||||
if (TotalSectors == 0)
|
||||
{
|
||||
ERR("Unknown geometry\n");
|
||||
|
||||
/* Fix up sector count for the READ CAPACITY command */
|
||||
TotalSectors = 1;
|
||||
|
||||
/* Avoid dividing by zero in READ/WRITE commands */
|
||||
DevExt->Device.SectorsPerTrack = 1;
|
||||
DevExt->Device.Heads = 1;
|
||||
}
|
||||
|
||||
DevExt->Device.TotalSectors = TotalSectors;
|
||||
|
||||
DevExt->Device.SectorSize = AtaDevBytesPerLogicalSector(IdentifyData);
|
||||
ASSERT(DevExt->Device.SectorSize >= ATA_MIN_SECTOR_SIZE);
|
||||
DevExt->Device.SectorSize = max(DevExt->Device.SectorSize, ATA_MIN_SECTOR_SIZE);
|
||||
|
||||
INFO("Total sectors %I64u of size %lu, CHS %u:%u:%u\n",
|
||||
DevExt->Device.TotalSectors,
|
||||
DevExt->Device.SectorSize,
|
||||
DevExt->Device.Cylinders,
|
||||
DevExt->Device.Heads,
|
||||
DevExt->Device.SectorsPerTrack);
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaDeviceEnableQueuedCommands(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
ULONG DeviceQueueDepth;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
DeviceQueueDepth = AtaDevQueueDepth(&DevExt->IdentifyDeviceData);
|
||||
if (DeviceQueueDepth == 0)
|
||||
return;
|
||||
|
||||
/* Do not set the queue depth to larger than the HBA can handle */
|
||||
DeviceQueueDepth = min(DeviceQueueDepth, DevExt->Device.PortData->QueueDepth);
|
||||
|
||||
DevExt->Device.TransportFlags |= DeviceQueueDepth << DEVICE_QUEUE_DEPTH_SHIFT;
|
||||
|
||||
DevExt->Device.DeviceFlags |= DEVICE_NCQ;
|
||||
|
||||
INFO("NCQ enabled, queue depth %lu/%lu\n",
|
||||
DeviceQueueDepth,
|
||||
DevExt->Device.PortData->QueueDepth);
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
BOOLEAN
|
||||
AtaDeviceIsSuperFloppy(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
if (DevExt->InquiryData.DeviceType == DIRECT_ACCESS_DEVICE)
|
||||
{
|
||||
/* Look for ATAPI SuperDisk drives. For example, 'MATSHITA LS-120 COSM 03' */
|
||||
return (strstr(DevExt->FriendlyName, " LS-120") ||
|
||||
strstr(DevExt->FriendlyName, " LS-240"));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaPdoInit(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
AtaPdoFillIdentificationStrings(DevExt);
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
{
|
||||
if (AtaDeviceIsSuperFloppy(DevExt))
|
||||
DevExt->Device.DeviceFlags |= DEVICE_IS_SUPER_FLOPPY;
|
||||
|
||||
if (AtaDevIsTape(&DevExt->IdentifyPacketData))
|
||||
{
|
||||
INFO("Tape drive detected '%s'\n", DevExt->FriendlyName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ChanExt->PortData.PortFlags & PORT_FLAG_NCQ)
|
||||
AtaDeviceEnableQueuedCommands(DevExt);
|
||||
|
||||
AtaDeviceSetAddressingMode(DevExt);
|
||||
AtaCreateStandardInquiryData(DevExt);
|
||||
}
|
||||
|
||||
if (DevExt->Device.AtaScsiAddress.Lun == 0)
|
||||
{
|
||||
AtaSetRegistryKey(ChanExt,
|
||||
DevExt->Device.AtaScsiAddress.TargetId,
|
||||
DD_ATA_REG_ATA_DEVICE_TYPE,
|
||||
DevExt->DeviceType);
|
||||
|
||||
AtaSetRegistryKey(ChanExt,
|
||||
DevExt->Device.AtaScsiAddress.TargetId,
|
||||
DD_ATA_REG_SCSI_DEVICE_TYPE,
|
||||
DevExt->InquiryData.DeviceType);
|
||||
|
||||
AtaGetRegistryKey(ChanExt,
|
||||
DevExt->Device.AtaScsiAddress.TargetId,
|
||||
DD_ATA_REG_XFER_MODE_ALLOWED,
|
||||
&DevExt->TransferModeUserAllowedMask,
|
||||
MAXULONG);
|
||||
}
|
||||
|
||||
/* Will be unlocked upon PnP START IRP */
|
||||
AtaReqFreezeQueue(DevExt, QUEUE_FLAG_FROZEN_PNP);
|
||||
|
||||
if (!AtaDevIsSsd(&DevExt->IdentifyDeviceData))
|
||||
{
|
||||
/* The spindle motor requires an inrush of power at start-up */
|
||||
DevExt->Common.Self->Flags |= DO_POWER_INRUSH;
|
||||
}
|
||||
if (DevExt->InquiryData.RemovableMedia)
|
||||
{
|
||||
/* Distinguish between fixed and removable drives (e.g. CFA media) */
|
||||
DevExt->Common.Self->Characteristics |= FILE_REMOVABLE_MEDIA;
|
||||
}
|
||||
DevExt->Common.Self->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
|
||||
DevExt->Device.DeviceFlags &= ~DEVICE_UNINITIALIZED;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
ULONG
|
||||
AtaFdoQueryDeviceCount(
|
||||
_In_ PATAPORT_PORT_DATA PortData)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
KeClearEvent(&PortData->Worker.EnumerationEvent);
|
||||
AtaDeviceQueueEvent(PortData, NULL, ACTION_ENUM_PORT);
|
||||
KeWaitForSingleObject(&PortData->Worker.EnumerationEvent,
|
||||
Executive,
|
||||
KernelMode,
|
||||
FALSE,
|
||||
NULL);
|
||||
return PortData->Worker.DeviceCount;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
ATA_DEVICE_STATUS
|
||||
AtaFdoQueryDeviceStatus(
|
||||
_In_ PATAPORT_PORT_DATA PortData,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ ATA_PORT_ACTION Action)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
KeClearEvent(&DevExt->Worker.EnumerationEvent);
|
||||
AtaDeviceQueueEvent(PortData, DevExt, Action);
|
||||
KeWaitForSingleObject(&DevExt->Worker.EnumerationEvent,
|
||||
Executive,
|
||||
KernelMode,
|
||||
FALSE,
|
||||
NULL);
|
||||
return DevExt->Worker.EnumStatus;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaFdoEnumeratePort(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
||||
{
|
||||
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
||||
ULONG i, DeviceCount;
|
||||
BOOLEAN DiscoveredNewDevice = FALSE;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
DeviceCount = AtaFdoQueryDeviceCount(PortData);
|
||||
|
||||
for (i = 0; i < DeviceCount; ++i)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
ATA_DEVICE_STATUS EnumStatus;
|
||||
|
||||
AtaScsiAddress = AtaMarshallScsiAddress(PortData->PortNumber, i, 0);
|
||||
|
||||
/* Try to find an existing device */
|
||||
DevExt = AtaFdoFindDeviceByPath(ChanExt, AtaScsiAddress, NULL);
|
||||
if (!DevExt)
|
||||
{
|
||||
DevExt = AtaPdoCreateDevice(ChanExt, AtaScsiAddress);
|
||||
if (!DevExt)
|
||||
{
|
||||
/* We are out of memory, trying to continue process the QBR IRP anyway */
|
||||
ERR("Failed to allocate PDO extension\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Query the last known device type */
|
||||
AtaGetRegistryKey(ChanExt,
|
||||
i,
|
||||
DD_ATA_REG_ATA_DEVICE_TYPE,
|
||||
&DevExt->DeviceType,
|
||||
DEV_NONE);
|
||||
|
||||
PortData->Worker.EnumDevExt = DevExt;
|
||||
EnumStatus = AtaFdoQueryDeviceStatus(PortData, DevExt, ACTION_ENUM_DEVICE_NEW);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnumStatus = AtaFdoQueryDeviceStatus(PortData, DevExt, ACTION_ENUM_DEVICE);
|
||||
}
|
||||
|
||||
/* Determine the type of the device */
|
||||
switch (EnumStatus)
|
||||
{
|
||||
/* No device present at this SCSI address */
|
||||
case DEV_STATUS_FAILED:
|
||||
case DEV_STATUS_NO_DEVICE:
|
||||
{
|
||||
if (DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED)
|
||||
AtaPdoFreeDevice(DevExt);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* It's the same device still */
|
||||
case DEV_STATUS_SAME_DEVICE:
|
||||
{
|
||||
ASSERT(!(DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED));
|
||||
|
||||
DevExt->NotPresent = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* At this point, we assume that the drive is a new device */
|
||||
case DEV_STATUS_NEW_DEVICE:
|
||||
{
|
||||
/* Device type has changed because of a hot-plug event */
|
||||
if (!(DevExt->Device.DeviceFlags & DEVICE_UNINITIALIZED))
|
||||
{
|
||||
/* Create a new PDO for the newly hotplugged device */
|
||||
--i; // Retry
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ASSERT(FALSE);
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
AtaPdoInit(ChanExt, DevExt);
|
||||
AtaFdoDeviceListInsert(ChanExt, DevExt, TRUE);
|
||||
|
||||
DiscoveredNewDevice = TRUE;
|
||||
}
|
||||
|
||||
/* Prepare the channel for a new device */
|
||||
if (DiscoveredNewDevice)
|
||||
AtaDeviceQueueEvent(PortData, NULL, ACTION_PORT_TIMING);
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaFdoInitializeDeviceRelations(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PDEVICE_RELATIONS DeviceRelations)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
DeviceRelations->Count = 0;
|
||||
|
||||
AtaScsiAddress.AsULONG = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, TRUE, NULL);
|
||||
if (!DevExt)
|
||||
break;
|
||||
|
||||
if (DevExt->NotPresent)
|
||||
{
|
||||
if (AtaScsiAddress.Lun == 0)
|
||||
{
|
||||
AtaSetRegistryKey(ChanExt,
|
||||
AtaScsiAddress.TargetId,
|
||||
DD_ATA_REG_ATA_DEVICE_TYPE,
|
||||
DEV_NONE);
|
||||
}
|
||||
|
||||
DevExt->ReportedMissing = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AtaScsiAddress.Lun == 0)
|
||||
{
|
||||
AtaSetRegistryKey(ChanExt, AtaScsiAddress.TargetId,
|
||||
DD_ATA_REG_XFER_MODE_SUPPORTED,
|
||||
DevExt->TransferModeSupportedBitmap);
|
||||
|
||||
AtaSetRegistryKey(ChanExt,
|
||||
AtaScsiAddress.TargetId,
|
||||
DD_ATA_REG_XFER_MODE_SELECTED,
|
||||
DevExt->TransferModeSelectedBitmap);
|
||||
}
|
||||
|
||||
DeviceRelations->Objects[DeviceRelations->Count++] = DevExt->Common.Self;
|
||||
ObReferenceObject(DevExt->Common.Self);
|
||||
}
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoQueryBusRelations(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PDEVICE_RELATIONS DeviceRelations = NULL;
|
||||
ULONG Size, PdoCount;
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
#if DBG
|
||||
LARGE_INTEGER TimeStart, TimeFinish;
|
||||
ULONG EnumTimeMs;
|
||||
#endif
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
#if DBG
|
||||
KeQuerySystemTime(&TimeStart);
|
||||
#endif
|
||||
|
||||
AtaScsiAddress.AsULONG = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
|
||||
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, TRUE, NULL);
|
||||
if (!DevExt)
|
||||
break;
|
||||
|
||||
DevExt->NotPresent = TRUE;
|
||||
|
||||
if (DevExt->Device.AtaScsiAddress.Lun == 0)
|
||||
{
|
||||
AtaGetRegistryKey(ChanExt,
|
||||
DevExt->Device.AtaScsiAddress.TargetId,
|
||||
DD_ATA_REG_XFER_MODE_ALLOWED,
|
||||
&DevExt->TransferModeUserAllowedMask,
|
||||
MAXULONG);
|
||||
}
|
||||
}
|
||||
|
||||
AtaFdoEnumeratePort(ChanExt);
|
||||
|
||||
PdoCount = 0;
|
||||
AtaScsiAddress.AsULONG = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
|
||||
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, TRUE, NULL);
|
||||
if (!DevExt)
|
||||
break;
|
||||
|
||||
if (!DevExt->NotPresent)
|
||||
++PdoCount;
|
||||
}
|
||||
|
||||
Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects[PdoCount]);
|
||||
DeviceRelations = ExAllocatePoolUninitialized(PagedPool, Size, ATAPORT_TAG);
|
||||
if (!DeviceRelations)
|
||||
{
|
||||
ERR("Failed to allocate device relations\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
AtaFdoInitializeDeviceRelations(ChanExt, DeviceRelations);
|
||||
|
||||
#if DBG
|
||||
KeQuerySystemTime(&TimeFinish);
|
||||
EnumTimeMs = (TimeFinish.QuadPart - TimeStart.QuadPart) / 10000;
|
||||
if (EnumTimeMs >= 5000)
|
||||
{
|
||||
WARN("%lu: QBR request took %lu ms, %lu devices\n",
|
||||
ChanExt->ScsiPortNumber,
|
||||
EnumTimeMs,
|
||||
DeviceRelations->Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO("%lu: QBR request took %lu ms, %lu devices\n",
|
||||
ChanExt->ScsiPortNumber,
|
||||
EnumTimeMs,
|
||||
DeviceRelations->Count);
|
||||
}
|
||||
#endif
|
||||
|
||||
Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Cleanup:
|
||||
if (DeviceRelations)
|
||||
ExFreePoolWithTag(DeviceRelations, ATAPORT_TAG);
|
||||
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
758
drivers/storage/ide/atapi/fdo.c
Normal file
758
drivers/storage/ide/atapi/fdo.c
Normal file
@@ -0,0 +1,758 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: ATA channel device object (FDO) dispatch routines
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
DECLARE_PAGED_WSTRING(AtapDevSymLinkFormat, L"\\Device\\ScsiPort%lu");
|
||||
DECLARE_PAGED_WSTRING(AtapDosSymLinkFormat, L"\\DosDevices\\Scsi%lu:");
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoCreateSymLinks(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
||||
{
|
||||
UNICODE_STRING ChannelName;
|
||||
ULONG ScsiAdapter, ScsiPortCount;
|
||||
WCHAR ChannelNameBuffer[sizeof("\\Device\\Ide\\IdePort99999")];
|
||||
NTSTATUS Status;
|
||||
DECLARE_PAGED_WSTRING(FdoFormat, L"\\Device\\Ide\\IdePort%lu");
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (ChanExt->PortData.PortFlags & PORT_FLAG_SYMLINK_CREATED)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Status = RtlStringCbPrintfW(ChannelNameBuffer,
|
||||
sizeof(ChannelNameBuffer),
|
||||
FdoFormat,
|
||||
ChanExt->DeviceObjectNumber);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&ChannelName, ChannelNameBuffer);
|
||||
|
||||
ScsiPortCount = IoGetConfigurationInformation()->ScsiPortCount;
|
||||
|
||||
/* Search for a free SCSI port adapter in the system */
|
||||
for (ScsiAdapter = 0; ScsiAdapter <= ScsiPortCount; ++ScsiAdapter)
|
||||
{
|
||||
WCHAR SymLinkNameBuffer[sizeof("\\DosDevices\\Scsi99999:")];
|
||||
UNICODE_STRING SymLinkName;
|
||||
|
||||
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
|
||||
sizeof(SymLinkNameBuffer),
|
||||
AtapDevSymLinkFormat,
|
||||
ScsiAdapter);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
|
||||
|
||||
/* Create a symbolic link '\Device\ScsiPortX' -> '\Device\Ide\IdePortN' */
|
||||
Status = IoCreateSymbolicLink(&SymLinkName, &ChannelName);
|
||||
if (!NT_SUCCESS(Status))
|
||||
continue;
|
||||
|
||||
INFO("Symlink created '%wZ' -> '%wZ'\n", &SymLinkName, &ChannelName);
|
||||
|
||||
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
|
||||
sizeof(SymLinkNameBuffer),
|
||||
AtapDosSymLinkFormat,
|
||||
ScsiAdapter);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
|
||||
|
||||
/* Create a symbolic link '\DosDevices\ScsiX:' -> '\Device\Ide\IdePortN' */
|
||||
Status = IoCreateSymbolicLink(&SymLinkName, &ChannelName);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
INFO("Symlink created '%wZ' -> '%wZ'\n", &SymLinkName, &ChannelName);
|
||||
}
|
||||
|
||||
/* Register ourselves (ATA channel) as a SCSI port adapter */
|
||||
IoGetConfigurationInformation()->ScsiPortCount++;
|
||||
|
||||
ChanExt->ScsiPortNumber = ScsiAdapter;
|
||||
ChanExt->PortData.PortFlags |= PORT_FLAG_SYMLINK_CREATED;
|
||||
break;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoAllocateLocalBuffer(
|
||||
_In_ PATAPORT_PORT_DATA PortData)
|
||||
{
|
||||
PHYSICAL_ADDRESS PhysicalAddress;
|
||||
PHYSICAL_ADDRESS HighestAcceptableAddress;
|
||||
PHYSICAL_ADDRESS LowestAcceptableAddress;
|
||||
PHYSICAL_ADDRESS BoundaryAddressMultiple;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (PortData->LocalBuffer)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
LowestAcceptableAddress.QuadPart = 0;
|
||||
HighestAcceptableAddress.QuadPart = 0xFFFFFFFF; // 32-bit DMA
|
||||
BoundaryAddressMultiple.QuadPart = 0x10000; // 64k, for PATA compability
|
||||
PortData->LocalBuffer = MmAllocateContiguousMemorySpecifyCache(ATA_LOCAL_BUFFER_SIZE,
|
||||
LowestAcceptableAddress,
|
||||
HighestAcceptableAddress,
|
||||
BoundaryAddressMultiple,
|
||||
MmNonCached);
|
||||
if (!PortData->LocalBuffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
PhysicalAddress = MmGetPhysicalAddress(PortData->LocalBuffer);
|
||||
|
||||
PortData->LocalSgList.NumberOfElements = 1;
|
||||
PortData->LocalSgList.Elements[0].Length = ATA_LOCAL_BUFFER_SIZE;
|
||||
PortData->LocalSgList.Elements[0].Address.QuadPart = PhysicalAddress.QuadPart;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoCreatePortThread(
|
||||
_In_ PATAPORT_PORT_DATA PortData)
|
||||
{
|
||||
OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, OBJ_KERNEL_HANDLE);
|
||||
HANDLE ThreadHandle;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (PortData->Worker.Thread)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
KeInitializeEvent(&PortData->Worker.ThreadEvent, NotificationEvent, FALSE);
|
||||
|
||||
Status = PsCreateSystemThread(&ThreadHandle,
|
||||
THREAD_ALL_ACCESS,
|
||||
&ObjectAttributes,
|
||||
NULL,
|
||||
NULL,
|
||||
AtaPortWorkerThread,
|
||||
PortData);
|
||||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
|
||||
Status = ObReferenceObjectByHandle(ThreadHandle,
|
||||
THREAD_ALL_ACCESS,
|
||||
NULL,
|
||||
KernelMode,
|
||||
(PVOID*)&PortData->Worker.Thread,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
|
||||
ZwClose(ThreadHandle);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaFdoDestroyPortThread(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
||||
{
|
||||
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!PortData->Worker.Thread)
|
||||
return;
|
||||
|
||||
PortData->PortFlags |= PORT_FLAG_EXIT_THREAD;
|
||||
AtaPortSignalWorkerThread(PortData);
|
||||
|
||||
KeWaitForSingleObject(PortData->Worker.Thread, Executive, KernelMode, FALSE, NULL);
|
||||
|
||||
ObDereferenceObject(PortData->Worker.Thread);
|
||||
|
||||
PortData->Worker.Thread = NULL;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaFdoRemoveSymLinks(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
UNICODE_STRING SymLinkName;
|
||||
WCHAR SymLinkNameBuffer[sizeof("\\DosDevices\\Scsi99999:")];
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!(ChanExt->PortData.PortFlags & PORT_FLAG_SYMLINK_CREATED))
|
||||
return;
|
||||
|
||||
/* Delete the '\DosDevices\\ScsiX:' symbolic link */
|
||||
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
|
||||
sizeof(SymLinkNameBuffer),
|
||||
AtapDevSymLinkFormat,
|
||||
ChanExt->ScsiPortNumber);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
|
||||
(VOID)IoDeleteSymbolicLink(&SymLinkName);
|
||||
|
||||
/* Delete the '\Device\\ScsiPortX' symbolic link */
|
||||
Status = RtlStringCbPrintfW(SymLinkNameBuffer,
|
||||
sizeof(SymLinkNameBuffer),
|
||||
AtapDosSymLinkFormat,
|
||||
ChanExt->ScsiPortNumber);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&SymLinkName, SymLinkNameBuffer);
|
||||
(VOID)IoDeleteSymbolicLink(&SymLinkName);
|
||||
|
||||
/* Unregister the SCSI port adapter */
|
||||
IoGetConfigurationInformation()->ScsiPortCount--;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoStartDevice(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PCM_RESOURCE_LIST ResourcesTranslated)
|
||||
{
|
||||
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
||||
PCIIDEX_CHANNEL_INTERFACE ChannelInterface = { 0 };
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
INFO("Starting channel %lu\n", ChanExt->DeviceObjectNumber);
|
||||
|
||||
/* Get the interface of ATA channel */
|
||||
Status = AtaPnpQueryInterface(&ChanExt->Common,
|
||||
&GUID_PCIIDE_INTERFACE_ROS,
|
||||
&ChannelInterface,
|
||||
PCIIDEX_INTERFACE_VERSION,
|
||||
sizeof(ChannelInterface));
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("Failed to query channel interface %lx\n", Status);
|
||||
return Status;
|
||||
}
|
||||
PortData->ChannelContext = ChannelInterface.ChannelContext;
|
||||
PortData->AttachChannel = ChannelInterface.AttachChannel;
|
||||
PortData->SetDeviceData = ChannelInterface.SetDeviceData;
|
||||
PortData->GetInitTaskFile = ChannelInterface.GetInitTaskFile;
|
||||
PortData->DowngradeInterfaceSpeed = ChannelInterface.DowngradeInterfaceSpeed;
|
||||
PortData->InterruptObject = ChannelInterface.InterruptObject;
|
||||
PortData->AbortChannel = ChannelInterface.AbortChannel;
|
||||
PortData->ResetChannel = ChannelInterface.ResetChannel;
|
||||
PortData->EnumerateChannel = ChannelInterface.EnumerateChannel;
|
||||
PortData->IdentifyDevice = ChannelInterface.IdentifyDevice;
|
||||
PortData->SetTransferMode = ChannelInterface.SetTransferMode;
|
||||
PortData->AllocateSlot = ChannelInterface.AllocateSlot;
|
||||
PortData->PreparePrdTable = ChannelInterface.PreparePrdTable;
|
||||
PortData->PrepareIo = ChannelInterface.PrepareIo;
|
||||
PortData->StartIo = ChannelInterface.StartIo;
|
||||
PortData->MaxTargetId = ChannelInterface.MaxTargetId;
|
||||
PortData->MaximumTransferLength = ChannelInterface.MaximumTransferLength;
|
||||
PortData->MaximumPhysicalPages = ChannelInterface.MaximumPhysicalPages;
|
||||
PortData->QueueDepth = ChannelInterface.QueueDepth;
|
||||
PortData->PortNumber = ChannelInterface.Channel;
|
||||
PortData->DmaAdapter = ChannelInterface.DmaAdapter;
|
||||
PortData->ChannelObject = ChannelInterface.ChannelObject;
|
||||
|
||||
PortData->InterruptFlags = PORT_INT_FLAG_IS_IO_ACTIVE;
|
||||
PortData->FreeSlotsBitmap =
|
||||
PortData->MaxSlotsBitmap = NUM_TO_BITMAP(PortData->QueueDepth);
|
||||
/* We need the slot numbers to start from zero */
|
||||
PortData->LastUsedSlot = RTL_BITS_OF(ULONG) - 1;
|
||||
|
||||
if (!(ChannelInterface.TransferModeSupported & ~PIO_ALL))
|
||||
PortData->PortFlags |= PORT_FLAG_PIO_ONLY;
|
||||
|
||||
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_VIA_DMA)
|
||||
PortData->PortFlags |= PORT_FLAG_PIO_VIA_DMA;
|
||||
|
||||
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_NCQ)
|
||||
PortData->PortFlags |= PORT_FLAG_NCQ;
|
||||
|
||||
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_IS_AHCI)
|
||||
PortData->PortFlags |= PORT_FLAG_IS_AHCI;
|
||||
|
||||
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_IS_EXTERNAL)
|
||||
PortData->PortFlags |= PORT_FLAG_IS_EXTERNAL;
|
||||
|
||||
if (ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_FOR_LBA48_XFER)
|
||||
PortData->PortFlags |= PORT_FLAG_PIO_FOR_LBA48_XFER;
|
||||
|
||||
if (ChannelInterface.HwSyncObject)
|
||||
{
|
||||
PortData->PortFlags |= PORT_FLAG_IS_SIMPLEX;
|
||||
PortData->HwSyncObject = ChannelInterface.HwSyncObject;
|
||||
}
|
||||
|
||||
/* Reserve PIO memory resources early. Storage drivers should not fail paging I/O operations */
|
||||
if (!(ChannelInterface.Flags & ATA_CHANNEL_FLAG_PIO_VIA_DMA) && !PortData->ReservedVaSpace)
|
||||
{
|
||||
PortData->ReservedVaSpace = MmAllocateMappingAddress(ATA_RESERVED_PAGES * PAGE_SIZE,
|
||||
ATAPORT_TAG);
|
||||
}
|
||||
|
||||
Status = AtaFdoAllocateLocalBuffer(PortData);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("CH %lu: Failed to allocate local buffer 0x%lx\n", PortData->PortNumber, Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
InitializeListHead(&PortData->PortQueueList);
|
||||
KeInitializeEvent(&PortData->QueueStoppedEvent, NotificationEvent, FALSE);
|
||||
|
||||
KeInitializeDpc(&PortData->Worker.Dpc, AtaPortWorkerSignalDpc, PortData);
|
||||
KeInitializeDpc(&PortData->Worker.NotificationDpc, AtaStorageNotificationlDpc, PortData);
|
||||
KeInitializeSpinLock(&PortData->Worker.Lock);
|
||||
KeInitializeEvent(&PortData->Worker.EnumerationEvent, NotificationEvent, FALSE);
|
||||
|
||||
KeInitializeEvent(&PortData->Worker.CompletionEvent, NotificationEvent, FALSE);
|
||||
PortData->Worker.InternalRequest.Complete = AtaPortCompleteInternalRequest;
|
||||
#if DBG
|
||||
PortData->Worker.InternalRequest.Signature = ATA_DEVICE_REQUEST_SIGNATURE;
|
||||
#endif
|
||||
|
||||
Status = AtaFdoCreatePortThread(PortData);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("CH %lu: Failed to create port thread 0x%lx\n", PortData->PortNumber, Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = AtaFdoCreateSymLinks(ChanExt);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("CH %lu: Failed to create symbolic links 0x%lx\n", PortData->PortNumber, Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
AtaSetPortRegistryKey(ChanExt, DD_ATA_REG_MAX_TARGET_ID, PortData->MaxTargetId);
|
||||
|
||||
Status = IoRegisterDeviceInterface(ChanExt->Common.Self,
|
||||
&GUID_DEVINTERFACE_STORAGEPORT,
|
||||
NULL,
|
||||
&ChanExt->StorageInterfaceName);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
INFO("InterfaceName: '%wZ'\n", &ChanExt->StorageInterfaceName);
|
||||
|
||||
Status = IoSetDeviceInterfaceState(&ChanExt->StorageInterfaceName, TRUE);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
RtlFreeUnicodeString(&ChanExt->StorageInterfaceName);
|
||||
ChanExt->StorageInterfaceName.Buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*ChannelInterface.PortContext = PortData;
|
||||
*ChannelInterface.PortNotification = AtaPortNotification;
|
||||
*ChannelInterface.Slots = PortData->Slots;
|
||||
|
||||
Status = IoInitializeTimer(ChanExt->Common.Self, AtaPortIoTimer, PortData);
|
||||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
|
||||
IoStartTimer(ChanExt->Common.Self);
|
||||
PortData->PortFlags |= PORT_FLAG_IO_TIMER_ACTIVE;
|
||||
|
||||
Status = PortData->AttachChannel(PortData->ChannelContext, TRUE);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("CH %lu: Failed to attach channel %lx\n", PortData->PortNumber, Status);
|
||||
return Status;
|
||||
}
|
||||
PortData->PortFlags |= PORT_FLAG_CHANNEL_ATTACHED;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
VOID
|
||||
AtaFdoDetachChannel(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt)
|
||||
{
|
||||
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (PortData->AttachChannel)
|
||||
{
|
||||
PortData->AttachChannel(PortData->ChannelContext, FALSE);
|
||||
PortData->AttachChannel = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoStopDevice(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
AtaFdoDetachChannel(ChanExt);
|
||||
|
||||
if (ChanExt->StorageInterfaceName.Buffer)
|
||||
IoSetDeviceInterfaceState(&ChanExt->StorageInterfaceName, FALSE);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoRemoveDevice(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ BOOLEAN FinalRemove)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (ChanExt->StorageInterfaceName.Buffer)
|
||||
{
|
||||
IoSetDeviceInterfaceState(&ChanExt->StorageInterfaceName, FALSE);
|
||||
|
||||
RtlFreeUnicodeString(&ChanExt->StorageInterfaceName);
|
||||
ChanExt->StorageInterfaceName.Buffer = NULL;
|
||||
}
|
||||
|
||||
AtaFdoDestroyPortThread(ChanExt);
|
||||
|
||||
if (ChanExt->PortData.PortFlags & PORT_FLAG_IO_TIMER_ACTIVE)
|
||||
{
|
||||
IoStopTimer(ChanExt->Common.Self);
|
||||
ChanExt->PortData.PortFlags &= ~PORT_FLAG_IO_TIMER_ACTIVE;
|
||||
}
|
||||
|
||||
if (ChanExt->PortData.PortFlags & PORT_FLAG_CHANNEL_ATTACHED)
|
||||
{
|
||||
AtaFdoDetachChannel(ChanExt);
|
||||
ChanExt->PortData.PortFlags &= ~PORT_FLAG_CHANNEL_ATTACHED;
|
||||
}
|
||||
|
||||
if (FinalRemove)
|
||||
{
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
|
||||
IoReleaseRemoveLockAndWait(&ChanExt->Common.RemoveLock, Irp);
|
||||
|
||||
AtaScsiAddress.AsULONG = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
|
||||
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, TRUE, NULL);
|
||||
if (!DevExt)
|
||||
break;
|
||||
|
||||
AtaReqFlushDeviceQueue(&DevExt->Device);
|
||||
AtaPdoFreeDevice(DevExt);
|
||||
}
|
||||
|
||||
AtaFdoRemoveSymLinks(ChanExt);
|
||||
|
||||
if (ChanExt->PortData.LocalBuffer)
|
||||
{
|
||||
MmFreeContiguousMemorySpecifyCache(ChanExt->PortData.LocalBuffer,
|
||||
ATA_LOCAL_BUFFER_SIZE,
|
||||
MmNonCached);
|
||||
ChanExt->PortData.LocalBuffer = NULL;
|
||||
}
|
||||
|
||||
if (ChanExt->PortData.ReservedVaSpace)
|
||||
{
|
||||
MmFreeMappingAddress(ChanExt->PortData.ReservedVaSpace, ATAPORT_TAG);
|
||||
ChanExt->PortData.ReservedVaSpace = NULL;
|
||||
}
|
||||
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
Status = IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
||||
|
||||
IoDetachDevice(ChanExt->Common.LowerDeviceObject);
|
||||
IoDeleteDevice(ChanExt->Common.Self);
|
||||
}
|
||||
else
|
||||
{
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
Status = IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
||||
|
||||
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoPnp(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
INFO("(%p, %p) Ch.%lu %s\n",
|
||||
ChanExt->Common.Self,
|
||||
Irp,
|
||||
ChanExt->DeviceObjectNumber,
|
||||
GetIRPMinorFunctionString(IoGetCurrentIrpStackLocation(Irp)->MinorFunction));
|
||||
|
||||
Status = IoAcquireRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
switch (IoStack->MinorFunction)
|
||||
{
|
||||
case IRP_MN_START_DEVICE:
|
||||
{
|
||||
if (!NT_VERIFY(IoForwardIrpSynchronously(ChanExt->Common.LowerDeviceObject, Irp)))
|
||||
{
|
||||
Status = STATUS_UNSUCCESSFUL;
|
||||
goto CompleteIrp;
|
||||
}
|
||||
Status = Irp->IoStatus.Status;
|
||||
if (!NT_SUCCESS(Status))
|
||||
goto CompleteIrp;
|
||||
|
||||
Status = AtaFdoStartDevice(ChanExt,
|
||||
IoStack->Parameters.
|
||||
StartDevice.AllocatedResourcesTranslated);
|
||||
goto CompleteIrp;
|
||||
}
|
||||
|
||||
case IRP_MN_STOP_DEVICE:
|
||||
Status = AtaFdoStopDevice(ChanExt, Irp);
|
||||
break;
|
||||
|
||||
case IRP_MN_REMOVE_DEVICE:
|
||||
case IRP_MN_SURPRISE_REMOVAL:
|
||||
return AtaFdoRemoveDevice(ChanExt,
|
||||
Irp,
|
||||
(IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE));
|
||||
|
||||
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
||||
Status = AtaPnpQueryPnpDeviceState(&ChanExt->Common, Irp);
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||||
{
|
||||
if (IoStack->Parameters.QueryDeviceRelations.Type != BusRelations)
|
||||
break;
|
||||
|
||||
Status = AtaFdoQueryBusRelations(ChanExt, Irp);
|
||||
if (!NT_SUCCESS(Status))
|
||||
goto CompleteIrp;
|
||||
|
||||
Irp->IoStatus.Status = Status;
|
||||
break;
|
||||
}
|
||||
|
||||
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
|
||||
Status = AtaPnpQueryDeviceUsageNotification(&ChanExt->Common, Irp);
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_STOP_DEVICE:
|
||||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||||
case IRP_MN_CANCEL_STOP_DEVICE:
|
||||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
Status = IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
||||
|
||||
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
|
||||
CompleteIrp:
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
DECLSPEC_NOINLINE_FROM_PAGED
|
||||
PATAPORT_DEVICE_EXTENSION
|
||||
AtaFdoFindDeviceByPath(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ ATA_SCSI_ADDRESS AtaScsiAddress,
|
||||
_In_ PVOID ReferenceTag)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt, Result = NULL;
|
||||
PSINGLE_LIST_ENTRY Entry;
|
||||
KIRQL OldLevel;
|
||||
NTSTATUS Status;
|
||||
|
||||
KeAcquireSpinLock(&ChanExt->PdoListLock, &OldLevel);
|
||||
|
||||
for (Entry = ChanExt->PdoList.Next; Entry != NULL; Entry = Entry->Next)
|
||||
{
|
||||
DevExt = CONTAINING_RECORD(Entry, ATAPORT_DEVICE_EXTENSION, ListEntry);
|
||||
|
||||
if (DevExt->Device.AtaScsiAddress.AsULONG != AtaScsiAddress.AsULONG)
|
||||
continue;
|
||||
|
||||
if (DevExt->ReportedMissing || DevExt->RemovalPending)
|
||||
continue;
|
||||
|
||||
if (ReferenceTag)
|
||||
{
|
||||
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, ReferenceTag);
|
||||
if (!NT_SUCCESS(Status))
|
||||
break;
|
||||
}
|
||||
|
||||
Result = DevExt;
|
||||
break;
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&ChanExt->PdoListLock, OldLevel);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
DECLSPEC_NOINLINE_FROM_PAGED
|
||||
PATAPORT_DEVICE_EXTENSION
|
||||
AtaFdoFindNextDeviceByPath(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_Inout_ PATA_SCSI_ADDRESS AtaScsiAddress,
|
||||
_In_ BOOLEAN SearchRemoveDev,
|
||||
_In_ PVOID ReferenceTag)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt, Result = NULL;
|
||||
PSINGLE_LIST_ENTRY Entry;
|
||||
KIRQL OldLevel;
|
||||
NTSTATUS Status;
|
||||
|
||||
KeAcquireSpinLock(&ChanExt->PdoListLock, &OldLevel);
|
||||
|
||||
for (Entry = ChanExt->PdoList.Next; Entry != NULL; Entry = Entry->Next)
|
||||
{
|
||||
DevExt = CONTAINING_RECORD(Entry, ATAPORT_DEVICE_EXTENSION, ListEntry);
|
||||
|
||||
if (DevExt->Device.AtaScsiAddress.AsULONG <= AtaScsiAddress->AsULONG)
|
||||
continue;
|
||||
|
||||
if (DevExt->ReportedMissing)
|
||||
continue;
|
||||
|
||||
if (!SearchRemoveDev && DevExt->RemovalPending)
|
||||
continue;
|
||||
|
||||
*AtaScsiAddress = DevExt->Device.AtaScsiAddress;
|
||||
|
||||
if (ReferenceTag)
|
||||
{
|
||||
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, ReferenceTag);
|
||||
if (!NT_SUCCESS(Status))
|
||||
continue;
|
||||
}
|
||||
Result = DevExt;
|
||||
break;
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&ChanExt->PdoListLock, OldLevel);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
DECLSPEC_NOINLINE_FROM_PAGED
|
||||
VOID
|
||||
AtaFdoDeviceListInsert(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ BOOLEAN DoInsert)
|
||||
{
|
||||
PSINGLE_LIST_ENTRY Entry, PrevEntry;
|
||||
KIRQL OldLevel;
|
||||
ULONG Address = DevExt->Device.AtaScsiAddress.AsULONG;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
KeAcquireSpinLock(&ChanExt->PdoListLock, &OldLevel);
|
||||
|
||||
for (Entry = ChanExt->PdoList.Next, PrevEntry = NULL;
|
||||
Entry != NULL;
|
||||
Entry = Entry->Next)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION CurrentDevExt;
|
||||
|
||||
CurrentDevExt = CONTAINING_RECORD(Entry, ATAPORT_DEVICE_EXTENSION, ListEntry);
|
||||
|
||||
if (DoInsert)
|
||||
{
|
||||
if (CurrentDevExt->Device.AtaScsiAddress.AsULONG > Address)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CurrentDevExt->Device.AtaScsiAddress.AsULONG == Address)
|
||||
break;
|
||||
}
|
||||
|
||||
PrevEntry = Entry;
|
||||
}
|
||||
|
||||
/* The device list is ordered by SCSI address (Path:Target:Lun), smallest first */
|
||||
if (PrevEntry)
|
||||
{
|
||||
/* Before the current entry */
|
||||
if (DoInsert)
|
||||
{
|
||||
DevExt->ListEntry.Next = PrevEntry->Next;
|
||||
PrevEntry->Next = &DevExt->ListEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrevEntry->Next = DevExt->ListEntry.Next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* In the beginning */
|
||||
if (DoInsert)
|
||||
PushEntryList(&ChanExt->PdoList, &DevExt->ListEntry);
|
||||
else
|
||||
PopEntryList(&DevExt->ListEntry);
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&ChanExt->PdoListLock, OldLevel);
|
||||
}
|
||||
940
drivers/storage/ide/atapi/ioctl.c
Normal file
940
drivers/storage/ide/atapi/ioctl.c
Normal file
@@ -0,0 +1,940 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: I/O control handling
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
#define AtaVerifyInBuffer(IoStack, Length) \
|
||||
((IoStack)->Parameters.DeviceIoControl.InputBufferLength >= (Length))
|
||||
|
||||
#define AtaVerifyOutBuffer(IoStack, Length) \
|
||||
((IoStack)->Parameters.DeviceIoControl.OutputBufferLength >= (Length))
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
BOOLEAN
|
||||
AtaCheckPropertyQuery(
|
||||
_Inout_ PIRP Irp,
|
||||
_In_ ULONG DescriptorSize,
|
||||
_Out_ NTSTATUS* Status)
|
||||
{
|
||||
PSTORAGE_PROPERTY_QUERY PropertyQuery = Irp->AssociatedIrp.SystemBuffer;
|
||||
PSTORAGE_DESCRIPTOR_HEADER DescriptorHeader;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
if (!AtaVerifyInBuffer(IoStack, sizeof(*PropertyQuery)))
|
||||
{
|
||||
*Status = STATUS_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check the type of a property query */
|
||||
if (PropertyQuery->QueryType != PropertyStandardQuery &&
|
||||
PropertyQuery->QueryType != PropertyExistsQuery)
|
||||
{
|
||||
*Status = STATUS_NOT_SUPPORTED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* The requested property is supported */
|
||||
if (PropertyQuery->QueryType == PropertyExistsQuery)
|
||||
{
|
||||
*Status = STATUS_SUCCESS;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!AtaVerifyOutBuffer(IoStack, sizeof(*DescriptorHeader)))
|
||||
{
|
||||
*Status = STATUS_INFO_LENGTH_MISMATCH;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Caller can determine required size based upon DescriptorHeader */
|
||||
if (!AtaVerifyOutBuffer(IoStack, DescriptorSize))
|
||||
{
|
||||
DescriptorHeader = (PSTORAGE_DESCRIPTOR_HEADER)PropertyQuery;
|
||||
DescriptorHeader->Version = DescriptorSize;
|
||||
DescriptorHeader->Size = DescriptorSize;
|
||||
|
||||
Irp->IoStatus.Information = sizeof(*DescriptorHeader);
|
||||
|
||||
*Status = STATUS_SUCCESS;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryStorageDeviceProperty(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PINQUIRYDATA InquiryData = &DevExt->InquiryData;
|
||||
PSTORAGE_DEVICE_DESCRIPTOR DeviceDescriptor;
|
||||
NTSTATUS Status;
|
||||
ULONG DescriptorSize, RawPropertiesLength, Length1, Length2, Length3;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
Length1 = (ULONG)strlen(DevExt->FriendlyName) + sizeof(ANSI_NULL);
|
||||
Length2 = (ULONG)strlen(DevExt->RevisionNumber) + sizeof(ANSI_NULL);
|
||||
Length3 = (ULONG)strlen(DevExt->SerialNumber) + sizeof(ANSI_NULL);
|
||||
RawPropertiesLength = Length1 + Length2 + Length3;
|
||||
|
||||
DescriptorSize = FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR, RawDeviceProperties) +
|
||||
RawPropertiesLength;
|
||||
|
||||
if (!AtaCheckPropertyQuery(Irp, DescriptorSize, &Status))
|
||||
return Status;
|
||||
|
||||
DeviceDescriptor = Irp->AssociatedIrp.SystemBuffer;
|
||||
DeviceDescriptor->RawPropertiesLength = RawPropertiesLength;
|
||||
DeviceDescriptor->Version = sizeof(*DeviceDescriptor);
|
||||
DeviceDescriptor->Size = DescriptorSize;
|
||||
DeviceDescriptor->DeviceType = InquiryData->DeviceType;
|
||||
DeviceDescriptor->DeviceTypeModifier = InquiryData->DeviceTypeModifier;
|
||||
DeviceDescriptor->RemovableMedia = InquiryData->RemovableMedia;
|
||||
DeviceDescriptor->CommandQueueing = FALSE; // Disable request tagging
|
||||
if (DevExt->Device.PortData->PortFlags & PORT_FLAG_IS_AHCI)
|
||||
DeviceDescriptor->BusType = BusTypeSata;
|
||||
else
|
||||
DeviceDescriptor->BusType = BusTypeAta;
|
||||
|
||||
/* Property 1: The vendor ID. We return a NULL string here */
|
||||
DeviceDescriptor->VendorIdOffset = 0;
|
||||
|
||||
/* Property 2: The product ID */
|
||||
DeviceDescriptor->ProductIdOffset =
|
||||
FIELD_OFFSET(STORAGE_DEVICE_DESCRIPTOR, RawDeviceProperties);
|
||||
RtlCopyMemory((PUCHAR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->ProductIdOffset),
|
||||
DevExt->FriendlyName,
|
||||
Length1);
|
||||
|
||||
/* Property 3: The product revision */
|
||||
DeviceDescriptor->ProductRevisionOffset = DeviceDescriptor->ProductIdOffset + Length1;
|
||||
RtlCopyMemory((PUCHAR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->ProductRevisionOffset),
|
||||
DevExt->RevisionNumber,
|
||||
Length2);
|
||||
|
||||
/* Property 4: The serial number */
|
||||
DeviceDescriptor->SerialNumberOffset = DeviceDescriptor->ProductRevisionOffset + Length2;
|
||||
RtlCopyMemory((PUCHAR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->SerialNumberOffset),
|
||||
DevExt->SerialNumber,
|
||||
Length3);
|
||||
|
||||
Irp->IoStatus.Information = DescriptorSize;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryStorageAccessAlignmentProperty(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR AccessAlignmentDescriptor;
|
||||
ULONG LogicalSectorsPerPhysicalSector, Dummy;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
if (!AtaCheckPropertyQuery(Irp, sizeof(*AccessAlignmentDescriptor), &Status))
|
||||
return Status;
|
||||
|
||||
AccessAlignmentDescriptor = Irp->AssociatedIrp.SystemBuffer;
|
||||
AccessAlignmentDescriptor->Version = sizeof(*AccessAlignmentDescriptor);
|
||||
AccessAlignmentDescriptor->Size = sizeof(*AccessAlignmentDescriptor);
|
||||
AccessAlignmentDescriptor->BytesPerCacheLine = 0;
|
||||
AccessAlignmentDescriptor->BytesOffsetForCacheAlignment = 0;
|
||||
AccessAlignmentDescriptor->BytesPerLogicalSector = DevExt->Device.SectorSize;
|
||||
|
||||
LogicalSectorsPerPhysicalSector =
|
||||
AtaDevLogicalSectorsPerPhysicalSector(&DevExt->IdentifyDeviceData, &Dummy);
|
||||
|
||||
AccessAlignmentDescriptor->BytesPerPhysicalSector =
|
||||
DevExt->Device.SectorSize * LogicalSectorsPerPhysicalSector;
|
||||
|
||||
AccessAlignmentDescriptor->BytesOffsetForSectorAlignment =
|
||||
DevExt->Device.SectorSize * AtaDevLogicalSectorAlignment(&DevExt->IdentifyDeviceData);
|
||||
|
||||
Irp->IoStatus.Information = sizeof(*AccessAlignmentDescriptor);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryStorageDeviceSeekPenaltyProperty(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PDEVICE_SEEK_PENALTY_DESCRIPTOR PenaltyDescriptor;
|
||||
NTSTATUS Status;
|
||||
BOOLEAN IncursSeekPenalty;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!AtaCheckPropertyQuery(Irp, sizeof(*PenaltyDescriptor), &Status))
|
||||
return Status;
|
||||
|
||||
if (AtaDevIsRotatingDevice(&DevExt->IdentifyDeviceData))
|
||||
IncursSeekPenalty = TRUE;
|
||||
else if (AtaDevIsSsd(&DevExt->IdentifyDeviceData))
|
||||
IncursSeekPenalty = FALSE;
|
||||
else
|
||||
return STATUS_UNSUCCESSFUL; // Undetermined
|
||||
|
||||
PenaltyDescriptor = Irp->AssociatedIrp.SystemBuffer;
|
||||
PenaltyDescriptor->Version = sizeof(*PenaltyDescriptor);
|
||||
PenaltyDescriptor->Size = sizeof(*PenaltyDescriptor);
|
||||
PenaltyDescriptor->IncursSeekPenalty = IncursSeekPenalty;
|
||||
|
||||
Irp->IoStatus.Information = sizeof(*PenaltyDescriptor);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryStorageDeviceTrimProperty(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PDEVICE_TRIM_DESCRIPTOR TrimDescriptor;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
if (!AtaCheckPropertyQuery(Irp, sizeof(*TrimDescriptor), &Status))
|
||||
return Status;
|
||||
|
||||
TrimDescriptor = Irp->AssociatedIrp.SystemBuffer;
|
||||
TrimDescriptor->Version = sizeof(*TrimDescriptor);
|
||||
TrimDescriptor->Size = sizeof(*TrimDescriptor);
|
||||
TrimDescriptor->TrimEnabled = AtaDevHasTrimFunction(&DevExt->IdentifyDeviceData);
|
||||
|
||||
Irp->IoStatus.Information = sizeof(*TrimDescriptor);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoHandleStorageQueryProperty(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack,
|
||||
_Out_ PBOOLEAN ForwardToFdo)
|
||||
{
|
||||
PSTORAGE_PROPERTY_QUERY PropertyQuery;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!AtaVerifyInBuffer(IoStack, sizeof(*PropertyQuery)))
|
||||
{
|
||||
ERR("Buffer too small\n");
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
PropertyQuery = Irp->AssociatedIrp.SystemBuffer;
|
||||
switch (PropertyQuery->PropertyId)
|
||||
{
|
||||
case StorageDeviceProperty:
|
||||
Status = AtaPdoQueryStorageDeviceProperty(DevExt, Irp);
|
||||
break;
|
||||
|
||||
case StorageAccessAlignmentProperty:
|
||||
Status = AtaPdoQueryStorageAccessAlignmentProperty(DevExt, Irp);
|
||||
break;
|
||||
|
||||
case StorageDeviceSeekPenaltyProperty:
|
||||
Status = AtaPdoQueryStorageDeviceSeekPenaltyProperty(DevExt, Irp);
|
||||
break;
|
||||
|
||||
case StorageDeviceTrimProperty:
|
||||
Status = AtaPdoQueryStorageDeviceTrimProperty(DevExt, Irp);
|
||||
break;
|
||||
|
||||
default:
|
||||
*ForwardToFdo = TRUE;
|
||||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
*ForwardToFdo = FALSE;
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoHandleGetScsiAddress(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt;
|
||||
PSCSI_ADDRESS ScsiAddress;
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!AtaVerifyOutBuffer(IoStack, sizeof(*ScsiAddress)))
|
||||
{
|
||||
ERR("Buffer too small\n");
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
ChanExt = DevExt->Common.FdoExt;
|
||||
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
|
||||
|
||||
ScsiAddress = Irp->AssociatedIrp.SystemBuffer;
|
||||
ScsiAddress->Length = sizeof(*ScsiAddress);
|
||||
ScsiAddress->PortNumber = ChanExt->ScsiPortNumber;
|
||||
ScsiAddress->PathId = AtaScsiAddress.PathId;
|
||||
ScsiAddress->TargetId = AtaScsiAddress.TargetId;
|
||||
ScsiAddress->Lun = AtaScsiAddress.Lun;
|
||||
|
||||
Irp->IoStatus.Information = sizeof(*ScsiAddress);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoSendHbaControl(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PSRB_IO_CONTROL SrbControl,
|
||||
_In_ ULONG BufferSize)
|
||||
{
|
||||
PSCSI_REQUEST_BLOCK Srb;
|
||||
IO_STATUS_BLOCK IoStatusBlock;
|
||||
PIRP Irp;
|
||||
KEVENT Event;
|
||||
NTSTATUS Status;
|
||||
LARGE_INTEGER LargeInt;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
Srb = ExAllocatePoolZero(NonPagedPool, sizeof(*Srb), ATAPORT_TAG);
|
||||
if (!Srb)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||||
|
||||
LargeInt.QuadPart = 1; // For compatibility only
|
||||
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SCSI,
|
||||
DevExt->Common.Self,
|
||||
SrbControl,
|
||||
BufferSize,
|
||||
&LargeInt,
|
||||
&Event,
|
||||
&IoStatusBlock);
|
||||
if (!Irp)
|
||||
{
|
||||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Srb->OriginalRequest = Irp;
|
||||
|
||||
Srb->Function = SRB_FUNCTION_IO_CONTROL;
|
||||
Srb->Length = sizeof(*Srb);
|
||||
|
||||
Srb->TargetId = DevExt->Device.AtaScsiAddress.TargetId;
|
||||
Srb->Lun = DevExt->Device.AtaScsiAddress.Lun;
|
||||
Srb->PathId = DevExt->Device.AtaScsiAddress.PathId;
|
||||
|
||||
Srb->TimeOutValue = SrbControl->Timeout;
|
||||
|
||||
Srb->SrbFlags = SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_DATA_IN;
|
||||
|
||||
Srb->DataBuffer = SrbControl;
|
||||
Srb->DataTransferLength = BufferSize;
|
||||
|
||||
IoStack = IoGetNextIrpStackLocation(Irp);
|
||||
IoStack->Parameters.Scsi.Srb = Srb;
|
||||
|
||||
Status = IoCallDriver(DevExt->Common.Self, Irp);
|
||||
if (Status == STATUS_PENDING)
|
||||
{
|
||||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
||||
Status = IoStatusBlock.Status;
|
||||
}
|
||||
|
||||
Cleanup:
|
||||
ExFreePoolWithTag(Srb, ATAPORT_TAG);
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoHandleStorageManageDataSetAttributes(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
// TODO: Implement
|
||||
return Irp->IoStatus.Status;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoHandleScsiMiniport(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_Inout_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PSRB_IO_CONTROL SrbControl;
|
||||
NTSTATUS Status;
|
||||
ULONG CmdBufferSize, BufferSize;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!AtaVerifyInBuffer(IoStack, sizeof(*SrbControl)))
|
||||
{
|
||||
ERR("Buffer too small\n");
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
SrbControl = Irp->AssociatedIrp.SystemBuffer;
|
||||
if (SrbControl->HeaderLength != sizeof(*SrbControl))
|
||||
{
|
||||
ERR("Unknown structure size %lu\n", SrbControl->HeaderLength);
|
||||
return STATUS_REVISION_MISMATCH;
|
||||
}
|
||||
|
||||
Status = RtlULongAdd(SrbControl->Length, sizeof(*SrbControl), &BufferSize);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("Too large buffer 0x%lx\n", SrbControl->Length);
|
||||
return Status;
|
||||
}
|
||||
|
||||
CmdBufferSize = IoStack->Parameters.DeviceIoControl.InputBufferLength;
|
||||
CmdBufferSize = max(CmdBufferSize, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
|
||||
if (CmdBufferSize < BufferSize)
|
||||
{
|
||||
ERR("Cmd buffer too small\n");
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (RtlEqualMemory(SrbControl->Signature, "SCSIDISK", sizeof("SCSIDISK") - 1))
|
||||
Status = AtaPdoSendHbaControl(DevExt, SrbControl, BufferSize);
|
||||
else
|
||||
Status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoHandleAtaPassthrough(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
return SptiHandleAtaPassthru(DevExt->Common.Self,
|
||||
Irp,
|
||||
ChanExt->PortData.MaximumTransferLength,
|
||||
ChanExt->PortData.MaximumPhysicalPages);
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoHandleScsiPassthrough(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_Inout_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/* Skip requests that bypassed the class driver. See also cdrom!RequestHandleScsiPassThrough */
|
||||
if ((IoStack->MinorFunction == 0) && DevExt->DeviceClaimed)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
ChanExt = DevExt->Common.FdoExt;
|
||||
|
||||
return SptiHandleScsiPassthru(DevExt->Common.Self,
|
||||
Irp,
|
||||
ChanExt->PortData.MaximumTransferLength,
|
||||
ChanExt->PortData.MaximumPhysicalPages);
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaPdoDeviceControl(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
NTSTATUS Status;
|
||||
BOOLEAN ForwardToFdo = FALSE;
|
||||
|
||||
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
Irp->IoStatus.Information = 0;
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
|
||||
{
|
||||
case IOCTL_STORAGE_QUERY_PROPERTY:
|
||||
Status = AtaPdoHandleStorageQueryProperty(DevExt, Irp, IoStack, &ForwardToFdo);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_GET_ADDRESS:
|
||||
Status = AtaPdoHandleGetScsiAddress(DevExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_MINIPORT:
|
||||
Status = AtaPdoHandleScsiMiniport(DevExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES:
|
||||
Status = AtaPdoHandleStorageManageDataSetAttributes(DevExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_ATA_PASS_THROUGH:
|
||||
case IOCTL_ATA_PASS_THROUGH_DIRECT:
|
||||
Status = AtaPdoHandleAtaPassthrough(DevExt, Irp);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_PASS_THROUGH:
|
||||
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
|
||||
Status = AtaPdoHandleScsiPassthrough(DevExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_GET_CAPABILITIES:
|
||||
case IOCTL_SCSI_GET_INQUIRY_DATA:
|
||||
ForwardToFdo = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ForwardToFdo)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
|
||||
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
Status = IoCallDriver(ChanExt->Common.Self, Irp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
}
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoQueryStorageAdapterProperty(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PSTORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!AtaCheckPropertyQuery(Irp, sizeof(*AdapterDescriptor), &Status))
|
||||
return Status;
|
||||
|
||||
AdapterDescriptor = Irp->AssociatedIrp.SystemBuffer;
|
||||
|
||||
/*
|
||||
* This structure has to be zeroed out first
|
||||
* in order to not confuse the upper storage class drivers.
|
||||
* Also the new version of the descriptor structure (NT6.2+)
|
||||
* has two extra fields instead of the unnamed "padding" fields.
|
||||
*/
|
||||
RtlZeroMemory(AdapterDescriptor, sizeof(*AdapterDescriptor));
|
||||
|
||||
AdapterDescriptor->Version = sizeof(*AdapterDescriptor);
|
||||
AdapterDescriptor->Size = sizeof(*AdapterDescriptor);
|
||||
AdapterDescriptor->MaximumTransferLength = ChanExt->PortData.MaximumTransferLength;
|
||||
AdapterDescriptor->MaximumPhysicalPages = ChanExt->PortData.MaximumPhysicalPages;
|
||||
AdapterDescriptor->AlignmentMask = ChanExt->Common.Self->AlignmentRequirement;
|
||||
AdapterDescriptor->AdapterUsesPio = !!(ChanExt->PortData.PortFlags && PORT_FLAG_PIO_ONLY);
|
||||
AdapterDescriptor->AdapterScansDown = FALSE;
|
||||
AdapterDescriptor->CommandQueueing = FALSE; // Disable request tagging
|
||||
AdapterDescriptor->AcceleratedTransfer = FALSE;
|
||||
if (ChanExt->PortData.PortFlags & PORT_FLAG_IS_AHCI)
|
||||
AdapterDescriptor->BusType = BusTypeSata;
|
||||
else
|
||||
AdapterDescriptor->BusType = BusTypeAta;
|
||||
AdapterDescriptor->BusMajorVersion = 1;
|
||||
AdapterDescriptor->BusMinorVersion = 0;
|
||||
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
||||
AdapterDescriptor->SrbType = SRB_TYPE_SCSI_REQUEST_BLOCK;
|
||||
AdapterDescriptor->AddressType = STORAGE_ADDRESS_TYPE_BTL8;
|
||||
#endif
|
||||
|
||||
Irp->IoStatus.Information = sizeof(*AdapterDescriptor);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoHandleStorageQueryProperty(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PSTORAGE_PROPERTY_QUERY PropertyQuery;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!AtaVerifyInBuffer(IoStack, sizeof(*PropertyQuery)))
|
||||
{
|
||||
ERR("Buffer too small\n");
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
PropertyQuery = Irp->AssociatedIrp.SystemBuffer;
|
||||
switch (PropertyQuery->PropertyId)
|
||||
{
|
||||
case StorageAdapterProperty:
|
||||
Status = AtaFdoQueryStorageAdapterProperty(ChanExt, Irp);
|
||||
break;
|
||||
|
||||
default:
|
||||
Status = STATUS_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoHandleGetScsiCapabilities(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PIO_SCSI_CAPABILITIES Capabilities;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!AtaVerifyOutBuffer(IoStack, sizeof(*Capabilities)))
|
||||
{
|
||||
ERR("Buffer too small\n");
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
Capabilities = Irp->AssociatedIrp.SystemBuffer;
|
||||
Capabilities->Length = sizeof(*Capabilities);
|
||||
Capabilities->MaximumTransferLength = ChanExt->PortData.MaximumTransferLength;
|
||||
Capabilities->MaximumPhysicalPages = ChanExt->PortData.MaximumPhysicalPages;
|
||||
Capabilities->SupportedAsynchronousEvents = FALSE;
|
||||
Capabilities->AlignmentMask = ChanExt->Common.Self->AlignmentRequirement;
|
||||
Capabilities->TaggedQueuing = FALSE;
|
||||
Capabilities->AdapterScansDown = FALSE;
|
||||
Capabilities->AdapterUsesPio = !!(ChanExt->PortData.PortFlags && PORT_FLAG_PIO_ONLY);
|
||||
|
||||
Irp->IoStatus.Information = sizeof(*Capabilities);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoHandleGetScsiInquiryData(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PSCSI_INQUIRY_DATA ScsiInquiryData;
|
||||
PSCSI_ADAPTER_BUS_INFO ScsiAdapterBusInfo;
|
||||
ULONG PdoCount, EntrySize, TotalSize;
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
PdoCount = 0;
|
||||
AtaScsiAddress.AsULONG = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
|
||||
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, FALSE, NULL);
|
||||
if (!DevExt)
|
||||
break;
|
||||
|
||||
++PdoCount;
|
||||
}
|
||||
|
||||
EntrySize = ALIGN_UP(sizeof(*ScsiInquiryData) - 1 + INQUIRYDATABUFFERSIZE, ULONG);
|
||||
TotalSize = sizeof(*ScsiAdapterBusInfo) + EntrySize * PdoCount;
|
||||
TRACE("Total size %lu\n", TotalSize);
|
||||
|
||||
if (!AtaVerifyOutBuffer(IoStack, TotalSize))
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
|
||||
Irp->IoStatus.Information = TotalSize;
|
||||
|
||||
ScsiAdapterBusInfo = Irp->AssociatedIrp.SystemBuffer;
|
||||
ScsiAdapterBusInfo->NumberOfBuses = 1;
|
||||
ScsiAdapterBusInfo->BusData[0].NumberOfLogicalUnits = 0;
|
||||
ScsiAdapterBusInfo->BusData[0].InitiatorBusId = 0xFF;
|
||||
ScsiAdapterBusInfo->BusData[0].InquiryDataOffset = sizeof(*ScsiAdapterBusInfo);
|
||||
|
||||
ScsiInquiryData = (PSCSI_INQUIRY_DATA)(ScsiAdapterBusInfo + 1);
|
||||
|
||||
AtaScsiAddress.AsULONG = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
PINQUIRYDATA InquiryData;
|
||||
|
||||
DevExt = AtaFdoFindNextDeviceByPath(ChanExt, &AtaScsiAddress, FALSE, Irp);
|
||||
if (!DevExt)
|
||||
break;
|
||||
|
||||
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
|
||||
|
||||
ScsiInquiryData->PathId = AtaScsiAddress.PathId;
|
||||
ScsiInquiryData->TargetId = AtaScsiAddress.TargetId;
|
||||
ScsiInquiryData->Lun = AtaScsiAddress.Lun;
|
||||
ScsiInquiryData->InquiryDataLength = INQUIRYDATABUFFERSIZE;
|
||||
ScsiInquiryData->DeviceClaimed = DevExt->DeviceClaimed;
|
||||
ScsiInquiryData->NextInquiryDataOffset =
|
||||
(ULONG)((ULONG_PTR)ScsiInquiryData + EntrySize - (ULONG_PTR)ScsiAdapterBusInfo);
|
||||
|
||||
RtlCopyMemory(ScsiInquiryData->InquiryData,
|
||||
&DevExt->InquiryData,
|
||||
INQUIRYDATABUFFERSIZE);
|
||||
|
||||
/* This is a legacy IOCTL, limit the standard INQUIRY data to 36 bytes */
|
||||
InquiryData = (PINQUIRYDATA)ScsiInquiryData->InquiryData;
|
||||
InquiryData->AdditionalLength =
|
||||
INQUIRYDATABUFFERSIZE - RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
|
||||
ScsiInquiryData = (PSCSI_INQUIRY_DATA)((ULONG_PTR)ScsiInquiryData + EntrySize);
|
||||
|
||||
if (++ScsiAdapterBusInfo->BusData[0].NumberOfLogicalUnits >= PdoCount)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Terminate the last entry */
|
||||
if (ScsiAdapterBusInfo->BusData[0].NumberOfLogicalUnits != 0)
|
||||
{
|
||||
ScsiInquiryData = ((PSCSI_INQUIRY_DATA)((ULONG_PTR)ScsiInquiryData - EntrySize));
|
||||
ScsiInquiryData->NextInquiryDataOffset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScsiAdapterBusInfo->BusData[0].InquiryDataOffset = 0;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoHandleScsiMiniport(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/* FDO requests are routed directly to 0:0:0 */
|
||||
DevExt = AtaFdoFindDeviceByPath(ChanExt, AtaMarshallScsiAddress(0, 0, 0), Irp);
|
||||
if (!DevExt)
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
|
||||
Status = AtaPdoHandleScsiMiniport(DevExt, Irp, IoStack);
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
DECLSPEC_NOINLINE_FROM_NOT_PAGED
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoHandleScsiPassthrough(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
PSCSI_PASS_THROUGH Spt;
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
NTSTATUS Status;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (!AtaVerifyInBuffer(IoStack, RTL_SIZEOF_THROUGH_FIELD(SCSI_PASS_THROUGH, Lun)))
|
||||
{
|
||||
ERR("Buffer too small\n");
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
Spt = Irp->AssociatedIrp.SystemBuffer;
|
||||
|
||||
DevExt = AtaFdoFindDeviceByPath(ChanExt,
|
||||
AtaMarshallScsiAddress(Spt->PathId, Spt->TargetId, Spt->Lun),
|
||||
Irp);
|
||||
if (!DevExt)
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
|
||||
Status = AtaPdoHandleScsiPassthrough(DevExt, Irp, IoStack);
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
AtaFdoDeviceControl(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
NTSTATUS Status;
|
||||
|
||||
Status = IoAcquireRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
Irp->IoStatus.Information = 0;
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
switch (IoStack->Parameters.DeviceIoControl.IoControlCode)
|
||||
{
|
||||
case IOCTL_STORAGE_QUERY_PROPERTY:
|
||||
Status = AtaFdoHandleStorageQueryProperty(ChanExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_GET_CAPABILITIES:
|
||||
Status = AtaFdoHandleGetScsiCapabilities(ChanExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_GET_INQUIRY_DATA:
|
||||
Status = AtaFdoHandleGetScsiInquiryData(ChanExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_MINIPORT:
|
||||
Status = AtaFdoHandleScsiMiniport(ChanExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_PASS_THROUGH:
|
||||
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
|
||||
Status = AtaFdoHandleScsiPassthrough(ChanExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IOCTL_SCSI_RESCAN_BUS:
|
||||
IoInvalidateDeviceRelations(ChanExt->Pdo, BusRelations);
|
||||
Status = STATUS_SUCCESS;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
Status = IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
||||
|
||||
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
IoReleaseRemoveLock(&ChanExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/*
|
||||
* For storage drivers this dispatch function must be not paged,
|
||||
* because it must be present when it is received unknown IOCTL,
|
||||
* otherwise we risk locking up the whole system.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaDispatchDeviceControl(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
if (IS_FDO(DeviceObject->DeviceExtension))
|
||||
return AtaFdoDeviceControl(DeviceObject->DeviceExtension, Irp);
|
||||
else
|
||||
return AtaPdoDeviceControl(DeviceObject->DeviceExtension, Irp);
|
||||
}
|
||||
798
drivers/storage/ide/atapi/pdo.c
Normal file
798
drivers/storage/ide/atapi/pdo.c
Normal file
@@ -0,0 +1,798 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: Child device object (PDO) dispatch routines
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoStartDevice(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
if (!(DevExt->Device.DeviceFlags & DEVICE_PNP_STARTED))
|
||||
{
|
||||
PATAPORT_PORT_DATA PortData = DevExt->Device.PortData;
|
||||
|
||||
AtaPdoWmiRegistration(DevExt, TRUE);
|
||||
|
||||
/* Update the type of device connected to the port */
|
||||
PortData->SetDeviceData(PortData->ChannelContext,
|
||||
DevExt->Common.Self,
|
||||
&DevExt->IdentifyDeviceData);
|
||||
|
||||
/* Get the ATA initialization commands to restore the boot up defaults */
|
||||
if (!DevExt->GtfDataBuffer)
|
||||
{
|
||||
DevExt->GtfDataBuffer = PortData->GetInitTaskFile(PortData->ChannelContext,
|
||||
DevExt->Common.Self);
|
||||
}
|
||||
|
||||
/* Use the standard power policy for mass storage devices */
|
||||
DevExt->Device.PowerIdleCounter = PoRegisterDeviceForIdleDetection(DevExt->Common.Self,
|
||||
(ULONG)-1,
|
||||
(ULONG)-1,
|
||||
PowerDeviceD3);
|
||||
|
||||
/* Set the new ATA volatile settings */
|
||||
KeClearEvent(&DevExt->Worker.ConfigureEvent);
|
||||
AtaDeviceQueueEvent(DevExt->Device.PortData, DevExt, ACTION_DEVICE_CONFIG);
|
||||
KeWaitForSingleObject(&DevExt->Worker.ConfigureEvent,
|
||||
Executive,
|
||||
KernelMode,
|
||||
FALSE,
|
||||
NULL);
|
||||
|
||||
DevExt->Device.DeviceFlags |= DEVICE_PNP_STARTED;
|
||||
}
|
||||
|
||||
AtaReqThawQueue(DevExt, QUEUE_FLAG_FROZEN_PNP);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoStopDevice(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
AtaReqFreezeQueue(DevExt, QUEUE_FLAG_FROZEN_PNP);
|
||||
AtaReqWaitForOutstandingIoToComplete(&DevExt->Device, NULL);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoRemoveDevice(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_Inout_ PIRP Irp,
|
||||
_In_ BOOLEAN FinalRemove)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
|
||||
NTSTATUS Status;
|
||||
BOOLEAN WasRemoved = FALSE;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
AtaReqFreezeQueue(DevExt, QUEUE_FLAG_FROZEN_REMOVED);
|
||||
AtaReqFlushDeviceQueue(&DevExt->Device);
|
||||
AtaPdoWmiRegistration(DevExt, FALSE);
|
||||
|
||||
if (FinalRemove && DevExt->ReportedMissing)
|
||||
{
|
||||
IoReleaseRemoveLockAndWait(&DevExt->Common.RemoveLock, Irp);
|
||||
|
||||
AtaFdoDeviceListInsert(ChanExt, DevExt, FALSE);
|
||||
AtaPdoFreeDevice(DevExt);
|
||||
WasRemoved = TRUE;
|
||||
}
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
if (!WasRemoved)
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryStopRemoveDevice(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
if (DevExt->Common.PageFiles ||
|
||||
DevExt->Common.HibernateFiles ||
|
||||
DevExt->Common.DumpFiles)
|
||||
{
|
||||
return STATUS_DEVICE_BUSY;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaPdoCompletionRoutine(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_In_ PIRP Irp,
|
||||
_In_reads_opt_(_Inexpressible_("varies")) PVOID Context)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DeviceObject);
|
||||
|
||||
if (Irp->PendingReturned)
|
||||
KeSetEvent(Context, IO_NO_INCREMENT, FALSE);
|
||||
|
||||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryCapabilities(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
DEVICE_CAPABILITIES ParentCapabilities;
|
||||
PDEVICE_CAPABILITIES DeviceCapabilities;
|
||||
NTSTATUS Status;
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/* Get the capabilities of the parent device */
|
||||
RtlZeroMemory(&ParentCapabilities, sizeof(ParentCapabilities));
|
||||
ParentCapabilities.Size = sizeof(ParentCapabilities);
|
||||
ParentCapabilities.Version = 1;
|
||||
ParentCapabilities.Address = MAXULONG;
|
||||
ParentCapabilities.UINumber = MAXULONG;
|
||||
Status = AtaPnpRepeatRequest(&DevExt->Common, Irp, &ParentCapabilities);
|
||||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
|
||||
DeviceCapabilities = IoStack->Parameters.DeviceCapabilities.Capabilities;
|
||||
RtlCopyMemory(DeviceCapabilities, &ParentCapabilities, sizeof(*DeviceCapabilities));
|
||||
|
||||
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
|
||||
|
||||
/* Override some fields */
|
||||
DeviceCapabilities->UINumber = AtaScsiAddress.TargetId;
|
||||
DeviceCapabilities->UniqueID = FALSE;
|
||||
DeviceCapabilities->SurpriseRemovalOK = FALSE;
|
||||
DeviceCapabilities->Removable = !!(DevExt->Device.DeviceFlags & DEVICE_IS_PDO_REMOVABLE);
|
||||
DeviceCapabilities->D1Latency =
|
||||
DeviceCapabilities->D2Latency =
|
||||
DeviceCapabilities->D3Latency = 31 * 10000; // 31 seconds (legacy ATA timeout)
|
||||
/*
|
||||
* See ACPI specification, _ADR (Address).
|
||||
* The goal of the LUN field here is to hide devices with LUN>0 from the ACPI driver,
|
||||
* since these devices are not mentioned in the ACPI spec at all.
|
||||
*/
|
||||
DeviceCapabilities->Address = (AtaScsiAddress.Lun << 4) | AtaScsiAddress.TargetId;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryTargetDeviceRelations(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp)
|
||||
{
|
||||
PDEVICE_RELATIONS DeviceRelations;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
DeviceRelations = ExAllocatePoolUninitialized(PagedPool,
|
||||
sizeof(*DeviceRelations),
|
||||
ATAPORT_TAG);
|
||||
if (!DeviceRelations)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
DeviceRelations->Count = 1;
|
||||
DeviceRelations->Objects[0] = DevExt->Common.Self;
|
||||
ObReferenceObject(DevExt->Common.Self);
|
||||
|
||||
Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryId(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
CHAR LocalBuffer[ATAPORT_FN_FIELD + ATAPORT_RN_FIELD];
|
||||
NTSTATUS Status;
|
||||
PWCHAR Buffer, End;
|
||||
PCSTR DeviceType, GenericType;
|
||||
size_t CharCount, Remaining;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
switch (IoStack->Parameters.QueryId.IdType)
|
||||
{
|
||||
case BusQueryDeviceID:
|
||||
{
|
||||
DeviceType = AtaTypeCodeToName(DevExt, GetDeviceType);
|
||||
|
||||
/* 'IDE\CdRomVBOX_CD-ROM_____________________________1.0_____' */
|
||||
CharCount = (sizeof("IDE\\") - 1) + strlen(DeviceType) +
|
||||
ATAPORT_FN_FIELD + ATAPORT_RN_FIELD +
|
||||
sizeof(ANSI_NULL);
|
||||
|
||||
Buffer = ExAllocatePoolUninitialized(PagedPool,
|
||||
CharCount * sizeof(WCHAR),
|
||||
ATAPORT_TAG);
|
||||
if (!Buffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
AtaCopyIdStringSafe(LocalBuffer,
|
||||
(PUCHAR)DevExt->FriendlyName,
|
||||
ATAPORT_FN_FIELD,
|
||||
'_');
|
||||
|
||||
AtaCopyIdStringSafe(&LocalBuffer[ATAPORT_FN_FIELD],
|
||||
(PUCHAR)DevExt->RevisionNumber,
|
||||
ATAPORT_RN_FIELD,
|
||||
'_');
|
||||
|
||||
Status = RtlStringCchPrintfExW(Buffer,
|
||||
CharCount,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
L"IDE\\%hs%.48hs",
|
||||
DeviceType,
|
||||
LocalBuffer);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
INFO("DeviceID: '%S'\n", Buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case BusQueryHardwareIDs:
|
||||
{
|
||||
PWCHAR IdStart;
|
||||
|
||||
DBG_UNREFERENCED_LOCAL_VARIABLE(IdStart);
|
||||
|
||||
DeviceType = AtaTypeCodeToName(DevExt, GetDeviceType);
|
||||
GenericType = AtaTypeCodeToName(DevExt, GetGenericType);
|
||||
|
||||
/*
|
||||
* |------------------ 40 -----------------|-- 8 --|
|
||||
* v v v
|
||||
* 1) 'IDE\CdRomVBOX_CD-ROM_____________________________1.0_____'
|
||||
* 2) 'IDE\VBOX_CD-ROM_____________________________1.0_____'
|
||||
* 3) 'IDE\CdRomVBOX_CD-ROM_____________________________'
|
||||
* 4) 'VBOX_CD-ROM_____________________________1.0_____'
|
||||
* 5) 'GenCdRom'
|
||||
*/
|
||||
CharCount = strlen(DeviceType) * 2 +
|
||||
strlen(GenericType) +
|
||||
(sizeof("IDE\\") - 1) * 3 +
|
||||
ATAPORT_FN_FIELD * 4 +
|
||||
ATAPORT_RN_FIELD * 3 +
|
||||
5 * sizeof(ANSI_NULL) +
|
||||
sizeof(ANSI_NULL); /* multi-string */
|
||||
|
||||
Buffer = ExAllocatePoolUninitialized(PagedPool,
|
||||
CharCount * sizeof(WCHAR),
|
||||
ATAPORT_TAG);
|
||||
if (!Buffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
AtaCopyIdStringSafe(LocalBuffer,
|
||||
(PUCHAR)DevExt->FriendlyName,
|
||||
ATAPORT_FN_FIELD,
|
||||
'_');
|
||||
|
||||
AtaCopyIdStringSafe(&LocalBuffer[ATAPORT_FN_FIELD],
|
||||
(PUCHAR)DevExt->RevisionNumber,
|
||||
ATAPORT_RN_FIELD,
|
||||
'_');
|
||||
|
||||
INFO("HardwareIDs:\n");
|
||||
|
||||
/* ID 1 */
|
||||
Status = RtlStringCchPrintfExW(Buffer,
|
||||
CharCount,
|
||||
&End,
|
||||
&Remaining,
|
||||
0,
|
||||
L"IDE\\%hs%.48hs",
|
||||
DeviceType,
|
||||
LocalBuffer);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
INFO(" '%S'\n", Buffer);
|
||||
|
||||
++End;
|
||||
--Remaining;
|
||||
|
||||
/* ID 2 */
|
||||
IdStart = End;
|
||||
Status = RtlStringCchPrintfExW(End,
|
||||
Remaining,
|
||||
&End,
|
||||
&Remaining,
|
||||
0,
|
||||
L"IDE\\%.48hs",
|
||||
LocalBuffer);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
INFO(" '%S'\n", IdStart);
|
||||
|
||||
++End;
|
||||
--Remaining;
|
||||
|
||||
/* ID 3 */
|
||||
IdStart = End;
|
||||
Status = RtlStringCchPrintfExW(End,
|
||||
Remaining,
|
||||
&End,
|
||||
&Remaining,
|
||||
0,
|
||||
L"IDE\\%hs%.40hs",
|
||||
DeviceType,
|
||||
LocalBuffer);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
INFO(" '%S'\n", IdStart);
|
||||
|
||||
++End;
|
||||
--Remaining;
|
||||
|
||||
/* ID 4 */
|
||||
IdStart = End;
|
||||
Status = RtlStringCchPrintfExW(End,
|
||||
Remaining,
|
||||
&End,
|
||||
&Remaining,
|
||||
0,
|
||||
L"%.48hs",
|
||||
LocalBuffer);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
INFO(" '%S'\n", IdStart);
|
||||
|
||||
++End;
|
||||
--Remaining;
|
||||
|
||||
/* ID 5 */
|
||||
IdStart = End;
|
||||
Status = RtlStringCchPrintfExW(End,
|
||||
Remaining,
|
||||
&End,
|
||||
&Remaining,
|
||||
0,
|
||||
L"%hs",
|
||||
GenericType);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
*++End = UNICODE_NULL; /* multi-string */
|
||||
|
||||
INFO(" '%S'\n", IdStart);
|
||||
break;
|
||||
}
|
||||
|
||||
case BusQueryCompatibleIDs:
|
||||
{
|
||||
GenericType = AtaTypeCodeToName(DevExt, GetGenericType);
|
||||
|
||||
/* 'GenCdRom' */
|
||||
CharCount = strlen(GenericType) + 2 * sizeof(ANSI_NULL); /* multi-string */
|
||||
|
||||
Buffer = ExAllocatePoolUninitialized(PagedPool,
|
||||
CharCount * sizeof(WCHAR),
|
||||
ATAPORT_TAG);
|
||||
if (!Buffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
Status = RtlStringCchPrintfExW(Buffer,
|
||||
CharCount,
|
||||
&End,
|
||||
&Remaining,
|
||||
0,
|
||||
L"%hs",
|
||||
GenericType);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
*++End = UNICODE_NULL; /* multi-string */
|
||||
|
||||
INFO("CompatibleIDs: '%S'\n", Buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case BusQueryInstanceID:
|
||||
{
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
|
||||
/* 'Path.Larget.Lun' */
|
||||
CharCount = sizeof("FF.FF.FF");
|
||||
|
||||
Buffer = ExAllocatePoolUninitialized(PagedPool,
|
||||
CharCount * sizeof(WCHAR),
|
||||
ATAPORT_TAG);
|
||||
if (!Buffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
|
||||
Status = RtlStringCchPrintfExW(Buffer,
|
||||
CharCount,
|
||||
&End,
|
||||
&Remaining,
|
||||
0,
|
||||
L"%x.%x.%x",
|
||||
AtaScsiAddress.PathId,
|
||||
AtaScsiAddress.TargetId,
|
||||
AtaScsiAddress.Lun);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
INFO("InstanceID: '%S'\n", Buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return Irp->IoStatus.Status;
|
||||
}
|
||||
|
||||
Irp->IoStatus.Information = (ULONG_PTR)Buffer;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoQueryDeviceText(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PIRP Irp,
|
||||
_In_ PIO_STACK_LOCATION IoStack)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
PWCHAR Buffer;
|
||||
size_t CharCount;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
switch (IoStack->Parameters.QueryDeviceText.DeviceTextType)
|
||||
{
|
||||
case DeviceTextDescription:
|
||||
{
|
||||
CharCount = strlen(DevExt->FriendlyName) + sizeof(ANSI_NULL);
|
||||
|
||||
Buffer = ExAllocatePoolUninitialized(PagedPool,
|
||||
CharCount * sizeof(WCHAR),
|
||||
ATAPORT_TAG);
|
||||
if (!Buffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
Status = RtlStringCchPrintfExW(Buffer,
|
||||
CharCount,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
L"%hs",
|
||||
DevExt->FriendlyName);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
INFO("TextDescription: '%S'\n", Buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceTextLocationInformation:
|
||||
{
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
|
||||
CharCount = sizeof("Channel 255, Target 255, Lun 255");
|
||||
|
||||
Buffer = ExAllocatePoolUninitialized(PagedPool,
|
||||
CharCount * sizeof(WCHAR),
|
||||
ATAPORT_TAG);
|
||||
if (!Buffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
|
||||
Status = RtlStringCchPrintfExW(Buffer,
|
||||
CharCount,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
L"Channel %u, Target %u, Lun %u",
|
||||
AtaScsiAddress.PathId,
|
||||
AtaScsiAddress.TargetId,
|
||||
AtaScsiAddress.Lun);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
|
||||
INFO("TextLocationInformation: '%S'\n", Buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return Irp->IoStatus.Status;
|
||||
}
|
||||
|
||||
Irp->IoStatus.Information = (ULONG_PTR)Buffer;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoPnp(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
INFO("(%p, %p) Tid.%lu %s\n",
|
||||
DevExt->Common.Self,
|
||||
Irp,
|
||||
DevExt->Device.AtaScsiAddress.TargetId,
|
||||
GetIRPMinorFunctionString(IoGetCurrentIrpStackLocation(Irp)->MinorFunction));
|
||||
|
||||
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
switch (IoStack->MinorFunction)
|
||||
{
|
||||
case IRP_MN_START_DEVICE:
|
||||
Status = AtaPdoStartDevice(DevExt, Irp);
|
||||
break;
|
||||
|
||||
case IRP_MN_STOP_DEVICE:
|
||||
Status = AtaPdoStopDevice(DevExt, Irp);
|
||||
break;
|
||||
|
||||
case IRP_MN_REMOVE_DEVICE:
|
||||
case IRP_MN_SURPRISE_REMOVAL:
|
||||
return AtaPdoRemoveDevice(DevExt,
|
||||
Irp,
|
||||
(IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE));
|
||||
|
||||
case IRP_MN_QUERY_STOP_DEVICE:
|
||||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||||
Status = AtaPdoQueryStopRemoveDevice(DevExt, Irp);
|
||||
break;
|
||||
|
||||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||||
case IRP_MN_CANCEL_STOP_DEVICE:
|
||||
Status = STATUS_SUCCESS;
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_CAPABILITIES:
|
||||
Status = AtaPdoQueryCapabilities(DevExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_PNP_DEVICE_STATE:
|
||||
Status = AtaPnpQueryPnpDeviceState(&DevExt->Common, Irp);
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_ID:
|
||||
Status = AtaPdoQueryId(DevExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_DEVICE_TEXT:
|
||||
Status = AtaPdoQueryDeviceText(DevExt, Irp, IoStack);
|
||||
break;
|
||||
|
||||
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
|
||||
Status = AtaPnpQueryDeviceUsageNotification(&DevExt->Common, Irp);
|
||||
break;
|
||||
|
||||
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||||
{
|
||||
if (IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation)
|
||||
Status = AtaPdoQueryTargetDeviceRelations(DevExt, Irp);
|
||||
else
|
||||
Status = Irp->IoStatus.Status;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Status = Irp->IoStatus.Status;
|
||||
break;
|
||||
}
|
||||
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaDispatchPnp(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
if (IS_FDO(DeviceObject->DeviceExtension))
|
||||
return AtaFdoPnp(DeviceObject->DeviceExtension, Irp);
|
||||
else
|
||||
return AtaPdoPnp(DeviceObject->DeviceExtension, Irp);
|
||||
}
|
||||
|
||||
VOID
|
||||
CODE_SEG("PAGE")
|
||||
AtaPdoFreeDevice(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
if (DevExt->Device.PowerIdleCounter)
|
||||
{
|
||||
PoRegisterDeviceForIdleDetection(DevExt->Common.Self,
|
||||
0,
|
||||
0,
|
||||
PowerDeviceD3);
|
||||
}
|
||||
|
||||
if (DevExt->GtfDataBuffer)
|
||||
{
|
||||
ExFreePoolWithTag(DevExt->GtfDataBuffer, ATAPORT_TAG);
|
||||
DevExt->GtfDataBuffer = NULL;
|
||||
}
|
||||
|
||||
if (DevExt->Device.Requests)
|
||||
ExFreePoolWithTag(DevExt->Device.Requests, ATAPORT_TAG);
|
||||
|
||||
IoDeleteDevice(DevExt->Common.Self);
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
PATAPORT_DEVICE_EXTENSION
|
||||
AtaPdoCreateDevice(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_In_ ATA_SCSI_ADDRESS AtaScsiAddress)
|
||||
{
|
||||
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
||||
NTSTATUS Status;
|
||||
UNICODE_STRING DeviceName;
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
PDEVICE_OBJECT Pdo;
|
||||
WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\IdeDeviceP99999T9L9-FFF")];
|
||||
ULONG i;
|
||||
DECLARE_PAGED_WSTRING(PdoFormat, L"\\Device\\Ide\\IdeDeviceP%luT%luL%lu-%lx");
|
||||
static ULONG AtapPdoNumber = 0;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
Status = RtlStringCbPrintfW(DeviceNameBuffer,
|
||||
sizeof(DeviceNameBuffer),
|
||||
PdoFormat,
|
||||
ChanExt->DeviceObjectNumber,
|
||||
AtaScsiAddress.TargetId,
|
||||
AtaScsiAddress.Lun,
|
||||
AtapPdoNumber++);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
|
||||
|
||||
Status = IoCreateDevice(ChanExt->Common.Self->DriverObject,
|
||||
sizeof(*DevExt),
|
||||
&DeviceName,
|
||||
FILE_DEVICE_MASS_STORAGE,
|
||||
FILE_DEVICE_SECURE_OPEN,
|
||||
FALSE,
|
||||
&Pdo);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("Failed to create PDO '%wZ' with status 0x%lx\n", &DeviceName, Status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INFO("Created device object %p '%wZ'\n", Pdo, &DeviceName);
|
||||
|
||||
/* DMA buffers alignment */
|
||||
Pdo->AlignmentRequirement = ChanExt->Common.Self->AlignmentRequirement;
|
||||
Pdo->AlignmentRequirement = max(Pdo->AlignmentRequirement, ATA_MIN_BUFFER_ALIGNMENT);
|
||||
|
||||
Pdo->Flags |= DO_DIRECT_IO;
|
||||
|
||||
DevExt = Pdo->DeviceExtension;
|
||||
|
||||
RtlZeroMemory(DevExt, sizeof(*DevExt));
|
||||
AtaPnpInitializeCommonExtension(&DevExt->Common, Pdo, ChanExt->Common.Flags & ~DO_IS_FDO);
|
||||
DevExt->Common.FdoExt = ChanExt;
|
||||
DevExt->TransferModeAllowedMask = MAXULONG;
|
||||
DevExt->Device.SectorSize = ATA_MIN_SECTOR_SIZE;
|
||||
DevExt->Device.PortData = PortData;
|
||||
DevExt->Device.LocalBuffer = PortData->LocalBuffer;
|
||||
|
||||
DevExt->Device.AtaScsiAddress = AtaScsiAddress;
|
||||
DevExt->Device.TransportFlags = AtaScsiAddress.TargetId;
|
||||
DevExt->Device.DeviceSelect = IDE_DRIVE_SELECT | AtaScsiAddress.Lun;
|
||||
|
||||
if (!(PortData->PortFlags & PORT_FLAG_IS_AHCI))
|
||||
{
|
||||
/* Master/Slave select bit */
|
||||
DevExt->Device.DeviceSelect |= ((AtaScsiAddress.TargetId & 1) << 4);
|
||||
}
|
||||
|
||||
DevExt->Device.DeviceFlags = DEVICE_UNINITIALIZED;
|
||||
|
||||
if (PortData->PortFlags & PORT_FLAG_IS_EXTERNAL)
|
||||
DevExt->Device.DeviceFlags |= DEVICE_IS_PDO_REMOVABLE;
|
||||
|
||||
if (PortData->PortFlags & PORT_FLAG_PIO_VIA_DMA)
|
||||
DevExt->Device.DeviceFlags |= DEVICE_PIO_VIA_DMA;
|
||||
|
||||
if (PortData->PortFlags & PORT_FLAG_PIO_FOR_LBA48_XFER)
|
||||
DevExt->Device.DeviceFlags |= DEVICE_PIO_FOR_LBA48_XFER;
|
||||
|
||||
/* Device's capability not explored, yet */
|
||||
DevExt->Device.DeviceFlags |= DEVICE_PIO_ONLY;
|
||||
|
||||
DevExt->Device.FreeRequestsBitmap =
|
||||
DevExt->Device.MaxRequestsBitmap = NUM_TO_BITMAP(PortData->QueueDepth);
|
||||
|
||||
DevExt->Device.Requests = ExAllocatePoolZero(NonPagedPool,
|
||||
sizeof(ATA_DEVICE_REQUEST) * PortData->QueueDepth,
|
||||
ATAPORT_TAG);
|
||||
if (!DevExt->Device.Requests)
|
||||
goto Failure;
|
||||
|
||||
for (i = 0; i < PortData->QueueDepth; ++i)
|
||||
{
|
||||
PATA_DEVICE_REQUEST Request = &DevExt->Device.Requests[i];
|
||||
|
||||
Request->Device = (PATA_IO_CONTEXT_COMMON)&DevExt->Device;
|
||||
#if DBG
|
||||
Request->Signature = ATA_DEVICE_REQUEST_SIGNATURE;
|
||||
#endif
|
||||
}
|
||||
|
||||
KeInitializeSpinLock(&DevExt->Device.QueueLock);
|
||||
InitializeListHead(&DevExt->Device.DeviceQueueList);
|
||||
InitializeListHead(&DevExt->PowerIrpQueueList);
|
||||
KeInitializeEvent(&DevExt->Device.QueueStoppedEvent, NotificationEvent, FALSE);
|
||||
KeInitializeEvent(&DevExt->Worker.EnumerationEvent, NotificationEvent, FALSE);
|
||||
KeInitializeEvent(&DevExt->Worker.ConfigureEvent, NotificationEvent, FALSE);
|
||||
|
||||
return DevExt;
|
||||
|
||||
Failure:
|
||||
AtaPdoFreeDevice(DevExt);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
1004
drivers/storage/ide/atapi/portstate.c
Normal file
1004
drivers/storage/ide/atapi/portstate.c
Normal file
File diff suppressed because it is too large
Load Diff
2063
drivers/storage/ide/atapi/satl.c
Normal file
2063
drivers/storage/ide/atapi/satl.c
Normal file
File diff suppressed because it is too large
Load Diff
1812
drivers/storage/ide/atapi/scsi.c
Normal file
1812
drivers/storage/ide/atapi/scsi.c
Normal file
File diff suppressed because it is too large
Load Diff
210
drivers/storage/ide/atapi/scsiex.h
Normal file
210
drivers/storage/ide/atapi/scsiex.h
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: Extra SCSI definitions
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pshpack1.h>
|
||||
|
||||
typedef union _SCSI_SENSE_CODE
|
||||
{
|
||||
struct
|
||||
{
|
||||
UCHAR SrbStatus;
|
||||
UCHAR SenseKey;
|
||||
UCHAR AdditionalSenseCode;
|
||||
UCHAR AdditionalSenseCodeQualifier;
|
||||
};
|
||||
ULONG AsULONG;
|
||||
} SCSI_SENSE_CODE, *PSCSI_SENSE_CODE;
|
||||
|
||||
typedef struct _SCSI_SENSE_KEY_SPECIFIC_FIELD_POINTER
|
||||
{
|
||||
UCHAR BitPointer:3;
|
||||
UCHAR BitPointerValid:1;
|
||||
UCHAR Reserved:2;
|
||||
UCHAR CommandData:1;
|
||||
UCHAR SenseKeySpecificValid:1;
|
||||
UCHAR FieldPointer[2];
|
||||
} SCSI_SENSE_KEY_SPECIFIC_FIELD_POINTER, *PSCSI_SENSE_KEY_SPECIFIC_FIELD_POINTER;
|
||||
// CommandData = 1
|
||||
// SenseKeySpecificValid = 1
|
||||
|
||||
C_ASSERT(sizeof(SCSI_SENSE_KEY_SPECIFIC_FIELD_POINTER) == 3);
|
||||
|
||||
typedef struct _MODE_CACHING_PAGE_SPC5
|
||||
{
|
||||
UCHAR PageCode:6;
|
||||
UCHAR Reserved:1;
|
||||
UCHAR PageSavable:1;
|
||||
UCHAR PageLength;
|
||||
UCHAR ReadDisableCache:1;
|
||||
UCHAR MultiplicationFactor:1;
|
||||
UCHAR WriteCacheEnable:1;
|
||||
UCHAR Reserved2:5;
|
||||
UCHAR WriteRetensionPriority:4;
|
||||
UCHAR ReadRetensionPriority:4;
|
||||
UCHAR DisablePrefetchTransfer[2];
|
||||
UCHAR MinimumPrefetch[2];
|
||||
UCHAR MaximumPrefetch[2];
|
||||
UCHAR MaximumPrefetchCeiling[2];
|
||||
UCHAR NV_DIS:1;
|
||||
UCHAR SYNC_PROG:2;
|
||||
UCHAR Reserved1:2;
|
||||
UCHAR DisableReadAHead:1;
|
||||
UCHAR LBCSS:1;
|
||||
UCHAR FSW:1;
|
||||
UCHAR NumberOfCacheSegments;
|
||||
UCHAR CacheSegmentSize[2];
|
||||
UCHAR Reserved3;
|
||||
UCHAR Obsolete[3];
|
||||
} MODE_CACHING_PAGE_SPC5, *PMODE_CACHING_PAGE_SPC5;
|
||||
|
||||
C_ASSERT(sizeof(MODE_CACHING_PAGE_SPC5) == 20);
|
||||
|
||||
typedef struct _MODE_CONTROL_EXTENSION_PAGE
|
||||
{
|
||||
UCHAR PageCode:6;
|
||||
UCHAR SubPageFormat:1;
|
||||
UCHAR PageSavable:1;
|
||||
UCHAR SubPageCode;
|
||||
UCHAR PageLength[2];
|
||||
UCHAR IALUAE:1;
|
||||
UCHAR SCSIP:1;
|
||||
UCHAR TCMOS:1;
|
||||
UCHAR Reserved:5;
|
||||
UCHAR InitialCommandPriority:4;
|
||||
UCHAR Reserved1:4;
|
||||
UCHAR MaximumSenseDataLength;
|
||||
UCHAR Reserved2[25];
|
||||
} MODE_CONTROL_EXTENSION_PAGE, *PMODE_CONTROL_EXTENSION_PAGE;
|
||||
|
||||
C_ASSERT(sizeof(MODE_CONTROL_EXTENSION_PAGE) == 32);
|
||||
|
||||
#include <poppack.h>
|
||||
|
||||
#define SCSI_ADSENSE_ADDRESS_MARK_NOT_FOUND_FOR_DATA_FIELD 0x13
|
||||
|
||||
/* SAT-6 */
|
||||
C_ASSERT(sizeof(MODE_INFO_EXCEPTIONS) == 12);
|
||||
C_ASSERT(sizeof(MODE_CONTROL_PAGE) == 12);
|
||||
C_ASSERT(sizeof(MODE_READ_WRITE_RECOVERY_PAGE) == 12);
|
||||
C_ASSERT(sizeof(POWER_CONDITION_PAGE) == 12);
|
||||
C_ASSERT(sizeof(VPD_ATA_INFORMATION_PAGE) == 572);
|
||||
C_ASSERT(sizeof(VPD_BLOCK_LIMITS_PAGE) == 0x3c+4);
|
||||
C_ASSERT(sizeof(VPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE) == 0x3c+4);
|
||||
|
||||
FORCEINLINE
|
||||
UCHAR
|
||||
CdbGetAllocationLength6(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
return Cdb->CDB6GENERIC.CommandUniqueBytes[2];
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
USHORT
|
||||
CdbGetAllocationLength10(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
return (Cdb->CDB10.TransferBlocksMsb << 8) |
|
||||
(Cdb->CDB10.TransferBlocksLsb << 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
CdbGetAllocationLength16(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
return (Cdb->CDB16.TransferLength[0] << 24) |
|
||||
(Cdb->CDB16.TransferLength[1] << 16) |
|
||||
(Cdb->CDB16.TransferLength[2] << 8) |
|
||||
(Cdb->CDB16.TransferLength[3] << 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
USHORT
|
||||
CdbGetTransferLength10(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
/* Bytes 7:8 */
|
||||
return (Cdb->CDB10.TransferBlocksMsb << 8) |
|
||||
(Cdb->CDB10.TransferBlocksLsb << 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
CdbGetTransferLength12(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
/* Bytes 6:9 */
|
||||
return (Cdb->CDB12.TransferLength[0] << 24) |
|
||||
(Cdb->CDB12.TransferLength[1] << 16) |
|
||||
(Cdb->CDB12.TransferLength[2] << 8) |
|
||||
(Cdb->CDB12.TransferLength[3] << 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
CdbGetTransferLength16(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
/* Bytes 10:13 */
|
||||
return (Cdb->CDB16.TransferLength[0] << 24) |
|
||||
(Cdb->CDB16.TransferLength[1] << 16) |
|
||||
(Cdb->CDB16.TransferLength[2] << 8) |
|
||||
(Cdb->CDB16.TransferLength[3] << 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
CdbGetLogicalBlockAddress6(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
/* Bytes 2:3 */
|
||||
return (Cdb->CDB6READWRITE.LogicalBlockMsb0 << 8) |
|
||||
(Cdb->CDB6READWRITE.LogicalBlockLsb << 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
CdbGetLogicalBlockAddress10(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
/* Bytes 2:5 */
|
||||
return (Cdb->CDB10.LogicalBlockByte0 << 24) |
|
||||
(Cdb->CDB10.LogicalBlockByte1 << 16) |
|
||||
(Cdb->CDB10.LogicalBlockByte2 << 8) |
|
||||
(Cdb->CDB10.LogicalBlockByte3 << 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
CdbGetLogicalBlockAddress12(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
/* Bytes 2:5 */
|
||||
return (Cdb->CDB12.LogicalBlock[0] << 24) |
|
||||
(Cdb->CDB12.LogicalBlock[1] << 16) |
|
||||
(Cdb->CDB12.LogicalBlock[2] << 8) |
|
||||
(Cdb->CDB12.LogicalBlock[3] << 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG64
|
||||
CdbGetLogicalBlockAddress16(
|
||||
_In_ PCDB Cdb)
|
||||
{
|
||||
/* Bytes 2:9 */
|
||||
return ((ULONG64)Cdb->CDB16.LogicalBlock[0] << 56) |
|
||||
((ULONG64)Cdb->CDB16.LogicalBlock[1] << 48) |
|
||||
((ULONG64)Cdb->CDB16.LogicalBlock[2] << 40) |
|
||||
((ULONG64)Cdb->CDB16.LogicalBlock[3] << 32) |
|
||||
((ULONG64)Cdb->CDB16.LogicalBlock[4] << 24) |
|
||||
((ULONG64)Cdb->CDB16.LogicalBlock[5] << 16) |
|
||||
((ULONG64)Cdb->CDB16.LogicalBlock[6] << 8) |
|
||||
((ULONG64)Cdb->CDB16.LogicalBlock[7] << 0);
|
||||
}
|
||||
298
drivers/storage/ide/atapi/smart.c
Normal file
298
drivers/storage/ide/atapi/smart.c
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: SMART Feature Set support
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaIdeRegsToTaskFile(
|
||||
_In_ ATAPORT_DEVICE_EXTENSION* __restrict DevExt,
|
||||
_In_ IDEREGS* __restrict IdeRegs,
|
||||
_Out_ ATA_DEVICE_REQUEST* __restrict Request)
|
||||
{
|
||||
PATA_TASKFILE TaskFile = &Request->TaskFile;
|
||||
|
||||
TaskFile->Feature = IdeRegs->bFeaturesReg;
|
||||
TaskFile->SectorCount = IdeRegs->bSectorCountReg;
|
||||
TaskFile->LowLba = IdeRegs->bSectorNumberReg;
|
||||
TaskFile->MidLba = IdeRegs->bCylLowReg;
|
||||
TaskFile->HighLba = IdeRegs->bCylHighReg;
|
||||
TaskFile->Command = IDE_COMMAND_SMART; // SMART_CMD
|
||||
|
||||
/* Set the master/slave bit to the correct value */
|
||||
TaskFile->DriveSelect = IdeRegs->bDriveHeadReg & ~IDE_DRIVE_SELECT_SLAVE;
|
||||
TaskFile->DriveSelect |= DevExt->Device.DeviceSelect & IDE_DRIVE_SELECT_SLAVE;
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
AtaTaskFileToIdeRegs(
|
||||
_In_ ATA_DEVICE_REQUEST* __restrict Request,
|
||||
_Out_ IDEREGS* __restrict IdeRegs)
|
||||
{
|
||||
PATA_TASKFILE TaskFile = &Request->Output;
|
||||
|
||||
IdeRegs->bFeaturesReg = TaskFile->Feature;
|
||||
IdeRegs->bSectorCountReg = TaskFile->SectorCount;
|
||||
IdeRegs->bSectorNumberReg = TaskFile->LowLba;
|
||||
IdeRegs->bCylLowReg = TaskFile->MidLba;
|
||||
IdeRegs->bCylHighReg = TaskFile->HighLba;
|
||||
IdeRegs->bCommandReg = TaskFile->Command;
|
||||
IdeRegs->bDriveHeadReg = TaskFile->DriveSelect;
|
||||
}
|
||||
|
||||
static
|
||||
ATA_COMPLETION_ACTION
|
||||
AtaReqCompleteSmartIoControl(
|
||||
_In_ PATA_DEVICE_REQUEST Request)
|
||||
{
|
||||
PSENDCMDOUTPARAMS CmdOut;
|
||||
|
||||
CmdOut = (PSENDCMDOUTPARAMS)(((PUCHAR)Request->Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
|
||||
|
||||
if (Request->SrbStatus == SRB_STATUS_SUCCESS)
|
||||
{
|
||||
CmdOut->DriverStatus.bDriverError = SMART_NO_ERROR;
|
||||
CmdOut->DriverStatus.bIDEError = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CmdOut->DriverStatus.bDriverError = SMART_IDE_ERROR;
|
||||
CmdOut->DriverStatus.bIDEError = Request->Output.Error;
|
||||
}
|
||||
|
||||
/* Return the SMART status */
|
||||
if (Request->Flags & REQUEST_FLAG_SAVE_TASK_FILE)
|
||||
{
|
||||
if (Request->Flags & REQUEST_FLAG_HAS_TASK_FILE)
|
||||
{
|
||||
PIDEREGS IdeRegs = (PIDEREGS)&CmdOut->bBuffer;
|
||||
|
||||
AtaTaskFileToIdeRegs(Request, IdeRegs);
|
||||
}
|
||||
else
|
||||
{
|
||||
CmdOut->DriverStatus.bDriverError = SMART_IDE_ERROR;
|
||||
CmdOut->DriverStatus.bIDEError = Request->Output.Error;
|
||||
}
|
||||
}
|
||||
|
||||
return COMPLETE_IRP;
|
||||
}
|
||||
|
||||
UCHAR
|
||||
AtaReqSmartIoControl(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PATA_DEVICE_REQUEST Request,
|
||||
_In_ PSCSI_REQUEST_BLOCK Srb)
|
||||
{
|
||||
union _PARAMS
|
||||
{
|
||||
PSENDCMDINPARAMS CmdIn;
|
||||
PSENDCMDOUTPARAMS CmdOut;
|
||||
} Buffer;
|
||||
|
||||
if (Srb->DataTransferLength < (sizeof(SRB_IO_CONTROL) + sizeof(*Buffer.CmdIn) - 1))
|
||||
return SRB_STATUS_INVALID_REQUEST;
|
||||
|
||||
Buffer.CmdIn = (PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
|
||||
|
||||
if (Buffer.CmdIn->irDriveRegs.bCommandReg != SMART_CMD)
|
||||
return SRB_STATUS_INVALID_REQUEST;
|
||||
|
||||
switch (Buffer.CmdIn->irDriveRegs.bFeaturesReg)
|
||||
{
|
||||
case READ_ATTRIBUTES:
|
||||
{
|
||||
Request->DataTransferLength = READ_ATTRIBUTE_BUFFER_SIZE;
|
||||
Request->Flags = REQUEST_FLAG_DATA_IN;
|
||||
break;
|
||||
}
|
||||
|
||||
case READ_THRESHOLDS:
|
||||
{
|
||||
Request->DataTransferLength = READ_THRESHOLD_BUFFER_SIZE;
|
||||
Request->Flags = REQUEST_FLAG_DATA_IN;
|
||||
break;
|
||||
}
|
||||
|
||||
case SMART_READ_LOG:
|
||||
{
|
||||
Request->DataTransferLength =
|
||||
Buffer.CmdIn->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE;
|
||||
Request->Flags = REQUEST_FLAG_DATA_IN;
|
||||
break;
|
||||
}
|
||||
|
||||
case SMART_WRITE_LOG:
|
||||
{
|
||||
Request->DataTransferLength =
|
||||
Buffer.CmdIn->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE;
|
||||
Request->Flags = REQUEST_FLAG_DATA_OUT;
|
||||
break;
|
||||
}
|
||||
|
||||
case RETURN_SMART_STATUS:
|
||||
{
|
||||
Request->DataTransferLength = sizeof(IDEREGS);
|
||||
Request->Flags = REQUEST_FLAG_SAVE_TASK_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
case EXECUTE_OFFLINE_DIAGS:
|
||||
{
|
||||
UCHAR Subcommand = Buffer.CmdIn->irDriveRegs.bSectorNumberReg;
|
||||
|
||||
if (Subcommand == SMART_SHORT_SELFTEST_CAPTIVE ||
|
||||
Subcommand == SMART_EXTENDED_SELFTEST_CAPTIVE)
|
||||
{
|
||||
return SRB_STATUS_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
__fallthrough;
|
||||
}
|
||||
case ENABLE_DISABLE_AUTOSAVE:
|
||||
case SAVE_ATTRIBUTE_VALUES:
|
||||
case ENABLE_SMART:
|
||||
case DISABLE_SMART:
|
||||
case ENABLE_DISABLE_AUTO_OFFLINE:
|
||||
{
|
||||
Request->DataTransferLength = 0;
|
||||
Request->Flags = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return SRB_STATUS_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
if (Request->Flags & (REQUEST_FLAG_DATA_IN | REQUEST_FLAG_SAVE_TASK_FILE))
|
||||
{
|
||||
if (Srb->DataTransferLength <
|
||||
(sizeof(SRB_IO_CONTROL) + sizeof(*Buffer.CmdOut) - 1 + Request->DataTransferLength))
|
||||
{
|
||||
return SRB_STATUS_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
Request->DataBuffer = Buffer.CmdOut->bBuffer;
|
||||
}
|
||||
else if (Request->Flags & REQUEST_FLAG_DATA_OUT)
|
||||
{
|
||||
if (Srb->DataTransferLength <
|
||||
(sizeof(SRB_IO_CONTROL) + sizeof(*Buffer.CmdIn) - 1 + Request->DataTransferLength))
|
||||
{
|
||||
return SRB_STATUS_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
Request->DataBuffer = Buffer.CmdIn->bBuffer;
|
||||
}
|
||||
|
||||
Request->Complete = AtaReqCompleteSmartIoControl;
|
||||
|
||||
AtaIdeRegsToTaskFile(DevExt, &Buffer.CmdIn->irDriveRegs, Request);
|
||||
|
||||
return SRB_STATUS_PENDING;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoHandleMiniportIdentify(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PSCSI_REQUEST_BLOCK Srb)
|
||||
{
|
||||
union _PARAMS
|
||||
{
|
||||
PSENDCMDINPARAMS CmdIn;
|
||||
PSENDCMDOUTPARAMS CmdOut;
|
||||
} Buffer;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (Srb->DataTransferLength <
|
||||
(sizeof(SRB_IO_CONTROL) + sizeof(*Buffer.CmdOut) - 1 + IDENTIFY_BUFFER_SIZE))
|
||||
{
|
||||
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
Buffer.CmdIn = (PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
|
||||
|
||||
if (IS_ATAPI(&DevExt->Device) || (Buffer.CmdIn->irDriveRegs.bCommandReg != ID_CMD))
|
||||
{
|
||||
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
|
||||
Buffer.CmdOut->cBufferSize = IDENTIFY_BUFFER_SIZE;
|
||||
Buffer.CmdOut->DriverStatus.bDriverError = 0;
|
||||
Buffer.CmdOut->DriverStatus.bIDEError = 0;
|
||||
|
||||
RtlCopyMemory(Buffer.CmdOut->bBuffer, &DevExt->IdentifyDeviceData, IDENTIFY_BUFFER_SIZE);
|
||||
|
||||
Srb->SrbStatus = SRB_STATUS_SUCCESS;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoHandleMiniportSmartVersion(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ PSCSI_REQUEST_BLOCK Srb)
|
||||
{
|
||||
PATAPORT_CHANNEL_EXTENSION ChanExt = DevExt->Common.FdoExt;
|
||||
PATAPORT_PORT_DATA PortData = &ChanExt->PortData;
|
||||
PGETVERSIONINPARAMS VersionParameters;
|
||||
ULONG i;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (Srb->DataTransferLength < (sizeof(*VersionParameters) + sizeof(SRB_IO_CONTROL)))
|
||||
{
|
||||
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
VersionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
|
||||
|
||||
/* SMART 1.03 */
|
||||
VersionParameters->bVersion = 1;
|
||||
VersionParameters->bRevision = 1;
|
||||
VersionParameters->bReserved = 0;
|
||||
|
||||
VersionParameters->fCapabilities = (CAP_ATA_ID_CMD | CAP_ATAPI_ID_CMD | CAP_SMART_CMD);
|
||||
|
||||
VersionParameters->bIDEDeviceMap = 0;
|
||||
|
||||
/* Emulate the PATA behavior */
|
||||
for (i = 0; i < MAX_IDE_DEVICE; ++i)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
ULONG DeviceFlag;
|
||||
|
||||
DevExt = AtaFdoFindDeviceByPath(ChanExt,
|
||||
AtaMarshallScsiAddress(PortData->PortNumber, i, 0),
|
||||
Srb);
|
||||
if (!DevExt)
|
||||
continue;
|
||||
|
||||
DeviceFlag = 1 << i;
|
||||
if (PortData->PortNumber != 0)
|
||||
DeviceFlag <<= 2;
|
||||
if (IS_ATAPI(&DevExt->Device))
|
||||
DeviceFlag <<= 4;
|
||||
VersionParameters->bIDEDeviceMap |= DeviceFlag;
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Srb);
|
||||
}
|
||||
|
||||
Srb->SrbStatus = SRB_STATUS_SUCCESS;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
213
drivers/storage/ide/atapi/wmi.c
Normal file
213
drivers/storage/ide/atapi/wmi.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* PROJECT: ReactOS ATA Port Driver
|
||||
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: WMI support
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "atapi.h"
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
static WMIGUIDREGINFO AtapWmiGuidList[] =
|
||||
{
|
||||
{&MSIde_PortDeviceInfo_GUID, 1, 0}
|
||||
};
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaQueryWmiRegInfo(
|
||||
_Inout_ PDEVICE_OBJECT DeviceObject,
|
||||
_Inout_ PULONG RegFlags,
|
||||
_Inout_ PUNICODE_STRING InstanceName,
|
||||
_Outptr_result_maybenull_ PUNICODE_STRING *RegistryPath,
|
||||
_Inout_ PUNICODE_STRING MofResourceName,
|
||||
_Outptr_result_maybenull_ PDEVICE_OBJECT *Pdo)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt = DeviceObject->DeviceExtension;
|
||||
|
||||
UNREFERENCED_PARAMETER(InstanceName);
|
||||
UNREFERENCED_PARAMETER(MofResourceName);
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
*RegFlags = WMIREG_FLAG_INSTANCE_PDO;
|
||||
*RegistryPath = &AtapDriverRegistryPath;
|
||||
*Pdo = DevExt->Common.Self;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaQueryWmiDataBlock(
|
||||
_Inout_ PDEVICE_OBJECT DeviceObject,
|
||||
_Inout_ PIRP Irp,
|
||||
_In_ ULONG GuidIndex,
|
||||
_In_ ULONG InstanceIndex,
|
||||
_In_ ULONG InstanceCount,
|
||||
_Out_writes_opt_(InstanceCount) PULONG InstanceLengthArray,
|
||||
_In_ ULONG BufferAvail,
|
||||
_Out_writes_bytes_opt_(BufferAvail) PUCHAR Buffer)
|
||||
{
|
||||
PATAPORT_DEVICE_EXTENSION DevExt;
|
||||
NTSTATUS Status;
|
||||
ATA_SCSI_ADDRESS AtaScsiAddress;
|
||||
PMSIde_PortDeviceInfo DeviceInfo;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (GuidIndex > RTL_NUMBER_OF(AtapWmiGuidList))
|
||||
{
|
||||
Status = STATUS_WMI_GUID_NOT_FOUND;
|
||||
goto Complete;
|
||||
}
|
||||
|
||||
/* Only ever register 1 instance per GUID */
|
||||
if (InstanceIndex != 0 || InstanceCount != 1)
|
||||
{
|
||||
Status = STATUS_WMI_INSTANCE_NOT_FOUND;
|
||||
goto Complete;
|
||||
}
|
||||
|
||||
if (!InstanceLengthArray || BufferAvail < sizeof(*DeviceInfo))
|
||||
{
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto Complete;
|
||||
}
|
||||
|
||||
DevExt = DeviceObject->DeviceExtension;
|
||||
AtaScsiAddress = DevExt->Device.AtaScsiAddress;
|
||||
|
||||
DeviceInfo = (PMSIde_PortDeviceInfo)Buffer;
|
||||
DeviceInfo->Bus = AtaScsiAddress.PathId;
|
||||
DeviceInfo->Target = AtaScsiAddress.TargetId;
|
||||
DeviceInfo->Lun = AtaScsiAddress.Lun;
|
||||
|
||||
*InstanceLengthArray = sizeof(*DeviceInfo);
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
|
||||
Complete:
|
||||
return WmiCompleteRequest(DeviceObject,
|
||||
Irp,
|
||||
Status,
|
||||
sizeof(*DeviceInfo),
|
||||
IO_NO_INCREMENT);
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoWmi(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
SYSCTL_IRP_DISPOSITION Disposition;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
TRACE("%s(%p, %p)\n", __FUNCTION__, DevExt, Irp);
|
||||
|
||||
Status = IoAcquireRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = WmiSystemControl(&DevExt->WmiLibInfo,
|
||||
DevExt->Common.Self,
|
||||
Irp,
|
||||
&Disposition);
|
||||
switch (Disposition)
|
||||
{
|
||||
case IrpProcessed:
|
||||
break;
|
||||
|
||||
case IrpNotCompleted:
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT(FALSE);
|
||||
__fallthrough;
|
||||
case IrpForward:
|
||||
case IrpNotWmi:
|
||||
Status = STATUS_NOT_SUPPORTED;
|
||||
Irp->IoStatus.Status = Status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
break;
|
||||
}
|
||||
|
||||
IoReleaseRemoveLock(&DevExt->Common.RemoveLock, Irp);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaFdoWmi(
|
||||
_In_ PATAPORT_CHANNEL_EXTENSION ChanExt,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
return IoCallDriver(ChanExt->Common.LowerDeviceObject, Irp);
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
AtaDispatchWmi(
|
||||
_In_ PDEVICE_OBJECT DeviceObject,
|
||||
_Inout_ PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
if (IS_FDO(DeviceObject->DeviceExtension))
|
||||
return AtaFdoWmi(DeviceObject->DeviceExtension, Irp);
|
||||
else
|
||||
return AtaPdoWmi(DeviceObject->DeviceExtension, Irp);
|
||||
}
|
||||
|
||||
CODE_SEG("PAGE")
|
||||
NTSTATUS
|
||||
AtaPdoWmiRegistration(
|
||||
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
||||
_In_ BOOLEAN Register)
|
||||
{
|
||||
ULONG Action;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
if (Register)
|
||||
{
|
||||
DevExt->WmiLibInfo.GuidCount = RTL_NUMBER_OF(AtapWmiGuidList);
|
||||
DevExt->WmiLibInfo.GuidList = AtapWmiGuidList;
|
||||
|
||||
DevExt->WmiLibInfo.QueryWmiRegInfo = AtaQueryWmiRegInfo;
|
||||
DevExt->WmiLibInfo.QueryWmiDataBlock = AtaQueryWmiDataBlock;
|
||||
|
||||
Action = WMIREG_ACTION_REGISTER;
|
||||
}
|
||||
else
|
||||
{
|
||||
Action = WMIREG_ACTION_DEREGISTER;
|
||||
}
|
||||
|
||||
return IoWMIRegistrationControl(DevExt->Common.Self, Action);
|
||||
}
|
||||
595
sdk/include/reactos/drivers/ata/identify_funcs.h
Normal file
595
sdk/include/reactos/drivers/ata/identify_funcs.h
Normal file
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* PROJECT: ReactOS Storage Stack
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* or MIT (https://spdx.org/licenses/MIT)
|
||||
* PURPOSE: ATA IDENTIFY DEVICE and IDENTIFY PACKET DEVICE data helper functions
|
||||
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasCdbInterrupt(
|
||||
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
|
||||
{
|
||||
/* Bits 5:6 of word 0 */
|
||||
return (IdentifyPacketData->GeneralConfiguration.DrqDelay == 1);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
UCHAR
|
||||
AtaDevCdbSizeInWords(
|
||||
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
|
||||
{
|
||||
/* Bits 0:2 of word 0 */
|
||||
return (IdentifyPacketData->GeneralConfiguration.PacketType != 0) ? 8 : 6;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevMaxLun(
|
||||
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
|
||||
{
|
||||
/* Bits 0:2 of word 126 */
|
||||
USHORT LastLunIdentifier = IdentifyPacketData->ReservedWord126 & 7;
|
||||
|
||||
/*
|
||||
* We perform additional validation because
|
||||
* most ATAPI devices ignore the LUN field in the CDB and respond to each LUN.
|
||||
*/
|
||||
|
||||
/* Make sure this field has no value that represents all bits set */
|
||||
if (LastLunIdentifier != 7)
|
||||
return LastLunIdentifier + 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsDmaDirectionRequired(
|
||||
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
|
||||
{
|
||||
/* Bit 15 of word 62 */
|
||||
if (IdentifyPacketData->DMADIR.DMADIRBitRequired)
|
||||
{
|
||||
return !(IdentifyPacketData->MultiWordDMASupport & 0x7) && // Bits 0:2 of word 63
|
||||
!IdentifyPacketData->Capabilities.DmaSupported && // Bit 8 of word 49
|
||||
!IdentifyPacketData->Capabilities.InterleavedDmaSupported && // Bit 15 of word 49
|
||||
!(IdentifyPacketData->UltraDMASupport & 0x7F); // Bits 0:6 of word 88
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsTape(
|
||||
_In_ PIDENTIFY_PACKET_DATA IdentifyPacketData)
|
||||
{
|
||||
/* Bits 8:12 of word 0 (sequential-access device) */
|
||||
return (IdentifyPacketData->GeneralConfiguration.CommandPacketType == 1);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsIdentifyDataValid(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
ULONG i;
|
||||
UCHAR Crc;
|
||||
|
||||
/* Bits 0:8 of word 255 */
|
||||
if (IdentifyData->Signature != 0xA5)
|
||||
{
|
||||
/* The integrity word is missing, assume the data provided by the device is valid */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Verify the checksum */
|
||||
Crc = 0;
|
||||
for (i = 0; i < sizeof(*IdentifyData); ++i)
|
||||
{
|
||||
Crc += ((PUCHAR)IdentifyData)[i];
|
||||
}
|
||||
|
||||
return (Crc == 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasLbaTranslation(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bit 9 of word 49 */
|
||||
return IdentifyData->Capabilities.LbaSupported;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
AtaDevUserAddressableSectors28Bit(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Words 60-61 */
|
||||
return IdentifyData->UserAddressableSectors;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG64
|
||||
AtaDevUserAddressableSectors48Bit(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Words 100-103 */
|
||||
return ((ULONG64)IdentifyData->Max48BitLBA[1] << 32) | IdentifyData->Max48BitLBA[0];
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHas48BitAddressFeature(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 83: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetSupport.WordValid83 == 1)
|
||||
{
|
||||
/* Bit 10 of word 83 */
|
||||
return IdentifyData->CommandSetSupport.BigLba;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsCurrentGeometryValid(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
return ((IdentifyData->TranslationFieldsValid & 1) &&
|
||||
(IdentifyData->NumberOfCurrentCylinders != 0) &&
|
||||
(IdentifyData->NumberOfCurrentCylinders <= 63) &&
|
||||
(IdentifyData->NumberOfCurrentHeads != 0) &&
|
||||
(IdentifyData->NumberOfCurrentHeads <= 16) &&
|
||||
(IdentifyData->CurrentSectorsPerTrack != 0));
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
VOID
|
||||
AtaDevDefaultChsTranslation(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
|
||||
_Out_ PUSHORT Cylinders,
|
||||
_Out_ PUSHORT Heads,
|
||||
_Out_ PUSHORT SectorsPerTrack)
|
||||
{
|
||||
/* Word 1 */
|
||||
*Cylinders = IdentifyData->NumCylinders;
|
||||
/* Word 3 */
|
||||
*Heads = IdentifyData->NumHeads;
|
||||
/* Word 6 */
|
||||
*SectorsPerTrack = IdentifyData->NumSectorsPerTrack;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
VOID
|
||||
AtaDevCurrentChsTranslation(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
|
||||
_Out_ PUSHORT Cylinders,
|
||||
_Out_ PUSHORT Heads,
|
||||
_Out_ PUSHORT SectorsPerTrack)
|
||||
{
|
||||
/* Word 54 */
|
||||
*Cylinders = IdentifyData->NumberOfCurrentCylinders;
|
||||
/* Word 55 */
|
||||
*Heads = IdentifyData->NumberOfCurrentHeads;
|
||||
/* Word 55 */
|
||||
*SectorsPerTrack = IdentifyData->CurrentSectorsPerTrack;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
UCHAR
|
||||
AtaDevCurrentSectorsPerDrq(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
UCHAR MultiSectorCurrent;
|
||||
|
||||
/* Bit 8 of word 59 */
|
||||
if (!(IdentifyData->MultiSectorSettingValid))
|
||||
return 0;
|
||||
|
||||
/* The word 59 should be a power of 2 */
|
||||
MultiSectorCurrent = IdentifyData->CurrentMultiSectorSetting;
|
||||
if ((MultiSectorCurrent > 0) && ((MultiSectorCurrent & (MultiSectorCurrent - 1)) == 0))
|
||||
return MultiSectorCurrent;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
UCHAR
|
||||
AtaDevMaximumSectorsPerDrq(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
UCHAR MultiSectorMax;
|
||||
|
||||
/* The word 47 should be a power of 2 */
|
||||
MultiSectorMax = IdentifyData->MaximumBlockTransfer;
|
||||
if ((MultiSectorMax > 0) && ((MultiSectorMax & (MultiSectorMax - 1)) == 0))
|
||||
return MultiSectorMax;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
AtaDevBytesPerLogicalSector(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
ULONG WordCount;
|
||||
|
||||
/* Word 106: 15 = 0, 14 = 1, 12 = 1 */
|
||||
if (IdentifyData->PhysicalLogicalSectorSize.Reserved1 == 1 &&
|
||||
IdentifyData->PhysicalLogicalSectorSize.LogicalSectorLongerThan256Words)
|
||||
{
|
||||
/* Words 116-117 */
|
||||
WordCount = IdentifyData->WordsPerLogicalSector[0];
|
||||
WordCount |= (ULONG)IdentifyData->WordsPerLogicalSector[1] << 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 256 words = 512 bytes */
|
||||
WordCount = 256;
|
||||
}
|
||||
|
||||
return WordCount * sizeof(USHORT);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
AtaDevLogicalSectorsPerPhysicalSector(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData,
|
||||
_Out_ PULONG Exponent)
|
||||
{
|
||||
/* Word 106: 15 = 0, 14 = 1, 13 = 1 */
|
||||
if (IdentifyData->PhysicalLogicalSectorSize.Reserved1 == 1 &&
|
||||
IdentifyData->PhysicalLogicalSectorSize.MultipleLogicalSectorsPerPhysicalSector)
|
||||
{
|
||||
/* Bits 0:3 of word 106 */
|
||||
*Exponent = IdentifyData->PhysicalLogicalSectorSize.LogicalSectorsPerPhysicalSector;
|
||||
|
||||
return 1 << *Exponent;
|
||||
}
|
||||
|
||||
*Exponent = 0;
|
||||
return 1 << 0;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
AtaDevLogicalSectorAlignment(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 209: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->BlockAlignment.Word209Supported &&
|
||||
IdentifyData->BlockAlignment.Reserved0 == 0)
|
||||
{
|
||||
/* Bits 0:13 of word 209 */
|
||||
return IdentifyData->BlockAlignment.AlignmentOfLogicalWithinPhysical;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevInPuisState(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 2 */
|
||||
return (IdentifyData->SpecificConfiguration == 0x37C8) ||
|
||||
(IdentifyData->SpecificConfiguration == 0x738C);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsIdentifyDataIncomplete(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bit 2 of word 0 */
|
||||
return IdentifyData->GeneralConfiguration.ResponseIncomplete;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsRemovable(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bit 7 of word 0 */
|
||||
return IdentifyData->GeneralConfiguration.RemovableMedia;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasRemovableMediaFeature(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
if (AtaDevIsRemovable(IdentifyData))
|
||||
{
|
||||
/* Word 83: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetSupport.WordValid == 1)
|
||||
{
|
||||
/* Bit 2 of word 82 */
|
||||
return IdentifyData->CommandSetSupport.RemovableMediaFeature;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
ULONG
|
||||
AtaDevQueueDepth(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bit 8 of word 76 */
|
||||
if (IdentifyData->SerialAtaCapabilities.NCQ)
|
||||
{
|
||||
/* Bits 0:4 of word 75 */
|
||||
return IdentifyData->QueueDepth + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasNcqAutosense(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
USHORT Word76 = ((PUSHORT)IdentifyData)[76]; // IdentifyData->SerialAtaCapabilities
|
||||
|
||||
if (Word76 != 0x0000 && Word76 != 0xFFFF)
|
||||
{
|
||||
/* Bit 7 of word 78 */
|
||||
return IdentifyData->SerialAtaFeaturesSupported.NCQAutosense;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasSenseDataReporting(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
if (IdentifyData->CommandSetActive.Words119_120Valid && // Word 86: bit 15 = 1
|
||||
IdentifyData->CommandSetSupportExt.WordValid == 1) // Word 119: 15 = 0, 14 = 1
|
||||
{
|
||||
/* Bit 6 of word 119 */
|
||||
return IdentifyData->CommandSetSupportExt.SenseDataReporting;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasRemovableMediaStatusNotification(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 127: bit 0 = 1, bit 1 = 0 */
|
||||
return (IdentifyData->MsnSupport == 1);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasIeee1667(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
// TODO: Verify word 48
|
||||
|
||||
/* Bit 0 of word 48 */
|
||||
if (IdentifyData->TrustedComputing.FeatureSupported)
|
||||
{
|
||||
/* Bit 7 of word 69 */
|
||||
return IdentifyData->AdditionalSupported.IEEE1667;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasWorldWideName(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 87: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetActive.Reserved4 == 1)
|
||||
{
|
||||
/* Bit 8 of word 87 */
|
||||
return IdentifyData->CommandSetActive.WWN64Bit;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsZonedDevice(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bits 0:1 of word 69 */
|
||||
return (IdentifyData->AdditionalSupported.ZonedCapabilities != 0);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
UCHAR
|
||||
AtaDevZonedCapabilities(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bits 0:1 of word 69 */
|
||||
return IdentifyData->AdditionalSupported.ZonedCapabilities;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasSecurityModeFeature(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 83: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetSupport.WordValid == 1)
|
||||
{
|
||||
/* Bit 1 of word 82 */
|
||||
return IdentifyData->CommandSetSupport.SecurityMode;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasSmartFeature(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bit 0 of word 82 */
|
||||
return IdentifyData->CommandSetSupport.SmartCommands;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsVolatileWriteCacheEnabled(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 83: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetSupport.WordValid == 1)
|
||||
{
|
||||
/* Bit 5 of word 82 and bit 5 of word 85 */
|
||||
return (IdentifyData->CommandSetSupport.WriteCache &&
|
||||
IdentifyData->CommandSetActive.WriteCache);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsReadLookAHeadEnabled(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 83: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetSupport.WordValid == 1)
|
||||
{
|
||||
/* Bit 6 of word 82 and bit 6 of word 85 */
|
||||
return (IdentifyData->CommandSetSupport.LookAhead &&
|
||||
IdentifyData->CommandSetActive.LookAhead);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
UCHAR
|
||||
AtaDevNominalFormFactor(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bits 0:3 of word 168 */
|
||||
return IdentifyData->NominalFormFactor;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
USHORT
|
||||
AtaDevMediumRotationRate(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 217 */
|
||||
return IdentifyData->NominalMediaRotationRate;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsRotatingDevice(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 217 */
|
||||
return (IdentifyData->NominalMediaRotationRate >= 0x0401 &&
|
||||
IdentifyData->NominalMediaRotationRate <= 0xFFFE);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevIsSsd(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 217 */
|
||||
return (IdentifyData->NominalMediaRotationRate == 1);
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasTrimFunction(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bit 0 of word 169 */
|
||||
return IdentifyData->DataSetManagementFeature.SupportsTrim;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasRzatFunction(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bit 5 of word 69 */
|
||||
return IdentifyData->AdditionalSupported.ReadZeroAfterTrimSupported;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasDratFunction(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Bit 14 of word 69 */
|
||||
return IdentifyData->AdditionalSupported.DeterministicReadAfterTrimSupported;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasForceUnitAccessCommands(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 83: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetSupport.WordValid83 == 1)
|
||||
{
|
||||
/* Bit 6 of word 84 */
|
||||
return IdentifyData->CommandSetSupport.WriteFua;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasFlushCache(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 83: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetSupport.WordValid == 1)
|
||||
{
|
||||
/* Bit 12 of word 83 */
|
||||
return IdentifyData->CommandSetSupport.FlushCache;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
BOOLEAN
|
||||
AtaDevHasFlushCacheExt(
|
||||
_In_ PIDENTIFY_DEVICE_DATA IdentifyData)
|
||||
{
|
||||
/* Word 83: 15 = 0, 14 = 1 */
|
||||
if (IdentifyData->CommandSetSupport.WordValid == 1)
|
||||
{
|
||||
/* Bit 13 of word 83 */
|
||||
return IdentifyData->CommandSetSupport.FlushCacheExt;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
Reference in New Issue
Block a user