#include "miniport.h"
#include "ide2lpt.h"    // includes scsi.h




//
// Device extension
//

typedef struct _HW_DEVICE_EXTENSION {

    //
    // Current request on controller.
    //

    WORD wLptBase;

    PSCSI_REQUEST_BLOCK CurrentSrb;

    //
    // Data buffer pointer.
    //

    PUSHORT DataBuffer;

    //
    // Data words left.
    //

    ULONG WordsLeft;


    //
    // Count of errors. Used to turn off features.
    //

    ULONG ErrorCount;

    //
    // Indicates number of platters on changer-ish devices.
    //

    ULONG DiscsPresent;

    //
    // Flags word for each possible device.
    //

    USHORT DeviceFlags;

    //
    // Indicate last tape command was DSC Restrictive.
    //

    BOOLEAN RDP;

    //
    // Indicates the number of blocks transferred per int. according to the
    // identify data.
    //

    UCHAR MaximumBlockXfer;

    //
    // Placeholder for status register after a GET_MEDIA_STATUS command
    //

    UCHAR ReturningMediaStatus;

    UCHAR Reserved[1];

    //
    // Identify data for device
    //

    IDENTIFY_DATA FullIdentifyData;
    IDENTIFY_DATA2 IdentifyData;

    //
    // Mechanism Status Srb Data
    //
    PSCSI_REQUEST_BLOCK OriginalSrb;
    SCSI_REQUEST_BLOCK InternalSrb;

} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;

//
// Logical unit extension
//

typedef struct _HW_LU_EXTENSION {
   ULONG Reserved;
} HW_LU_EXTENSION, *PHW_LU_EXTENSION;


///////////////////////////////////////////////////////////////////////

VOID OutPort( WORD port, BYTE value ){
    _asm{
        mov al,value
        mov dx,port
        out dx,al
        jmp _1
    }
_1: _asm jmp _2
_2:
    return;
}

BYTE InPort( WORD port ){
    BYTE value;
    _asm{
        mov dx,port
        in  al,dx
        mov value,al
        jmp _1
    }
_1: _asm jmp _2
_2:
    return value;
}

///////////////////////////////////////////////////////////////////////

BOOL isAttached( WORD wLptBase ){
    DWORD b;

    OutPort( wLptBase+2, 0x04 );

    OutPort( wLptBase+0, 0xaa );
    OutPort( wLptBase+2, 0x00 );
    OutPort( wLptBase+2, 0x04 );

    OutPort( wLptBase+0, 0x55 );
    OutPort( wLptBase+2, 0x06 );
    OutPort( wLptBase+2, 0x04 );

    OutPort( wLptBase+0, 0xf8 );
    OutPort( wLptBase+2, 0x0c );
    OutPort( wLptBase+2, 0x04 );

    OutPort( wLptBase+0, 3 );
    b  = ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) << 8 );
    OutPort( wLptBase+0, 2 );
    b |= ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) << 4 );
    OutPort( wLptBase+0, 1 );
    b |= ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) << 0 );
    OutPort( wLptBase+0, 0 );
    b |= ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) >> 4 );

    return ( b==0x55aa );
}

DWORD Ide2LptInpByte( WORD wLptBase, DWORD port ){
    DWORD b;
    _asm cli
    b = (port & 0x07) | 0x80 | ( (port&0x300)==0x100 ? 0x08 : 0x10 );
    OutPort( wLptBase+0,  b      ); /* set -cs<1|3>fx & ha2..ha0 */
    OutPort( wLptBase+2,  0x0c   );
    OutPort( wLptBase+2,  0x04   );
    OutPort( wLptBase+0,  b|0x20 ); /* set -hrd */
    OutPort( wLptBase+2,  0x0c   );
    OutPort( wLptBase+2,  0x04   );
    OutPort( wLptBase+0,  b      ); /* reset -hrd */
    OutPort( wLptBase+2,  0x0c   );
    OutPort( wLptBase+2,  0x04   );
    OutPort( wLptBase+0,  b|0x18 ); /* reset -cs<1|3>fx */
    OutPort( wLptBase+2,  0x0c   );
    OutPort( wLptBase+2,  0x04   );

    OutPort( wLptBase+0, 1 );
    b  = (   ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 );
    OutPort( wLptBase+0, 0 );
    b |= ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) >> 4 );
    _asm sti
    return b;
}


VOID Ide2LptOutByte( WORD wLptBase, DWORD port, DWORD byte ){
    BYTE b = byte;
    _asm cli
    OutPort( wLptBase+0, b      );
    OutPort( wLptBase+2, 0x00   );            /* set hd7..hd0 */
    OutPort( wLptBase+2, 0x04   );
    b = (port & 0x07) | 0x80 | ( (port&0x300)==0x100 ? 0x08 : 0x10 );
    OutPort( wLptBase+0, b      );            /* set -cs<1|3>fx and ha2..ha0 */
    OutPort( wLptBase+2, 0x0c   );
    OutPort( wLptBase+2, 0x04   );
    OutPort( wLptBase+0, b|0x40 );            /* reset -hwr */
    OutPort( wLptBase+2, 0x0c   );
    OutPort( wLptBase+2, 0x04   );
    OutPort( wLptBase+0, b      );           /* reset -hwr */
    OutPort( wLptBase+2, 0x0c   );
    OutPort( wLptBase+2, 0x04   );
    OutPort( wLptBase+0, b|0x18 );           /* reset -cs<1|3>fx */
    OutPort( wLptBase+2, 0x0c   );
    OutPort( wLptBase+2, 0x04   );
    _asm sti
    return;
}

VOID Ide2LptOutWord( WORD wLptBase, DWORD word ){
    _asm cli
    OutPort( wLptBase+0, word&0xff );         /* set hd7..hd0 */
    OutPort( wLptBase+2, 0x00   );
    OutPort( wLptBase+2, 0x04   );
    OutPort( wLptBase+0, (word&0xff00)>>8 );    /* set hd15..hd8 */
    OutPort( wLptBase+2, 0x06   );
    OutPort( wLptBase+2, 0x04   );

    OutPort( wLptBase+0, 0x88   );            /* set -cs1fx and ha2..ha0 */
    OutPort( wLptBase+2, 0x0c   );
    OutPort( wLptBase+2, 0x04   );
    OutPort( wLptBase+0, 0xc8   );            /* set -hwr */
    OutPort( wLptBase+2, 0x0c   );
    OutPort( wLptBase+2, 0x04   );
    OutPort( wLptBase+0, 0x88   );           /* reset -hwr */
    OutPort( wLptBase+2, 0x0c   );
    OutPort( wLptBase+2, 0x04   );
    OutPort( wLptBase+0, 0x98   );           /* reset -cs<1|3>fx */
    OutPort( wLptBase+2, 0x0c   );
    OutPort( wLptBase+2, 0x04   );
    _asm sti
    return;
}

DWORD Ide2LptInpWord( WORD wLptBase ){
    DWORD b;
    _asm cli
    OutPort( wLptBase+0,  0x88 );            /* set -cs1fx & ha2..ha0 */
    OutPort( wLptBase+2,  0x0c );
    OutPort( wLptBase+2,  0x04 );
    OutPort( wLptBase+0,  0xa8 );            /* set -hrd */
    OutPort( wLptBase+2,  0x0c );
    OutPort( wLptBase+2,  0x04 );
    OutPort( wLptBase+0,  0x88 );            /* reset -hrd */
    OutPort( wLptBase+2,  0x0c );
    OutPort( wLptBase+2,  0x04 );
    OutPort( wLptBase+0,  0x98 );            /* reset -cs1fx & ha2..ha0 */
    OutPort( wLptBase+2,  0x0c );
    OutPort( wLptBase+2,  0x04 );

    OutPort( wLptBase+0, 3 );
    b  = ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) << 8 );
    OutPort( wLptBase+0, 2 );
    b |= ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) << 4 );
    OutPort( wLptBase+0, 1 );
    b |= ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) << 0 );
    OutPort( wLptBase+0, 0 );
    b |= ( ( ( InPort( wLptBase+1 ) & 0x0f0 ) ^ 0x80 ) >> 4 );
    _asm sti
    return b;
}

///////////////////////////////////////////////////////////////////////
LONG Ide2LptStringCmp (
    PCHAR FirstStr,
    PCHAR SecondStr,
    ULONG Count
    )
{
    UCHAR  first ,last;

    if (Count) {
        do {

            //
            // Get next char.
            //

            first = *FirstStr++;
            last = *SecondStr++;

            if (first != last) {

                //
                // If no match, try lower-casing.
                //

                if (first>='A' && first<='Z') {
                    first = first - 'A' + 'a';
                }
                if (last>='A' && last<='Z') {
                    last = last - 'A' + 'a';
                }
                if (first != last) {

                    //
                    // No match
                    //

                    return first - last;
                }
            }
        }while (--Count && first);
    }

    return 0;
}

VOID Ide2LptZeroMemory(
    IN PCHAR Buffer,
    IN ULONG Count
    )
{
    ULONG i;

    for (i = 0; i < Count; i++) {
        Buffer[i] = 0;
    }
}

VOID Ide2LptHexToString (
    IN ULONG Value,
    IN OUT PCHAR *Buffer
    )
{
    PCHAR  string;
    PCHAR  firstdig;
    CHAR   temp;
    ULONG i;
    USHORT digval;

    string = *Buffer;

    firstdig = string;

    for (i = 0; i < 4; i++) {
        digval = (USHORT)(Value % 16);
        Value /= 16;

        //
        // convert to ascii and store. Note this will create
        // the buffer with the digits reversed.
        //

        if (digval > 9) {
            *string++ = (char) (digval - 10 + 'a');
        } else {
            *string++ = (char) (digval + '0');
        }

    }

    //
    // Reverse the digits.
    //

    *string-- = '\0';

    do {
        temp = *string;
        *string = *firstdig;
        *firstdig = temp;
        --string;
        ++firstdig;
    } while (firstdig < string);
}

Ide2LptMoveMemory( IN PVOID WriteBuffer, IN PVOID ReadBuffer, IN ULONG Length ){
    ULONG i;
    for( i=0; i!=Length; i++ ){
        *(((BYTE*)WriteBuffer)+i) = *(((BYTE*)ReadBuffer)+i);
    }
}

///////////////////////////////////////////////////////////////////////

BOOLEAN Ide2LptHwInitialize( IN PVOID HwDeviceExtension )
/*++

Routine Description:

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage

Return Value:

    TRUE - if initialization successful.
    FALSE - if initialization unsuccessful.

--*/

{
    return TRUE;

} // end AtapiHwInitialize()

///////////////////////////////////////////////////////////////////////
BOOLEAN
IssueIdentify(
    IN PVOID HwDeviceExtension,
    IN UCHAR Command
    )

/*++

Routine Description:

    Issue IDENTIFY command to a device.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage
    Command - Either the standard (EC) or the ATAPI packet (A1) IDENTIFY.

Return Value:

    TRUE if all goes well.

--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    ULONG                waitCount = 20000;
    ULONG                i,j;
    UCHAR                statusByte;
    UCHAR                signatureLow,
                         signatureHigh;

    //
    // Select device 0.
    //

    Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );

    //
    // Check that the status register makes sense.
    //

    GetBaseStatus(wLptBase, statusByte);

    if (Command == IDE_COMMAND_IDENTIFY) {

        //
        // Mask status byte ERROR bits.
        //

        statusByte &= ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX);

        DebugPrint((1,
                    "IssueIdentify: Checking for IDE. Status (%x)\n",
                    statusByte));

        //
        // Check if register value is reasonable.
        //

        if (statusByte != IDE_STATUS_IDLE) {

            //
            // Reset the controller.
            //

            AtapiSoftReset(wLptBase);

            Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );

            WaitOnBusy(wLptBase, statusByte);

            signatureLow = Ide2LptInpByte(wLptBase, IDE_CYLINDER_LOW);
            signatureHigh = Ide2LptInpByte(wLptBase, IDE_CYLINDER_HIGH);

            if (signatureLow == 0x14 && signatureHigh == 0xEB) {

                //
                // Device is Atapi.
                //

                return FALSE;
            }

            DebugPrint((1,
                        "IssueIdentify: Resetting controller.\n"));

            Ide2LptOutByte( wLptBase, IDE_DRIVE_CONTROL, IDE_DC_RESET_CONTROLLER );
            ScsiPortStallExecution(500 * 1000);
            Ide2LptOutByte( wLptBase, IDE_DRIVE_CONTROL, (IDE_DC_REENABLE_CONTROLLER|IDE_DC_DISABLE_INTERRUPTS) );


            // We really should wait up to 31 seconds
            // The ATA spec. allows device 0 to come back from BUSY in 31 seconds!
            // (30 seconds for device 1)
            do {

                //
                // Wait for Busy to drop.
                //

                ScsiPortStallExecution(100);
                GetStatus(wLptBase, statusByte);

            } while ((statusByte & IDE_STATUS_BUSY) && waitCount--);

            Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );

            //
            // Another check for signature, to deal with one model Atapi that doesn't assert signature after
            // a soft reset.
            //

            signatureLow = Ide2LptInpByte(wLptBase, IDE_CYLINDER_LOW);
            signatureHigh = Ide2LptInpByte(wLptBase, IDE_CYLINDER_HIGH);

            if (signatureLow == 0x14 && signatureHigh == 0xEB) {

                //
                // Device is Atapi.
                //

                return FALSE;
            }

            statusByte &= ~IDE_STATUS_INDEX;

            if (statusByte != IDE_STATUS_IDLE) {

                //
                // Give up on this.
                //

                return FALSE;
            }

        }

    } else {

        DebugPrint((1,
                    "IssueIdentify: Checking for ATAPI. Status (%x)\n",
                    statusByte));

    }

    //
    // Load CylinderHigh and CylinderLow with number bytes to transfer.
    //

    Ide2LptOutByte( wLptBase, IDE_CYLINDER_HIGH, (0x200 >> 8) );
    Ide2LptOutByte( wLptBase, IDE_CYLINDER_LOW, (0x200 & 0xff) );

    for (j = 0; j < 2; j++) {

        //
        // Send IDENTIFY command.
        //

        WaitOnBusy(wLptBase, statusByte);

        WriteCommand(wLptBase, Command);

        //
        // Wait for DRQ.
        //

        for (i = 0; i < 4; i++) {

            WaitForDrq(wLptBase, statusByte);

            if (statusByte & IDE_STATUS_DRQ) {

                //
                // Read status to acknowledge any interrupts generated.
                //

                GetBaseStatus(wLptBase, statusByte);

                //
                // One last check for Atapi.
                //


                signatureLow = Ide2LptInpByte(wLptBase, IDE_CYLINDER_LOW);
                signatureHigh = Ide2LptInpByte(wLptBase, IDE_CYLINDER_HIGH);

                if (signatureLow == 0x14 && signatureHigh == 0xEB) {

                    //
                    // Device is Atapi.
                    //

                    return FALSE;
                }

                break;
            }

            if (Command == IDE_COMMAND_IDENTIFY) {

                //
                // Check the signature. If DRQ didn't come up it's likely Atapi.
                //

                signatureLow = Ide2LptInpByte(wLptBase, IDE_CYLINDER_LOW);
                signatureHigh = Ide2LptInpByte(wLptBase, IDE_CYLINDER_HIGH);


                if (signatureLow == 0x14 && signatureHigh == 0xEB) {

                    //
                    // Device is Atapi.
                    //

                    return FALSE;
                }
            }

            WaitOnBusy(wLptBase, statusByte);
        }

        if (i == 4 && j == 0) {

            //
            // Device didn't respond correctly. It will be given one more chances.
            //

            DebugPrint((1,
                        "IssueIdentify: DRQ never asserted (%x). Error reg (%x)\n",
                        statusByte,
                         Ide2LptInpByte(wLptBase, IDE_ERROR)));

            AtapiSoftReset(wLptBase);

            GetStatus(wLptBase, statusByte);

            DebugPrint((1,
                       "IssueIdentify: Status after soft reset (%x)\n",
                       statusByte));

        } else {

            break;

        }
    }

    //
    // Check for error on really stupid master devices that assert random
    // patterns of bits in the status register at the slave address.
    //

    if ((Command == IDE_COMMAND_IDENTIFY) && (statusByte & IDE_STATUS_ERROR)) {
        return FALSE;
    }

    DebugPrint((1,
               "IssueIdentify: Status before read words %x\n",
               statusByte));

    //
    // Suck out 256 words. After waiting for one model that asserts busy
    // after receiving the Packet Identify command.
    //

    WaitOnBusy(wLptBase, statusByte);

    if (!(statusByte & IDE_STATUS_DRQ)) {
        return FALSE;
    }

    ReadBuffer( wLptBase,
               ((PUSHORT)&deviceExtension->FullIdentifyData),
               256);

    //
    // Check out a few capabilities / limitations of the device.
    //

    if (deviceExtension->FullIdentifyData.SpecialFunctionsEnabled & 1) {

        //
        // Determine if this drive supports the MSN functions.
        //

        DebugPrint((2,"IssueIdentify: Marking drive as removable. SFE = %d\n",
                    deviceExtension->FullIdentifyData.SpecialFunctionsEnabled));


        deviceExtension->DeviceFlags |= DFLAGS_REMOVABLE_DRIVE;
    }

    if (deviceExtension->FullIdentifyData.MaximumBlockTransfer) {

        //
        // Determine max. block transfer for this device.
        //

        deviceExtension->MaximumBlockXfer =
            (UCHAR)(deviceExtension->FullIdentifyData.MaximumBlockTransfer & 0xFF);
    }

    Ide2LptMoveMemory(&deviceExtension->IdentifyData,&deviceExtension->FullIdentifyData,sizeof(IDENTIFY_DATA2));

    if (deviceExtension->IdentifyData.GeneralConfiguration & 0x20 &&
        Command != IDE_COMMAND_IDENTIFY) {

        //
        // This device interrupts with the assertion of DRQ after receiving
        // Atapi Packet Command
        //

        deviceExtension->DeviceFlags |= DFLAGS_INT_DRQ;

        DebugPrint((2,
                    "IssueIdentify: Device interrupts on assertion of DRQ.\n"));

    } else {

        DebugPrint((2,
                    "IssueIdentify: Device does not interrupt on assertion of DRQ.\n"));
    }

    if (((deviceExtension->IdentifyData.GeneralConfiguration & 0xF00) == 0x100) &&
        Command != IDE_COMMAND_IDENTIFY) {

        //
        // This is a tape.
        //

        deviceExtension->DeviceFlags |= DFLAGS_TAPE_DEVICE;

        DebugPrint((2,
                    "IssueIdentify: Device is a tape drive.\n"));

    } else {

        DebugPrint((2,
                    "IssueIdentify: Device is not a tape drive.\n"));
    }

    //
    // Work around for some IDE and one model Atapi that will present more than
    // 256 bytes for the Identify data.
    //

    WaitOnBusy(wLptBase, statusByte);

    for (i = 0; i < 0x10000; i++) {

        GetStatus(wLptBase, statusByte);

        if (statusByte & IDE_STATUS_DRQ) {

            //
            // Suck out any remaining bytes and throw away.
            //

            Ide2LptInpWord(wLptBase);

        } else {

            break;

        }
    }

    DebugPrint((3,
               "IssueIdentify: Status after read words (%x)\n",
               statusByte));

    return TRUE;

} // end IssueIdentify()

///////////////////////////////////////////////////////////////////////
BOOLEAN SetDriveParameters(
    IN PVOID HwDeviceExtension
    )

/*++

Routine Description:

    Set drive parameters using the IDENTIFY data.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage

Return Value:

    TRUE if all goes well.


--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    PIDENTIFY_DATA2      identifyData   = &deviceExtension->IdentifyData;
    ULONG i;
    UCHAR statusByte;

    DebugPrint((1,
               "SetDriveParameters: Number of heads %x\n",
               identifyData->NumberOfHeads));

    DebugPrint((1,
               "SetDriveParameters: Sectors per track %x\n",
                identifyData->SectorsPerTrack));

    //
    // Set up registers for SET PARAMETER command.
    //


    Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD,
                           IDE_DRIVE_SELECT_1 | (identifyData->NumberOfHeads - 1) );

    Ide2LptOutByte( wLptBase, IDE_BLOCK_COUNT,
                           (UCHAR)identifyData->SectorsPerTrack );

    //
    // Send SET PARAMETER command.
    //

    WriteCommand( wLptBase, IDE_COMMAND_SET_DRIVE_PARAMETERS );

    //
    // Wait for up to 30 milliseconds for ERROR or command complete.
    //

    for (i=0; i<30 * 1000; i++) {

        UCHAR errorByte;

        GetStatus(wLptBase, statusByte);

        if (statusByte & IDE_STATUS_ERROR) {
            errorByte = Ide2LptInpByte( wLptBase, IDE_ERROR );
            DebugPrint((1,
                        "SetDriveParameters: Error bit set. Status %x, error %x\n",
                        errorByte,
                        statusByte));

            return FALSE;
        } else if ((statusByte & ~IDE_STATUS_INDEX ) == IDE_STATUS_IDLE) {
            break;
        } else {
            ScsiPortStallExecution(100);
        }
    }

    //
    // Check for timeout.
    //

    if (i == 30 * 1000) {
        return FALSE;
    } else {
        return TRUE;
    }

} // end SetDriveParameters()

///////////////////////////////////////////////////////////////////////
BOOLEAN FindDevices(
    IN PVOID HwDeviceExtension
    )

/*++

Routine Description:

    This routine is called from Ide2LptFindController to identify
    devices attached to an IDE controller.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage

Return Value:

    TRUE - True if devices found.

--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;

    WORD wLptBase = deviceExtension -> wLptBase;

    BOOLEAN              deviceResponded = FALSE,
                         skipSetParameters = FALSE;
    ULONG                waitCount = 10000;
    ULONG                i;
    UCHAR                signatureLow,
                         signatureHigh;
    UCHAR                statusByte;

    //
    // Clear expecting interrupt flag and current SRB field.
    //

    deviceExtension->CurrentSrb = NULL;

    IdeHardReset( wLptBase, statusByte );

    //
    // Select the device.
    //

    Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );

    //
    // Check here for some SCSI adapters that incorporate IDE emulation.
    //

    GetStatus( wLptBase, statusByte );

    if (statusByte != 0xFF) {

        AtapiSoftReset(wLptBase);
        WaitOnBusy(wLptBase, statusByte);

        signatureLow = Ide2LptInpByte(wLptBase, IDE_CYLINDER_LOW);
        signatureHigh = Ide2LptInpByte(wLptBase, IDE_CYLINDER_HIGH);

        if (signatureLow == 0x14 && signatureHigh == 0xEB) {

            //
            // ATAPI signature found.
            // Issue the ATAPI identify command if this
            // is not for the crash dump utility.
            //

atapiIssueId:


            //
            // Issue ATAPI packet identify command.
            //

            if (IssueIdentify(HwDeviceExtension, IDE_COMMAND_ATAPI_IDENTIFY)) {

                //
                // Indicate ATAPI device.
                //

                DebugPrint((1,
                            "FindDevices: Device is ATAPI\n"
                          ));

                deviceExtension->DeviceFlags |= DFLAGS_ATAPI_DEVICE;
                deviceExtension->DeviceFlags |= DFLAGS_DEVICE_PRESENT;

                deviceResponded = TRUE;

                GetStatus( wLptBase, statusByte );
                if (statusByte & IDE_STATUS_ERROR) {
                    AtapiSoftReset(wLptBase);
                }

            } else {

                //
                // Indicate no working device.
                //

                DebugPrint((1,
                            "FindDevices: Device %x not responding\n"
                          ));

                deviceExtension->DeviceFlags &= ~DFLAGS_DEVICE_PRESENT;
            }
        } else {

            //
            // Issue IDE Identify. If an Atapi device is actually present, the signature
            // will be asserted, and the drive will be recognized as such.
            //

            if (IssueIdentify(HwDeviceExtension,
                        IDE_COMMAND_IDENTIFY)) {

                //
                // IDE drive found.
                //


                DebugPrint((1,
                           "FindDevices: Device is IDE\n"
                      ));

                deviceExtension->DeviceFlags |= DFLAGS_DEVICE_PRESENT;

                deviceResponded = TRUE;

                //
                // Indicate IDE - not ATAPI device.
                //

                deviceExtension->DeviceFlags &= ~DFLAGS_ATAPI_DEVICE;

            } else {

                //
                // Look to see if an Atapi device is present.
                //

                AtapiSoftReset(wLptBase);

                WaitOnBusy(wLptBase, statusByte);

                signatureLow = Ide2LptInpByte(wLptBase, IDE_CYLINDER_LOW);
                signatureHigh = Ide2LptInpByte(wLptBase, IDE_CYLINDER_HIGH);

                if (signatureLow == 0x14 && signatureHigh == 0xEB) {
                  goto atapiIssueId;
                }
            }
        }
    }

    if ((deviceExtension->DeviceFlags & DFLAGS_DEVICE_PRESENT) &&
        (!(deviceExtension->DeviceFlags & DFLAGS_ATAPI_DEVICE)) && deviceResponded) {

        //
        // This hideous hack is to deal with ESDI devices that return
        // garbage geometry in the IDENTIFY data.
        // This is ONLY for the crashdump environment as
        // these are ESDI devices.
        //

        if (deviceExtension->IdentifyData.SectorsPerTrack == 0x35 &&
            deviceExtension->IdentifyData.NumberOfHeads == 0x07) {

            DebugPrint((1,
                    "FindDevices: Found nasty Compaq ESDI!\n"));

            //
            // Change these values to something reasonable.
            //

            deviceExtension->IdentifyData.SectorsPerTrack = 0x34;
            deviceExtension->IdentifyData.NumberOfHeads = 0x0E;
        }

        if (deviceExtension->IdentifyData.SectorsPerTrack == 0x35 &&
            deviceExtension->IdentifyData.NumberOfHeads == 0x0F) {

            DebugPrint((1,
                    "FindDevices: Found nasty Compaq ESDI!\n"));

            //
            // Change these values to something reasonable.
            //

            deviceExtension->IdentifyData.SectorsPerTrack = 0x34;
            deviceExtension->IdentifyData.NumberOfHeads = 0x0F;
        }

        if (deviceExtension->IdentifyData.SectorsPerTrack == 0x36 &&
            deviceExtension->IdentifyData.NumberOfHeads == 0x07) {

            DebugPrint((1,
                    "FindDevices: Found nasty UltraStor ESDI!\n"));

            //
            // Change these values to something reasonable.
            //

            deviceExtension->IdentifyData.SectorsPerTrack = 0x3F;
                deviceExtension->IdentifyData.NumberOfHeads = 0x10;
                skipSetParameters = TRUE;
        }


        if (!skipSetParameters) {

            WaitOnBusy(wLptBase, statusByte);

            //
            // Select the device.
            //

            Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );

            GetStatus(wLptBase, statusByte);

            if (statusByte & IDE_STATUS_ERROR) {

                //
                // Reset the device.
                //

                DebugPrint((2,
                        "FindDevices: Resetting controller before SetDriveParameters.\n"));

                Ide2LptOutByte( wLptBase, IDE_DRIVE_CONTROL, IDE_DC_RESET_CONTROLLER );
                ScsiPortStallExecution(500 * 1000);
                Ide2LptOutByte( wLptBase, IDE_DRIVE_CONTROL, (IDE_DC_REENABLE_CONTROLLER|IDE_DC_DISABLE_INTERRUPTS) );
                Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );

                do {

                    //
                    // Wait for Busy to drop.
                    //

                    ScsiPortStallExecution(100);
                    GetStatus( wLptBase, statusByte );

                } while ((statusByte & IDE_STATUS_BUSY) && waitCount--);
            }

            WaitOnBusy(wLptBase, statusByte);
            DebugPrint((2,
                        "FindDevices: Status before SetDriveParameters: (%x) (%x)\n",
                        statusByte,
                        Ide2LptInpByte(wLptBase, IDE_STATUS) ));

            //
            // Use the IDENTIFY data to set drive parameters.
            //

            if (!SetDriveParameters(HwDeviceExtension)) {

                DebugPrint((0,
                            "AtapHwInitialize: Set drive parameters for device failed\n"
                            ));

                //
                // Don't use this device as writes could cause corruption.
                //

                deviceExtension->DeviceFlags = 0;

            }else{
                deviceResponded = TRUE;
            }
        }
    }

    //
    // Make sure master device is selected on exit.
    //
    Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );


    //
    // Reset the controller. This is a feeble attempt to leave the ESDI
    // controllers in a state that ATDISK driver will recognize them.
    // The problem in ATDISK has to do with timings as it is not reproducible
    // in debug. The reset should restore the controller to its poweron state
    // and give the system enough time to settle.
    //

    if (!deviceResponded) {
        Ide2LptOutByte( wLptBase, IDE_DRIVE_CONTROL, IDE_DC_RESET_CONTROLLER );
        ScsiPortStallExecution(50 * 1000);
        Ide2LptOutByte( wLptBase, IDE_DRIVE_CONTROL, (IDE_DC_REENABLE_CONTROLLER|IDE_DC_DISABLE_INTERRUPTS) );
    }

    return deviceResponded;

} // end FindDevices()



///////////////////////////////////////////////////////////////////////
ULONG Ide2LptFindController(
    IN PVOID HwDeviceExtension,
    IN PVOID Context,
    IN PVOID BusInformation,
    IN PCHAR ArgumentString,
    IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
    OUT PBOOLEAN Again
    )
/*++

Routine Description:

    This function is called by the OS-specific port driver after
    the necessary storage has been allocated, to gather information
    about the adapter's configuration.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage
    Context - Address of adapter count
    ArgumentString - Used to determine whether driver is client of ntldr or crash dump utility.
    ConfigInfo - Configuration information structure describing HBA
    Again - Indicates search for adapters to continue

Return Value:

    ULONG

--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    PULONG               adapterCount    = (PULONG)Context;
    PUCHAR               ioSpace;
    ULONG                i,j;
    ULONG                retryCount;
    UCHAR                statusByte;
    BOOLEAN              preConfig = FALSE;
    //
    // The following table specifies the ports to be checked when searching for
    // an IDE controller.  A zero entry terminates the search.
    //

    CONST ULONG awLptAddresses[] = {0x378, 0x278, 0};

    //
    // The following table specifies interrupt levels corresponding to the
    // port addresses in the previous table.
    //

    if (!deviceExtension) {
        return SP_RETURN_ERROR;
    }

    //
    // Check to see if this is a special configuration environment.
    //

    deviceExtension->wLptBase = 0;

    if (ArgumentString) {
        // TO DO: Now we can parse argument string
    }

    //
    // Zero device fields to ensure that if earlier devices were found,
    // but not claimed, the fields are cleared.
    //

    deviceExtension->DeviceFlags &= ~(DFLAGS_ATAPI_DEVICE | DFLAGS_DEVICE_PRESENT | DFLAGS_TAPE_DEVICE);

    //
    // Scan though the adapter address looking for adapters.
    //

    for( i=0; (deviceExtension->wLptBase=awLptAddresses[i])!=0; i++ ){
        if( isAttached( deviceExtension->wLptBase ) ) break;
    }

    *Again = FALSE;

    if( deviceExtension->wLptBase != 0 ){

        ConfigInfo->NumberOfBuses = 1;
        ConfigInfo->MaximumNumberOfTargets = 1;

        //
        // Indicate maximum transfer length is 64k.
        //

        ConfigInfo->MaximumTransferLength = 0x10000;

        DebugPrint((1,
                   "AtapiFindController: Found IDE2LPT at %x\n",
                   deviceExtension->wLptBase));

        //
        // Search for devices on this controller.
        //

        if (FindDevices(HwDeviceExtension)) {

            ConfigInfo->Master = FALSE;
            ConfigInfo->CachesData = FALSE;

            return(SP_RETURN_FOUND);
        }
    }

    //
    // The entire table has been searched and no adapters have been found.
    // There is no need to call again and the device base can now be freed.
    // Clear the adapter count for the next bus.
    //

    *Again = FALSE;
    *(adapterCount) = 0;

    return(SP_RETURN_NOT_FOUND);

} // end AtapiFindController()

///////////////////////////////////////////////////////////////////////
BOOLEAN Ide2LptResetController(
    IN PVOID HwDeviceExtension,
    IN ULONG PathId
    )

/*++

Routine Description:

    Reset IDE controller and/or Atapi device.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage

Return Value:

    Nothing.


--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    BOOLEAN result = FALSE;
    ULONG i,j;
    UCHAR statusByte;

    DebugPrint((2,"AtapiResetController: Reset IDE\n"));

    //
    // Check if request is in progress.
    //

    if (deviceExtension->CurrentSrb) {

        //
        // Clear request tracking fields.
        //

        deviceExtension->CurrentSrb = NULL;
        deviceExtension->WordsLeft = 0;
        deviceExtension->DataBuffer = NULL;

        //
        // Indicate ready for next request.
        //

        ScsiPortNotification(NextRequest,
                             deviceExtension,
                             NULL);
    }

    deviceExtension->RDP = FALSE;

    IdeHardReset( wLptBase, result);

    return result;

} // end AtapiResetController()

///////////////////////////////////////////////////////////////////////
ULONG MapError(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )

/*++

Routine Description:

    This routine maps ATAPI and IDE errors to specific SRB statuses.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage
    Srb - IO request packet

Return Value:

    SRB status

--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    ULONG i;
    UCHAR errorByte;
    UCHAR srbStatus;
    UCHAR scsiStatus;

    //
    // Read the error register.
    //

    errorByte = Ide2LptInpByte( wLptBase, IDE_ERROR );
    DebugPrint((1,
               "MapError: Error register is %x\n",
               errorByte));

    if (deviceExtension->DeviceFlags & DFLAGS_ATAPI_DEVICE) {

        switch (errorByte >> 4) {
        case SCSI_SENSE_NO_SENSE:

            DebugPrint((1,
                       "ATAPI: No sense information\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_RECOVERED_ERROR:

            DebugPrint((1,
                       "ATAPI: Recovered error\n"));
            scsiStatus = 0;
            srbStatus = SRB_STATUS_SUCCESS;
            break;

        case SCSI_SENSE_NOT_READY:

            DebugPrint((1,
                       "ATAPI: Device not ready\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_MEDIUM_ERROR:

            DebugPrint((1,
                       "ATAPI: Media error\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_HARDWARE_ERROR:

            DebugPrint((1,
                       "ATAPI: Hardware error\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_ILLEGAL_REQUEST:

            DebugPrint((1,
                       "ATAPI: Illegal request\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_UNIT_ATTENTION:

            DebugPrint((1,
                       "ATAPI: Unit attention\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_DATA_PROTECT:

            DebugPrint((1,
                       "ATAPI: Data protect\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_BLANK_CHECK:

            DebugPrint((1,
                       "ATAPI: Blank check\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_ABORTED_COMMAND:
            DebugPrint((1,
                        "Atapi: Command Aborted\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        default:

            DebugPrint((1,
                       "ATAPI: Invalid sense information\n"));
            scsiStatus = 0;
            srbStatus = SRB_STATUS_ERROR;
            break;
        }

    } else {

        scsiStatus = 0;

        //
        // Save errorByte,to be used by SCSIOP_REQUEST_SENSE.
        //

        deviceExtension->ReturningMediaStatus = errorByte;

        if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) {
            DebugPrint((1,
                       "IDE: Media change\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;

        } else if (errorByte & IDE_ERROR_COMMAND_ABORTED) {
            DebugPrint((1,
                       "IDE: Command abort\n"));
            srbStatus = SRB_STATUS_ABORTED;
            scsiStatus = SCSISTAT_CHECK_CONDITION;

            if (Srb->SenseInfoBuffer) {

                PSENSE_DATA  senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

                senseBuffer->ErrorCode = 0x70;
                senseBuffer->Valid     = 1;
                senseBuffer->AdditionalSenseLength = 0xb;
                senseBuffer->SenseKey =  SCSI_SENSE_ABORTED_COMMAND;
                senseBuffer->AdditionalSenseCode = 0;
                senseBuffer->AdditionalSenseCodeQualifier = 0;

                srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            }

            deviceExtension->ErrorCount++;

        } else if (errorByte & IDE_ERROR_END_OF_MEDIA) {

            DebugPrint((1,
                       "IDE: End of media\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            if (!(deviceExtension->DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED)){
                deviceExtension->ErrorCount++;
            }

        } else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) {

            DebugPrint((1,
                       "IDE: Illegal length\n"));
            srbStatus = SRB_STATUS_INVALID_REQUEST;

        } else if (errorByte & IDE_ERROR_BAD_BLOCK) {

            DebugPrint((1,
                       "IDE: Bad block\n"));
            srbStatus = SRB_STATUS_ERROR;
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            if (Srb->SenseInfoBuffer) {

                PSENSE_DATA  senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

                senseBuffer->ErrorCode = 0x70;
                senseBuffer->Valid     = 1;
                senseBuffer->AdditionalSenseLength = 0xb;
                senseBuffer->SenseKey =  SCSI_SENSE_MEDIUM_ERROR;
                senseBuffer->AdditionalSenseCode = 0;
                senseBuffer->AdditionalSenseCodeQualifier = 0;

                srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            }

        } else if (errorByte & IDE_ERROR_ID_NOT_FOUND) {

            DebugPrint((1,
                       "IDE: Id not found\n"));
            srbStatus = SRB_STATUS_ERROR;
            scsiStatus = SCSISTAT_CHECK_CONDITION;

            if (Srb->SenseInfoBuffer) {

                PSENSE_DATA  senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

                senseBuffer->ErrorCode = 0x70;
                senseBuffer->Valid     = 1;
                senseBuffer->AdditionalSenseLength = 0xb;
                senseBuffer->SenseKey =  SCSI_SENSE_MEDIUM_ERROR;
                senseBuffer->AdditionalSenseCode = 0;
                senseBuffer->AdditionalSenseCodeQualifier = 0;

                srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            }

            deviceExtension->ErrorCount++;

        } else if (errorByte & IDE_ERROR_MEDIA_CHANGE) {

            DebugPrint((1,
                       "IDE: Media change\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;

            if (Srb->SenseInfoBuffer) {

                PSENSE_DATA  senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

                senseBuffer->ErrorCode = 0x70;
                senseBuffer->Valid     = 1;
                senseBuffer->AdditionalSenseLength = 0xb;
                senseBuffer->SenseKey =  SCSI_SENSE_UNIT_ATTENTION;
                senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
                senseBuffer->AdditionalSenseCodeQualifier = 0;

                srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            }

        } else if (errorByte & IDE_ERROR_DATA_ERROR) {

            DebugPrint((1,
                   "IDE: Data error\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;

            if (!(deviceExtension->DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED)){
                deviceExtension->ErrorCount++;
            }

            //
            // Build sense buffer
            //

            if (Srb->SenseInfoBuffer) {

                PSENSE_DATA  senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

                senseBuffer->ErrorCode = 0x70;
                senseBuffer->Valid     = 1;
                senseBuffer->AdditionalSenseLength = 0xb;
                senseBuffer->SenseKey =  SCSI_SENSE_MEDIUM_ERROR;
                senseBuffer->AdditionalSenseCode = 0;
                senseBuffer->AdditionalSenseCodeQualifier = 0;

                srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            }
        }

        if (deviceExtension->ErrorCount >= MAX_ERRORS) {
            UCHAR statusByte;

            deviceExtension->MaximumBlockXfer = 0;

            DebugPrint((1,
                        "MapError: Disabling 32-bit PIO and Multi-sector IOs\n"));

            //
            // Log the error.
            //

            ScsiPortLogError( HwDeviceExtension,
                              Srb,
                              Srb->PathId,
                              Srb->TargetId,
                              Srb->Lun,
                              SP_BAD_FW_WARNING,
                              4);
            //
            // Reprogram to not use Multi-sector.
            //

            if (deviceExtension->DeviceFlags & DFLAGS_DEVICE_PRESENT &&
                  !(deviceExtension->DeviceFlags & DFLAGS_ATAPI_DEVICE)) {

                //
                // Select the device.
                //

                Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );

                //
                // Setup sector count to reflect the # of blocks.
                //

                Ide2LptOutByte( wLptBase, IDE_BLOCK_COUNT, 0);

                //
                // Issue the command.
                //

                Ide2LptOutByte( wLptBase, IDE_COMMAND, IDE_COMMAND_SET_MULTIPLE);

                //
                // Wait for busy to drop.
                //

                WaitOnBaseBusy(wLptBase,statusByte);

                //
                // Check for errors. Reset the value to 0 (disable MultiBlock) if the
                // command was aborted.
                //

                if (statusByte & IDE_STATUS_ERROR) {

                    //
                    // Read the error register.
                    //

                    errorByte = Ide2LptInpByte( wLptBase, IDE_ERROR );

                    DebugPrint((1,
                                "AtapiHwInitialize: Error setting multiple mode. Status %x, error byte %x\n",
                                statusByte,
                                errorByte));
                    //
                    // Adjust the devExt. value, if necessary.
                    //

                    deviceExtension->MaximumBlockXfer = 0;

                }
            }
        }
    }


    //
    // Set SCSI status to indicate a check condition.
    //

    Srb->ScsiStatus = scsiStatus;

    return srbStatus;

} // end MapError()
///////////////////////////////////////////////////////////////////////
ULONG ProceedRequest(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    BYTE statusByte, errorByte;
    ULONG status;
    ULONG wordCount = 0, wordsThisRequest = 256;
    ULONG i;
    UCHAR requestReason;

    GetBaseStatus( wLptBase, statusByte );

    if( (statusByte & IDE_STATUS_ERROR)
        && !(statusByte & (IDE_STATUS_BUSY|IDE_STATUS_DRQ))
      )
    {
        errorByte = Ide2LptInpByte( wLptBase, IDE_ERROR ) >> 4;
        if( Srb->Cdb[0]==0 ){
            if( errorByte != 5 ){
                status = SRB_STATUS_ERROR;
                goto CompleteRequest;
            }
        }else if (Srb->Cdb[0] != SCSIOP_REQUEST_SENSE) {
                status = SRB_STATUS_ERROR;
                goto CompleteRequest;
        }
    }

    if (deviceExtension->DeviceFlags & DFLAGS_ATAPI_DEVICE) {

        requestReason = Ide2LptInpByte( wLptBase, ATAPI_INTERRUPT_REASON );

    }else{
        if (statusByte & IDE_STATUS_DRQ) {

            if (deviceExtension->MaximumBlockXfer) {
                wordsThisRequest = 256 * deviceExtension->MaximumBlockXfer;
            }

            if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
                requestReason = 0x2;
            } else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
                requestReason = 0x0;
            } else {
                status = SRB_STATUS_ERROR;
                goto CompleteRequest;
            }

        }else if (statusByte & IDE_STATUS_BUSY) {

            return FALSE;

        } else {
            requestReason = 0x3;
        }
    }

    if( requestReason == 0x1 && (statusByte & IDE_STATUS_DRQ)) {

        //
        // Write the packet.
        //
        DebugPrint((2,
                    "AtapiInterrupt: Writing Atapi packet.\n"));

        //
        // Send CDB to device.
        //
        WriteBuffer( wLptBase, (PUSHORT)Srb->Cdb, 6 );
        return TRUE;

    } else if (requestReason == 0x0 && (statusByte & IDE_STATUS_DRQ)) {

        //
        // Write the data.
        //

        if (deviceExtension->DeviceFlags & DFLAGS_ATAPI_DEVICE) {

            //
            // Pick up bytes to transfer and convert to words.
            //

            wordCount  = Ide2LptInpByte( wLptBase, ATAPI_BYTE_COUNT_LOW );

            wordCount |= Ide2LptInpByte( wLptBase, ATAPI_BYTE_COUNT_LOW ) << 8;

            //
            // Covert bytes to words.
            //

            wordCount >>= 1;

            if (wordCount != deviceExtension->WordsLeft) {
                DebugPrint((3,
                           "AtapiInterrupt: %d words requested; %d words xferred\n",
                           deviceExtension->WordsLeft,
                           wordCount));
            }

            //
            // Verify this makes sense.
            //

            if (wordCount > deviceExtension->WordsLeft) {
                wordCount = deviceExtension->WordsLeft;
            }

        } else {

            //
            // IDE path. Check if words left is at least 256.
            //

            if (deviceExtension->WordsLeft < wordsThisRequest) {

               //
               // Transfer only words requested.
               //

               wordCount = deviceExtension->WordsLeft;

            } else {

               //
               // Transfer next block.
               //

               wordCount = wordsThisRequest;
            }
        }
        //
        // Ensure that this is a write command.
        //

        if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {

           DebugPrint((3,
                      "AtapiInterrupt: Write interrupt\n"));

           WaitOnBusy( wLptBase, statusByte );


           WriteBuffer( wLptBase,
                        deviceExtension->DataBuffer,
                        wordCount);
        } else {

            DebugPrint((1,
                        "AtapiInterrupt: Int reason %x, but srb is for a write %x.\n",
                        requestReason,
                        Srb));

            //
            // Fail this request.
            //

            status = SRB_STATUS_ERROR;
            goto CompleteRequest;
        }


        //
        // Advance data buffer pointer and bytes left.
        //

        deviceExtension->DataBuffer += wordCount;
        deviceExtension->WordsLeft -= wordCount;

        return TRUE;

    } else if (requestReason == 0x2 && (statusByte & IDE_STATUS_DRQ)) {

        if (deviceExtension->DeviceFlags & DFLAGS_ATAPI_DEVICE) {

            wordCount  = Ide2LptInpByte( wLptBase, ATAPI_BYTE_COUNT_LOW );

            wordCount |= Ide2LptInpByte( wLptBase, ATAPI_BYTE_COUNT_LOW ) << 8;

            //
            // Covert bytes to words.
            //

            wordCount >>= 1;

            if (wordCount != deviceExtension->WordsLeft) {
                DebugPrint((3,
                           "AtapiInterrupt: %d words requested; %d words xferred\n",
                           deviceExtension->WordsLeft,
                           wordCount));
            }

            //
            // Verify this makes sense.
            //

            if (wordCount > deviceExtension->WordsLeft) {
                wordCount = deviceExtension->WordsLeft;
            }

            //
            // Verify this makes sense.
            //

            if (wordCount > deviceExtension->WordsLeft) {
                wordCount = deviceExtension->WordsLeft;
            }

        } else {

            //
            // Check if words left is at least 256.
            //

            if (deviceExtension->WordsLeft < wordsThisRequest) {

               //
               // Transfer only words requested.
               //

               wordCount = deviceExtension->WordsLeft;

            } else {

               //
               // Transfer next block.
               //

               wordCount = wordsThisRequest;
            }
        }

        //
        // Ensure that this is a read command.
        //

        if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {

           DebugPrint((3,
                      "AtapiInterrupt: Read interrupt\n"));

           WaitOnBusy(wLptBase,statusByte);

           ReadBuffer(wLptBase,
                         deviceExtension->DataBuffer,
                         wordCount);

        } else {

            DebugPrint((1,
                        "AtapiInterrupt: Int reason %x, but srb is for a read %x.\n",
                        requestReason,
                        Srb));

            //
            // Fail this request.
            //

            status = SRB_STATUS_ERROR;
            goto CompleteRequest;
        }

        //
        // Advance data buffer pointer and bytes left.
        //

        deviceExtension->DataBuffer += wordCount;
        deviceExtension->WordsLeft -= wordCount;

        //
        // Check for read command complete.
        //

        if (deviceExtension->WordsLeft == 0) {

            if (deviceExtension->DeviceFlags & DFLAGS_ATAPI_DEVICE) {

                //
                // Work around to make many atapi devices return correct sector size
                // of 2048. Also certain devices will have sector count == 0x00, check
                // for that also.
                //

                if ((Srb->Cdb[0] == 0x25) &&
                    ((deviceExtension->IdentifyData.GeneralConfiguration >> 8) & 0x1f) == 0x05) {

                    deviceExtension->DataBuffer -= wordCount;
                    if (deviceExtension->DataBuffer[0] == 0x00) {

                        *((ULONG *) &(deviceExtension->DataBuffer[0])) = 0xFFFFFF7F;

                    }

                    *((ULONG *) &(deviceExtension->DataBuffer[2])) = 0x00080000;
                    deviceExtension->DataBuffer += wordCount;
                }
            } else {

                //
                // Completion for IDE drives.
                //


                if (deviceExtension->WordsLeft) {

                    status = SRB_STATUS_DATA_OVERRUN;

                } else {

                    status = SRB_STATUS_SUCCESS;

                }

                goto CompleteRequest;

            }
        }

        return TRUE;

    } else if (requestReason == 0x3  && !(statusByte & IDE_STATUS_DRQ)) {

        //
        // Command complete.
        //

        if (deviceExtension->WordsLeft) {

            status = SRB_STATUS_DATA_OVERRUN;

        } else {

            status = SRB_STATUS_SUCCESS;

        }
CompleteRequest:

        if (status == SRB_STATUS_ERROR) {

            //
            // Map error to specific SRB status and handle request sense.
            //

            status = MapError(deviceExtension, Srb);

            deviceExtension->RDP = FALSE;

        } else {

            //
            // Wait for busy to drop.
            //

            for (i = 0; i < 30; i++) {
                GetStatus(wLptBase,statusByte);
                if (!(statusByte & IDE_STATUS_BUSY)) {
                    break;
                }
                ScsiPortStallExecution(500);
            }

            if (i == 30) {

                //
                // reset the controller.
                //

                DebugPrint((1,
                            "AtapiInterrupt: Resetting due to BSY still up - %x.\n",
                            statusByte
                          ));
                Ide2LptResetController(HwDeviceExtension,Srb->PathId);
                return TRUE;
            }

            //
            // Check to see if DRQ is still up.
            //

            if (statusByte & IDE_STATUS_DRQ) {

                for (i = 0; i < 500; i++) {
                    GetStatus( wLptBase,statusByte );
                    if (!(statusByte & IDE_STATUS_DRQ)) {
                        break;
                    }
                    ScsiPortStallExecution(100);

                }

                if (i == 500) {

                    //
                    // reset the controller.
                    //

                    DebugPrint((1,
                                "AtapiInterrupt: Resetting due to DRQ still up - %x\n",
                                statusByte));
                    Ide2LptResetController(HwDeviceExtension,Srb->PathId);
                    return TRUE;
                }

            }
        }

        //
        // Sanity check that there is a current request.
        //

        if (Srb != NULL) {

            //
            // Set status in SRB.
            //

            Srb->SrbStatus = (UCHAR)status;

            //
            // Check for underflow.
            //

            if (deviceExtension->WordsLeft) {

                //
                // Subtract out residual words and update if filemark hit,
                // setmark hit , end of data, end of media...
                //

                if (!(deviceExtension->DeviceFlags & DFLAGS_TAPE_DEVICE)) {
                if (status == SRB_STATUS_DATA_OVERRUN) {
                    Srb->DataTransferLength -= deviceExtension->WordsLeft;
                } else {
                    Srb->DataTransferLength = 0;
                }
                } else {
                    Srb->DataTransferLength -= deviceExtension->WordsLeft;
                }
            }

            /*
            //
            // Indicate command complete.
            //

            if (!(deviceExtension->RDP)) {
            ScsiPortNotification(RequestComplete,
                                deviceExtension,
                                Srb);

            }
            */

        } else {

            DebugPrint((1,
                       "AtapiInterrupt: No SRB!\n"));
        }

        /*

        //
        // Indicate ready for next request.
        //

        //
        // Clear current SRB.
        //

        deviceExtension->CurrentSrb = NULL;

        ScsiPortNotification(NextRequest,
                             deviceExtension,
                             NULL);
        */
        return TRUE;

    } else {

        //
        // Unexpected int.
        //

        DebugPrint((3,
                    "AtapiInterrupt: Unexpected interrupt. InterruptReason %x. Status %x.\n",
                    requestReason,
                    statusByte));
        return FALSE;
    }

    return TRUE;



}
///////////////////////////////////////////////////////////////////////
ULONG
IdeReadSector(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    UCHAR          statusByte;
    ULONG          i,wordCount;

    do{

        GetStatus(wLptBase, statusByte);

        for (i=0; i<100; i++) {
          GetBaseStatus( wLptBase, statusByte );
            if (statusByte & IDE_STATUS_BUSY) {
                ScsiPortStallExecution(100);
            } else if (statusByte & IDE_STATUS_DRQ) {
                break;
            } else {
                ScsiPortStallExecution(200);
            }
        }

        if( statusByte & IDE_STATUS_ERROR || !(statusByte & IDE_STATUS_DRQ) ){
            return SRB_STATUS_ERROR;
        }

        if( deviceExtension->WordsLeft < 256 ){
            wordCount = deviceExtension->WordsLeft;
        }else{
            wordCount = 256;
        }

        if( wordCount>0 ){
               ReadBuffer(wLptBase, deviceExtension->DataBuffer, wordCount);
        }

        deviceExtension->DataBuffer += wordCount;
        deviceExtension->WordsLeft -= wordCount;

    }while( deviceExtension->WordsLeft > 0 );
    return SRB_STATUS_SUCCESS;

}

///////////////////////////////////////////////////////////////////////
ULONG
IdeWriteSector(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    UCHAR          statusByte;
    ULONG          i,wordCount;

    do{

        GetStatus(wLptBase, statusByte);

        for (i=0; i<100; i++) {
          GetBaseStatus( wLptBase, statusByte );
            if (statusByte & IDE_STATUS_BUSY) {
                ScsiPortStallExecution(100);
            } else if (statusByte & IDE_STATUS_DRQ) {
                break;
            } else {
                ScsiPortStallExecution(200);
            }
        }

        if( statusByte & IDE_STATUS_ERROR || !(statusByte & IDE_STATUS_DRQ) ){
            return SRB_STATUS_ERROR;
        }

        if( deviceExtension->WordsLeft < 256 ){
            wordCount = deviceExtension->WordsLeft;
        }else{
            wordCount = 256;
        }

        if( wordCount>0 ){
               WriteBuffer(wLptBase, deviceExtension->DataBuffer, wordCount);
        }

        deviceExtension->DataBuffer += wordCount;
        deviceExtension->WordsLeft -= wordCount;

    }while( deviceExtension->WordsLeft > 0 );
    return SRB_STATUS_SUCCESS;

}
///////////////////////////////////////////////////////////////////////

ULONG
IdeReadWrite(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )

/*++

Routine Description:

    This routine handles IDE read and writes.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage
    Srb - IO request packet

Return Value:

    SRB status

--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    ULONG                startingSector,i;
    ULONG                wordCount;
    UCHAR                statusByte,statusByte2;
    UCHAR                cylinderHigh,cylinderLow,drvSelect,sectorNumber;
    ULONG                result;

    //
    // Select device 0.
    //


    WaitOnBusy( wLptBase, statusByte2 );

    Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 );

    WaitOnBusy( wLptBase, statusByte2 );

    if (statusByte2 & IDE_STATUS_BUSY) {
        DebugPrint((1,
                    "IdeReadWrite: Returning BUSY status\n"));
        return SRB_STATUS_BUSY;
    }

    //
    // Set data buffer pointer and words left.
    //

    deviceExtension->DataBuffer = (PUSHORT)Srb->DataBuffer;
    deviceExtension->WordsLeft = Srb->DataTransferLength / 2;

    //
    // Indicate expecting an interrupt.
    //

    //
    // Set up sector count register. Round up to next block.
    //

    Ide2LptOutByte( wLptBase, IDE_BLOCK_COUNT,
                           (UCHAR)((Srb->DataTransferLength + 0x1FF) / 0x200));

    //
    // Get starting sector number from CDB.
    //

    startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
                     ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
                     ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
                     ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;

    DebugPrint((2,
               "IdeReadWrite: Starting sector is %x, Number of bytes %x\n",
               startingSector,
               Srb->DataTransferLength));

    //
    // Set up sector number register.
    //

    sectorNumber =  (UCHAR)((startingSector % deviceExtension->IdentifyData.SectorsPerTrack) + 1);
    Ide2LptOutByte( wLptBase, IDE_BLOCK_NUMBER ,sectorNumber );

    //
    // Set up cylinder low register.
    //

    cylinderLow =  (UCHAR)(startingSector / (deviceExtension->IdentifyData.SectorsPerTrack *
                           deviceExtension->IdentifyData.NumberOfHeads));
    Ide2LptOutByte( wLptBase, IDE_CYLINDER_LOW, cylinderLow );

    //
    // Set up cylinder high register.
    //

    cylinderHigh = (UCHAR)((startingSector / (deviceExtension->IdentifyData.SectorsPerTrack *
                           deviceExtension->IdentifyData.NumberOfHeads)) >> 8);
    Ide2LptOutByte( wLptBase, IDE_CYLINDER_HIGH, cylinderHigh );

    //
    // Set up head and drive select register.
    //

    drvSelect = (UCHAR)(((startingSector / deviceExtension->IdentifyData.SectorsPerTrack) %
                      deviceExtension->IdentifyData.NumberOfHeads) | IDE_DRIVE_SELECT_1);

    Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD, drvSelect );

    DebugPrint((2,
               "IdeReadWrite: Cylinder %x Head %x Sector %x\n",
               startingSector /
               (deviceExtension->IdentifyData.SectorsPerTrack *
               deviceExtension->IdentifyData.NumberOfHeads),
               (startingSector /
               deviceExtension->IdentifyData.SectorsPerTrack) %
               deviceExtension->IdentifyData.NumberOfHeads,
               startingSector %
               deviceExtension->IdentifyData.SectorsPerTrack + 1));

    //
    // Check if write request.
    //

    if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
        Ide2LptOutByte( wLptBase, IDE_COMMAND, IDE_COMMAND_READ);
        ScsiPortStallExecution(10);
        result = IdeReadSector( deviceExtension, Srb );
    } else {
        Ide2LptOutByte( wLptBase, IDE_COMMAND, IDE_COMMAND_WRITE);
        ScsiPortStallExecution(10);
        result = IdeWriteSector( deviceExtension, Srb );
    }

    return result;

} // end IdeReadWrite()

///////////////////////////////////////////////////////////////////////
ULONG IdeVerify(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )

/*++

Routine Description:

    This routine handles IDE Verify.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage
    Srb - IO request packet

Return Value:

    SRB status

--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase = deviceExtension->wLptBase;
    ULONG                startingSector;
    ULONG                sectors;
    ULONG                endSector;
    USHORT               sectorCount;

    //
    // Drive has these number sectors.
    //

    sectors = deviceExtension->IdentifyData.SectorsPerTrack *
              deviceExtension->IdentifyData.NumberOfHeads *
              deviceExtension->IdentifyData.NumberOfCylinders;

    DebugPrint((3,
                "IdeVerify: Total sectors %x\n",
                sectors));

    //
    // Get starting sector number from CDB.
    //

    startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
                     ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
                     ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
                     ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;

    DebugPrint((3,
                "IdeVerify: Starting sector %x. Number of blocks %x\n",
                startingSector,
                ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb));

    sectorCount = (USHORT)(((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8 |
                           ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb );
    endSector = startingSector + sectorCount;

    DebugPrint((3,
                "IdeVerify: Ending sector %x\n",
                endSector));

    if (endSector > sectors) {

        //
        // Too big, round down.
        //

        DebugPrint((1,
                    "IdeVerify: Truncating request to %x blocks\n",
                    sectors - startingSector - 1));

        Ide2LptOutByte( wLptBase, IDE_BLOCK_COUNT, (UCHAR)(sectors - startingSector - 1) );

    } else {

        //
        // Set up sector count register. Round up to next block.
        //

        if (sectorCount > 0xFF) {
            sectorCount = (USHORT)0xFF;
        }

        Ide2LptOutByte( wLptBase, IDE_BLOCK_COUNT, (UCHAR)sectorCount );
    }

    //
    // Set data buffer pointer and words left.
    //

    deviceExtension->DataBuffer = (PUSHORT)Srb->DataBuffer;
    deviceExtension->WordsLeft = Srb->DataTransferLength / 2;

    //
    // Set up sector number register.
    //

    Ide2LptOutByte( wLptBase, IDE_BLOCK_NUMBER,
                           (UCHAR)((startingSector %
                           deviceExtension->IdentifyData.SectorsPerTrack) + 1)
                  );

    //
    // Set up cylinder low register.
    //

    Ide2LptOutByte( wLptBase, IDE_CYLINDER_LOW,
                           (UCHAR)(startingSector /
                           (deviceExtension->IdentifyData.SectorsPerTrack *
                           deviceExtension->IdentifyData.NumberOfHeads))
                  );

    //
    // Set up cylinder high register.
    //

    Ide2LptOutByte( wLptBase, IDE_CYLINDER_HIGH,
                           (UCHAR)((startingSector /
                           (deviceExtension->IdentifyData.SectorsPerTrack *
                           deviceExtension->IdentifyData.NumberOfHeads)) >> 8)
                  );

    //
    // Set up head and drive select register.
    //

    Ide2LptOutByte( wLptBase, IDE_DRIVE_HEAD,
                           (UCHAR)(((startingSector /
                           deviceExtension->IdentifyData.SectorsPerTrack) %
                           deviceExtension->IdentifyData.NumberOfHeads) |
                           IDE_DRIVE_SELECT_1)
                  );

    DebugPrint((2,
               "IdeVerify: Cylinder %x Head %x Sector %x\n",
               startingSector /
               (deviceExtension->IdentifyData.SectorsPerTrack *
               deviceExtension->IdentifyData.NumberOfHeads),
               (startingSector /
               deviceExtension->IdentifyData.SectorsPerTrack) %
               deviceExtension->IdentifyData.NumberOfHeads,
               startingSector %
               deviceExtension->IdentifyData.SectorsPerTrack + 1));


    //
    // Send verify command.
    //

    Ide2LptOutByte( wLptBase, IDE_COMMAND, IDE_COMMAND_VERIFY);

    ScsiPortStallExecution(10);

    return ProceedRequest( deviceExtension, Srb );

} // end IdeVerify()
///////////////////////////////////////////////////////////////////////
ULONG IdeSendCommand(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )

/*++

Routine Description:

    Program ATA registers for IDE disk transfer.

Arguments:

    HwDeviceExtension - ATAPI driver storage.
    Srb - System request block.

Return Value:

    SRB status (pending if all goes well).

--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    WORD wLptBase  = deviceExtension->wLptBase;
    PCDB cdb;

    UCHAR statusByte,errorByte;
    ULONG status;
    ULONG i;
    PMODE_PARAMETER_HEADER   modeData;

    DebugPrint((2,
               "IdeSendCommand: Command %x to device %d\n",
               Srb->Cdb[0],
               Srb->TargetId));



    switch (Srb->Cdb[0]) {
    case SCSIOP_INQUIRY:

        //
        // Filter out all TIDs but 0 and 1 since this is an IDE interface
        // which support up to two devices.
        //

        if ((Srb->Lun != 0) ||
            (!deviceExtension->DeviceFlags & DFLAGS_DEVICE_PRESENT)) {

            //
            // Indicate no device found at this address.
            //

            status = SRB_STATUS_SELECTION_TIMEOUT;
            break;

        } else {

            PINQUIRYDATA    inquiryData  = Srb->DataBuffer;
            PIDENTIFY_DATA2 identifyData = &deviceExtension->IdentifyData;

            //
            // Zero INQUIRY data structure.
            //

            for (i = 0; i < Srb->DataTransferLength; i++) {
               ((PUCHAR)Srb->DataBuffer)[i] = 0;
            }

            //
            // Standard IDE interface only supports disks.
            //

            inquiryData->DeviceType = DIRECT_ACCESS_DEVICE;

            //
            // Set the removable bit, if applicable.
            //

            if (deviceExtension->DeviceFlags & DFLAGS_REMOVABLE_DRIVE) {
                inquiryData->RemovableMedia = 1;
            }

            //
            // Fill in vendor identification fields.
            //

            for (i = 0; i < 20; i += 2) {
               inquiryData->VendorId[i] =
                   ((PUCHAR)identifyData->ModelNumber)[i + 1];
               inquiryData->VendorId[i+1] =
                   ((PUCHAR)identifyData->ModelNumber)[i];
            }

            //
            // Initialize unused portion of product id.
            //

            for (i = 0; i < 4; i++) {
               inquiryData->ProductId[12+i] = ' ';
            }

            //
            // Move firmware revision from IDENTIFY data to
            // product revision in INQUIRY data.
            //

            for (i = 0; i < 4; i += 2) {
               inquiryData->ProductRevisionLevel[i] =
                   ((PUCHAR)identifyData->FirmwareRevision)[i+1];
               inquiryData->ProductRevisionLevel[i+1] =
                   ((PUCHAR)identifyData->FirmwareRevision)[i];
            }

            status = SRB_STATUS_SUCCESS;
        }

        break;

    case SCSIOP_MODE_SENSE:

        status = SRB_STATUS_INVALID_REQUEST;
        break;

    case SCSIOP_TEST_UNIT_READY:

        status = SRB_STATUS_SUCCESS;
        break;

    case SCSIOP_READ_CAPACITY:

        //
        // Claim 512 byte blocks (big-endian).
        //

        ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock = 0x20000;

       //
       // Calculate last sector.
       //


       i = (deviceExtension->IdentifyData.NumberOfHeads *
            deviceExtension->IdentifyData.NumberOfCylinders *
            deviceExtension->IdentifyData.SectorsPerTrack) - 1;

       ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress =
           (((PUCHAR)&i)[0] << 24) |  (((PUCHAR)&i)[1] << 16) |
           (((PUCHAR)&i)[2] << 8) | ((PUCHAR)&i)[3];

       DebugPrint((1,
                  "IDE disk %x - #sectors %x, #heads %x, #cylinders %x\n",
                  Srb->TargetId,
                  deviceExtension->IdentifyData.SectorsPerTrack,
                  deviceExtension->IdentifyData.NumberOfHeads,
                  deviceExtension->IdentifyData.NumberOfCylinders));


       status = SRB_STATUS_SUCCESS;
       break;

    case SCSIOP_VERIFY:
       status = IdeVerify(HwDeviceExtension,Srb);

       break;

    case SCSIOP_READ:
    case SCSIOP_WRITE:

       status = IdeReadWrite(HwDeviceExtension,
                                  Srb);
       break;

    default:

       DebugPrint((1,
                  "IdeSendCommand: Unsupported command %x\n",
                  Srb->Cdb[0]));

       status = SRB_STATUS_INVALID_REQUEST;

    } // end switch

    return status;

} // end IdeSendCommand()

///////////////////////////////////////////////////////////////////////
BOOLEAN Ide2LptStartIo(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )

/*++

Routine Description:

    This routine is called from the SCSI port driver synchronized
    with the kernel to start an IO request.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage
    Srb - IO request packet

Return Value:

    TRUE

--*/

{
    PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
    ULONG status;

    if( Srb->TargetId > 0 ){
        status = SRB_STATUS_SELECTION_TIMEOUT;
    }else{

        //
        // Determine which function.
        //

        switch (Srb->Function) {

        case SRB_FUNCTION_EXECUTE_SCSI:

            //
            // Sanity check. Only one request can be outstanding on a
            // controller.
            //

            if (deviceExtension->CurrentSrb) {

                DebugPrint((1,
                           "AtapiStartIo: Already have a request!\n"));
                Srb->SrbStatus = SRB_STATUS_BUSY;
                ScsiPortNotification(RequestComplete,
                                     deviceExtension,
                                     Srb);
                return FALSE;
            }

            //
            // Indicate that a request is active on the controller.
            //

            deviceExtension->CurrentSrb = Srb;

            //
            // Send command to device.
            //

            if (deviceExtension->DeviceFlags & DFLAGS_ATAPI_DEVICE) {

               //status = AtapiSendCommand(HwDeviceExtension, Srb);
               status = SRB_STATUS_SELECTION_TIMEOUT;

            } else if (deviceExtension->DeviceFlags & DFLAGS_DEVICE_PRESENT) {

                status = IdeSendCommand(HwDeviceExtension,
                                            Srb);
            } else {

                status = SRB_STATUS_SELECTION_TIMEOUT;
            }

            break;

        case SRB_FUNCTION_ABORT_COMMAND:

            //
            // Verify that SRB to abort is still outstanding.
            //

            if (!deviceExtension->CurrentSrb) {

                DebugPrint((1, "AtapiStartIo: SRB to abort already completed\n"));

                //
                // Complete abort SRB.
                //

                status = SRB_STATUS_ABORT_FAILED;

                break;
            }

            //
            // Abort function indicates that a request timed out.
            // Call reset routine. Card will only be reset if
            // status indicates something is wrong.
            // Fall through to reset code.
            //

        case SRB_FUNCTION_RESET_BUS:

            //
            // Reset Atapi and SCSI bus.
            //

            DebugPrint((1, "AtapiStartIo: Reset bus request received\n"));

            if (!Ide2LptResetController(deviceExtension,
                                 Srb->PathId)) {

                  DebugPrint((1,"AtapiStartIo: Reset bus failed\n"));

                //
                // Log reset failure.
                //

                ScsiPortLogError(
                    HwDeviceExtension,
                    NULL,
                    0,
                    0,
                    0,
                    SP_INTERNAL_ADAPTER_ERROR,
                    5 << 8
                 );

                status = SRB_STATUS_ERROR;

            } else {

                  status = SRB_STATUS_SUCCESS;
            }

            break;

        default:

            //
            // Indicate unsupported command.
            //

            status = SRB_STATUS_INVALID_REQUEST;

            break;

        } // end switch
    }

    //
    // Check if command complete.
    //

    if (status == SRB_STATUS_ERROR) {
        MapError( deviceExtension, deviceExtension->CurrentSrb );
    }

    DebugPrint((2,
               "AtapiStartIo: Srb %x complete with status %x\n",
               Srb,
               status));


    //
    // Set status in SRB.
    //

    Srb->SrbStatus = (UCHAR)status;

    //
    // Indicate command complete.
    //

    ScsiPortNotification(RequestComplete,
                        deviceExtension,
                        Srb);

    //
    // Clear current SRB.
    //
    deviceExtension->CurrentSrb = NULL;

    //
    // Indicate ready for next request.
    //

    ScsiPortNotification(NextRequest,
                         deviceExtension,
                         NULL);

    return TRUE;

} // end AtapiStartIo()

///////////////////////////////////////////////////////////////////////

ULONG DriverEntry( IN PVOID DriverObject, IN PVOID Argument2 )

/*++

Routine Description:

    Installable driver initialization entry point for system.

Arguments:

    Driver Object

Return Value:

    Status from ScsiPortInitialize()

--*/

{
    HW_INITIALIZATION_DATA hwInitializationData;
    ULONG                  adapterCount;
    ULONG                  statusToReturn;

    DebugPrint((1,"\n\nIDE2LPT MiniPort Driver\n"));

    statusToReturn = 0xffffffff;

    Ide2LptZeroMemory(((PUCHAR)&hwInitializationData), sizeof(HW_INITIALIZATION_DATA));

    hwInitializationData.HwInitializationDataSize =
      sizeof(HW_INITIALIZATION_DATA);

    hwInitializationData.HwInitialize = Ide2LptHwInitialize;
    hwInitializationData.HwResetBus = Ide2LptResetController;
    hwInitializationData.HwStartIo = Ide2LptStartIo;
    hwInitializationData.HwFindAdapter = Ide2LptFindController;

    hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
    hwInitializationData.SpecificLuExtensionSize = sizeof(HW_LU_EXTENSION);

    hwInitializationData.AdapterInterfaceType = Isa;

    statusToReturn =  ScsiPortInitialize(DriverObject,
                                    Argument2,
                                    &hwInitializationData,
                                    &adapterCount);

    return statusToReturn;

} // end DriverEntry()
