[FREELDR] Add ATA/ATAPI driver. (#2167) 2167/head
authorDmitry Borisov <di.sean@protonmail.com>
Sun, 22 Dec 2019 21:52:40 +0000 (03:52 +0600)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sun, 12 Jan 2020 20:18:07 +0000 (21:18 +0100)
CORE-16220 CORE-16216

* Create a new driver.
* Use the functions from the library in xboxdisk.c

Driver now supports PC, Xbox and NEC PC-98.

Co-Authored-By: Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
boot/freeldr/freeldr/CMakeLists.txt
boot/freeldr/freeldr/arch/drivers/hwide.c [new file with mode: 0644]
boot/freeldr/freeldr/arch/i386/machxbox.c
boot/freeldr/freeldr/arch/i386/xboxdisk.c
boot/freeldr/freeldr/include/arch/i386/machxbox.h
boot/freeldr/freeldr/include/hwide.h [new file with mode: 0644]

index d4086a7..37f4400 100644 (file)
@@ -66,6 +66,7 @@ list(APPEND FREELDR_NTLDR_SOURCE
 
 list(APPEND FREELDR_ARC_SOURCE
     arcname.c
+    arch/drivers/hwide.c
     arch/arcemul.c
     arch/archwsup.c
     disk/disk.c
diff --git a/boot/freeldr/freeldr/arch/drivers/hwide.c b/boot/freeldr/freeldr/arch/drivers/hwide.c
new file mode 100644 (file)
index 0000000..51f5985
--- /dev/null
@@ -0,0 +1,822 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     ATA/ATAPI polled I/O driver.
+ * COPYRIGHT:   Copyright 2019 Dmitry Borisov (di.sean@protonmail.com)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <freeldr.h>
+#include <hwide.h>
+
+/* DDK */
+#include <ata.h>
+#include <scsi.h>
+
+#include <debug.h>
+DBG_DEFAULT_CHANNEL(DISK);
+
+/* GLOBALS ********************************************************************/
+
+#define TAG_ATA_DEVICE 'DatA'
+#define ATAPI_PACKET_SIZE(IdentifyData) (IdentifyData.AtapiCmdSize ? 16 : 12)
+
+/* Used in WaitForFlags() and should be 31 seconds (31e5), but it's too much for polled I/O */
+#define ATA_STATUS_TIMEOUT 36000
+#define ATA_READ_TIMEOUT   4e5
+
+#define AtaWritePort(Channel, Port, Data) \
+    WRITE_PORT_UCHAR(UlongToPtr(BaseArray[(Channel)] + (Port)), (Data))
+
+#define AtaReadPort(Channel, Port) \
+    READ_PORT_UCHAR(UlongToPtr(BaseArray[(Channel)] + (Port)))
+
+#define AtaWriteBuffer(Channel, Buffer, Count) \
+    WRITE_PORT_BUFFER_USHORT(UlongToPtr(BaseArray[(Channel)] + IDX_IO1_o_Data), \
+                             (PUSHORT)(Buffer), (Count)/sizeof(USHORT))
+
+#define AtaReadBuffer(Channel, Buffer, Count) \
+    READ_PORT_BUFFER_USHORT(UlongToPtr(BaseArray[(Channel)] + IDX_IO1_i_Data), \
+                            (PUSHORT)(Buffer), (Count)/sizeof(USHORT))
+
+/* IDE/ATA Channels base - Primary, Secondary, Tertiary, Quaternary */
+static const ULONG BaseArray[] =
+{
+#if defined(SARCH_XBOX)
+    0x1F0
+#elif defined(SARCH_PC98)
+    0x640, 0x640
+#else
+    0x1F0, 0x170, 0x1E8, 0x168
+#endif
+};
+
+#define MAX_CHANNELS RTL_NUMBER_OF(BaseArray)
+#define MAX_DEVICES  2 /* Master/Slave */
+
+static PDEVICE_UNIT Units[MAX_CHANNELS * MAX_DEVICES];
+
+/* PRIVATE PROTOTYPES *********************************************************/
+
+static
+BOOLEAN
+WaitForFlags(
+    IN UCHAR Channel,
+    IN UCHAR Flags,
+    IN UCHAR ExpectedValue,
+    IN ULONG Timeout
+);
+
+static
+BOOLEAN
+SelectDevice(
+    IN UCHAR Channel,
+    IN UCHAR DeviceNumber
+);
+
+static
+BOOLEAN
+IdentifyDevice(
+    IN UCHAR Channel,
+    IN UCHAR DeviceNumber,
+    OUT PDEVICE_UNIT *DeviceUnit
+);
+
+static
+BOOLEAN
+AtapiReadyCheck(
+    IN OUT PDEVICE_UNIT DeviceUnit
+);
+
+static
+BOOLEAN
+AtapiReadLogicalSectorLBA(
+    IN PDEVICE_UNIT DeviceUnit,
+    IN ULONGLONG SectorNumber,
+    OUT PVOID Buffer
+);
+
+static
+BOOLEAN
+AtaReadLogicalSectorsLBA(
+    IN PDEVICE_UNIT DeviceUnit,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer
+);
+
+/* FUNCTIONS ******************************************************************/
+
+/* Don't call this before running the system timer calibration and MM initialization */
+BOOLEAN
+AtaInit(OUT PUCHAR DetectedCount)
+{
+    UCHAR Channel, DeviceNumber;
+    PDEVICE_UNIT DeviceUnit = NULL;
+
+    TRACE("AtaInit()\n");
+
+    *DetectedCount = 0;
+
+    RtlZeroMemory(&Units, sizeof(Units));
+
+    /* Detect and enumerate ATA/ATAPI devices */
+    for (Channel = 0; Channel < MAX_CHANNELS; ++Channel)
+    {
+        for (DeviceNumber = 0; DeviceNumber < MAX_DEVICES; ++DeviceNumber)
+        {
+            if (IdentifyDevice(Channel, DeviceNumber, &DeviceUnit))
+            {
+                Units[(*DetectedCount)++] = DeviceUnit;
+            }
+        }
+    }
+
+    return (*DetectedCount > 0);
+}
+
+VOID
+AtaFree(VOID)
+{
+    UCHAR i;
+
+    for (i = 0; i < RTL_NUMBER_OF(Units); ++i)
+    {
+        if (Units[i])
+            FrLdrTempFree(Units[i], TAG_ATA_DEVICE);
+    }
+}
+
+PDEVICE_UNIT
+AtaGetDevice(IN UCHAR UnitNumber)
+{
+    if (UnitNumber < RTL_NUMBER_OF(Units))
+        return Units[UnitNumber];
+    else
+        return NULL;
+}
+
+BOOLEAN
+AtaAtapiReadLogicalSectorsLBA(
+    IN OUT PDEVICE_UNIT DeviceUnit,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer)
+{
+    UCHAR RetryCount;
+    BOOLEAN Success;
+
+    if (DeviceUnit == NULL || SectorCount == 0)
+        return FALSE;
+
+    if (DeviceUnit->Flags & ATA_DEVICE_ATAPI)
+    {
+        if ((DeviceUnit->Flags & ATA_DEVICE_NO_MEDIA) || (DeviceUnit->Flags & ATA_DEVICE_NOT_READY))
+        {
+            /* Retry 4 times */
+            for (RetryCount = 0; RetryCount < 4; ++RetryCount)
+            {
+                /* Make the device ready */
+                if (AtapiReadyCheck(DeviceUnit))
+                    break;
+            }
+            if (RetryCount >= 4)
+            {
+                ERR("AtaAtapiReadLogicalSectorsLBA(): Device not ready.\n");
+                return FALSE;
+            }
+        }
+        if (SectorNumber + SectorCount > DeviceUnit->TotalSectors + 1)
+        {
+            ERR("AtaAtapiReadLogicalSectorsLBA(): Attempt to read more than there is to read.\n");
+            return FALSE;
+        }
+
+        while (SectorCount > 0)
+        {
+            /* Read a single sector */
+            Success = AtapiReadLogicalSectorLBA(DeviceUnit, SectorNumber, Buffer);
+            if (!Success)
+                return FALSE;
+
+            --SectorCount;
+            ++SectorNumber;
+            Buffer = (PVOID)((ULONG_PTR)Buffer + DeviceUnit->SectorSize);
+        }
+    }
+    else
+    {
+        /* Retry 3 times */
+        for (RetryCount = 0; RetryCount < 3; ++RetryCount)
+        {
+            /* Read a multiple sectors */
+            Success = AtaReadLogicalSectorsLBA(DeviceUnit, SectorNumber, SectorCount, Buffer);
+            if (Success)
+                return TRUE;
+        }
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static
+BOOLEAN
+AtaReadLogicalSectorsLBA(
+    IN PDEVICE_UNIT DeviceUnit,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer)
+{
+    UCHAR Command;
+    ULONG ChsTemp;
+    USHORT Cylinder;
+    UCHAR Head;
+    UCHAR Sector;
+    ULONG BlockCount;
+    ULONG RemainingBlockCount;
+    ULONGLONG Lba;
+    BOOLEAN UseLBA48;
+
+    UseLBA48 = (DeviceUnit->Flags & ATA_DEVICE_LBA48) &&
+                (((SectorNumber + SectorCount) >= UINT64_C(0x0FFFFF80)) || SectorCount > 256);
+
+    while (SectorCount > 0)
+    {
+    /* Prevent sector count overflow, divide it into maximum possible chunks and loop each one */
+    if (UseLBA48)
+        BlockCount = min(SectorCount, USHRT_MAX);
+    else
+        BlockCount = min(SectorCount, UCHAR_MAX);
+
+    /* Convert LBA into a format CHS if needed */
+    if (DeviceUnit->Flags & ATA_DEVICE_CHS)
+    {
+        ChsTemp = DeviceUnit->IdentifyData.SectorsPerTrack * DeviceUnit->IdentifyData.NumberOfHeads;
+        if (ChsTemp)
+        {
+            Cylinder = SectorNumber / ChsTemp;
+            Head = (SectorNumber % ChsTemp) / DeviceUnit->IdentifyData.SectorsPerTrack;
+            Sector = (SectorNumber % DeviceUnit->IdentifyData.SectorsPerTrack) + 1;
+        }
+        else
+        {
+            Cylinder = 0;
+            Head = 0;
+            Sector = 1;
+        }
+        Lba = (Sector & 0xFF) | ((Cylinder & 0xFFFFF) << 8) | ((Head & 0x0F) << 24);
+    }
+    else
+    {
+        Lba = SectorNumber;
+    }
+
+    /* Select the drive */
+    if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber))
+        return FALSE;
+
+    /* Disable interrupts */
+#ifndef SARCH_PC98
+    AtaWritePort(DeviceUnit->Channel, IDX_IO2_o_AltStatus, IDE_DC_DISABLE_INTERRUPTS);
+    StallExecutionProcessor(1);
+#endif
+
+    if (UseLBA48)
+    {
+        /* FIFO */
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, 0);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, ATA_PIO);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockCount, (BlockCount & 0xFF) >> 8);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockCount, BlockCount & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockNumber, (Lba >> 24) & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockNumber, Lba & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderLow, (Lba >> 32) & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderLow, (Lba >> 8) & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderHigh, (Lba >> 40) & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderHigh, (Lba >> 16) & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_DriveSelect,
+                     IDE_USE_LBA | (DeviceUnit->DeviceNumber ? IDE_DRIVE_2 : IDE_DRIVE_1));
+        Command = IDE_COMMAND_READ_EXT;
+    }
+    else
+    {
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Feature, ATA_PIO);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockCount, BlockCount & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_BlockNumber, Lba & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderLow, (Lba >> 8) & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_CylinderHigh, (Lba >> 16) & 0xFF);
+        AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_DriveSelect,
+                     ((Lba >> 24) & 0x0F) |
+                     (DeviceUnit->Flags & ATA_DEVICE_CHS ? 0x00 : IDE_USE_LBA) |
+                     (DeviceUnit->DeviceNumber ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1));
+        Command = IDE_COMMAND_READ;
+    }
+
+    /* Send read command */
+    AtaWritePort(DeviceUnit->Channel, IDX_IO1_o_Command, Command);
+    StallExecutionProcessor(5);
+
+    for (RemainingBlockCount = BlockCount; RemainingBlockCount > 0; --RemainingBlockCount)
+    {
+        /* Wait for ready to transfer data block */
+        if (!WaitForFlags(DeviceUnit->Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_ERROR),
+                          IDE_STATUS_DRQ, ATA_READ_TIMEOUT))
+        {
+            ERR("AtaReadLogicalSectorsLBA() failed. Status: 0x%02x, Error: 0x%02x\n",
+                AtaReadPort(DeviceUnit->Channel, IDX_IO1_i_Status),
+                AtaReadPort(DeviceUnit->Channel, IDX_IO1_i_Error));
+            return FALSE;
+        }
+
+        /* Transfer the data block */
+        AtaReadBuffer(DeviceUnit->Channel, Buffer, DeviceUnit->SectorSize);
+
+        Buffer = (PVOID)((ULONG_PTR)Buffer + DeviceUnit->SectorSize);
+    }
+
+    SectorNumber += BlockCount;
+    SectorCount -= BlockCount;
+    }
+
+    return TRUE;
+}
+
+static
+BOOLEAN
+AtaSendAtapiPacket(
+    IN UCHAR Channel,
+    IN PUCHAR AtapiPacket,
+    IN UCHAR PacketSize,
+    IN USHORT ByteCount)
+{
+    /* No DRQ for TEST UNIT READY */
+    BOOLEAN NoData = (AtapiPacket[0] == SCSIOP_TEST_UNIT_READY);
+    UCHAR ExpectedFlags = NoData ? IDE_STATUS_DRDY : (IDE_STATUS_DRQ | IDE_STATUS_DRDY);
+
+    /* PIO mode */
+    AtaWritePort(Channel, IDX_ATAPI_IO1_o_Feature, ATA_PIO);
+
+    /* Maximum byte count that is to be transferred */
+    AtaWritePort(Channel, IDX_ATAPI_IO1_o_ByteCountLow, ByteCount & 0xFF);
+    AtaWritePort(Channel, IDX_ATAPI_IO1_o_ByteCountHigh, (ByteCount >> 8) & 0xFF);
+
+    /* Prepare to transfer a device command via a command packet */
+    AtaWritePort(Channel, IDX_ATAPI_IO1_o_Command, IDE_COMMAND_ATAPI_PACKET);
+    StallExecutionProcessor(50);
+    if (!WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_ERROR),
+                      IDE_STATUS_DRQ, NoData ? ATA_STATUS_TIMEOUT : ATA_READ_TIMEOUT))
+    {
+        ERR("AtaSendAtapiPacket(0x%x) failed. A device error occurred Status: 0x%02x, Error: 0x%02x\n",
+            AtapiPacket[0], AtaReadPort(Channel, IDX_ATAPI_IO1_i_Status), AtaReadPort(Channel, IDX_ATAPI_IO1_i_Error));
+        return FALSE;
+    }
+
+    /* Command packet transfer */
+    AtaWriteBuffer(Channel, AtapiPacket, PacketSize);
+    if (!WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_DRDY | IDE_STATUS_ERROR),
+                      ExpectedFlags, NoData ? ATA_STATUS_TIMEOUT : ATA_READ_TIMEOUT))
+    {
+        TRACE("AtaSendAtapiPacket(0x%x) failed. An execution error occurred Status: 0x%02x, Error: 0x%02x\n",
+              AtapiPacket[0], AtaReadPort(Channel, IDX_ATAPI_IO1_i_Status), AtaReadPort(Channel, IDX_ATAPI_IO1_i_Error));
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static
+BOOLEAN
+AtapiReadLogicalSectorLBA(
+    IN PDEVICE_UNIT DeviceUnit,
+    IN ULONGLONG SectorNumber,
+    OUT PVOID Buffer)
+{
+    UCHAR AtapiPacket[16];
+    USHORT DataSize;
+    BOOLEAN Success;
+
+    /* Select the drive */
+    if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber))
+        return FALSE;
+
+    /* Disable interrupts */
+    AtaWritePort(DeviceUnit->Channel, IDX_IO2_o_AltStatus, IDE_DC_DISABLE_INTERRUPTS);
+    StallExecutionProcessor(1);
+
+    /* Send the SCSI READ command */
+    RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
+#if defined(SARCH_PC98)
+    AtapiPacket[0] = SCSIOP_READ;
+    AtapiPacket[8] = 1;
+    AtapiPacket[9] = 0;
+#else
+    AtapiPacket[0] = SCSIOP_READ12;
+    AtapiPacket[8] = 0;
+    AtapiPacket[9] = 1;
+#endif
+    AtapiPacket[2] = (SectorNumber >> 24) & 0xFF;
+    AtapiPacket[3] = (SectorNumber >> 16) & 0xFF;
+    AtapiPacket[4] = (SectorNumber >> 8) & 0xFF;
+    AtapiPacket[5] = SectorNumber & 0xFF;
+    Success = AtaSendAtapiPacket(DeviceUnit->Channel,
+                                 AtapiPacket,
+                                 ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
+                                 DeviceUnit->SectorSize);
+    if (!Success)
+    {
+        ERR("AtapiReadLogicalSectorLBA() failed. A read error occurred.\n");
+        return FALSE;
+    }
+
+    DataSize = (AtaReadPort(DeviceUnit->Channel, IDX_ATAPI_IO1_i_ByteCountHigh) << 8) |
+                AtaReadPort(DeviceUnit->Channel, IDX_ATAPI_IO1_i_ByteCountLow);
+
+    /* Transfer the data block */
+    AtaReadBuffer(DeviceUnit->Channel, Buffer, DataSize);
+
+    return TRUE;
+}
+
+static
+VOID
+AtapiCapacityDetect(
+    IN PDEVICE_UNIT DeviceUnit,
+    OUT PULONGLONG TotalSectors,
+    OUT PULONG SectorSize)
+{
+    UCHAR AtapiPacket[16];
+    UCHAR AtapiCapacity[8];
+
+    /* Send the SCSI READ CAPACITY(10) command */
+    RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
+    AtapiPacket[0] = SCSIOP_READ_CAPACITY;
+    if (AtaSendAtapiPacket(DeviceUnit->Channel, AtapiPacket, ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData), 8))
+    {
+        AtaReadBuffer(DeviceUnit->Channel, &AtapiCapacity, 8);
+
+        *TotalSectors = (AtapiCapacity[0] << 24) | (AtapiCapacity[1] << 16) |
+                        (AtapiCapacity[2] << 8) | AtapiCapacity[3];
+
+        *SectorSize = (AtapiCapacity[4] << 24) | (AtapiCapacity[5] << 16) |
+                      (AtapiCapacity[6] << 8) | AtapiCapacity[7];
+    }
+    else
+    {
+        *TotalSectors = 0;
+        *SectorSize = 0;
+    }
+}
+
+static
+BOOLEAN
+AtapiReadyCheck(IN OUT PDEVICE_UNIT DeviceUnit)
+{
+    UCHAR AtapiPacket[16];
+    UCHAR DummyData[MAXIMUM_CDROM_SIZE];
+    SENSE_DATA SenseData;
+    BOOLEAN Success;
+
+    /* Select the drive */
+    if (!SelectDevice(DeviceUnit->Channel, DeviceUnit->DeviceNumber))
+        return FALSE;
+
+    /* Send the SCSI TEST UNIT READY command */
+    RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
+    AtapiPacket[0] = SCSIOP_TEST_UNIT_READY;
+    AtaSendAtapiPacket(DeviceUnit->Channel,
+                       AtapiPacket,
+                       ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
+                       0);
+
+    /* Send the SCSI REQUEST SENSE command */
+    RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
+    RtlZeroMemory(&SenseData, SENSE_BUFFER_SIZE);
+    AtapiPacket[0] = SCSIOP_REQUEST_SENSE;
+    AtapiPacket[4] = SENSE_BUFFER_SIZE;
+    Success = AtaSendAtapiPacket(DeviceUnit->Channel,
+                                 AtapiPacket,
+                                 ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
+                                 SENSE_BUFFER_SIZE);
+    if (!Success)
+        return FALSE;
+
+    AtaReadBuffer(DeviceUnit->Channel, &SenseData, SENSE_BUFFER_SIZE);
+    TRACE("SK 0x%x, ASC 0x%x, ASCQ 0x%x\n",
+          SenseData.SenseKey,
+          SenseData.AdditionalSenseCode,
+          SenseData.AdditionalSenseCodeQualifier);
+
+    if (SenseData.SenseKey == SCSI_SENSE_NOT_READY)
+    {
+        if (SenseData.AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)
+        {
+            switch (SenseData.AdditionalSenseCodeQualifier)
+            {
+                case SCSI_SENSEQ_BECOMING_READY:
+                    /* Wait until the CD is spun up */
+                    StallExecutionProcessor(4e6);
+                    return FALSE;
+
+                case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
+                    /* The drive needs to be spun up, send the SCSI READ TOC command */
+                    RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
+                    AtapiPacket[0] = SCSIOP_READ_TOC;
+                    AtapiPacket[7] = (MAXIMUM_CDROM_SIZE << 8) & 0xFF;
+                    AtapiPacket[8] = MAXIMUM_CDROM_SIZE & 0xFF;
+                    AtapiPacket[9] = READ_TOC_FORMAT_SESSION << 6;
+                    Success = AtaSendAtapiPacket(DeviceUnit->Channel,
+                                                 AtapiPacket,
+                                                 ATAPI_PACKET_SIZE(DeviceUnit->IdentifyData),
+                                                 MAXIMUM_CDROM_SIZE);
+                    if (!Success)
+                        return FALSE;
+
+                    AtaReadBuffer(DeviceUnit->Channel, &DummyData, MAXIMUM_CDROM_SIZE);
+                    /* fall through */
+
+                default:
+                    DeviceUnit->Flags &= ~ATA_DEVICE_NOT_READY;
+                    return FALSE;
+
+            }
+        }
+        else if (SenseData.AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
+        {
+            DeviceUnit->Flags |= ATA_DEVICE_NO_MEDIA;
+            return FALSE;
+        }
+    }
+    else
+    {
+        DeviceUnit->Flags &= ~ATA_DEVICE_NOT_READY;
+    }
+
+    if (DeviceUnit->Flags & ATA_DEVICE_NO_MEDIA)
+    {
+        /* Detect a medium's capacity */
+        AtapiCapacityDetect(DeviceUnit, &DeviceUnit->TotalSectors, &DeviceUnit->SectorSize);
+
+        /* If nothing was returned, reset to defaults */
+        if (DeviceUnit->SectorSize == 0)
+            DeviceUnit->SectorSize = 2048;
+        if (DeviceUnit->TotalSectors == 0)
+            DeviceUnit->TotalSectors = 0xFFFFFFFF;
+
+        DeviceUnit->Flags &= ~ATA_DEVICE_NO_MEDIA;
+    }
+
+    return TRUE;
+}
+
+static
+BOOLEAN
+WaitForFlags(
+    IN UCHAR Channel,
+    IN UCHAR Flags,
+    IN UCHAR ExpectedValue,
+    IN ULONG Timeout)
+{
+    ASSERT(Timeout != 0);
+
+    while (Timeout--)
+    {
+        if ((AtaReadPort(Channel, IDX_IO1_i_Status) & Flags) == ExpectedValue)
+            return TRUE;
+        else
+            StallExecutionProcessor(10);
+    }
+    return FALSE;
+}
+
+static
+VOID
+AtaHardReset(IN UCHAR Channel)
+{
+    TRACE("AtaHardReset(Controller %d)\n", Channel);
+
+    AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_RESET_CONTROLLER);
+    StallExecutionProcessor(200000);
+    AtaWritePort(Channel, IDX_IO2_o_Control, IDE_DC_REENABLE_CONTROLLER);
+    StallExecutionProcessor(1);
+}
+
+static
+BOOLEAN
+SelectDevice(IN UCHAR Channel, IN UCHAR DeviceNumber)
+{
+#if defined(SARCH_PC98)
+    /* Select IDE Channel */
+    WRITE_PORT_UCHAR((PUCHAR)IDE_IO_o_BankSelect, Channel);
+    StallExecutionProcessor(1);
+#endif
+
+    AtaWritePort(Channel, IDX_IO1_o_DriveSelect,
+                 DeviceNumber ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1);
+    StallExecutionProcessor(500);
+    if (!WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ), 0, ATA_STATUS_TIMEOUT))
+    {
+        TRACE("SelectDevice() failed. Device(%d:%d) is busy.\n", Channel, DeviceNumber);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static
+BOOLEAN
+IdentifyDevice(
+    IN UCHAR Channel,
+    IN UCHAR DeviceNumber,
+    OUT PDEVICE_UNIT *DeviceUnit)
+{
+    UCHAR SignatureLow, SignatureHigh;
+    IDENTIFY_DATA Id;
+    INQUIRYDATA AtapiInquiry;
+    BOOLEAN Success;
+    UCHAR AtapiPacket[16];
+    ULONG i;
+    ULONG SectorSize;
+    ULONGLONG TotalSectors;
+    USHORT Flags = 0;
+
+    TRACE("IdentifyDevice() Channel = %x, Device = %x, BaseIoAddress = 0x%x\n",
+          Channel, DeviceNumber, BaseArray[Channel]);
+
+    /* Reset the controller */
+    AtaHardReset(Channel);
+
+    /* Select the drive */
+    if (!SelectDevice(Channel, DeviceNumber))
+        goto Failure;
+
+    /* Send the IDENTIFY DEVICE command */
+    AtaWritePort(Channel, IDX_IO1_o_Command, IDE_COMMAND_IDENTIFY);
+    StallExecutionProcessor(50);
+    if (WaitForFlags(Channel, (IDE_STATUS_BUSY | IDE_STATUS_DRQ | IDE_STATUS_ERROR),
+                     IDE_STATUS_DRQ, ATA_STATUS_TIMEOUT))
+    {
+        SignatureLow = AtaReadPort(Channel, IDX_IO1_i_CylinderLow);
+        SignatureHigh = AtaReadPort(Channel, IDX_IO1_i_CylinderHigh);
+        TRACE("IdentifyDevice(): SignatureLow = 0x%x, SignatureHigh = 0x%x\n", SignatureLow, SignatureHigh);
+        if (SignatureLow == 0x00 && SignatureHigh == 0x00)
+        {
+            /* This is PATA */
+            TRACE("IdentifyDevice(): Found PATA device at %d:%d\n", Channel, DeviceNumber);
+            goto Identify;
+        }
+    }
+
+    /* If not PATA, ATAPI maybe? Send the IDENTIFY PACKET DEVICE command */
+    AtaWritePort(Channel, IDX_ATAPI_IO1_o_Command, IDE_COMMAND_ATAPI_IDENTIFY);
+    StallExecutionProcessor(500);
+    SignatureLow = AtaReadPort(Channel, IDX_IO1_i_CylinderLow);
+    SignatureHigh = AtaReadPort(Channel, IDX_IO1_i_CylinderHigh);
+    TRACE("IdentifyDevice(): SignatureLow = 0x%x, SignatureHigh = 0x%x\n", SignatureLow, SignatureHigh);
+    /* Check for devices that implements the PACKET Command feature */
+    if (SignatureLow == ATAPI_MAGIC_LSB && SignatureHigh == ATAPI_MAGIC_MSB)
+    {
+        /* This is ATAPI */
+        Flags |= ATA_DEVICE_ATAPI | ATA_DEVICE_LBA | ATA_DEVICE_NOT_READY;
+        TRACE("IdentifyDevice(): Found ATAPI device at %d:%d\n", Channel, DeviceNumber);
+        goto Identify;
+    }
+    else
+    {
+        goto Failure;
+    }
+
+Identify:
+    /* Receive parameter information from the device */
+    AtaReadBuffer(Channel, &Id, IDENTIFY_DATA_SIZE);
+
+    /* Swap byte order of the ASCII data */
+    for (i = 0; i < RTL_NUMBER_OF(Id.SerialNumber); ++i)
+        Id.SerialNumber[i] = RtlUshortByteSwap(Id.SerialNumber[i]);
+
+    for (i = 0; i < RTL_NUMBER_OF(Id.FirmwareRevision); ++i)
+        Id.FirmwareRevision[i] = RtlUshortByteSwap(Id.FirmwareRevision[i]);
+
+    for (i = 0; i < RTL_NUMBER_OF(Id.ModelNumber); ++i)
+        Id.ModelNumber[i] = RtlUshortByteSwap(Id.ModelNumber[i]);
+
+    TRACE("S/N %.*s\n", sizeof(Id.SerialNumber), Id.SerialNumber);
+    TRACE("FR %.*s\n", sizeof(Id.FirmwareRevision), Id.FirmwareRevision);
+    TRACE("MN %.*s\n", sizeof(Id.ModelNumber), Id.ModelNumber);
+
+    /* Detect the type of device */
+    if (Flags & ATA_DEVICE_ATAPI)
+    {
+        /* Send the SCSI INQUIRY command */
+        RtlZeroMemory(&AtapiPacket, sizeof(AtapiPacket));
+        AtapiPacket[0] = SCSIOP_INQUIRY;
+        AtapiPacket[4] = INQUIRYDATABUFFERSIZE;
+        Success = AtaSendAtapiPacket(Channel, AtapiPacket, ATAPI_PACKET_SIZE(Id), INQUIRYDATABUFFERSIZE);
+        if (!Success)
+            goto Failure;
+
+        AtaReadBuffer(Channel, &AtapiInquiry, INQUIRYDATABUFFERSIZE);
+        if (AtapiInquiry.DeviceType != READ_ONLY_DIRECT_ACCESS_DEVICE)
+        {
+            TRACE("IdentifyDevice(): Unsupported device type 0x%x!\n", AtapiInquiry.DeviceType);
+            goto Failure;
+        }
+    }
+
+    /* Allocate a new device unit structure */
+    *DeviceUnit = FrLdrTempAlloc(sizeof(DEVICE_UNIT), TAG_ATA_DEVICE);
+    if (*DeviceUnit == NULL)
+    {
+        ERR("Failed to allocate device unit!\n");
+        return FALSE;
+    }
+
+    RtlZeroMemory(*DeviceUnit, sizeof(DEVICE_UNIT));
+    (*DeviceUnit)->Channel = Channel;
+    (*DeviceUnit)->DeviceNumber = DeviceNumber;
+    (*DeviceUnit)->IdentifyData = Id;
+
+    /* Detect a medium's capacity */
+    if (Flags & ATA_DEVICE_ATAPI)
+    {
+        AtapiCapacityDetect(*DeviceUnit, &TotalSectors, &SectorSize);
+        if (SectorSize == 0 || TotalSectors == 0)
+        {
+            /* It's ok and can be used to show alert like "Please insert the CD" */
+            TRACE("No media found.\n");
+            Flags |= ATA_DEVICE_NO_MEDIA;
+        }
+    }
+    else
+    {
+        if (Id.SupportLba || (Id.MajorRevision && Id.UserAddressableSectors))
+        {
+            if (Id.FeaturesSupport.Address48)
+            {
+                TRACE("Using LBA48 addressing mode.\n");
+                Flags |= ATA_DEVICE_LBA48 | ATA_DEVICE_LBA;
+                TotalSectors = Id.UserAddressableSectors48;
+            }
+            else
+            {
+                TRACE("Using LBA28 addressing mode.\n");
+                Flags |= ATA_DEVICE_LBA;
+                TotalSectors = Id.UserAddressableSectors;
+            }
+
+            /* LBA ATA drives always have a sector size of 512 */
+            SectorSize = 512;
+        }
+        else
+        {
+            TRACE("Using CHS addressing mode.\n");
+            Flags |= ATA_DEVICE_CHS;
+
+            if (Id.UnformattedBytesPerSector == 0)
+            {
+                SectorSize = 512;
+            }
+            else
+            {
+                for (i = 1 << 15; i > 0; i >>= 1)
+                {
+                    if ((Id.UnformattedBytesPerSector & i) != 0)
+                    {
+                        SectorSize = i;
+                        break;
+                    }
+                }
+            }
+            TotalSectors = Id.NumberOfCylinders * Id.NumberOfHeads * Id.SectorsPerTrack;
+        }
+    }
+    TRACE("Sector size %d ; Total sectors %I64d\n", SectorSize, TotalSectors);
+
+    (*DeviceUnit)->Flags = Flags;
+    (*DeviceUnit)->SectorSize = SectorSize;
+    (*DeviceUnit)->TotalSectors = TotalSectors;
+    if (Flags & ATA_DEVICE_ATAPI)
+    {
+        (*DeviceUnit)->Cylinders = 0xFFFFFFFF;
+        (*DeviceUnit)->Heads = 0xFFFFFFFF;
+        (*DeviceUnit)->Sectors = 0xFFFFFFFF;
+    }
+    else
+    {
+        (*DeviceUnit)->Cylinders = Id.NumberOfCylinders;
+        (*DeviceUnit)->Heads = Id.NumberOfHeads;
+        (*DeviceUnit)->Sectors = Id.SectorsPerTrack;
+    }
+
+#if DBG
+    DbgDumpBuffer(DPRINT_DISK, &Id, IDENTIFY_DATA_SIZE);
+#endif
+
+    TRACE("IdentifyDevice() done.\n");
+    return TRUE;
+
+Failure:
+    TRACE("IdentifyDevice() done. No device present at %d:%d\n", Channel, DeviceNumber);
+    return FALSE;
+}
index 7b12084..ed99abb 100644 (file)
@@ -299,8 +299,9 @@ XboxMachInit(const char *CmdLine)
 VOID
 XboxPrepareForReactOS(VOID)
 {
-    /* On XBOX, prepare video and turn off the floppy motor */
+    /* On Xbox, prepare video and disk support */
     XboxVideoPrepareForReactOS();
+    XboxDiskInit(FALSE);
     DiskStopFloppyMotor();
 }
 
index e1e4d35..26a00e1 100644 (file)
 /*
- *  FreeLoader
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Note: mostly ripped from atapi.c
- *       Some of this code was based on knowledge and/or code developed
- *       by the Xbox Linux group: http://www.xbox-linux.org
- *
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Xbox specific disk access routines
+ * COPYRIGHT:   Copyright 2004 Gé van Geldorp (gvg@reactos.com)
+ *              Copyright 2019 Dmitry Borisov (di.sean@protonmail.com)
  */
 
+/* INCLUDES *******************************************************************/
+
 #include <freeldr.h>
+#include <hwide.h>
 
 #include <debug.h>
-
 DBG_DEFAULT_CHANNEL(DISK);
 
-#define XBOX_IDE_COMMAND_PORT 0x1f0
-#define XBOX_IDE_CONTROL_PORT 0x170
-
-#define  IDE_SECTOR_BUF_SZ      512
-#define  IDE_MAX_POLL_RETRIES   100000
-#define  IDE_MAX_BUSY_RETRIES   50000
-
-/* Control Block offsets and masks */
-#define  IDE_REG_ALT_STATUS     0x0000
-#define  IDE_REG_DEV_CNTRL      0x0000  /* device control register */
-#define    IDE_DC_SRST            0x04  /* drive reset (both drives) */
-#define    IDE_DC_nIEN            0x02  /* IRQ enable (active low) */
-#define  IDE_REG_DRV_ADDR       0x0001
-
-/* Command Block offsets and masks */
-#define  IDE_REG_DATA_PORT      0x0000
-#define  IDE_REG_ERROR          0x0001  /* error register */
-#define    IDE_ER_AMNF            0x01  /* addr mark not found */
-#define    IDE_ER_TK0NF           0x02  /* track 0 not found */
-#define    IDE_ER_ABRT            0x04  /* command aborted */
-#define    IDE_ER_MCR             0x08  /* media change requested */
-#define    IDE_ER_IDNF            0x10  /* ID not found */
-#define    IDE_ER_MC              0x20  /* Media changed */
-#define    IDE_ER_UNC             0x40  /* Uncorrectable data error */
-#define  IDE_REG_PRECOMP        0x0001
-#define  IDE_REG_SECTOR_CNT     0x0002
-#define  IDE_REG_SECTOR_NUM     0x0003
-#define  IDE_REG_CYL_LOW        0x0004
-#define  IDE_REG_CYL_HIGH       0x0005
-#define  IDE_REG_DRV_HEAD       0x0006
-#define    IDE_DH_FIXED           0xA0
-#define    IDE_DH_LBA             0x40
-#define    IDE_DH_HDMASK          0x0F
-#define    IDE_DH_DRV0            0x00
-#define    IDE_DH_DRV1            0x10
-#define  IDE_REG_STATUS           0x0007
-#define    IDE_SR_BUSY              0x80
-#define    IDE_SR_DRDY              0x40
-#define    IDE_SR_WERR              0x20
-#define    IDE_SR_DRQ               0x08
-#define    IDE_SR_ERR               0x01
-#define  IDE_REG_COMMAND          0x0007
-
-/* IDE/ATA commands */
-#define    IDE_CMD_RESET            0x08
-#define    IDE_CMD_READ             0x20
-#define    IDE_CMD_READ_RETRY       0x21
-#define    IDE_CMD_WRITE            0x30
-#define    IDE_CMD_WRITE_RETRY      0x31
-#define    IDE_CMD_PACKET           0xA0
-#define    IDE_CMD_READ_MULTIPLE    0xC4
-#define    IDE_CMD_WRITE_MULTIPLE   0xC5
-#define    IDE_CMD_READ_DMA         0xC8
-#define    IDE_CMD_WRITE_DMA        0xCA
-#define    IDE_CMD_FLUSH_CACHE      0xE7
-#define    IDE_CMD_FLUSH_CACHE_EXT  0xEA
-#define    IDE_CMD_IDENT_ATA_DRV    0xEC
-#define    IDE_CMD_IDENT_ATAPI_DRV  0xA1
-#define    IDE_CMD_GET_MEDIA_STATUS 0xDA
-
-/*
- *  Access macros for command registers
- *  Each macro takes an address of the command port block, and data
- */
-#define IDEReadError(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ERROR)))
-#define IDEWritePrecomp(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_PRECOMP), (Data)))
-#define IDEReadSectorCount(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT)))
-#define IDEWriteSectorCount(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT), (Data)))
-#define IDEReadSectorNum(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM)))
-#define IDEWriteSectorNum(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM), (Data)))
-#define IDEReadCylinderLow(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW)))
-#define IDEWriteCylinderLow(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW), (Data)))
-#define IDEReadCylinderHigh(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH)))
-#define IDEWriteCylinderHigh(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH), (Data)))
-#define IDEReadDriveHead(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD)))
-#define IDEWriteDriveHead(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD), (Data)))
-#define IDEReadStatus(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_STATUS)))
-#define IDEWriteCommand(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_COMMAND), (Data)))
-#define IDEReadDMACommand(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address))))
-#define IDEWriteDMACommand(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address)), (Data)))
-#define IDEReadDMAStatus(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + 2)))
-#define IDEWriteDMAStatus(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + 2), (Data)))
-#define IDEWritePRDTable(Address, Data) \
-    (WRITE_PORT_ULONG((PULONG)((Address) + 4), (Data)))
-
-/*
- *  Data block read and write commands
- */
-#define IDEReadBlock(Address, Buffer, Count) \
-    (READ_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2))
-#define IDEWriteBlock(Address, Buffer, Count) \
-    (WRITE_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2))
-
-#define IDEReadBlock32(Address, Buffer, Count) \
-    (READ_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4))
-#define IDEWriteBlock32(Address, Buffer, Count) \
-    (WRITE_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4))
+/* GLOBALS ********************************************************************/
 
-#define IDEReadWord(Address) \
-    (READ_PORT_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT)))
+static PDEVICE_UNIT HardDrive = NULL;
+static PDEVICE_UNIT CdDrive = NULL;
+static BOOLEAN AtaInitialized = FALSE;
 
-/*
- *  Access macros for control registers
- *  Each macro takes an address of the control port blank and data
- */
-#define IDEReadAltStatus(Address) \
-    (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ALT_STATUS)))
-#define IDEWriteDriveControl(Address, Data) \
-    (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DEV_CNTRL), (Data)))
+/* FUNCTIONS ******************************************************************/
 
-/* IDE_DRIVE_IDENTIFY */
-typedef struct _IDE_DRIVE_IDENTIFY
+VOID
+XboxDiskInit(BOOLEAN Init)
 {
-    USHORT   ConfigBits;          /*00*/
-    USHORT   LogicalCyls;         /*01*/
-    USHORT   Reserved02;          /*02*/
-    USHORT   LogicalHeads;        /*03*/
-    USHORT   BytesPerTrack;       /*04*/
-    USHORT   BytesPerSector;      /*05*/
-    USHORT   SectorsPerTrack;     /*06*/
-    UCHAR    InterSectorGap;      /*07*/
-    UCHAR    InterSectorGapSize;
-    UCHAR    Reserved08H;         /*08*/
-    UCHAR    BytesInPLO;
-    USHORT   VendorUniqueCnt;     /*09*/
-    UCHAR    SerialNumber[20];    /*10*/
-    USHORT   ControllerType;      /*20*/
-    USHORT   BufferSize;          /*21*/
-    USHORT   ECCByteCnt;          /*22*/
-    UCHAR    FirmwareRev[8];      /*23*/
-    UCHAR    ModelNumber[40];     /*27*/
-    USHORT   RWMultImplemented;   /*47*/
-    USHORT   DWordIo;             /*48*/
-    USHORT   Capabilities;        /*49*/
-#define IDE_DRID_STBY_SUPPORTED   0x2000
-#define IDE_DRID_IORDY_SUPPORTED  0x0800
-#define IDE_DRID_IORDY_DISABLE    0x0400
-#define IDE_DRID_LBA_SUPPORTED    0x0200
-#define IDE_DRID_DMA_SUPPORTED    0x0100
-    USHORT   Reserved50;          /*50*/
-    USHORT   MinPIOTransTime;     /*51*/
-    USHORT   MinDMATransTime;     /*52*/
-    USHORT   TMFieldsValid;       /*53*/
-    USHORT   TMCylinders;         /*54*/
-    USHORT   TMHeads;             /*55*/
-    USHORT   TMSectorsPerTrk;     /*56*/
-    USHORT   TMCapacityLo;        /*57*/
-    USHORT   TMCapacityHi;        /*58*/
-    USHORT   RWMultCurrent;       /*59*/
-    USHORT   TMSectorCountLo;     /*60*/
-    USHORT   TMSectorCountHi;     /*61*/
-    USHORT   DmaModes;            /*62*/
-    USHORT   MultiDmaModes;       /*63*/
-    USHORT   Reserved64[5];       /*64*/
-    USHORT   Reserved69[2];       /*69*/
-    USHORT   Reserved71[4];       /*71*/
-    USHORT   MaxQueueDepth;       /*75*/
-    USHORT   Reserved76[4];       /*76*/
-    USHORT   MajorRevision;       /*80*/
-    USHORT   MinorRevision;       /*81*/
-    USHORT   SupportedFeatures82; /*82*/
-    USHORT   SupportedFeatures83; /*83*/
-    USHORT   SupportedFeatures84; /*84*/
-    USHORT   EnabledFeatures85;   /*85*/
-    USHORT   EnabledFeatures86;   /*86*/
-    USHORT   EnabledFeatures87;   /*87*/
-    USHORT   UltraDmaModes;       /*88*/
-    USHORT   Reserved89[11];      /*89*/
-    USHORT   Max48BitAddress[4];  /*100*/
-    USHORT   Reserved104[151];    /*104*/
-    USHORT   Checksum;            /*255*/
-} IDE_DRIVE_IDENTIFY, *PIDE_DRIVE_IDENTIFY;
-
-/*  XboxDiskPolledRead
- *
- *  DESCRIPTION:
- *    Read a sector of data from the drive in a polled fashion.
- *
- *  RUN LEVEL:
- *    PASSIVE_LEVEL
- *
- *  ARGUMENTS:
- *    ULONG CommandPort     Address of command port for drive
- *    ULONG ControlPort     Address of control port for drive
- *    UCHAR PreComp         Value to write to precomp register
- *    UCHAR SectorCnt       Value to write to sectorCnt register
- *    UCHAR SectorNum       Value to write to sectorNum register
- *    UCHAR CylinderLow     Value to write to CylinderLow register
- *    UCHAR CylinderHigh    Value to write to CylinderHigh register
- *    UCHAR DrvHead         Value to write to Drive/Head register
- *    UCHAR Command         Value to write to Command register
- *    PVOID Buffer          Buffer for output data
- *
- *  RETURNS:
- *    BOOLEAN: TRUE success, FALSE error
- */
-static BOOLEAN
-XboxDiskPolledRead(ULONG CommandPort,
-                   ULONG ControlPort,
-                   UCHAR PreComp,
-                   UCHAR SectorCnt,
-                   UCHAR SectorNum,
-                   UCHAR CylinderLow,
-                   UCHAR CylinderHigh,
-                   UCHAR DrvHead,
-                   UCHAR Command,
-                   PVOID Buffer)
-{
-    ULONG SectorCount = 0;
-    ULONG RetryCount;
-    BOOLEAN Junk = FALSE;
-    UCHAR Status;
-
-    /* Wait for BUSY to clear */
-    for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++)
-    {
-        Status = IDEReadStatus(CommandPort);
-        if (!(Status & IDE_SR_BUSY))
-            break;
-
-        StallExecutionProcessor(10);
-    }
-    TRACE("status=0x%x\n", Status);
-    TRACE("waited %d usecs for busy to clear\n", RetryCount * 10);
-    if (RetryCount >= IDE_MAX_BUSY_RETRIES)
-    {
-        WARN("Drive is BUSY for too long\n");
-        return FALSE;
-    }
-
-    /* Write Drive/Head to select drive */
-    IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead);
-    StallExecutionProcessor(500);
-
-    /* Disable interrupts */
-    IDEWriteDriveControl(ControlPort, IDE_DC_nIEN);
-    StallExecutionProcessor(500);
-
-    /* Issue command to drive */
-    if (DrvHead & IDE_DH_LBA)
-    {
-        TRACE("READ:DRV=%d:LBA=1:BLK=%d:SC=0x%x:CM=0x%x\n",
-              DrvHead & IDE_DH_DRV1 ? 1 : 0,
-              ((DrvHead & 0x0f) << 24) + (CylinderHigh << 16) + (CylinderLow << 8) + SectorNum,
-              SectorCnt,
-              Command);
-    }
-    else
-    {
-        TRACE("READ:DRV=%d:LBA=0:CH=0x%x:CL=0x%x:HD=0x%x:SN=0x%x:SC=0x%x:CM=0x%x\n",
-              DrvHead & IDE_DH_DRV1 ? 1 : 0,
-              CylinderHigh,
-              CylinderLow,
-              DrvHead & 0x0f,
-              SectorNum,
-              SectorCnt,
-              Command);
-    }
-
-    /* Setup command parameters */
-    IDEWritePrecomp(CommandPort, PreComp);
-    IDEWriteSectorCount(CommandPort, SectorCnt);
-    IDEWriteSectorNum(CommandPort, SectorNum);
-    IDEWriteCylinderHigh(CommandPort, CylinderHigh);
-    IDEWriteCylinderLow(CommandPort, CylinderLow);
-    IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead);
-
-    /* Issue the command */
-    IDEWriteCommand(CommandPort, Command);
-    StallExecutionProcessor(50);
-
-    /* Wait for DRQ or error */
-    for (RetryCount = 0; RetryCount < IDE_MAX_POLL_RETRIES; RetryCount++)
-    {
-        Status = IDEReadStatus(CommandPort);
-        if (!(Status & IDE_SR_BUSY))
-        {
-            if (Status & IDE_SR_ERR)
-            {
-                IDEWriteDriveControl(ControlPort, 0);
-                StallExecutionProcessor(50);
-                IDEReadStatus(CommandPort);
-                return FALSE;
-            }
-
-            if (Status & IDE_SR_DRQ)
-            {
-                break;
-            }
-            else
-            {
-                IDEWriteDriveControl(ControlPort, 0);
-                StallExecutionProcessor(50);
-                IDEReadStatus(CommandPort);
-                return FALSE;
-            }
-        }
-        StallExecutionProcessor(10);
-    }
-
-    /* Timed out */
-    if (RetryCount >= IDE_MAX_POLL_RETRIES)
-    {
-        IDEWriteDriveControl(ControlPort, 0);
-        StallExecutionProcessor(50);
-        IDEReadStatus(CommandPort);
-        return FALSE;
-    }
+    UCHAR DetectedCount;
+    UCHAR UnitNumber;
+    PDEVICE_UNIT DeviceUnit = NULL;
 
-    while (1)
+    if (Init & !AtaInitialized)
     {
-        /* Read data into buffer */
-        if (Junk == FALSE)
+        /* Find first HDD and CD */
+        AtaInit(&DetectedCount);
+        for (UnitNumber = 0; UnitNumber <= DetectedCount; UnitNumber++)
         {
-            IDEReadBlock(CommandPort, Buffer, IDE_SECTOR_BUF_SZ);
-            Buffer = (PVOID)((ULONG_PTR)Buffer + IDE_SECTOR_BUF_SZ);
-        }
-        else
-        {
-            UCHAR JunkBuffer[IDE_SECTOR_BUF_SZ];
-            IDEReadBlock(CommandPort, JunkBuffer, IDE_SECTOR_BUF_SZ);
-        }
-        SectorCount++;
-
-        /* Check for error or more sectors to read */
-        for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++)
-        {
-            Status = IDEReadStatus(CommandPort);
-            if (!(Status & IDE_SR_BUSY))
+            DeviceUnit = AtaGetDevice(UnitNumber);
+            if (DeviceUnit)
             {
-                if (Status & IDE_SR_ERR)
-                {
-                    IDEWriteDriveControl(ControlPort, 0);
-                    StallExecutionProcessor(50);
-                    IDEReadStatus(CommandPort);
-                    return FALSE;
-                }
-                if (Status & IDE_SR_DRQ)
+                if (DeviceUnit->Flags & ATA_DEVICE_ATAPI)
                 {
-                    if (SectorCount >= SectorCnt)
-                    {
-                        TRACE("Buffer size exceeded!\n");
-                        Junk = TRUE;
-                    }
-                    break;
+                    if (!CdDrive)
+                        CdDrive = DeviceUnit;
                 }
                 else
                 {
-                    if (SectorCount > SectorCnt)
-                    {
-                        TRACE("Read %lu sectors of junk!\n",
-                              SectorCount - SectorCnt);
-                    }
-
-                    IDEWriteDriveControl(ControlPort, 0);
-                    StallExecutionProcessor(50);
-                    IDEReadStatus(CommandPort);
-                    return TRUE;
+                    if (!HardDrive)
+                        HardDrive = DeviceUnit;
                 }
             }
         }
+        AtaInitialized = TRUE;
+    }
+    else
+    {
+        AtaFree();
     }
 }
 
+static
+inline
 BOOLEAN
-XboxDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
+XboxDiskDriveNumberToDeviceUnit(UCHAR DriveNumber, PDEVICE_UNIT *DeviceUnit)
 {
-    ULONG StartSector;
-    UCHAR Count;
-
-    if (DriveNumber < 0x80 || (DriveNumber & 0x0f) >= 2)
-    {
-        /* Xbox has only 1 IDE controller and no floppy */
-        WARN("Invalid drive number\n");
+    /* Xbox has only 1 IDE controller and no floppy */
+    if (DriveNumber < 0x80 || (DriveNumber & 0x0F) >= 2)
         return FALSE;
-    }
 
-    if (((SectorNumber + SectorCount) & UINT64_C(0xfffffffff0000000)) != UINT64_C(0))
+    if (!AtaInitialized)
+        XboxDiskInit(TRUE);
+
+    /* HDD */
+    if ((DriveNumber == 0x80) && HardDrive)
     {
-        FIXME("48bit LBA required but not implemented\n");
-        return FALSE;
+        *DeviceUnit = HardDrive;
+        return TRUE;
     }
 
-    StartSector = (ULONG) SectorNumber;
-    while (SectorCount > 0)
+    /* CD */
+    if ((DriveNumber & 0xF0) > 0x80 && CdDrive)
     {
-        Count = (SectorCount <= 255 ? (UCHAR)SectorCount : 255);
-        if (!XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT,
-                                XBOX_IDE_CONTROL_PORT,
-                                0, Count,
-                                StartSector & 0xff,
-                                (StartSector >> 8) & 0xff,
-                                (StartSector >> 16) & 0xff,
-                                ((StartSector >> 24) & 0x0f) | IDE_DH_LBA |
-                                ((DriveNumber & 0x0f) == 0 ? IDE_DH_DRV0 : IDE_DH_DRV1),
-                                IDE_CMD_READ,
-                                Buffer))
-        {
-            return FALSE;
-        }
-        SectorCount -= Count;
-        Buffer = (PVOID) ((PCHAR) Buffer + Count * IDE_SECTOR_BUF_SZ);
+        *DeviceUnit = CdDrive;
+        return TRUE;
     }
 
-    return TRUE;
+    return FALSE;
 }
 
 BOOLEAN
-XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
+XboxDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
 {
-    IDE_DRIVE_IDENTIFY DrvParms;
-    ULONG i;
-    BOOLEAN Atapi;
+    PDEVICE_UNIT DeviceUnit = NULL;
 
-    Atapi = FALSE; /* FIXME */
-    /* Get the Drive Identify block from drive or die */
-    if (!XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT,
-                            XBOX_IDE_CONTROL_PORT,
-                            0,
-                            1,
-                            0,
-                            0,
-                            0,
-                            ((DriveNumber & 0x0f) == 0 ? IDE_DH_DRV0 : IDE_DH_DRV1),
-                            (Atapi ? IDE_CMD_IDENT_ATAPI_DRV : IDE_CMD_IDENT_ATA_DRV),
-                            (PUCHAR) &DrvParms))
-    {
-        ERR("XboxDiskPolledRead() failed\n");
+    TRACE("XboxDiskReadLogicalSectors() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n",
+          DriveNumber, SectorNumber, SectorCount, Buffer);
+
+    if (!XboxDiskDriveNumberToDeviceUnit(DriveNumber, &DeviceUnit))
         return FALSE;
-    }
 
-    Geometry->Cylinders = DrvParms.LogicalCyls;
-    Geometry->Heads = DrvParms.LogicalHeads;
-    Geometry->Sectors = DrvParms.SectorsPerTrack;
+    return AtaAtapiReadLogicalSectorsLBA(DeviceUnit, SectorNumber, SectorCount, Buffer);
+}
 
-    if (!Atapi && (DrvParms.Capabilities & IDE_DRID_LBA_SUPPORTED) != 0)
-    {
-        /* LBA ATA drives always have a sector size of 512 */
-        Geometry->BytesPerSector = 512;
-    }
-    else
-    {
-        TRACE("BytesPerSector %d\n", DrvParms.BytesPerSector);
-        if (DrvParms.BytesPerSector == 0)
-        {
-            Geometry->BytesPerSector = 512;
-        }
-        else
-        {
-            for (i = 1 << 15; i; i /= 2)
-            {
-                if ((DrvParms.BytesPerSector & i) != 0)
-                {
-                    Geometry->BytesPerSector = i;
-                    break;
-                }
-            }
-        }
-    }
-    TRACE("Cylinders %d\n", Geometry->Cylinders);
-    TRACE("Heads %d\n", Geometry->Heads);
-    TRACE("Sectors %d\n", Geometry->Sectors);
-    TRACE("BytesPerSector %d\n", Geometry->BytesPerSector);
+BOOLEAN
+XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
+{
+    PDEVICE_UNIT DeviceUnit = NULL;
+
+    TRACE("XboxDiskGetDriveGeometry(0x%x)\n", DriveNumber);
+
+    if (!XboxDiskDriveNumberToDeviceUnit(DriveNumber, &DeviceUnit))
+        return FALSE;
+
+    Geometry->Cylinders = DeviceUnit->Cylinders;
+    Geometry->Heads = DeviceUnit->Heads;
+    Geometry->Sectors = DeviceUnit->Sectors;
+    Geometry->BytesPerSector = DeviceUnit->SectorSize;
 
     return TRUE;
 }
@@ -515,8 +122,19 @@ XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
 ULONG
 XboxDiskGetCacheableBlockCount(UCHAR DriveNumber)
 {
-    /* 64 seems a nice number, it is used by the machpc code for LBA devices */
-    return 64;
-}
+    PDEVICE_UNIT DeviceUnit = NULL;
+
+    TRACE("XboxDiskGetCacheableBlockCount(0x%x)\n", DriveNumber);
 
-/* EOF */
+    if (!XboxDiskDriveNumberToDeviceUnit(DriveNumber, &DeviceUnit))
+        return 0;
+
+    /*
+     * If LBA is supported then the block size will be 64 sectors (32k)
+     * If not then the block size is the size of one track.
+     */
+    if (DeviceUnit->Flags & ATA_DEVICE_LBA)
+        return 64;
+    else
+        return DeviceUnit->Sectors;
+}
index 31f8f7e..4b85e9b 100644 (file)
@@ -74,6 +74,7 @@ VOID XboxPrepareForReactOS(VOID);
 VOID XboxMemInit(VOID);
 PFREELDR_MEMORY_DESCRIPTOR XboxMemGetMemoryMap(ULONG *MemoryMapSize);
 
+VOID XboxDiskInit(BOOLEAN Init);
 BOOLEAN XboxDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer);
 BOOLEAN XboxDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY DriveGeometry);
 ULONG XboxDiskGetCacheableBlockCount(UCHAR DriveNumber);
diff --git a/boot/freeldr/freeldr/include/hwide.h b/boot/freeldr/freeldr/include/hwide.h
new file mode 100644 (file)
index 0000000..04f9f2d
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     ATA/ATAPI polled I/O driver header file.
+ * COPYRIGHT:   Copyright 2019 Dmitry Borisov (di.sean@protonmail.com)
+ */
+
+/* GLOBALS ********************************************************************/
+
+/* Some definitions were taken from UniATA driver by Alter */
+
+/*
+ * IDE registers offsets
+ */
+#if defined(SARCH_PC98)
+#define IDX_IO1_i_Data          0x00
+#define IDX_IO1_i_Error         0x02
+#define IDX_IO1_i_BlockCount    0x04
+#define IDX_IO1_i_BlockNumber   0x06
+#define IDX_IO1_i_CylinderLow   0x08
+#define IDX_IO1_i_CylinderHigh  0x0A
+#define IDX_IO1_i_DriveSelect   0x0C
+#define IDX_IO1_i_Status        0x0E
+
+#define IDX_IO2_i_AltStatus     0x10C
+#define IDX_IO2_i_DriveAddress  0x10E
+#define IDE_IO_i_Bank           0x432
+
+#define IDX_IO1_o_Data          0x00
+#define IDX_IO1_o_Feature       0x02
+#define IDX_IO1_o_BlockCount    0x04
+#define IDX_IO1_o_BlockNumber   0x06
+#define IDX_IO1_o_CylinderLow   0x08
+#define IDX_IO1_o_CylinderHigh  0x0A
+#define IDX_IO1_o_DriveSelect   0x0C
+#define IDX_IO1_o_Command       0x0E
+
+#define IDX_IO2_o_AltStatus     0x10C
+#define IDX_IO2_o_Control       0x10E
+#define IDE_IO_o_BankSelect     0x432
+#else /* SARCH_PC98 */
+#define IDX_IO1_i_Data          0x00
+#define IDX_IO1_i_Error         0x01
+#define IDX_IO1_i_BlockCount    0x02
+#define IDX_IO1_i_BlockNumber   0x03
+#define IDX_IO1_i_CylinderLow   0x04
+#define IDX_IO1_i_CylinderHigh  0x05
+#define IDX_IO1_i_DriveSelect   0x06
+#define IDX_IO1_i_Status        0x07
+
+#define IDX_IO2_i_AltStatus     0x206
+#define IDX_IO2_i_DriveAddress  0x207
+
+#define IDX_IO1_o_Data          0x00
+#define IDX_IO1_o_Feature       0x01
+#define IDX_IO1_o_BlockCount    0x02
+#define IDX_IO1_o_BlockNumber   0x03
+#define IDX_IO1_o_CylinderLow   0x04
+#define IDX_IO1_o_CylinderHigh  0x05
+#define IDX_IO1_o_DriveSelect   0x06
+#define IDX_IO1_o_Command       0x07
+
+#define IDX_IO2_o_AltStatus     0x206
+#define IDX_IO2_o_Control       0x207
+#endif
+
+/*
+ * ATAPI registers offsets
+ */
+#if defined(SARCH_PC98)
+#define IDX_ATAPI_IO1_i_Data              0x00
+#define IDX_ATAPI_IO1_i_Error             0x02
+#define IDX_ATAPI_IO1_i_InterruptReason   0x04
+#define IDX_ATAPI_IO1_i_Unused1           0x06
+#define IDX_ATAPI_IO1_i_ByteCountLow      0x08
+#define IDX_ATAPI_IO1_i_ByteCountHigh     0x0A
+#define IDX_ATAPI_IO1_i_DriveSelect       0x0C
+#define IDX_ATAPI_IO1_i_Status            0x0E
+
+#define IDX_ATAPI_IO1_o_Data              0x00
+#define IDX_ATAPI_IO1_o_Feature           0x02
+#define IDX_ATAPI_IO1_o_Unused0           0x04
+#define IDX_ATAPI_IO1_o_Unused1           0x06
+#define IDX_ATAPI_IO1_o_ByteCountLow      0x08
+#define IDX_ATAPI_IO1_o_ByteCountHigh     0x0A
+#define IDX_ATAPI_IO1_o_DriveSelect       0x0C
+#define IDX_ATAPI_IO1_o_Command           0x0E
+#else /* SARCH_PC98 */
+#define IDX_ATAPI_IO1_i_Data              0x00
+#define IDX_ATAPI_IO1_i_Error             0x01
+#define IDX_ATAPI_IO1_i_InterruptReason   0x02
+#define IDX_ATAPI_IO1_i_Unused1           0x03
+#define IDX_ATAPI_IO1_i_ByteCountLow      0x04
+#define IDX_ATAPI_IO1_i_ByteCountHigh     0x05
+#define IDX_ATAPI_IO1_i_DriveSelect       0x06
+#define IDX_ATAPI_IO1_i_Status            0x07
+
+#define IDX_ATAPI_IO1_o_Data              0x00
+#define IDX_ATAPI_IO1_o_Feature           0x01
+#define IDX_ATAPI_IO1_o_Unused0           0x02
+#define IDX_ATAPI_IO1_o_Unused1           0x03
+#define IDX_ATAPI_IO1_o_ByteCountLow      0x04
+#define IDX_ATAPI_IO1_o_ByteCountHigh     0x05
+#define IDX_ATAPI_IO1_o_DriveSelect       0x06
+#define IDX_ATAPI_IO1_o_Command           0x07
+#endif
+
+/*
+ * IDE status definitions
+ */
+#define IDE_STATUS_SUCCESS           0x00
+#define IDE_STATUS_ERROR             0x01
+#define IDE_STATUS_INDEX             0x02
+#define IDE_STATUS_CORRECTED_ERROR   0x04
+#define IDE_STATUS_DRQ               0x08
+#define IDE_STATUS_DSC               0x10
+#define IDE_STATUS_DMA               0x20      /* DMA ready */
+#define IDE_STATUS_DWF               0x20      /* drive write fault */
+#define IDE_STATUS_DRDY              0x40
+#define IDE_STATUS_IDLE              0x50
+#define IDE_STATUS_BUSY              0x80
+
+#define IDE_STATUS_WRONG             0xff
+#define IDE_STATUS_MASK              0xff
+
+/*
+ * IDE drive select/head definitions
+ */
+#define IDE_DRIVE_SELECT             0xA0
+#define IDE_DRIVE_1                  0x00
+#define IDE_DRIVE_2                  0x10
+#define IDE_DRIVE_SELECT_1           (IDE_DRIVE_SELECT | IDE_DRIVE_1)
+#define IDE_DRIVE_SELECT_2           (IDE_DRIVE_SELECT | IDE_DRIVE_2)
+#define IDE_DRIVE_MASK               (IDE_DRIVE_SELECT_1 | IDE_DRIVE_SELECT_2)
+#define IDE_USE_LBA                  0x40
+
+/*
+ * IDE drive control definitions
+ */
+#define IDE_DC_DISABLE_INTERRUPTS    0x02
+#define IDE_DC_RESET_CONTROLLER      0x04
+#define IDE_DC_A_4BIT                0x80
+#define IDE_DC_USE_HOB               0x80 // use high-order byte(s)
+#define IDE_DC_REENABLE_CONTROLLER   0x00
+
+/*
+ * IDE error definitions
+ */
+#define IDE_ERROR_ICRC               0x80
+#define IDE_ERROR_BAD_BLOCK          0x80
+#define IDE_ERROR_DATA_ERROR         0x40
+#define IDE_ERROR_MEDIA_CHANGE       0x20
+#define IDE_ERROR_ID_NOT_FOUND       0x10
+#define IDE_ERROR_MEDIA_CHANGE_REQ   0x08
+#define IDE_ERROR_COMMAND_ABORTED    0x04
+#define IDE_ERROR_END_OF_MEDIA       0x02
+#define IDE_ERROR_NO_MEDIA           0x02
+#define IDE_ERROR_ILLEGAL_LENGTH     0x01
+
+/*
+ * Values for TransferMode
+ */
+#define ATA_PIO                      0x00
+
+/*
+ * IDENTIFY data
+ */
+#include <pshpack1.h>
+typedef struct _IDENTIFY_DATA
+{
+    UCHAR  AtapiCmdSize:2;                  // 00 00 General configuration
+    UCHAR  Unused1:3;
+    UCHAR  DrqType:2;
+    UCHAR  Removable:1;
+    UCHAR  DeviceType:5;
+    UCHAR  Unused2:1;
+    UCHAR  CmdProtocol:2;
+    USHORT NumberOfCylinders;               // 02  1
+    USHORT Reserved1;                       // 04  2
+    USHORT NumberOfHeads;                   // 06  3
+    USHORT UnformattedBytesPerTrack;        // 08  4
+    USHORT UnformattedBytesPerSector;       // 0A  5
+    USHORT SectorsPerTrack;                 // 0C  6
+    USHORT VendorUnique1[3];                // 0E  7-9
+    USHORT SerialNumber[10];                // 14  10-19
+    USHORT BufferType;                      // 28  20
+    USHORT BufferSectorSize;                // 2A  21
+    USHORT NumberOfEccBytes;                // 2C  22
+    USHORT FirmwareRevision[4];             // 2E  23-26
+    USHORT ModelNumber[20];                 // 36  27-46
+    UCHAR  ReadWriteMultipleSupport;        // 5E  47
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60  48
+    USHORT Reserved62_0:8;                  // 62  49 Capabilities
+    USHORT SupportDma:1;
+    USHORT SupportLba:1;
+    USHORT DisableIordy:1;
+    USHORT SupportIordy:1;
+    USHORT SoftReset:1;
+    USHORT StandbyOverlap:1;
+    USHORT SupportQTag:1;
+    USHORT SupportIDma:1;
+    USHORT Reserved2;                       // 64  50
+    UCHAR  VendorUnique3;                   // 66  51
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68  52
+    UCHAR  DmaCycleTimingMode;              // 69
+    USHORT TranslationFieldsValid:1;        // 6A  53
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C  54
+    USHORT NumberOfCurrentHeads;            // 6E  55
+    USHORT CurrentSectorsPerTrack;          // 70  56
+    ULONG  CurrentSectorCapacity;           // 72  57-58
+    USHORT CurrentMultiSectorSetting;       //     59
+    ULONG  UserAddressableSectors;          //     60-61
+    USHORT SingleWordDMASupport:8;          //     62
+    USHORT SingleWordDMAActive:8;
+    USHORT MultiWordDMASupport:8;           //     63
+    USHORT MultiWordDMAActive:8;
+    USHORT AdvancedPIOModes:8;              //     64
+    USHORT Reserved4:8;
+    USHORT MinimumMWXferCycleTime;          //     65
+    USHORT RecommendedMWXferCycleTime;      //     66
+    USHORT MinimumPIOCycleTime;             //     67
+    USHORT MinimumPIOCycleTimeIORDY;        //     68
+    USHORT Reserved5[2];                    //     69-70
+    USHORT ReleaseTimeOverlapped;           //     71
+    USHORT ReleaseTimeServiceCommand;       //     72
+    USHORT Reserved73_74[2];                //     73-74
+    USHORT QueueLength:5;                   //     75
+    USHORT Reserved75_6:11;
+    USHORT SataCapabilities;                //     76
+    USHORT Reserved77;                      //     77
+    USHORT SataSupport;                     //     78
+    USHORT SataEnable;                      //     79
+    USHORT MajorRevision;                   //     80
+    USHORT MinorRevision;                   //     81
+    struct {
+        USHORT Smart:1;                     //     82
+        USHORT Security:1;
+        USHORT Removable:1;
+        USHORT PowerMngt:1;
+        USHORT Packet:1;
+        USHORT WriteCache:1;
+        USHORT LookAhead:1;
+        USHORT ReleaseDRQ:1;
+        USHORT ServiceDRQ:1;
+        USHORT Reset:1;
+        USHORT Protected:1;
+        USHORT Reserved_82_11:1;
+        USHORT WriteBuffer:1;
+        USHORT ReadBuffer:1;
+        USHORT Nop:1;
+        USHORT Reserved_82_15:1;
+        USHORT Microcode:1;                 //     83/86
+        USHORT Queued:1;
+        USHORT CFA:1;
+        USHORT APM:1;
+        USHORT Notify:1;
+        USHORT Standby:1;
+        USHORT Spinup:1;
+        USHORT Reserver_83_7:1;
+        USHORT MaxSecurity:1;
+        USHORT AutoAcoustic:1;
+        USHORT Address48:1;
+        USHORT ConfigOverlay:1;
+        USHORT FlushCache:1;
+        USHORT FlushCache48:1;
+        USHORT SupportOne:1;
+        USHORT SupportZero:1;
+        USHORT SmartErrorLog:1;             //     84/87
+        USHORT SmartSelfTest:1;
+        USHORT MediaSerialNo:1;
+        USHORT MediaCardPass:1;
+        USHORT Streaming:1;
+        USHORT Logging:1;
+        USHORT Reserver_84_6:8;
+        USHORT ExtendedOne:1;
+        USHORT ExtendedZero:1;
+    } FeaturesSupport, FeaturesEnabled;
+    USHORT Reserved6[13];                   //     88-99
+    ULONGLONG UserAddressableSectors48;     //     100-103
+    USHORT Reserved7[151];                  //     104-255
+} IDENTIFY_DATA, *PIDENTIFY_DATA;
+#include <poppack.h>
+#define IDENTIFY_DATA_SIZE sizeof(IDENTIFY_DATA)
+
+#define ATAPI_MAGIC_LSB         0x14
+#define ATAPI_MAGIC_MSB         0xEB
+#define MAXIMUM_CDROM_SIZE      804
+
+typedef struct _DEVICE_UNIT
+{
+    UCHAR         Channel;
+    UCHAR         DeviceNumber;
+    ULONG         Cylinders;
+    ULONG         Heads;
+    ULONG         Sectors;
+    ULONG         SectorSize;
+    ULONGLONG     TotalSectors; /* This number starts from 0 */
+    USHORT        Flags;
+    IDENTIFY_DATA IdentifyData;
+} DEVICE_UNIT, *PDEVICE_UNIT;
+
+#define ATA_DEVICE_ATAPI        (1 << 0)
+#define ATA_DEVICE_NO_MEDIA     (1 << 1)
+#define ATA_DEVICE_NOT_READY    (1 << 2)
+#define ATA_DEVICE_LBA48        (1 << 3)
+#define ATA_DEVICE_LBA          (1 << 4)
+#define ATA_DEVICE_CHS          (1 << 5)
+
+/* PROTOTYPES ****************************************************************/
+
+BOOLEAN
+AtaInit(
+    OUT PUCHAR DetectedCount
+);
+
+VOID
+AtaFree();
+
+PDEVICE_UNIT
+AtaGetDevice(
+    IN UCHAR UnitNumber
+);
+
+BOOLEAN
+AtaAtapiReadLogicalSectorsLBA(
+    IN OUT PDEVICE_UNIT DeviceUnit,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer
+);