[PCIIDEX] Add DMA support

CORE-17256
This commit is contained in:
Dmitry Borisov
2024-03-03 19:02:29 +06:00
committed by Carl J. Bialorucki
parent 5418987cb8
commit ae2827f481
38 changed files with 15159 additions and 899 deletions

View File

@@ -0,0 +1,17 @@
add_library(legide MODULE legide.c legide.rc)
set_module_type(legide kernelmodedriver)
if(MSVC)
# Required for NT5.x pciidex.sys
target_link_options(legide PRIVATE /SECTION:.rsrc,!D)
else()
add_custom_command(
TARGET legide POST_BUILD
COMMAND native-pefixup --section:.rsrc,!D $<TARGET_FILE:legide>
VERBATIM)
endif()
add_importlibs(legide pciidex ntoskrnl hal)
add_cd_file(TARGET legide DESTINATION reactos/system32/drivers NO_CAB FOR all)
add_registry_inf(legide_reg.inf)

View File

@@ -0,0 +1,850 @@
/*
* PROJECT: Legacy PATA Bus Enumerator Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Main file
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/*
* This driver detects non-PnP PATA controllers in the system.
* The legacy PATA device detection should be done after PCI bus enumeration,
* therefore this logic is implemented in a separate kernel-mode driver.
*/
/* INCLUDES *******************************************************************/
#include <ntddk.h>
#include <ide.h>
#include <ata.h>
#include <scsi.h>
#include <reactos/drivers/ata/ata_shared.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
#define ATAPORT_TAG 'PedI'
#define IDE_DRIVE_SELECT 0xA0
#define ATA_READ(Port) READ_PORT_UCHAR((Port))
#define ATA_WRITE(Port, Value) WRITE_PORT_UCHAR((Port), (Value))
#define ATA_IO_WAIT() KeStallExecutionProcessor(1)
#define ATA_TIME_BUSY_SELECT 3000 // 30 ms
#define ATA_TIME_BUSY_IDENTIFY 500000 // 5 s
#define ATA_TIME_DRQ_CLEAR 1000 // 10 ms
#define ATA_TIME_RESET_SELECT 200000 // 2 s
#define ATA_TIME_BUSY_RESET 1000000 // 10 s
typedef struct _ATA_LEGACY_CHANNEL
{
ULONG IoBase;
ULONG Irq;
} ATA_LEGACY_CHANNEL, *PATA_LEGACY_CHANNEL;
CODE_SEG("INIT")
DRIVER_INITIALIZE DriverEntry;
/* FUNCTIONS ******************************************************************/
static
CODE_SEG("INIT")
UCHAR
AtaWait(
_In_ PIDE_REGISTERS Registers,
_In_range_(>, 0) ULONG Timeout,
_In_ UCHAR Mask,
_In_ UCHAR Value)
{
UCHAR IdeStatus;
ULONG i;
for (i = 0; i < Timeout; ++i)
{
IdeStatus = ATA_READ(Registers->Status);
if ((IdeStatus & Mask) == Value)
break;
if (IdeStatus == 0xFF)
break;
KeStallExecutionProcessor(10);
}
return IdeStatus;
}
static
CODE_SEG("INIT")
VOID
AtaLegacyFetchIdentifyData(
_In_ PIDE_REGISTERS Registers)
{
USHORT Buffer[256];
READ_PORT_BUFFER_USHORT((PUSHORT)Registers->Data, Buffer, RTL_NUMBER_OF(Buffer));
}
static
CODE_SEG("INIT")
BOOLEAN
AtaLegacyIdentifyDevice(
_In_ PIDE_REGISTERS Registers,
_In_ ULONG DeviceNumber,
_In_ UCHAR Command)
{
UCHAR IdeStatus;
/* Select the device */
ATA_WRITE(Registers->Device, ((DeviceNumber << 4) | IDE_DRIVE_SELECT));
ATA_IO_WAIT();
IdeStatus = AtaWait(Registers, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0);
if (IdeStatus & IDE_STATUS_BUSY)
{
DPRINT("Timeout, status 0x%02x\n", IdeStatus);
return FALSE;
}
ATA_WRITE(Registers->Features, 0);
ATA_WRITE(Registers->SectorCount, 0);
ATA_WRITE(Registers->LbaLow, 0);
ATA_WRITE(Registers->LbaMid, 0);
ATA_WRITE(Registers->LbaHigh, 0);
ATA_WRITE(Registers->Command, Command);
/* Need to wait for a valid status */
ATA_IO_WAIT();
/* Now wait for busy to clear */
IdeStatus = AtaWait(Registers, ATA_TIME_BUSY_IDENTIFY, IDE_STATUS_BUSY, 0);
if (IdeStatus & IDE_STATUS_BUSY)
{
DPRINT("Timeout, status 0x%02x\n", IdeStatus);
return FALSE;
}
if (IdeStatus & ((IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT)))
{
DPRINT("Command 0x%02x aborted, status 0x%02x, error 0x%02x\n",
Command,
IdeStatus,
ATA_READ(Registers->Error));
return FALSE;
}
if (!(IdeStatus & IDE_STATUS_DRQ))
{
DPRINT1("DRQ not set, status 0x%02x\n", IdeStatus);
return FALSE;
}
/* Complete the data transfer */
AtaLegacyFetchIdentifyData(Registers);
/* All data has been transferred, wait for DRQ to clear */
IdeStatus = AtaWait(Registers,
ATA_TIME_DRQ_CLEAR,
IDE_STATUS_BUSY | IDE_STATUS_DRQ,
0);
if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ))
{
DPRINT1("DRQ not cleared, status 0x%02x\n", IdeStatus);
return FALSE;
}
return TRUE;
}
static
CODE_SEG("INIT")
BOOLEAN
AtaLegacyPerformSoftwareReset(
_In_ PIDE_REGISTERS Registers,
_In_ ULONG DeviceNumber)
{
UCHAR IdeStatus;
ULONG i;
/* Perform a software reset */
ATA_WRITE(Registers->Control, IDE_DC_RESET_CONTROLLER);
KeStallExecutionProcessor(20);
ATA_WRITE(Registers->Control, IDE_DC_DISABLE_INTERRUPTS);
KeStallExecutionProcessor(20);
/* The reset will cause the master device to be selected */
if ((DeviceNumber & 1) != 0)
{
for (i = ATA_TIME_RESET_SELECT; i > 0; i--)
{
/* Select the device again */
ATA_WRITE(Registers->Device, ((DeviceNumber << 4) | IDE_DRIVE_SELECT));
ATA_IO_WAIT();
/* Check whether the selection was successful */
ATA_WRITE(Registers->ByteCountLow, 0xAA);
ATA_WRITE(Registers->ByteCountLow, 0x55);
ATA_WRITE(Registers->ByteCountLow, 0xAA);
if (ATA_READ(Registers->ByteCountLow) == 0xAA)
break;
KeStallExecutionProcessor(10);
}
if (i == 0)
{
DPRINT("Selection timeout\n");
return FALSE;
}
}
/* Now wait for busy to clear */
IdeStatus = AtaWait(Registers, ATA_TIME_BUSY_RESET, IDE_STATUS_BUSY, 0);
if (IdeStatus & IDE_STATUS_BUSY)
{
DPRINT1("Timeout, status 0x%02x\n", IdeStatus);
return FALSE;
}
return FALSE;
}
static
CODE_SEG("INIT")
BOOLEAN
AtaLegacyFindAtaDevice(
_In_ PIDE_REGISTERS Registers,
_In_ ULONG DeviceNumber)
{
UCHAR IdeStatus, SignatureLow, SignatureHigh;
/* Select the device */
ATA_WRITE(Registers->Device, ((DeviceNumber << 4) | IDE_DRIVE_SELECT));
ATA_IO_WAIT();
/* Do a quick check first */
IdeStatus = ATA_READ(Registers->Status);
if (IdeStatus == 0xFF || IdeStatus == 0x7F)
return FALSE;
/* Look at controller */
ATA_WRITE(Registers->ByteCountLow, 0x55);
ATA_WRITE(Registers->ByteCountLow, 0xAA);
ATA_WRITE(Registers->ByteCountLow, 0x55);
if (ATA_READ(Registers->ByteCountLow) != 0x55)
return FALSE;
ATA_WRITE(Registers->ByteCountHigh, 0xAA);
ATA_WRITE(Registers->ByteCountHigh, 0x55);
ATA_WRITE(Registers->ByteCountHigh, 0xAA);
if (ATA_READ(Registers->ByteCountHigh) != 0xAA)
return FALSE;
/* Wait for busy to clear */
IdeStatus = AtaWait(Registers, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0);
if (IdeStatus & IDE_STATUS_BUSY)
{
DPRINT1("Device is busy, attempting to recover %02x\n", IdeStatus);
if (!AtaLegacyPerformSoftwareReset(Registers, DeviceNumber))
{
DPRINT1("Failed to reset device %02x\n", ATA_READ(Registers->Status));
return FALSE;
}
}
/* Check for ATA */
if (AtaLegacyIdentifyDevice(Registers, DeviceNumber, IDE_COMMAND_IDENTIFY))
return TRUE;
SignatureLow = ATA_READ(Registers->SignatureLow);
SignatureHigh = ATA_READ(Registers->SignatureHigh);
DPRINT("SL = 0x%02x, SH = 0x%02x\n", SignatureLow, SignatureHigh);
/* Check for ATAPI */
if (SignatureLow == 0x14 && SignatureHigh == 0xEB)
return TRUE;
/*
* ATAPI devices abort the IDENTIFY command and return an ATAPI signature
* in the task file registers. However the NEC CDR-260 drive doesn't return
* the correct signature, but instead shows up with zeroes.
* This drive also reports an ATA signature after device reset.
* To overcome this behavior, we try the ATAPI IDENTIFY command.
* It should be successfully completed or failed (aborted or time-out).
*/
return AtaLegacyIdentifyDevice(Registers, DeviceNumber, IDE_COMMAND_ATAPI_IDENTIFY);
}
static
CODE_SEG("INIT")
BOOLEAN
AtaLegacyChannelPresent(
_In_ PIDE_REGISTERS Registers)
{
ULONG i;
/* Check for the PC-98 on-board IDE interface */
if (IsNEC_98)
return (ATA_READ((PUCHAR)0x432) != 0xFF);
/*
* The only reliable way to detect the legacy IDE channel
* is to check for devices attached to the bus.
*/
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
if (AtaLegacyFindAtaDevice(Registers, i))
{
DPRINT("Found IDE device\n");
return TRUE;
}
}
return FALSE;
}
static
CODE_SEG("INIT")
VOID
AtaLegacyMakePortResource(
_Out_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
_In_ ULONG IoBase,
_In_ ULONG Length)
{
Descriptor->Type = CmResourceTypePort;
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
Descriptor->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
Descriptor->u.Port.Start.u.LowPart = IoBase;
Descriptor->u.Port.Length = Length;
}
static
CODE_SEG("INIT")
VOID
AtaLegacyMakeInterruptResource(
_Out_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
_In_ ULONG Level)
{
Descriptor->Type = CmResourceTypeInterrupt;
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
Descriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
Descriptor->u.Interrupt.Level = Level;
Descriptor->u.Interrupt.Vector = Level;
Descriptor->u.Interrupt.Affinity = (KAFFINITY)-1;
}
static
CODE_SEG("INIT")
BOOLEAN
AtaLegacyChannelBuildResources(
_In_ PATA_LEGACY_CHANNEL LegacyChannel,
_Inout_ PCM_RESOURCE_LIST ResourceList)
{
PCONFIGURATION_INFORMATION ConfigInfo = IoGetConfigurationInformation();
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
ULONG i;
Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[0];
if (IsNEC_98)
{
if (ConfigInfo->AtDiskPrimaryAddressClaimed || ConfigInfo->AtDiskSecondaryAddressClaimed)
return FALSE;
#define CHANNEL_PC98_RESOURCE_COUNT 12
/*
* Make a resource list for the internal IDE interface:
*
* [ShareDisposition 1, Flags 11] IO: Start 0:640, Len 1
* [ShareDisposition 1, Flags 11] IO: Start 0:74C, Len 1
* [ShareDisposition 1, Flags 1] INT: Lev 9 Vec 9 Aff FFFFFFFF
* [ShareDisposition 1, Flags 11] IO: Start 0:642, Len 1
* [ShareDisposition 1, Flags 11] IO: Start 0:644, Len 1
* [ShareDisposition 1, Flags 11] IO: Start 0:646, Len 1
* [ShareDisposition 1, Flags 11] IO: Start 0:648, Len 1
* [ShareDisposition 1, Flags 11] IO: Start 0:64A, Len 1
* [ShareDisposition 1, Flags 11] IO: Start 0:64C, Len 1
* [ShareDisposition 1, Flags 11] IO: Start 0:64E, Len 1
* [ShareDisposition 1, Flags 11] IO: Start 0:432, Len 2
* [ShareDisposition 1, Flags 11] IO: Start 0:435, Len 1
*/
AtaLegacyMakePortResource(Descriptor++, 0x640, 1);
AtaLegacyMakePortResource(Descriptor++, 0x74C, 1);
AtaLegacyMakeInterruptResource(Descriptor++, 9);
for (i = 0; i < 7; ++i)
{
AtaLegacyMakePortResource(Descriptor++, 0x642 + i * 2, 1);
}
AtaLegacyMakePortResource(Descriptor++, 0x432, 2);
AtaLegacyMakePortResource(Descriptor++, 0x435, 1);
}
else
{
if (LegacyChannel->IoBase == 0x1F0 && ConfigInfo->AtDiskPrimaryAddressClaimed)
return FALSE;
if (LegacyChannel->IoBase == 0x170 && ConfigInfo->AtDiskSecondaryAddressClaimed)
return FALSE;
#define CHANNEL_PCAT_RESOURCE_COUNT 3
/*
* For example, the following resource list is created for the primary IDE channel:
*
* [ShareDisposition 1, Flags 11] IO: Start 0:1F0, Len 8
* [ShareDisposition 1, Flags 11] IO: Start 0:3F6, Len 1
* [ShareDisposition 1, Flags 1] INT: Lev A Vec A Aff FFFFFFFF
*/
AtaLegacyMakePortResource(Descriptor++, LegacyChannel->IoBase, 8);
AtaLegacyMakePortResource(Descriptor++, LegacyChannel->IoBase + 0x206, 1);
AtaLegacyMakeInterruptResource(Descriptor++, LegacyChannel->Irq);
}
return TRUE;
}
static
CODE_SEG("INIT")
VOID
AtaLegacyChannelTranslateResources(
_Inout_ PCM_RESOURCE_LIST ResourceList,
_In_ PUCHAR CommandPortBase,
_In_ PUCHAR ControlPortBase)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
KIRQL Irql;
Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[0];
/* Move on to the interrupt descriptor */
if (IsNEC_98)
{
Descriptor += 2;
}
else
{
Descriptor->u.Port.Start.QuadPart = (ULONG_PTR)CommandPortBase;
++Descriptor;
Descriptor->u.Port.Start.QuadPart = (ULONG_PTR)ControlPortBase;
++Descriptor;
}
ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
Descriptor->u.Interrupt.Vector = HalGetInterruptVector(Isa,
0,
Descriptor->u.Interrupt.Level,
Descriptor->u.Interrupt.Vector,
&Irql,
&Descriptor->u.Interrupt.Affinity);
Descriptor->u.Interrupt.Level = Irql;
}
static
CODE_SEG("INIT")
PVOID
AtaLegacyTranslateBusAddress(
_In_ ULONG Address,
_In_ ULONG NumberOfBytes,
_Out_ PBOOLEAN IsAddressMmio)
{
ULONG AddressSpace;
BOOLEAN Success;
PHYSICAL_ADDRESS BusAddress, TranslatedAddress;
BusAddress.QuadPart = Address;
AddressSpace = 1; // I/O space
Success = HalTranslateBusAddress(Isa, 0, BusAddress, &AddressSpace, &TranslatedAddress);
if (!Success)
return NULL;
/* I/O space */
if (AddressSpace != 0)
{
*IsAddressMmio = FALSE;
return (PVOID)(ULONG_PTR)TranslatedAddress.QuadPart;
}
else
{
*IsAddressMmio = TRUE;
return MmMapIoSpace(TranslatedAddress, NumberOfBytes, MmNonCached);
}
}
static
CODE_SEG("INIT")
BOOLEAN
AtaLegacyClaimHardwareResources(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PCM_RESOURCE_LIST ResourceList,
_In_ ULONG ResourceListSize)
{
NTSTATUS Status;
BOOLEAN ConflictDetected;
Status = IoReportResourceForDetection(DriverObject,
ResourceList,
ResourceListSize,
NULL,
NULL,
0,
&ConflictDetected);
/* HACK: We really need to fix a number of resource bugs in the kernel */
if (IsNEC_98)
return TRUE;
if (!NT_SUCCESS(Status) || ConflictDetected)
return FALSE;
return TRUE;
}
static
CODE_SEG("INIT")
VOID
AtaLegacyReleaseHardwareResources(
_In_ PDRIVER_OBJECT DriverObject)
{
BOOLEAN Dummy;
IoReportResourceForDetection(DriverObject, NULL, 0, NULL, NULL, 0, &Dummy);
}
static
CODE_SEG("INIT")
BOOLEAN
AtaLegacyDetectChannel(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PPCIIDEX_LEGACY_CONTROLLER_INTERFACE ControllerInferface,
_In_ PATA_LEGACY_CHANNEL LegacyChannel,
_In_ PCM_RESOURCE_LIST ResourceList,
_In_ ULONG ResourceListSize)
{
BOOLEAN IsAddressMmio[2];
PDEVICE_OBJECT PhysicalDeviceObject;
PVOID ControllerContext;
IDE_REGISTERS Registers;
NTSTATUS Status;
ULONG Spare;
DPRINT("IDE Channel IO %lx, Irq %lu\n", LegacyChannel->IoBase, LegacyChannel->Irq);
if (!AtaLegacyChannelBuildResources(LegacyChannel, ResourceList))
{
DPRINT("Failed to build the resource list\n");
return FALSE;
}
if (!AtaLegacyClaimHardwareResources(DriverObject, ResourceList, ResourceListSize))
{
DPRINT("Failed to claim resources\n");
return FALSE;
}
Status = STATUS_SUCCESS;
IsAddressMmio[0] = FALSE;
IsAddressMmio[1] = FALSE;
if (IsNEC_98)
{
/* No translation required for the C-Bus I/O space */
Registers.Data = (PVOID)0x640;
Registers.Control = (PVOID)0x74C;
Spare = 2;
}
else
{
Registers.Data = AtaLegacyTranslateBusAddress(LegacyChannel->IoBase,
8,
&IsAddressMmio[0]);
if (!Registers.Data)
{
DPRINT("Failed to map command port\n");
Status = STATUS_UNSUCCESSFUL;
goto ReleaseResources;
}
Registers.Control = AtaLegacyTranslateBusAddress(LegacyChannel->IoBase + 0x206,
1,
&IsAddressMmio[1]);
if (!Registers.Control)
{
DPRINT("Failed to map control port\n");
Status = STATUS_UNSUCCESSFUL;
goto ReleaseResources;
}
Spare = 1;
}
Registers.Error = (PVOID)((ULONG_PTR)Registers.Data + 1 * Spare);
Registers.SectorCount = (PVOID)((ULONG_PTR)Registers.Data + 2 * Spare);
Registers.LbaLow = (PVOID)((ULONG_PTR)Registers.Data + 3 * Spare);
Registers.LbaMid = (PVOID)((ULONG_PTR)Registers.Data + 4 * Spare);
Registers.LbaHigh = (PVOID)((ULONG_PTR)Registers.Data + 5 * Spare);
Registers.Device = (PVOID)((ULONG_PTR)Registers.Data + 6 * Spare);
Registers.Status = (PVOID)((ULONG_PTR)Registers.Data + 7 * Spare);
if (!AtaLegacyChannelPresent(&Registers))
{
DPRINT("No IDE devices found\n");
Status = STATUS_UNSUCCESSFUL;
}
ReleaseResources:
AtaLegacyReleaseHardwareResources(DriverObject);
if (!NT_SUCCESS(Status))
goto Cleanup;
PhysicalDeviceObject = NULL;
Status = IoReportDetectedDevice(DriverObject,
InterfaceTypeUndefined,
(ULONG)-1,
(ULONG)-1,
ResourceList,
NULL,
0,
&PhysicalDeviceObject);
if (!NT_SUCCESS(Status))
{
DPRINT("IoReportDetectedDevice() failed with status 0x%lx\n", Status);
goto Cleanup;
}
if (ControllerInferface->Version != PCIIDEX_INTERFACE_VERSION)
{
/* ReactOS-specific: Retrieve the interface for legacy device detection */
Status = PciIdeXInitialize(DriverObject,
NULL,
(PVOID)ControllerInferface,
PCIIDEX_GET_CONTROLLER_INTERFACE_SIGNATURE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to register the legacy channel 0x%lx, status 0x%lx\n",
LegacyChannel->IoBase, Status);
goto Cleanup;
}
if (ControllerInferface->Version != PCIIDEX_INTERFACE_VERSION)
{
DPRINT1("Unknown interface version 0x%lx\n", ControllerInferface->Version);
Status = STATUS_REVISION_MISMATCH;
goto Cleanup;
}
}
Status = ControllerInferface->AddDevice(DriverObject, PhysicalDeviceObject, &ControllerContext);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to add the legacy channel 0x%lx, status 0x%lx\n",
LegacyChannel->IoBase, Status);
goto Cleanup;
}
AtaLegacyChannelTranslateResources(ResourceList, Registers.Data, Registers.Control);
Status = ControllerInferface->StartDevice(ControllerContext, ResourceList);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to start the legacy channel 0x%lx, status 0x%lx\n",
LegacyChannel->IoBase, Status);
ControllerInferface->RemoveDevice(ControllerContext);
}
Cleanup:
if (IsAddressMmio[0] && Registers.Data)
{
MmUnmapIoSpace(Registers.Data, 8);
}
if (IsAddressMmio[1] && Registers.Control)
{
MmUnmapIoSpace(Registers.Control, 1);
}
return NT_SUCCESS(Status);
}
static
CODE_SEG("INIT")
NTSTATUS
AtaOpenRegistryKey(
_Out_ PHANDLE KeyHandle,
_In_ HANDLE RootKey,
_In_ PUNICODE_STRING KeyName)
{
OBJECT_ATTRIBUTES ObjectAttributes;
PAGED_CODE();
InitializeObjectAttributes(&ObjectAttributes,
KeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
RootKey,
NULL);
return ZwOpenKey(KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
}
static
CODE_SEG("INIT")
BOOLEAN
AtaLegacyShouldDetectChannels(
_In_ PUNICODE_STRING RegistryPath)
{
NTSTATUS Status;
ULONG KeyValue;
BOOLEAN PerformDetection;
HANDLE SoftKeyHandle, ParamsKeyHandle;
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
UNICODE_STRING ParametersKeyName = RTL_CONSTANT_STRING(L"Parameters");
static const WCHAR MapperKeyPath[] =
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Pnp";
PAGED_CODE();
/*
* Read the firmware mapper key. If the firmware mapper is disabled,
* it is the responsibility of PnP drivers (ACPI, PCI, and others)
* to detect and enumerate IDE channels.
*/
KeyValue = 0;
RtlZeroMemory(QueryTable, sizeof(QueryTable));
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
QueryTable[0].Name = L"DisableFirmwareMapper";
QueryTable[0].EntryContext = &KeyValue;
Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MapperKeyPath, QueryTable, NULL, NULL);
if (NT_SUCCESS(Status) && (KeyValue != 0))
{
DPRINT("Skipping legacy detection on a PnP system\n");
return FALSE;
}
/* Open the driver's software key */
Status = AtaOpenRegistryKey(&SoftKeyHandle, NULL, RegistryPath);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to open the '%wZ' key, status 0x%lx\n", RegistryPath, Status);
return FALSE;
}
/* Open the 'Parameters' key */
Status = AtaOpenRegistryKey(&ParamsKeyHandle, SoftKeyHandle, &ParametersKeyName);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to open the 'Parameters' key, status 0x%lx\n", Status);
ZwClose(SoftKeyHandle);
return FALSE;
}
/* Check whether it is the first time we detect IDE channels */
KeyValue = 0;
RtlZeroMemory(QueryTable, sizeof(QueryTable));
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[0].Name = L"LegacyDetection";
QueryTable[0].EntryContext = &KeyValue;
RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
(PWSTR)ParamsKeyHandle,
QueryTable,
NULL,
NULL);
PerformDetection = (KeyValue != 0);
/* Do not detect devices again on subsequent boots after driver installation */
if (PerformDetection)
{
KeyValue = 0;
RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
(PWSTR)ParamsKeyHandle,
L"LegacyDetection",
REG_DWORD,
&KeyValue,
sizeof(KeyValue));
}
ZwClose(ParamsKeyHandle);
ZwClose(SoftKeyHandle);
return PerformDetection;
}
CODE_SEG("INIT")
NTSTATUS
NTAPI
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath)
{
PCIIDEX_LEGACY_CONTROLLER_INTERFACE ControllerInferface = { 0 };
ATA_LEGACY_CHANNEL LegacyChannel[4 + 1] = { 0 };
PCM_RESOURCE_LIST ResourceList;
ULONG i, ListSize, ResourceCount;
NTSTATUS Status;
if (!AtaLegacyShouldDetectChannels(RegistryPath))
return STATUS_CANCELLED;
if (IsNEC_98)
{
/* Internal IDE interface */
LegacyChannel[0].IoBase = 0x640;
LegacyChannel[0].Irq = 9;
ResourceCount = CHANNEL_PC98_RESOURCE_COUNT;
}
else
{
/* Primary IDE channel */
LegacyChannel[0].IoBase = 0x1F0;
LegacyChannel[0].Irq = 14;
/* Secondary IDE channel */
LegacyChannel[1].IoBase = 0x170;
LegacyChannel[1].Irq = 15;
/* Tertiary IDE channel */
LegacyChannel[2].IoBase = 0x1E8;
LegacyChannel[2].Irq = 11;
/* Quaternary IDE channel */
LegacyChannel[3].IoBase = 0x168;
LegacyChannel[3].Irq = 10;
ResourceCount = CHANNEL_PCAT_RESOURCE_COUNT;
}
ListSize = FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.PartialDescriptors) +
sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * ResourceCount;
ResourceList = ExAllocatePoolZero(PagedPool, ListSize, ATAPORT_TAG);
if (!ResourceList)
{
DPRINT1("Failed to allocate the resource list\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
ResourceList->Count = 1;
ResourceList->List[0].InterfaceType = Isa;
ResourceList->List[0].BusNumber = 0;
ResourceList->List[0].PartialResourceList.Version = 1;
ResourceList->List[0].PartialResourceList.Revision = 1;
ResourceList->List[0].PartialResourceList.Count = ResourceCount;
Status = STATUS_NOT_FOUND;
for (i = 0; LegacyChannel[i].IoBase != 0; ++i)
{
if (AtaLegacyDetectChannel(DriverObject,
&ControllerInferface,
&LegacyChannel[i],
ResourceList,
ListSize))
{
Status = STATUS_SUCCESS;
}
}
ExFreePoolWithTag(ResourceList, ATAPORT_TAG);
return Status;
}

View File

@@ -0,0 +1,5 @@
#define REACTOS_VERSION_DLL
#define REACTOS_STR_FILE_DESCRIPTION "Legacy PATA Bus Enumerator Driver"
#define REACTOS_STR_INTERNAL_NAME "legide"
#define REACTOS_STR_ORIGINAL_FILENAME "legide.sys"
#include <reactos/version.rc>

View File

@@ -0,0 +1,9 @@
; Legacy PATA Bus Enumerator Driver
[AddReg]
HKLM,"SYSTEM\CurrentControlSet\Services\legide","ErrorControl",0x00010001,0x00000003
HKLM,"SYSTEM\CurrentControlSet\Services\legide","Group",0x00000000,"SCSI Miniport"
HKLM,"SYSTEM\CurrentControlSet\Services\legide","ImagePath",0x00020000,"system32\drivers\legide.sys"
HKLM,"SYSTEM\CurrentControlSet\Services\legide","Start",0x00010001,0x00000000
HKLM,"SYSTEM\CurrentControlSet\Services\legide","Type",0x00010001,0x00000001
HKLM,"SYSTEM\CurrentControlSet\Services\legide","Tag",0x00010001,0x00000021
HKLM,"SYSTEM\CurrentControlSet\Services\legide\Parameters","LegacyDetection",0x00010001,0x00000001

View File

@@ -3,7 +3,7 @@
* PROJECT: PCI IDE bus driver
* FILE: drivers/storage/pciide/pciide.c
* PURPOSE: Main file
* PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
* PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
*/
#include "pciide.h"
@@ -16,34 +16,8 @@ PciIdeChannelEnabled(
IN PVOID DeviceExtension,
IN ULONG Channel)
{
PCI_COMMON_CONFIG PciConfig;
NTSTATUS Status;
DPRINT("PciIdeChannelEnabled(%p, %lu)\n", DeviceExtension, Channel);
Status = PciIdeXGetBusData(
DeviceExtension,
&PciConfig,
0,
PCI_COMMON_HDR_LENGTH);
if (!NT_SUCCESS(Status))
{
DPRINT("PciIdeXGetBusData() failed with status 0x%08lx\n", Status);
return ChannelStateUnknown;
}
if (PCI_CONFIGURATION_TYPE(&PciConfig) != PCI_DEVICE_TYPE)
{
DPRINT("Wrong PCI card type. Disabling IDE channel #%lu\n", Channel);
return ChannelDisabled;
}
if (PciConfig.BaseClass != PCI_CLASS_MASS_STORAGE_CTLR || PciConfig.SubClass != PCI_SUBCLASS_MSC_IDE_CTLR)
{
DPRINT("Wrong PCI card base class/sub class. Disabling IDE channel #%lu\n", Channel);
return ChannelDisabled;
}
return ChannelStateUnknown;
}
@@ -51,24 +25,9 @@ BOOLEAN NTAPI
PciIdeSyncAccessRequired(
IN PVOID DeviceExtension)
{
DPRINT1("PciIdeSyncAccessRequired %p\n", DeviceExtension);
DPRINT("PciIdeSyncAccessRequired %p\n", DeviceExtension);
return FALSE; /* FIXME */
}
NTSTATUS NTAPI
PciIdeTransferModeSelect(
IN PVOID DeviceExtension,
IN PPCIIDE_TRANSFER_MODE_SELECT XferMode)
{
ULONG i;
DPRINT1("PciIdeTransferModeSelect(%p %p)\n", DeviceExtension, XferMode);
for (i = 0; i < MAX_IDE_DEVICE; i++)
XferMode->DevicePresent[i] = FALSE; /* FIXME */
return STATUS_SUCCESS;
return FALSE;
}
ULONG NTAPI
@@ -88,27 +47,40 @@ PciIdeGetControllerProperties(
IN PVOID DeviceExtension,
OUT PIDE_CONTROLLER_PROPERTIES ControllerProperties)
{
ULONG SupportedMode;
USHORT PciCommand;
if (ControllerProperties->Size != sizeof(IDE_CONTROLLER_PROPERTIES))
return STATUS_REVISION_MISMATCH;
ControllerProperties->PciIdeChannelEnabled = PciIdeChannelEnabled;
ControllerProperties->PciIdeSyncAccessRequired = PciIdeSyncAccessRequired;
ControllerProperties->PciIdeTransferModeSelect = PciIdeTransferModeSelect;
ControllerProperties->IgnoreActiveBitForAtaDevice = FALSE;
ControllerProperties->AlwaysClearBusMasterInterrupt = TRUE;
ControllerProperties->PciIdeUseDma = PciIdeUseDma;
ControllerProperties->AlignmentRequirement = 1;
ControllerProperties->DefaultPIO = 0; /* FIXME */
ControllerProperties->PciIdeUdmaModesSupported = NULL; /* optional */
PciIdeXGetBusData(DeviceExtension,
&PciCommand,
FIELD_OFFSET(PCI_COMMON_HEADER, Command),
sizeof(PciCommand));
if (!(PciCommand & PCI_ENABLE_BUS_MASTER))
{
SupportedMode = PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4;
}
else
{
SupportedMode = PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4 |
SWDMA_MODE0 | SWDMA_MODE1 | SWDMA_MODE2 |
MWDMA_MODE0 | MWDMA_MODE1 | MWDMA_MODE2 |
UDMA_MODE0 | UDMA_MODE1 | UDMA_MODE2 |
UDMA_MODE3 | UDMA_MODE4 | UDMA_MODE5 | UDMA_MODE6;
}
ControllerProperties->SupportedTransferMode[0][0] =
ControllerProperties->SupportedTransferMode[0][1] =
ControllerProperties->SupportedTransferMode[1][0] =
ControllerProperties->SupportedTransferMode[1][1] =
PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4 |
SWDMA_MODE0 | SWDMA_MODE1 | SWDMA_MODE2 |
MWDMA_MODE0 | MWDMA_MODE1 | MWDMA_MODE2 |
UDMA_MODE0 | UDMA_MODE1 | UDMA_MODE2 | UDMA_MODE3 | UDMA_MODE4;
ControllerProperties->SupportedTransferMode[1][1] = SupportedMode;
return STATUS_SUCCESS;
}

View File

@@ -2,12 +2,36 @@
spec2def(pciidex.sys pciidex.spec ADD_IMPORTLIB)
list(APPEND SOURCE
chipset/ahci_generic.c
chipset/ahci_hw.c
chipset/ahci_io.c
chipset/amd.c
chipset/ati.c
chipset/cmd.c
chipset/intel.c
chipset/pata_generic.c
chipset/pata_hw.c
chipset/pata_io.c
chipset/pctech.c
chipset/sil680.c
chipset/svw_pata.c
chipset/svw_sata.c
chipset/toshiba.c
chipset/via.c
acpi.c
ahci.h
debug.h
fdo.c
miniport.c
pciidex.c
pciidex.h
pdo.c
power.c
pciidex.h)
power.c)
if(ARCH STREQUAL "i386")
add_definitions(-DATA_DETECT_LEGACY_DEVICES)
list(APPEND SOURCE chipset/pata_legacy.c)
endif()
add_library(pciidex MODULE
${SOURCE}

View File

@@ -0,0 +1,337 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: ACPI interface with SATA ports, IDE controllers and drives
* COPYRIGHT: Copyright 2026 <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
#include <acpiioct.h>
/* FUNCTIONS ******************************************************************/
_IRQL_requires_max_(DISPATCH_LEVEL)
static
NTSTATUS
AtaAcpiEvaluateObject(
_In_ PDEVICE_OBJECT DeviceObject,
_In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_Out_writes_bytes_opt_(OutputBufferLength) PACPI_EVAL_OUTPUT_BUFFER OutputBuffer,
_In_ ULONG OutputBufferLength)
{
PIRP Irp;
PIO_STACK_LOCATION IoStack;
KEVENT Event;
NTSTATUS Status;
PDEVICE_OBJECT TopDeviceObject;
/* Get the ACPI bus filter device for this DO */
TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
/*
* We could be called at DISPATCH_LEVEL,
* so use IoAllocateIrp() rather going through IoBuildDeviceIoControlRequest().
*/
Irp = IoAllocateIrp(TopDeviceObject->StackSize, 0);
if (!Irp)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
Irp->AssociatedIrp.SystemBuffer = InputBuffer;
Irp->UserBuffer = OutputBuffer;
Irp->Flags |= IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
/*
* Submit an asynchronous evaluation request.
* Note that _STM should be evaluated while the device queue has been paused,
* so we must not cause *any* page faults and use IOCTL_ACPI_EVAL_METHOD.
* AtaAcpiEvaluateObject() also has to be non-pageable.
*/
IoStack = IoGetNextIrpStackLocation(Irp);
IoStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_ACPI_ASYNC_EVAL_METHOD;
IoStack->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
IoStack->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
IoSetCompletionRoutine(Irp,
PciIdeXPdoCompletionRoutine,
&Event,
TRUE,
TRUE,
TRUE);
Status = IoCallDriver(TopDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = Irp->IoStatus.Status;
}
RtlCopyMemory(OutputBuffer, InputBuffer, min(Irp->IoStatus.Information, OutputBufferLength));
if (OutputBuffer && NT_SUCCESS(Status))
{
ASSERT(OutputBuffer->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE);
if ((OutputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) ||
(OutputBuffer->Count == 0))
{
ERR("Invalid ACPI output buffer\n");
Status = STATUS_ACPI_INVALID_DATA;
}
}
Exit:
ObDereferenceObject(TopDeviceObject);
return Status;
}
BOOLEAN
AtaAcpiGetTimingMode(
_In_ PDEVICE_OBJECT DeviceObject,
_Out_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode)
{
UCHAR Buffer[FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*TimingMode))];
ACPI_EVAL_INPUT_BUFFER InputBuffer;
NTSTATUS Status;
PACPI_EVAL_OUTPUT_BUFFER OutputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)Buffer;
RtlZeroMemory(Buffer, sizeof(Buffer));
InputBuffer.MethodNameAsUlong = 'MTG_'; // _GTM
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
/* Evaluate the _GTM method */
Status = AtaAcpiEvaluateObject(DeviceObject,
&InputBuffer,
sizeof(InputBuffer),
OutputBuffer,
sizeof(Buffer));
if (!NT_SUCCESS(Status))
{
ASSERT(Status != STATUS_BUFFER_OVERFLOW);
TRACE("Failed to evaluate the _GTM method, status 0x%lx\n", Status);
return FALSE;
}
if (OutputBuffer->Argument[0].DataLength < sizeof(*TimingMode))
{
ERR("Buffer too small, size %u/%u\n",
OutputBuffer->Argument[0].DataLength, sizeof(*TimingMode));
return FALSE;
}
if (OutputBuffer->Argument[0].Type != ACPI_METHOD_ARGUMENT_BUFFER)
{
ERR("Unexpected method type %u\n", OutputBuffer->Argument[0].Type);
return FALSE;
}
RtlCopyMemory(TimingMode, OutputBuffer->Argument[0].Data, sizeof(*TimingMode));
return TRUE;
}
NTSTATUS
AtaAcpiSetTimingMode(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode,
_In_opt_ PIDENTIFY_DEVICE_DATA IdBlock1,
_In_opt_ PIDENTIFY_DEVICE_DATA IdBlock2)
{
PACPI_EVAL_INPUT_BUFFER_COMPLEX InputBuffer;
PACPI_METHOD_ARGUMENT Argument;
NTSTATUS Status;
ULONG InputSize;
InputSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*TimingMode)) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*IdBlock1)) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*IdBlock2));
InputBuffer = ExAllocatePoolUninitialized(NonPagedPool, InputSize, TAG_PCIIDEX);
if (!InputBuffer)
{
ERR("Failed to allocate memory\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
InputBuffer->MethodNameAsUlong = 'MTS_'; // _STM
InputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
InputBuffer->ArgumentCount = 3;
InputBuffer->Size = InputSize;
/* Argument 1: The channel timing information block */
Argument = InputBuffer->Argument;
ACPI_METHOD_SET_ARGUMENT_BUFFER(Argument, TimingMode, sizeof(*TimingMode));
/* Argument 2: The ATA drive ID block */
Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
Argument->Type = ACPI_METHOD_ARGUMENT_BUFFER;
Argument->DataLength = sizeof(*IdBlock1);
if (IdBlock1)
RtlCopyMemory(Argument->Data, IdBlock1, sizeof(*IdBlock1));
else
RtlZeroMemory(Argument->Data, sizeof(*IdBlock1));
/* Argument 3: The ATA drive ID block */
Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
Argument->Type = ACPI_METHOD_ARGUMENT_BUFFER;
Argument->DataLength = sizeof(*IdBlock2);
if (IdBlock2)
RtlCopyMemory(Argument->Data, IdBlock2, sizeof(*IdBlock2));
else
RtlZeroMemory(Argument->Data, sizeof(*IdBlock2));
/* Evaluate the _STM method */
Status = AtaAcpiEvaluateObject(DeviceObject, InputBuffer, InputSize, NULL, 0);
if (!NT_SUCCESS(Status))
{
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
INFO("Failed to set transfer timings, status 0x%lx\n", Status);
else
ERR("Failed to set transfer timings, status 0x%lx\n", Status);
}
ExFreePoolWithTag(InputBuffer, TAG_PCIIDEX);
return Status;
}
CODE_SEG("PAGE")
PVOID
AtaAcpiGetTaskFile(
_In_ PDEVICE_OBJECT DeviceObject)
{
ACPI_EVAL_INPUT_BUFFER InputBuffer;
PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
ULONG RetryCount, OutputSize;
NTSTATUS Status;
PAGED_CODE();
/*
* We invoke this routine within the PnP START handler only,
* and thus specify that the code is in a pageable section.
*/
ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess);
InputBuffer.MethodNameAsUlong = 'FTG_'; // _GTF
InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
/*
* The output buffer must be large enough to hold the list of ATA commands to the drive.
* We assume that 10 commands is the common case.
*/
OutputSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ATA_ACPI_TASK_FILE) * 10);
for (RetryCount = 0; RetryCount < 2; ++RetryCount)
{
OutputBuffer = ExAllocatePoolZero(NonPagedPool, OutputSize, TAG_PCIIDEX);
if (!OutputBuffer)
{
ERR("Failed to allocate memory of size %lu\n", OutputSize);
return NULL;
}
/* Evaluate the _GTF method */
Status = AtaAcpiEvaluateObject(DeviceObject,
&InputBuffer,
sizeof(InputBuffer),
OutputBuffer,
OutputSize);
/* Increase the allocation size if it's too small */
if (Status == STATUS_BUFFER_OVERFLOW)
{
OutputSize = OutputBuffer->Length;
ExFreePoolWithTag(OutputBuffer, TAG_PCIIDEX);
continue;
}
break;
}
if (!NT_SUCCESS(Status))
goto Cleanup;
if (OutputBuffer->Argument[0].Type != ACPI_METHOD_ARGUMENT_BUFFER)
{
ERR("Unexpected method type %u\n", OutputBuffer->Argument[0].Type);
goto Cleanup;
}
if (OutputBuffer->Argument[0].DataLength % sizeof(ATA_ACPI_TASK_FILE))
{
ERR("Incorrect command stream length %u\n", OutputBuffer->Argument[0].DataLength);
goto Cleanup;
}
if (OutputBuffer->Argument[0].DataLength == 0)
{
WARN("Empty command list\n");
goto Cleanup;
}
return OutputBuffer;
Cleanup:
ExFreePoolWithTag(OutputBuffer, TAG_PCIIDEX);
return NULL;
}
CODE_SEG("PAGE")
VOID
AtaAcpiSetDeviceData(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIDENTIFY_DEVICE_DATA IdBlock)
{
PACPI_EVAL_INPUT_BUFFER_COMPLEX InputBuffer;
PACPI_METHOD_ARGUMENT Argument;
NTSTATUS Status;
ULONG InputSize;
PAGED_CODE();
ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess);
InputSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*IdBlock));
InputBuffer = ExAllocatePoolUninitialized(NonPagedPool, InputSize, TAG_PCIIDEX);
if (!InputBuffer)
{
ERR("Failed to allocate memory\n");
return;
}
InputBuffer->MethodNameAsUlong = 'DDS_'; // _SDD
InputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
InputBuffer->ArgumentCount = 1;
InputBuffer->Size = InputSize;
/* Argument 1: The ATA drive ID block */
Argument = InputBuffer->Argument;
ACPI_METHOD_SET_ARGUMENT_BUFFER(Argument, IdBlock, sizeof(*IdBlock));
/* Evaluate the _SDD method */
Status = AtaAcpiEvaluateObject(DeviceObject, InputBuffer, InputSize, NULL, 0);
if (!NT_SUCCESS(Status))
{
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
TRACE("Failed to set device data, status 0x%lx\n", Status);
else
WARN("Failed to set device data, status 0x%lx\n", Status);
}
ExFreePoolWithTag(InputBuffer, TAG_PCIIDEX);
}

View File

@@ -0,0 +1,563 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: AHCI header file
* COPYRIGHT: Copyright 2026 <di.sean@protonmail.com>
*/
#pragma once
#define AHCI_MAX_PORTS 32
#define AHCI_MAX_PORT_DEVICES 1
#define AHCI_MAX_PMP_DEVICES 15
#define AHCI_MAX_COMMAND_SLOTS 32
#define AHCI_MAX_PRDT_ENTRIES 0x000FFFFF // 65535
#define AHCI_MAX_PRD_LENGTH 0x003FFFFF // 4MB
#define AHCI_PMP_CONTROL_PORT 15
#define AHCI_COMMAND_TABLE_ALIGNMENT 128
#define AHCI_RECEIVED_FIS_ALIGNMENT 256
#define AHCI_COMMAND_LIST_ALIGNMENT 1024
#define AHCI_RECEIVED_FIS_FBS_ALIGNMENT 4096
#define AHCI_FBS_RECEIVE_AREA_SIZE 4096
#define AHCI_DELAY_1_SECOND (1000 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_CR_START_STOP (500 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_FR_START_STOP (500 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_DET_PRESENCE (40 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_DET_STABLE (200 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_READY_DRIVE (10000 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_PMP_READY_DRIVE (400 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_PMP_DET_PRESENSE (100 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_PMP_DET_STABLE (300 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_CLO_CLEAR (10 / PORT_TIMER_TICK_MS)
#define AHCI_DELAY_INTERFACE_CHANGE (10 / PORT_TIMER_TICK_MS)
#define AHCI_FIS_REGISTER_HOST_TO_DEVICE 0x27
#define AHCI_FIS_REGISTER_DEVICE_TO_HOST 0x34
#define AHCI_FIS_DMA_ACTIVATE_DEVICE_TO_HOST 0x39
#define AHCI_FIS_DMA_SETUP 0x41
#define AHCI_FIS_DATA 0x46
#define AHCI_FIS_BIST_ACTIVATE 0x58
#define AHCI_FIS_PIO_SETUP_DEVICE_TO_HOST 0x5F
#define AHCI_FIS_SET_DEVICE_BITS_DEVICE_TO_HOST 0xA1
#define IDE_COMMAND_READ_PORT_MULTIPLIER 0xE4
#define IDE_COMMAND_WRITE_PORT_MULTIPLIER 0xE8
typedef enum _SATA_SCR_REGISTER
{
ATA_SSTATUS = 0,
ATA_SERROR = 1,
ATA_SCONTROL = 2,
ATA_SACTIVE = 3,
ATA_SNOTIFICATION = 4,
} SATA_SCR_REGISTER;
#define AHCI_INTERNAL_SLOT 0
/**
* Port Multiplier Registers
*/
typedef enum _AHCI_PORT_MULTIPLIER_REGISTER
{
PmpProductId = 0,
PmpRevisionInfo = 1,
PmpPortInfo = 2,
PmpErrorInfo = 32,
PmpErrorControl = 33,
PmpPhyEventCounterControl = 34,
PmpCapabilities = 64,
PmpFeaturesEnabled = 96,
#define AHCI_PMP_BIST 0x00000001
#define AHCI_PMP_PMREQ 0x00000002
#define AHCI_PMP_SSC 0x00000004
#define AHCI_PMP_SNTF 0x00000008
} AHCI_PORT_MULTIPLIER_REGISTER;
typedef enum _AHCI_HOST_BUS_ADAPTER_REGISTER
{
HbaCapabilities = 0x00,
HbaGlobalControl = 0x04,
HbaInterruptStatus = 0x08,
HbaPortBitmap = 0x0C,
HbaAhciVersion = 0x10,
HbaCoalescingControl = 0x14,
HbaCoalescingPorts = 0x18,
HbaEnclosureManagementLocation = 0x1C,
HbaEnclosureManagementControl = 0x20,
HbaCapabilitiesEx = 0x24,
HbaBiosHandoffControl = 0x28,
} AHCI_HOST_BUS_ADAPTER_REGISTER;
typedef enum _AHCI_PORT_REGISTER
{
PxCommandListBaseLow = 0x00,
PxCommandListBaseHigh = 0x04,
PxFisBaseLow = 0x08,
PxFisBaseHigh = 0x0C,
PxInterruptStatus = 0x10,
PxInterruptEnable = 0x14,
PxCmdStatus = 0x18,
PxTaskFileData = 0x20,
PxSignature = 0x24,
PxSataStatus = 0x28,
PxSataControl = 0x2C,
PxSataError = 0x30,
PxSataActive = 0x34,
PxCommandIssue = 0x38,
PxSataNotification = 0x3C,
PxFisSwitchingControl = 0x40,
PxDeviceSleep = 0x44,
} AHCI_PORT_REGISTER;
/*
* Interrupt Status/Enable Register
*/
#define AHCI_PXIRQ_DHRS 0x00000001
#define AHCI_PXIRQ_PSS 0x00000002
#define AHCI_PXIRQ_DSS 0x00000004
#define AHCI_PXIRQ_SDBS 0x00000008
#define AHCI_PXIRQ_UFS 0x00000010
#define AHCI_PXIRQ_DPS 0x00000020
#define AHCI_PXIRQ_PCS 0x00000040
#define AHCI_PXIRQ_DMPS 0x00000080
#define AHCI_PXIRQ_RSV1 0x003FFF00
#define AHCI_PXIRQ_PRCS 0x00400000
#define AHCI_PXIRQ_IPMS 0x00800000
#define AHCI_PXIRQ_OFS 0x01000000
#define AHCI_PXIRQ_RSV2 0x02000000
#define AHCI_PXIRQ_INFS 0x04000000
#define AHCI_PXIRQ_IFS 0x08000000
#define AHCI_PXIRQ_HBDS 0x10000000
#define AHCI_PXIRQ_HBFS 0x20000000
#define AHCI_PXIRQ_TFES 0x40000000
#define AHCI_PXIRQ_CPDS 0x80000000
#define AHCI_PXIRQ_FATAL_ERROR \
(AHCI_PXIRQ_TFES | AHCI_PXIRQ_IFS | AHCI_PXIRQ_HBDS | AHCI_PXIRQ_HBFS)
#define AHCI_PXIRQ_PORT_STATUS \
(AHCI_PXIRQ_PCS | AHCI_PXIRQ_PRCS | AHCI_PXIRQ_DMPS)
/*
* Command and Status Register
*/
#define AHCI_PXCMD_ST 0x00000001
#define AHCI_PXCMD_SUD 0x00000002
#define AHCI_PXCMD_POD 0x00000004
#define AHCI_PXCMD_CLO 0x00000008
#define AHCI_PXCMD_FRE 0x00000010
#define AHCI_PXCMD_RSV 0x000000E0
#define AHCI_PXCMD_CCS_MASK 0x00001F00
#define AHCI_PXCMD_MPSS 0x00002000
#define AHCI_PXCMD_FR 0x00004000
#define AHCI_PXCMD_CR 0x00008000
#define AHCI_PXCMD_CPS 0x00010000
#define AHCI_PXCMD_PMA 0x00020000
#define AHCI_PXCMD_HPCP 0x00040000
#define AHCI_PXCMD_MPSP 0x00080000
#define AHCI_PXCMD_CPD 0x00100000
#define AHCI_PXCMD_ESP 0x00200000
#define AHCI_PXCMD_FBSCP 0x00400000
#define AHCI_PXCMD_APSTE 0x00800000
#define AHCI_PXCMD_ATAPI 0x01000000
#define AHCI_PXCMD_DLAE 0x02000000
#define AHCI_PXCMD_ALPE 0x04000000
#define AHCI_PXCMD_ASP 0x08000000
#define AHCI_PXCMD_ICC_MASK 0xF0000000
#define AHCI_PXCMD_ICC_IDLE 0x00000000
#define AHCI_PXCMD_ICC_ACTIVE 0x10000000
#define AHCI_PXCMD_ICC_PARTIAL 0x20000000
#define AHCI_PXCMD_ICC_SLUMBER 0x60000000
#define AHCI_PXCMD_ICC_DEVSLEEP 0x80000000
#define AHCI_PXCMD_ICC_DEVSLEEP 0x80000000
#define AHCI_PXCMD_CCS_SHIFT 8
#define AHCI_PXCMD_CCS(Value) (((Value) & AHCI_PXCMD_CCS_MASK) >> AHCI_PXCMD_CCS_SHIFT)
/*
* Task File Data Register
*/
#define AHCI_PXTFD_STATUS_MASK 0x000000FF
#define AHCI_PXTFD_ERROR_MASK 0x0000FF00
#define AHCI_PXTFD_ERROR_SHIFT 8
/*
* Signature Register
*/
#define AHCI_PXSIG_INVALID 0xFFFFFFFF
#define AHCI_PXSIG_ATAPI 0xEB140101
#define AHCI_PXSIG_PMP 0x96690101
/*
* Some PATA ATAPI devices (NEC CDR-C251) do not update the Sector Count and LBA Low registers.
* Just to be safe, the contents of these registers is also ignored for AHCI devices.
*/
#define AHCI_PXSIG_MASK 0xFFFF0000
/*
* Serial ATA Status Register
*/
#define AHCI_PXSSTS_DET_MASK 0x0000000F
#define AHCI_PXSSTS_SPD_MASK 0x000000F0
#define AHCI_PXSSTS_IPM_MASK 0x00000F00
#define AHCI_PXSSTS_DET_NO_DEVICE 0x00000000
#define AHCI_PXSSTS_DET_PHY_NOT_READY 0x00000001
#define AHCI_PXSSTS_DET_PHY_OK 0x00000003
#define AHCI_PXSSTS_DET_PHY_OFFLINE 0x00000004
#define AHCI_PXSSTS_SPD_UNKNOWN 0x00000000
#define AHCI_PXSSTS_SPD_SATA1 0x00000010
#define AHCI_PXSSTS_SPD_SATA2 0x00000020
#define AHCI_PXSSTS_SPD_SATA3 0x00000030
#define AHCI_PXSSTS_IPM_NO_DEVICE 0x00000000
#define AHCI_PXSSTS_IPM_ACTIVE 0x00000100
#define AHCI_PXSSTS_IPM_PARTIAL 0x00000200
#define AHCI_PXSSTS_IPM_SLUMBER 0x00000600
#define AHCI_PXSSTS_IPM_DEVSLEEP 0x00000800
/*
* Serial ATA Control Register
*/
#define AHCI_PXCTL_DET_MASK 0x0000000F
#define AHCI_PXCTL_SPD_MASK 0x000000F0
#define AHCI_PXCTL_IPM_MASK 0x00000F00
#define AHCI_PXCTL_DET_IDLE 0x00000000
#define AHCI_PXCTL_DET_RESET 0x00000001
#define AHCI_PXCTL_DET_DISABLE_SATA 0x00000004
#define AHCI_PXCTL_SPD_LIMIT_NONE 0x00000000
#define AHCI_PXCTL_SPD_LIMIT_SATA1 0x00000010
#define AHCI_PXCTL_SPD_LIMIT_SATA2 0x00000020
#define AHCI_PXCTL_SPD_LIMIT_SATA3 0x00000030
#define AHCI_PXCTL_SPD_LIMIT_LEVEL 0x00000010
#define AHCI_PXCTL_IPM_DISABLE_NONE 0x00000000
#define AHCI_PXCTL_IPM_DISABLE_PARTIAL 0x00000100
#define AHCI_PXCTL_IPM_DISABLE_SLUMBER 0x00000200
#define AHCI_PXCTL_IPM_DISABLE_DEVSLEEP 0x00000400
#define AHCI_PXCTL_IPM_DISABLE_ALL 0x00000700
/*
* FIS-based Switching Control Register
*/
#define AHCI_FBS_ENABLE 0x00000001
#define AHCI_FBS_DEV_ERROR_CLEAR 0x00000002
#define AHCI_FBS_SINGLE_DEV_ERROR 0x00000004
#define AHCI_FBS_ISSUE_MASK 0x00000F00
#define AHCI_FBS_ACTIVE_DEV_OPT_MASK 0x0000F000
#define AHCI_FBS_DEV_WITH_ERROR_MASK 0x000F0000
#define AHCI_FBS_ISSUE_SHIFT 8
/*
* Device Sleep Register
*/
#define AHCI_PXDEVSLP_ADSE 0x00000001
#define AHCI_PXDEVSLP_DSP 0x00000002
#define AHCI_PXDEVSLP_DETO_MASK 0x000003FC
#define AHCI_PXDEVSLP_MDAT_MASK 0x00007C00
#define AHCI_PXDEVSLP_DITO_MASK 0x01FF8000
#define AHCI_PXDEVSLP_DM_MASK 0x1E000000
#define AHCI_PORT_INTERRUPT_MASK \
(AHCI_PXIRQ_DHRS | \
AHCI_PXIRQ_PSS | \
AHCI_PXIRQ_DSS | \
AHCI_PXIRQ_SDBS | \
AHCI_PXIRQ_UFS | \
AHCI_PXIRQ_DPS | \
AHCI_PXIRQ_PCS | \
AHCI_PXIRQ_DMPS | \
AHCI_PXIRQ_PRCS | \
AHCI_PXIRQ_IPMS | \
AHCI_PXIRQ_OFS | \
AHCI_PXIRQ_INFS | \
AHCI_PXIRQ_IFS | \
AHCI_PXIRQ_HBDS | \
AHCI_PXIRQ_HBFS | \
AHCI_PXIRQ_TFES | \
AHCI_PXIRQ_CPDS)
/*
* AHCI Version Register
*/
#define AHCI_VERSION_0_95 0x00000905
#define AHCI_VERSION_1_0 0x00010000
#define AHCI_VERSION_1_2 0x00010200
#define AHCI_VERSION_1_3_0 0x00010300
#define AHCI_VERSION_1_3_1 0x00010301
/*
* Global HBA Control Register
*/
#define AHCI_GHC_HR 0x00000001
#define AHCI_GHC_IE 0x00000002
#define AHCI_GHC_MRSM 0x00000004
#define AHCI_GHC_AE 0x80000000
/*
* HBA Capabilities Register
*/
#define AHCI_CAP_NP 0x0000001F
#define AHCI_CAP_SXS 0x00000020
#define AHCI_CAP_EMS 0x00000040
#define AHCI_CAP_CCCS 0x00000080
#define AHCI_CAP_NCS 0x00001F00
#define AHCI_CAP_PSC 0x00002000
#define AHCI_CAP_SSC 0x00004000
#define AHCI_CAP_PMD 0x00008000
#define AHCI_CAP_FBSS 0x00010000
#define AHCI_CAP_SPM 0x00020000
#define AHCI_CAP_SAM 0x00040000
#define AHCI_CAP_RSV 0x00080000
#define AHCI_CAP_ISS 0x00F00000
#define AHCI_CAP_SCLO 0x01000000
#define AHCI_CAP_SAL 0x02000000
#define AHCI_CAP_SALP 0x04000000
#define AHCI_CAP_SSS 0x08000000
#define AHCI_CAP_SMPS 0x10000000
#define AHCI_CAP_SSNTF 0x20000000
#define AHCI_CAP_SNCQ 0x40000000
#define AHCI_CAP_S64A 0x80000000
/*
* HBA Capabilities Extended Register
*/
#define AHCI_CAP2_BOH 0x00000001
#define AHCI_CAP2_NVMP 0x00000002
#define AHCI_CAP2_APST 0x00000004
#define AHCI_CAP2_SDS 0x00000008
#define AHCI_CAP2_SADM 0x00000010
#define AHCI_CAP2_DESO 0x00000020
#define AHCI_BOHC_BIOS_SEMAPHORE 0x00000001
#define AHCI_BOHC_OS_SEMAPHORE 0x00000002
#define AHCI_BOHC_SMI_ON_OS_OWNERSHIP_CHANGE 0x00000004
#define AHCI_BOHC_OS_OWNERSHIP_CHANGE 0x00000008
#define AHCI_BOHC_BIOS_BUSY 0x00000010
#include <pshpack1.h>
typedef struct _AHCI_FIS_HOST_TO_DEVICE
{
UCHAR Type; // 0x27
UCHAR Flags;
#define UPDATE_COMMAND 0x80
#define PMP_NUMBER 0x0F
UCHAR Command;
UCHAR Features;
UCHAR LbaLow;
UCHAR LbaMid;
UCHAR LbaHigh;
UCHAR Device;
UCHAR LbaLowEx;
UCHAR LbaMidEx;
UCHAR LbaHighEx;
UCHAR FeaturesEx;
UCHAR SectorCount;
UCHAR SectorCountEx;
UCHAR Icc;
UCHAR Control;
ULONG Auxiliary;
} AHCI_FIS_HOST_TO_DEVICE, *PAHCI_FIS_HOST_TO_DEVICE;
C_ASSERT(sizeof(AHCI_FIS_HOST_TO_DEVICE) == 20);
typedef struct _AHCI_FIS_PIO_SETUP
{
UCHAR Type; // 0x5F
UCHAR Flags;
UCHAR Status;
UCHAR Error;
UCHAR LbaLow;
UCHAR LbaMid;
UCHAR LbaHigh;
UCHAR Device;
UCHAR LbaLowEx;
UCHAR LbaMidEx;
UCHAR LbaHighEx;
UCHAR Reserved;
UCHAR SectorCount;
UCHAR SectorCountEx;
UCHAR Reserved1;
UCHAR EStatus;
USHORT TransferCount;
USHORT Reserved2;
} AHCI_FIS_PIO_SETUP, *PAHCI_FIS_PIO_SETUP;
C_ASSERT(sizeof(AHCI_FIS_PIO_SETUP) == 20);
typedef struct _AHCI_FIS_DEVICE_TO_HOST
{
UCHAR Type; // 0x34
UCHAR Flags;
UCHAR Status;
UCHAR Error;
UCHAR LbaLow;
UCHAR LbaMid;
UCHAR LbaHigh;
UCHAR Device;
UCHAR LbaLowEx;
UCHAR LbaMidEx;
UCHAR LbaHighEx;
UCHAR Reserved;
UCHAR SectorCount;
UCHAR SectorCountEx;
UCHAR Reserved1;
UCHAR Reserved2;
ULONG Reserved3;
} AHCI_FIS_DEVICE_TO_HOST, *PAHCI_FIS_DEVICE_TO_HOST;
C_ASSERT(sizeof(AHCI_FIS_DEVICE_TO_HOST) == 20);
typedef struct _AHCI_FIS_SET_DEVICE_BITS
{
UCHAR Type; // 0xA1
UCHAR Flags;
UCHAR Status;
UCHAR Error;
ULONG Reserved;
} AHCI_FIS_SET_DEVICE_BITS, *PAHCI_FIS_SET_DEVICE_BITS;
C_ASSERT(sizeof(AHCI_FIS_SET_DEVICE_BITS) == 8);
typedef struct _AHCI_RECEIVED_FIS
{
UCHAR DmaSetupFis[0x1C];
ULONG Reserved;
AHCI_FIS_PIO_SETUP PioSetupFis;
ULONG Reserved2[3];
AHCI_FIS_DEVICE_TO_HOST DeviceToHostFis;
ULONG Reserved3;
AHCI_FIS_SET_DEVICE_BITS SetDeviceBitsFis;
UCHAR UnknownFis[0x40];
UCHAR Reserved4[0x60];
} AHCI_RECEIVED_FIS, *PAHCI_RECEIVED_FIS;
C_ASSERT(sizeof(AHCI_RECEIVED_FIS) == 256);
typedef struct _AHCI_COMMAND_HEADER
{
ULONG Control;
#define AHCI_COMMAND_HEADER_COMMAND_FIS_LENGTH 0x0000001F
#define AHCI_COMMAND_HEADER_ATAPI 0x00000020
#define AHCI_COMMAND_HEADER_WRITE 0x00000040
#define AHCI_COMMAND_HEADER_PREFETCHABLE 0x00000080
#define AHCI_COMMAND_HEADER_RESET 0x00000100
#define AHCI_COMMAND_HEADER_BIST 0x00000200
#define AHCI_COMMAND_HEADER_CLEAR_BUSY_UPON_OK 0x00000400
#define AHCI_COMMAND_HEADER_PMP 0x0000F000
#define AHCI_COMMAND_HEADER_PRDT_LENGTH 0xFFFF0000
#define AHCI_COMMAND_HEADER_PRDT_LENGTH_SHIFT 16
#define AHCI_COMMAND_HEADER_PMP_SHIFT 12
ULONG PrdByteCount;
ULONG CommandTableBaseLow;
ULONG CommandTableBaseHigh;
ULONG Reserved[4];
} AHCI_COMMAND_HEADER, *PAHCI_COMMAND_HEADER;
C_ASSERT(sizeof(AHCI_COMMAND_HEADER) == 32);
typedef struct _AHCI_COMMAND_LIST
{
AHCI_COMMAND_HEADER CommandHeader[ANYSIZE_ARRAY];
} AHCI_COMMAND_LIST, *PAHCI_COMMAND_LIST;
typedef struct _AHCI_PRD_TABLE_ENTRY
{
ULONG DataBaseLow;
ULONG DataBaseHigh;
ULONG Reserved;
ULONG ByteCount;
#define AHCI_PRD_INTERRUPT_ON_COMPLETION 0x80000000
} AHCI_PRD_TABLE_ENTRY, *PAHCI_PRD_TABLE_ENTRY;
C_ASSERT(sizeof(AHCI_PRD_TABLE_ENTRY) == 16);
typedef struct _AHCI_COMMAND_TABLE
{
union
{
AHCI_FIS_HOST_TO_DEVICE HostToDeviceFis;
UCHAR CommandFis[64];
};
UCHAR AtapiCommand[16];
UCHAR Reserved[48];
AHCI_PRD_TABLE_ENTRY PrdTable[ANYSIZE_ARRAY];
} AHCI_COMMAND_TABLE, *PAHCI_COMMAND_TABLE;
C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, PrdTable) == 128);
#include <poppack.h>
#define AHCI_PORT_BASE(HbaIoBase, PortNumber) \
(PULONG)((ULONG_PTR)(HbaIoBase) + (PortNumber) * 0x80 + 0x100)
FORCEINLINE
ULONG
AHCI_HBA_READ(
_In_ PVOID HbaIoBase,
_In_ AHCI_HOST_BUS_ADAPTER_REGISTER Register)
{
return READ_REGISTER_ULONG((PULONG)((ULONG_PTR)HbaIoBase + Register));
}
FORCEINLINE
VOID
AHCI_HBA_WRITE(
_In_ PVOID HbaIoBase,
_In_ AHCI_HOST_BUS_ADAPTER_REGISTER Register,
_In_ ULONG Value)
{
WRITE_REGISTER_ULONG((PULONG)((ULONG_PTR)HbaIoBase + Register), Value);
}
FORCEINLINE
ULONG
AHCI_PORT_READ(
_In_ PVOID PortIoBase,
_In_ AHCI_PORT_REGISTER Register)
{
return READ_REGISTER_ULONG((PULONG)((ULONG_PTR)PortIoBase + Register));
}
FORCEINLINE
VOID
AHCI_PORT_WRITE(
_In_ PVOID PortIoBase,
_In_ AHCI_PORT_REGISTER Register,
_In_ ULONG Value)
{
WRITE_REGISTER_ULONG((PULONG)((ULONG_PTR)PortIoBase + Register), Value);
}

View File

@@ -0,0 +1,752 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: AHCI controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
PCIIDEX_PAGED_DATA
static const struct
{
USHORT VendorID;
USHORT DeviceID;
} AhciControllerList[] =
{
{ PCI_VEN_NVIDIA, 0x0550 },
{ PCI_VEN_NVIDIA, 0x0584 },
};
/* FUNCTIONS ******************************************************************/
static
CODE_SEG("PAGE")
NTSTATUS
AtaAhciAllocateMemory(
_In_ PVOID ChannelContext)
{
PCHANNEL_DATA_AHCI ChanData = ChannelContext;
PDMA_OPERATIONS DmaOperations = ChanData->DmaAdapter->DmaOperations;
ULONG i, j, SlotNumber, CommandSlots, BlockSize;
ULONG CommandListSize, CommandTableLength, CommandTablesPerPage;
PVOID Buffer;
ULONG_PTR BufferVa;
ULONG64 BufferPa;
PHYSICAL_ADDRESS PhysicalAddress;
PAGED_CODE();
/* The maximum size of the command list is 1024 bytes */
CommandSlots = ChanData->Controller->QueueDepth;
CommandListSize = FIELD_OFFSET(AHCI_COMMAND_LIST, CommandHeader[CommandSlots]);
BlockSize = CommandListSize + (AHCI_COMMAND_LIST_ALIGNMENT - 1);
/* Add the receive area structure (256 bytes) */
if (!(ChanData->ChanInfo & CHANNEL_FLAG_HAS_FBS))
{
BlockSize += sizeof(AHCI_RECEIVED_FIS);
/* The command list is 1024-byte aligned, which saves us some bytes of allocation size */
BlockSize += ALIGN_UP_BY(CommandListSize, AHCI_RECEIVED_FIS_ALIGNMENT) - CommandListSize;
}
Buffer = DmaOperations->AllocateCommonBuffer(ChanData->DmaAdapter,
BlockSize,
&PhysicalAddress,
TRUE); // Cached
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(Buffer, BlockSize);
ChanData->Mem.CommandListSize = BlockSize;
ChanData->Mem.CommandListOriginal = Buffer;
ChanData->Mem.CommandListPhysOriginal.QuadPart = PhysicalAddress.QuadPart;
BufferVa = (ULONG_PTR)Buffer;
BufferPa = PhysicalAddress.QuadPart;
/* Command list */
BufferVa = ALIGN_UP_BY(BufferVa, AHCI_COMMAND_LIST_ALIGNMENT);
BufferPa = ALIGN_UP_BY(BufferPa, AHCI_COMMAND_LIST_ALIGNMENT);
ChanData->CommandList = (PVOID)BufferVa;
ChanData->Mem.CommandListPhys = BufferPa;
BufferVa += CommandListSize;
BufferPa += CommandListSize;
/* Alignment requirement */
ASSERT((ULONG_PTR)ChanData->Mem.CommandListPhys % AHCI_COMMAND_LIST_ALIGNMENT == 0);
/* Received FIS structure */
if (!(ChanData->ChanInfo & CHANNEL_FLAG_HAS_FBS))
{
BufferVa = ALIGN_UP_BY(BufferVa, AHCI_RECEIVED_FIS_ALIGNMENT);
BufferPa = ALIGN_UP_BY(BufferPa, AHCI_RECEIVED_FIS_ALIGNMENT);
ChanData->ReceivedFis = (PVOID)BufferVa;
ChanData->Mem.ReceivedFisPhys = BufferPa;
BufferVa += sizeof(AHCI_RECEIVED_FIS);
BufferPa += sizeof(AHCI_RECEIVED_FIS);
/* Alignment requirement */
ASSERT((ULONG_PTR)ChanData->Mem.ReceivedFisPhys % AHCI_RECEIVED_FIS_ALIGNMENT == 0);
}
if (ChanData->ChanInfo & CHANNEL_FLAG_HAS_FBS)
{
/* The FBS receive area is 4kB, allocate a page which is also 4kB-aligned */
BlockSize = PAGE_SIZE;
/*
* Some other architectures, like ia64, use a different page size,
* that is a multiple of 4096.
*/
C_ASSERT(PAGE_SIZE % AHCI_RECEIVED_FIS_FBS_ALIGNMENT == 0);
Buffer = DmaOperations->AllocateCommonBuffer(ChanData->DmaAdapter,
BlockSize,
&PhysicalAddress,
TRUE);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(Buffer, BlockSize);
ChanData->Mem.ReceivedFisOriginal = Buffer;
ChanData->Mem.ReceivedFisPhysOriginal.QuadPart = PhysicalAddress.QuadPart;
BufferVa = (ULONG_PTR)Buffer;
BufferPa = PhysicalAddress.QuadPart;
ChanData->ReceivedFis = (PVOID)BufferVa;
ChanData->Mem.ReceivedFisPhys = BufferPa;
/* Alignment requirement */
ASSERT(BufferPa % AHCI_RECEIVED_FIS_FBS_ALIGNMENT == 0);
}
/* 32-bit DMA */
if (!(ChanData->ChanInfo & CHANNEL_FLAG_64_BIT_DMA))
{
ASSERT((ULONG)(ChanData->Mem.CommandListPhys >> 32) == 0);
ASSERT((ULONG)(ChanData->Mem.ReceivedFisPhys >> 32) == 0);
}
CommandTableLength = FIELD_OFFSET(AHCI_COMMAND_TABLE, PrdTable[ChanData->MaximumPhysicalPages]);
ASSERT(ChanData->MaximumPhysicalPages != 0 &&
ChanData->MaximumPhysicalPages <= AHCI_MAX_PRDT_ENTRIES);
/*
* See ATA_MAX_TRANSFER_LENGTH, currently the MaximumPhysicalPages is restricted to
* a maximum of (0x20000 / PAGE_SIZE) + 1 = 33 pages.
* Each command table will require us 128 + 16 * 33 + (128 - 1) = 783 bytes of shared memory.
*/
ASSERT(PAGE_SIZE > (CommandTableLength + (AHCI_COMMAND_TABLE_ALIGNMENT - 1)));
/* Allocate one-page chunks to avoid having a large chunk of contiguous memory */
CommandTablesPerPage = PAGE_SIZE / (CommandTableLength + (AHCI_COMMAND_TABLE_ALIGNMENT - 1));
/* Command tables allocation loop */
SlotNumber = 0;
i = CommandSlots;
while (i > 0)
{
ULONG TableCount;
TableCount = min(i, CommandTablesPerPage);
BlockSize = (CommandTableLength + (AHCI_COMMAND_TABLE_ALIGNMENT - 1)) * TableCount;
/* Allocate a chunk of memory */
Buffer = DmaOperations->AllocateCommonBuffer(ChanData->DmaAdapter,
BlockSize,
&PhysicalAddress,
TRUE);
if (!Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(Buffer, BlockSize);
ChanData->Mem.CommandTableOriginal[SlotNumber] = Buffer;
ChanData->Mem.CommandTablePhysOriginal[SlotNumber].QuadPart = PhysicalAddress.QuadPart;
ChanData->Mem.CommandTableSize[SlotNumber] = BlockSize;
BufferVa = (ULONG_PTR)Buffer;
BufferPa = PhysicalAddress.QuadPart;
/* Split the allocation into command tables */
for (j = 0; j < TableCount; ++j)
{
PAHCI_COMMAND_HEADER CommandHeader;
BufferVa = ALIGN_UP_BY(BufferVa, AHCI_COMMAND_TABLE_ALIGNMENT);
BufferPa = ALIGN_UP_BY(BufferPa, AHCI_COMMAND_TABLE_ALIGNMENT);
/* Alignment requirement */
ASSERT(BufferPa % AHCI_COMMAND_TABLE_ALIGNMENT == 0);
/* 32-bit DMA */
if (!(ChanData->ChanInfo & CHANNEL_FLAG_64_BIT_DMA))
{
ASSERT((ULONG)(BufferPa >> 32) == 0);
}
ChanData->CommandTable[SlotNumber] = (PAHCI_COMMAND_TABLE)BufferVa;
CommandHeader = &ChanData->CommandList->CommandHeader[SlotNumber];
CommandHeader->CommandTableBaseLow = (ULONG)BufferPa;
CommandHeader->CommandTableBaseHigh = (ULONG)(BufferPa >> 32);
++SlotNumber;
BufferVa += CommandTableLength;
BufferPa += CommandTableLength;
}
i -= TableCount;
}
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
VOID
AtaAhciFreeMemory(
_In_ PVOID ChannelContext)
{
PCHANNEL_DATA_AHCI ChanData = ChannelContext;
PDMA_ADAPTER DmaAdapter = ChanData->DmaAdapter;
PDMA_OPERATIONS DmaOperations = ChanData->DmaAdapter->DmaOperations;
ULONG i;
PAGED_CODE();
if (ChanData->Mem.CommandListOriginal)
{
DmaOperations->FreeCommonBuffer(DmaAdapter,
ChanData->Mem.CommandListSize,
ChanData->Mem.CommandListPhysOriginal,
ChanData->Mem.CommandListOriginal,
TRUE); // Cached
ChanData->Mem.CommandListOriginal = NULL;
}
if (ChanData->Mem.ReceivedFisOriginal)
{
DmaOperations->FreeCommonBuffer(DmaAdapter,
PAGE_SIZE,
ChanData->Mem.ReceivedFisPhysOriginal,
ChanData->Mem.ReceivedFisOriginal,
TRUE);
ChanData->Mem.ReceivedFisOriginal = NULL;
}
for (i = 0; i < AHCI_MAX_COMMAND_SLOTS; ++i)
{
if (ChanData->Mem.CommandTableOriginal[i])
{
DmaOperations->FreeCommonBuffer(DmaAdapter,
ChanData->Mem.CommandTableSize[i],
ChanData->Mem.CommandTablePhysOriginal[i],
ChanData->Mem.CommandTableOriginal[i],
TRUE);
ChanData->Mem.CommandTableOriginal[i] = NULL;
}
}
}
#if DBG
static
CODE_SEG("PAGE")
BOOLEAN
AtaAhciIsVbox(VOID)
{
UCHAR Buffer[RTL_SIZEOF_THROUGH_FIELD(PCI_COMMON_HEADER, DeviceID)];
PPCI_COMMON_HEADER PciData = (PPCI_COMMON_HEADER)Buffer; // Partial PCI header
ULONG BytesRead;
PCI_SLOT_NUMBER Slot;
PAGED_CODE();
Slot.u.AsULONG = 0;
Slot.u.bits.DeviceNumber = 4;
Slot.u.bits.FunctionNumber = 0;
BytesRead = HalGetBusDataByOffset(PCIConfiguration,
0,
Slot.u.AsULONG,
&Buffer,
FIELD_OFFSET(PCI_COMMON_HEADER, VendorID),
sizeof(Buffer));
return (BytesRead == sizeof(Buffer)) &&
(PciData->VendorID == 0x80EE) &&
(PciData->DeviceID == 0xCAFE);
}
#endif
static
CODE_SEG("PAGE")
VOID
AtaAhciHbaRequestOsOwnership(
_In_ PVOID IoBase)
{
ULONG i, Control;
PAGED_CODE();
Control = AHCI_HBA_READ(IoBase, HbaBiosHandoffControl);
if (Control & AHCI_BOHC_OS_SEMAPHORE)
return;
INFO("HBA ownership change\n");
AHCI_HBA_WRITE(IoBase, HbaBiosHandoffControl, Control | AHCI_BOHC_OS_SEMAPHORE);
/* Wait up to 2 seconds */
for (i = 0; i < 200000; ++i)
{
Control = AHCI_HBA_READ(IoBase, HbaBiosHandoffControl);
if (!(Control & (AHCI_BOHC_BIOS_BUSY | AHCI_BOHC_BIOS_SEMAPHORE)))
return;
KeStallExecutionProcessor(10);
}
WARN("Unable to acquire the OS semaphore %08lx\n", Control);
}
/**
* See eSATA paper
* http://download.microsoft.com/download/7/E/7/7E7662CF-CBEA-470B-A97E-CE7CE0D98DC2/eSATA.docx
*/
static
CODE_SEG("PAGE")
BOOLEAN
AtaAhciIsPortRemovable(
_In_ ULONG AhciCapabilities,
_In_ ULONG CmdStatus)
{
PAGED_CODE();
if (CmdStatus & AHCI_PXCMD_HPCP)
return TRUE;
if ((AhciCapabilities & AHCI_CAP_SXS) && (CmdStatus & AHCI_PXCMD_ESP))
return TRUE;
if ((AhciCapabilities & AHCI_CAP_SMPS) && (CmdStatus & AHCI_PXCMD_MPSP))
return TRUE;
return FALSE;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaAhciCreateChannelData(
_In_ PATA_CONTROLLER Controller)
{
PCHANNEL_DATA_AHCI ChanData;
ULONG i;
PAGED_CODE();
ASSERT(Controller->MaxChannels != 0);
ChanData = ExAllocatePoolZero(NonPagedPool,
sizeof(*ChanData) * Controller->MaxChannels,
TAG_PCIIDEX);
if (!ChanData)
return STATUS_INSUFFICIENT_RESOURCES;
Controller->ChanDataBlock = ChanData;
for (i = 0; i < AHCI_MAX_PORTS; ++i)
{
ULONG CmdStatus;
if (!(Controller->ChannelBitmap & (1 << i)))
continue;
Controller->Channels[i] = ChanData;
ChanData->Channel = i;
ChanData->Controller = Controller;
ChanData->AllocateMemory = AtaAhciAllocateMemory;
ChanData->FreeMemory = AtaAhciFreeMemory;
ChanData->EnableInterrupts = AtaAhciEnableInterrupts;
ChanData->PreparePrdTable = AtaAhciPreparePrdTable;
ChanData->PrepareIo = AtaAhciPrepareIo;
ChanData->StartIo = AtaAhciStartIo;
ChanData->SetTransferMode = SataSetTransferMode;
ChanData->TransferModeSupported = SATA_ALL;
ChanData->IoBase = AHCI_PORT_BASE(Controller->IoBase, i);
ChanData->EnableInterrupts(ChanData, FALSE);
/* Begin the process of stopping the command list DMA engine for later initialization */
CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus);
if (CmdStatus & AHCI_PXCMD_ST)
{
CmdStatus &= ~AHCI_PXCMD_ST;
AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus);
}
/* The AHCI HBA can only perform DMA I/O and PIO is not supported */
ChanData->ChanInfo = CHANNEL_FLAG_PIO_VIA_DMA;
if (Controller->AhciCapabilities & AHCI_CAP_S64A)
ChanData->ChanInfo |= CHANNEL_FLAG_64_BIT_DMA;
if (Controller->AhciCapabilities & AHCI_CAP_SNCQ)
ChanData->ChanInfo |= CHANNEL_FLAG_HAS_NCQ;
/* Check for the FIS-based switching feature support */
if ((Controller->AhciCapabilities & AHCI_CAP_SPM) &&
(Controller->AhciCapabilities & AHCI_CAP_FBSS) &&
(CmdStatus & AHCI_PXCMD_FBSCP))
{
INFO("CH %lu: FBS supported\n", ChanData->Channel);
ChanData->ChanInfo |= CHANNEL_FLAG_HAS_FBS;
}
if (AtaAhciIsPortRemovable(Controller->AhciCapabilities, CmdStatus))
{
INFO("CH %lu: Port is external\n", ChanData->Channel);
ChanData->ChanInfo |= CHANNEL_FLAG_IS_EXTERNAL;
}
++ChanData;
}
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
PVOID
AtaAhciGetAbar(
_Inout_ PATA_CONTROLLER Controller)
{
PVOID Abar;
ULONG i, Index;
PCIIDEX_PAGED_DATA static const struct
{
USHORT VendorID;
USHORT DeviceID;
ULONG Index;
} AbarLocations[] =
{
{ PCI_VEN_CAVIUM, 0xA01C, 0 },
};
PAGED_CODE();
/* Default index */
Index = 5;
for (i = 0; i < RTL_NUMBER_OF(AbarLocations); ++i)
{
if ((Controller->Pci.VendorID == AbarLocations[i].VendorID) &&
(Controller->Pci.DeviceID == AbarLocations[i].DeviceID))
{
Index = AbarLocations[i].Index;
break;
}
}
if (!(Controller->AccessRange[Index].Flags & RANGE_IS_MEMORY))
return NULL;
Abar = AtaCtrlPciMapBar(Controller, Index, 0);
if (!Abar)
return NULL;
return Abar;
}
static
CODE_SEG("PAGE")
NTSTATUS
AtaAhciAttachChannel(
_In_ PVOID ChannelContext,
_In_ BOOLEAN Attach)
{
PCHANNEL_DATA_AHCI ChanData = ChannelContext;
PAGED_CODE();
if (Attach)
{
/* We do enable interrupts in the QBR handler */
}
else
{
AtaChanEnableInterruptsSync(ChanData, FALSE);
AtaAhciStopDma(ChanData);
}
return STATUS_SUCCESS;
}
static
VOID
AtaAhciHbaStart(
_In_ PATA_CONTROLLER Controller)
{
ULONG GlobalControl;
GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl);
if (!(GlobalControl & AHCI_GHC_AE))
{
/* Set AE on power up */
GlobalControl |= AHCI_GHC_AE;
AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl);
}
/* Clear HBA interrupts */
AHCI_HBA_WRITE(Controller->IoBase, HbaInterruptStatus, 0xFFFFFFFF);
/* Enable interrupts */
GlobalControl |= AHCI_GHC_IE;
AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl);
}
static
VOID
AtaAhciHbaStop(
_In_ PATA_CONTROLLER Controller)
{
ULONG GlobalControl;
KIRQL OldIrql;
/* Failed to connect interrupt */
if (!Controller->InterruptObject)
return;
OldIrql = KeAcquireInterruptSpinLock(Controller->InterruptObject);
/* Disable interrupts */
GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl);
GlobalControl &= ~AHCI_GHC_IE;
AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl);
/* Clear HBA interrupts */
AHCI_HBA_WRITE(Controller->IoBase, HbaInterruptStatus, 0xFFFFFFFF);
KeReleaseInterruptSpinLock(Controller->InterruptObject, OldIrql);
}
static
CODE_SEG("PAGE")
VOID
AtaAhciHbaFreeResouces(
_In_ PATA_CONTROLLER Controller)
{
PAGED_CODE();
if (Controller->InterruptObject)
{
IoDisconnectInterrupt(Controller->InterruptObject);
Controller->InterruptObject = NULL;
}
}
static
CODE_SEG("PAGE")
BOOLEAN
AhciControllerIsSubClassCheckNeeded(
_In_ PATA_CONTROLLER Controller)
{
PAGED_CODE();
if (Controller->Pci.VendorID == PCI_VEN_INTEL)
{
/* IDE or AHCI */
if ((Controller->Pci.DeviceID == 0x2652) || (Controller->Pci.DeviceID == 0x2653))
return TRUE;
}
/* IDE, RAID, or AHCI */
if ((Controller->Pci.VendorID == PCI_VEN_VIA) && (Controller->Pci.DeviceID == 0x3349))
return TRUE;
return FALSE;
}
static
CODE_SEG("PAGE")
BOOLEAN
AhciMatchController(
_In_ PATA_CONTROLLER Controller)
{
ULONG i;
PAGED_CODE();
/* Some controllers share the same PCI ID between AHCI and IDE/RAID modes */
if (AhciControllerIsSubClassCheckNeeded(Controller))
{
if (Controller->Pci.SubClass != PCI_SUBCLASS_MSC_AHCI_CTLR)
return FALSE;
}
/*
* Match the controller through the PCI ID.
* We do not want to check the PCI subclass code because of
* some AHCI controllers that support AHCI even in IDE emulation mode.
* For example, nVidia 10DE:0550 uses the same PCI ID between AHCI and IDE modes,
* but BAR5 will always be available and AHCI can be enabled by setting GHC.AE to 1.
*/
for (i = 0; i < RTL_NUMBER_OF(AhciControllerList); ++i)
{
if ((Controller->Pci.VendorID == AhciControllerList[i].VendorID) &&
(Controller->Pci.DeviceID == AhciControllerList[i].DeviceID))
{
return TRUE;
}
}
/* Check for generic PCI AHCI controller */
if ((Controller->Pci.BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
(Controller->Pci.SubClass == PCI_SUBCLASS_MSC_AHCI_CTLR))
{
return TRUE;
}
return FALSE;
}
CODE_SEG("PAGE")
NTSTATUS
AhciGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc = &Controller->InterruptDesc;
ULONG i, GlobalControl;
NTSTATUS Status;
PAGED_CODE();
if (!AhciMatchController(Controller))
return STATUS_NO_MATCH;
if (Controller->InterruptDesc.Type != CmResourceTypeInterrupt)
{
ERR("No interrupt resource\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
Controller->IoBase = AtaAhciGetAbar(Controller);
if (!Controller->IoBase)
return STATUS_NO_MATCH; // Try IDE/RAID
Controller->Flags = CTRL_FLAG_IS_AHCI | CTRL_FLAG_SATA_HBA_ACPI;
Controller->Start = AtaAhciHbaStart;
Controller->Stop = AtaAhciHbaStop;
Controller->FreeResources = AtaAhciHbaFreeResouces;
Controller->AttachChannel = AtaAhciAttachChannel;
/* Set AE before accessing other AHCI registers */
GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl);
GlobalControl |= AHCI_GHC_AE;
AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl);
Controller->AhciCapabilities = AHCI_HBA_READ(Controller->IoBase, HbaCapabilities);
Controller->ChannelBitmap = AHCI_HBA_READ(Controller->IoBase, HbaPortBitmap);
Controller->MaxChannels = CountSetBits(Controller->ChannelBitmap);
if (Controller->MaxChannels == 0)
{
ASSERT(Controller->MaxChannels == 0);
return STATUS_DEVICE_HARDWARE_ERROR;
}
Controller->AhciVersion = AHCI_HBA_READ(Controller->IoBase, HbaAhciVersion);
if (Controller->AhciVersion >= AHCI_VERSION_1_2)
{
Controller->AhciCapabilitiesEx = AHCI_HBA_READ(Controller->IoBase, HbaCapabilitiesEx);
if (Controller->AhciCapabilitiesEx & AHCI_CAP2_BOH)
AtaAhciHbaRequestOsOwnership(Controller->IoBase);
}
/* Reset the HBA into a consistent state */
GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl);
GlobalControl |= AHCI_GHC_HR;
AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl);
/* HBA reset may take up to 1 second */
for (i = 100000; i > 0; i--)
{
GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl);
if (!(GlobalControl & AHCI_GHC_HR))
break;
KeStallExecutionProcessor(10);
}
if (i == 0)
{
ERR("HBA reset failed %08lx\n", GlobalControl);
return STATUS_IO_TIMEOUT;
}
/* Re-enable AE */
GlobalControl |= AHCI_GHC_AE;
AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl);
/* Disable interrupts */
GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl);
if (GlobalControl & AHCI_GHC_IE)
{
GlobalControl &= ~AHCI_GHC_IE;
AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl);
}
#if DBG
/* On virtual machines, this will allow us to test the PMP support code */
if (AtaAhciIsVbox())
Controller->AhciCapabilities |= AHCI_CAP_SPM;
#endif
Status = AtaAhciCreateChannelData(Controller);
if (!NT_SUCCESS(Status))
return Status;
Controller->QueueDepth = ((Controller->AhciCapabilities & AHCI_CAP_NCS) >> 8) + 1;
INFO("%04X:%04X.%02X: Ver %08lX, PI %08lX, CAP %08lX, CAP2 %08lX\n",
Controller->Pci.VendorID,
Controller->Pci.DeviceID,
Controller->Pci.RevisionID,
Controller->AhciVersion,
Controller->ChannelBitmap,
Controller->AhciCapabilities,
Controller->AhciCapabilitiesEx);
/* Clear HBA interrupts */
AHCI_HBA_WRITE(Controller->IoBase, HbaInterruptStatus, 0xFFFFFFFF);
Status = IoConnectInterrupt(&Controller->InterruptObject,
AtaAhciHbaIsr,
Controller,
NULL,
InterruptDesc->u.Interrupt.Vector,
InterruptDesc->u.Interrupt.Level,
InterruptDesc->u.Interrupt.Level,
(InterruptDesc->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
? Latched : LevelSensitive,
(InterruptDesc->ShareDisposition == CmResourceShareShared),
InterruptDesc->u.Interrupt.Affinity,
FALSE);
if (!NT_SUCCESS(Status))
{
ERR("Could not connect to interrupt %lu, status 0x%lx\n",
InterruptDesc->u.Interrupt.Vector, Status);
return Status;
}
return STATUS_SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,429 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: AHCI I/O request handling
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* FUNCTIONS ******************************************************************/
static
VOID
AtaAhciBeginHostToDeviceFis(
_In_ ATA_DEVICE_REQUEST* __restrict Request,
_Out_ AHCI_FIS_HOST_TO_DEVICE* __restrict Fis)
{
RtlZeroMemory(Fis, sizeof(*Fis));
Fis->Type = AHCI_FIS_REGISTER_HOST_TO_DEVICE;
Fis->Flags = UPDATE_COMMAND | DEV_NUMBER(Request->Device);
if (Request->Flags & REQUEST_FLAG_SET_DEVICE_REGISTER)
Fis->Device = Request->TaskFile.DriveSelect;
else
Fis->Device = IDE_DRIVE_SELECT;
Fis->Control = IDE_DC_ALWAYS;
}
static
VOID
AtaAhciBuildPacketCommandFis(
_In_ ATA_DEVICE_REQUEST* __restrict Request,
_Out_ AHCI_FIS_HOST_TO_DEVICE* __restrict Fis)
{
Fis->Command = IDE_COMMAND_ATAPI_PACKET;
if (Request->Flags & REQUEST_FLAG_DMA)
{
/* DMA transfer */
if ((Request->Device->TransportFlags & DEVICE_NEED_DMA_DIRECTION) &&
(Request->Flags & REQUEST_FLAG_DATA_IN))
{
/* Some SATA-to-PATA bridges require the DMADIR bit to be set */
Fis->Features = IDE_FEATURE_DMA | IDE_FEATURE_DMADIR;
}
else
{
Fis->Features = IDE_FEATURE_DMA;
}
}
else
{
USHORT ByteCount;
/* PIO transfer */
ByteCount = min(Request->DataTransferLength, ATAPI_MAX_DRQ_DATA_BLOCK);
Fis->LbaMid = (UCHAR)ByteCount;
Fis->LbaHigh = ByteCount >> 8;
}
}
static
VOID
AtaAhciBuildAtaCommandFis(
_In_ ATA_DEVICE_REQUEST* __restrict Request,
_Out_ AHCI_FIS_HOST_TO_DEVICE* __restrict Fis)
{
PATA_TASKFILE TaskFile = &Request->TaskFile;
Fis->Command = TaskFile->Command;
Fis->Features = TaskFile->Feature;
Fis->LbaLow = TaskFile->LowLba;
Fis->LbaMid = TaskFile->MidLba;
Fis->LbaHigh = TaskFile->HighLba;
Fis->SectorCount = TaskFile->SectorCount;
/* Unique queue tag */
if (Request->Flags & REQUEST_FLAG_NCQ)
Fis->SectorCount |= Request->Slot << 3;
if (Request->Flags & REQUEST_FLAG_LBA48)
{
Fis->LbaLowEx = TaskFile->LowLbaEx;
Fis->LbaMidEx = TaskFile->MidLbaEx;
Fis->LbaHighEx = TaskFile->HighLbaEx;
Fis->FeaturesEx = TaskFile->FeatureEx;
Fis->SectorCountEx = TaskFile->SectorCountEx;
}
if (Request->Flags & REQUEST_FLAG_SET_ICC_FIELD)
Fis->Icc = TaskFile->Icc;
if (Request->Flags & REQUEST_FLAG_SET_AUXILIARY_FIELD)
Fis->Auxiliary = TaskFile->Auxiliary;
}
/** Copy the CDB bytes (12 or 16 bytes) */
static
VOID
AtaAhciTransferACMDRegion(
_In_ VOID* __restrict Destination,
_In_ VOID* __restrict Source,
_In_ ATA_IO_CONTEXT_COMMON* __restrict Device)
{
/* Both addresses are 8-byte aligned */
#if defined(_WIN64)
PULONG64 Dest = Destination, Src = Source;
*Dest++ = *Src++;
if ((Device->CdbSize & (sizeof(ULONG64) - 1)) == 0)
*Dest = *Src;
else
*(PULONG)Dest = *(PULONG)Src;
#else
PULONG Dest = Destination, Src = Source;
*Dest++ = *Src++;
*Dest++ = *Src++;
*Dest++ = *Src++;
if ((Device->CdbSize & (sizeof(ULONG64) - 1)) == 0)
*Dest = *Src;
#endif
}
C_ASSERT(FIELD_OFFSET(ATA_DEVICE_REQUEST, Cdb) % sizeof(ULONG64) == 0);
C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, AtapiCommand) % sizeof(ULONG64) == 0);
BOOLEAN
AtaAhciStartIo(
_In_ PVOID ChannelContext,
_In_ PATA_DEVICE_REQUEST Request)
{
PCHANNEL_DATA_AHCI ChanData = ChannelContext;
PULONG IoBase = ChanData->IoBase;
ULONG IssueSlot;
IssueSlot = 1 << Request->Slot;
ChanData->ActiveSlotsBitmap |= IssueSlot;
if (Request->Flags & REQUEST_FLAG_NCQ)
{
ChanData->ActiveQueuedSlotsBitmap |= IssueSlot;
AHCI_PORT_WRITE(IoBase, PxSataActive, IssueSlot);
}
if ((ChanData->ChanInfo & CHANNEL_FLAG_FBS_ENABLED) &&
(ChanData->LastFbsDeviceNumber != DEV_NUMBER(Request->Device)))
{
ULONG Control;
ChanData->LastFbsDeviceNumber = DEV_NUMBER(Request->Device);
Control = AHCI_FBS_ENABLE;
Control |= DEV_NUMBER(Request->Device) << AHCI_FBS_ISSUE_SHIFT;
AHCI_PORT_WRITE(IoBase, PxFisSwitchingControl, Control);
}
AHCI_PORT_WRITE(IoBase, PxCommandIssue, IssueSlot);
return FALSE;
}
VOID
AtaAhciPrepareIo(
_In_ PVOID ChannelContext,
_In_ PATA_DEVICE_REQUEST Request)
{
PCHANNEL_DATA_AHCI ChanData = ChannelContext;
PAHCI_COMMAND_TABLE CommandTable;
PAHCI_COMMAND_HEADER CommandHeader;
PAHCI_FIS_HOST_TO_DEVICE Fis;
ULONG Control;
Control = sizeof(*Fis) / sizeof(ULONG);
Control |= DEV_NUMBER(Request->Device) << AHCI_COMMAND_HEADER_PMP_SHIFT;
if (Request->Flags & (REQUEST_FLAG_DATA_IN | REQUEST_FLAG_DATA_OUT))
{
ASSERT(Request->Flags & REQUEST_FLAG_HAS_SG_LIST);
Control |= Request->SgList->NumberOfElements << AHCI_COMMAND_HEADER_PRDT_LENGTH_SHIFT;
if (Request->Flags & REQUEST_FLAG_DATA_OUT)
{
Control |= AHCI_COMMAND_HEADER_WRITE;
}
}
CommandTable = ChanData->CommandTable[Request->Slot];
Fis = &CommandTable->HostToDeviceFis;
AtaAhciBeginHostToDeviceFis(Request, Fis);
if (Request->Flags & REQUEST_FLAG_PACKET_COMMAND)
{
AtaAhciBuildPacketCommandFis(Request, Fis);
AtaAhciTransferACMDRegion(CommandTable->AtapiCommand,
Request->Cdb,
Request->Device);
Control |= AHCI_COMMAND_HEADER_ATAPI;
}
else
{
AtaAhciBuildAtaCommandFis(Request, Fis);
}
CommandHeader = &ChanData->CommandList->CommandHeader[Request->Slot];
CommandHeader->Control = Control;
CommandHeader->PrdByteCount = 0;
KeFlushIoBuffers(Request->Mdl, !!(Request->Flags & REQUEST_FLAG_DATA_IN), TRUE);
}
VOID
AtaAhciPreparePrdTable(
_In_ PVOID ChannelContext,
_In_ PATA_DEVICE_REQUEST Request,
_In_ SCATTER_GATHER_LIST* __restrict SgList)
{
PCHANNEL_DATA_AHCI ChanData = ChannelContext;
PAHCI_PRD_TABLE_ENTRY PrdTableEntry;
ULONG i;
#if DBG
BOOLEAN Is64BitDma = !!(ChanData->Controller->AhciCapabilities & AHCI_CAP_S64A);
#endif
ASSUME(SgList->NumberOfElements > 0);
ASSERT(SgList->NumberOfElements < AHCI_MAX_PRDT_ENTRIES);
PrdTableEntry = ChanData->CommandTable[Request->Slot]->PrdTable;
for (i = 0; i < SgList->NumberOfElements; ++i)
{
ASSERT(SgList->Elements[i].Length != 0);
ASSERT(SgList->Elements[i].Length < AHCI_MAX_PRD_LENGTH);
ASSERT((SgList->Elements[i].Length & ATA_MIN_BUFFER_ALIGNMENT) == 0);
ASSERT(Is64BitDma || (SgList->Elements[i].Address.HighPart == 0));
ASSERT(i < ChanData->MaximumPhysicalPages);
PrdTableEntry->DataBaseLow = SgList->Elements[i].Address.LowPart;
PrdTableEntry->DataBaseHigh = SgList->Elements[i].Address.HighPart;
PrdTableEntry->ByteCount = SgList->Elements[i].Length - 1;
++PrdTableEntry;
}
/* Enable IRQ on last entry */
--PrdTableEntry;
PrdTableEntry->ByteCount |= AHCI_PRD_INTERRUPT_ON_COMPLETION;
}
BOOLEAN
AtaAhciAllocateSlot(
_In_ PVOID ChannelContext,
_In_ PATA_DEVICE_REQUEST Request,
_In_ BOOLEAN Allocate)
{
PCHANNEL_DATA_AHCI ChanData = ChannelContext;
if (Allocate)
{
/*
* We cannot issue commands to more than one device behind the Port Multiplier
* that supports only command-based switching.
*/
if ((ChanData->ChanInfo & CHANNEL_FLAG_IS_PMP) &&
!(ChanData->ChanInfo & CHANNEL_FLAG_FBS_ENABLED))
{
if ((ChanData->LastPmpDeviceNumber != 0xFF) &&
(ChanData->LastPmpDeviceNumber != DEV_NUMBER(Request->Device)))
{
return FALSE;
}
ChanData->LastPmpDeviceNumber = DEV_NUMBER(Request->Device);
Request->Flags |= REQUEST_FLAG_DEVICE_EXCLUSIVE_ACCESS;
}
}
else
{
if (Request->Flags & REQUEST_FLAG_DEVICE_EXCLUSIVE_ACCESS)
{
ChanData->LastPmpDeviceNumber = 0xFF;
}
}
return TRUE;
}
static
VOID
AtaAhciPortCompleteCommands(
_In_ PCHANNEL_DATA_AHCI ChanData,
_In_ ULONG CommandsCompleted)
{
ULONG Slot;
while (_BitScanForward(&Slot, CommandsCompleted) != 0)
{
PATA_DEVICE_REQUEST Request;
UCHAR SrbStatus;
CommandsCompleted &= ~(1 << Slot);
Request = ChanData->Slots[Slot];
ASSERT(Request);
ASSERT(Request->Slot == Slot);
SrbStatus = SRB_STATUS_SUCCESS;
if (!(Request->Flags & REQUEST_FLAG_NCQ) &&
Request->Flags & (REQUEST_FLAG_DATA_IN | REQUEST_FLAG_DATA_OUT))
{
PAHCI_COMMAND_HEADER CommandHeader = &ChanData->CommandList->CommandHeader[Slot];
/* This indicates a residual underrun */
if (CommandHeader->PrdByteCount < Request->DataTransferLength)
{
Request->DataTransferLength = CommandHeader->PrdByteCount;
SrbStatus = SRB_STATUS_DATA_OVERRUN;
}
}
Request->SrbStatus = SrbStatus;
/* Save the latest copy of the task file registers */
if (Request->Flags & REQUEST_FLAG_SAVE_TASK_FILE)
{
AtaAhciSaveTaskFile(ChanData, Request, FALSE);
}
}
}
static
VOID
AtaAhciPortHandleInterrupt(
_In_ PCHANNEL_DATA_AHCI ChanData)
{
ULONG InterruptStatus, CommandsIssued, CommandsCompleted;
AHCI_PORT_REGISTER CommandRegister;
/* Clear all pending events */
InterruptStatus = AHCI_PORT_READ(ChanData->IoBase, PxInterruptStatus);
AHCI_PORT_WRITE(ChanData->IoBase, PxInterruptStatus, InterruptStatus);
TRACE("CH %lu: Intr %08lx\n", ChanData->Channel, InterruptStatus);
/* Clear interface errors */
AHCI_PORT_WRITE(ChanData->IoBase, PxSataError,
AHCI_PORT_READ(ChanData->IoBase, PxSataError));
/* Determine commands that have completed */
if (ChanData->ActiveQueuedSlotsBitmap != 0)
CommandRegister = PxSataActive;
else
CommandRegister = PxCommandIssue;
CommandsIssued = AHCI_PORT_READ(ChanData->IoBase, CommandRegister);
CommandsCompleted = ~CommandsIssued & ChanData->ActiveSlotsBitmap;
/* Complete processed commands */
if (CommandsCompleted != 0)
{
ChanData->ActiveSlotsBitmap &= ~CommandsCompleted;
ChanData->ActiveQueuedSlotsBitmap &= ~CommandsCompleted;
AtaAhciPortCompleteCommands(ChanData, CommandsCompleted);
ChanData->PortNotification(AtaRequestComplete, ChanData->PortContext, CommandsCompleted);
}
/* Asynchronous notification received */
if (InterruptStatus & AHCI_PXIRQ_SDBS)
{
ULONG Message = AHCI_PORT_READ(ChanData->IoBase, PxSataNotification);
if (Message != 0)
{
AHCI_PORT_WRITE(ChanData->IoBase, PxSataNotification, Message);
WARN("CH %lu: Notification %08lx arrived\n", ChanData->Channel, Message);
ChanData->PortNotification(AtaAsyncNotificationDetected, ChanData->PortContext, 0x1);
}
}
/* Handle various errors and link change events */
if (InterruptStatus & AHCI_PXIRQ_PORT_STATUS)
{
AtaAhciHandlePortStateChange(ChanData, InterruptStatus);
}
if (InterruptStatus & AHCI_PXIRQ_FATAL_ERROR)
{
AtaAhciHandleFatalError(ChanData);
}
}
BOOLEAN
NTAPI
AtaAhciHbaIsr(
_In_ PKINTERRUPT Interrupt,
_In_ PVOID Context)
{
PATA_CONTROLLER Controller = Context;
ULONG Port, InterruptStatus, PortInterruptBitmap;
InterruptStatus = AHCI_HBA_READ(Controller->IoBase, HbaInterruptStatus);
if (InterruptStatus == 0)
return FALSE;
PortInterruptBitmap = InterruptStatus & Controller->ChannelBitmap;
while (_BitScanForward(&Port, PortInterruptBitmap) != 0)
{
PortInterruptBitmap &= ~(1 << Port);
AtaAhciPortHandleInterrupt(Controller->Channels[Port]);
}
/* Clear pending HBA interrupts */
AHCI_HBA_WRITE(Controller->IoBase, HbaInterruptStatus, InterruptStatus);
return TRUE;
}

View File

@@ -0,0 +1,430 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: AMD ATA controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_AMD_756 0x7409
#define PCI_DEV_AMD_766 0x7411
#define PCI_DEV_AMD_768 0x7441
#define PCI_DEV_AMD_8111 0x7469
#define PCI_DEV_AMD_CS5536 0x2092
#define PCI_DEV_AMD_CS5536_2 0x209A
#define PCI_DEV_NFORCE_IDE 0x01BC
#define PCI_DEV_NFORCE2_IDE 0x0065
#define PCI_DEV_NFORCE2_IDE_2 0x0085
#define PCI_DEV_NFORCE3_IDE 0x00D5
#define PCI_DEV_NFORCE3_IDE_2 0x00E5
#define PCI_DEV_CK804_IDE 0x0053
#define PCI_DEV_MCP04_IDE 0x0035
#define PCI_DEV_MCP51_IDE 0x0265
#define PCI_DEV_MCP55_IDE 0x036E
#define PCI_DEV_MCP61_IDE 0x03EC
#define PCI_DEV_MCP65_IDE 0x0448
#define PCI_DEV_MCP67_IDE 0x0560
#define PCI_DEV_MCP73_IDE 0x056C
#define PCI_DEV_MCP77_IDE 0x0759
#define PCI_DEV_NFORCE2_SATA 0x008E
#define PCI_DEV_NFORCE3_SATA 0x00E3
#define PCI_DEV_NFORCE3_SATA_2 0x00EE
#define PCI_DEV_CK804_SATA 0x0054
#define PCI_DEV_CK804_SATA_2 0x0055
#define PCI_DEV_MCP04_SATA 0x0036
#define PCI_DEV_MCP04_SATA_2 0x003E
#define PCI_DEV_MCP51_SATA 0x0266
#define PCI_DEV_MCP51_SATA_2 0x0267
#define PCI_DEV_MCP55_SATA 0x037E
#define PCI_DEV_MCP55_SATA_2 0x037F
#define PCI_DEV_MCP61_SATA 0x03E7
#define PCI_DEV_MCP61_SATA_2 0x03F6
#define PCI_DEV_MCP61_SATA_3 0x03F7
#define PCI_DEV_MCP89_SATA 0x0D85
#define PCI_SUBSYSTEM_AMD_SERENADE 0x36C0
#define AMD_CONFIG_BASE 0x40
#define NV_CONFIG_BASE 0x50
#define AMD_PCI_CLOCK 30000
#define AMD_REG_CONFIG_PREFETCH(IoBase) ((IoBase) + 0x01)
#define AMD_REG_CONFIG_CR(IoBase) ((IoBase) + 0x02)
#define AMD_REG_TIMING_CTRL(IoBase) ((IoBase) + 0x08)
#define AMD_REG_ADDRESS_SETUP(IoBase) ((IoBase) + 0x0C)
#define AMD_REG_UDMA(IoBase, Channel) ((IoBase) + 0x10 + (2 - ((Channel) * 2)))
#define AMD_CONFIG_PREFETCH(Channel) (0xC0 >> ((Channel) * 2))
#define AMD_CONFIG_CR(Channel) (0x03 << ((Channel) * 2))
#define AMD_UDMA_TIME(x) (x)
#define AMD_UDMA_EN 0xC0
#define AMD_UDMA_CTRL(Drive, Value) ((Value) << ((1 - (Drive)) * 8))
PCIIDEX_PAGED_DATA
static const struct
{
USHORT VendorID;
USHORT DeviceID;
USHORT Flags;
#define HW_FLAGS_UDMA4 0x0001
#define HW_FLAGS_UDMA5 0x0002
#define HW_FLAGS_CHECK_SYSBOARD 0x0004
#define HW_FLAGS_NO_PREFETCH 0x0008
#define HW_FLAGS_SATA 0x0010
} AmdControllerList[] =
{
{ PCI_VEN_AMD, PCI_DEV_AMD_756, HW_FLAGS_UDMA4 },
{ PCI_VEN_AMD, PCI_DEV_AMD_766, HW_FLAGS_UDMA5 | HW_FLAGS_NO_PREFETCH },
{ PCI_VEN_AMD, PCI_DEV_AMD_768, HW_FLAGS_UDMA5 },
{ PCI_VEN_AMD, PCI_DEV_AMD_8111, HW_FLAGS_CHECK_SYSBOARD },
{ PCI_VEN_AMD, PCI_DEV_AMD_CS5536, HW_FLAGS_UDMA5 },
{ PCI_VEN_AMD, PCI_DEV_AMD_CS5536_2, HW_FLAGS_UDMA5 },
{ PCI_VEN_NVIDIA, PCI_DEV_NFORCE_IDE, HW_FLAGS_UDMA5 },
{ PCI_VEN_NVIDIA, PCI_DEV_NFORCE2_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_NFORCE2_IDE_2, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_NFORCE3_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_NFORCE3_IDE_2, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_CK804_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP04_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP51_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP55_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP61_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP65_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP67_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP73_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP77_IDE, 0 },
{ PCI_VEN_NVIDIA, PCI_DEV_NFORCE2_SATA, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_NFORCE3_SATA, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_NFORCE3_SATA_2, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_CK804_SATA, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_CK804_SATA_2, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP04_SATA, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP04_SATA_2, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP51_SATA, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP51_SATA_2, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP55_SATA, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP55_SATA_2, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP61_SATA, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP61_SATA_2, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP61_SATA_3, HW_FLAGS_SATA },
{ PCI_VEN_NVIDIA, PCI_DEV_MCP89_SATA, HW_FLAGS_SATA },
};
static const UCHAR AmdUdmaSettings[] =
{
AMD_UDMA_EN | AMD_UDMA_TIME(2), // 0
AMD_UDMA_EN | AMD_UDMA_TIME(1), // 1
AMD_UDMA_EN | AMD_UDMA_TIME(0), // 2
AMD_UDMA_EN | AMD_UDMA_TIME(4), // 3
AMD_UDMA_EN | AMD_UDMA_TIME(5), // 4
AMD_UDMA_EN | AMD_UDMA_TIME(6), // 5
AMD_UDMA_EN | AMD_UDMA_TIME(7), // 6
};
static const ULONG AmdTimingControlShift[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] =
{
// M S
{ 24, 16 }, // Pri
{ 8, 0 }, // Sec
};
static const ULONG AmdAddressSetupShift[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] =
{
// M S
{ 6, 4 }, // Pri
{ 2, 0 }, // Sec
};
static const ULONG AmdPortTimShift[MAX_IDE_CHANNEL] =
{
24, // Pri
16 // Sec
};
PCIIDEX_PAGED_DATA
static const ATA_PCI_ENABLE_BITS AmdEnableBits[MAX_IDE_CHANNEL] =
{
{ AMD_CONFIG_BASE, 0x02, 0x02 },
{ AMD_CONFIG_BASE, 0x01, 0x01 },
};
PCIIDEX_PAGED_DATA
static const ATA_PCI_ENABLE_BITS NvEnableBits[MAX_IDE_CHANNEL] =
{
{ NV_CONFIG_BASE, 0x02, 0x02 },
{ NV_CONFIG_BASE, 0x01, 0x01 },
};
/* FUNCTIONS ******************************************************************/
static
ULONG
AmdGetPciConfigIoBase(
_In_ PATA_CONTROLLER Controller)
{
if (Controller->Pci.VendorID == PCI_VEN_AMD)
return AMD_CONFIG_BASE;
return NV_CONFIG_BASE;
}
static
VOID
AmdEnablePostedWriteBuffer(
_In_ PATA_CONTROLLER Controller,
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList,
_In_ ULONG IoBase)
{
UCHAR ConfigReg;
ULONG i;
ConfigReg = PciRead8(Controller, AMD_REG_CONFIG_PREFETCH(IoBase));
if (ChanData->HwFlags & HW_FLAGS_NO_PREFETCH)
{
/* Errata: IDE Read / Write Prefetch Hangs PCI Bus */
ConfigReg &= ~(AMD_CONFIG_PREFETCH(0) | AMD_CONFIG_PREFETCH(1));
}
else
{
ConfigReg |= AMD_CONFIG_PREFETCH(Channel);
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
if (Device && !Device->IsFixedDisk)
{
ConfigReg &= ~AMD_CONFIG_PREFETCH(Channel);
break;
}
}
}
PciWrite8(Controller, AMD_REG_CONFIG_PREFETCH(IoBase), ConfigReg);
}
static
VOID
AmdSetTransferMode(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel];
const ULONG IoBase = AmdGetPciConfigIoBase(Controller);
ATA_TIMING DeviceTimings[MAX_IDE_DEVICE];
ULONG i, DriveTimReg, PortTimReg, UdmaTimReg;
AtaSelectTimings(DeviceList, DeviceTimings, AMD_PCI_CLOCK, SHARED_CMD_TIMINGS);
DriveTimReg = PciRead32(Controller, AMD_REG_TIMING_CTRL(IoBase));
PortTimReg = PciRead32(Controller, AMD_REG_ADDRESS_SETUP(IoBase));
UdmaTimReg = PciRead32(Controller, AMD_REG_UDMA(IoBase, 1));
INFO("CH %lu: Config (before)\n"
"DRV %08lX\n"
"PORT %08lX\n"
"UDMA %08lX\n"
"CFG %08lX\n",
Channel, DriveTimReg, PortTimReg, UdmaTimReg, PciRead32(Controller, IoBase));
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
PATA_TIMING Timing = &DeviceTimings[i];
ULONG Value;
/* UDMA timings */
if (Device && (Device->DmaMode >= UDMA_MODE(0)))
{
Value = AmdUdmaSettings[Device->DmaMode - UDMA_MODE(0)];
}
else
{
/* Set "Slow UDMA mode 0" by default */
if (Controller->Pci.VendorID == PCI_VEN_AMD)
Value = AMD_UDMA_TIME(3);
else
Value = 0;
}
UdmaTimReg &= ~(0xFF << AmdTimingControlShift[Channel][i]);
UdmaTimReg |= Value << AmdTimingControlShift[Channel][i];
if (!Device)
continue;
/* PIO and DMA timings */
ViaClampTimings(Timing);
Value = Timing->AddressSetup;
PortTimReg &= ~(0x03 << AmdAddressSetupShift[Channel][i]);
PortTimReg |= Value << AmdAddressSetupShift[Channel][i];
Value = (Timing->CmdActive << 4) | Timing->CmdRecovery;
PortTimReg &= ~(0xFF << AmdPortTimShift[Channel]);
PortTimReg |= Value << AmdPortTimShift[Channel];
Value = (Timing->DataActive << 4) | Timing->DataRecovery;
DriveTimReg &= ~(0xFF << AmdTimingControlShift[Channel][i]);
DriveTimReg |= Value << AmdTimingControlShift[Channel][i];
}
AmdEnablePostedWriteBuffer(Controller, ChanData, Channel, DeviceList, IoBase);
PciWrite32(Controller, AMD_REG_TIMING_CTRL(IoBase), DriveTimReg);
PciWrite32(Controller, AMD_REG_ADDRESS_SETUP(IoBase), PortTimReg);
PciWrite32(Controller, AMD_REG_UDMA(IoBase, 1), UdmaTimReg);
INFO("CH %lu: Config (after)\n"
"DRV %08lX\n"
"PORT %08lX\n"
"UDMA %08lX\n"
"CFG %08lX\n",
Channel, DriveTimReg, PortTimReg, UdmaTimReg, PciRead32(Controller, IoBase));
}
static
VOID
AmdControllerStart(
_In_ PATA_CONTROLLER Controller)
{
UCHAR ConfigReg;
ASSERT(Controller->Pci.VendorID == PCI_VEN_AMD);
/* Initialize controller before issuing the identify command */
ConfigReg = PciRead8(Controller, AMD_REG_CONFIG_PREFETCH(AMD_CONFIG_BASE));
ConfigReg &= ~(AMD_CONFIG_PREFETCH(0) | AMD_CONFIG_PREFETCH(1));
PciWrite8(Controller, AMD_REG_CONFIG_PREFETCH(AMD_CONFIG_BASE), ConfigReg);
}
static
BOOLEAN
CODE_SEG("PAGE")
NvHasUdmaCable(
_In_ USHORT UdmaTimReg,
_In_ ULONG Drive)
{
PAGED_CODE();
/* UDMA was disabled by BIOS */
if ((AMD_UDMA_CTRL(Drive, UdmaTimReg) & AMD_UDMA_EN) != AMD_UDMA_EN)
return FALSE;
/* Check clock settings, see if UDMA3 or higher mode is active */
return !!((AMD_UDMA_CTRL(Drive, UdmaTimReg) & 0x7) & AMD_UDMA_TIME(4));
}
CODE_SEG("PAGE")
NTSTATUS
AmdGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i, HwFlags, SupportedMode;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_AMD || Controller->Pci.VendorID == PCI_VEN_NVIDIA);
for (i = 0; i < RTL_NUMBER_OF(AmdControllerList); ++i)
{
HwFlags = AmdControllerList[i].Flags;
if ((Controller->Pci.VendorID == AmdControllerList[i].VendorID) &&
(Controller->Pci.DeviceID == AmdControllerList[i].DeviceID))
{
break;
}
}
if (i == RTL_NUMBER_OF(AmdControllerList))
return STATUS_NO_MATCH;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
if (HwFlags & HW_FLAGS_SATA)
{
SupportedMode = SATA_ALL;
}
else
{
SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_ALL;
if (HwFlags & HW_FLAGS_CHECK_SYSBOARD)
{
if (Controller->Pci.SubSystemID == PCI_SUBSYSTEM_AMD_SERENADE)
SupportedMode &= ~UDMA_MODE6;
}
else
{
if (HwFlags & HW_FLAGS_UDMA5)
SupportedMode &= ~UDMA_MODE6;
else if (HwFlags & HW_FLAGS_UDMA4)
SupportedMode &= ~UDMA_MODES(5, 6);
}
if (Controller->Pci.VendorID == PCI_VEN_AMD)
{
Controller->ChannelEnableBits = AmdEnableBits;
Controller->Start = AmdControllerStart;
}
else
{
Controller->ChannelEnableBits = NvEnableBits;
}
}
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
ChanData->HwFlags = HwFlags;
ChanData->TransferModeSupported = SupportedMode;
if (HwFlags & HW_FLAGS_SATA)
{
ChanData->ChanInfo |= CHANNEL_FLAG_NO_SLAVE;
ChanData->SetTransferMode = SataSetTransferMode;
}
else
{
ChanData->SetTransferMode = AmdSetTransferMode;
/* Check for 80-conductor cable */
if (Controller->Pci.VendorID == PCI_VEN_AMD)
{
UCHAR ConfigReg = PciRead8(Controller, AMD_REG_CONFIG_CR(AMD_CONFIG_BASE));
if (!(ConfigReg & AMD_REG_CONFIG_CR(i)))
{
INFO("CH %lu: BIOS detected 40-conductor cable\n", i);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
}
else
{
USHORT UdmaTimReg = PciRead16(Controller, AMD_REG_UDMA(NV_CONFIG_BASE, i));
if (!NvHasUdmaCable(UdmaTimReg, 0) && !NvHasUdmaCable(UdmaTimReg, 1))
{
INFO("CH %lu: BIOS hasn't selected mode faster than UDMA 2, "
"assume 40-conductor cable\n",
ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
}
}
}
return STATUS_SUCCESS;
}

View File

@@ -0,0 +1,83 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause)
* PURPOSE: ATI ATA controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/*
* Adapted from the FreeBSD ata-ati driver
* Copyright (c) 1998-2008 Søren Schmidt <sos@FreeBSD.org>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_IXP200_IDE 0x4349
#define PCI_DEV_IXP300_IDE 0x4369
#define PCI_DEV_IXP400_IDE 0x4376
#define PCI_DEV_IXP600_IDE 0x438C
#define PCI_DEV_IXP700_IDE 0x439C
PCIIDEX_PAGED_DATA
static const ATA_PCI_ENABLE_BITS AtiEnableBits[MAX_IDE_CHANNEL] =
{
{ 0x48, 0x01, 0x00 },
{ 0x48, 0x08, 0x00 },
};
/* FUNCTIONS ******************************************************************/
CODE_SEG("PAGE")
NTSTATUS
AtiGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_ATI);
if (Controller->Pci.DeviceID != PCI_DEV_IXP200_IDE &&
Controller->Pci.DeviceID != PCI_DEV_IXP300_IDE &&
Controller->Pci.DeviceID != PCI_DEV_IXP400_IDE &&
Controller->Pci.DeviceID != PCI_DEV_IXP600_IDE &&
Controller->Pci.DeviceID != PCI_DEV_IXP700_IDE)
{
return STATUS_NO_MATCH;
}
Controller->ChannelEnableBits = AtiEnableBits;
if (Controller->Pci.DeviceID == PCI_DEV_IXP600_IDE)
Controller->MaxChannels = 1;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
ChanData->SetTransferMode = SvwSetTransferMode;
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5);
if (Controller->Pci.DeviceID == PCI_DEV_IXP200_IDE)
ChanData->TransferModeSupported &= ~UDMA_MODE5;
if (!SvwHasUdmaCable(Controller, i))
{
INFO("CH %lu: BIOS hasn't selected mode faster than UDMA 2, "
"assume 40-conductor cable\n",
ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
}
return STATUS_SUCCESS;
}

View File

@@ -0,0 +1,577 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: CMD PCI IDE controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/*
* The CMD-640 does not support 32-bit PCI configuration space writes.
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_PCI0640 0x0640
#define PCI_DEV_PCI0643 0x0643
#define PCI_DEV_PCI0646 0x0646
#define PCI_DEV_PCI0648 0x0648
#define PCI_DEV_CMD0649 0x0649
#define HW_FLAGS_PRIMARY_ENABLED 0x0001
#define HW_FLAGS_HAS_UDMA_REG 0x0002
#define HW_FLAGS_NEED_PIO_FIX 0x0004
#define HW_FLAGS_NO_PREFETCH 0x0008
#define CMD_PCI_CLOCK 30000
#define CMD_REG_CFR 0x50
#define CMD_REG_CNTRL 0x51
#define CMD_REG_CMDTIM 0x52
#define CMD_REG_ARTTIM0 0x53
#define CMD_REG_DRWTIM0 0x54
#define CMD_REG_ARTTIM1 0x55
#define CMD_REG_DRWTIM1 0x56
#define CMD_REG_ARTTIM23 0x57
#define CMD_REG_DRWTIM2 0x58
#define CMD_REG_DRWTIM23 0x58 // 640 only
#define CMD_REG_DRWTIM3 0x5B
#define CMD_REG_MRDMODE 0x71
#define CMD_REG_BMIDECSR 0x79
#define CMD_REG_UDIDETCR(Channel) (((Channel) == 0) ? 0x73 : 0x7B)
#define CMD_CFR_INTR(Channel) (((Channel) == 0) ? 0x04 : 0x10)
#define CMD_CNTRL_CHAN_EN(Channel) (0x04 << (Channel))
#define CMD_ARTTIM_ART_MASK 0xC0
#define CMD_MRDMODE_READ_MULTIPLE 0x01
#define CMD_MRDMODE_INTR(Channel) (0x04 << (Channel))
#define CMD_MRDMODE_INTR_CH0_BLOCK 0x10
#define CMD_MRDMODE_INTR_CH1_BLOCK 0x20
#define CMD_BMIDECSR_CR(Channel) (0x01 << (Channel))
#define CMD_UDIDETCR_CLEAR(Drive) (((Drive) == 0) ? 0x35 : 0xCA)
#define CMD_UDIDETCR_EN(Drive) (0x01 << (Drive))
typedef struct _CMD_HW_EXTENSION
{
ATATIM CmdActive[MAX_IDE_CHANNEL * MAX_IDE_DEVICE];
ATATIM CmdRecovery[MAX_IDE_CHANNEL * MAX_IDE_DEVICE];
} CMD_HW_EXTENSION, *PCMD_HW_EXTENSION;
static const UCHAR CmdPrefetchDisable[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] =
{
// M S
{ 0x40, 0x80 }, // Pri
{ 0x04, 0x08 } // Sec
};
static const ULONG CmdPrefetchRegs[MAX_IDE_CHANNEL] =
{
// Pri Sec
CMD_REG_CNTRL, CMD_REG_ARTTIM23
};
static const ULONG CmdDrwTimRegs[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] =
{
// M S
{ CMD_REG_DRWTIM0, CMD_REG_DRWTIM1 }, // Pri
{ CMD_REG_DRWTIM2, CMD_REG_DRWTIM3 } // Sec
};
static const ULONG CmdArtTimRegs[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] =
{
// M S
{ CMD_REG_ARTTIM0, CMD_REG_ARTTIM1 }, // Pri
{ CMD_REG_ARTTIM23, CMD_REG_ARTTIM23 } // Sec
};
static const UCHAR CmdArtTimings[] =
{
0x40, // 0
0x40, // 1
0x40, // 2
0x80, // 3
0x00, // 4
0xC0 // 5
};
static const UCHAR CmdUdmaTimings[6][MAX_IDE_DEVICE] =
{
// M S
{ 0x31, 0xC2 }, // 0
{ 0x21, 0x82 }, // 1
{ 0x11, 0x42 }, // 2
{ 0x25, 0x8A }, // 3
{ 0x15, 0x4A }, // 4
{ 0x05, 0x0A } // 5
};
/* FUNCTIONS ******************************************************************/
static
VOID
CmdFixRecoveryTiming(
_In_ PCHANNEL_DEVICE_CONFIG Device,
_Inout_ PATA_TIMING Timing,
_In_ ATATIM CycleTimeClocks,
_In_ ATATIM CmdActiveClocks,
_In_ ATATIM CmdRecoveryClocks,
_In_ ATATIM DataActiveClocks,
_In_ ATATIM DataRecoveryClocks)
{
if (((Timing->DataActive + Timing->DataRecovery) > CycleTimeClocks) || // Cycle time not met
(Device->DmaMode == MWDMA_MODE(0))) // Data active time not met
{
if (Timing->CmdActive < CmdActiveClocks)
Timing->CmdActive = CmdActiveClocks;
if (Timing->CmdRecovery < CmdRecoveryClocks)
Timing->CmdRecovery = CmdRecoveryClocks;
if (Timing->DataActive < DataActiveClocks)
Timing->DataActive = DataActiveClocks;
if (Timing->DataRecovery < DataRecoveryClocks)
Timing->DataRecovery = DataRecoveryClocks;
}
else
{
/* Use recommended values */
Timing->CmdActive = CmdActiveClocks;
Timing->CmdRecovery = CmdRecoveryClocks;
Timing->DataActive = DataActiveClocks;
Timing->DataRecovery = DataRecoveryClocks;
}
}
static
VOID
CmdDerateTimings(
_In_ PCHANNEL_DEVICE_CONFIG Device,
_Inout_ PATA_TIMING Timing)
{
/* Make the timings run slower to address the PCI Bus Hang errata */
switch (Device->PioMode)
{
case PIO_MODE(0):
{
/*
* 600ns is minimum in PIO mode 0, but program the chip to 570ns.
* 480 + 90 = 570
*/
CmdFixRecoveryTiming(Device, Timing, 20, 16, 3, 16, 3);
break;
}
case PIO_MODE(1):
{
/* 300 + 90 = 390 */
CmdFixRecoveryTiming(Device, Timing, 13, 10, 3, 10, 3);
break;
}
case PIO_MODE(2):
{
/*
* Cmd 300 + 90 = 390
* Data 150 + 90 = 240
*/
CmdFixRecoveryTiming(Device, Timing, 8, 10, 3, 5, 3);
break;
}
/* PIO 3 & 4 unaffected by the errata */
default:
break;
}
}
static
UCHAR
CmdPackTimings(
_In_ PATA_CONTROLLER Controller,
_In_ ATATIM Active,
_In_ ATATIM Recovery)
{
if (Controller->Pci.DeviceID == PCI_DEV_PCI0640)
{
if (Active >= 16)
Active = 0;
else if (Active < 2)
Active = 2;
if (Controller->Pci.RevisionID > 1)
{
/* 640B */
if (Recovery >= 17)
Recovery = 0;
else if (Recovery > 2)
--Recovery;
else
Recovery = 2;
}
else
{
/* 640A */
if (Recovery >= 16)
Recovery = 0;
else if (Recovery < 2)
Recovery = 2;
}
}
else
{
if (Active >= 16)
Active = 0;
if (Recovery >= 16)
Recovery = 0;
else if (Recovery > 1)
--Recovery;
else
Recovery = 15;
}
return (Active << 4) | Recovery;
}
static
VOID
CmdSetTransferMode(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel];
PCMD_HW_EXTENSION HwExt = Controller->HwExt;
ATA_TIMING DeviceTimings[MAX_IDE_DEVICE];
ULONG i, TimingFlags;
UCHAR Value;
/* Some timings are shared between both drives drives on the secondary channel */
TimingFlags = 0;
if (Channel != 0)
{
TimingFlags |= SHARED_ADDR_TIMINGS;
if (Controller->Pci.DeviceID == PCI_DEV_PCI0640)
TimingFlags |= SHARED_DATA_TIMINGS;
}
AtaSelectTimings(DeviceList, DeviceTimings, CMD_PCI_CLOCK, TimingFlags);
/* Enable or disable prefetch mode */
Value = PciRead8(Controller, CmdPrefetchRegs[Channel]);
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
if (!Device)
continue;
if ((ChanData->HwFlags & HW_FLAGS_NO_PREFETCH) || !Device->IsFixedDisk)
Value |= CmdPrefetchDisable[Channel][i];
else
Value &= ~CmdPrefetchDisable[Channel][i];
}
PciWrite8(Controller, CmdPrefetchRegs[Channel], Value);
/* Program timing settings */
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
PATA_TIMING Timing = &DeviceTimings[i];
UCHAR UdmaTimReg;
ULONG Register;
/* UDMA timings */
if (ChanData->HwFlags & HW_FLAGS_HAS_UDMA_REG)
{
UdmaTimReg = PciRead8(Controller, CMD_REG_UDIDETCR(Channel));
if (Device && (Device->DmaMode >= UDMA_MODE(0)))
{
UdmaTimReg &= ~CMD_UDIDETCR_CLEAR(i);
UdmaTimReg |= CmdUdmaTimings[Device->DmaMode - UDMA_MODE(0)][i];
}
else
{
UdmaTimReg &= ~CMD_UDIDETCR_EN(i);
}
PciWrite8(Controller, CMD_REG_UDIDETCR(Channel), UdmaTimReg);
}
if (!Device)
{
HwExt->CmdActive[i + Channel * MAX_IDE_DEVICE] = 0;
HwExt->CmdRecovery[i + Channel * MAX_IDE_DEVICE] = 0;
continue;
}
if (ChanData->HwFlags & HW_FLAGS_NEED_PIO_FIX)
CmdDerateTimings(Device, Timing);
/* 8-bit timings */
HwExt->CmdActive[i + Channel * MAX_IDE_DEVICE] = Timing->CmdActive;
HwExt->CmdRecovery[i + Channel * MAX_IDE_DEVICE] = Timing->CmdRecovery;
/* Address setup timing */
if (Timing->AddressSetup > RTL_NUMBER_OF(CmdArtTimings) - 1)
Timing->AddressSetup = RTL_NUMBER_OF(CmdArtTimings) - 1;
Value = PciRead8(Controller, CmdArtTimRegs[Channel][i]);
Value &= ~CMD_ARTTIM_ART_MASK;
Value |= CmdArtTimings[Timing->AddressSetup];
PciWrite8(Controller, CmdArtTimRegs[Channel][i], Value);
/* 16-bit timings */
Value = CmdPackTimings(Controller, Timing->DataActive, Timing->DataRecovery);
if ((Controller->Pci.DeviceID == PCI_DEV_PCI0640) && (Channel != 0))
Register = CMD_REG_DRWTIM23;
else
Register = CmdDrwTimRegs[Channel][i];
PciWrite8(Controller, Register, Value);
}
/* 8-bit timings are shared between both the primary and secondary channels */
for (i = 0; i < MAX_IDE_CHANNEL * MAX_IDE_DEVICE; ++i)
{
if (HwExt->CmdActive[i] > DeviceTimings[0].CmdActive)
DeviceTimings[0].CmdActive = HwExt->CmdActive[i];
if (HwExt->CmdRecovery[i] > DeviceTimings[0].CmdRecovery)
DeviceTimings[0].CmdRecovery = HwExt->CmdRecovery[i];
}
if ((DeviceTimings[0].CmdActive != 0) || (DeviceTimings[0].CmdRecovery != 0))
{
Value = CmdPackTimings(Controller,
DeviceTimings[0].CmdActive,
DeviceTimings[0].CmdRecovery);
PciWrite8(Controller, CMD_REG_CMDTIM, Value);
}
}
static
IDE_CHANNEL_STATE
CmdChannelEnabledTest(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel];
UCHAR Control;
/* Some controllers lack the primary channel enable bit */
if ((Channel == 0) && (ChanData->HwFlags & HW_FLAGS_PRIMARY_ENABLED))
return ChannelEnabled;
Control = PciRead8(Controller, CMD_REG_CNTRL);
if (Control & CMD_CNTRL_CHAN_EN(Channel))
return ChannelEnabled;
return ChannelDisabled;
}
static
BOOLEAN
CmdCheckInterruptMrdMode(
_In_ PCHANNEL_DATA_PATA ChanData)
{
PATA_CONTROLLER Controller = ChanData->Controller;
UCHAR Control, InterruptMask;
if (ChanData->Regs.Dma)
Control = ATA_READ(ChanData->Regs.Dma + 1, ChanData, MRES_DMA);
else
Control = PciRead8(Controller, CMD_REG_MRDMODE);
InterruptMask = CMD_MRDMODE_INTR(ChanData->Channel);
if (!(Control & InterruptMask))
return FALSE;
Control &= ~(CMD_MRDMODE_INTR(0) | CMD_MRDMODE_INTR(1));
Control |= InterruptMask;
/* Make sure we do not clear the write-only bits */
Control |= CMD_MRDMODE_READ_MULTIPLE;
/* Clear the interrupt */
if (ChanData->Regs.Dma)
ATA_WRITE(ChanData->Regs.Dma + 1, Control, ChanData, MRES_DMA);
else
PciWrite8(Controller, CMD_REG_MRDMODE, Control);
return TRUE;
}
static
BOOLEAN
CmdCheckInterruptPci(
_In_ PCHANNEL_DATA_PATA ChanData)
{
PATA_CONTROLLER Controller = ChanData->Controller;
UCHAR Status, InterruptMask;
ULONG Register;
InterruptMask = CMD_CFR_INTR(ChanData->Channel);
Register = (ChanData->Channel == 0) ? CMD_REG_CFR : CMD_REG_ARTTIM23;
/* Read CFR or REG_ARTTIM23 to clear the interrupt */
Status = PciRead8(Controller, Register);
return !!(Status & InterruptMask);
}
static
VOID
CmdControllerStart(
_In_ PATA_CONTROLLER Controller)
{
ULONG i;
UCHAR Control;
/*
* Initialize the controller to compatible PIO timings.
* We dynamically adjust these timings depending on attached devices.
*/
PciWrite8(Controller, CMD_REG_CMDTIM, 0);
if (Controller->Pci.DeviceID == PCI_DEV_PCI0640)
{
/*
* Errata: Disable the Read-Ahead Mode.
* https://www.mindprod.com/jgloss/eideflaw.html
*/
for (i = 0; i < RTL_NUMBER_OF(CmdPrefetchRegs); ++i)
{
Control = PciRead8(Controller, CmdPrefetchRegs[i]);
Control |= CmdPrefetchDisable[i][0] | CmdPrefetchDisable[i][1];
PciWrite8(Controller, CmdPrefetchRegs[i], Control);
}
}
else
{
Control = PciRead8(Controller, CMD_REG_MRDMODE);
Control &= ~(CMD_MRDMODE_INTR_CH0_BLOCK | CMD_MRDMODE_INTR_CH1_BLOCK);
Control |= CMD_MRDMODE_READ_MULTIPLE;
PciWrite8(Controller, CMD_REG_MRDMODE, Control);
}
}
CODE_SEG("PAGE")
NTSTATUS
CmdGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i, SupportedMode, HwFlags = 0;
PCHANNEL_CHECK_INTERRUPT CheckInterrupt = CmdCheckInterruptPci;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_CMD);
switch (Controller->Pci.DeviceID)
{
case PCI_DEV_PCI0640:
/*
* Errata: The CMD-640 has one set of task file registers per controller
* and thus the two channels cannot be used simultaneously.
* https://www.mindprod.com/jgloss/eideflaw.html
*/
Controller->Flags |= CTRL_FLAG_IS_SIMPLEX;
SupportedMode = PIO_ALL;
HwFlags |= HW_FLAGS_PRIMARY_ENABLED | HW_FLAGS_NO_PREFETCH;
break;
case PCI_DEV_PCI0643:
SupportedMode = PIO_ALL | SWDMA_ALL | MWDMA_ALL;
if (Controller->Pci.RevisionID < 6)
HwFlags |= HW_FLAGS_PRIMARY_ENABLED;
break;
case PCI_DEV_PCI0646:
SupportedMode = PIO_ALL | SWDMA_ALL | MWDMA_ALL;
HwFlags |= HW_FLAGS_HAS_UDMA_REG;
if (Controller->Pci.RevisionID < 3)
HwFlags |= HW_FLAGS_PRIMARY_ENABLED;
else
CheckInterrupt = CmdCheckInterruptMrdMode;
if (Controller->Pci.RevisionID == 5 || Controller->Pci.RevisionID == 6)
{
/*
* Early 646U2 revisions can support UDMA2 only at a PCI bus speed of 33MHz.
* When it runs at 25 MHz or 30 MHZ, the transfer speed must be limited to UDMA1.
*/
SupportedMode |= UDMA_MODES(0, 1);
}
else if (Controller->Pci.RevisionID > 6)
{
SupportedMode |= UDMA_MODES(0, 2);
}
break;
case PCI_DEV_PCI0648:
SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 4);
/* Prefetch bits have different meaning on this controller */
HwFlags |= HW_FLAGS_HAS_UDMA_REG | HW_FLAGS_NO_PREFETCH;
CheckInterrupt = CmdCheckInterruptMrdMode;
break;
case PCI_DEV_CMD0649:
SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5);
HwFlags |= HW_FLAGS_HAS_UDMA_REG;
if (Controller->Pci.RevisionID == 2)
HwFlags |= HW_FLAGS_NEED_PIO_FIX;
CheckInterrupt = CmdCheckInterruptMrdMode;
break;
default:
return STATUS_NO_MATCH;
}
Controller->Start = CmdControllerStart;
Controller->Flags |= CTRL_FLAG_USE_TEST_FUNCTION;
Controller->ChannelEnabledTest = CmdChannelEnabledTest;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
Controller->HwExt = ExAllocatePoolZero(NonPagedPool, sizeof(CMD_HW_EXTENSION), TAG_PCIIDEX);
if (!Controller->HwExt)
return STATUS_INSUFFICIENT_RESOURCES;
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
ChanData->HwFlags = HwFlags;
ChanData->CheckInterrupt = CheckInterrupt;
ChanData->SetTransferMode = CmdSetTransferMode;
ChanData->TransferModeSupported = SupportedMode;
if (Controller->Pci.DeviceID == PCI_DEV_PCI0640)
{
ChanData->ChanInfo &= ~CHANNEL_FLAG_IO32;
}
else
{
/* Check for 80-conductor cable */
if (ChanData->TransferModeSupported & UDMA_80C_ALL)
{
UCHAR BmControl = PciRead8(Controller, CMD_REG_BMIDECSR);
if (!(BmControl & CMD_BMIDECSR_CR(i)))
{
INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
}
}
}
return STATUS_SUCCESS;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: PATA hardware support
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* FUNCTIONS ******************************************************************/
static
BOOLEAN
PataIsDevicePresent(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ ULONG DeviceNumber)
{
UCHAR IdeStatus;
/* Select the device */
ATA_SELECT_DEVICE(ChanData, DeviceNumber, IDE_DRIVE_SELECT | ((DeviceNumber & 1) << 4));
/* Do a quick check first */
IdeStatus = ChanData->ReadStatus(ChanData);
INFO("CH %lu: Device %lu status %02x\n", ChanData->Channel, DeviceNumber, IdeStatus);
if (IdeStatus == 0xFF || IdeStatus == 0x7F)
return FALSE;
/* Look at controller */
ATA_WRITE(ChanData->Regs.ByteCountLow, 0x55, ChanData, MRES_TF);
ATA_WRITE(ChanData->Regs.ByteCountLow, 0xAA, ChanData, MRES_TF);
ATA_WRITE(ChanData->Regs.ByteCountLow, 0x55, ChanData, MRES_TF);
if (ATA_READ(ChanData->Regs.ByteCountLow, ChanData, MRES_TF) != 0x55)
return FALSE;
ATA_WRITE(ChanData->Regs.ByteCountHigh, 0xAA, ChanData, MRES_TF);
ATA_WRITE(ChanData->Regs.ByteCountHigh, 0x55, ChanData, MRES_TF);
ATA_WRITE(ChanData->Regs.ByteCountHigh, 0xAA, ChanData, MRES_TF);
if (ATA_READ(ChanData->Regs.ByteCountHigh, ChanData, MRES_TF) != 0xAA)
return FALSE;
return TRUE;
}
static
VOID
PataIssueSoftwareReset(
_In_ PCHANNEL_DATA_PATA ChanData)
{
ATA_WRITE(ChanData->Regs.Control, IDE_DC_RESET_CONTROLLER | IDE_DC_ALWAYS,
ChanData, MRES_CTRL);
KeStallExecutionProcessor(20);
ATA_WRITE(ChanData->Regs.Control, IDE_DC_DISABLE_INTERRUPTS | IDE_DC_ALWAYS,
ChanData, MRES_CTRL);
KeStallExecutionProcessor(20);
}
static
VOID
PataDrainDeviceBuffer(
_In_ PCHANNEL_DATA_PATA ChanData)
{
ULONG i;
USHORT LocalBuffer;
/* Try to clear the DRQ indication */
for (i = 0; i < ATA_MAX_TRANSFER_LENGTH / sizeof(USHORT); ++i)
{
UCHAR IdeStatus = ChanData->ReadStatus(ChanData);
if (!(IdeStatus & IDE_STATUS_DRQ))
break;
ATA_READ_BLOCK_16((PUSHORT)ChanData->Regs.Data,
&LocalBuffer,
1,
ChanData,
MRES_TF);
}
if (i > 0)
INFO("CH %lu: Total drained %lu bytes\n", ChanData->Channel, i * sizeof(USHORT));
}
ULONG
PataChannelGetMaximumDeviceCount(
_In_ PVOID ChannelContext)
{
PCHANNEL_DATA_PATA ChanData = ChannelContext;
#if defined(_M_IX86)
if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS)
return 4;
#endif
return (ChanData->ChanInfo & CHANNEL_FLAG_NO_SLAVE) ? 1 : 2;
}
VOID
PataResetChannel(
_In_ PVOID ChannelContext)
{
PCHANNEL_DATA_PATA ChanData = ChannelContext;
ULONG i, DeviceNumber, DeviceCount, DeviceBitmap = 0;
ChanData->IsPollingActive = FALSE;
#if defined(_M_IX86)
if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS)
ChanData->LastAtaBankId = 0xFF;
#endif
/*
* Reset the start/stop bus master bit, also needed on some chips to recover,
* see for example Intel 82371AB/EB/MB Specification Update 297738-017 #10.
*/
if (ChanData->Regs.Dma != NULL)
PciIdeDmaStop(ChanData);
PataDrainDeviceBuffer(ChanData);
DeviceCount = PataChannelGetMaximumDeviceCount(ChanData);
for (DeviceNumber = 0; DeviceNumber < DeviceCount; ++DeviceNumber)
{
if (PataIsDevicePresent(ChanData, DeviceNumber))
DeviceBitmap |= 1 << DeviceNumber;
}
/* Restore the device selection */
ATA_SELECT_DEVICE(ChanData, 0, IDE_DRIVE_SELECT);
WARN("CH %lu: Resetting IDE channel, devices map 0x%lx\n", ChanData->Channel, DeviceBitmap);
#if defined(_M_IX86)
if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS)
{
/* Reset the secondary IDE channel if present */
if (DeviceBitmap & ((1 << 2) | (1 << 3)))
{
WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, 1);
PataIssueSoftwareReset(ChanData);
}
WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, 0);
ChanData->LastAtaBankId = 0xFF;
}
#endif
PataIssueSoftwareReset(ChanData);
for (DeviceNumber = 0; DeviceNumber < DeviceCount; ++DeviceNumber)
{
UCHAR IdeStatus;
/* The reset will cause the master device to be selected */
if ((DeviceNumber % 2) != 0)
{
/* Always check BSY for the master device regardless of its presence */
if (!(DeviceBitmap & (1 << DeviceNumber)))
continue;
for (i = ATA_TIME_RESET_SELECT; i > 0; i--)
{
/* Select the device again */
ATA_SELECT_DEVICE(ChanData,
DeviceNumber,
IDE_DRIVE_SELECT | ((DeviceNumber & 1) << 4));
/* Check whether the selection was successful */
ATA_WRITE(ChanData->Regs.ByteCountLow, 0xAA, ChanData, MRES_TF);
ATA_WRITE(ChanData->Regs.ByteCountLow, 0x55, ChanData, MRES_TF);
ATA_WRITE(ChanData->Regs.ByteCountLow, 0xAA, ChanData, MRES_TF);
if (ATA_READ(ChanData->Regs.ByteCountLow, ChanData, MRES_TF) == 0xAA)
break;
AtaSleep();
}
if (i == 0)
{
ERR("CH %lu: Device %lu selection timeout %02x\n",
ChanData->Channel, DeviceNumber, ChanData->ReadStatus(ChanData));
continue;
}
}
/* Now wait for busy to clear */
for (i = 0; i < ATA_TIME_BUSY_RESET; ++i)
{
IdeStatus = ChanData->ReadStatus(ChanData);
if (IdeStatus == 0xFF)
break;
if (!(IdeStatus & IDE_STATUS_BUSY))
break;
AtaSleep();
}
if (IdeStatus & IDE_STATUS_BUSY)
{
ERR("CH %lu: Failed to reset device %lu, status %02x\n",
ChanData->Channel, DeviceNumber, IdeStatus);
}
else
{
INFO("CH %lu: Device %lu online with status %02x\n",
ChanData->Channel, DeviceNumber, IdeStatus);
}
}
AtaChanEnableInterruptsSync(ChanData, TRUE);
}
ATA_CONNECTION_STATUS
PataIdentifyDevice(
_In_ PVOID ChannelContext,
_In_ ULONG DeviceNumber)
{
PCHANNEL_DATA_PATA ChanData = ChannelContext;
UCHAR IdeStatus;
if (!PataIsDevicePresent(ChanData, DeviceNumber))
return CONN_STATUS_NO_DEVICE;
/* Wait for busy to clear */
IdeStatus = ATA_WAIT(ChanData, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0);
if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ))
{
/*
* This status indicates that the previous command hasn't been completed
* and the device is left in an unexpected state.
* A device reset is required to recover.
*/
WARN("CH %lu: Device %lu is busy %02x\n", ChanData->Channel, DeviceNumber, IdeStatus);
return CONN_STATUS_FAILURE;
}
/*
* Also we do not check the device signature,
* because early ATAPI drives (NEC CDR-260, and some other devices)
* report an ATA signature.
*/
return CONN_STATUS_DEV_UNKNOWN;
}
ULONG
PataEnumerateChannel(
_In_ PVOID ChannelContext)
{
PCHANNEL_DATA_PATA ChanData = ChannelContext;
/*
* By default, we do not issue the software reset to detect the device signature,
* since attached devices may reset their current transfer mode.
* We need to know what transfer mode is actually set by BIOS in order to
* select any transfer mode correctly by our generic PATA chipset driver
* (PciIdeAcpiSetTransferMode() or PciIdeBiosSetTransferMode()).
*/
return PataChannelGetMaximumDeviceCount(ChanData);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Legacy (non-PnP) IDE controllers support
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* FUNCTIONS ******************************************************************/
CODE_SEG("PAGE")
NTSTATUS
PataGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller,
_In_ PCM_RESOURCE_LIST ResourcesTranslated)
{
PCHANNEL_DATA_PATA ChanData;
NTSTATUS Status;
PAGED_CODE();
ASSERT(Controller->Flags & CTRL_FLAG_NON_PNP);
Controller->MaxChannels = 1;
Controller->ChannelBitmap = 0x1;
Controller->Flags &= ~CTRL_FLAG_NATIVE_PCI;
Controller->Flags |= CTRL_FLAG_MANUAL_RES;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
ChanData = Controller->Channels[0];
ChanData->ChanInfo &= ~CHANNEL_FLAG_IO32;
ChanData->TransferModeSupported = PIO_ALL;
Status = PciIdeParseResources(ChanData, ResourcesTranslated);
if (!NT_SUCCESS(Status))
return Status;
#if defined(_M_IX86)
if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS)
{
UCHAR Value, Result;
/* Check whether the 32-bit data port can be enabled on the southbridge */
Value = READ_PORT_UCHAR((PUCHAR)PC98_ATA_BANK);
WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, Value ^ PC98_ATA_BANK_32BIT_PORT);
Result = READ_PORT_UCHAR((PUCHAR)PC98_ATA_BANK);
if ((Result ^ Value) & PC98_ATA_BANK_32BIT_PORT)
{
ChanData->ChanInfo |= CHANNEL_FLAG_IO32;
}
}
#endif
return STATUS_SUCCESS;
}

View File

@@ -0,0 +1,67 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: PC-Tech RZ1000 PCI IDE controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_RZ1000 0x1000
#define PCI_DEV_RZ1001 0x1001
#define REG_CONFIG 0x40
#define CONFIG_PREFETCH 0x2000
/* FUNCTIONS ******************************************************************/
/*
* Errata: Disable the Read-Ahead Mode.
* https://web.archive.org/web/19961222024423/http://www.intel.com/procs/support/rz1000/rztech.htm
* https://web.archive.org/web/20250324132451/https://www.mindprod.com/jgloss/eideflaw.html
*/
static
VOID
PcTechControllerStart(
_In_ PATA_CONTROLLER Controller)
{
USHORT ConfigReg;
ConfigReg = PciRead16(Controller, REG_CONFIG);
ConfigReg &= ~CONFIG_PREFETCH;
PciWrite16(Controller, REG_CONFIG, ConfigReg);
}
CODE_SEG("PAGE")
NTSTATUS
PcTechGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_PC_TECH);
if (Controller->Pci.DeviceID != PCI_DEV_RZ1000 && Controller->Pci.DeviceID != PCI_DEV_RZ1001)
return STATUS_NO_MATCH;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
Controller->Start = PcTechControllerStart;
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
ChanData->ChanInfo &= ~CHANNEL_FLAG_IO32;
}
return STATUS_SUCCESS;
}

View File

@@ -0,0 +1,249 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: Silicon Image 0680 PCI IDE controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_SIL0680 0x0680
#define SIL_REG_XFER_MODE(Channel) (0x80 + ((Channel) << 2))
#define SIL_REG_SYS_CFG 0x8A
#define SIL_REG_CFG(Channel) (0xA0 + ((Channel) << 4))
#define SIL_REG_STATUS(Channel) (0xA1 + ((Channel) << 4))
#define SIL_REG_TF_TIM(Channel) (0xA2 + ((Channel) << 4))
#define SIL_REG_PIO_TIMING(Channel, Drive) (0xA4 + ((Channel) << 4) + ((Drive) << 1))
#define SIL_REG_DMA_TIMING(Channel, Drive) (0xA8 + ((Channel) << 4) + ((Drive) << 1))
#define SIL_REG_UDMA_TIMING(Channel, Drive) (0xAC + ((Channel) << 4) + ((Drive) << 1))
#define SIL_CFG_CABLE_80 0x00000001
#define SIL_CFG_MONITOR_IORDY 0x00000200
#define SIL_STATUS_INTR 0x08
#define SIL_CLK_MASK 0x30
#define SIL_CLK_100MHZ 0x00
#define SIL_CLK_133MHZ 0x10
#define SIL_CLK_X2 0x20
#define SIL_CLK_DISABLED 0x30
static const USHORT Sil680TaskFileTimings[] =
{
0x328A, // 0
0x2283, // 1
0x1281, // 2
0x10C3, // 3
0x10C1 // 4
};
static const USHORT Sil680PioTimings[] =
{
0x328A, // 0
0x2283, // 1
0x1104, // 2
0x10C3, // 3
0x10C1 // 4
};
static const USHORT Sil680MwDmaTimings[] =
{
0x2208, // 0
0x10C2, // 1
0x10C1, // 2
};
static const UCHAR Sil680UdmaTimings[2][7] =
{
// 0 1 2 3 4 5 6
{ 0x0B, 0x07, 0x05, 0x04, 0x02, 0x01 }, // 100MHz
{ 0x0F, 0x0B, 0x07, 0x05, 0x03, 0x02, 0x01 }, // 133MHz
};
/* FUNCTIONS ******************************************************************/
static
VOID
Sil680SetTransferMode(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList)
{
ULONG i, SlowestPioMode, Config;
UCHAR ModeReg;
BOOLEAN MonitorIoReady = FALSE;
INFO("CH %lu: Config (before)\n"
"MODE %08lX\n"
"CFG %08lX\n"
"PIO %08lX\n"
"DMA %08lX\n"
"UDMA %08lX\n",
Channel,
PciRead32(Controller, SIL_REG_XFER_MODE(Channel)),
PciRead32(Controller, SIL_REG_CFG(Channel)),
PciRead32(Controller, SIL_REG_PIO_TIMING(Channel, 0)),
PciRead32(Controller, SIL_REG_DMA_TIMING(Channel, 0)),
PciRead32(Controller, SIL_REG_UDMA_TIMING(Channel, 0)));
SlowestPioMode = UDMA_MODE(0);
ModeReg = PciRead8(Controller, SIL_REG_XFER_MODE(Channel));
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
UCHAR XferMode, SysConfig;
if (!Device)
continue;
/* PIO timings */
SlowestPioMode = min(SlowestPioMode, Device->PioMode);
PciWrite16(Controller, SIL_REG_PIO_TIMING(Channel, i), Sil680PioTimings[Device->PioMode]);
if (Device->PioMode >= PIO_MODE(3))
MonitorIoReady = TRUE;
if (Device->DmaMode >= UDMA_MODE(0))
{
SysConfig = PciRead8(Controller, SIL_REG_SYS_CFG);
/* Try to enable the 133MHz IDE clock */
if ((SysConfig & SIL_CLK_MASK) == SIL_CLK_100MHZ)
{
PciWrite8(Controller, SIL_REG_SYS_CFG, SysConfig | SIL_CLK_133MHZ);
SysConfig = PciRead8(Controller, SIL_REG_SYS_CFG);
}
/* If we are unable to do so, skip UDMA6 selection */
if ((Device->DmaMode == UDMA_MODE(6)) &&
(SysConfig & SIL_CLK_MASK) == SIL_CLK_100MHZ)
{
ULONG SupportedModes = Device->SupportedModes & ~(PIO_ALL | UDMA_MODE6);
if (!_BitScanReverse(&Device->DmaMode, SupportedModes))
Device->DmaMode = PIO_MODE(0);
}
}
/* UDMA timings */
if (Device->DmaMode >= UDMA_MODE(0))
{
ULONG ClockIndex = ((SysConfig & SIL_CLK_MASK) == SIL_CLK_100MHZ) ? 0 : 1;
USHORT UdmaTim;
XferMode = 3;
UdmaTim = PciRead16(Controller, SIL_REG_UDMA_TIMING(Channel, i));
UdmaTim &= ~0x003F;
UdmaTim |= Sil680UdmaTimings[ClockIndex][Device->DmaMode - UDMA_MODE(0)];
PciWrite16(Controller, SIL_REG_UDMA_TIMING(Channel, i), UdmaTim);
}
/* DMA timings */
else if (Device->DmaMode != PIO_MODE(0))
{
ULONG ModeIndex = Device->DmaMode - MWDMA_MODE(0);
XferMode = 2;
PciWrite16(Controller, SIL_REG_DMA_TIMING(Channel, i), Sil680MwDmaTimings[ModeIndex]);
}
else
{
if (Device->IoReadySupported)
XferMode = 1;
else
XferMode = 0;
}
ModeReg &= ~(0x03 << (i * 4));
ModeReg |= XferMode << (i * 4);
}
/* Transfer mode */
PciWrite8(Controller, SIL_REG_XFER_MODE(Channel), ModeReg);
Config = PciRead32(Controller, SIL_REG_CFG(Channel));
if (MonitorIoReady)
Config |= SIL_CFG_MONITOR_IORDY;
else
Config &= ~SIL_CFG_MONITOR_IORDY;
/* Task file timings */
if (SlowestPioMode != UDMA_MODE(0))
{
Config &= ~0xFFFF0000;
Config |= Sil680TaskFileTimings[SlowestPioMode] << 16;
}
PciWrite32(Controller, SIL_REG_CFG(Channel), Config);
INFO("CH %lu: Config (after)\n"
"MODE %08lX\n"
"CFG %08lX\n"
"PIO %08lX\n"
"DMA %08lX\n"
"UDMA %08lX\n",
Channel,
PciRead32(Controller, SIL_REG_XFER_MODE(Channel)),
Config,
PciRead32(Controller, SIL_REG_PIO_TIMING(Channel, 0)),
PciRead32(Controller, SIL_REG_DMA_TIMING(Channel, 0)),
PciRead32(Controller, SIL_REG_UDMA_TIMING(Channel, 0)));
}
static
BOOLEAN
Sil680CheckInterrupt(
_In_ PCHANNEL_DATA_PATA ChanData)
{
PATA_CONTROLLER Controller = ChanData->Controller;
UCHAR Status;
Status = PciRead8(Controller, SIL_REG_STATUS(ChanData->Channel));
return !!(Status & SIL_STATUS_INTR);
}
CODE_SEG("PAGE")
NTSTATUS
Sil680GetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_CMD);
if (Controller->Pci.VendorID != PCI_DEV_SIL0680)
return STATUS_NO_MATCH;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
UCHAR Config;
ChanData->CheckInterrupt = Sil680CheckInterrupt;
ChanData->SetTransferMode = Sil680SetTransferMode;
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 6);
/* Check for 80-conductor cable */
Config = PciRead8(Controller, SIL_REG_CFG(i));
if (!(Config & SIL_CFG_CABLE_80))
{
INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
}
return STATUS_NOT_IMPLEMENTED;
}

View File

@@ -0,0 +1,361 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause)
* PURPOSE: ServerWorks/Broadcom ATA controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/*
* Adapted from the FreeBSD ata-serverworks driver
* Copyright (c) 1998-2008 Søren Schmidt <sos@FreeBSD.org>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_OSB4_IDE 0x0211
#define PCI_DEV_CSB5_IDE 0x0212
#define PCI_DEV_CSB6_IDE 0x0213
#define PCI_DEV_CSB6_IDE_THIRD 0x0217
#define PCI_DEV_HT1000_IDE 0x0214
#define PCI_DEV_OSB4_BRIDGE 0x0210
#define SVW_REG_PIO_TIMING 0x40
#define SVW_REG_DMA_TIMING 0x44
#define SVW_REG_PIO_MODE 0x4A // CSB5 and later
#define SVW_REG_UDMA_ENABLE 0x54
#define SVW_REG_UDMA_MODE 0x56
#define SVW_REG_UDMA_CONTROL 0x5A // Absent on ATI
#define SVW_UDMA_CTRL_MODE_MASK 0x03
#define SVW_UDMA_CTRL_DISABLE 0x40
#define SVW_UDMA_CTRL_MODE_UDMA4 0x02
#define SVW_UDMA_CTRL_MODE_UDMA5 0x03
static const struct
{
UCHAR Value;
ULONG CycleTime;
} SvwPioTimings[] =
{
{ 0x5D, 600 }, // 0
{ 0x47, 390 }, // 1
{ 0x34, 270 }, // 2
{ 0x22, 180 }, // 3
{ 0x20, 120 }, // 4
};
static const struct
{
UCHAR Value;
ULONG CycleTime;
} SvwMwDmaTimings[] =
{
{ 0x77, 480 }, // 0
{ 0x21, 150 }, // 1
{ 0x20, 120 }, // 2
};
/* FUNCTIONS ******************************************************************/
static
VOID
SvwChooseDeviceSpeed(
_In_ ULONG Channel,
_In_ PCHANNEL_DEVICE_CONFIG Device)
{
ULONG Mode;
/* PIO speed */
for (Mode = Device->PioMode; Mode > PIO_MODE(0); Mode--)
{
if (!(Device->SupportedModes & (1 << Mode)))
continue;
if (SvwPioTimings[Mode].CycleTime >= Device->MinPioCycleTime)
break;
}
if (Mode != Device->PioMode)
INFO("CH %lu: Downgrade PIO speed from %lu to %lu\n", Channel, Device->PioMode, Mode);
Device->PioMode = Mode;
/* UDMA works independently of any PIO mode */
if (Device->DmaMode == PIO_MODE(0) || Device->DmaMode >= UDMA_MODE(0))
return;
/* DMA speed */
for (Mode = Device->DmaMode; Mode > PIO_MODE(0); Mode--)
{
if (!(Device->SupportedModes & ~PIO_ALL & (1 << Mode)))
continue;
ASSERT((1 << Mode) & MWDMA_ALL);
if (SvwMwDmaTimings[Mode - MWDMA_MODE(0)].CycleTime >= Device->MinMwDmaCycleTime)
break;
}
if (Mode != Device->DmaMode)
{
if (Mode == PIO_MODE(0))
WARN("CH %lu: Too slow device '%s', disabling DMA\n", Channel, Device->FriendlyName);
else
INFO("CH %lu: Downgrade DMA speed from %lu to %lu\n", Channel, Device->DmaMode, Mode);
}
Device->DmaMode = Mode;
}
VOID
SvwSetTransferMode(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList)
{
USHORT PioModeReg, UdmaModeReg;
UCHAR UdmaEnableReg;
ULONG i, PioTimReg, DmaTimReg;
if (Controller->Pci.DeviceID != PCI_DEV_OSB4_IDE)
PioModeReg = PciRead16(Controller, SVW_REG_PIO_MODE);
else
PioModeReg = 0;
PioTimReg = PciRead32(Controller, SVW_REG_PIO_TIMING);
DmaTimReg = PciRead32(Controller, SVW_REG_DMA_TIMING);
UdmaEnableReg = PciRead8(Controller, SVW_REG_UDMA_ENABLE);
UdmaModeReg = PciRead16(Controller, SVW_REG_UDMA_MODE);
INFO("CH %lu: Config (before)\n"
"DMA Timing %08lX\n"
"PIO Timing %08lX\n"
"PIO Mode %04X\n"
"UDMA Mode %04X\n",
"UDMA Enable %02X\n",
Channel,
DmaTimReg,
PioTimReg,
PioModeReg,
UdmaModeReg,
UdmaEnableReg);
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
const ULONG DeviceIndex = (Channel << 1) + i; // 0 1 2 3
const ULONG Shift = (DeviceIndex ^ 1) << 3; // 8 0 24 16
UdmaModeReg &= ~(0x0F << (DeviceIndex << 2));
UdmaEnableReg &= ~(0x01 << DeviceIndex);
if (!Device)
continue;
/* Make sure that the selected PIO and DMA speeds match the cycle time */
SvwChooseDeviceSpeed(Channel, Device);
/* UDMA timings */
if (Device->DmaMode >= UDMA_MODE(0))
{
ULONG ModeIndex = Device->DmaMode - UDMA_MODE(0);
UdmaModeReg |= ModeIndex << (DeviceIndex << 2);
UdmaEnableReg |= 1 << DeviceIndex;
DmaTimReg &= ~(0xFF << Shift);
DmaTimReg |= SvwMwDmaTimings[2].Value << Shift;
}
/* DMA timings */
else if (Device->DmaMode != PIO_MODE(0))
{
ULONG ModeIndex = Device->DmaMode - MWDMA_MODE(0);
DmaTimReg &= ~(0xFF << Shift);
DmaTimReg |= SvwMwDmaTimings[ModeIndex].Value << Shift;
}
/* PIO timings */
PioModeReg &= ~(0x0F << (DeviceIndex << 2));
PioModeReg |= Device->PioMode << (DeviceIndex << 2);
PioTimReg &= ~(0xFF << Shift);
PioTimReg |= SvwPioTimings[Device->PioMode].Value << Shift;
}
PciWrite32(Controller, SVW_REG_PIO_TIMING, PioTimReg);
if (Controller->Pci.DeviceID != PCI_DEV_OSB4_IDE)
PciWrite16(Controller, SVW_REG_PIO_MODE, PioModeReg);
PciWrite32(Controller, SVW_REG_DMA_TIMING, DmaTimReg);
PciWrite16(Controller, SVW_REG_UDMA_MODE, UdmaModeReg);
PciWrite8(Controller, SVW_REG_UDMA_ENABLE, UdmaEnableReg);
INFO("CH %lu: Config (after)\n"
"DMA Timing %08lX\n"
"PIO Timing %08lX\n"
"PIO Mode %04X\n"
"UDMA Mode %04X\n",
"UDMA Enable %02X\n",
Channel,
DmaTimReg,
PioTimReg,
PioModeReg,
UdmaModeReg,
UdmaEnableReg);
}
CODE_SEG("PAGE")
BOOLEAN
SvwHasUdmaCable(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel)
{
UCHAR UdmaModeReg;
PAGED_CODE();
UdmaModeReg = PciRead8(Controller, SVW_REG_UDMA_MODE + Channel);
/* Check mode settings, see if UDMA3 or higher mode is active */
return ((UdmaModeReg & 0x07) > 2) || ((UdmaModeReg & 0x70) > 0x20);
}
static
BOOLEAN
SvwOsb4PciBridgeInit(
_In_ PVOID Context,
_In_ ULONG BusNumber,
_In_ PCI_SLOT_NUMBER PciSlot,
_In_ PPCI_COMMON_HEADER PciConfig)
{
ULONG Value;
UNREFERENCED_PARAMETER(Context);
if (PciConfig->VendorID != PCI_VEN_SERVERWORKS ||
PciConfig->DeviceID != PCI_DEV_OSB4_BRIDGE)
{
return FALSE;
}
HalGetBusDataByOffset(PCIConfiguration,
BusNumber,
PciSlot.u.AsULONG,
&Value,
0x64,
sizeof(Value));
Value &= ~0x00002000; // Disable 600ns interrupt mask
Value |= 0x00004000; // Enable UDMA/33 support
HalSetBusDataByOffset(PCIConfiguration,
BusNumber,
PciSlot.u.AsULONG,
&Value,
0x64,
sizeof(Value));
return TRUE;
}
static
VOID
SvwPataControllerStart(
_In_ PATA_CONTROLLER Controller)
{
if (Controller->Pci.DeviceID == PCI_DEV_OSB4_IDE)
{
PciFindDevice(SvwOsb4PciBridgeInit, NULL);
}
else
{
PCHANNEL_DATA_COMMON ChanData = Controller->Channels[0];
UCHAR UdmaCtrlReg = PciRead8(Controller, SVW_REG_UDMA_CONTROL);
UdmaCtrlReg &= ~(SVW_UDMA_CTRL_DISABLE | SVW_UDMA_CTRL_MODE_MASK);
if (ChanData->TransferModeSupported & UDMA_MODE5)
UdmaCtrlReg |= SVW_UDMA_CTRL_MODE_UDMA5;
else
UdmaCtrlReg |= SVW_UDMA_CTRL_MODE_UDMA4;
PciWrite8(Controller, SVW_REG_UDMA_CONTROL, UdmaCtrlReg);
}
}
CODE_SEG("PAGE")
NTSTATUS
SvwPataGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i, SupportedMode;
BOOLEAN No64KDma = FALSE;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_SERVERWORKS);
switch (Controller->Pci.DeviceID)
{
case PCI_DEV_OSB4_IDE:
/* UDMA is supported but its use is discouraged */
SupportedMode = PIO_ALL | MWDMA_ALL;
No64KDma = TRUE;
break;
case PCI_DEV_CSB5_IDE:
if (Controller->Pci.RevisionID < 0x92)
SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 4);
else
SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5);
Controller->Flags |= CTRL_FLAG_DMA_INTERRUPT;
break;
case PCI_DEV_CSB6_IDE_THIRD:
SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 4);
Controller->MaxChannels = 1;
break;
case PCI_DEV_CSB6_IDE:
SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5);
break;
case PCI_DEV_HT1000_IDE:
SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5);
Controller->MaxChannels = 1;
No64KDma = TRUE;
break;
default:
return STATUS_NO_MATCH;
}
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
Controller->Start = SvwPataControllerStart;
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
if (No64KDma)
ChanData->MaximumTransferLength = PCIIDE_PRD_LIMIT - 0x4000;
ChanData->SetTransferMode = SvwSetTransferMode;
ChanData->TransferModeSupported = SupportedMode;
/* Check for 80-conductor cable */
if ((ChanData->TransferModeSupported & UDMA_80C_ALL) && !SvwHasUdmaCable(Controller, i))
{
INFO("CH %lu: BIOS hasn't selected mode faster than UDMA 2, "
"assume 40-conductor cable\n",
ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
}
return STATUS_SUCCESS;
}

View File

@@ -0,0 +1,289 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause)
* PURPOSE: ServerWorks/Broadcom SATA controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/*
* Adapted from the FreeBSD ata-serverworks driver
* Copyright (c) 1998-2008 Søren Schmidt <sos@FreeBSD.org>
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_K2_SATA 0x0240
#define PCI_DEV_BCM5770_SATA 0x0241
#define PCI_DEV_BCM5770R_SATA 0x0242
#define PCI_DEV_HT1000_SATA 0x024A
#define PCI_DEV_HT1000_SATA_2 0x024B
#define PCI_DEV_HT1100_SATA 0x0410
#define PCI_DEV_HT1100_SATA_2 0x0411
#define SVW_SATA_PORT_BASE_OFFSET 0x100
#define SVW_SATA_PORT_CONTROL_OFFSET 0x20
#define SVW_SATA_PORT_DMA_OFFSET 0x30
#define SVW_SATA_PORT_SCR_OFFSET 0x40
#define SVW_SATA_PORT_TF_STRIDE 4
#define HW_FLAGS_8_PORTS 0x01
#define HW_FLAGS_NO_ATAPI_DMA 0x02
PCIIDEX_PAGED_DATA
static const struct
{
USHORT DeviceID;
USHORT Flags;
} SvwSataControllerList[] =
{
{ PCI_DEV_K2_SATA, HW_FLAGS_NO_ATAPI_DMA },
{ PCI_DEV_BCM5770_SATA, HW_FLAGS_NO_ATAPI_DMA | HW_FLAGS_8_PORTS },
{ PCI_DEV_BCM5770R_SATA, HW_FLAGS_NO_ATAPI_DMA },
{ PCI_DEV_HT1000_SATA, HW_FLAGS_NO_ATAPI_DMA },
{ PCI_DEV_HT1000_SATA_2, HW_FLAGS_NO_ATAPI_DMA },
{ PCI_DEV_HT1100_SATA, 0 },
{ PCI_DEV_HT1100_SATA_2, 0 },
};
/* FUNCTIONS ******************************************************************/
static
BOOLEAN
SvwSataScrRead(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ SATA_SCR_REGISTER Register,
_In_ ULONG PortNumber,
_In_ PULONG Result)
{
UNREFERENCED_PARAMETER(PortNumber);
if (Register > ATA_SCONTROL)
return FALSE;
*Result = READ_REGISTER_ULONG((PULONG)(ChanData->Regs.Scr + (Register * 4)));
return TRUE;
}
static
BOOLEAN
SvwSataScrWrite(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ SATA_SCR_REGISTER Register,
_In_ ULONG PortNumber,
_In_ ULONG Value)
{
UNREFERENCED_PARAMETER(PortNumber);
if (Register > ATA_SCONTROL)
return FALSE;
WRITE_REGISTER_ULONG((PULONG)(ChanData->Regs.Scr + (Register * 4)), Value);
return TRUE;
}
static
inline
ULONG
SvwSataGetIoBarIndex(
_In_ PATA_CONTROLLER Controller)
{
return (Controller->Pci.DeviceID == PCI_DEV_HT1100_SATA) ? 3 : 5;
}
static
BOOLEAN
SvwSataCheckInterrupt(
_In_ PCHANNEL_DATA_PATA ChanData)
{
PATA_CONTROLLER Controller = ChanData->Controller;
PUCHAR IoBase = Controller->AccessRange[SvwSataGetIoBarIndex(Controller)].IoBase;
ULONG InterruptStatus;
InterruptStatus = READ_REGISTER_ULONG((PULONG)(IoBase + 0x1F80));
return !!(InterruptStatus & (1 << ChanData->Channel));
}
static
UCHAR
SvwSataReadStatus(
_In_ PCHANNEL_DATA_PATA ChanData)
{
/*
* The SATA hardware expects 32-bit reads on the status register
* to get it latched propertly.
*/
return READ_REGISTER_ULONG((PULONG)ChanData->Regs.Status);
}
static
VOID
SvwSataLoadTaskFile(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ PATA_DEVICE_REQUEST Request)
{
USHORT Features, SectorCount, LbaLow, LbaMid, LbaHigh;
Features = Request->TaskFile.Feature;
SectorCount = Request->TaskFile.SectorCount;
LbaLow = Request->TaskFile.LowLba;
LbaMid = Request->TaskFile.MidLba;
LbaHigh = Request->TaskFile.HighLba;
if (Request->Flags & REQUEST_FLAG_LBA48)
{
Features |= (USHORT)Request->TaskFile.FeatureEx << 8;
SectorCount |= (USHORT)Request->TaskFile.SectorCountEx << 8;
LbaLow |= (USHORT)Request->TaskFile.LowLbaEx << 8;
LbaMid |= (USHORT)Request->TaskFile.MidLbaEx << 8;
LbaHigh |= (USHORT)Request->TaskFile.HighLbaEx << 8;
}
/* The SATA hardware needs 16-bit accesses for the second byte of FIFO */
WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.Features, Features);
WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.SectorCount, SectorCount);
WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaLow, LbaLow);
WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaMid, LbaMid);
WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaHigh, LbaHigh);
if (Request->Flags & REQUEST_FLAG_SET_DEVICE_REGISTER)
WRITE_REGISTER_UCHAR(ChanData->Regs.Device, Request->TaskFile.DriveSelect);
}
static
VOID
SvwSataSaveTaskFile(
_In_ PCHANNEL_DATA_PATA ChanData,
_Inout_ PATA_DEVICE_REQUEST Request)
{
PATA_TASKFILE TaskFile = &Request->Output;
USHORT Error, SectorCount, LbaLow, LbaMid, LbaHigh;
TaskFile->DriveSelect = READ_REGISTER_UCHAR(ChanData->Regs.Device);
TaskFile->Command = READ_REGISTER_UCHAR(ChanData->Regs.Command);
Error = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.Error);
SectorCount = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.SectorCount);
LbaLow = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaLow);
LbaMid = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaMid);
LbaHigh = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaHigh);
TaskFile->Error = Error & 0xFF;
TaskFile->SectorCount = SectorCount & 0xFF;
TaskFile->LowLba = LbaLow & 0xFF;
TaskFile->MidLba = LbaMid & 0xFF;
TaskFile->HighLba = LbaHigh & 0xFF;
if (Request->Flags & REQUEST_FLAG_LBA48)
{
TaskFile->FeatureEx = Error >> 8;
TaskFile->SectorCountEx = SectorCount >> 8;
TaskFile->LowLbaEx = LbaLow >> 8;
TaskFile->MidLbaEx = LbaMid >> 8;
TaskFile->HighLbaEx = LbaHigh >> 8;
}
}
static
CODE_SEG("PAGE")
VOID
SvwSataParseResources(
_Inout_ PCHANNEL_DATA_PATA ChanData,
_In_ PUCHAR IoBase,
_In_ ULONG PortIndex)
{
PUCHAR ChanBase = IoBase + (PortIndex * SVW_SATA_PORT_BASE_OFFSET);
PAGED_CODE();
ChanData->ChanInfo |= CHANNEL_FLAG_MRES_TF | CHANNEL_FLAG_MRES_CTRL | CHANNEL_FLAG_MRES_DMA;
ChanData->Regs.Dma = ChanBase + SVW_SATA_PORT_DMA_OFFSET;
ChanData->Regs.Scr = ChanBase + SVW_SATA_PORT_SCR_OFFSET;
PciIdeInitTaskFileIoResources(ChanData,
(ULONG_PTR)ChanBase,
(ULONG_PTR)ChanBase + SVW_SATA_PORT_CONTROL_OFFSET,
SVW_SATA_PORT_TF_STRIDE);
}
CODE_SEG("PAGE")
NTSTATUS
SvwSataGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i, HwFlags;
PUCHAR IoBase;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_SERVERWORKS);
for (i = 0; i < RTL_NUMBER_OF(SvwSataControllerList); ++i)
{
HwFlags = SvwSataControllerList[i].Flags;
if (Controller->Pci.DeviceID == SvwSataControllerList[i].DeviceID)
break;
}
if (i == RTL_NUMBER_OF(SvwSataControllerList))
return STATUS_NO_MATCH;
if (HwFlags & HW_FLAGS_8_PORTS)
Controller->MaxChannels = 8;
else
Controller->MaxChannels = 4;
Controller->Flags |= CTRL_FLAG_MANUAL_RES;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
IoBase = AtaCtrlPciMapBar(Controller, SvwSataGetIoBarIndex(Controller), 0);
if (!IoBase)
return STATUS_DEVICE_CONFIGURATION_ERROR;
/*
* Errata: Set SICR registers to turn off waiting for a status message
* before sending FIS, fixes some issues with Seagate hard drives.
*/
WRITE_REGISTER_ULONG((PULONG)(IoBase + 0x80),
READ_REGISTER_ULONG((PULONG)(IoBase + 0x80)) & ~0x00040000);
/* Clear interrupts */
WRITE_REGISTER_ULONG((PULONG)(IoBase + 0x44), 0xFFFFFFFF);
WRITE_REGISTER_ULONG((PULONG)(IoBase + 0x88), 0);
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
ChanData->TransferModeSupported = SATA_ALL;
ChanData->SetTransferMode = SataSetTransferMode;
if (HwFlags & HW_FLAGS_NO_ATAPI_DMA)
ChanData->ChanInfo |= CHANNEL_FLAG_NO_ATAPI_DMA;
/*
* Errata: Enable the DMA engine before sending the drive command
* to avoid data corruption.
*/
ChanData->ChanInfo |= CHANNEL_FLAG_DMA_BEFORE_CMD;
ChanData->ChanInfo |= CHANNEL_FLAG_NO_SLAVE;
ChanData->ReadStatus = SvwSataReadStatus;
ChanData->LoadTaskFile = SvwSataLoadTaskFile;
ChanData->SaveTaskFile = SvwSataSaveTaskFile;
ChanData->CheckInterrupt = SvwSataCheckInterrupt;
ChanData->ScrRead = SvwSataScrRead;
ChanData->ScrWrite = SvwSataScrWrite;
SvwSataParseResources(ChanData, IoBase, i);
}
return STATUS_SUCCESS;
}

View File

@@ -0,0 +1,134 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause)
* PURPOSE: Toshiba PCI IDE controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/*
* Adapted from the NetBSD toshide driver
* Copyright (c) 2009 The NetBSD Foundation, Inc
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_PICCOLO_1 0x0101
#define PCI_DEV_PICCOLO_2 0x0102
#define PCI_DEV_PICCOLO_3 0x0103
#define PCI_DEV_PICCOLO_5 0x0105
#define PICCOLO_REG_PIO_TIMING 0x50
#define PICCOLO_REG_DMA_TIMING 0x5C
#define PICCOLO_PIO_MASK 0xFFFFE088
#define PICCOLO_DMA_MASK 0xFFFFE088
#define PICCOLO_UDMA_MASK 0x78FFE088
static const ULONG PiccoloPioTimings[] =
{
0x0566, // Mode 0
0x0433, // Mode 1
0x0311, // Mode 2
0x0201, // Mode 3
0x0200 // Mode 4
};
static const ULONG PiccoloMwDmaTimings[] =
{
0x0655, // Mode 0
0x0200, // Mode 1
0x0200, // Mode 2
};
static const ULONG PiccoloUdmaTimings[] =
{
0x84000222, // Mode 0
0x83000111, // Mode 1
0x82000000, // Mode 2
};
/* FUNCTIONS ******************************************************************/
static
VOID
ToshibaSetTransferMode(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList)
{
ULONG i;
UNREFERENCED_PARAMETER(Channel);
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
ULONG TimingReg;
if (!Device)
continue;
TimingReg = PciRead32(Controller, PICCOLO_REG_PIO_TIMING);
TimingReg &= PICCOLO_PIO_MASK;
TimingReg |= PiccoloPioTimings[Device->PioMode];
PciWrite32(Controller, PICCOLO_REG_PIO_TIMING, TimingReg);
if (Device->DmaMode != PIO_MODE(0))
{
TimingReg = PciRead32(Controller, PICCOLO_REG_DMA_TIMING);
if (Device->DmaMode >= UDMA_MODE(0))
{
TimingReg &= PICCOLO_UDMA_MASK;
TimingReg |= PiccoloUdmaTimings[Device->DmaMode - UDMA_MODE(0)];
}
else
{
TimingReg &= PICCOLO_DMA_MASK;
TimingReg |= PiccoloMwDmaTimings[Device->DmaMode - MWDMA_MODE(0)];
}
PciWrite32(Controller, PICCOLO_REG_DMA_TIMING, TimingReg);
}
}
}
CODE_SEG("PAGE")
NTSTATUS
ToshibaGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_TOSHIBA);
if (Controller->Pci.DeviceID != PCI_DEV_PICCOLO_1 &&
Controller->Pci.DeviceID != PCI_DEV_PICCOLO_2 &&
Controller->Pci.DeviceID != PCI_DEV_PICCOLO_3 &&
Controller->Pci.DeviceID != PCI_DEV_PICCOLO_5)
{
return STATUS_NO_MATCH;
}
Controller->MaxChannels = 1;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 2);
ChanData->SetTransferMode = ToshibaSetTransferMode;
}
return STATUS_SUCCESS;
}

View File

@@ -0,0 +1,899 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause)
* PURPOSE: VIA ATA controller minidriver
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
*/
/*
* Some code taken from the NetBSD viaide driver
* Copyright (c) 1999, 2000, 2001 Manuel Bouyer
*/
/* INCLUDES *******************************************************************/
#include "pciidex.h"
/* GLOBALS ********************************************************************/
#define PCI_DEV_VT82C576M_IDE 0x1571
#define PCI_DEV_VT82C586A_IDE 0x0571
#define PCI_DEV_VT6410_IDE_RAID 0x3164
#define PCI_DEV_VT6415_IDE 0x0415
#define PCI_DEV_VT6420_IDE 0x4149
#define PCI_DEV_VT6421A_RAID 0x3249
#define PCI_DEV_CX700M2_IDE_SATA 0x5324
#define PCI_DEV_CX700M2_IDE_SATA_RAID 0x0581
#define PCI_DEV_VX855_IDE 0xC409
#define PCI_DEV_VT8237_SATA 0x3149
#define PCI_DEV_VT8237A_SATA 0x0591
#define PCI_DEV_VT8237A_SATA_2 0x5337
#define PCI_DEV_VT8237S_SATA 0x5372
#define PCI_DEV_VT8237S_SATA_RAID 0x7372
#define PCI_DEV_VT8251_SATA_AHCI 0x3349
#define PCI_DEV_VT8251_SATA_2 0x5287
#define PCI_DEV_VT8261_SATA 0x9000
#define PCI_DEV_VT8261_SATA_RAID 0x9040
#define PCI_DEV_VX900_SATA 0x9001
#define PCI_DEV_VX900_SATA_RAID 0x9041
#define PCI_DEV_BRIDGE_VT82C586 0x0586
#define PCI_DEV_BRIDGE_VT82C596A 0x0596
#define PCI_DEV_BRIDGE_VT82C686A 0x0686
#define PCI_DEV_BRIDGE_VT8231 0x8231
#define PCI_DEV_BRIDGE_VT8233 0x3074
#define PCI_DEV_BRIDGE_VT8233A 0x3147
#define PCI_DEV_BRIDGE_VT8233C 0x3109
#define PCI_DEV_BRIDGE_VT8235 0x3177
#define PCI_DEV_BRIDGE_VT8237 0x3227
#define PCI_DEV_BRIDGE_VT8237A 0x3337
#define PCI_DEV_BRIDGE_VT8237S 0x3372
#define PCI_DEV_BRIDGE_VT8251 0x3287
#define PCI_DEV_BRIDGE_VT8261 0x3402
#define HW_FLAGS_TYPE_MASK 0x000F
#define HW_FLAGS_CHECK_BRIDGE 0x0010
#define HW_FLAGS_HAS_UDMA_CLOCK 0x0020
#define HW_FLAGS_SINGLE_CHAN 0x0040
#define HW_FLAGS_SINGLE_PORT 0x0080
#define HW_FLAGS_PCI_SCR 0x0100
#define TYPE_33 0
#define TYPE_66 1
#define TYPE_100 2
#define TYPE_133 3
#define TYPE_MWDMA 4
#define TYPE_SATA 5
#define GET_TYPE(Flags) ((Flags) & HW_FLAGS_TYPE_MASK)
#define VIA_PCI_CLOCK 30000
#define VIA_REG_ENABLE_CTRL 0x40
#define VIA_REG_DRIVE_TIMING 0x48
#define VIA_REG_PORT_TIMING(Channel) (0x4F - (Channel))
#define VIA_REG_ADDRESS_SETUP 0x4C
#define VIA_REG_UDMA_CTRL 0x50
#define VIA_REG_SSTATUS 0xA0
#define VIA_REG_SCONTROL 0xA4
#define VIA_REG_SERROR 0xA8
#define VIA_REG_SERROR_VT8237 0xB0
#define VIA_UDMA_CLOCK_UDMA66 0x08
#define VIA_UDMA_CABLE_BITS 0x10
#define VIA_UDMA_CLEAR_MASK 0xEF // Save the cable bits
#define VIA_UDMA_CLOCK_ENABLED(Reg32, Channel) \
(((Reg32) & (0x00080000 >> ((Channel) * 16))) != 0)
#define VIA_UDMA_CABLE_PRESENT(Reg32, Channel) \
(((Reg32) & (0x10100000 >> ((Channel) * 16))) != 0)
#define VIA_SINGLE_CHAN_UDMA_CABLE_PRESENT(Controller) \
((PciRead16(Controller, 0x50) & 0x1010) != 0)
/* Note that the cable bits are inverted */
#define VT6421A_UDMA_CABLE_PRESENT(Controller) \
((PciRead16(Controller, 0xB2) & 0x1010) == 0)
#define VIA_REG_SATA_PORT_MAP 0x49
#define VT6421A_REG_SATA_CTRL 0x52 // Also for VT8237
#define VT6421A_REG_TIMING_CTRL(Drive) (0xAB - (Drive))
#define VT6421A_REG_UDMA_CTRL(Drive) (0xB3 - (Drive))
#define VT6421A_UDMA_SLOW 0x0F
#define VT6421A_FIFO_WATERMARK_64DW 0x04
PCIIDEX_PAGED_DATA
static const struct
{
USHORT DeviceID;
USHORT Flags;
} ViaControllerList[] =
{
{ PCI_DEV_VT82C576M_IDE, TYPE_MWDMA },
{ PCI_DEV_VT82C586A_IDE, HW_FLAGS_CHECK_BRIDGE },
{ PCI_DEV_VT6410_IDE_RAID, TYPE_133 },
{ PCI_DEV_VT6415_IDE, TYPE_133 | HW_FLAGS_SINGLE_CHAN },
{ PCI_DEV_VT6420_IDE, TYPE_133 | HW_FLAGS_SINGLE_CHAN },
{ PCI_DEV_VX855_IDE, TYPE_133 | HW_FLAGS_SINGLE_CHAN },
{ PCI_DEV_VT8237_SATA, TYPE_SATA | HW_FLAGS_SINGLE_PORT },
{ PCI_DEV_VT8237A_SATA, TYPE_SATA | HW_FLAGS_SINGLE_PORT },
{ PCI_DEV_VT8237A_SATA_2, TYPE_SATA | HW_FLAGS_SINGLE_PORT },
{ PCI_DEV_VT8237S_SATA, TYPE_SATA | HW_FLAGS_SINGLE_PORT },
{ PCI_DEV_VT8237S_SATA_RAID, TYPE_SATA | HW_FLAGS_SINGLE_PORT | HW_FLAGS_PCI_SCR },
{ PCI_DEV_VT8251_SATA_AHCI, TYPE_SATA | HW_FLAGS_PCI_SCR },
{ PCI_DEV_VT8251_SATA_2, TYPE_SATA | HW_FLAGS_PCI_SCR },
{ PCI_DEV_VT8261_SATA, TYPE_SATA | HW_FLAGS_PCI_SCR },
{ PCI_DEV_VT8261_SATA_RAID, TYPE_SATA | HW_FLAGS_PCI_SCR },
{ PCI_DEV_VX900_SATA, TYPE_SATA | HW_FLAGS_PCI_SCR },
{ PCI_DEV_VX900_SATA_RAID, TYPE_SATA | HW_FLAGS_PCI_SCR },
};
static const struct
{
UCHAR DefaultValue;
UCHAR Data[7];
} ViaUdmaTimings[] =
{
// UDMA 0 1 2 3 4 5 6
{ 0x03, { 0xE2, 0xE1, 0xE0 } }, // TYPE_33
{ 0x03, { 0xE6, 0xE4, 0xE2, 0xE1, 0xE0 } }, // TYPE_66
{ 0x07, { 0xEA, 0xE6, 0xE4, 0xE2, 0xE1, 0xE0 } }, // TYPE_100
{ 0x07, { 0xEE, 0xE8, 0xE6, 0xE4, 0xE2, 0xE1, 0xE0 } }, // TYPE_133
};
PCIIDEX_PAGED_DATA
static const ATA_PCI_ENABLE_BITS ViaPataEnableBits[MAX_IDE_CHANNEL] =
{
{ 0x40, 0x02, 0x02 },
{ 0x40, 0x01, 0x01 },
};
PCIIDEX_PAGED_DATA
static const ATA_PCI_ENABLE_BITS ViaCx700EnableBits[MAX_IDE_CHANNEL] =
{
{ 0x40, 0x02, 0x02 },
{ 0xC0, 0x01, 0x01 },
};
/* FUNCTIONS ******************************************************************/
VOID
ViaClampTimings(
_Inout_ PATA_TIMING Timing)
{
Timing->AddressSetup = CLAMP_TIMING(Timing->AddressSetup, 1, 4) - 1;
Timing->CmdRecovery = CLAMP_TIMING(Timing->CmdRecovery, 1, 16) - 1;
Timing->CmdActive = CLAMP_TIMING(Timing->CmdActive, 1, 16) - 1;
Timing->DataRecovery = CLAMP_TIMING(Timing->DataRecovery, 1, 16) - 1;
Timing->DataActive = CLAMP_TIMING(Timing->DataActive, 1, 16) - 1;
}
static
VOID
ViaSetTransferMode(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel];
ATA_TIMING DeviceTimings[MAX_IDE_DEVICE];
ULONG i;
ULONG DriveTimReg, UdmaTimReg;
UCHAR PortTimReg;
AtaSelectTimings(DeviceList, DeviceTimings, VIA_PCI_CLOCK, SHARED_CMD_TIMINGS);
INFO("CH %lu: Config (before)\n"
"DRV %08lX\n"
"PORT %08lX\n"
"UDMA %08lX\n"
"CFG %08lX\n",
Channel,
PciRead32(Controller, 0x48),
PciRead32(Controller, 0x4C),
PciRead32(Controller, 0x50),
PciRead32(Controller, 0x40));
DriveTimReg = PciRead32(Controller, VIA_REG_DRIVE_TIMING);
PortTimReg = PciRead8(Controller, VIA_REG_PORT_TIMING(Channel));
if (GET_TYPE(ChanData->HwFlags) != TYPE_MWDMA)
UdmaTimReg = PciRead32(Controller, VIA_REG_UDMA_CTRL);
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
PATA_TIMING Timing = &DeviceTimings[i];
const ULONG DeviceIndex = (Channel << 1) + i; // 0 1 2 3
const ULONG Shift = (3 - DeviceIndex) << 3; // 24 16 8 0
ULONG Value;
/* UDMA timings */
if (GET_TYPE(ChanData->HwFlags) != TYPE_MWDMA)
{
if (Device && (Device->DmaMode >= UDMA_MODE(0)))
{
ULONG ModeIndex = Device->DmaMode - UDMA_MODE(0);
Value = ViaUdmaTimings[GET_TYPE(ChanData->HwFlags)].Data[ModeIndex];
}
else
{
Value = ViaUdmaTimings[GET_TYPE(ChanData->HwFlags)].DefaultValue;
}
/* Turn on the 66MHz UDMA clock */
if ((i != 0) && (GET_TYPE(ChanData->HwFlags) == TYPE_66))
Value |= VIA_UDMA_CLOCK_UDMA66;
UdmaTimReg &= ~(VIA_UDMA_CLEAR_MASK << Shift);
UdmaTimReg |= Value << Shift;
}
if (!Device)
continue;
/* PIO and DMA timings */
ViaClampTimings(Timing);
Value = (Timing->DataActive << 4) | Timing->DataRecovery;
DriveTimReg &= ~(0xFF << Shift);
DriveTimReg |= Value << Shift;
PortTimReg = (Timing->CmdActive << 4) | Timing->CmdRecovery;
}
PciWrite8(Controller, VIA_REG_PORT_TIMING(Channel), PortTimReg);
PciWrite32(Controller, VIA_REG_DRIVE_TIMING, DriveTimReg);
if (GET_TYPE(ChanData->HwFlags) != TYPE_MWDMA)
PciWrite32(Controller, VIA_REG_UDMA_CTRL, UdmaTimReg);
INFO("CH %lu: Config (after)\n"
"DRV %08lX\n"
"PORT %08lX\n"
"UDMA %08lX\n"
"CFG %08lX\n",
Channel,
PciRead32(Controller, 0x48),
PciRead32(Controller, 0x4C),
PciRead32(Controller, 0x50),
PciRead32(Controller, 0x40));
}
static
VOID
ViaSataSetTransferMode(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList)
{
UCHAR SataControl = PciRead8(Controller, VT6421A_REG_SATA_CTRL);
ULONG i;
if (!(SataControl & VT6421A_FIFO_WATERMARK_64DW))
{
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
if (!Device)
continue;
/* Fix freezes with Western Digital drives. See CORE-5897 for more details */
if (_strnicmp(Device->FriendlyName, "WD", 2) == 0)
{
/* This will slow down I/O performance */
WARN("Enabling workaround for the '%s' drive\n", Device->FriendlyName);
SataControl |= VT6421A_FIFO_WATERMARK_64DW;
PciWrite8(Controller, VT6421A_REG_SATA_CTRL, SataControl);
break;
}
}
}
SataSetTransferMode(Controller, Channel, DeviceList);
}
static
VOID
Via6421SetTransferMode(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList)
{
ATA_TIMING DeviceTimings[MAX_IDE_DEVICE];
ULONG i;
UNREFERENCED_PARAMETER(Channel);
AtaSelectTimings(DeviceList, DeviceTimings, VIA_PCI_CLOCK, SHARED_CMD_TIMINGS);
for (i = 0; i < MAX_IDE_DEVICE; ++i)
{
PCHANNEL_DEVICE_CONFIG Device = DeviceList[i];
PATA_TIMING Timing = &DeviceTimings[i];
UCHAR UdmaTimReg;
/* UDMA timings */
UdmaTimReg = PciRead8(Controller, VT6421A_REG_UDMA_CTRL(i));
UdmaTimReg &= VIA_UDMA_CABLE_BITS;
if (Device && (Device->DmaMode >= UDMA_MODE(0)))
UdmaTimReg |= ViaUdmaTimings[TYPE_133].Data[Device->DmaMode - UDMA_MODE(0)];
else
UdmaTimReg |= VT6421A_UDMA_SLOW;
PciWrite8(Controller, VT6421A_REG_UDMA_CTRL(i), UdmaTimReg);
if (!Device)
continue;
ViaClampTimings(Timing);
/* PIO timings */
PciWrite8(Controller,
VT6421A_REG_TIMING_CTRL(i),
(Timing->DataActive << 4) | Timing->DataRecovery);
}
}
static
ULONG
ViaGetSerrOffset(
_In_ PATA_CONTROLLER Controller)
{
if ((Controller->Pci.DeviceID == PCI_DEV_VT8237S_SATA_RAID))
return VIA_REG_SERROR_VT8237;
return VIA_REG_SERROR;
}
static
BOOLEAN
ViaScrReadPci(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ SATA_SCR_REGISTER Register,
_In_ ULONG PortNumber,
_In_ PULONG Result)
{
PATA_CONTROLLER Controller = ChanData->Controller;
ULONG Value, Offset = 2 * ChanData->Channel + PortNumber;
UCHAR Reg;
switch (Register)
{
case ATA_SSTATUS:
{
Reg = PciRead8(Controller, VIA_REG_SSTATUS + Offset);
Value = Reg & 0x03;
if (Reg & 0x04)
Value |= AHCI_PXSSTS_IPM_PARTIAL;
else if (Reg & 0x08)
Value |= AHCI_PXSSTS_IPM_SLUMBER;
else
Value |= AHCI_PXSSTS_IPM_ACTIVE;
if (Reg & 0x10)
Value |= AHCI_PXSSTS_SPD_SATA2;
else
Value |= AHCI_PXSSTS_SPD_SATA1;
break;
}
case ATA_SERROR:
{
Value = PciRead32(Controller, ViaGetSerrOffset(Controller) + Offset * 4);
break;
}
case ATA_SCONTROL:
{
Value = 0;
Reg = PciRead8(Controller, VIA_REG_SCONTROL + Offset);
if (Reg & 0x01)
Value |= AHCI_PXCTL_DET_RESET;
if (Reg & 0x02)
Value |= AHCI_PXCTL_DET_DISABLE_SATA;
if (Reg & 0x04)
Value |= AHCI_PXCTL_IPM_DISABLE_PARTIAL;
if (Reg & 0x08)
Value |= AHCI_PXCTL_IPM_DISABLE_SLUMBER;
break;
}
default:
return FALSE;
}
*Result = Value;
return TRUE;
}
static
BOOLEAN
ViaScrWritePci(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ SATA_SCR_REGISTER Register,
_In_ ULONG PortNumber,
_In_ ULONG Value)
{
PATA_CONTROLLER Controller = ChanData->Controller;
ULONG Offset = 2 * ChanData->Channel + PortNumber;
switch (Register)
{
case ATA_SERROR:
{
PciWrite32(Controller, ViaGetSerrOffset(Controller) + Offset * 4, Value);
break;
}
case ATA_SCONTROL:
{
UCHAR Val = 0;
if (Value & AHCI_PXCTL_DET_RESET)
Val |= 0x01;
if (Value & AHCI_PXCTL_DET_DISABLE_SATA)
Val |= 0x02;
if (Value & AHCI_PXCTL_IPM_DISABLE_PARTIAL)
Val |= 0x04;
if (Value & AHCI_PXCTL_IPM_DISABLE_SLUMBER)
Val |= 0x08;
PciWrite8(Controller, VIA_REG_SCONTROL + Offset, Val);
break;
}
default:
return FALSE;
}
return TRUE;
}
static
BOOLEAN
ViaScrReadIoPort(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ SATA_SCR_REGISTER Register,
_In_ ULONG PortNumber,
_In_ PULONG Result)
{
UNREFERENCED_PARAMETER(PortNumber);
if (Register > ATA_SCONTROL)
return FALSE;
*Result = READ_PORT_ULONG((PULONG)(ChanData->Regs.Scr + (Register * 4)));
return TRUE;
}
static
BOOLEAN
ViaScrWriteIoPort(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ SATA_SCR_REGISTER Register,
_In_ ULONG PortNumber,
_In_ ULONG Value)
{
UNREFERENCED_PARAMETER(PortNumber);
if (Register > ATA_SCONTROL)
return FALSE;
WRITE_PORT_ULONG((PULONG)(ChanData->Regs.Scr + (Register * 4)), Value);
return TRUE;
}
static
CODE_SEG("PAGE")
NTSTATUS
Via6421ParseResources(
_Inout_ PCHANNEL_DATA_PATA ChanData,
_In_ ULONG PortIndex)
{
PUCHAR CommandPortBase, ControlPortBase, DmaBase, ScrBase;
PAGED_CODE();
/*
* [BAR 0]: I/O ports at E150 [size=16] Channel 0
* [BAR 1]: I/O ports at E140 [size=16] Channel 1
* [BAR 2]: I/O ports at E130 [size=16] Channel 2
* [BAR 3]: I/O ports at E120 [size=16]
* [BAR 4]: I/O ports at E100 [size=32] BM DMA
* [BAR 5]: I/O ports at E000 [size=256] SATA PHY (minimum size = 128)
*/
CommandPortBase = AtaCtrlPciMapBar(ChanData->Controller, PortIndex, 16);
if (!CommandPortBase)
return STATUS_DEVICE_CONFIGURATION_ERROR;
DmaBase = AtaCtrlPciMapBar(ChanData->Controller, 4, 32);
if (!DmaBase)
return STATUS_DEVICE_CONFIGURATION_ERROR;
ScrBase = AtaCtrlPciMapBar(ChanData->Controller, 5, 128);
if (!ScrBase)
return STATUS_DEVICE_CONFIGURATION_ERROR;
ChanData->Regs.Dma = DmaBase + (PortIndex * 8);
ChanData->Regs.Scr = ScrBase + (PortIndex * 64);
ControlPortBase = CommandPortBase + PCIIDE_COMMAND_IO_RANGE_LENGTH;
PciIdeInitTaskFileIoResources(ChanData,
(ULONG_PTR)CommandPortBase,
(ULONG_PTR)ControlPortBase + PCIIDE_CONTROL_IO_BAR_OFFSET,
1);
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
Via6421GetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i;
PAGED_CODE();
/*
* Channel 0: SATA port #0 (M only)
* Channel 1: SATA port #1 (M only)
* Channel 2: PATA channel
*/
Controller->MaxChannels = 3;
Controller->Flags |= CTRL_FLAG_MANUAL_RES;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
if (i == 2)
{
/* No MWDMA support */
ChanData->TransferModeSupported = PIO_ALL | UDMA_ALL;
ChanData->SetTransferMode = Via6421SetTransferMode;
if (!VT6421A_UDMA_CABLE_PRESENT(Controller))
{
INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
}
else
{
ChanData->ChanInfo |= CHANNEL_FLAG_NO_SLAVE;
ChanData->SetTransferMode = ViaSataSetTransferMode;
ChanData->TransferModeSupported = SATA_ALL;
ChanData->ScrRead = ViaScrReadIoPort;
ChanData->ScrWrite = ViaScrWriteIoPort;
}
Status = Via6421ParseResources(ChanData, i);
if (!NT_SUCCESS(Status))
return Status;
}
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
ViaCx700GetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i;
PAGED_CODE();
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
Controller->ChannelEnableBits = ViaCx700EnableBits;
/*
* Channel 0: 2 SATA ports (M/S emulation)
* Channel 1: PATA channel
*/
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
if (i == 0)
{
ChanData->SetTransferMode = SataSetTransferMode;
ChanData->TransferModeSupported = SATA_ALL;
ChanData->ScrRead = ViaScrReadPci;
ChanData->ScrWrite = ViaScrWritePci;
}
else
{
ChanData->HwFlags |= TYPE_133;
ChanData->SetTransferMode = ViaSetTransferMode;
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_ALL;
if (!VIA_SINGLE_CHAN_UDMA_CABLE_PRESENT(Controller))
{
INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
}
}
return STATUS_SUCCESS;
}
static
VOID
Via6410ControllerStart(
_In_ PATA_CONTROLLER Controller)
{
UCHAR Value;
/*
* Enable the primary and secondary IDE channels.
*
* The VT6410 can be either a controller soldered onto the motherboard
* or added on an external PCI card. These PCI cards usually come
* without an option ROM on their own and have these bits set to 0s by default.
*/
Value = PciRead8(Controller, VIA_REG_ENABLE_CTRL);
Value |= 0x03;
PciWrite8(Controller, VIA_REG_ENABLE_CTRL, Value);
}
static
CODE_SEG("PAGE")
BOOLEAN
ViaQuerySouthBridgeInformation(
_In_ PVOID Context,
_In_ ULONG BusNumber,
_In_ PCI_SLOT_NUMBER PciSlot,
_In_ PPCI_COMMON_HEADER PciConfig)
{
PULONG HwFlags = Context;
ULONG i;
PCIIDEX_PAGED_DATA
static const struct
{
USHORT DeviceID;
UCHAR MinimumRevisionID;
UCHAR Flags;
} ViaBridgeList[] =
{
{ PCI_DEV_BRIDGE_VT82C586, 0x20, TYPE_33 },
{ PCI_DEV_BRIDGE_VT82C586, 0, TYPE_MWDMA },
{ PCI_DEV_BRIDGE_VT82C596A, 0x10, TYPE_66 | HW_FLAGS_HAS_UDMA_CLOCK },
{ PCI_DEV_BRIDGE_VT82C596A, 0, TYPE_33 },
{ PCI_DEV_BRIDGE_VT82C686A, 0x40, TYPE_100 },
{ PCI_DEV_BRIDGE_VT82C686A, 0, TYPE_66 | HW_FLAGS_HAS_UDMA_CLOCK },
{ PCI_DEV_BRIDGE_VT8231, 0, TYPE_100 },
{ PCI_DEV_BRIDGE_VT8233, 0, TYPE_100 },
{ PCI_DEV_BRIDGE_VT8233A, 0, TYPE_133 },
{ PCI_DEV_BRIDGE_VT8233C, 0, TYPE_100 },
{ PCI_DEV_BRIDGE_VT8235, 0, TYPE_133 },
{ PCI_DEV_BRIDGE_VT8237, 0, TYPE_133 },
{ PCI_DEV_BRIDGE_VT8237A, 0, TYPE_133 },
{ PCI_DEV_BRIDGE_VT8237S, 0, TYPE_133 },
{ PCI_DEV_BRIDGE_VT8251, 0, TYPE_133 },
{ PCI_DEV_BRIDGE_VT8261, 0, TYPE_133 },
};
UNREFERENCED_PARAMETER(BusNumber);
UNREFERENCED_PARAMETER(PciSlot);
PAGED_CODE();
if (PciConfig->VendorID != PCI_VEN_VIA)
return FALSE;
for (i = 0; i < RTL_NUMBER_OF(ViaBridgeList); ++i)
{
if ((PciConfig->DeviceID == ViaBridgeList[i].DeviceID) &&
(PciConfig->RevisionID >= ViaBridgeList[i].MinimumRevisionID))
{
INFO("Found %04X:%04X.%02X VIA bridge\n",
PciConfig->VendorID, PciConfig->DeviceID, PciConfig->RevisionID);
*HwFlags = ViaBridgeList[i].Flags;
return TRUE;
}
}
return FALSE;
}
static
CODE_SEG("PAGE")
NTSTATUS
ViaPataGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller,
_In_ ULONG HwFlags)
{
ULONG i, UdmaTimReg;
PAGED_CODE();
/*
* Look for a PCI-ISA bridge to see what features are available.
* Early VIA PATA controllers share the same PCI device ID
* but have very different capabilities.
*/
if (HwFlags & HW_FLAGS_CHECK_BRIDGE)
{
if (!PciFindDevice(ViaQuerySouthBridgeInformation, &HwFlags))
{
ERR("Unable to find the VIA bridge\n");
ASSERT(FALSE);
return STATUS_NO_MATCH;
}
}
if (Controller->Pci.DeviceID == PCI_DEV_VT6410_IDE_RAID)
Controller->Start = Via6410ControllerStart;
/* The VT6415 has no channel enable bits */
if (Controller->Pci.DeviceID != PCI_DEV_VT6415_IDE)
Controller->ChannelEnableBits = ViaPataEnableBits;
if (GET_TYPE(HwFlags) != TYPE_MWDMA)
UdmaTimReg = PciRead32(Controller, VIA_REG_UDMA_CTRL);
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
ChanData->HwFlags = HwFlags;
ChanData->SetTransferMode = ViaSetTransferMode;
switch (GET_TYPE(ChanData->HwFlags))
{
case TYPE_MWDMA:
{
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL;
break;
}
case TYPE_33:
{
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 2);
break;
}
case TYPE_66:
{
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 4);
/* Check to see if the BIOS has enabled the 66MHz UDMA clock for us */
if (!VIA_UDMA_CLOCK_ENABLED(UdmaTimReg, i))
{
/* It seems that the clock can also be disabled due to hardware errata */
INFO("CH %lu: BIOS detected 40-conductor cable "
"or this chip is ATA/33 capable\n", ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
/* Use timing table based on a 33MHz UDMA clock */
ChanData->HwFlags &= ~HW_FLAGS_TYPE_MASK;
ChanData->HwFlags |= TYPE_33;
}
break;
}
case TYPE_100:
{
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5);
if (!VIA_UDMA_CABLE_PRESENT(UdmaTimReg, i))
{
INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
break;
}
case TYPE_133:
{
ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 6);
if (!VIA_UDMA_CABLE_PRESENT(UdmaTimReg, i))
{
INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel);
ChanData->TransferModeSupported &= ~UDMA_80C_ALL;
}
break;
}
default:
ASSERT(FALSE);
UNREACHABLE;
}
}
return STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NTSTATUS
ViaSataGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller,
_In_ ULONG HwFlags)
{
ULONG i;
PAGED_CODE();
INFO("Port map: %02X\n", PciRead8(Controller, VIA_REG_SATA_PORT_MAP));
for (i = 0; i < Controller->MaxChannels; ++i)
{
PCHANNEL_DATA_PATA ChanData = Controller->Channels[i];
ChanData->HwFlags = HwFlags;
ChanData->TransferModeSupported = SATA_ALL;
ChanData->SetTransferMode = SataSetTransferMode;
if (HwFlags & HW_FLAGS_SINGLE_PORT)
ChanData->ChanInfo |= CHANNEL_FLAG_NO_SLAVE;
if (HwFlags & HW_FLAGS_PCI_SCR)
{
ChanData->ScrRead = ViaScrReadPci;
ChanData->ScrWrite = ViaScrWritePci;
}
}
return STATUS_SUCCESS;
}
CODE_SEG("PAGE")
NTSTATUS
ViaGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller)
{
NTSTATUS Status;
ULONG i, HwFlags;
PAGED_CODE();
ASSERT(Controller->Pci.VendorID == PCI_VEN_VIA);
if (Controller->Pci.DeviceID == PCI_DEV_VT6421A_RAID)
{
return Via6421GetControllerProperties(Controller);
}
else if ((Controller->Pci.DeviceID == PCI_DEV_CX700M2_IDE_SATA) ||
(Controller->Pci.DeviceID == PCI_DEV_CX700M2_IDE_SATA_RAID))
{
return ViaCx700GetControllerProperties(Controller);
}
for (i = 0; i < RTL_NUMBER_OF(ViaControllerList); ++i)
{
HwFlags = ViaControllerList[i].Flags;
if (Controller->Pci.DeviceID == ViaControllerList[i].DeviceID)
break;
}
if (i == RTL_NUMBER_OF(ViaControllerList))
return STATUS_NO_MATCH;
if (HwFlags & HW_FLAGS_SINGLE_CHAN)
Controller->MaxChannels = 1;
Status = PciIdeCreateChannelData(Controller, 0);
if (!NT_SUCCESS(Status))
return Status;
if (GET_TYPE(HwFlags) == TYPE_SATA)
return ViaSataGetControllerProperties(Controller, HwFlags);
return ViaPataGetControllerProperties(Controller, HwFlags);
}

View File

@@ -0,0 +1,95 @@
/*
* PROJECT: ReactOS ATA Bus 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

File diff suppressed because it is too large Load Diff

View File

@@ -7,42 +7,9 @@
#include "pciidex.h"
#define NDEBUG
#include <debug.h>
/** @brief Global debugging level. Valid values are between 0 (Error) and 3 (Trace). */
ULONG PciIdeDebug = 0;
CODE_SEG("PAGE")
NTSTATUS
PciIdeXStartMiniport(
_In_ PFDO_DEVICE_EXTENSION FdoExtension)
{
PPCIIDEX_DRIVER_EXTENSION DriverExtension;
NTSTATUS Status;
PAGED_CODE();
if (FdoExtension->MiniportStarted)
return STATUS_SUCCESS;
DPRINT("Starting miniport\n");
DriverExtension = IoGetDriverObjectExtension(FdoExtension->DriverObject,
FdoExtension->DriverObject);
ASSERT(DriverExtension);
FdoExtension->Properties.Size = sizeof(IDE_CONTROLLER_PROPERTIES);
FdoExtension->Properties.ExtensionSize = DriverExtension->MiniControllerExtensionSize;
Status = DriverExtension->HwGetControllerProperties(FdoExtension->MiniControllerExtension,
&FdoExtension->Properties);
if (!NT_SUCCESS(Status))
return Status;
FdoExtension->MiniportStarted = TRUE;
return STATUS_SUCCESS;
}
CODE_SEG("PAGE")
IDE_CHANNEL_STATE
PciIdeXChannelState(
@@ -95,21 +62,21 @@ PciIdeXGetBusData(
_In_ ULONG ConfigDataOffset,
_In_ ULONG BufferLength)
{
PFDO_DEVICE_EXTENSION FdoExtension;
PFDO_DEVICE_EXTENSION FdoExt;
PATA_CONTROLLER Controller;
ULONG BytesRead;
DPRINT("PciIdeXGetBusData(%p %p 0x%lx 0x%lx)\n",
DeviceExtension, Buffer, ConfigDataOffset, BufferLength);
INFO("PciIdeXGetBusData(%p %p 0x%lx 0x%lx)\n",
DeviceExtension, Buffer, ConfigDataOffset, BufferLength);
FdoExtension = CONTAINING_RECORD(DeviceExtension,
FDO_DEVICE_EXTENSION,
MiniControllerExtension);
FdoExt = CONTAINING_RECORD(DeviceExtension, FDO_DEVICE_EXTENSION, MiniControllerExtension);
Controller = &FdoExt->Controller;
BytesRead = (*FdoExtension->BusInterface.GetBusData)(FdoExtension->BusInterface.Context,
PCI_WHICHSPACE_CONFIG,
Buffer,
ConfigDataOffset,
BufferLength);
BytesRead = Controller->GetBusData(Controller->BusInterfaceContext,
PCI_WHICHSPACE_CONFIG,
Buffer,
ConfigDataOffset,
BufferLength);
if (BytesRead != BufferLength)
return STATUS_UNSUCCESSFUL;
@@ -126,24 +93,38 @@ PciIdeXSetBusData(
_In_ ULONG ConfigDataOffset,
_In_ ULONG BufferLength)
{
PFDO_DEVICE_EXTENSION FdoExtension;
PFDO_DEVICE_EXTENSION FdoExt;
PATA_CONTROLLER Controller;
UCHAR LocalBuffer[4];
ULONG i, BytesWritten;
PUCHAR CurrentBuffer;
KIRQL OldIrql;
NTSTATUS Status;
DPRINT("PciIdeXSetBusData(%p %p %p 0x%lx 0x%lx)\n",
DeviceExtension, Buffer, DataMask, ConfigDataOffset, BufferLength);
INFO("PciIdeXSetBusData(%p %p %p 0x%lx 0x%lx)\n",
DeviceExtension, Buffer, DataMask, ConfigDataOffset, BufferLength);
CurrentBuffer = ExAllocatePoolWithTag(NonPagedPool, BufferLength, TAG_PCIIDEX);
if (!CurrentBuffer)
return STATUS_INSUFFICIENT_RESOURCES;
/* Optimize the common case (4-byte PCI access) */
if (BufferLength <= sizeof(LocalBuffer))
{
CurrentBuffer = LocalBuffer;
}
else
{
CurrentBuffer = ExAllocatePoolUninitialized(NonPagedPool, BufferLength, TAG_PCIIDEX);
if (!CurrentBuffer)
return STATUS_INSUFFICIENT_RESOURCES;
}
FdoExtension = CONTAINING_RECORD(DeviceExtension,
FDO_DEVICE_EXTENSION,
MiniControllerExtension);
FdoExt = CONTAINING_RECORD(DeviceExtension, FDO_DEVICE_EXTENSION, MiniControllerExtension);
Controller = &FdoExt->Controller;
KeAcquireSpinLock(&FdoExtension->BusDataLock, &OldIrql);
/*
* This spinlock protects the PCI configuration space against concurrent modifications.
* For example, the PIIX register SIDETIM (0x44) is a byte-sized register
* and controls both of the IDE channels.
*/
KeAcquireSpinLock(&Controller->Lock, &OldIrql);
Status = PciIdeXGetBusData(DeviceExtension, Buffer, ConfigDataOffset, BufferLength);
if (!NT_SUCCESS(Status))
@@ -155,19 +136,40 @@ PciIdeXSetBusData(
(((PUCHAR)DataMask)[i] & ((PUCHAR)Buffer)[i]);
}
BytesWritten = (*FdoExtension->BusInterface.SetBusData)(FdoExtension->BusInterface.Context,
PCI_WHICHSPACE_CONFIG,
CurrentBuffer,
ConfigDataOffset,
BufferLength);
BytesWritten = Controller->SetBusData(Controller->BusInterfaceContext,
PCI_WHICHSPACE_CONFIG,
CurrentBuffer,
ConfigDataOffset,
BufferLength);
if (BytesWritten != BufferLength)
Status = STATUS_UNSUCCESSFUL;
else
Status = STATUS_SUCCESS;
Cleanup:
KeReleaseSpinLock(&FdoExtension->BusDataLock, OldIrql);
KeReleaseSpinLock(&Controller->Lock, OldIrql);
ExFreePoolWithTag(CurrentBuffer, TAG_PCIIDEX);
if (CurrentBuffer != LocalBuffer)
ExFreePoolWithTag(CurrentBuffer, TAG_PCIIDEX);
return Status;
}
NTSTATUS
NTAPI
PciIdexGetBusLocation(
_In_ PVOID DeviceExtension,
_Out_ PULONG BusLocation)
{
PFDO_DEVICE_EXTENSION FdoExt;
ULONG Length;
NTSTATUS Status;
FdoExt = CONTAINING_RECORD(DeviceExtension, FDO_DEVICE_EXTENSION, MiniControllerExtension);
Status = IoGetDeviceProperty(FdoExt->Pdo,
DevicePropertyAddress,
sizeof(*BusLocation),
BusLocation,
&Length);
return Status;
}

View File

@@ -0,0 +1,323 @@
/*
* PROJECT: ReactOS ATA Bus Driver
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: PATA definitions
* COPYRIGHT: Copyright 2026 <di.sean@protonmail.com>
*/
#pragma once
#define PCI_VEN_ATI 0x1002
#define PCI_VEN_AMD 0x1022
#define PCI_VEN_NVIDIA 0x10DE
#define PCI_VEN_PC_TECH 0x1042
#define PCI_VEN_CMD 0x1095
#define PCI_VEN_VIA 0x1106
#define PCI_VEN_SERVERWORKS 0x1166
#define PCI_VEN_TOSHIBA 0x1179
#define PCI_VEN_CAVIUM 0x177D
#define PCI_VEN_INTEL 0x8086
/** Master/Slave devices */
#define CHANNEL_PCAT_MAX_DEVICES 2
/** Master/Slave devices for Bank 0 and Bank 1 */
#define CHANNEL_PC98_MAX_DEVICES 4
/** PC-98 ATA bank register */
#define PC98_ATA_BANK 0x432
#define PC98_ATA_BANK_32BIT_PORT 0x08
/** Delay of 400ns */
#define ATA_IO_WAIT() KeStallExecutionProcessor(1)
#define NUM_TO_BITMAP(num) (0xFFFFFFFF >> (RTL_BITS_OF(ULONG) - (num)))
/** Supported transfer modes by SATA devices */
#define SATA_ALL \
(PIO_ALL | MWDMA_ALL | UDMA_ALL)
/** UDMA modes that require the presence of an 80-conductor ATA cable */
#define UDMA_80C_ALL \
(UDMA_MODE3 | UDMA_MODE4 | UDMA_MODE5 | UDMA_MODE6)
/** Used to specify a range of MWDMA modes */
#define MWDMA_MODES(MinMode, MaxMode) \
(NUM_TO_BITMAP(MWDMA_MODE((MaxMode) + 1)) & ~NUM_TO_BITMAP(MWDMA_MODE(MinMode)))
/** Used to specify a range of UDMA modes */
#define UDMA_MODES(MinMode, MaxMode) \
(NUM_TO_BITMAP(UDMA_MODE((MaxMode) + 1)) & ~NUM_TO_BITMAP(UDMA_MODE(MinMode)))
#define IDE_DC_ALWAYS 0x08
#define IDE_DRIVE_SELECT 0xA0
#define IDE_HIGH_ORDER_BYTE 0x80
#define IDE_FEATURE_PIO 0x00
#define IDE_FEATURE_DMA 0x01
#define IDE_FEATURE_DMADIR 0x04
/**
* If a larger transfer is attempted, the 16-bit ByteCount register might overflow.
* In this case we round down the length to the closest multiple of 2.
*/
#define ATAPI_MAX_DRQ_DATA_BLOCK 0xFFFE
/**
* Legacy ranges and interrupts
*/
/*@{*/
#define PCIIDE_LEGACY_RESOURCE_COUNT 3
#define PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH 8
#define PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH 1
#define PCIIDE_LEGACY_PRIMARY_COMMAND_BASE 0x1F0
#define PCIIDE_LEGACY_PRIMARY_CONTROL_BASE 0x3F6
#define PCIIDE_LEGACY_PRIMARY_IRQ 14
#define PCIIDE_LEGACY_SECONDARY_COMMAND_BASE 0x170
#define PCIIDE_LEGACY_SECONDARY_CONTROL_BASE 0x376
#define PCIIDE_LEGACY_SECONDARY_IRQ 15
/*@}*/
#define PCIIDE_COMMAND_IO_RANGE_LENGTH 8
#define PCIIDE_CONTROL_IO_RANGE_LENGTH 4
#define PCIIDE_CONTROL_IO_BAR_OFFSET 2
#define PCIIDE_DMA_IO_BAR 4
#define PCIIDE_DMA_IO_RANGE_LENGTH 16
/** Offset from base address */
#define PCIIDE_DMA_SECONDARY_CHANNEL_OFFSET 8
/**
* Programming Interface Register
*/
/*@{*/
#define PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE 0x01
#define PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE_CAPABLE 0x02
#define PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE 0x04
#define PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE_CAPABLE 0x08
#define PCIIDE_PROGIF_DMA_CAPABLE 0x80
/*@}*/
/**
* IDE Bus Master I/O Registers
*/
/*@{*/
#define PCIIDE_DMA_COMMAND 0
#define PCIIDE_DMA_STATUS 2
#define PCIIDE_DMA_PRDT_PHYSICAL_ADDRESS 4
/*@}*/
/**
* IDE Bus Master Command Register
*/
/*@{*/
#define PCIIDE_DMA_COMMAND_STOP 0x00
#define PCIIDE_DMA_COMMAND_START 0x01
#define PCIIDE_DMA_COMMAND_READ_FROM_SYSTEM_MEMORY 0x00
#define PCIIDE_DMA_COMMAND_WRITE_TO_SYSTEM_MEMORY 0x08
/*@}*/
/**
* IDE Bus Master Status Register
*/
/*@{*/
#define PCIIDE_DMA_STATUS_ACTIVE 0x01
#define PCIIDE_DMA_STATUS_ERROR 0x02
#define PCIIDE_DMA_STATUS_INTERRUPT 0x04
#define PCIIDE_DMA_STATUS_RESERVED1 0x08
#define PCIIDE_DMA_STATUS_RESERVED2 0x10
#define PCIIDE_DMA_STATUS_DRIVE0_DMA_CAPABLE 0x20
#define PCIIDE_DMA_STATUS_DRIVE1_DMA_CAPABLE 0x40
#define PCIIDE_DMA_STATUS_SIMPLEX 0x80
/*@}*/
/**
* ATAPI Interrupt Status
*/
/*@{*/
/** Command or Data */
#define ATAPI_INT_REASON_COD 0x01
/** Read direction */
#define ATAPI_INT_REASON_IO 0x02
/** Bus release */
#define ATAPI_INT_REASON_RELEASE 0x04
/** Command Tag for the command */
#define ATAPI_INT_REASON_TAG 0xF8
#define ATAPI_INT_REASON_MASK (ATAPI_INT_REASON_IO | ATAPI_INT_REASON_COD)
/** Status - Register contains Completion Status (NEC CDR-260) */
#define ATAPI_INT_REASON_STATUS_NEC 0x00
/** Status - Register contains Completion Status */
#define ATAPI_INT_REASON_STATUS (ATAPI_INT_REASON_IO | ATAPI_INT_REASON_COD)
/** Data From Host - Receive command parameter data from the host */
#define ATAPI_INT_REASON_DATA_OUT IDE_STATUS_DRQ
/** CDB - Ready for Command Packet */
#define ATAPI_INT_REASON_AWAIT_CDB (IDE_STATUS_DRQ | ATAPI_INT_REASON_COD)
/** Data To Host - Send command parameter data to the host */
#define ATAPI_INT_REASON_DATA_IN (ATAPI_INT_REASON_IO | IDE_STATUS_DRQ)
/*@}*/
#define ATA_TIME_BUSY_SELECT 3000 ///< 30 ms
#define ATA_TIME_BUSY_NORMAL 50000 ///< 500 ms
#define ATA_TIME_BUSY_POLL 5 ///< 50 us
#define ATA_TIME_DRQ_CLEAR 1000 ///< 10 ms
#define ATA_TIME_PHASE_CHANGE 100 ///< 1 ms
/* We keep the value as small as possible, since large timeout can break our error handling */
#define ATA_TIME_DRQ_ASSERT 15 ///< 150 us
#define ATA_TIME_RESET_SELECT (2000 / PORT_TIMER_TICK_MS) ///< 2 s
#define ATA_TIME_BUSY_RESET (10000 / PORT_TIMER_TICK_MS) ///< 10 s
#define CMD_FLAG_NONE 0x00000000
#define CMD_FLAG_TRANSFER_MASK 0x00000003
#define CMD_FLAG_AWAIT_CDB 0x00000004
#define CMD_FLAG_DATA_IN 0x00000040
#define CMD_FLAG_DATA_OUT 0x00000080
#define CMD_FLAG_AWAIT_INTERRUPT 0x80000000
#define CMD_FLAG_ATAPI_PIO_TRANSFER 0x00000001
#define CMD_FLAG_ATA_PIO_TRANSFER 0x00000002
#define CMD_FLAG_DMA_TRANSFER 0x00000003
/** The IDE interface has one slot per channel */
#define PATA_CHANNEL_SLOT 0
#define PATA_CHANNEL_QUEUE_DEPTH 1
C_ASSERT(CMD_FLAG_DMA_TRANSFER == (CMD_FLAG_ATAPI_PIO_TRANSFER | CMD_FLAG_ATA_PIO_TRANSFER));
C_ASSERT(CMD_FLAG_DATA_IN == REQUEST_FLAG_DATA_IN);
C_ASSERT(CMD_FLAG_DATA_OUT == REQUEST_FLAG_DATA_OUT);
C_ASSERT(CMD_FLAG_AWAIT_INTERRUPT == REQUEST_FLAG_POLL);
#include <pshpack1.h>
/**
* Physical Region Descriptor Table Entry
*/
typedef struct _PCIIDE_PRD_TABLE_ENTRY
{
ULONG Address;
ULONG Length; //!< 0 means 0x10000 bytes
#define PCIIDE_PRD_LENGTH_MASK 0xFFFF
#define PCIIDE_PRD_END_OF_TABLE 0x80000000
} PCIIDE_PRD_TABLE_ENTRY, *PPCIIDE_PRD_TABLE_ENTRY;
#include <poppack.h>
/** 64 kB boundary */
#define PCIIDE_PRD_LIMIT 0x10000
typedef USHORT ATATIM;
typedef struct _ATA_TIMING
{
ATATIM AddressSetup; // t1
/* Register transfers (8-bit) */
ATATIM CmdActive; // t2
ATATIM CmdRecovery; // t2i
/* PIO data transfers (16-bit) */
ATATIM DataActive; // t2
ATATIM DataRecovery; // t2i
} ATA_TIMING, *PATA_TIMING;
#define SHARED_CMD_TIMINGS 0x00000001
#define SHARED_DATA_TIMINGS 0x00000002
#define SHARED_ADDR_TIMINGS 0x00000004
FORCEINLINE
ATATIM
CLAMP_TIMING(
_In_ ATATIM Value,
_In_ ATATIM Minimum,
_In_ ATATIM Maximum)
{
if (Value < Minimum)
return Minimum;
if (Value > Maximum)
return Maximum;
return Value;
}
#define ATA_READ_BLOCK_16(Port, Buffer, Count, Ctx, MmioFlag) \
AtaReadBlock16(Port, Buffer, Count, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag)
#define ATA_WRITE_BLOCK_16(Port, Buffer, Count, Ctx, MmioFlag) \
AtaWriteBlock16(Port, Buffer, Count, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag)
#define ATA_READ_BLOCK_32(Port, Buffer, Count, Ctx, MmioFlag) \
AtaReadBlock32(Port, Buffer, Count, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag)
#define ATA_WRITE_BLOCK_32(Port, Buffer, Count, Ctx, MmioFlag) \
AtaWriteBlock32(Port, Buffer, Count, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag)
#define ATA_READ(Port, Ctx, MmioFlag) \
AtaReadPortUchar(Port, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag)
#define ATA_WRITE(Port, Value, Ctx, MmioFlag) \
AtaWritePortUchar(Port, Value, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag)
#define ATA_WRITE_ULONG(Port, Value, Ctx, MmioFlag) \
AtaWritePortUlong(Port, Value, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag)
FORCEINLINE
VOID
ATA_SELECT_DEVICE(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ UCHAR DeviceNumber,
_In_ UCHAR DeviceSelect)
{
#if defined(_M_IX86)
/* NEC extension to allow 4 drives per channel */
if ((ChanData->ChanInfo & CHANNEL_FLAG_CBUS) &&
(ChanData->LastAtaBankId != DeviceNumber))
{
UCHAR AtaBank;
ChanData->LastAtaBankId = DeviceNumber;
/* The 0x432 port is used to select the primary (0) or secondary (1) IDE channel */
AtaBank = DeviceNumber >> 1;
if (ChanData->ChanInfo & CHANNEL_FLAG_IO32)
AtaBank |= PC98_ATA_BANK_32BIT_PORT;
WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, AtaBank);
}
#endif
ATA_WRITE(ChanData->Regs.Device, DeviceSelect, ChanData, MRES_TF);
ATA_IO_WAIT();
}
FORCEINLINE
UCHAR
ATA_WAIT(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_range_(>, 0) ULONG Timeout,
_In_ UCHAR Mask,
_In_ UCHAR Value)
{
UCHAR IdeStatus;
ULONG i;
for (i = 0; i < Timeout; ++i)
{
IdeStatus = ChanData->ReadStatus(ChanData);
if ((IdeStatus & Mask) == Value)
break;
if (IdeStatus == 0xFF)
break;
KeStallExecutionProcessor(10);
}
return IdeStatus;
}

View File

@@ -7,11 +7,110 @@
#include "pciidex.h"
#define NDEBUG
#include <debug.h>
ULONG PciIdeControllerNumber = 0;
BOOLEAN
PciFindDevice(
_In_ __callback PATA_PCI_MATCH_FN MatchFunction,
_In_ PVOID Context)
{
ULONG BusNumber, DeviceNumber, FunctionNumber;
for (BusNumber = 0; BusNumber < 0xFF; ++BusNumber)
{
for (DeviceNumber = 0; DeviceNumber < PCI_MAX_DEVICES; ++DeviceNumber)
{
for (FunctionNumber = 0; FunctionNumber < PCI_MAX_FUNCTION; ++FunctionNumber)
{
UCHAR Buffer[RTL_SIZEOF_THROUGH_FIELD(PCI_COMMON_HEADER, RevisionID)];
PPCI_COMMON_HEADER PciConfig = (PPCI_COMMON_HEADER)Buffer; // Partial PCI header
PCI_SLOT_NUMBER PciSlot;
ULONG BytesRead;
PciSlot.u.AsULONG = 0;
PciSlot.u.bits.DeviceNumber = DeviceNumber;
PciSlot.u.bits.FunctionNumber = FunctionNumber;
BytesRead = HalGetBusDataByOffset(PCIConfiguration,
BusNumber,
PciSlot.u.AsULONG,
&Buffer,
0,
sizeof(Buffer));
if (BytesRead != sizeof(Buffer) ||
PciConfig->VendorID == PCI_INVALID_VENDORID ||
PciConfig->VendorID == 0)
{
if (FunctionNumber == 0)
{
/* This slot has no single- or a multi-function device */
break;
}
else
{
/* Continue scanning the functions */
continue;
}
}
if (MatchFunction(Context, BusNumber, PciSlot, PciConfig))
return TRUE;
if (!PCI_MULTIFUNCTION_DEVICE(PciConfig))
{
/* The device is a single function device */
break;
}
}
}
}
return FALSE;
}
VOID
PciRead(
_In_ PATA_CONTROLLER Controller,
_Out_writes_bytes_all_(BufferLength) PVOID Buffer,
_In_ ULONG ConfigDataOffset,
_In_ ULONG BufferLength)
{
Controller->GetBusData(Controller->BusInterfaceContext,
PCI_WHICHSPACE_CONFIG,
Buffer,
ConfigDataOffset,
BufferLength);
}
VOID
PciWrite(
_In_ PATA_CONTROLLER Controller,
_In_reads_bytes_(BufferLength) PVOID Buffer,
_In_ ULONG ConfigDataOffset,
_In_ ULONG BufferLength)
{
Controller->SetBusData(Controller->BusInterfaceContext,
PCI_WHICHSPACE_CONFIG,
Buffer,
ConfigDataOffset,
BufferLength);
}
VOID
AtaSleep(VOID)
{
KTIMER Timer;
LARGE_INTEGER DueTime;
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
DueTime.QuadPart = PORT_TIMER_TICK_MS * -10000LL;
KeInitializeTimer(&Timer);
KeSetTimer(&Timer, DueTime, 0);
KeWaitForSingleObject(&Timer, Executive, KernelMode, FALSE, 0);
}
CODE_SEG("PAGE")
NTSTATUS
NTAPI
@@ -28,7 +127,7 @@ PciIdeXDispatchWmi(
PFDO_DEVICE_EXTENSION FdoExtension = DeviceObject->DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
Status = IoCallDriver(FdoExtension->Ldo, Irp);
Status = IoCallDriver(FdoExtension->Common.LowerDeviceObject, Irp);
}
else
{
@@ -52,13 +151,156 @@ PciIdeXUnload(
NOTHING;
}
CODE_SEG("PAGE")
NTSTATUS
PciIdeXPnpRepeatRequest(
_In_ PCOMMON_DEVICE_EXTENSION CommonExt,
_In_ PIRP Irp,
_In_opt_ PDEVICE_CAPABILITIES DeviceCapabilities)
{
PCOMMON_DEVICE_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,
PciIdeXPdoCompletionRoutine,
&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
PciIdeXPnpQueryDeviceUsageNotification(
_In_ PCOMMON_DEVICE_EXTENSION CommonExt,
_In_ PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
volatile LONG* Counter;
PAGED_CODE();
if (!IS_FDO(CommonExt))
{
Status = PciIdeXPnpRepeatRequest(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
PciIdeXPnpQueryPnpDeviceState(
_In_ PCOMMON_DEVICE_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;
}
NTSTATUS
NTAPI
PciIdeXPdoCompletionRoutine(
_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
PciIdeXQueryInterface(
_In_ PFDO_DEVICE_EXTENSION FdoExtension,
PciIdeXPnpQueryInterface(
_In_ PCOMMON_DEVICE_EXTENSION CommonExt,
_In_ const GUID* Guid,
_Out_ PVOID Interface,
_In_ ULONG Version,
_In_ ULONG Size)
{
KEVENT Event;
@@ -72,7 +314,7 @@ PciIdeXQueryInterface(
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
FdoExtension->Ldo,
CommonExt->LowerDeviceObject,
NULL,
0,
NULL,
@@ -87,12 +329,12 @@ PciIdeXQueryInterface(
Stack = IoGetNextIrpStackLocation(Irp);
Stack->MinorFunction = IRP_MN_QUERY_INTERFACE;
Stack->Parameters.QueryInterface.InterfaceType = Guid;
Stack->Parameters.QueryInterface.Version = 1;
Stack->Parameters.QueryInterface.Version = Version;
Stack->Parameters.QueryInterface.Size = Size;
Stack->Parameters.QueryInterface.Interface = Interface;
Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
Status = IoCallDriver(FdoExtension->Ldo, Irp);
Status = IoCallDriver(CommonExt->LowerDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
@@ -105,86 +347,40 @@ PciIdeXQueryInterface(
static
CODE_SEG("PAGE")
NTSTATUS
PciIdeXGetConfigurationInfo(
_In_ PFDO_DEVICE_EXTENSION FdoExtension)
{
UCHAR Buffer[RTL_SIZEOF_THROUGH_FIELD(PCI_COMMON_HEADER, BaseClass)];
PPCI_COMMON_HEADER PciConfig = (PPCI_COMMON_HEADER)Buffer;
ULONG BytesRead;
PAGED_CODE();
BytesRead = (*FdoExtension->BusInterface.GetBusData)(FdoExtension->BusInterface.Context,
PCI_WHICHSPACE_CONFIG,
Buffer,
0,
sizeof(Buffer));
if (BytesRead != sizeof(Buffer))
return STATUS_IO_DEVICE_ERROR;
FdoExtension->VendorId = PciConfig->VendorID;
FdoExtension->DeviceId = PciConfig->DeviceID;
if (PciConfig->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR)
{
if (PciConfig->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)
{
/* Both IDE channels in native mode */
FdoExtension->InNativeMode =
(PciConfig->ProgIf & PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE) &&
(PciConfig->ProgIf & PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE);
}
else if (PciConfig->SubClass == PCI_SUBCLASS_MSC_RAID_CTLR)
{
FdoExtension->InNativeMode = TRUE;
}
}
DPRINT("Controller %04x:%04x, Interface byte 0x%02x, Native mode %d\n",
FdoExtension->VendorId,
FdoExtension->DeviceId,
PciConfig->ProgIf,
FdoExtension->InNativeMode);
return STATUS_SUCCESS;
}
CODE_SEG("PAGE")
NTSTATUS
NTAPI
PciIdeXAddDevice(
PciIdeXAddDeviceEx(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PDEVICE_OBJECT PhysicalDeviceObject)
_In_ PDEVICE_OBJECT PhysicalDeviceObject,
_Out_ PVOID *ControllerContext)
{
PPCIIDEX_DRIVER_EXTENSION DriverExtension;
PFDO_DEVICE_EXTENSION FdoExtension;
ULONG DeviceExtensionSize;
PDEVICE_OBJECT Fdo;
UNICODE_STRING DeviceName;
WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\PciIde999")];
WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\PciIde99999")];
NTSTATUS Status;
PAGED_CODE();
Status = RtlStringCbPrintfW(DeviceNameBuffer,
sizeof(DeviceNameBuffer),
L"\\Device\\Ide\\PciIde%u",
L"\\Device\\Ide\\PciIde%lu",
PciIdeControllerNumber);
ASSERT(NT_SUCCESS(Status));
RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
DPRINT("%s(%p, %p) '%wZ'\n", __FUNCTION__, DriverObject, PhysicalDeviceObject, &DeviceName);
INFO("%s(%p, %p, %p) '%wZ'\n",
__FUNCTION__, DriverObject, PhysicalDeviceObject, ControllerContext, &DeviceName);
DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
ASSERT(DriverExtension);
Status = RtlULongAdd(DriverExtension->MiniControllerExtensionSize,
sizeof(FDO_DEVICE_EXTENSION),
sizeof(*FdoExtension),
&DeviceExtensionSize);
if (!NT_SUCCESS(Status))
{
DPRINT1("Invalid miniport extension size %lx\n",
DriverExtension->MiniControllerExtensionSize);
ERR("Invalid miniport extension size %lx\n", DriverExtension->MiniControllerExtensionSize);
return Status;
}
@@ -197,61 +393,90 @@ PciIdeXAddDevice(
&Fdo);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to create FDO 0x%lx\n", Status);
ERR("Failed to create FDO 0x%lx\n", Status);
return Status;
}
FdoExtension = Fdo->DeviceExtension;
RtlZeroMemory(FdoExtension, sizeof(FDO_DEVICE_EXTENSION));
FdoExtension->Common.IsFDO = TRUE;
RtlZeroMemory(FdoExtension, sizeof(*FdoExtension));
FdoExtension->Common.Flags = DO_IS_FDO;
FdoExtension->Common.Self = Fdo;
IoInitializeRemoveLock(&FdoExtension->Common.RemoveLock, TAG_PCIIDEX, 0, 0);
FdoExtension->DriverObject = DriverObject;
FdoExtension->Pdo = PhysicalDeviceObject;
FdoExtension->ControllerNumber = PciIdeControllerNumber++;
KeInitializeSpinLock(&FdoExtension->BusDataLock);
ExInitializeFastMutex(&FdoExtension->DeviceSyncMutex);
ExInitializeFastMutex(&FdoExtension->PdoListSyncMutex);
InitializeListHead(&FdoExtension->PdoListHead);
Status = IoAttachDeviceToDeviceStackSafe(Fdo, PhysicalDeviceObject, &FdoExtension->Ldo);
KeInitializeSpinLock(&FdoExtension->Controller.Lock);
Status = IoAttachDeviceToDeviceStackSafe(Fdo,
PhysicalDeviceObject,
&FdoExtension->Common.LowerDeviceObject);
if (!NT_SUCCESS(Status))
{
DPRINT("Failed to attach FDO 0x%lx\n", Status);
ERR("Failed to attach FDO 0x%lx\n", Status);
goto Failure;
}
/* DMA buffers alignment */
Fdo->AlignmentRequirement = max(FdoExtension->Ldo->AlignmentRequirement, FILE_WORD_ALIGNMENT);
Fdo->AlignmentRequirement = FdoExtension->Common.LowerDeviceObject->AlignmentRequirement;
Fdo->AlignmentRequirement = max(Fdo->AlignmentRequirement, ATA_MIN_BUFFER_ALIGNMENT);
Status = PciIdeXQueryInterface(FdoExtension,
&GUID_BUS_INTERFACE_STANDARD,
&FdoExtension->BusInterface,
sizeof(BUS_INTERFACE_STANDARD));
if (!NT_SUCCESS(Status))
#if defined(ATA_DETECT_LEGACY_DEVICES)
if (ControllerContext)
{
DPRINT1("No bus interface 0x%lx\n", Status);
goto Failure;
FdoExtension->Controller.Flags |= CTRL_FLAG_NON_PNP;
*ControllerContext = FdoExtension;
}
Status = PciIdeXGetConfigurationInfo(FdoExtension);
if (!NT_SUCCESS(Status))
else
#endif
{
DPRINT1("Unable to retrieve the configuration info %lx\n", Status);
goto Failure;
BUS_INTERFACE_STANDARD BusInterface;
Status = PciIdeXPnpQueryInterface(&FdoExtension->Common,
&GUID_BUS_INTERFACE_STANDARD,
&BusInterface,
PCI_BUS_INTERFACE_STANDARD_VERSION,
sizeof(BusInterface));
if (!NT_SUCCESS(Status))
{
ERR("No PCI bus interface 0x%lx\n", Status);
goto Failure;
}
FdoExtension->Controller.BusInterfaceContext = BusInterface.Context;
FdoExtension->Controller.SetBusData = BusInterface.SetBusData;
FdoExtension->Controller.GetBusData = BusInterface.GetBusData;
}
Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
Failure:
if (FdoExtension->Ldo)
IoDetachDevice(FdoExtension->Ldo);
if (FdoExtension->Common.LowerDeviceObject)
IoDetachDevice(FdoExtension->Common.LowerDeviceObject);
IoDeleteDevice(Fdo);
return Status;
}
CODE_SEG("PAGE")
NTSTATUS
NTAPI
PciIdeXAddDevice(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PDEVICE_OBJECT PhysicalDeviceObject)
{
PAGED_CODE();
return PciIdeXAddDeviceEx(DriverObject, PhysicalDeviceObject, NULL);
}
static
CODE_SEG("PAGE")
VOID
@@ -297,17 +522,35 @@ PciIdeXInitialize(
PAGED_CODE();
DPRINT("PciIdeXInitialize(%p '%wZ' %p 0x%lx)\n",
DriverObject, RegistryPath, HwGetControllerProperties, ExtensionSize);
INFO("PciIdeXInitialize(%p '%wZ' %p 0x%lx)\n",
DriverObject, RegistryPath, HwGetControllerProperties, ExtensionSize);
/* ReactOS-specific: Check for our legacy detection magic */
if (ExtensionSize == PCIIDEX_GET_CONTROLLER_INTERFACE_SIGNATURE)
{
#if defined(ATA_DETECT_LEGACY_DEVICES)
PPCIIDEX_LEGACY_CONTROLLER_INTERFACE ControllerInferface;
ControllerInferface = (PVOID)HwGetControllerProperties;
ControllerInferface->Version = PCIIDEX_INTERFACE_VERSION;
ControllerInferface->AddDevice = PciIdeXAddDeviceEx;
ControllerInferface->StartDevice = PciIdeXFdoStartDevice;
ControllerInferface->RemoveDevice = PciIdeXFdoRemoveDevice;
ExtensionSize = 0;
#else
return STATUS_NOT_SUPPORTED;
#endif
}
Status = IoAllocateDriverObjectExtension(DriverObject,
DriverObject,
sizeof(PCIIDEX_DRIVER_EXTENSION),
sizeof(*DriverExtension),
(PVOID*)&DriverExtension);
if (!NT_SUCCESS(Status))
return Status;
RtlZeroMemory(DriverExtension, sizeof(PCIIDEX_DRIVER_EXTENSION));
RtlZeroMemory(DriverExtension, sizeof(*DriverExtension));
DriverExtension->MiniControllerExtensionSize = ExtensionSize;
DriverExtension->HwGetControllerProperties = HwGetControllerProperties;

View File

@@ -6,47 +6,346 @@
* Copyright 2023 Dmitry Borisov <di.sean@protonmail.com>
*/
#ifndef _PCIIDEX_PCH_
#define _PCIIDEX_PCH_
#pragma once
#include <ntddk.h>
#include <ntstrsafe.h>
#include <ntintsafe.h>
#include <initguid.h>
#include <wdmguid.h>
#include <ata.h>
#include <ide.h>
#include <scsi.h>
#include <reactos/drivers/ata/ata_shared.h>
#include <reactos/drivers/ata/ata_user.h>
#include "debug.h"
#include "ahci.h"
#include <debug/driverdbg.h>
typedef struct _CHANNEL_DATA_PATA CHANNEL_DATA_PATA, *PCHANNEL_DATA_PATA;
typedef struct _CHANNEL_DATA_COMMON CHANNEL_DATA_COMMON, *PCHANNEL_DATA_COMMON;
typedef struct _PCIIDE_PRD_TABLE_ENTRY PCIIDE_PRD_TABLE_ENTRY, *PPCIIDE_PRD_TABLE_ENTRY;
#define TAG_PCIIDEX 'XedI'
#define IS_FDO(p) (((PCOMMON_DEVICE_EXTENSION)(p))->IsFDO)
#define ASSUME(cond) \
do { \
ASSERT(cond); \
__assume(cond); \
} while (0)
#define IS_FDO(p) \
((((PCOMMON_DEVICE_EXTENSION)(p))->Flags & DO_IS_FDO) != 0)
#define IS_PRIMARY_CHANNEL(PdoExtension) (PdoExtension->Channel == 0)
/*
* Legacy ranges and interrupts
*/
#define PCIIDE_LEGACY_RESOURCE_COUNT 3
#define PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH 8
#define PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH 1
#define PCIIDE_LEGACY_PRIMARY_COMMAND_BASE 0x1F0
#define PCIIDE_LEGACY_PRIMARY_CONTROL_BASE 0x3F6
#define PCIIDE_LEGACY_PRIMARY_IRQ 14
#define PCIIDE_LEGACY_SECONDARY_COMMAND_BASE 0x170
#define PCIIDE_LEGACY_SECONDARY_CONTROL_BASE 0x376
#define PCIIDE_LEGACY_SECONDARY_IRQ 15
#if defined(_MSC_VER)
#pragma section("PAGECONS", read)
#endif
/*
* Programming Interface Register
*/
#define PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE 0x01
#define PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE_CAPABLE 0x02
#define PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE 0x04
#define PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE_CAPABLE 0x08
#define PCIIDE_PROGIF_DMA_CAPABLE 0x80
/** Pageable read-only data */
#define PCIIDEX_PAGED_DATA DATA_SEG("PAGECONS")
#define BM_SECONDARY_CHANNEL_OFFSET 8
#define MAX_CHANNELS 32
#define PORT_TIMER_TICK_MS 10 // Timer interval is 10ms
#define DEV_NUMBER(Device) ((Device)->TransportFlags & DEVICE_NUMBER_MASK)
typedef struct _PDO_DEVICE_EXTENSION PDO_DEVICE_EXTENSION, *PPDO_DEVICE_EXTENSION;
typedef struct _ATA_CONTROLLER ATA_CONTROLLER, *PATA_CONTROLLER;
_IRQL_requires_max_(APC_LEVEL)
typedef BOOLEAN
(ATA_PCI_MATCH_FN)(
_In_ PVOID Context,
_In_ ULONG BusNumber,
_In_ PCI_SLOT_NUMBER PciSlot,
_In_ PPCI_COMMON_HEADER PciConfig);
typedef ATA_PCI_MATCH_FN *PATA_PCI_MATCH_FN;
_IRQL_requires_max_(APC_LEVEL)
typedef IDE_CHANNEL_STATE
(CONTROLLER_CHANNEL_ENABLED)(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel);
typedef CONTROLLER_CHANNEL_ENABLED *PCONTROLLER_CHANNEL_ENABLED;
_IRQL_requires_max_(DISPATCH_LEVEL)
typedef VOID
(CONTROLLER_START)(
_In_ PATA_CONTROLLER Controller);
typedef CONTROLLER_START *PCONTROLLER_START;
_IRQL_requires_max_(DISPATCH_LEVEL)
typedef VOID
(CONTROLLER_STOP)(
_In_ PATA_CONTROLLER Controller);
typedef CONTROLLER_STOP *PCONTROLLER_STOP;
typedef VOID
(CONTROLLER_FREE_RESOURCES)(
_In_ PATA_CONTROLLER Controller);
typedef CONTROLLER_FREE_RESOURCES *PCONTROLLER_FREE_RESOURCES;
typedef NTSTATUS
(CONTROLLER_ATTACH_CHANNEL_EX)(
_In_ PVOID ChannelContext,
_In_ BOOLEAN Attach);
typedef CONTROLLER_ATTACH_CHANNEL_EX *PCONTROLLER_ATTACH_CHANNEL_EX;
_IRQL_requires_(DISPATCH_LEVEL)
typedef VOID
(CHANNEL_SET_MODE_EX)(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel,
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList);
typedef CHANNEL_SET_MODE_EX *PCHANNEL_SET_MODE_EX;
typedef NTSTATUS
(CHANNEL_ALLOCATE_MEMORY)(
_In_ PVOID ChannelContext);
typedef CHANNEL_ALLOCATE_MEMORY *PCHANNEL_ALLOCATE_MEMORY;
typedef VOID
(CHANNEL_FREE_MEMORY)(
_In_ PVOID ChannelContext);
typedef CHANNEL_FREE_MEMORY *PCHANNEL_FREE_MEMORY;
typedef VOID
(CHANNEL_ENABLE_INTERRUPTS)(
_In_ PVOID ChannelContext,
_In_ BOOLEAN Enable);
typedef CHANNEL_ENABLE_INTERRUPTS *PCHANNEL_ENABLE_INTERRUPTS;
typedef BOOLEAN
(CHANNEL_CHECK_INTERRUPT)(
_In_ PCHANNEL_DATA_PATA ChanData);
typedef CHANNEL_CHECK_INTERRUPT *PCHANNEL_CHECK_INTERRUPT;
typedef VOID
(CHANNEL_LOAD_TASK_FILE)(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ PATA_DEVICE_REQUEST Request);
typedef CHANNEL_LOAD_TASK_FILE *PCHANNEL_LOAD_TASK_FILE;
typedef VOID
(CHANNEL_SAVE_TASK_FILE)(
_In_ PCHANNEL_DATA_PATA ChanData,
_Inout_ PATA_DEVICE_REQUEST Request);
typedef CHANNEL_SAVE_TASK_FILE *PCHANNEL_SAVE_TASK_FILE;
typedef UCHAR
(CHANNEL_READ_STATUS)(
_In_ PCHANNEL_DATA_PATA ChanData);
typedef CHANNEL_READ_STATUS *PCHANNEL_READ_STATUS;
typedef BOOLEAN
(CHANNEL_READ_SCR)(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ SATA_SCR_REGISTER Register,
_In_ ULONG PortNumber,
_In_ PULONG Result);
typedef CHANNEL_READ_SCR *PCHANNEL_READ_SCR;
typedef BOOLEAN
(CHANNEL_WRITE_SCR)(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ SATA_SCR_REGISTER Register,
_In_ ULONG PortNumber,
_In_ ULONG Value);
typedef CHANNEL_WRITE_SCR *PCHANNEL_WRITE_SCR;
typedef struct _ATA_PCI_ENABLE_BITS
{
UCHAR Register;
UCHAR Mask;
UCHAR ValueEnabled;
} ATA_PCI_ENABLE_BITS, *PATA_PCI_ENABLE_BITS;
typedef struct _ATA_CONTROLLER
{
CM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc;
PKINTERRUPT InterruptObject;
struct
{
USHORT VendorID;
USHORT DeviceID;
USHORT Command;
UCHAR RevisionID;
UCHAR ProgIf;
UCHAR SubClass;
UCHAR BaseClass;
UCHAR CacheLineSize;
USHORT SubVendorID;
USHORT SubSystemID;
} Pci;
struct
{
ULONG Flags;
#define RANGE_IS_VALID 0x01
#define RANGE_IS_MEMORY 0x02
#define RANGE_IS_MAPPED 0x04
ULONG Length;
PVOID IoBase;
} AccessRange[PCI_TYPE0_ADDRESSES];
PGET_SET_DEVICE_DATA SetBusData;
PGET_SET_DEVICE_DATA GetBusData;
PVOID BusInterfaceContext;
PVOID ChanDataBlock;
PCONTROLLER_OBJECT HwSyncObject;
PCONTROLLER_START Start;
PCONTROLLER_STOP Stop;
PCONTROLLER_FREE_RESOURCES FreeResources;
PCONTROLLER_ATTACH_CHANNEL_EX AttachChannel;
union
{
const ATA_PCI_ENABLE_BITS* ChannelEnableBits;
PCONTROLLER_CHANNEL_ENABLED ChannelEnabledTest;
};
PVOID HwExt;
ULONG AlignmentRequirement;
ULONG MaxChannels;
ULONG ChannelBitmap;
PUCHAR IoBase;
ULONG AhciVersion;
ULONG AhciCapabilities;
ULONG AhciCapabilitiesEx;
ULONG QueueDepth;
ULONG Flags;
#define CTRL_FLAG_NATIVE_PCI 0x00000001
#define CTRL_FLAG_IS_SIMPLEX 0x00000002
#define CTRL_FLAG_SATA_HBA_ACPI 0x00000002
#define CTRL_FLAG_USE_TEST_FUNCTION 0x00000004
#define CTRL_FLAG_IS_AHCI 0x00000008
#define CTRL_FLAG_NON_PNP 0x00000010
#define CTRL_FLAG_MANUAL_RES 0x00000020
#define CTRL_FLAG_DMA_INTERRUPT 0x00000040
KSPIN_LOCK Lock;
PVOID Channels[MAX_CHANNELS];
} ATA_CONTROLLER, *PATA_CONTROLLER;
typedef struct _CHANNEL_DATA_COMMON
{
PPDO_DEVICE_EXTENSION PdoExt;
CM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc;
PKINTERRUPT InterruptObject;
PCHANNEL_ALLOCATE_MEMORY AllocateMemory;
PCHANNEL_FREE_MEMORY FreeMemory;
PCHANNEL_SET_MODE_EX SetTransferMode;
PCHANNEL_PREPARE_PRD_TABLE PreparePrdTable;
PCHANNEL_PREPARE_IO PrepareIo;
PCHANNEL_START_IO StartIo;
PCHANNEL_ENABLE_INTERRUPTS EnableInterrupts;
PATA_CONTROLLER Controller;
PDMA_ADAPTER DmaAdapter;
ULONG TransferModeSupported;
ULONG MaximumTransferLength;
ULONG Channel;
struct
{
ULONG TransferModeSupported;
ULONG MaximumTransferLength;
} Current;
ULONG MaximumPhysicalPages;
ULONG ChanInfo;
#define CHANNEL_FLAG_IO32 0x00000001
#define CHANNEL_FLAG_DRIVE0_DMA_CAPABLE 0x00000002
#define CHANNEL_FLAG_DRIVE1_DMA_CAPABLE 0x00000004
#define CHANNEL_FLAG_CONTROL_PORT_BASE_MAPPED 0x00000008
#define CHANNEL_FLAG_COMMAND_PORT_BASE_MAPPED 0x00000010
#define CHANNEL_FLAG_DMA_PORT_BASE_MAPPED 0x00000020
#define CHANNEL_FLAG_64_BIT_DMA 0x00000040
#define CHANNEL_FLAG_PRIMARY_ADDRESS_CLAIMED 0x00000080
#define CHANNEL_FLAG_SECONDARY_ADDRESS_CLAIMED 0x00000100
#define CHANNEL_FLAG_PIO_VIA_DMA 0x00000200
#define CHANNEL_FLAG_NO_SLAVE 0x00000400
#define CHANNEL_FLAG_IS_EXTERNAL 0x00000800
#define CHANNEL_FLAG_IS_PMP 0x00001000
#define CHANNEL_FLAG_HAS_FBS 0x00002000
#define CHANNEL_FLAG_FBS_ENABLED 0x00004000
#define CHANNEL_FLAG_HAS_ACPI_GTM 0x00008000
#define CHANNEL_FLAG_HAS_NCQ 0x00010000
#define CHANNEL_FLAG_PIO_FOR_LBA48_XFER 0x00020000 // TODO: Not used yet
#define CHANNEL_FLAG_NO_ATAPI_DMA 0x00040000
#define CHANNEL_FLAG_DMA_BEFORE_CMD 0x00080000
#define CHANNEL_FLAG_MRES_TF 0x00100000 // MRES_TF
#define CHANNEL_FLAG_MRES_DMA 0x00200000 // MRES_DMA
#define CHANNEL_FLAG_MRES_CTRL 0x00400000 // MRES_CTRL
#define CHANNEL_FLAG_CBUS 0x80000000
PVOID PortContext;
PPORT_NOTIFICATION PortNotification;
ULONG ActiveSlotsBitmap;
ULONG ActiveQueuedSlotsBitmap;
PATA_DEVICE_REQUEST* Slots;
} CHANNEL_DATA_COMMON, *PCHANNEL_DATA_COMMON;
typedef struct _CHANNEL_DATA_PATA
{
/** Common data, must be the first member. */
CHANNEL_DATA_COMMON;
PCHANNEL_LOAD_TASK_FILE LoadTaskFile;
PCHANNEL_SAVE_TASK_FILE SaveTaskFile;
PCHANNEL_READ_STATUS ReadStatus;
PPCIIDE_PRD_TABLE_ENTRY PrdTable;
ULONG PrdTablePhysicalAddress;
IDE_REGISTERS Regs;
PCHANNEL_CHECK_INTERRUPT CheckInterrupt;
PUCHAR DataBuffer;
ULONG BytesToTransfer;
ULONG CommandFlags;
ULONG DrqByteCount;
#if defined(_M_IX86)
UCHAR LastAtaBankId;
#endif
BOOLEAN IsPollingActive;
KDPC PollingTimerDpc;
PCHANNEL_READ_SCR ScrRead;
PCHANNEL_WRITE_SCR ScrWrite;
IDE_ACPI_TIMING_MODE_BLOCK CurrentTimingMode;
ULONG HwFlags;
PUCHAR HwExt[ANYSIZE_ARRAY];
} CHANNEL_DATA_PATA, *PCHANNEL_DATA_PATA;
typedef struct _CHANNEL_INFO_AHCI
{
ULONG64 ReceivedFisPhys;
ULONG64 CommandListPhys;
PVOID ReceivedFisOriginal;
PVOID CommandListOriginal;
PVOID CommandTableOriginal[AHCI_MAX_COMMAND_SLOTS];
ULONG CommandTableSize[AHCI_MAX_COMMAND_SLOTS];
ULONG CommandListSize;
PHYSICAL_ADDRESS ReceivedFisPhysOriginal;
PHYSICAL_ADDRESS CommandListPhysOriginal;
PHYSICAL_ADDRESS CommandTablePhysOriginal[AHCI_MAX_COMMAND_SLOTS];
PVOID LocalBuffer;
} CHANNEL_INFO_AHCI, *PCHANNEL_INFO_AHCI;
typedef struct _CHANNEL_DATA_AHCI
{
/** Common data, must be the first member. */
CHANNEL_DATA_COMMON;
PVOID IoBase;
PAHCI_RECEIVED_FIS ReceivedFis;
PAHCI_COMMAND_LIST CommandList;
PAHCI_COMMAND_TABLE CommandTable[AHCI_MAX_COMMAND_SLOTS];
UCHAR LastPmpDeviceNumber;
UCHAR LastFbsDeviceNumber;
ULONG TotalPortCount;
CHANNEL_INFO_AHCI Mem;
} CHANNEL_DATA_AHCI, *PCHANNEL_DATA_AHCI;
typedef struct _PCIIDEX_DRIVER_EXTENSION
{
@@ -56,9 +355,6 @@ typedef struct _PCIIDEX_DRIVER_EXTENSION
typedef struct _COMMON_DEVICE_EXTENSION
{
BOOLEAN IsFDO;
PDEVICE_OBJECT Self;
_Write_guarded_by_(_Global_interlock_)
volatile LONG PageFiles;
@@ -67,30 +363,36 @@ typedef struct _COMMON_DEVICE_EXTENSION
_Write_guarded_by_(_Global_interlock_)
volatile LONG DumpFiles;
union
{
/** Lower device object. This applies to FDO only. */
PDEVICE_OBJECT LowerDeviceObject;
/** Parent FDO. This applies to PDO only. */
PVOID FdoExt;
};
PDEVICE_OBJECT Self;
ULONG Flags;
#define DO_IS_FDO 0x80000000
IO_REMOVE_LOCK RemoveLock;
} COMMON_DEVICE_EXTENSION, *PCOMMON_DEVICE_EXTENSION;
typedef struct _FDO_DEVICE_EXTENSION
{
/** Common data, must be the first member. */
COMMON_DEVICE_EXTENSION Common;
PDEVICE_OBJECT Ldo;
ULONG ControllerNumber;
BOOLEAN InNativeMode;
BOOLEAN IoBaseMapped;
BOOLEAN MiniportStarted;
FAST_MUTEX DeviceSyncMutex;
_Guarded_by_(DeviceSyncMutex)
PPDO_DEVICE_EXTENSION Channels[MAX_IDE_CHANNEL];
USHORT VendorId;
USHORT DeviceId;
PDRIVER_OBJECT DriverObject;
PUCHAR BusMasterPortBase;
KSPIN_LOCK BusDataLock;
BUS_INTERFACE_STANDARD BusInterface;
PDEVICE_OBJECT Pdo;
ULONG ControllerNumber;
FAST_MUTEX PdoListSyncMutex;
_Guarded_by_(PdoListSyncMutex)
LIST_ENTRY PdoListHead;
ATA_CONTROLLER Controller;
IDE_CONTROLLER_PROPERTIES Properties;
/* Must be the last entry */
@@ -99,13 +401,110 @@ typedef struct _FDO_DEVICE_EXTENSION
typedef struct _PDO_DEVICE_EXTENSION
{
/** Common data, must be the first member. */
COMMON_DEVICE_EXTENSION Common;
ULONG Channel;
PFDO_DEVICE_EXTENSION ParentController;
BOOLEAN ReportedMissing;
PUCHAR IoBase;
ULONG Flags;
#define PDO_FLAG_NOT_PRESENT 0x00000001
#define PDO_FLAG_REPORTED_MISSING 0x00000002
PCHANNEL_DATA_COMMON ChanData;
LIST_ENTRY ListEntry;
} PDO_DEVICE_EXTENSION, *PPDO_DEVICE_EXTENSION;
VOID
AtaWritePortUchar(
_In_ PUCHAR Port,
_In_ UCHAR Value,
_In_ ULONG MmioFlags);
#include "pata.h"
extern NTSYSAPI ULONG InitSafeBootMode;
/* acpi.c *********************************************************************/
BOOLEAN
AtaAcpiGetTimingMode(
_In_ PDEVICE_OBJECT DeviceObject,
_Out_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode);
NTSTATUS
AtaAcpiSetTimingMode(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode,
_In_opt_ PIDENTIFY_DEVICE_DATA IdBlock1,
_In_opt_ PIDENTIFY_DEVICE_DATA IdBlock2);
CODE_SEG("PAGE")
PVOID
AtaAcpiGetTaskFile(
_In_ PDEVICE_OBJECT DeviceObject);
CODE_SEG("PAGE")
VOID
AtaAcpiSetDeviceData(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIDENTIFY_DEVICE_DATA IdBlock);
/* fdo.c **********************************************************************/
CODE_SEG("PAGE")
CONTROLLER_ATTACH_CHANNEL AtaCtrlAttachChannel;
CODE_SEG("PAGE")
CHANNEL_SET_DEVICE_DATA AtaCtrlSetDeviceData;
CODE_SEG("PAGE")
CHANNEL_GET_INIT_TASK_FILE AtaCtrlGetInitTaskFile;
CHANNEL_SET_MODE AtaCtrlSetTransferMode;
CHANNEL_DOWNGRADE_INTERFACE_SPEED AtaCtrlDowngradeInterfaceSpeed;
CHANNEL_ABORT_CHANNEL AtaCtrlAbortChannel;
CODE_SEG("PAGE")
CONTROLLER_PNP_START_DEVICE PciIdeXFdoStartDevice;
CODE_SEG("PAGE")
CONTROLLER_PNP_REMOVE_DEVICE PciIdeXFdoRemoveDevice;
DECLSPEC_NOINLINE_FROM_PAGED
VOID
AtaChanEnableInterruptsSync(
_In_ PVOID ChannelContext,
_In_ BOOLEAN Enable);
CODE_SEG("PAGE")
PVOID
AtaCtrlPciMapBar(
_In_ PATA_CONTROLLER Controller,
_In_range_(0, PCI_TYPE0_ADDRESSES) ULONG Index,
_In_ ULONG MinimumIoLength);
CODE_SEG("PAGE")
IDE_CHANNEL_STATE
PciIdeXGetChannelState(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel);
CODE_SEG("PAGE")
NTSTATUS
PciIdeXFdoDispatchPnp(
_In_ PFDO_DEVICE_EXTENSION FdoExtension,
_Inout_ PIRP Irp);
/* miniport.c *****************************************************************/
CODE_SEG("PAGE")
IDE_CHANNEL_STATE
PciIdeXChannelState(
_In_ PFDO_DEVICE_EXTENSION FdoExtension,
_In_ ULONG Channel);
/* pciidex.c ******************************************************************/
CODE_SEG("PAGE")
DRIVER_INITIALIZE DriverEntry;
@@ -115,32 +514,396 @@ DRIVER_UNLOAD PciIdeXUnload;
CODE_SEG("PAGE")
DRIVER_ADD_DEVICE PciIdeXAddDevice;
_Dispatch_type_(IRP_MJ_PNP)
CODE_SEG("PAGE")
DRIVER_DISPATCH_PAGED PciIdeXDispatchPnp;
_Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
CODE_SEG("PAGE")
DRIVER_DISPATCH_PAGED PciIdeXDispatchWmi;
IO_COMPLETION_ROUTINE PciIdeXPdoCompletionRoutine;
BOOLEAN
PciFindDevice(
_In_ __callback PATA_PCI_MATCH_FN MatchFunction,
_In_ PVOID Context);
VOID
PciRead(
_In_ PATA_CONTROLLER Controller,
_Out_writes_bytes_all_(BufferLength) PVOID Buffer,
_In_ ULONG ConfigDataOffset,
_In_ ULONG BufferLength);
VOID
PciWrite(
_In_ PATA_CONTROLLER Controller,
_In_reads_bytes_(BufferLength) PVOID Buffer,
_In_ ULONG ConfigDataOffset,
_In_ ULONG BufferLength);
VOID
AtaSleep(VOID);
CODE_SEG("PAGE")
NTSTATUS
PciIdeXPnpRepeatRequest(
_In_ PCOMMON_DEVICE_EXTENSION CommonExt,
_In_ PIRP Irp,
_In_opt_ PDEVICE_CAPABILITIES DeviceCapabilities);
CODE_SEG("PAGE")
NTSTATUS
PciIdeXPnpQueryDeviceUsageNotification(
_In_ PCOMMON_DEVICE_EXTENSION CommonExt,
_In_ PIRP Irp);
CODE_SEG("PAGE")
NTSTATUS
PciIdeXPnpQueryPnpDeviceState(
_In_ PCOMMON_DEVICE_EXTENSION CommonExt,
_In_ PIRP Irp);
/* pdo.c **********************************************************************/
_Dispatch_type_(IRP_MJ_PNP)
CODE_SEG("PAGE")
DRIVER_DISPATCH_PAGED PciIdeXDispatchPnp;
CODE_SEG("PAGE")
NTSTATUS
PciIdeXPdoRemoveDevice(
_In_ PPDO_DEVICE_EXTENSION PdoExtension,
_In_ PIRP Irp,
_In_ BOOLEAN FinalRemove);
/* power.c ********************************************************************/
_Dispatch_type_(IRP_MJ_POWER)
DRIVER_DISPATCH_RAISED PciIdeXDispatchPower;
CODE_SEG("PAGE")
NTSTATUS
PciIdeXFdoDispatchPnp(
_In_ PFDO_DEVICE_EXTENSION FdoExtension,
_Inout_ PIRP Irp);
/* ahci_generic.c *************************************************************/
CODE_SEG("PAGE")
NTSTATUS
PciIdeXStartMiniport(
_In_ PFDO_DEVICE_EXTENSION FdoExtension);
AhciGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
/* ahci_hw.c ******************************************************************/
CHANNEL_ENABLE_INTERRUPTS AtaAhciEnableInterrupts;
CHANNEL_ENUMERATE_CHANNEL AtaAhciEnumerateChannel;
CHANNEL_IDENTIFY_DEVICE AtaAhciIdentifyDevice;
CHANNEL_RESET_CHANNEL AtaAhciResetChannel;
VOID
AtaAhciStopDma(
_In_ PCHANNEL_DATA_AHCI ChanData);
VOID
AtaAhciSaveTaskFile(
_In_ PCHANNEL_DATA_AHCI ChanData,
_Inout_ PATA_DEVICE_REQUEST Request,
_In_ BOOLEAN ProcessErrorStatus);
VOID
AtaAhciHandleFatalError(
_In_ PCHANNEL_DATA_AHCI ChanData);
VOID
AtaAhciHandlePortStateChange(
_In_ PCHANNEL_DATA_AHCI ChanData,
_In_ ULONG InterruptStatus);
BOOLEAN
AtaAhciDowngradeInterfaceSpeed(
_In_ PCHANNEL_DATA_AHCI ChanData);
ULONG
AtaAhciChannelGetMaximumDeviceCount(
_In_ PVOID ChannelContext);
/* ahci_io.c ******************************************************************/
CHANNEL_START_IO AtaAhciStartIo;
CHANNEL_PREPARE_IO AtaAhciPrepareIo;
CHANNEL_PREPARE_PRD_TABLE AtaAhciPreparePrdTable;
CHANNEL_ALLOCATE_SLOT AtaAhciAllocateSlot;
KSERVICE_ROUTINE AtaAhciHbaIsr;
/* pata_generic.c *************************************************************/
CHANNEL_SET_MODE_EX SataSetTransferMode;
CODE_SEG("PAGE")
CONTROLLER_ATTACH_CHANNEL_EX PciIdeAttachChannel;
VOID
AtaSelectTimings(
_In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList,
_Out_writes_all_(MAX_IDE_DEVICE) PATA_TIMING Timings,
_In_range_(>, 0) ULONG ClockPeriodPs,
_In_ ULONG Flags);
CODE_SEG("PAGE")
VOID
PciIdeInitTaskFileIoResources(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ ULONG_PTR CommandPortBase,
_In_ ULONG_PTR ControlPortBase,
_In_ ULONG CommandBlockSpare);
CODE_SEG("PAGE")
IDE_CHANNEL_STATE
PciIdeXChannelState(
_In_ PFDO_DEVICE_EXTENSION FdoExtension,
PciIdeGetChannelState(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel);
#endif /* _PCIIDEX_PCH_ */
CODE_SEG("PAGE")
NTSTATUS
PciIdeParseResources(
_In_ PCHANNEL_DATA_PATA ChanData,
_In_ PCM_RESOURCE_LIST ResourcesTranslated);
CODE_SEG("PAGE")
VOID
PciIdeFreeResources(
_In_ PVOID ChannelContext);
CODE_SEG("PAGE")
NTSTATUS
PciIdeConnectInterrupt(
_In_ PVOID ChannelContext);
CODE_SEG("PAGE")
NTSTATUS
PciIdeCreateChannelData(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG HwExtensionSize);
CODE_SEG("PAGE")
NTSTATUS
PciIdeGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
/* pata_hw.c ******************************************************************/
CHANNEL_RESET_CHANNEL PataResetChannel;
CHANNEL_ENUMERATE_CHANNEL PataEnumerateChannel;
CHANNEL_IDENTIFY_DEVICE PataIdentifyDevice;
ULONG
PataChannelGetMaximumDeviceCount(
_In_ PVOID ChannelContext);
/* pata_io.c ******************************************************************/
CHANNEL_ALLOCATE_SLOT PataAllocateSlot;
CHANNEL_PREPARE_PRD_TABLE PciIdePreparePrdTable;
CHANNEL_PREPARE_IO PataPrepareIo;
CHANNEL_START_IO PataStartIo;
KSERVICE_ROUTINE PataChannelIsr;
KSERVICE_ROUTINE PciIdeChannelIsr;
CHANNEL_CHECK_INTERRUPT PciIdeCheckInterrupt;
CHANNEL_LOAD_TASK_FILE PataLoadTaskFile;
CHANNEL_SAVE_TASK_FILE PataSaveTaskFile;
CHANNEL_READ_STATUS PataReadStatus;
KDEFERRED_ROUTINE PataPollingTimerDpc;
VOID
AtaWriteBlock16(
_In_ PUSHORT Port,
_In_ PUSHORT Buffer,
_In_ ULONG Count,
_In_ ULONG MmioFlags);
VOID
AtaReadBlock16(
_In_ PUSHORT Port,
_In_ PUSHORT Buffer,
_In_ ULONG Count,
_In_ ULONG MmioFlags);
VOID
AtaWriteBlock32(
_In_ PULONG Port,
_In_ PULONG Buffer,
_In_ ULONG Count,
_In_ ULONG MmioFlags);
VOID
AtaReadBlock32(
_In_ PULONG Port,
_In_ PULONG Buffer,
_In_ ULONG Count,
_In_ ULONG MmioFlags);
UCHAR
AtaReadPortUchar(
_In_ PUCHAR Port,
_In_ ULONG MmioFlags);
VOID
AtaWritePortUlong(
_In_ PULONG Port,
_In_ ULONG Value,
_In_ ULONG MmioFlags);
VOID
PciIdeDmaStop(
_In_ PCHANNEL_DATA_PATA ChanData);
/* misc ***********************************************************************/
#if defined(ATA_DETECT_LEGACY_DEVICES)
CODE_SEG("PAGE")
NTSTATUS
PataGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller,
_In_ PCM_RESOURCE_LIST ResourcesTranslated);
#endif
CODE_SEG("PAGE")
NTSTATUS
AmdGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
AtiGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
CmdGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
Sil680GetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
IntelGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
PcTechGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
SvwPataGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
SvwSataGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
ToshibaGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CODE_SEG("PAGE")
NTSTATUS
ViaGetControllerProperties(
_Inout_ PATA_CONTROLLER Controller);
CHANNEL_SET_MODE_EX SvwSetTransferMode;
CODE_SEG("PAGE")
BOOLEAN
SvwHasUdmaCable(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG Channel);
VOID
ViaClampTimings(
_Inout_ PATA_TIMING Timing);
FORCEINLINE
ULONG
CountSetBits(
_In_ ULONG x)
{
x -= x >> 1 & 0x55555555;
x = (x & 0x33333333) + (x >> 2 & 0x33333333);
return ((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101 >> 24;
}
FORCEINLINE
BOOLEAN
IsPowerOfTwo(
_In_ ULONG x)
{
/* Also exclude zero numbers */
return (x != 0) && ((x & (x - 1)) == 0);
}
FORCEINLINE
UCHAR
PciRead8(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG ConfigDataOffset)
{
UCHAR Result;
PciRead(Controller, &Result, ConfigDataOffset, sizeof(Result));
return Result;
}
FORCEINLINE
USHORT
PciRead16(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG ConfigDataOffset)
{
USHORT Result;
PciRead(Controller, &Result, ConfigDataOffset, sizeof(Result));
return Result;
}
FORCEINLINE
ULONG
PciRead32(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG ConfigDataOffset)
{
ULONG Result;
PciRead(Controller, &Result, ConfigDataOffset, sizeof(Result));
return Result;
}
FORCEINLINE
VOID
PciWrite8(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG ConfigDataOffset,
_In_ UCHAR Value)
{
PciWrite(Controller, &Value, ConfigDataOffset, sizeof(Value));
}
FORCEINLINE
VOID
PciWrite16(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG ConfigDataOffset,
_In_ USHORT Value)
{
PciWrite(Controller, &Value, ConfigDataOffset, sizeof(Value));
}
FORCEINLINE
VOID
PciWrite32(
_In_ PATA_CONTROLLER Controller,
_In_ ULONG ConfigDataOffset,
_In_ ULONG Value)
{
PciWrite(Controller, &Value, ConfigDataOffset, sizeof(Value));
}

View File

@@ -1,5 +1,5 @@
#define REACTOS_VERSION_DLL
#define REACTOS_STR_FILE_DESCRIPTION "PCI IDE bus driver extension"
#define REACTOS_STR_FILE_DESCRIPTION "ATA Bus Driver Extension"
#define REACTOS_STR_INTERNAL_NAME "pciidex"
#define REACTOS_STR_ORIGINAL_FILENAME "pciidex.sys"
#include <reactos/version.rc>

View File

@@ -2,3 +2,4 @@
@ stdcall PciIdeXGetBusData(ptr ptr long long)
@ stdcall PciIdeXInitialize(ptr ptr ptr long)
@ stdcall PciIdeXSetBusData(ptr ptr ptr long long)
@ stdcall -version=0x600+ PciIdexGetBusLocation(ptr ptr)

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,6 @@
#include "pciidex.h"
#define NDEBUG
#include <debug.h>
static
NTSTATUS
PciIdeXPdoDispatchPower(
@@ -48,7 +45,7 @@ PciIdeXFdoDispatchPower(
{
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(FdoExtension->Ldo, Irp);
return PoCallDriver(FdoExtension->Common.LowerDeviceObject, Irp);
}
NTSTATUS
@@ -57,10 +54,24 @@ PciIdeXDispatchPower(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp)
{
PVOID DeviceExtension = DeviceObject->DeviceExtension;
PCOMMON_DEVICE_EXTENSION CommonExt = DeviceObject->DeviceExtension;
NTSTATUS Status;
if (IS_FDO(DeviceExtension))
return PciIdeXFdoDispatchPower(DeviceExtension, Irp);
Status = IoAcquireRemoveLock(&CommonExt->RemoveLock, Irp);
if (!NT_SUCCESS(Status))
{
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
if (IS_FDO(CommonExt))
Status = PciIdeXFdoDispatchPower((PVOID)CommonExt, Irp);
else
return PciIdeXPdoDispatchPower(DeviceExtension, Irp);
Status = PciIdeXPdoDispatchPower((PVOID)CommonExt, Irp);
IoReleaseRemoveLock(&CommonExt->RemoveLock, Irp);
return Status;
}

View File

@@ -294,6 +294,7 @@ PciIdeXSetBusData(
#define UDMA_MODE3 (1 << 14)
#define UDMA_MODE4 (1 << 15)
#define UDMA_MODE5 (1 << 16)
#define UDMA_MODE6 (1 << 17)
#ifdef __cplusplus
}

View File

@@ -0,0 +1,547 @@
/*
* 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: Internal shared ATA driver definitions
* COPYRIGHT: Copyright 2026 Dmitry Borisov (di.sean@protonmail.com)
*/
#pragma once
/** @sa PCIIDE_INTERFACE */
#define PCIIDEX_INTERFACE_VERSION 1
/** Maximum number of devices (target ID) per channel */
#define ATA_MAX_DEVICE 15
/**
* @brief 256 sectors of 512 bytes (128 kB).
*
* This ensures that the sector count register will not overflow in LBA-28 and CHS modes.
* In the case of the sector count (0x100) being truncated to 8-bits, 0 still means 256 sectors.
*/
/*@{*/
#define ATA_MIN_SECTOR_SIZE 512
#define ATA_MAX_SECTORS_PER_IO 0x100
#define ATA_MAX_TRANSFER_LENGTH (ATA_MAX_SECTORS_PER_IO * ATA_MIN_SECTOR_SIZE) // 0x20000
/*@}*/
/** Minimum DMA buffer alignment because of the reserved PRDT byte 0 */
#define ATA_MIN_BUFFER_ALIGNMENT FILE_WORD_ALIGNMENT
#include <pshpack1.h>
/** IDE channel timing information block */
typedef struct _IDE_ACPI_TIMING_MODE_BLOCK
{
struct
{
ULONG PioSpeed; ///< PIO cycle timing in ns
ULONG DmaSpeed; ///< DMA cycle timing in ns
/** The mode is not supported */
#define IDE_ACPI_TIMING_MODE_NOT_SUPPORTED 0xFFFFFFFF // aka -1 ns
} Drive[MAX_IDE_DEVICE];
ULONG ModeFlags;
/** Use the UDMA mode on drive 0/1 */
#define IDE_ACPI_TIMING_MODE_FLAG_UDMA(Drive) (0x01 << (2 * (Drive)))
/** Enable the IORDY signal on drive 0/1 */
#define IDE_ACPI_TIMING_MODE_FLAG_IORDY(Drive) (0x02 << (2 * (Drive)))
/** Independent timing available */
#define IDE_ACPI_TIMING_MODE_FLAG_INDEPENDENT_TIMINGS 0x10
} IDE_ACPI_TIMING_MODE_BLOCK, *PIDE_ACPI_TIMING_MODE_BLOCK;
/** _GTF data buffer */
typedef struct _ATA_ACPI_TASK_FILE
{
UCHAR Feature;
UCHAR SectorCount;
UCHAR LowLba;
UCHAR MidLba;
UCHAR HighLba;
UCHAR DriveSelect;
UCHAR Command;
} ATA_ACPI_TASK_FILE, *PATA_ACPI_TASK_FILE;
#include <poppack.h>
typedef struct _ATA_DEVICE_REQUEST ATA_DEVICE_REQUEST, *PATA_DEVICE_REQUEST;
typedef enum _PORT_NOTIFICATION_TYPE
{
AtaRequestComplete = 0,
AtaResetDetected,
AtaBusChangeDetected,
AtaRequestFailed,
AtaAsyncNotificationDetected,
} PORT_NOTIFICATION_TYPE, *PPORT_NOTIFICATION_TYPE;
typedef enum _ATA_CONNECTION_STATUS
{
CONN_STATUS_FAILURE,
CONN_STATUS_NO_DEVICE,
CONN_STATUS_DEV_UNKNOWN,
CONN_STATUS_DEV_ATA,
CONN_STATUS_DEV_ATAPI,
} ATA_CONNECTION_STATUS;
typedef struct _IDE_REGISTERS
{
PUCHAR Data;
union
{
PUCHAR Features;
PUCHAR Error;
};
union
{
PUCHAR SectorCount;
PUCHAR InterruptReason;
};
PUCHAR LbaLow; ///< LBA bits 0-7, 24-31
union
{
PUCHAR LbaMid; ///< LBA bits 8-15, 32-39
PUCHAR ByteCountLow;
PUCHAR SignatureLow;
};
union
{
PUCHAR LbaHigh; ///< LBA bits 16-23, 40-47
PUCHAR ByteCountHigh;
PUCHAR SignatureHigh;
};
PUCHAR Device;
union
{
PUCHAR Command;
PUCHAR Status;
};
union
{
PUCHAR Control;
PUCHAR AlternateStatus;
};
PUCHAR Dma;
PUCHAR Scr;
} IDE_REGISTERS, *PIDE_REGISTERS;
typedef struct _CHANNEL_DEVICE_CONFIG
{
ULONG SupportedModes;
ULONG PioMode;
ULONG DmaMode;
ULONG MinPioCycleTime;
ULONG MinMwDmaCycleTime;
ULONG MinSwDmaCycleTime;
ULONG CurrentModes;
BOOLEAN IsFixedDisk;
BOOLEAN IoReadySupported;
BOOLEAN IsNewDevice;
PCSTR FriendlyName;
PIDENTIFY_DEVICE_DATA IdentifyDeviceData;
} CHANNEL_DEVICE_CONFIG, *PCHANNEL_DEVICE_CONFIG;
typedef enum
{
COMPLETE_IRP = 0,
COMPLETE_NO_IRP,
COMPLETE_START_AGAIN,
} ATA_COMPLETION_ACTION;
typedef ATA_COMPLETION_ACTION
(REQUEST_COMPLETION_ROUTINE)(
_In_ PATA_DEVICE_REQUEST Request);
typedef REQUEST_COMPLETION_ROUTINE *PREQUEST_COMPLETION_ROUTINE;
typedef struct _ATA_IO_CONTEXT_COMMON
{
ULONG SectorSize;
ULONG TransportFlags;
#define DEVICE_NUMBER_MASK 0x0000000F
#define DEVICE_IS_ATAPI 0x00000010
#define DEVICE_HAS_CDB_INTERRUPT 0x00000020
#define DEVICE_NEED_DMA_DIRECTION 0x00000040
#define DEVICE_IS_NEC_CDR260 0x00000080
#define DEVICE_QUEUE_DEPTH_MASK 0x0000FF00
#define DEVICE_QUEUE_DEPTH_SHIFT 8
UCHAR MultiSectorCount;
UCHAR CdbSize;
UCHAR DeviceSelect;
} ATA_IO_CONTEXT_COMMON, *PATA_IO_CONTEXT_COMMON;
/** ATA Task File interface */
typedef struct _ATA_TASKFILE
{
union
{
UCHAR Command;
UCHAR Status;
};
union
{
UCHAR Feature;
UCHAR Error;
};
UCHAR LowLba; ///< LBA bits 0-7
UCHAR MidLba; ///< LBA bits 8-15
UCHAR HighLba; ///< LBA bits 16-23
UCHAR DriveSelect;
UCHAR LowLbaEx; ///< LBA bits 24-31
UCHAR MidLbaEx; ///< LBA bits 32-39
UCHAR HighLbaEx; ///< LBA bits 40-47
UCHAR FeatureEx;
UCHAR SectorCount;
UCHAR SectorCountEx;
UCHAR Icc; ///< Isochronous Command Completion
ULONG Auxiliary;
} ATA_TASKFILE, *PATA_TASKFILE;
/** ATA device request context */
typedef struct _ATA_DEVICE_REQUEST
{
union
{
/** Completion queue entry */
SLIST_ENTRY CompletionEntry;
/** Port queue entry */
LIST_ENTRY PortEntry;
};
union
{
UCHAR Cdb[16];
ATA_TASKFILE TaskFile;
};
PVOID DataBuffer;
PSCATTER_GATHER_LIST SgList;
PATA_IO_CONTEXT_COMMON Device;
ULONG DataTransferLength;
ULONG Flags;
PMDL Mdl;
PIRP Irp;
PSCSI_REQUEST_BLOCK Srb;
UCHAR SrbStatus;
PREQUEST_COMPLETION_ROUTINE Complete;
ULONG Tag;
ULONG Slot;
ULONG TimeOut;
union
{
struct
{
UCHAR InternalState;
UCHAR TranslationState;
UCHAR Reserved1;
UCHAR Reserved2;
};
ULONG State;
};
/**
* @brief ATA normal/error outputs.
*
* The contents of registers are only valid
* if flags has the REQUEST_FLAG_HAS_TASK_FILE bit set.
*
* The driver always updates it on ATA errors as we have to set the ATA LBA field.
* For ATAPI errors only the Status and Error fields are updated, as an optimization.
*/
ATA_TASKFILE Output;
#if DBG
ULONG Signature;
#define ATA_DEVICE_REQUEST_SIGNATURE 'rATA'
#endif
} ATA_DEVICE_REQUEST, *PATA_DEVICE_REQUEST;
/** Initial state */
#define REQUEST_STATE_NONE 0
/** SRB not translated into the device request or translaton failed */
#define REQUEST_STATE_NOT_STARTED 1
/** Requeue the device request */
#define REQUEST_STATE_REQUEUE 2
/** Freeze the device queue upon completion of IRP */
#define REQUEST_STATE_FREEZE_QUEUE 3
/** ATA protocols */
/*@{*/
/** DMA ATA command */
#define REQUEST_FLAG_DMA 0x00000001
/** ATAPI PACKET command */
#define REQUEST_FLAG_PACKET_COMMAND 0x00000002
/** DMA Queued command */
#define REQUEST_FLAG_NCQ 0x00000004
/** Data-In command */
#define REQUEST_FLAG_DATA_IN 0x00000040
/** Data-Out command */
#define REQUEST_FLAG_DATA_OUT 0x00000080
/** Software Reset command (AHCI only) */
#define REQUEST_FLAG_RST_COMMAND 0x00040000
/*@}*/
/** 48-bit command */
#define REQUEST_FLAG_LBA48 0x00000008
/** Multiple mode command */
#define REQUEST_FLAG_READ_WRITE_MULTIPLE 0x00000010
/** ATA read/write command */
#define REQUEST_FLAG_READ_WRITE 0x00000020
/** Forced unit access command */
#define REQUEST_FLAG_FUA 0x00000100
/** Return the contents of task file registers to the caller */
#define REQUEST_FLAG_SAVE_TASK_FILE 0x00000200
/** Holds the saved contents of task file registers */
#define REQUEST_FLAG_HAS_TASK_FILE 0x00000400
/** Has extra bits in the device register */
#define REQUEST_FLAG_SET_DEVICE_REGISTER 0x00000800
/** Has extra bits in the Auxiliary field */
#define REQUEST_FLAG_SET_AUXILIARY_FIELD 0x00001000
/** Has extra bits in the Isochronous Command Completion field */
#define REQUEST_FLAG_SET_ICC_FIELD 0x00002000
/** Exclusive port access required */
#define REQUEST_FLAG_EXCLUSIVE 0x00004000
/** The request owns the local buffer */
#define REQUEST_FLAG_HAS_LOCAL_BUFFER 0x00008000
/** The request owns the S/G list and will also release it */
#define REQUEST_FLAG_HAS_SG_LIST 0x00010000
/** The request owns the MDL and will also release it */
#define REQUEST_FLAG_HAS_MDL 0x00020000
/** The request owns the reserved memory mapping and will also release it */
#define REQUEST_FLAG_HAS_RESERVED_MAPPING 0x00080000
/** Copy of SRB_FLAGS_NO_KEEP_AWAKE */
#define REQUEST_FLAG_NO_KEEP_AWAKE 0x00100000
/** Use the DMA engine for the transfer */
#define REQUEST_FLAG_PROGRAM_DMA 0x00200000
/** Internal command */
#define REQUEST_FLAG_INTERNAL 0x00400000
/** ATA or SCSI pass-through command */
#define REQUEST_FLAG_PASSTHROUGH 0x00800000
#define REQUEST_FLAG_DEVICE_EXCLUSIVE_ACCESS 0x01000000
/** Polled command */
#define REQUEST_FLAG_POLL 0x80000000
/**
* Exclusive port access required.
*
* 1) Since there is only one Received FIS structure, it should be necessary to
* synchronize RFIS reads amongst multiple I/O requests.
*
* 2) We also mark all requests that modify the device extension state
* (IDE_COMMAND_IDENTIFY, IDE_COMMAND_ATAPI_IDENTIFY,
* IDE_COMMAND_SET_FEATURE, and others) as REQUEST_FLAG_EXCLUSIVE.
*/
#define REQUEST_EXCLUSIVE_ACCESS_FLAGS \
(REQUEST_FLAG_SAVE_TASK_FILE | REQUEST_FLAG_EXCLUSIVE)
/** DMA command translation */
#define REQUEST_DMA_FLAGS \
(REQUEST_FLAG_DMA | REQUEST_FLAG_PROGRAM_DMA)
typedef VOID
(__cdecl PORT_NOTIFICATION)(
_In_ PORT_NOTIFICATION_TYPE NotificationType,
_In_ PVOID PortContext,
...);
typedef PORT_NOTIFICATION *PPORT_NOTIFICATION;
typedef NTSTATUS
(CONTROLLER_ATTACH_CHANNEL)(
_In_ PVOID ChannelContext,
_In_ BOOLEAN Attach);
typedef CONTROLLER_ATTACH_CHANNEL *PCONTROLLER_ATTACH_CHANNEL;
typedef VOID
(CHANNEL_SET_DEVICE_DATA)(
_In_ PVOID ChannelContext,
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIDENTIFY_DEVICE_DATA IdentifyDeviceData);
typedef CHANNEL_SET_DEVICE_DATA *PCHANNEL_SET_DEVICE_DATA;
typedef PVOID
(CHANNEL_GET_INIT_TASK_FILE)(
_In_ PVOID ChannelContext,
_In_ PDEVICE_OBJECT DeviceObject);
typedef CHANNEL_GET_INIT_TASK_FILE *PCHANNEL_GET_INIT_TASK_FILE;
typedef BOOLEAN
(CHANNEL_DOWNGRADE_INTERFACE_SPEED)(
_In_ PVOID ChannelContext);
typedef CHANNEL_DOWNGRADE_INTERFACE_SPEED *PCHANNEL_DOWNGRADE_INTERFACE_SPEED;
typedef VOID
(CHANNEL_ABORT_CHANNEL)(
_In_ PVOID ChannelContext,
_In_ BOOLEAN DisableInterrupts);
typedef CHANNEL_ABORT_CHANNEL *PCHANNEL_ABORT_CHANNEL;
typedef VOID
(CHANNEL_RESET_CHANNEL)(
_In_ PVOID ChannelContext);
typedef CHANNEL_RESET_CHANNEL *PCHANNEL_RESET_CHANNEL;
typedef ULONG
(CHANNEL_ENUMERATE_CHANNEL)(
_In_ PVOID ChannelContext);
typedef CHANNEL_ENUMERATE_CHANNEL *PCHANNEL_ENUMERATE_CHANNEL;
typedef ATA_CONNECTION_STATUS
(CHANNEL_IDENTIFY_DEVICE)(
_In_ PVOID ChannelContext,
_In_ ULONG DeviceNumber);
typedef CHANNEL_IDENTIFY_DEVICE *PCHANNEL_IDENTIFY_DEVICE;
_IRQL_requires_(DISPATCH_LEVEL)
typedef VOID
(CHANNEL_SET_MODE)(
_In_ PVOID ChannelContext,
_In_reads_(ATA_MAX_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList);
typedef CHANNEL_SET_MODE *PCHANNEL_SET_MODE;
_IRQL_requires_(DISPATCH_LEVEL)
typedef BOOLEAN
(CHANNEL_ALLOCATE_SLOT)(
_In_ PVOID ChannelContext,
_In_ PATA_DEVICE_REQUEST Request,
_In_ BOOLEAN Allocate);
typedef CHANNEL_ALLOCATE_SLOT *PCHANNEL_ALLOCATE_SLOT;
_IRQL_requires_(DISPATCH_LEVEL)
typedef VOID
(CHANNEL_PREPARE_PRD_TABLE)(
_In_ PVOID ChannelContext,
_In_ PATA_DEVICE_REQUEST Request,
_In_ SCATTER_GATHER_LIST* __restrict SgList);
typedef CHANNEL_PREPARE_PRD_TABLE *PCHANNEL_PREPARE_PRD_TABLE;
_IRQL_requires_(DISPATCH_LEVEL)
typedef VOID
(CHANNEL_PREPARE_IO)(
_In_ PVOID ChannelContext,
_In_ PATA_DEVICE_REQUEST Request);
typedef CHANNEL_PREPARE_IO *PCHANNEL_PREPARE_IO;
_IRQL_requires_(HIGH_LEVEL)
typedef BOOLEAN
(CHANNEL_START_IO)(
_In_ PVOID ChannelContext,
_In_ PATA_DEVICE_REQUEST Request);
typedef CHANNEL_START_IO *PCHANNEL_START_IO;
typedef NTSTATUS
(CONTROLLER_PNP_ADD_DEVICE)(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PDEVICE_OBJECT PhysicalDeviceObject,
_Out_ PVOID *ControllerContext);
typedef CONTROLLER_PNP_ADD_DEVICE *PCONTROLLER_PNP_ADD_DEVICE;
typedef NTSTATUS
(CONTROLLER_PNP_START_DEVICE)(
_In_ PVOID ControllerContext,
_In_ PCM_RESOURCE_LIST ResourcesTranslated);
typedef CONTROLLER_PNP_START_DEVICE *PCONTROLLER_PNP_START_DEVICE;
typedef VOID
(CONTROLLER_PNP_REMOVE_DEVICE)(
_In_ PVOID ControllerContext);
typedef CONTROLLER_PNP_REMOVE_DEVICE *PCONTROLLER_PNP_REMOVE_DEVICE;
/* ReactOS-specific legacy detection magic */
#define PCIIDEX_GET_CONTROLLER_INTERFACE_SIGNATURE (0xFFFFFFFF - 0x1000)
/**
* @brief Legacy detection interface with the PCIIDEX driver.
*
* @note This interface is ROS-specific.
*/
typedef struct _PCIIDEX_LEGACY_CONTROLLER_INTERFACE
{
ULONG Version;
PCONTROLLER_PNP_ADD_DEVICE AddDevice;
PCONTROLLER_PNP_START_DEVICE StartDevice;
PCONTROLLER_PNP_REMOVE_DEVICE RemoveDevice;
} PCIIDEX_LEGACY_CONTROLLER_INTERFACE, *PPCIIDEX_LEGACY_CONTROLLER_INTERFACE;
/**
* @brief Channel interface with the PCIIDEX driver.
*
* This interface is ROS-specific.
*/
typedef struct _PCIIDEX_CHANNEL_INTERFACE
{
/* Common interface header */
USHORT Size;
USHORT Version;
PVOID Context;
PINTERFACE_REFERENCE InterfaceReference;
PINTERFACE_DEREFERENCE InterfaceDereference;
PVOID ChannelContext;
PCONTROLLER_ATTACH_CHANNEL AttachChannel;
PKINTERRUPT InterruptObject;
ULONG Channel;
ULONG TransferModeSupported;
ULONG MaximumTransferLength;
ULONG MaximumPhysicalPages;
ULONG QueueDepth;
ULONG MaxTargetId;
PCHANNEL_SET_DEVICE_DATA SetDeviceData;
PCHANNEL_GET_INIT_TASK_FILE GetInitTaskFile;
PCHANNEL_DOWNGRADE_INTERFACE_SPEED DowngradeInterfaceSpeed;
PCHANNEL_ABORT_CHANNEL AbortChannel;
PCHANNEL_RESET_CHANNEL ResetChannel;
PCHANNEL_ENUMERATE_CHANNEL EnumerateChannel;
PCHANNEL_IDENTIFY_DEVICE IdentifyDevice;
PCHANNEL_SET_MODE SetTransferMode;
PCHANNEL_ALLOCATE_SLOT AllocateSlot;
PCHANNEL_PREPARE_PRD_TABLE PreparePrdTable;
PCHANNEL_PREPARE_IO PrepareIo;
PCHANNEL_START_IO StartIo;
PCONTROLLER_OBJECT HwSyncObject;
PDMA_ADAPTER DmaAdapter;
PDEVICE_OBJECT ChannelObject;
ULONG Flags;
#define ATA_CHANNEL_FLAG_PIO_VIA_DMA 0x00000001
#define ATA_CHANNEL_FLAG_IS_EXTERNAL 0x00000002
#define ATA_CHANNEL_FLAG_NCQ 0x00000004
#define ATA_CHANNEL_FLAG_IS_AHCI 0x00000008
#define ATA_CHANNEL_FLAG_PIO_FOR_LBA48_XFER 0x00000010
PVOID* PortContext;
PPORT_NOTIFICATION* PortNotification;
PATA_DEVICE_REQUEST** Slots;
} PCIIDEX_CHANNEL_INTERFACE, *PPCIIDEX_CHANNEL_INTERFACE;
DEFINE_GUID(GUID_PCIIDE_INTERFACE_ROS,
0xD677FBCF, 0xABED, 0x47C8, 0x80, 0xA3, 0xE4, 0x34, 0x7E, 0xA4, 0x96, 0x47);

View File

@@ -0,0 +1,52 @@
/*
* 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 driver user mode interface
* COPYRIGHT: Copyright 2026 Dmitry Borisov (di.sean@protonmail.com)
*/
#pragma once
#define DD_ATA_REG_ATA_DEVICE_TYPE L"DeviceType"
#define DD_ATA_REG_SCSI_DEVICE_TYPE L"ScsiDeviceType"
#define DD_ATA_REG_MAX_TARGET_ID L"MaxTargetId"
#define DD_ATA_REG_XFER_MODE_ALLOWED L"UserTimingModeAllowed"
#define DD_ATA_REG_XFER_MODE_SUPPORTED L"DeviceTimingModeSupported"
#define DD_ATA_REG_XFER_MODE_SELECTED L"DeviceTimingMode"
/** PIO modes 0-4 */
#define PIO_ALL \
(PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4)
/** SWDMA modes 0-2 */
#define SWDMA_ALL \
(SWDMA_MODE0 | SWDMA_MODE1 | SWDMA_MODE2)
/** MWDMA modes 0-2 */
#define MWDMA_ALL \
(MWDMA_MODE0 | MWDMA_MODE1 | MWDMA_MODE2)
/** UDMA modes 0-6 */
#define UDMA_ALL \
(UDMA_MODE0 | UDMA_MODE1 | UDMA_MODE2 | UDMA_MODE3 | UDMA_MODE4 | UDMA_MODE5 | UDMA_MODE6)
/** Converts the provided mode number into a mode index in the bit map */
/*@{*/
#define PIO_MODE(n) (n)
#define SWDMA_MODE(n) (5 + (n))
#define MWDMA_MODE(n) (8 + (n))
#define UDMA_MODE(n) (11 + (n))
/*@}*/
/**
* @brief Private enum between the ATA driver and storprop.dll
* @sa DD_ATA_REG_ATA_DEVICE_TYPE
*/
typedef enum _ATA_DEVICE_TYPE
{
DEV_UNKNOWN = 0,
DEV_ATA = 1,
DEV_ATAPI = 2,
DEV_NONE = 3
} ATA_DEVICE_TYPE;