/*++

Copyright (c) 2002-2011 Alexandr A. Telyatnikov (Alter)

Module Name:
    id_ata.cpp

Abstract:
    This is the miniport driver for ATA/ATAPI IDE controllers
    with Busmaster DMA and Serial ATA support

Author:
    Alexander A. Telyatnikov (Alter)

Environment:
    kernel mode only

Notes:

    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Revision History:

    The skeleton was taken from standard ATAPI.SYS from NT4 DDK by
         Mike Glass (MGlass)
         Chuck Park (ChuckP)

    Some parts of code were taken from FreeBSD 4.3-6.1 ATA driver by
         Søren Schmidt, Copyright (c) 1998-2007

    All parts of code are greatly changed/updated by
         Alter, Copyright (c) 2002-2007:

    1. Internal command queueing/reordering
    2. Drive identification
    3. Support for 2 _independent_ channels in a single PCI device
    4. Smart host<->drive transfer rate slowdown (for bad cable)
    5. W2k support (binary compatibility)
    6. HDD hot swap under NT4
    7. XP support (binary compatibility)
    8. Serial ATA (SATA/SATA2) support
    9. NT 3.51 support (binary compatibility)
    
    etc. (See todo.txt)


--*/

#include "stdafx.h"

#ifndef UNIATA_CORE

static const CHAR ver_string[] = "\n\nATAPI IDE MiniPort Driver (UniATA) v 0." UNIATA_VER_STR "\n";

static const CHAR uniata_comm_name[] = UNIATA_COMM_PORT_VENDOR_STR "    \n";

UNICODE_STRING SavedRegPath;
WCHAR SavedRegPathBuffer[256];

#endif //UNIATA_CORE

UCHAR AtaCommands48[256];
UCHAR AtaCommandFlags[256];

ULONG  SkipRaids = 1;
ULONG  ForceSimplex = 0;

LONGLONG g_Perf = 0;
ULONG    g_PerfDt = 0;

#ifdef _DEBUG
ULONG  g_LogToDisplay = 0;
#endif //_DEBUG

ULONG  g_WaitBusyInISR = 1;

ULONG  g_opt_WaitBusyCount = 200; // 20000
ULONG  g_opt_WaitBusyDelay = 10;  // 150
ULONG  g_opt_WaitDrqDelay  = 10; // 100
BOOLEAN g_opt_AtapiSendDisableIntr = 1; // 0
BOOLEAN g_opt_AtapiDmaRawRead = 1; // 0

BOOLEAN InDriverEntry = TRUE;

BOOLEAN g_opt_Verbose = 0;

BOOLEAN WinVer_WDM_Model = FALSE;

//UCHAR EnableDma = FALSE;
//UCHAR EnableReorder = FALSE;

UCHAR g_foo = 0;

BOOLEAN
NTAPI
AtapiResetController__(
    IN PVOID HwDeviceExtension,
    IN ULONG PathId,
    IN UCHAR CompleteType
    );

VOID
NTAPI
AtapiHwInitialize__(
    IN PHW_DEVICE_EXTENSION deviceExtension,
    IN ULONG lChannel
    );

#define RESET_COMPLETE_CURRENT  0x00
#define RESET_COMPLETE_ALL      0x01
#define RESET_COMPLETE_NONE     0x02

#ifndef UNIATA_CORE

VOID
NTAPI
AtapiCallBack_X(
    IN PVOID HwDeviceExtension
    );

#ifdef UNIATA_USE_XXableInterrupts
  #define RETTYPE_XXableInterrupts   BOOLEAN
  #define RETVAL_XXableInterrupts    TRUE
#else
  #define RETTYPE_XXableInterrupts   VOID
  #define RETVAL_XXableInterrupts
#endif

RETTYPE_XXableInterrupts
NTAPI
AtapiInterruptDpc(
    IN PVOID HwDeviceExtension
    );

RETTYPE_XXableInterrupts
NTAPI
AtapiEnableInterrupts__(
    IN PVOID HwDeviceExtension
    );

VOID
NTAPI
AtapiQueueTimerDpc(
    IN PVOID HwDeviceExtension,
    IN ULONG lChannel,
    IN PHW_TIMER HwScsiTimer,
    IN ULONG MiniportTimerValue
    );

SCSI_ADAPTER_CONTROL_STATUS
NTAPI
AtapiAdapterControl(
    IN PVOID HwDeviceExtension,
    IN SCSI_ADAPTER_CONTROL_TYPE ControlType,
    IN PVOID Parameters
    );

#endif //UNIATA_CORE

BOOLEAN
NTAPI
AtapiCheckInterrupt__(
    IN PVOID HwDeviceExtension,
    IN UCHAR c
    );


#ifndef UNIATA_CORE

BOOLEAN
NTAPI
AtapiRegGetStringParameterValue(
    IN PWSTR RegistryPath,
    IN PWSTR Name,
    IN PWCHAR Str,
    IN ULONG MaxLen
    )
{
#define ITEMS_TO_QUERY 2 // always 1 greater than what is searched 
    NTSTATUS          status;
    RTL_QUERY_REGISTRY_TABLE parameters[ITEMS_TO_QUERY];
    UNICODE_STRING ustr;

    ustr.Buffer = Str;
    ustr.Length =
    ustr.MaximumLength = (USHORT)MaxLen;
    RtlZeroMemory(parameters, (sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY));

    parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
    parameters[0].Name          = Name;
    parameters[0].EntryContext  = &ustr;
    parameters[0].DefaultType   = REG_SZ;
    parameters[0].DefaultData   = Str;
    parameters[0].DefaultLength = MaxLen;

    status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE /*| RTL_REGISTRY_OPTIONAL*/,
                                    RegistryPath, parameters, NULL, NULL);

    if(!NT_SUCCESS(status))
        return FALSE;

    return TRUE;

#undef ITEMS_TO_QUERY
} // end AtapiRegGetStringParameterValue()


#endif //UNIATA_CORE

VOID
DDKFASTAPI
UniataNanoSleep(
    ULONG nano
    )
{
    LONGLONG t;
    LARGE_INTEGER t0;

#ifdef NAVO_TEST
    return;
#endif //NAVO_TEST

    if(!nano || !g_Perf || !g_PerfDt)
        return;
    t = (g_Perf * nano) / g_PerfDt / 1000;
    if(!t) {
        t = 1;
    }
    do {
        KeQuerySystemTime(&t0);
        t--;
    } while(t);
} // end UniataNanoSleep()

#define AtapiWritePortN_template(_type, _Type, sz) \
VOID \
DDKFASTAPI \
AtapiWritePort##sz( \
    IN PHW_CHANNEL chan, \
    IN ULONGIO_PTR _port, \
    IN _type  data \
    ) \
{ \
    PIORES res; \
    if(_port >= IDX_MAX_REG) { \
        res = (PIORES)(_port);  \
    } else \
    if(chan) { \
        res = &chan->RegTranslation[_port];  \
    } else {                                     \
        KdPrint(("invalid io write request @ ch %x, res* %x\n", chan, _port)); \
        return; \
    } \
    if(res->Proc) {             \
    } else  \
    if(!res->MemIo) {             \
        ScsiPortWritePort##_Type((_type*)(res->Addr), data); \
    } else {                                      \
        /*KdPrint(("r_mem @ (%x) %x\n", _port, port));*/ \
        ScsiPortWriteRegister##_Type((_type*)(res->Addr), data); \
    }                                                        \
    return;                                                  \
}

AtapiWritePortN_template(ULONG,  Ulong,  4);
AtapiWritePortN_template(USHORT, Ushort, 2);
AtapiWritePortN_template(UCHAR,  Uchar,  1);

#define AtapiWritePortExN_template(_type, _Type, sz) \
VOID \
DDKFASTAPI \
AtapiWritePortEx##sz( \
    IN PHW_CHANNEL chan, \
    IN ULONGIO_PTR _port, \
    IN ULONG offs, \
    IN _type  data \
    ) \
{ \
    PIORES res; \
    if(_port >= IDX_MAX_REG) { \
        res = (PIORES)(_port);  \
    } else \
    if(chan) { \
        res = &chan->RegTranslation[_port];  \
    } else {                                     \
        KdPrint(("invalid io write request @ ch %x, res* %x, offs %x\n", chan, _port, offs)); \
        return; \
    } \
    if(res->Proc) {             \
    } else  \
    if(!res->MemIo) {             \
        ScsiPortWritePort##_Type((_type*)(res->Addr+offs), data); \
    } else {                                      \
        /*KdPrint(("r_mem @ (%x) %x\n", _port, port));*/ \
        ScsiPortWriteRegister##_Type((_type*)(res->Addr+offs), data); \
    }                                                        \
    return;                                                  \
}

AtapiWritePortExN_template(ULONG,  Ulong,  4);
//AtapiWritePortExN_template(USHORT, Ushort, 2);
AtapiWritePortExN_template(UCHAR,  Uchar,  1);

#define AtapiReadPortN_template(_type, _Type, sz) \
_type \
DDKFASTAPI \
AtapiReadPort##sz( \
    IN PHW_CHANNEL chan, \
    IN ULONGIO_PTR _port \
    ) \
{ \
    PIORES res; \
    if(_port >= IDX_MAX_REG) { \
        res = (PIORES)(_port);  \
    } else \
    if(chan) { \
        res = &chan->RegTranslation[_port];  \
    } else {                                     \
        KdPrint(("invalid io read request @ ch %x, res* %x\n", chan, _port)); \
        return (_type)(-1); \
    } \
    if(res->Proc) {             \
        return 0; \
    } else  \
    if(!res->MemIo) {             \
        /*KdPrint(("r_io @ (%x) %x\n", _port, res->Addr));*/ \
        return ScsiPortReadPort##_Type((_type*)(res->Addr)); \
    } else {                                      \
        /*KdPrint(("r_mem @ (%x) %x\n", _port, res->Addr));*/ \
        return ScsiPortReadRegister##_Type((_type*)(res->Addr)); \
    }                                                        \
}

AtapiReadPortN_template(ULONG,  Ulong,  4);
AtapiReadPortN_template(USHORT, Ushort, 2);
AtapiReadPortN_template(UCHAR,  Uchar,  1);

#define AtapiReadPortExN_template(_type, _Type, sz) \
_type \
DDKFASTAPI \
AtapiReadPortEx##sz( \
    IN PHW_CHANNEL chan, \
    IN ULONGIO_PTR _port, \
    IN ULONG offs \
    ) \
{ \
    PIORES res; \
    if(_port >= IDX_MAX_REG) { \
        res = (PIORES)(_port);  \
    } else \
    if(chan) { \
        res = &chan->RegTranslation[_port];  \
    } else {                                     \
        KdPrint(("invalid io read request @ ch %x, res* %x, offs %x\n", chan, _port, offs)); \
        return (_type)(-1); \
    } \
    if(res->Proc) {             \
        return 0; \
    } else  \
    if(!res->MemIo) {             \
        return ScsiPortReadPort##_Type((_type*)(res->Addr+offs)); \
    } else {                                      \
        /*KdPrint(("r_mem @ (%x) %x\n", _port, port));*/ \
        return ScsiPortReadRegister##_Type((_type*)(res->Addr+offs)); \
    }                                                        \
}

AtapiReadPortExN_template(ULONG,  Ulong,  4);
//AtapiReadPortExN_template(USHORT, Ushort, 2);
AtapiReadPortExN_template(UCHAR,  Uchar,  1);

#define AtapiReadPortBufferN_template(_type, _Type, sz) \
VOID \
DDKFASTAPI \
AtapiReadBuffer##sz( \
    IN PHW_CHANNEL chan, \
    IN ULONGIO_PTR _port, \
    IN PVOID Buffer, \
    IN ULONG Count,   \
    IN ULONG Timing   \
    ) \
{ \
    PIORES res; \
                 \
    if(Timing) { \
        while(Count) { \
            (*((_type*)Buffer)) = AtapiReadPort##sz(chan, _port); \
            Count--; \
            Buffer = ((_type*)Buffer)+1; \
            UniataNanoSleep(Timing); \
        } \
        return; \
    } \
           \
    if(_port >= IDX_MAX_REG) { \
        res = (PIORES)(_port);  \
    } else \
    if(chan) { \
        res = &chan->RegTranslation[_port];  \
    } else {                                     \
        KdPrint(("invalid io read request @ ch %x, res* %x\n", chan, _port)); \
        return; \
    } \
    if(!res->MemIo) {             \
        /*KdPrint(("r_io @ (%x) %x\n", _port, res->Addr));*/ \
        ScsiPortReadPortBuffer##_Type((_type*)(res->Addr), (_type*)Buffer, Count); \
        return; \
    }                                                        \
    while(Count) { \
        (*((_type*)Buffer)) = ScsiPortReadRegister##_Type((_type*)(res->Addr)); \
        Count--; \
        Buffer = ((_type*)Buffer)+1; \
    } \
    return;                                                  \
}

#define AtapiWritePortBufferN_template(_type, _Type, sz) \
VOID \
DDKFASTAPI \
AtapiWriteBuffer##sz( \
    IN PHW_CHANNEL chan, \
    IN ULONGIO_PTR _port, \
    IN PVOID Buffer, \
    IN ULONG Count,   \
    IN ULONG Timing   \
    ) \
{ \
    PIORES res; \
                 \
    if(Timing) { \
        while(Count) { \
            AtapiWritePort##sz(chan, _port, *((_type*)Buffer)); \
            Buffer = ((_type*)Buffer)+1; \
            Count--; \
            UniataNanoSleep(Timing); \
        } \
        return;                                                  \
    } \
           \
    if(_port >= IDX_MAX_REG) { \
        res = (PIORES)(_port);  \
    } else \
    if(chan) { \
        res = &chan->RegTranslation[_port];  \
    } else {                                     \
        KdPrint(("invalid io write request @ ch %x, res* %x\n", chan, _port)); \
        return; \
    } \
    if(!res->MemIo) {             \
        /*KdPrint(("r_io @ (%x) %x\n", _port, res->Addr));*/ \
        ScsiPortWritePortBuffer##_Type((_type*)(res->Addr), (_type*)Buffer, Count); \
        return; \
    }                                                        \
    while(Count) { \
        ScsiPortWriteRegister##_Type((_type*)(res->Addr), *((_type*)Buffer)); \
        Count--; \
        Buffer = ((_type*)Buffer)+1; \
    } \
    return;                                                  \
}

AtapiWritePortBufferN_template(ULONG,  Ulong,  4);
AtapiWritePortBufferN_template(USHORT, Ushort, 2);

AtapiReadPortBufferN_template(ULONG,  Ulong,  4);
AtapiReadPortBufferN_template(USHORT, Ushort, 2);


UCHAR
DDKFASTAPI
AtapiSuckPort2(
    IN PHW_CHANNEL chan
    )
{
    UCHAR statusByte;
    ULONG i;

    WaitOnBusyLong(chan);
    for (i = 0; i < 0x10000; i++) {

        GetStatus(chan, statusByte);
        if (statusByte & IDE_STATUS_DRQ) {
            // Suck out any remaining bytes and throw away.
            AtapiReadPort2(chan, IDX_IO1_i_Data);
        } else {
            break;
        }
    }
    if(i) {
        KdPrint2((PRINT_PREFIX "AtapiSuckPort2: overrun detected (%#x words)\n", i ));
    }
    return statusByte;
} // AtapiSuckPort2()

UCHAR
DDKFASTAPI
WaitOnBusy(
    IN PHW_CHANNEL   chan
    )
{
    ULONG i;
    UCHAR Status;
    for (i=0; i<200; i++) {
        GetStatus(chan, Status);
        if (Status & IDE_STATUS_BUSY) {
            AtapiStallExecution(10);
            continue;
        } else {
            break;
        }
    }
    return Status;
} // end WaitOnBusy()

UCHAR
DDKFASTAPI
WaitOnBusyLong(
    IN PHW_CHANNEL   chan
    )
{
    ULONG i;
    UCHAR Status;

    Status = WaitOnBusy(chan);
    if(!(Status & IDE_STATUS_BUSY))
        return Status;
    for (i=0; i<2000; i++) {
        GetStatus(chan, Status);
        if (Status & IDE_STATUS_BUSY) {
            AtapiStallExecution(250);
            continue;
        } else {
            break;
        }
    }
    return Status;
} // end WaitOnBusyLong()

UCHAR
DDKFASTAPI
WaitOnBaseBusy(
    IN PHW_CHANNEL   chan
    )
{
    ULONG i;
    UCHAR Status;
    for (i=0; i<g_opt_WaitBusyCount; i++) {
        GetBaseStatus(chan, Status);
        if (Status & IDE_STATUS_BUSY) {
            AtapiStallExecution(g_opt_WaitBusyDelay);
            continue;
        } else {
            break;
        }
    }
    return Status;
} // end WaitOnBaseBusy()

UCHAR
DDKFASTAPI
WaitOnBaseBusyLong(
    IN PHW_CHANNEL   chan
    )
{
    ULONG i;
    UCHAR Status;

    Status = WaitOnBaseBusy(chan);
    if(!(Status & IDE_STATUS_BUSY))
        return Status;
    for (i=0; i<2000; i++) {
        GetBaseStatus(chan, Status);
        if (Status & IDE_STATUS_BUSY) {
            AtapiStallExecution(250);
            continue;
        } else {
            break;
        }
    }
    return Status;
} // end WaitOnBaseBusyLong()

UCHAR
DDKFASTAPI
UniataIsIdle(
    IN struct _HW_DEVICE_EXTENSION* deviceExtension,
    IN UCHAR Status
    )
{
    UCHAR Status2;

    if(Status == 0xff) {
        return 0xff;
    }
    if(Status & IDE_STATUS_BUSY) {
        return Status;
    }
//    if(deviceExtension->HwFlags & UNIATA_SATA) {
    if(UniataIsSATARangeAvailable(deviceExtension, 0)) {
        if(Status & (IDE_STATUS_BUSY | IDE_STATUS_ERROR)) {
            return Status;
        }
    } else {
        Status2 = Status & ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX);
        if ((Status & IDE_STATUS_BUSY) ||
            (Status2 != IDE_STATUS_IDLE && Status2 != IDE_STATUS_DRDY)) {
            return Status;
        }
    }
    return IDE_STATUS_IDLE;
} // end UniataIsIdle()

UCHAR
DDKFASTAPI
WaitForIdleLong(
    IN PHW_CHANNEL   chan
    )
{
    ULONG i;
    UCHAR Status;
    UCHAR Status2;
    for (i=0; i<20000; i++) {
        GetStatus(chan, Status);
        Status2 = UniataIsIdle(chan->DeviceExtension, Status);
        if(Status2 == 0xff) {
            // no drive ?
            break;
        } else
        if(Status2 & IDE_STATUS_BUSY) {
            AtapiStallExecution(10);
            continue;
        } else {
            break;
        }
    }
    return Status;
} // end WaitForIdleLong()

UCHAR
DDKFASTAPI
WaitForDrq(
    IN PHW_CHANNEL   chan
    )
{
    ULONG i;
    UCHAR Status;
    for (i=0; i<1000; i++) {
        GetStatus(chan, Status);
        if (Status & IDE_STATUS_BUSY) {
            AtapiStallExecution(g_opt_WaitDrqDelay);
        } else if (Status & IDE_STATUS_DRQ) {
            break;
        } else {
            AtapiStallExecution(g_opt_WaitDrqDelay*2);
        }
    }
    return Status;
} // end WaitForDrq()

UCHAR
DDKFASTAPI
WaitShortForDrq(
    IN PHW_CHANNEL   chan
    )
{
    ULONG i;
    UCHAR Status;
    for (i=0; i<2; i++) {
        GetStatus(chan, Status);
        if (Status & IDE_STATUS_BUSY) {
            AtapiStallExecution(g_opt_WaitDrqDelay);
        } else if (Status & IDE_STATUS_DRQ) {
            break;
        } else {
            AtapiStallExecution(g_opt_WaitDrqDelay);
        }
    }
    return Status;
} // end WaitShortForDrq()

VOID
DDKFASTAPI
AtapiSoftReset(
    IN PHW_CHANNEL   chan,
    ULONG            DeviceNumber
    )
{
    ULONG c = chan->lChannel;
    ULONG i = 30 * 1000;
    UCHAR dma_status = 0;
    KdPrint2((PRINT_PREFIX "AtapiSoftReset:\n"));
    UCHAR statusByte2;

    GetBaseStatus(chan, statusByte2);
    KdPrint2((PRINT_PREFIX "  statusByte2 %x:\n", statusByte2));
    SelectDrive(chan, DeviceNumber);
    AtapiStallExecution(500);
    AtapiWritePort1(chan, IDX_IO1_o_Command, IDE_COMMAND_ATAPI_RESET);

    // ReactOS modification: Already stop looping when we know that the drive has finished resetting.
    // Not all controllers clear the IDE_STATUS_BUSY flag (e.g. not the VMware one), so ensure that
    // the maximum waiting time (30 * i = 0.9 seconds) does not exceed the one of the original
    // implementation. (which is around 1 second)
    while ((AtapiReadPort1(chan, IDX_IO1_i_Status) & IDE_STATUS_BUSY) &&
           i--)
    {
        AtapiStallExecution(30);
    }

    SelectDrive(chan, DeviceNumber);
    WaitOnBusy(chan);
    GetBaseStatus(chan, statusByte2);
    AtapiStallExecution(500);

    GetBaseStatus(chan, statusByte2);
    if(chan && chan->DeviceExtension) {
        dma_status = GetDmaStatus(chan->DeviceExtension, chan->lChannel);
        KdPrint2((PRINT_PREFIX "  DMA status %#x\n", dma_status));
    } else {
        KdPrint2((PRINT_PREFIX "  can't get DMA status\n"));
    }
    if(dma_status & BM_STATUS_INTR) {
        // bullshit, we have DMA interrupt, but had never initiate DMA operation
        KdPrint2((PRINT_PREFIX "  clear unexpected DMA intr on ATAPI reset\n"));
        AtapiDmaDone(chan->DeviceExtension, DeviceNumber, chan->lChannel, NULL);
        GetBaseStatus(chan, statusByte2);
    }
    if(chan->DeviceExtension->HwFlags & UNIATA_SATA) {
        UniataSataClearErr(chan->DeviceExtension, chan->lChannel, UNIATA_SATA_IGNORE_CONNECT, DeviceNumber);
/*        if(!(chan->ChannelCtrlFlags & CTRFLAGS_NO_SLAVE)) {
            UniataSataClearErr(chan->DeviceExtension, chan->lChannel, UNIATA_SATA_IGNORE_CONNECT, 1);
        }*/
    }
    return;

} // end AtapiSoftReset()

/*
    Send command to device.
    Translate to 48-Lba form if required
*/
UCHAR
NTAPI
AtaCommand48(
    IN PHW_DEVICE_EXTENSION deviceExtension,
    IN ULONG DeviceNumber,
    IN ULONG lChannel,
    IN UCHAR command,
    IN ULONGLONG lba,
    IN USHORT count,
    IN USHORT feature,
    IN ULONG flags
    )
{
    PHW_CHANNEL          chan = &deviceExtension->chan[lChannel];
    UCHAR                statusByte;
    ULONG ldev = lChannel*2 + DeviceNumber;
    ULONG i;
    PUCHAR plba;

    KdPrint2((PRINT_PREFIX "AtaCommand48: cntrlr %#x:%#x ldev %#x, cmd %#x, lba %#I64x count %#x feature %#x\n",
                 deviceExtension->DevIndex, deviceExtension->Channel, ldev, command, lba, count, feature ));

    //if(!(deviceExtension->HwFlags & UNIATA_SATA)) {
        SelectDrive(chan, DeviceNumber);

        statusByte = WaitOnBusy(chan);

        /* ready to issue command ? */
        if (statusByte & IDE_STATUS_BUSY) {
            KdPrint2((PRINT_PREFIX "  Returning BUSY status\n"));
            return statusByte;
        }
    //}
    // !!! We should not check ERROR condition here
    // ERROR bit may be asserted durring previous operation
    // and not cleared after SELECT

    //>>>>>> NV: 2006/08/03
    if((AtaCommandFlags[command] & ATA_CMD_FLAG_LBAIOsupp) &&
       CheckIfBadBlock(&(deviceExtension->lun[ldev]), lba, count)) {
        KdPrint3((PRINT_PREFIX ": artificial bad block, lba %#I64x count %#x\n", lba, count));
        return IDE_STATUS_ERROR;
        //return SRB_STATUS_ERROR;
    }
    //<<<<<< NV:  2006/08/03

    /* only use 48bit addressing if needed because of the overhead */
    if ((lba >= ATA_MAX_LBA28 || count > 256) &&
        deviceExtension->lun[ldev].IdentifyData.FeaturesSupport.Address48) {

        KdPrint2((PRINT_PREFIX "  ldev %#x USE_LBA_48\n", ldev ));
        /* translate command into 48bit version */
        if(AtaCommandFlags[command] & ATA_CMD_FLAG_48supp) {
            command = AtaCommands48[command];
        } else {
            KdPrint2((PRINT_PREFIX "  unhandled LBA48 command\n"));
			return (UCHAR)-1;
        }

        chan->ChannelCtrlFlags |= CTRFLAGS_LBA48;
        plba = (PUCHAR)&lba;

        AtapiWritePort1(chan, IDX_IO1_o_Feature,      (UCHAR)(feature>>8));
        AtapiWritePort1(chan, IDX_IO1_o_Feature,      (UCHAR)feature);
        AtapiWritePort1(chan, IDX_IO1_o_BlockCount,   (UCHAR)(count>>8));
        AtapiWritePort1(chan, IDX_IO1_o_BlockCount,   (UCHAR)count);
        AtapiWritePort1(chan, IDX_IO1_o_BlockNumber,  (UCHAR)(plba[3]));
        AtapiWritePort1(chan, IDX_IO1_o_BlockNumber,  (UCHAR)(plba[0]));
        AtapiWritePort1(chan, IDX_IO1_o_CylinderLow,  (UCHAR)(plba[4]));
        AtapiWritePort1(chan, IDX_IO1_o_CylinderLow,  (UCHAR)(plba[1]));
        AtapiWritePort1(chan, IDX_IO1_o_CylinderHigh, (UCHAR)(plba[5]));
        AtapiWritePort1(chan, IDX_IO1_o_CylinderHigh, (UCHAR)(plba[2]));

        //KdPrint2((PRINT_PREFIX "AtaCommand48: ldev %#x USE_LBA48 (2)\n", ldev ));
        AtapiWritePort1(chan, IDX_IO1_o_DriveSelect, IDE_USE_LBA | (DeviceNumber ? IDE_DRIVE_2 : IDE_DRIVE_1) );
    } else {

        plba = (PUCHAR)&lba; //ktp
        chan->ChannelCtrlFlags &= ~CTRFLAGS_LBA48;
        
        //if(feature ||
        //   (deviceExtension->lun[ldev].DeviceFlags & (DFLAGS_ATAPI_DEVICE | DFLAGS_TAPE_DEVICE | DFLAGS_LBA_ENABLED))) {
            AtapiWritePort1(chan, IDX_IO1_o_Feature,      (UCHAR)feature);
        //}
        AtapiWritePort1(chan, IDX_IO1_o_BlockCount,   (UCHAR)count);
        AtapiWritePort1(chan, IDX_IO1_o_BlockNumber,  (UCHAR)plba[0]);
        AtapiWritePort1(chan, IDX_IO1_o_CylinderLow,  (UCHAR)plba[1]);
        AtapiWritePort1(chan, IDX_IO1_o_CylinderHigh, (UCHAR)plba[2]);
        if(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_LBA_ENABLED) {
            //KdPrint2((PRINT_PREFIX "AtaCommand28: ldev %#x USE_LBA\n", ldev ));
            AtapiWritePort1(chan, IDX_IO1_o_DriveSelect,  (UCHAR)(plba[3] & 0xf) | IDE_USE_LBA | (DeviceNumber ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1) );
        } else {
            //KdPrint2((PRINT_PREFIX "AtaCommand28: ldev %#x USE_CHS\n", ldev ));
            AtapiWritePort1(chan, IDX_IO1_o_DriveSelect,  (UCHAR)(plba[3] & 0xf) | (DeviceNumber ? IDE_DRIVE_SELECT_2 : IDE_DRIVE_SELECT_1) );
        }
    }

    // write command code to device
    AtapiWritePort1(chan, IDX_IO1_o_Command, command);

    switch (flags) {
    case ATA_WAIT_INTR:

        // caller requested wait for interrupt
        for(i=0;i<4;i++) {
            WaitOnBusy(chan);
            statusByte = WaitForDrq(chan);
            if (statusByte & IDE_STATUS_DRQ)
                break;
            AtapiStallExecution(500);
            KdPrint2((PRINT_PREFIX "  retry waiting DRQ, status %#x\n", statusByte));
        }

        return statusByte;

    case ATA_WAIT_IDLE:

        // caller requested wait for entering Wait state
        for (i=0; i<30 * 1000; i++) {

            GetStatus(chan, statusByte);
            statusByte = UniataIsIdle(deviceExtension, statusByte);
            if(statusByte == 0xff) {
                // no drive ?
                break;
            } else
            if(statusByte & IDE_STATUS_ERROR) {
                break;
            } else
            if(statusByte & IDE_STATUS_BUSY) {
                AtapiStallExecution(100);
                continue;
            } else
            if(statusByte == IDE_STATUS_IDLE) {
                break;
            } else {
                //if(deviceExtension->HwFlags & UNIATA_SATA) {
                if(UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
                    break;
                }
                AtapiStallExecution(100);
            }
        }
        //statusByte |= IDE_STATUS_BUSY;
        break;

    case ATA_WAIT_READY:
        statusByte = WaitOnBusyLong(chan);
        break;
    case ATA_WAIT_BASE_READY:
        statusByte = WaitOnBaseBusyLong(chan);
        break;
    case ATA_IMMEDIATE:
        GetStatus(chan, statusByte);
        if (statusByte & IDE_STATUS_ERROR) {
            KdPrint2((PRINT_PREFIX "  Warning: Immed Status %#x :(\n", statusByte));
            if(statusByte == (IDE_STATUS_IDLE | IDE_STATUS_ERROR)) {
                break;
            }
            KdPrint2((PRINT_PREFIX "  try to continue\n"));
            statusByte &= ~IDE_STATUS_ERROR;
        }
        chan->ExpectingInterrupt = TRUE;
        // !!!!!
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);
        statusByte = 0;
        break;
    }

    KdPrint2((PRINT_PREFIX "  Status %#x\n", statusByte));

    return statusByte;
} // end AtaCommand48()

/*
    Send command to device.
    This is simply wrapper for AtaCommand48()
*/
UCHAR
NTAPI
AtaCommand(
    IN PHW_DEVICE_EXTENSION deviceExtension,
    IN ULONG DeviceNumber,
    IN ULONG lChannel,
    IN UCHAR command,
    IN USHORT cylinder,
    IN UCHAR head,
    IN UCHAR sector, 
    IN UCHAR count,
    IN UCHAR feature,
    IN ULONG flags
    )
{
    return AtaCommand48(deviceExtension, DeviceNumber, lChannel,
                        command, 
                        (ULONG)sector | ((ULONG)cylinder << 8) | ((ULONG)(head & 0x0f) << 24),
                        count, feature, flags);
} // end AtaCommand()

LONG
NTAPI
AtaPio2Mode(LONG pio)
{
    switch (pio) {
    default: return ATA_PIO;
    case 0: return ATA_PIO0;
    case 1: return ATA_PIO1;
    case 2: return ATA_PIO2;
    case 3: return ATA_PIO3;
    case 4: return ATA_PIO4;
    case 5: return ATA_PIO5;
    }
} // end AtaPio2Mode()

LONG
NTAPI
AtaPioMode(PIDENTIFY_DATA2 ident)
{
    if (ident->PioTimingsValid) {
        if (ident->AdvancedPIOModes & AdvancedPIOModes_5)
            return 5;
        if (ident->AdvancedPIOModes & AdvancedPIOModes_4)
            return 4;
        if (ident->AdvancedPIOModes & AdvancedPIOModes_3) 
            return 3;
    }        
    if (ident->PioCycleTimingMode == 2)
        return 2;
    if (ident->PioCycleTimingMode == 1)
        return 1;
    if (ident->PioCycleTimingMode == 0)
        return 0;
    return -1; 
} // end AtaPioMode()

LONG
NTAPI
AtaWmode(PIDENTIFY_DATA2 ident)
{
    if (ident->MultiWordDMASupport & 0x04)
        return 2;
    if (ident->MultiWordDMASupport & 0x02)
        return 1;
    if (ident->MultiWordDMASupport & 0x01)
        return 0;
    return -1;
} // end AtaWmode()

LONG
NTAPI
AtaUmode(PIDENTIFY_DATA2 ident)
{
    if (!ident->UdmaModesValid)
        return -1;
    if (ident->UltraDMASupport & 0x40)
        return 6;
    if (ident->UltraDMASupport & 0x20)
        return 5;
    if (ident->UltraDMASupport & 0x10)
        return 4;
    if (ident->UltraDMASupport & 0x08)
        return 3;
    if (ident->UltraDMASupport & 0x04)
        return 2;
    if (ident->UltraDMASupport & 0x02)
        return 1;
    if (ident->UltraDMASupport & 0x01)
        return 0;
    return -1;
} // end AtaUmode()


#ifndef UNIATA_CORE

VOID
NTAPI
AtapiTimerDpc(
    IN PVOID HwDeviceExtension
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PHW_TIMER HwScsiTimer;
    LARGE_INTEGER time;
    ULONG MiniportTimerValue;
    BOOLEAN recall = FALSE;
    ULONG lChannel;
    PHW_CHANNEL chan;

    KdPrint2((PRINT_PREFIX "AtapiTimerDpc:\n"));

    lChannel = deviceExtension->ActiveDpcChan = deviceExtension->FirstDpcChan;
    if(lChannel == CHAN_NOT_SPECIFIED) {
        KdPrint2((PRINT_PREFIX "AtapiTimerDpc: no items\n"));
        return;
    }
    chan = &deviceExtension->chan[lChannel];

    while(TRUE) {

        HwScsiTimer = chan->HwScsiTimer;
        chan->HwScsiTimer = NULL;

        deviceExtension->FirstDpcChan = chan->NextDpcChan;
        if(deviceExtension->FirstDpcChan != CHAN_NOT_SPECIFIED) {
            recall = TRUE;
        }

        HwScsiTimer(HwDeviceExtension);

        chan->NextDpcChan = CHAN_NOT_SPECIFIED;

        lChannel = deviceExtension->ActiveDpcChan = deviceExtension->FirstDpcChan;
        if(lChannel == CHAN_NOT_SPECIFIED) {
            KdPrint2((PRINT_PREFIX "AtapiTimerDpc: no more items\n"));
            deviceExtension->FirstDpcChan =
            deviceExtension->ActiveDpcChan = CHAN_NOT_SPECIFIED;
            return;
        }

        KeQuerySystemTime(&time);
        KdPrint2((PRINT_PREFIX "AtapiTimerDpc: KeQuerySystemTime=%#x%#x\n", time.HighPart, time.LowPart));

        chan = &deviceExtension->chan[lChannel];
        if(time.QuadPart >= chan->DpcTime - 10) {
            // call now
            KdPrint2((PRINT_PREFIX "AtapiTimerDpc: get next DPC, DpcTime1=%#x%#x\n",
                         (ULONG)(chan->DpcTime >> 32), (ULONG)(chan->DpcTime)));
            continue;
        }
        break;
    }

    if(recall) {
        deviceExtension->ActiveDpcChan = CHAN_NOT_SPECIFIED;
        MiniportTimerValue = (ULONG)(time.QuadPart - chan->DpcTime)/10;
        if(!MiniportTimerValue)
            MiniportTimerValue = 1;

        KdPrint2((PRINT_PREFIX "AtapiTimerDpc: recall AtapiTimerDpc\n"));
        ScsiPortNotification(RequestTimerCall, HwDeviceExtension,
                             AtapiTimerDpc,
                             MiniportTimerValue
                             );
    }
    return;

} // end AtapiTimerDpc()

/*
    Wrapper for ScsiPort, that implements smart Dpc
    queueing. We need it to allow parallel functioning
    of IDE channles with shared interrupt. Standard Dpc mechanism
    cancels previous Dpc request (if any), but we need Dpc queue.
*/
VOID
NTAPI
AtapiQueueTimerDpc(
    IN PVOID HwDeviceExtension,
    IN ULONG lChannel,
    IN PHW_TIMER HwScsiTimer,
    IN ULONG MiniportTimerValue
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    LARGE_INTEGER time;
    LARGE_INTEGER time2;
    ULONG i;
    PHW_CHANNEL prev_chan;
    PHW_CHANNEL chan;
//    BOOLEAN UseRequestTimerCall = TRUE;

    KdPrint2((PRINT_PREFIX "AtapiQueueTimerDpc: dt=%d for lChn %#x\n", MiniportTimerValue, lChannel));
    KeQuerySystemTime(&time);
    time2 = time;
    KdPrint2((PRINT_PREFIX "AtapiQueueTimerDpc: KeQuerySystemTime=%#x%#x\n", time.HighPart, time.LowPart));
    time.QuadPart += MiniportTimerValue*10;
    KdPrint2((PRINT_PREFIX "AtapiQueueTimerDpc: KeQuerySystemTime2=%#x%#x\n", time.HighPart, time.LowPart));

    KdPrint2((PRINT_PREFIX "  ActiveDpcChan=%d, FirstDpcChan=%d\n", deviceExtension->ActiveDpcChan, deviceExtension->FirstDpcChan));

    i = deviceExtension->FirstDpcChan;
    chan = prev_chan = NULL;
    while(i != CHAN_NOT_SPECIFIED) {
        prev_chan = chan;
        chan = &deviceExtension->chan[i];
        if(chan->DpcTime > time.QuadPart) {
            break;
        }
        i = chan->NextDpcChan;
    }
    chan = &deviceExtension->chan[lChannel];
    if(!prev_chan) {
        deviceExtension->FirstDpcChan = lChannel;
    } else {
        prev_chan->NextDpcChan = lChannel;
    }
    chan->NextDpcChan = i;
    chan->HwScsiTimer = HwScsiTimer;
    chan->DpcTime     = time.QuadPart;

    KdPrint2((PRINT_PREFIX "AtapiQueueTimerDpc: KeQuerySystemTime3=%#x%#x\n", time2.HighPart, time2.LowPart));
    if(time.QuadPart <= time2.QuadPart) {
        MiniportTimerValue = 1;
    } else {
        MiniportTimerValue = (ULONG)((time.QuadPart - time2.QuadPart) / 10);
    }

    KdPrint2((PRINT_PREFIX "AtapiQueueTimerDpc: dt=%d for lChn %#x\n", MiniportTimerValue, lChannel));
    ScsiPortNotification(RequestTimerCall, HwDeviceExtension,
                         AtapiTimerDpc,
                         MiniportTimerValue);

} // end AtapiQueueTimerDpc()

#endif //UNIATA_CORE

VOID
NTAPI
UniataDumpATARegs(
    IN PHW_CHANNEL chan
    )
{
    ULONG                j;
    UCHAR                statusByteAlt;

    GetStatus(chan, statusByteAlt);
    KdPrint2((PRINT_PREFIX "  AltStatus (%#x)\n", statusByteAlt));

    for(j=1; j<IDX_IO1_SZ; j++) {
        statusByteAlt = AtapiReadPort1(chan, IDX_IO1+j);
        KdPrint2((PRINT_PREFIX 
                   "  Reg_%#x (%#x) = %#x\n",
                   j,
                   chan->RegTranslation[IDX_IO1+j].Addr,
                   statusByteAlt));
    }
    for(j=0; j<IDX_BM_IO_SZ-1; j++) {
        statusByteAlt = AtapiReadPort1(chan, IDX_BM_IO+j);
        KdPrint2((PRINT_PREFIX 
                   "  BM_%#x (%#x) = %#x\n",
                   j,
                   chan->RegTranslation[IDX_BM_IO+j].Addr,
                   statusByteAlt));
    }
    return;
} // end UniataDumpATARegs()

/*++

Routine Description:

    Issue IDENTIFY command to a device.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage
    DeviceNumber - Indicates which device.
    Command - Either the standard (EC) or the ATAPI packet (A1) IDENTIFY.

Return Value:

    TRUE if all goes well.

--*/
BOOLEAN
NTAPI
IssueIdentify(
    IN PVOID HwDeviceExtension,
    IN ULONG DeviceNumber,
    IN ULONG lChannel,
    IN UCHAR Command,
    IN BOOLEAN NoSetup
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PHW_CHANNEL          chan = &deviceExtension->chan[lChannel];
    ULONG                waitCount = 50000;
    ULONG                j;
    UCHAR                statusByte;
    UCHAR                statusByte2;
    UCHAR                signatureLow,
                         signatureHigh;
    BOOLEAN              atapiDev = FALSE;
    ULONG                ldev = (lChannel * 2) + DeviceNumber;
    PHW_LU_EXTENSION     LunExt = &(deviceExtension->lun[ldev]);

    if(DeviceNumber && (chan->ChannelCtrlFlags & CTRFLAGS_NO_SLAVE)) {
        KdPrint2((PRINT_PREFIX "IssueIdentify: NO SLAVE\n"));
        return FALSE;
    }
    if(LunExt->DeviceFlags & DFLAGS_HIDDEN) {
        KdPrint2((PRINT_PREFIX "IssueIdentify: HIDDEN\n"));
        return FALSE;
    }

    SelectDrive(chan, DeviceNumber);
    AtapiStallExecution(10);
    statusByte = WaitOnBusyLong(chan);
    // Check that the status register makes sense.
    GetBaseStatus(chan, statusByte2);

    UniataDumpATARegs(chan);

    if (Command == IDE_COMMAND_IDENTIFY) {
        // Mask status byte ERROR bits.
        statusByte = UniataIsIdle(deviceExtension, statusByte & ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX));
        KdPrint2((PRINT_PREFIX "IssueIdentify: Checking for IDE. Status (%#x)\n", statusByte));
        // Check if register value is reasonable.

        if(statusByte != IDE_STATUS_IDLE) {

            // No reset here !!!
            KdPrint2((PRINT_PREFIX "IssueIdentify: statusByte != IDE_STATUS_IDLE\n"));

            //if(!(deviceExtension->HwFlags & UNIATA_SATA)) {
            if(!UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
                SelectDrive(chan, DeviceNumber);
                WaitOnBusyLong(chan);

                signatureLow = AtapiReadPort1(chan, IDX_IO1_i_CylinderLow);
                signatureHigh = AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh);

                if (signatureLow == ATAPI_MAGIC_LSB &&
                    signatureHigh == ATAPI_MAGIC_MSB) {
                    // Device is Atapi.
                    KdPrint2((PRINT_PREFIX "IssueIdentify: this is ATAPI (ldev %d)\n", ldev));
                    return FALSE;
                }

                // 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.
                    AtapiStallExecution(100);
                    GetStatus(chan, statusByte);

                } while ((statusByte & IDE_STATUS_BUSY) && waitCount--);
                GetBaseStatus(chan, statusByte2);

                SelectDrive(chan, DeviceNumber);
            } else {
                GetBaseStatus(chan, statusByte2);
            }
            // Another check for signature, to deal with one model Atapi that doesn't assert signature after
            // a soft reset.
            signatureLow = AtapiReadPort1(chan, IDX_IO1_i_CylinderLow);
            signatureHigh = AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh);

            if (signatureLow == ATAPI_MAGIC_LSB &&
                signatureHigh == ATAPI_MAGIC_MSB) {
                KdPrint2((PRINT_PREFIX "IssueIdentify: this is ATAPI (2) (ldev %d)\n", ldev));
                // Device is Atapi.
                return FALSE;
            }

            statusByte = UniataIsIdle(deviceExtension, statusByte) & ~IDE_STATUS_INDEX;
            if (statusByte != IDE_STATUS_IDLE) {
                // Give up on this.
                KdPrint2((PRINT_PREFIX "IssueIdentify: no dev (ldev %d)\n", ldev));
                return FALSE;
            }
        }
    } else {
        KdPrint2((PRINT_PREFIX "IssueIdentify: Checking for ATAPI. Status (%#x)\n", statusByte));
        //if(!(deviceExtension->HwFlags & UNIATA_SATA)) {
        if(!UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
            statusByte = WaitForIdleLong(chan);
            KdPrint2((PRINT_PREFIX "IssueIdentify: Checking for ATAPI (2). Status (%#x)\n", statusByte));
        }
        atapiDev = TRUE;
    }

//    if(deviceExtension->HwFlags & UNIATA_SATA) {
    if(UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
        j = 4;
    } else {
        j = 0;
    }
    for (; j < 4*2; j++) {
        // Send IDENTIFY command.
        statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel, Command, 0, 0, 0, (j >= 4) ? 0x200 : 0, 0, ATA_WAIT_INTR);
        // Clear interrupt

        if (statusByte & IDE_STATUS_DRQ) {
            // Read status to acknowledge any interrupts generated.
            KdPrint2((PRINT_PREFIX "IssueIdentify: IDE_STATUS_DRQ (%#x)\n", statusByte));
            GetBaseStatus(chan, statusByte);
            // One last check for Atapi.
            if (Command == IDE_COMMAND_IDENTIFY) {
                signatureLow = AtapiReadPort1(chan, IDX_IO1_i_CylinderLow);
                signatureHigh = AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh);

                if (signatureLow == ATAPI_MAGIC_LSB &&
                    signatureHigh == ATAPI_MAGIC_MSB) {
                    KdPrint2((PRINT_PREFIX "IssueIdentify: this is ATAPI (3) (ldev %d)\n", ldev));
                    // Device is Atapi.
                    return FALSE;
                }
            }
            break;
        } else {
            KdPrint2((PRINT_PREFIX "IssueIdentify: !IDE_STATUS_DRQ (%#x)\n", statusByte));
            if (Command == IDE_COMMAND_IDENTIFY) {
                // Check the signature. If DRQ didn't come up it's likely Atapi.
                signatureLow = AtapiReadPort1(chan, IDX_IO1_i_CylinderLow);
                signatureHigh = AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh);

                if (signatureLow == ATAPI_MAGIC_LSB &&
                    signatureHigh == ATAPI_MAGIC_MSB) {
                    // Device is Atapi.
                    KdPrint2((PRINT_PREFIX "IssueIdentify: this is ATAPI (4) (ldev %d)\n", ldev));
                    return FALSE;
                }
            } else {
                if(!(statusByte & IDE_STATUS_ERROR) && (statusByte & IDE_STATUS_BUSY)) {
                    KdPrint2((PRINT_PREFIX "IssueIdentify: DRQ not asserted immediately, BUSY -> WaitForDrq\n"));
                    break;
                }
            }
            // Device didn't respond correctly. It will be given one more chances.
            KdPrint2((PRINT_PREFIX "IssueIdentify: DRQ never asserted (%#x). Error reg (%#x)\n",
                        statusByte, AtapiReadPort1(chan, IDX_IO1_i_Error)));
            GetBaseStatus(chan, statusByte);
            AtapiSoftReset(chan,DeviceNumber);

            AtapiDisableInterrupts(deviceExtension, lChannel);
            AtapiEnableInterrupts(deviceExtension, lChannel);

            GetBaseStatus(chan, statusByte);
            //GetStatus(chan, statusByte);
            KdPrint2((PRINT_PREFIX "IssueIdentify: Status after soft reset (%#x)\n", statusByte));
        }
    }
    // 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)) {
        KdPrint2((PRINT_PREFIX "IssueIdentify: Exit on error (%#x)\n", statusByte));
        return FALSE;
    }

    KdPrint2((PRINT_PREFIX "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.
    statusByte = WaitForDrq(chan);
    statusByte = WaitOnBusyLong(chan);
        KdPrint2((PRINT_PREFIX "IssueIdentify: statusByte %#x\n", statusByte));

    if (!(statusByte & IDE_STATUS_DRQ)) {
        KdPrint2((PRINT_PREFIX "IssueIdentify: !IDE_STATUS_DRQ (2) (%#x)\n", statusByte));
        GetBaseStatus(chan, statusByte);
        return FALSE;
    }
    GetBaseStatus(chan, statusByte);
    KdPrint2((PRINT_PREFIX "IssueIdentify: BASE statusByte %#x\n", statusByte));

    if (atapiDev || !(LunExt->DeviceFlags & DFLAGS_DWORDIO_ENABLED) /*!deviceExtension->DWordIO*/) {
        
        KdPrint2((PRINT_PREFIX "  use 16bit IO\n"));
#if 0
        USHORT w;
        ULONG i;
        // ATI/SII chipsets with memory-mapped IO hangs when
        // I call ReadBuffer(), probably due to PCI burst/prefetch enabled
        // Unfortunately, I don't know yet how to workaround it except the way you see below.
        KdPrint2((PRINT_PREFIX 
                   "  IO_%#x (%#x), %s:\n",
                   IDX_IO1_i_Data,
                   chan->RegTranslation[IDX_IO1_i_Data].Addr,
                   chan->RegTranslation[IDX_IO1_i_Data].MemIo ? "Mem" : "IO"));
        for(i=0; i<256; i++) {
/*
            KdPrint2((PRINT_PREFIX 
                       "  IO_%#x (%#x):\n",
                       IDX_IO1_i_Data,
                       chan->RegTranslation[IDX_IO1_i_Data].Addr));
*/
            w = AtapiReadPort2(chan, IDX_IO1_i_Data);
            KdPrint2((PRINT_PREFIX 
                       "    %x\n", w));
            AtapiStallExecution(1);
            ((PUSHORT)&deviceExtension->FullIdentifyData)[i] = w;
        }
#else
        ReadBuffer(chan, (PUSHORT)&deviceExtension->FullIdentifyData, 256, PIO0_TIMING);
#endif
        // Work around for some IDE and one model Atapi that will present more than
        // 256 bytes for the Identify data.
        KdPrint2((PRINT_PREFIX "IssueIdentify: suck data port\n", statusByte));
        statusByte = AtapiSuckPort2(chan);
    } else {
        KdPrint2((PRINT_PREFIX "  use 32bit IO\n"));
        ReadBuffer2(chan, (PUSHORT)&deviceExtension->FullIdentifyData, 256/2, PIO0_TIMING);
    }

    KdPrint2((PRINT_PREFIX "IssueIdentify: statusByte %#x\n", statusByte));
    statusByte = WaitForDrq(chan);
    KdPrint2((PRINT_PREFIX "IssueIdentify: statusByte %#x\n", statusByte));
    GetBaseStatus(chan, statusByte);

    KdPrint2((PRINT_PREFIX "IssueIdentify: Status after read words %#x\n", statusByte));

    if(NoSetup) {
        KdPrint2((PRINT_PREFIX "IssueIdentify: no setup, exiting\n"));
        return TRUE;
    }

    KdPrint2((PRINT_PREFIX "Model: %20.20s\n", deviceExtension->FullIdentifyData.ModelNumber));
    KdPrint2((PRINT_PREFIX "FW:    %4.4s\n", deviceExtension->FullIdentifyData.FirmwareRevision));
    KdPrint2((PRINT_PREFIX "S/N:   %20.20s\n", deviceExtension->FullIdentifyData.SerialNumber));
    KdPrint2((PRINT_PREFIX "Pio:   %x\n", deviceExtension->FullIdentifyData.PioCycleTimingMode));
    if(deviceExtension->FullIdentifyData.PioTimingsValid) {
        KdPrint2((PRINT_PREFIX "APio:  %x\n", deviceExtension->FullIdentifyData.AdvancedPIOModes));
    }
    KdPrint2((PRINT_PREFIX "SWDMA: %x\n", deviceExtension->FullIdentifyData.SingleWordDMAActive));
    KdPrint2((PRINT_PREFIX "MWDMA: %x\n", deviceExtension->FullIdentifyData.MultiWordDMAActive));
    if(deviceExtension->FullIdentifyData.UdmaModesValid) {
        KdPrint2((PRINT_PREFIX "UDMA:  %x\n", deviceExtension->FullIdentifyData.UltraDMAActive));
    }
    KdPrint2((PRINT_PREFIX "SATA:  %x\n", deviceExtension->FullIdentifyData.SataEnable));

    // Check out a few capabilities / limitations of the device.
    if (deviceExtension->FullIdentifyData.RemovableStatus & 1) {
        // Determine if this drive supports the MSN functions.
        KdPrint2((PRINT_PREFIX "IssueIdentify: Marking drive %d as removable. SFE = %d\n",
                    ldev,
                    deviceExtension->FullIdentifyData.RemovableStatus));
        LunExt->DeviceFlags |= DFLAGS_REMOVABLE_DRIVE;
    }
    if (deviceExtension->FullIdentifyData.MaximumBlockTransfer) {
        // Determine max. block transfer for this device.
        LunExt->MaximumBlockXfer =
            (UCHAR)(deviceExtension->FullIdentifyData.MaximumBlockTransfer & 0xFF);
    }
    LunExt->NumOfSectors = 0;
    if (Command == IDE_COMMAND_IDENTIFY) {
        ULONGLONG NumOfSectors=0;
        ULONGLONG NativeNumOfSectors=0;
        ULONGLONG cylinders=0;
        ULONGLONG tmp_cylinders=0;
        // Read very-old-style drive geometry
        KdPrint2((PRINT_PREFIX "CHS %#x:%#x:%#x\n", 
                deviceExtension->FullIdentifyData.NumberOfCylinders,
                deviceExtension->FullIdentifyData.NumberOfHeads,
                deviceExtension->FullIdentifyData.SectorsPerTrack
                ));
        NumOfSectors = deviceExtension->FullIdentifyData.NumberOfCylinders *
                       deviceExtension->FullIdentifyData.NumberOfHeads *
                       deviceExtension->FullIdentifyData.SectorsPerTrack;
        KdPrint2((PRINT_PREFIX "NumOfSectors %#I64x\n", NumOfSectors));
        // Check for HDDs > 8Gb
        if ((deviceExtension->FullIdentifyData.NumberOfCylinders == 0x3fff) &&
/*            (deviceExtension->FullIdentifyData.TranslationFieldsValid) &&*/
            (NumOfSectors < deviceExtension->FullIdentifyData.UserAddressableSectors)) {
            KdPrint2((PRINT_PREFIX "NumberOfCylinders == 0x3fff\n"));
            cylinders = 
                (deviceExtension->FullIdentifyData.UserAddressableSectors /
                    (deviceExtension->FullIdentifyData.NumberOfHeads *
                       deviceExtension->FullIdentifyData.SectorsPerTrack));

            KdPrint2((PRINT_PREFIX "cylinders %#I64x\n", cylinders));

            NumOfSectors = cylinders *
                           deviceExtension->FullIdentifyData.NumberOfHeads *
                           deviceExtension->FullIdentifyData.SectorsPerTrack;

            KdPrint2((PRINT_PREFIX "NumOfSectors %#I64x\n", NumOfSectors));
        } else {

        }
        // Check for LBA mode
        KdPrint2((PRINT_PREFIX "SupportLba flag %#x\n", deviceExtension->FullIdentifyData.SupportLba));
        KdPrint2((PRINT_PREFIX "MajorRevision %#x\n", deviceExtension->FullIdentifyData.MajorRevision));
        KdPrint2((PRINT_PREFIX "UserAddressableSectors %#x\n", deviceExtension->FullIdentifyData.UserAddressableSectors));
        if ( deviceExtension->FullIdentifyData.SupportLba
                            ||
            (deviceExtension->FullIdentifyData.MajorRevision &&
/*             deviceExtension->FullIdentifyData.TranslationFieldsValid &&*/
             deviceExtension->FullIdentifyData.UserAddressableSectors)) {
            KdPrint2((PRINT_PREFIX "LBA mode\n"));
            LunExt->DeviceFlags |= DFLAGS_LBA_ENABLED;
        } else {
            KdPrint2((PRINT_PREFIX "Keep orig geometry\n"));
            LunExt->DeviceFlags |= DFLAGS_ORIG_GEOMETRY;
            goto skip_lba_staff;
        }
        // Check for LBA48 support
        if(LunExt->DeviceFlags & DFLAGS_LBA_ENABLED) {
            if(deviceExtension->FullIdentifyData.FeaturesSupport.Address48 &&
               deviceExtension->FullIdentifyData.FeaturesEnabled.Address48 &&
               (deviceExtension->FullIdentifyData.UserAddressableSectors48 > NumOfSectors)
               ) {
                KdPrint2((PRINT_PREFIX "LBA48\n"));
                cylinders = 
                    (deviceExtension->FullIdentifyData.UserAddressableSectors48 /
                        (deviceExtension->FullIdentifyData.NumberOfHeads *
                           deviceExtension->FullIdentifyData.SectorsPerTrack));

                KdPrint2((PRINT_PREFIX "cylinders %#I64x\n", cylinders));
                
                NativeNumOfSectors = cylinders *
                               deviceExtension->FullIdentifyData.NumberOfHeads *
                               deviceExtension->FullIdentifyData.SectorsPerTrack;

                KdPrint2((PRINT_PREFIX "NativeNumOfSectors %#I64x\n", NativeNumOfSectors));

                if(NativeNumOfSectors > NumOfSectors) {
                    KdPrint2((PRINT_PREFIX "Update NumOfSectors to %#I64x\n", NativeNumOfSectors));
                    NumOfSectors = NativeNumOfSectors;
                }
            }

            // Check drive capacity report for LBA48-capable drives.
            if(deviceExtension->FullIdentifyData.FeaturesSupport.Address48) {
                ULONG hNativeNumOfSectors;
                KdPrint2((PRINT_PREFIX "Use IDE_COMMAND_READ_NATIVE_SIZE48\n"));

                statusByte = AtaCommand48(deviceExtension, DeviceNumber, lChannel,
                             IDE_COMMAND_READ_NATIVE_SIZE48, 0, 0, 0, ATA_WAIT_READY);

                if(!(statusByte & IDE_STATUS_ERROR)) {
                    NativeNumOfSectors = (ULONG)AtapiReadPort1(chan, IDX_IO1_i_BlockNumber) |
                                        ((ULONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderLow)  << 8) |
                                        ((ULONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh) << 16) ;

                    AtapiWritePort1(chan, IDX_IO2_o_Control,
                                           IDE_DC_USE_HOB );

                    KdPrint2((PRINT_PREFIX "Read high order bytes\n"));
                    NativeNumOfSectors |=
                                        ((ULONG)AtapiReadPort1(chan, IDX_IO1_i_BlockNumber)  << 24 );
                    hNativeNumOfSectors= 
                                         (ULONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderLow) |
                                        ((ULONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh) << 8) ;
                    ((PULONG)&NativeNumOfSectors)[1] = hNativeNumOfSectors;

                    KdPrint2((PRINT_PREFIX "NativeNumOfSectors %#I64x\n", NativeNumOfSectors));

                    // Some drives report LBA48 capability while has capacity below 128Gb
                    // Probably they support large block-counters.
                    // But the problem is that some of them reports higher part of Max LBA equal to lower part.
                    // Here we check this
                    if((NativeNumOfSectors & 0xffffff) == ((NativeNumOfSectors >> 24) & 0xffffff)) {
                        KdPrint2((PRINT_PREFIX "High-order bytes == Low-order bytes !!!\n"));

                        statusByte = AtaCommand48(deviceExtension, DeviceNumber, lChannel,
                                     IDE_COMMAND_READ_NATIVE_SIZE48, 0, 0, 0, ATA_WAIT_READY);

                        if(!(statusByte & IDE_STATUS_ERROR)) {
                            NativeNumOfSectors = (ULONGLONG)AtapiReadPort1(chan, IDX_IO1_i_BlockNumber) |
                                                ((ULONGLONG)AtapiReadPort1(chan, IDX_IO1_i_BlockNumber)  << 24) |
                                                ((ULONGLONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderLow)  << 8 ) |
                                                ((ULONGLONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderLow)  << 32) |
                                                ((ULONGLONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh) << 16) |
                                                ((ULONGLONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh) << 40) 
                                                ;
                        }

                        if((NativeNumOfSectors & 0xffffff) == ((NativeNumOfSectors >> 24) & 0xffffff)) {
                            KdPrint2((PRINT_PREFIX "High-order bytes == Low-order bytes !!! (2)\n"));
                            NativeNumOfSectors = 0;
                        }
                    }

                    if(NumOfSectors <= ATA_MAX_LBA28 &&
                       NativeNumOfSectors > NumOfSectors) {

                        KdPrint2((PRINT_PREFIX "Use IDE_COMMAND_SET_NATIVE_SIZE48\n"));
                        KdPrint2((PRINT_PREFIX "Update NumOfSectors to %#I64x\n", NativeNumOfSectors));

                        statusByte = AtaCommand48(deviceExtension, DeviceNumber, lChannel,
                                     IDE_COMMAND_SET_NATIVE_SIZE, NativeNumOfSectors, 0, 0, ATA_WAIT_READY);
                        if(!(statusByte & IDE_STATUS_ERROR)) {
                            NumOfSectors = NativeNumOfSectors;
                        }
                    }
                }
            }
    
            if(NumOfSectors < 0x2100000 /*&& NumOfSectors > 31*1000*1000*/) {
                // check for native LBA size
                // some drives report ~32Gb in Identify Block
                KdPrint2((PRINT_PREFIX "Use IDE_COMMAND_READ_NATIVE_SIZE\n"));

                statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel, IDE_COMMAND_READ_NATIVE_SIZE,
                             0, IDE_USE_LBA, 0, 0, 0, ATA_WAIT_READY);

                if(!(statusByte & IDE_STATUS_ERROR)) {
                    NativeNumOfSectors = (ULONG)AtapiReadPort1(chan, IDX_IO1_i_BlockNumber) |
                                        ((ULONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderLow) << 8) |
                                        ((ULONG)AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh) << 16) |
                                       (((ULONG)AtapiReadPort1(chan, IDX_IO1_i_DriveSelect) & 0xf) << 24);

                    KdPrint2((PRINT_PREFIX "NativeNumOfSectors %#I64x\n", NativeNumOfSectors));

                    if(NativeNumOfSectors > NumOfSectors) {

                        KdPrint2((PRINT_PREFIX "Use IDE_COMMAND_SET_NATIVE_SIZE\n"));
                        KdPrint2((PRINT_PREFIX "Update NumOfSectors to %#I64x\n", NativeNumOfSectors));

                        statusByte = AtaCommand48(deviceExtension, DeviceNumber, lChannel,
                                     IDE_COMMAND_SET_NATIVE_SIZE, NativeNumOfSectors, 0, 0, ATA_WAIT_READY);
                        if(!(statusByte & IDE_STATUS_ERROR)) {
                            NumOfSectors = NativeNumOfSectors;
                        }
                    }
                }
            }

        } // if(LunExt->DeviceFlags & DFLAGS_LBA_ENABLED)

        // fill IdentifyData with bogus geometry
        KdPrint2((PRINT_PREFIX "requested LunExt->GeomType=%x\n", LunExt->opt_GeomType));
        tmp_cylinders = NumOfSectors / (deviceExtension->FullIdentifyData.CurrentSectorsPerTrack *
                                        deviceExtension->FullIdentifyData.NumberOfCurrentHeads);
        KdPrint2((PRINT_PREFIX "tmp_cylinders = %#I64x\n", tmp_cylinders));
        if((tmp_cylinders < 0xffff) || (LunExt->opt_GeomType == GEOM_ORIG)) {
            // ok, we can keep original values
            if(LunExt->opt_GeomType == GEOM_AUTO) {
                LunExt->opt_GeomType = GEOM_ORIG;
            }
        } else {
            tmp_cylinders = NumOfSectors / (255*63);
            if(tmp_cylinders < 0xffff) {
                // we can use generic values for H/S for generic geometry approach
                if(LunExt->opt_GeomType == GEOM_AUTO) {
                    LunExt->opt_GeomType = GEOM_STD;
                }
            } else {
                // we should use UNIATA geometry approach
                if(LunExt->opt_GeomType == GEOM_AUTO) {
                    LunExt->opt_GeomType = GEOM_UNIATA;
                }
            }
        }
        KdPrint2((PRINT_PREFIX "final LunExt->opt_GeomType=%x\n", LunExt->opt_GeomType));

        if(LunExt->opt_GeomType == GEOM_STD) {
            deviceExtension->FullIdentifyData.CurrentSectorsPerTrack =
            deviceExtension->FullIdentifyData.SectorsPerTrack = 63;

            deviceExtension->FullIdentifyData.NumberOfCurrentHeads =
            deviceExtension->FullIdentifyData.NumberOfHeads   = 255;

            cylinders = NumOfSectors / (255*63);
            KdPrint2((PRINT_PREFIX "Use GEOM_STD, CHS=%I64x/%x/%x\n", cylinders, 255, 63));
        } else
        if(LunExt->opt_GeomType == GEOM_UNIATA) {
            while ((cylinders > 0xffff) && (deviceExtension->FullIdentifyData.SectorsPerTrack < 0x80)) {
                cylinders /= 2;
                KdPrint2((PRINT_PREFIX "cylinders /= 2\n"));
                deviceExtension->FullIdentifyData.SectorsPerTrack *= 2;
                deviceExtension->FullIdentifyData.CurrentSectorsPerTrack *= 2;
            }
            while ((cylinders > 0xffff) && (deviceExtension->FullIdentifyData.NumberOfHeads < 0x80)) {
                cylinders /= 2;
                KdPrint2((PRINT_PREFIX "cylinders /= 2 (2)\n"));
                deviceExtension->FullIdentifyData.NumberOfHeads *= 2;
                deviceExtension->FullIdentifyData.NumberOfCurrentHeads *= 2;
            }
            while ((cylinders > 0xffff) && (deviceExtension->FullIdentifyData.SectorsPerTrack < 0x8000)) {
                cylinders /= 2;
                KdPrint2((PRINT_PREFIX "cylinders /= 2 (3)\n"));
                deviceExtension->FullIdentifyData.SectorsPerTrack *= 2;
                deviceExtension->FullIdentifyData.CurrentSectorsPerTrack *= 2;
            }
            while ((cylinders > 0xffff) && (deviceExtension->FullIdentifyData.NumberOfHeads < 0x8000)) {
                cylinders /= 2;
                KdPrint2((PRINT_PREFIX "cylinders /= 2 (4)\n"));
                deviceExtension->FullIdentifyData.NumberOfHeads *= 2;
                deviceExtension->FullIdentifyData.NumberOfCurrentHeads *= 2;
            }
            KdPrint2((PRINT_PREFIX "Use GEOM_UNIATA, CHS=%I64x/%x/%x\n", cylinders,
                deviceExtension->FullIdentifyData.NumberOfCurrentHeads,
                deviceExtension->FullIdentifyData.CurrentSectorsPerTrack));
        }
        if(!cylinders) {
            KdPrint2((PRINT_PREFIX "cylinders = tmp_cylinders (%x = %x)\n", cylinders, tmp_cylinders));
            cylinders = tmp_cylinders;
        }
        deviceExtension->FullIdentifyData.NumberOfCurrentCylinders =
        deviceExtension->FullIdentifyData.NumberOfCylinders = (USHORT)cylinders;

skip_lba_staff:

        KdPrint2((PRINT_PREFIX "Geometry: C %#x (%#x)\n",
                  deviceExtension->FullIdentifyData.NumberOfCylinders,
                  deviceExtension->FullIdentifyData.NumberOfCurrentCylinders
                  ));
        KdPrint2((PRINT_PREFIX "Geometry: H %#x (%#x)\n",
                  deviceExtension->FullIdentifyData.NumberOfHeads,
                  deviceExtension->FullIdentifyData.NumberOfCurrentHeads
                  ));
        KdPrint2((PRINT_PREFIX "Geometry: S %#x (%#x)\n",
                  deviceExtension->FullIdentifyData.SectorsPerTrack,
                  deviceExtension->FullIdentifyData.CurrentSectorsPerTrack
                  ));

        if(NumOfSectors)
            LunExt->NumOfSectors = NumOfSectors;
/*        if(deviceExtension->FullIdentifyData.MajorRevision &&
           deviceExtension->FullIdentifyData.DoubleWordIo) {
            LunExt->DeviceFlags |= DFLAGS_DWORDIO_ENABLED;
        }*/
    }

    ScsiPortMoveMemory(&LunExt->IdentifyData,
                       &deviceExtension->FullIdentifyData,sizeof(IDENTIFY_DATA2));

    InitBadBlocks(LunExt);

    if ((LunExt->IdentifyData.DrqType & ATAPI_DRQT_INTR) &&
        (Command != IDE_COMMAND_IDENTIFY)) {

        // This device interrupts with the assertion of DRQ after receiving
        // Atapi Packet Command
        LunExt->DeviceFlags |= DFLAGS_INT_DRQ;
        KdPrint2((PRINT_PREFIX "IssueIdentify: Device interrupts on assertion of DRQ.\n"));

    } else {
        KdPrint2((PRINT_PREFIX "IssueIdentify: Device does not interrupt on assertion of DRQ.\n"));
    }

    if(Command != IDE_COMMAND_IDENTIFY) {
        // ATAPI branch
        if(LunExt->IdentifyData.DeviceType == ATAPI_TYPE_TAPE) {
            // This is a tape.
            LunExt->DeviceFlags |= DFLAGS_TAPE_DEVICE;
            KdPrint2((PRINT_PREFIX "IssueIdentify: Device is a tape drive.\n"));
        } else
        if(LunExt->IdentifyData.DeviceType == ATAPI_TYPE_CDROM ||
            LunExt->IdentifyData.DeviceType == ATAPI_TYPE_OPTICAL) {
            KdPrint2((PRINT_PREFIX "IssueIdentify: Device is CD/Optical drive.\n"));
            // set CD default costs
            LunExt->RwSwitchCost  = REORDER_COST_SWITCH_RW_CD;
            LunExt->RwSwitchMCost = REORDER_MCOST_SWITCH_RW_CD;
            LunExt->SeekBackMCost = REORDER_MCOST_SEEK_BACK_CD;
            statusByte = WaitForDrq(chan);
        } else {
            KdPrint2((PRINT_PREFIX "IssueIdentify: ATAPI drive type %#x.\n",
                LunExt->IdentifyData.DeviceType));
        }
    } else {
        KdPrint2((PRINT_PREFIX "IssueIdentify: hard drive.\n"));
    }

    GetBaseStatus(chan, statusByte);
    KdPrint2((PRINT_PREFIX "IssueIdentify: final Status on exit (%#x)\n", statusByte));
    return TRUE;

} // end IssueIdentify()


/*++

Routine Description:
    Set drive parameters using the IDENTIFY data.

Arguments:
    HwDeviceExtension - HBA miniport driver's adapter data storage
    DeviceNumber - Indicates which device.

Return Value:
    TRUE if all goes well.

--*/
BOOLEAN
NTAPI
SetDriveParameters(
    IN PVOID HwDeviceExtension,
    IN ULONG DeviceNumber,
    IN ULONG lChannel
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PIDENTIFY_DATA2      identifyData   = &deviceExtension->lun[(lChannel * 2) + DeviceNumber].IdentifyData;
//    ULONG i;
    UCHAR statusByte;
    UCHAR errorByte;

    if(deviceExtension->lun[(lChannel * 2) + DeviceNumber].DeviceFlags &
          (DFLAGS_LBA_ENABLED | DFLAGS_ORIG_GEOMETRY))
       return TRUE;

    KdPrint2((PRINT_PREFIX "SetDriveParameters: Number of heads %#x\n", identifyData->NumberOfHeads));
    KdPrint2((PRINT_PREFIX "SetDriveParameters: Sectors per track %#x\n", identifyData->SectorsPerTrack));

    // Send SET PARAMETER command.
    statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel,
                            IDE_COMMAND_SET_DRIVE_PARAMETERS, 0,
                            (identifyData->NumberOfHeads - 1), 0,
                            (UCHAR)identifyData->SectorsPerTrack, 0, ATA_WAIT_IDLE);

    statusByte = UniataIsIdle(deviceExtension, statusByte);
    if(statusByte & IDE_STATUS_ERROR) {
        errorByte = AtapiReadPort1(&deviceExtension->chan[lChannel], IDX_IO1_i_Error);
        KdPrint2((PRINT_PREFIX "SetDriveParameters: Error bit set. Status %#x, error %#x\n",
                    errorByte, statusByte));
        return FALSE;
    }

    if(statusByte == IDE_STATUS_IDLE) {
        return TRUE;
    }

    return FALSE;

} // end SetDriveParameters()

VOID
NTAPI
UniataForgetDevice(
    PHW_LU_EXTENSION   LunExt
    )
{
    LunExt->DeviceFlags &= DFLAGS_HIDDEN;
} // end UniataForgetDevice()


/*++

Routine Description:
    Reset IDE controller and/or Atapi device.

Arguments:
    HwDeviceExtension - HBA miniport driver's adapter data storage

Return Value:
    Nothing.


--*/
BOOLEAN
NTAPI
AtapiResetController(
    IN PVOID HwDeviceExtension,
    IN ULONG PathId
    )
{
    KdPrint2((PRINT_PREFIX "AtapiResetController()\n"));
    return AtapiResetController__(HwDeviceExtension, PathId, RESET_COMPLETE_ALL);
} // end AtapiResetController()


BOOLEAN
NTAPI
AtapiResetController__(
    IN PVOID HwDeviceExtension,
    IN ULONG PathId,
    IN BOOLEAN CompleteType
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    ULONG                numberChannels  = deviceExtension->NumberChannels;
    PHW_CHANNEL          chan = NULL;
    ULONG i,j;
    ULONG max_ldev;
    UCHAR statusByte;
    PSCSI_REQUEST_BLOCK CurSrb;
    ULONG ChannelCtrlFlags;
    UCHAR dma_status = 0;

    ULONG slotNumber = deviceExtension->slotNumber;
    ULONG SystemIoBusNumber = deviceExtension->SystemIoBusNumber;
    ULONG VendorID =  deviceExtension->DevID        & 0xffff;
#ifdef _DEBUG
    ULONG DeviceID = (deviceExtension->DevID >> 16) & 0xffff;
#endif
    //ULONG RevID    =  deviceExtension->RevID;
    ULONG ChipFlags = deviceExtension->HwFlags & CHIPFLAG_MASK;
    //UCHAR tmp8;
    UCHAR tmp16;

    KdPrint2((PRINT_PREFIX "AtapiResetController: Reset IDE %#x/%#x @ %#x\n", VendorID, DeviceID, slotNumber));

    if(!deviceExtension->simplexOnly && (PathId != CHAN_NOT_SPECIFIED)) {
        // we shall reset both channels on SimplexOnly devices,
        // It's not worth doing so on normal controllers
        j = PathId;
        numberChannels = min(j+1, deviceExtension->NumberChannels);
    } else {
        j=0;
        numberChannels = deviceExtension->NumberChannels;
    }

    for (; j < numberChannels; j++) {

        KdPrint2((PRINT_PREFIX "AtapiResetController: Reset channel %d\n", j));
        chan = &deviceExtension->chan[j];
        KdPrint2((PRINT_PREFIX "  CompleteType %#x\n", CompleteType));
        max_ldev = (chan->ChannelCtrlFlags & CTRFLAGS_NO_SLAVE) ? 1 : 2;
        if(CompleteType != RESET_COMPLETE_NONE) {
#ifndef UNIATA_CORE
            while((CurSrb = UniataGetCurRequest(chan))) {

                PATA_REQ AtaReq = (PATA_REQ)(CurSrb->SrbExtension);

                KdPrint2((PRINT_PREFIX "AtapiResetController: pending SRB %#x\n", CurSrb));
                // Check and see if we are processing an internal srb
                if (AtaReq->OriginalSrb) {
                    KdPrint2((PRINT_PREFIX "  restore original SRB %#x\n", AtaReq->OriginalSrb));
                    AtaReq->Srb = AtaReq->OriginalSrb;
                    AtaReq->OriginalSrb = NULL;
                    // NOTE: internal SRB doesn't get to SRB queue !!!
                    CurSrb = AtaReq->Srb;
                }

                // Remove current request from queue
                UniataRemoveRequest(chan, CurSrb);

                // Check if request is in progress.
                ASSERT(AtaReq->Srb == CurSrb);
                if (CurSrb) {
                    // Complete outstanding request with SRB_STATUS_BUS_RESET.
                    UCHAR PathId   = CurSrb->PathId;
                    UCHAR TargetId = CurSrb->TargetId;
                    UCHAR Lun = CurSrb->Lun;

                    CurSrb->SrbStatus = ((CompleteType == RESET_COMPLETE_ALL) ? SRB_STATUS_BUS_RESET : SRB_STATUS_ABORTED) | SRB_STATUS_AUTOSENSE_VALID;
                    CurSrb->ScsiStatus = SCSISTAT_CHECK_CONDITION;

                    if (CurSrb->SenseInfoBuffer) {

                        PSENSE_DATA  senseBuffer = (PSENSE_DATA)CurSrb->SenseInfoBuffer;

                        senseBuffer->ErrorCode = 0x70;
                        senseBuffer->Valid     = 1;
                        senseBuffer->AdditionalSenseLength = 0xb;
                        if(CompleteType == RESET_COMPLETE_ALL) {
                            KdPrint2((PRINT_PREFIX "AtapiResetController: report SCSI_SENSE_UNIT_ATTENTION + SCSI_ADSENSE_BUS_RESET\n"));
                            senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION;
                            senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_BUS_RESET;
                            senseBuffer->AdditionalSenseCodeQualifier = SCSI_SENSEQ_SCSI_BUS;
                        } else {
                            KdPrint2((PRINT_PREFIX "AtapiResetController: report SCSI_SENSE_ABORTED_COMMAND\n"));
                            senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND;
                            senseBuffer->AdditionalSenseCode = 0;
                            senseBuffer->AdditionalSenseCodeQualifier = 0;
                        }
                    }

                    // Clear request tracking fields.
                    AtaReq->WordsLeft = 0;
                    AtaReq->DataBuffer = NULL;
                    AtaReq->TransferLength = 0;

                    ScsiPortNotification(RequestComplete,
                                         deviceExtension,
                                         CurSrb);

                    // Indicate ready for next request.
                    ScsiPortNotification(NextLuRequest,
                                         deviceExtension, 
                                         PathId,
                                         TargetId,
                                         Lun);
                }
                if(CompleteType != RESET_COMPLETE_ALL)
                    break;
            } // end while()
#endif //UNIATA_CORE
        } // end if (!CompleteType != RESET_COMPLETE_NONE)

        // Save control flags
        ChannelCtrlFlags = chan->ChannelCtrlFlags;
        // Clear expecting interrupt flag.
        chan->ExpectingInterrupt = FALSE;
        chan->RDP = FALSE;
        chan->ChannelCtrlFlags = 0;
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);
        
        // Reset controller
        KdPrint2((PRINT_PREFIX "  disable intr (0)\n"));
        AtapiDisableInterrupts(deviceExtension, j);
        KdPrint2((PRINT_PREFIX "  done\n"));
        switch(VendorID) {
        case ATA_INTEL_ID: {
            ULONG mask;
            ULONG timeout;
            if(!(ChipFlags & UNIATA_SATA))
                goto default_reset;
            if(!UniataIsSATARangeAvailable(deviceExtension, j)) {
                goto default_reset;
            }

#if 0
            /* ICH6 & ICH7 in compat mode has 4 SATA ports as master/slave on 2 ch's */
            if(ChipFlags & UNIATA_AHCI) {
                mask = 0x0005 << j;
            } else {
                /* ICH5 in compat mode has SATA ports as master/slave on 1 channel */
                GetPciConfig1(0x90, tmp8);
                if(tmp8 & 0x04) {
                    mask = 0x0003;
                } else {
                    mask = 0x0001 << j;
                }
            }
#else
            mask = 1 << chan->lun[0]->SATA_lun_map;
            if (max_ldev > 1) {
            	mask |= (1 << chan->lun[1]->SATA_lun_map);
            }
#endif
            ChangePciConfig2(0x92, a & ~mask);
            AtapiStallExecution(10);
            ChangePciConfig2(0x92, a | mask);
            timeout = 100;

            /* Wait up to 1 sec for "connect well". */
            if (ChipFlags & (I6CH | I6CH2))
                mask = mask << 8;
            else
                mask = mask << 4;

            while (timeout--) {
                AtapiStallExecution(10000);
                GetPciConfig2(0x92, tmp16);
                if ((tmp16 & mask) == mask) {
                    AtapiStallExecution(10000);
                    break;
                }
            }
            break; }
        case ATA_SIS_ID:
        case ATA_NVIDIA_ID: {
            KdPrint2((PRINT_PREFIX "  SIS/nVidia\n"));
            if(!(ChipFlags & UNIATA_SATA))
                goto default_reset;
            break; }
        case ATA_SILICON_IMAGE_ID: {
            ULONG offset;
            ULONG Channel = deviceExtension->Channel + j;
            if(!(ChipFlags & UNIATA_SATA))
                goto default_reset;
            offset = ((Channel & 1) << 7) + ((Channel & 2) << 8);
            /* disable PHY state change interrupt */
            AtapiWritePortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x148 + offset, 0);

            UniataSataClearErr(HwDeviceExtension, j, UNIATA_SATA_IGNORE_CONNECT, 0);

            /* reset controller part for this channel */
            AtapiWritePortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x48,
                 AtapiReadPortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x48) | (0xc0 >> Channel));
            AtapiStallExecution(1000);
            AtapiWritePortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x48,
                 AtapiReadPortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x48) & ~(0xc0 >> Channel));


            break; }
        case ATA_PROMISE_ID: {
            break; }
        default:
            if(ChipFlags & UNIATA_SATA) {
                KdPrint2((PRINT_PREFIX "  SATA generic reset\n"));
                UniataSataClearErr(HwDeviceExtension, j, UNIATA_SATA_IGNORE_CONNECT, 0);
            }
default_reset:
            KdPrint2((PRINT_PREFIX "  send reset\n"));
            AtapiWritePort1(chan, IDX_IO2_o_Control, IDE_DC_DISABLE_INTERRUPTS |
                                                                    IDE_DC_RESET_CONTROLLER );
            KdPrint2((PRINT_PREFIX "  wait a little\n"));
            AtapiStallExecution(10000);
            // Disable interrupts
            KdPrint2((PRINT_PREFIX "  disable intr\n"));
            AtapiDisableInterrupts(deviceExtension, j);
            AtapiStallExecution(100);
            KdPrint2((PRINT_PREFIX "  re-enable intr\n"));
            AtapiEnableInterrupts(deviceExtension, j);
            KdPrint2((PRINT_PREFIX "  wait a little (2)\n"));
            AtapiStallExecution(100000);
            KdPrint2((PRINT_PREFIX "  done\n"));

            break;
        } // end switch()

        //if(!(ChipFlags & UNIATA_SATA)) {
        if(!UniataIsSATARangeAvailable(deviceExtension, j)) {
            // Reset DMA engine if active
            KdPrint2((PRINT_PREFIX "  check DMA engine\n"));
            dma_status = GetDmaStatus(chan->DeviceExtension, chan->lChannel);
            KdPrint2((PRINT_PREFIX "  DMA status %#x\n", dma_status));
            if((ChannelCtrlFlags & CTRFLAGS_DMA_ACTIVE) ||
               (dma_status & BM_STATUS_INTR)) {
                AtapiDmaDone(HwDeviceExtension, 0, j, NULL);
            }
        }

        // all these shall be performed inside AtapiHwInitialize__() ?
#if 1
        KdPrint2((PRINT_PREFIX "  process connected devices\n"));
        // Do special processing for ATAPI and IDE disk devices.
        for (i = 0; i < max_ldev; i++) {

            // Check if device present.
            if (!(deviceExtension->lun[i + (j * 2)].DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
#ifdef NAVO_TEST
                continue;
#else //NAVO_TEST
                //if(!CheckDevice(HwDeviceExtension, i, j, FALSE))
                if(!UniataAnybodyHome(HwDeviceExtension, j, i)) {
                    continue;
                }
                if(!CheckDevice(HwDeviceExtension, j, i, TRUE)) {
                    continue;
                }
            } else {
                if(!UniataAnybodyHome(HwDeviceExtension, j, i)) {
                    KdPrint2((PRINT_PREFIX "  device have gone\n"));
                    UniataForgetDevice(&(deviceExtension->lun[i + (j * 2)]));
                }
#endif //NAVO_TEST
            }

            SelectDrive(chan, i);
            AtapiStallExecution(10);
            statusByte = WaitOnBusyLong(chan);
            statusByte = UniataIsIdle(deviceExtension, statusByte);
            if(statusByte == 0xff) {
                KdPrint2((PRINT_PREFIX 
                           "no drive, status %#x\n",
                           statusByte));
                UniataForgetDevice(&(deviceExtension->lun[i + (j * 2)]));
            } else
            // Check for ATAPI disk.
            if (deviceExtension->lun[i + (j * 2)].DeviceFlags & DFLAGS_ATAPI_DEVICE) {
                // Issue soft reset and issue identify.
                GetStatus(chan, statusByte);
                KdPrint2((PRINT_PREFIX "AtapiResetController: Status before Atapi reset (%#x).\n",
                            statusByte));

                AtapiDisableInterrupts(deviceExtension, j);
                AtapiSoftReset(chan, i);
                AtapiEnableInterrupts(deviceExtension, j);

                GetStatus(chan, statusByte);

                if(statusByte == IDE_STATUS_SUCCESS) {

                    IssueIdentify(HwDeviceExtension,
                                  i, j,
                                  IDE_COMMAND_ATAPI_IDENTIFY, FALSE);
                } else {

                    KdPrint2((PRINT_PREFIX 
                               "AtapiResetController: Status after soft reset %#x\n",
                               statusByte));
                }
                GetBaseStatus(chan, statusByte);

            } else {
                // Issue identify and reinit after channel reset.

                if (statusByte != IDE_STATUS_IDLE &&
                    statusByte != IDE_STATUS_SUCCESS &&
                    statusByte != IDE_STATUS_DRDY) {
//                    result2 = FALSE;
                    KdPrint2((PRINT_PREFIX "AtapiResetController: IdeHardReset failed\n"));
                } else
                if(!IssueIdentify(HwDeviceExtension,
                                  i, j,
                                  IDE_COMMAND_IDENTIFY, FALSE)) {
//                    result2 = FALSE;
                    KdPrint2((PRINT_PREFIX "AtapiResetController: IDE IssueIdentify failed\n"));
                } else
                // Set disk geometry parameters.
                if (!SetDriveParameters(HwDeviceExtension, i, j)) {
                    KdPrint2((PRINT_PREFIX "AtapiResetController: SetDriveParameters failed\n"));
                }
                GetBaseStatus(chan, statusByte);
            }
            // force DMA mode reinit
            deviceExtension->lun[i + (j * 2)].DeviceFlags |= DFLAGS_REINIT_DMA;
        }
#endif //0

        // Enable interrupts, note, the we can have here recursive disable
        AtapiStallExecution(10);
        KdPrint2((PRINT_PREFIX "AtapiResetController: deviceExtension->chan[%d].DisableIntr %d -> 1\n",
            j,
            chan->DisableIntr));
        AtapiEnableInterrupts(deviceExtension, j);

        // Call the HwInitialize routine to setup multi-block.
        AtapiHwInitialize__(deviceExtension, j);
    }
    ScsiPortNotification(NextRequest, deviceExtension, NULL);

    return TRUE;

} // end AtapiResetController__()


/*++

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

--*/
ULONG
NTAPI
MapError(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    ULONG lChannel = GET_CHANNEL(Srb);
    PHW_CHANNEL chan = &(deviceExtension->chan[lChannel]);
//    ULONG i;
    UCHAR errorByte;
    UCHAR srbStatus = SRB_STATUS_SUCCESS;
    UCHAR scsiStatus;
    ULONG ldev = GET_LDEV(Srb);

    // Read the error register.

    errorByte = AtapiReadPort1(chan, IDX_IO1_i_Error);
    KdPrint2((PRINT_PREFIX 
               "MapError: Error register is %#x\n",
               errorByte));

    if (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_ATAPI_DEVICE) {

        switch (errorByte >> 4) {
        case SCSI_SENSE_NO_SENSE:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: No sense information\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_RECOVERED_ERROR:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Recovered error\n"));
            scsiStatus = 0;
            srbStatus = SRB_STATUS_SUCCESS;
            break;

        case SCSI_SENSE_NOT_READY:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Device not ready\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_MEDIUM_ERROR:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Media error\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_HARDWARE_ERROR:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Hardware error\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_ILLEGAL_REQUEST:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Illegal request\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_UNIT_ATTENTION:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Unit attention\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_DATA_PROTECT:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Data protect\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_BLANK_CHECK:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Blank check\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        case SCSI_SENSE_ABORTED_COMMAND:
            KdPrint2((PRINT_PREFIX 
                        "Atapi: Command Aborted\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;
            break;

        default:

            KdPrint2((PRINT_PREFIX 
                       "ATAPI: Invalid sense information\n"));
            scsiStatus = 0;
            srbStatus = SRB_STATUS_ERROR;
            break;
        }

    } else {

        scsiStatus = 0;

        // Save errorByte,to be used by SCSIOP_REQUEST_SENSE.
        chan->ReturningMediaStatus = errorByte;

        if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) {
            KdPrint2((PRINT_PREFIX 
                       "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_COMMAND_ABORTED) {
            KdPrint2((PRINT_PREFIX 
                       "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->lun[ldev].ErrorCount++;

        } else if (errorByte & IDE_ERROR_END_OF_MEDIA) {

            KdPrint2((PRINT_PREFIX 
                       "IDE: End of media\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_MEDIA_STATE;
                senseBuffer->AdditionalSenseCodeQualifier = SCSI_SENSEQ_END_OF_MEDIUM;
                senseBuffer->EndOfMedia = 1;

                srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            }

            if (!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED)){
                deviceExtension->lun[ldev].ErrorCount++;
            }

        } else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) {

            KdPrint2((PRINT_PREFIX 
                       "IDE: Illegal length\n"));
            srbStatus = SRB_STATUS_INVALID_REQUEST;

            if (Srb->SenseInfoBuffer) {

                PSENSE_DATA  senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;

                senseBuffer->ErrorCode = 0x70;
                senseBuffer->Valid     = 1;
                senseBuffer->AdditionalSenseLength = 0xb;
                senseBuffer->SenseKey =  SCSI_SENSE_ILLEGAL_REQUEST;
                senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_INVALID_VALUE;
                senseBuffer->AdditionalSenseCodeQualifier = SCSI_SENSEQ_PARAM_INVALID_VALUE;
                senseBuffer->IncorrectLength = 1;

                srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            }

        } else if (errorByte & IDE_ERROR_BAD_BLOCK) {

            KdPrint2((PRINT_PREFIX 
                       "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) {

            KdPrint2((PRINT_PREFIX 
                       "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->lun[ldev].ErrorCount++;

        } else if (errorByte & IDE_ERROR_MEDIA_CHANGE) {

            KdPrint2((PRINT_PREFIX 
                       "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) {

            KdPrint2((PRINT_PREFIX 
                   "IDE: Data error\n"));
            scsiStatus = SCSISTAT_CHECK_CONDITION;
            srbStatus = SRB_STATUS_ERROR;

            if (!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED)){
                deviceExtension->lun[ldev].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->lun[ldev].ErrorCount >= MAX_ERRORS) {
//            deviceExtension->DWordIO = FALSE;

            KdPrint2((PRINT_PREFIX 
                        "MapError: ErrorCount >= MAX_ERRORS\n"));

            deviceExtension->lun[ldev].DeviceFlags &= ~DFLAGS_DWORDIO_ENABLED;
            deviceExtension->lun[ldev].MaximumBlockXfer = 0;
            BrutePoint();

            KdPrint2((PRINT_PREFIX 
                        "MapError: Disabling 32-bit PIO and Multi-sector IOs\n"));

            // Log the error.
            KdPrint2((PRINT_PREFIX 
                        "ScsiPortLogError: devExt %#x, Srb %#x, P:T:D=%d:%d:%d, MsgId %#x (%d)\n",
                              HwDeviceExtension,
                              Srb,
                              Srb->PathId,
                              Srb->TargetId,
                              Srb->Lun,
                              SP_BAD_FW_WARNING,
                              4
                        ));
            ScsiPortLogError( HwDeviceExtension,
                              Srb,
                              Srb->PathId,
                              Srb->TargetId,
                              Srb->Lun,
                              SP_BAD_FW_WARNING,
                              4);

            // Reprogram to not use Multi-sector.
            UCHAR statusByte;

            if (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_DEVICE_PRESENT &&
                 !(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_ATAPI_DEVICE)) {

                statusByte = AtaCommand(deviceExtension, ldev & 0x1, lChannel, IDE_COMMAND_SET_MULTIPLE, 0, 0, 0, 0, 0, ATA_WAIT_BASE_READY);

                // 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 = AtapiReadPort1(chan, IDX_IO1_i_Error);

                    KdPrint2((PRINT_PREFIX "MapError: Error setting multiple mode. Status %#x, error byte %#x\n",
                                statusByte,
                                errorByte));

                    // Adjust the devExt. value, if necessary.
                    deviceExtension->lun[ldev].MaximumBlockXfer = 0;
                    BrutePoint();

                }
            }
        }
    }

    // Set SCSI status to indicate a check condition.
    Srb->ScsiStatus = scsiStatus;

    return srbStatus;

} // end MapError()


/*++

Routine Description:

Arguments:
    HwDeviceExtension - HBA miniport driver's adapter data storage

Return Value:
    TRUE - if initialization successful.
    FALSE - if initialization unsuccessful.

--*/
BOOLEAN
NTAPI
AtapiHwInitialize(
    IN PVOID HwDeviceExtension
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    ULONG                numberChannels  = deviceExtension->NumberChannels;
    ULONG c;

    KdPrint2((PRINT_PREFIX "AtapiHwInitialize: (base)\n"));

    if(WinVer_WDM_Model) {
        AtapiResetController__(HwDeviceExtension, CHAN_NOT_SPECIFIED, RESET_COMPLETE_ALL);
    }

    /* do extra chipset specific setups */
    AtapiChipInit(HwDeviceExtension, DEVNUM_NOT_SPECIFIED, CHAN_NOT_SPECIFIED);
/*
    if(deviceExtension->Isr2DevObj && (deviceExtension->HwFlags & UNIATA_SATA)) {
        KdPrint2((PRINT_PREFIX " enable ISR2 to catch unexpected interrupts\n"));
        BMList[deviceExtension->DevIndex].Isr2Enable = TRUE;
    }
*/
    for (c = 0; c < numberChannels; c++) {
        AtapiHwInitialize__(deviceExtension, c);
    }
    KdPrint2((PRINT_PREFIX "AtapiHwInitialize: (base) done\n"));
    return TRUE;
} // end AtapiHwInitialize()

VOID
NTAPI
AtapiHwInitialize__(
    IN PHW_DEVICE_EXTENSION deviceExtension,
    IN ULONG lChannel
    )
{
    ULONG i;
    UCHAR statusByte, errorByte;
    PHW_CHANNEL chan = &(deviceExtension->chan[lChannel]);
    PHW_LU_EXTENSION     LunExt;
//    ULONG tmp32;
    ULONG PreferedMode = 0xffffffff;

    AtapiChipInit(deviceExtension, DEVNUM_NOT_SPECIFIED, lChannel);
    FindDevices(deviceExtension, 0, lChannel);

    for (i = lChannel*2; i < (lChannel+1)*2; i++) {

        KdPrint3((PRINT_PREFIX "AtapiHwInitialize: lChannel %#x, dev %x\n", lChannel, i));

        LunExt = &(deviceExtension->lun[i]);
        // skip empty slots
        if (!(LunExt->DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
            continue;
        }

        AtapiDisableInterrupts(deviceExtension, lChannel);
        AtapiStallExecution(1);

        if (!(LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE)) {

            KdPrint2((PRINT_PREFIX "AtapiHwInitialize: IDE branch\n"));
            // Enable media status notification
            IdeMediaStatus(TRUE,deviceExtension,(UCHAR)i);

            // If supported, setup Multi-block transfers.
            statusByte = AtaCommand(deviceExtension, i & 1, lChannel,
                                IDE_COMMAND_SET_MULTIPLE, 0, 0, 0,
                                LunExt->MaximumBlockXfer, 0, ATA_WAIT_BASE_READY);

            // 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 = AtapiReadPort1(chan, IDX_IO1_i_Error);

                KdPrint2((PRINT_PREFIX "AtapiHwInitialize: Error setting multiple mode. Status %#x, error byte %#x\n",
                            statusByte,
                            errorByte));

                statusByte = AtaCommand(deviceExtension, i & 1, lChannel,
                                    IDE_COMMAND_SET_MULTIPLE, 0, 0, 0,
                                    LunExt->MaximumBlockXfer, 0, ATA_WAIT_BASE_READY);

                if (statusByte & IDE_STATUS_ERROR) {
                    // Read the error register.
                    errorByte = AtapiReadPort1(chan, IDX_IO1_i_Error);

                    KdPrint2((PRINT_PREFIX "AtapiHwInitialize: Error disabling multiple mode. Status %#x, error byte %#x\n",
                                statusByte,
                                errorByte));
                }
                // Adjust the devExt. value, if necessary.
                LunExt->MaximumBlockXfer = 0;

            } else {
                KdPrint2((PRINT_PREFIX 
                            "AtapiHwInitialize: Using Multiblock on Device %d. Blocks / int - %d\n",
                            i,
                            LunExt->MaximumBlockXfer));
            }

            if(LunExt->IdentifyData.MajorRevision) {
            
                if(LunExt->opt_ReadCacheEnable) {
                    KdPrint2((PRINT_PREFIX "  Try Enable Read Cache\n"));
                    // If supported, setup read/write cacheing
                    statusByte = AtaCommand(deviceExtension, i & 1, lChannel,
                                        IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                        0, ATA_C_F_ENAB_RCACHE, ATA_WAIT_BASE_READY);

                    // Check for errors.
                    if (statusByte & IDE_STATUS_ERROR) {
                        KdPrint2((PRINT_PREFIX 
                                    "AtapiHwInitialize: Enable read/write cacheing on Device %d failed\n",
                                    i));
                        LunExt->DeviceFlags &= ~DFLAGS_RCACHE_ENABLED;
                    } else {
                        LunExt->DeviceFlags |= DFLAGS_RCACHE_ENABLED;
                    }
                } else {
                    KdPrint2((PRINT_PREFIX "  Disable Read Cache\n"));
                    statusByte = AtaCommand(deviceExtension, i & 1, lChannel,
                                        IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                        0, ATA_C_F_DIS_RCACHE, ATA_WAIT_BASE_READY);
                    LunExt->DeviceFlags &= ~DFLAGS_RCACHE_ENABLED;
                }
                if(LunExt->opt_WriteCacheEnable) {
                    KdPrint2((PRINT_PREFIX "  Try Enable Write Cache\n"));
                    // If supported & allowed, setup write cacheing
                    statusByte = AtaCommand(deviceExtension, i & 1, lChannel,
                                        IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                        0, ATA_C_F_ENAB_WCACHE, ATA_WAIT_BASE_READY);
                    // Check for errors.
                    if (statusByte & IDE_STATUS_ERROR) {
                        KdPrint2((PRINT_PREFIX 
                                    "AtapiHwInitialize: Enable write cacheing on Device %d failed\n",
                                    i));
                        LunExt->DeviceFlags &= ~DFLAGS_WCACHE_ENABLED;
                    } else {
                        LunExt->DeviceFlags |= DFLAGS_WCACHE_ENABLED;
                    }
                } else {
                    KdPrint2((PRINT_PREFIX "  Disable Write Cache\n"));
                    statusByte = AtaCommand(deviceExtension, i & 1, lChannel,
                                        IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                        0, ATA_C_F_ENAB_WCACHE, ATA_WAIT_BASE_READY);
                    LunExt->DeviceFlags &= ~DFLAGS_WCACHE_ENABLED;
                }
            }

        } else if (!(LunExt->DeviceFlags & DFLAGS_CHANGER_INITED)){

            ULONG j;
            BOOLEAN isSanyo = FALSE;
            CCHAR vendorId[26];

            KdPrint2((PRINT_PREFIX "AtapiHwInitialize: ATAPI/Changer branch\n"));

            // Attempt to identify any special-case devices - psuedo-atapi changers, atapi changers, etc.
            for (j = 0; j < 26; j += 2) {

                // Build a buffer based on the identify data.
                MOV_DW_SWP(vendorId[j], ((PUCHAR)LunExt->IdentifyData.ModelNumber)[j]);
            }

            if (!AtapiStringCmp (vendorId, "CD-ROM  CDR", 11)) {

                // Inquiry string for older model had a '-', newer is '_'
                if (vendorId[12] == 'C') {

                    // Torisan changer. Set the bit. This will be used in several places
                    // acting like 1) a multi-lun device and 2) building the 'special' TUR's.
                    LunExt->DeviceFlags |= (DFLAGS_CHANGER_INITED | DFLAGS_SANYO_ATAPI_CHANGER);
                    LunExt->DiscsPresent = 3;
                    isSanyo = TRUE;
                }
            }
        }

        PreferedMode = LunExt->opt_MaxTransferMode;
        if(PreferedMode == 0xffffffff) {
            KdPrint2((PRINT_PREFIX "MaxTransferMode (overriden): %#x\n", chan->MaxTransferMode));
            PreferedMode = chan->MaxTransferMode;
        }

        if(LunExt->opt_PreferedTransferMode != 0xffffffff) {
            KdPrint2((PRINT_PREFIX "PreferedTransferMode: %#x\n", PreferedMode));
            PreferedMode = min(LunExt->opt_PreferedTransferMode, PreferedMode);
        }

        KdPrint2((PRINT_PREFIX "  try mode %#x\n", PreferedMode));
        LunExt->OrigTransferMode =
        LunExt->LimitedTransferMode =
        LunExt->TransferMode =
            (CHAR)PreferedMode;

        AtapiDmaInit__(deviceExtension, i);

        LunExt->OrigTransferMode =
        LunExt->LimitedTransferMode =
            LunExt->TransferMode;
        KdPrint2((PRINT_PREFIX "Using %#x mode\n", LunExt->TransferMode));

        // We need to get our device ready for action before
        // returning from this function

        // According to the atapi spec 2.5 or 2.6, an atapi device
        // clears its status BSY bit when it is ready for atapi commands.
        // However, some devices (Panasonic SQ-TC500N) are still
        // not ready even when the status BSY is clear.  They don't react
        // to atapi commands.
        //
        // Since there is really no other indication that tells us
        // the drive is really ready for action.  We are going to check BSY
        // is clear and then just wait for an arbitrary amount of time!
        //
        if (LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) {
            ULONG waitCount;

            // have to get out of the loop sometime!
            // 10000 * 100us = 1000,000us = 1000ms = 1s
            waitCount = 10000;
            GetStatus(chan, statusByte);
            while ((statusByte & IDE_STATUS_BUSY) && waitCount) {

                KdPrint2((PRINT_PREFIX "Wait for ATAPI (status %x\n)", statusByte));
                // Wait for Busy to drop.
                AtapiStallExecution(100);
                GetStatus(chan, statusByte);
                waitCount--;
            }

            // 5000 * 100us = 500,000us = 500ms = 0.5s
            waitCount = 5000;
            do {
                AtapiStallExecution(100);
            } while (waitCount--);
        }
        GetBaseStatus(chan, statusByte);
        AtapiEnableInterrupts(deviceExtension, lChannel);
        AtapiStallExecution(10);
    }

    return;

} // end AtapiHwInitialize()


#ifndef UNIATA_CORE

VOID
NTAPI
AtapiHwInitializeChanger(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb,
    IN PMECHANICAL_STATUS_INFORMATION_HEADER MechanismStatus)
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    ULONG                ldev = GET_LDEV(Srb);

    if (MechanismStatus) {
        deviceExtension->lun[ldev].DiscsPresent = MechanismStatus->NumberAvailableSlots;
        if (deviceExtension->lun[ldev].DiscsPresent > 1) {
            deviceExtension->lun[ldev].DeviceFlags |= DFLAGS_ATAPI_CHANGER;
        }
    }
    return;
} // end AtapiHwInitializeChanger()


/*++

Routine Description:
    This routine will parse the string for a match on the keyword, then
    calculate the value for the keyword and return it to the caller.

Arguments:
    String - The ASCII string to parse.
    KeyWord - The keyword for the value desired.

Return Values:
    Zero if value not found
    Value converted from ASCII to binary.

--*/
ULONG
NTAPI
AtapiParseArgumentString(
    IN PCCH String,
    IN PCCH KeyWord
    )
{
    PCCH cptr;
    PCCH kptr;
    ULONG value;
    ULONG stringLength = 0;
    ULONG keyWordLength = 0;
    ULONG index;

    if (!String) {
        return 0;
    }
    if (!KeyWord) {
        return 0;
    }

    // Calculate the string length and lower case all characters.
    cptr = String;
    while (*cptr++) {
        stringLength++;
    }

    // Calculate the keyword length.
    kptr = KeyWord;
    while (*kptr++) {
        keyWordLength++;
    }

    if (keyWordLength > stringLength) {

        // Can't possibly have a match.
        return 0;
    }

    // Now setup and start the compare.
    cptr = String;

ContinueSearch:

    // The input string may start with white space.  Skip it.
    while (*cptr == ' ' || *cptr == '\t') {
        cptr++;
    }

    if (*cptr == '\0') {
        // end of string.
        return 0;
    }

    kptr = KeyWord;
    while ((*cptr == *kptr) ||
           (*cptr >= 'A' && *cptr <= 'Z' && *cptr + ('a' - 'A') == *kptr) ||
           (*cptr >= 'a' && *cptr <= 'z' && *cptr - ('a' - 'A') == *kptr)) {
        cptr++;
        kptr++;

        if (*cptr == '\0') {
            // end of string
            return 0;
        }
    }

    if (*kptr == '\0') {

        // May have a match backup and check for blank or equals.
        while (*cptr == ' ' || *cptr == '\t') {
            cptr++;
        }

        // Found a match.  Make sure there is an equals.
        if (*cptr != '=') {

            // Not a match so move to the next semicolon.
            while (*cptr) {
                if (*cptr++ == ';') {
                    goto ContinueSearch;
                }
            }
            return 0;
        }
        // Skip the equals sign.
        cptr++;

        // Skip white space.
        while ((*cptr == ' ') || (*cptr == '\t')) {
            cptr++;
        }

        if (*cptr == '\0') {
            // Early end of string, return not found
            return 0;
        }

        if (*cptr == ';') {
            // This isn't it either.
            cptr++;
            goto ContinueSearch;
        }

        value = 0;
        if ((*cptr == '0') && ((*(cptr + 1) == 'x') || (*(cptr + 1) == 'X'))) {
            // Value is in Hex.  Skip the "0x"
            cptr += 2;
            for (index = 0; *(cptr + index); index++) {

                if (*(cptr + index) == ' ' ||
                    *(cptr + index) == '\t' ||
                    *(cptr + index) == ';') {
                     break;
                }

                if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) {
                    value = (16 * value) + (*(cptr + index) - '0');
                } else {
                    if ((*(cptr + index) >= 'a') && (*(cptr + index) <= 'f')) {
                        value = (16 * value) + (*(cptr + index) - 'a' + 10);
                    } else if ((*(cptr + index) >= 'A') && (*(cptr + index) <= 'F')) {
                        value = (16 * value) + (*(cptr + index) - 'A' + 10);
                    } else {
                        // Syntax error, return not found.
                        return 0;
                    }
                }
            }
        } else {

            // Value is in Decimal.
            for (index = 0; *(cptr + index); index++) {

                if (*(cptr + index) == ' ' ||
                    *(cptr + index) == '\t' ||
                    *(cptr + index) == ';') {
                     break;
                }

                if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) {
                    value = (10 * value) + (*(cptr + index) - '0');
                } else {

                    // Syntax error return not found.
                    return 0;
                }
            }
        }

        return value;
    } else {

        // Not a match check for ';' to continue search.
        while (*cptr) {
            if (*cptr++ == ';') {
                goto ContinueSearch;
            }
        }

        return 0;
    }
} // end AtapiParseArgumentString()_

/*
    Timer callback
*/
VOID
NTAPI
AtapiCallBack__(
    IN PVOID HwDeviceExtension,
    IN UCHAR lChannel
    )
{

    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PHW_CHANNEL chan = &(deviceExtension->chan[lChannel]);
    ULONG c, _c;

    PSCSI_REQUEST_BLOCK  srb = UniataGetCurRequest(chan);
    UCHAR statusByte;

    KdPrint2((PRINT_PREFIX "AtapiCallBack:\n"));
    // If the last command was DSC restrictive, see if it's set. If so, the device is
    // ready for a new request. Otherwise, reset the timer and come back to here later.

    // If ISR decided to wait for BUSY or DRQ in DPC, we shall also get here.
    // In this case chan->ExpectingInterrupt == TRUE, but interrupts are disabled, thus,
    // we shall have no problem with interrupt handler.
    if (!srb || chan->ExpectingInterrupt) {
        KdPrint2((PRINT_PREFIX "AtapiCallBack: Calling ISR directly due to BUSY\n"));
        chan->DpcState = DPC_STATE_TIMER;
        if(!AtapiInterrupt__(HwDeviceExtension, lChannel)) {
            InterlockedExchange(&(chan->CheckIntr), CHECK_INTR_IDLE);
            KdPrint2((PRINT_PREFIX "AtapiCallBack: What's fucking this ???\n"));
        }
        goto ReturnCallback;
    }

#ifdef DBG
    if (!IS_RDP((srb->Cdb[0]))) {
        KdPrint2((PRINT_PREFIX "AtapiCallBack: Invalid CDB marked as RDP - %#x\n", srb->Cdb[0]));
    }
#endif
    if(!(chan->RDP)) {
        goto ReturnEnableIntr;
    }
    GetStatus(chan, statusByte);
    if (statusByte & IDE_STATUS_DSC) {

        UCHAR PathId   = srb->PathId;
        UCHAR TargetId = srb->TargetId;
        UCHAR Lun      = srb->Lun;

        KdPrint2((PRINT_PREFIX "AtapiCallBack: Found DSC for RDP - %#x\n", srb->Cdb[0]));
        AtapiDmaDBSync(chan, srb);
        UniataRemoveRequest(chan, srb);
        ScsiPortNotification(RequestComplete, deviceExtension, srb);
        // Clear current SRB.
        if(!deviceExtension->simplexOnly) {
            srb = UniataGetCurRequest(chan);
        } else {
            srb = NULL;
        }
        chan->RDP = FALSE;

        // Ask for next request.
        ScsiPortNotification(NextLuRequest,
                             deviceExtension, 
                             PathId,
                             TargetId,
                             Lun);
        ScsiPortNotification(NextRequest, deviceExtension, NULL);

        if(srb) {
            AtapiStartIo__(HwDeviceExtension, srb, FALSE);
        }

    } else {
        KdPrint2((PRINT_PREFIX "AtapiCallBack: Requesting another timer for Op %#x\n",
                    srb->Cdb[0]));

        AtapiQueueTimerDpc(HwDeviceExtension, lChannel,
                             AtapiCallBack_X,
                             1000);

        goto ReturnCallback;
    }

ReturnEnableIntr:

    if(CrNtInterlockedExchangeAdd(&(chan->DisableIntr), 0)) {
        KdPrint2((PRINT_PREFIX "AtapiCallBack: CallDisableInterrupts\n"));
        //ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
#ifdef UNIATA_USE_XXableInterrupts
        chan->ChannelCtrlFlags |= CTRFLAGS_ENABLE_INTR_REQ;
        // must be called on DISPATCH_LEVEL
        ScsiPortNotification(CallDisableInterrupts, HwDeviceExtension,
                             AtapiEnableInterrupts__);
#else
        AtapiEnableInterrupts(HwDeviceExtension, lChannel);
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);
        // Will raise IRQL to DIRQL
        AtapiQueueTimerDpc(HwDeviceExtension, lChannel,
                             AtapiEnableInterrupts__,
                             1);
        KdPrint2((PRINT_PREFIX "AtapiInterrupt: Timer DPC inited\n"));
#endif // UNIATA_USE_XXableInterrupts
    } else {
        //ASSERT(!deviceExtension->simplexOnly);
    }

ReturnCallback:

    // Check other channel
    // In simplex mode no interrupts must appear on other channels
    for(_c=0; _c<deviceExtension->NumberChannels-1; _c++) {
        c = (_c+deviceExtension->FirstChannelToCheck) % deviceExtension->NumberChannels;

        chan = &(deviceExtension->chan[c]);

        if((ULONG)CrNtInterlockedCompareExchange(CRNT_ILK_PTYPE &(chan->CheckIntr),
                                      CRNT_ILK_TYPE CHECK_INTR_ACTIVE,
                                      CRNT_ILK_TYPE CHECK_INTR_DETECTED) == CHECK_INTR_DETECTED)
        {
            //ASSERT(!deviceExtension->simplexOnly);
            chan->DpcState = DPC_STATE_ISR;
            if(!AtapiInterrupt__(HwDeviceExtension, (UCHAR)c)) {
                InterlockedExchange(&(chan->CheckIntr), CHECK_INTR_IDLE);
            }
        }
    }
    KdPrint2((PRINT_PREFIX "AtapiCallBack: return\n"));
    return;

} // end AtapiCallBack__()

VOID
NTAPI
AtapiCallBack_X(
    IN PVOID HwDeviceExtension
    )
{
    AtapiCallBack__(HwDeviceExtension, (UCHAR)((PHW_DEVICE_EXTENSION)HwDeviceExtension)->ActiveDpcChan);
}

#endif //UNIATA_CORE

/*++

Routine Description:

    This is the interrupt service routine for ATAPI IDE miniport driver.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage

Return Value:

    TRUE if expecting an interrupt.

--*/
BOOLEAN
NTAPI
AtapiInterrupt(
    IN PVOID HwDeviceExtension
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    ULONG c, _c;
    BOOLEAN status = FALSE;
    ULONG c_state;
    ULONG i_res = 0;
    ULONG pass;
    BOOLEAN checked[AHCI_MAX_PORT];

    KdPrint2((PRINT_PREFIX "Intr: VendorID+DeviceID/Rev %#x/%#x\n", deviceExtension->DevID, deviceExtension->RevID));

    for(_c=0; _c<deviceExtension->NumberChannels; _c++) {
        checked[_c] = FALSE;
    }
//    fc = 
//    atapiDev = (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_ATAPI_DEVICE) ? TRUE : FALSE;
    for(pass=0; pass<2; pass++) {
        for(_c=0; _c<deviceExtension->NumberChannels; _c++) {

            c = (_c+deviceExtension->FirstChannelToCheck) % deviceExtension->NumberChannels;
            //non_empty_chan = (deviceExtension->lun[c*2].DeviceFlags | deviceExtension->lun[c*2+1].DeviceFlags)
            //        & DFLAGS_DEVICE_PRESENT;

            if(checked[c])
                continue;

            // check non-empty and expecting interrupt channels first
            if(!pass && !deviceExtension->chan[c].ExpectingInterrupt)
                continue;

            checked[c] = TRUE;

            KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): cntrlr %#x chan %#x\n",deviceExtension->DevIndex, c));

            if(CrNtInterlockedExchangeAdd(&(deviceExtension->chan[c].DisableIntr), 0)) {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): disabled INTR on ch %d\n", c));
                continue;
            }
            // lock channel. Wait, while 2nd ISR checks interrupt on this channel
            do {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): try lock\n"));
                // c_state = deviceExtension->chan[c].CheckIntr;
                // if (deviceExtension->chan[c].CheckIntr == CHECK_INTR_DETECTED) {
                //     deviceExtension->chan[c].CheckIntr = CHECK_INTR_ACTIVE;
                // }
                c_state =
                    (ULONG)CrNtInterlockedCompareExchange(CRNT_ILK_PTYPE &(deviceExtension->chan[c].CheckIntr),
                                              CRNT_ILK_TYPE CHECK_INTR_ACTIVE,
                                              CRNT_ILK_TYPE CHECK_INTR_DETECTED);
                if(c_state == CHECK_INTR_IDLE) {
                    // c_state = deviceExtension->chan[c].CheckIntr;
                    // if (deviceExtension->chan[c].CheckIntr == CHECK_INTR_IDLE) {
                    //     deviceExtension->chan[c].CheckIntr = CHECK_INTR_ACTIVE
                    // }
                    c_state =
                        (ULONG)CrNtInterlockedCompareExchange(CRNT_ILK_PTYPE &(deviceExtension->chan[c].CheckIntr),
                                                  CRNT_ILK_TYPE CHECK_INTR_ACTIVE,
                                                  CRNT_ILK_TYPE CHECK_INTR_IDLE);
                }
            } while(c_state == CHECK_INTR_CHECK);
            KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): locked\n"));
            // check if already serviced
            if(c_state == CHECK_INTR_ACTIVE) {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): CHECK_INTR_ACTIVE\n"));
                continue;
            }

            if((c_state == CHECK_INTR_DETECTED) ||
               (i_res = AtapiCheckInterrupt__(deviceExtension, (UCHAR)c))) {

                if(i_res == 2) {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): Catch unexpected\n"));
                    InterlockedExchange(&(deviceExtension->chan[c].CheckIntr), CHECK_INTR_IDLE);
                    return TRUE;
                }
                // disable interrupts on other channel of legacy mode
                // ISA-bridged onboard controller
                if(deviceExtension->simplexOnly /*||
                   ((WinVer_Id() > WinVer_NT) && BMList[deviceExtension->DevIndex].MasterDev)*/) {
                    AtapiDisableInterrupts(deviceExtension, !c);
                }

                deviceExtension->chan[c].DpcState = DPC_STATE_ISR;
                if(AtapiInterrupt__(HwDeviceExtension, (UCHAR)c)) {
                    deviceExtension->LastInterruptedChannel = (UCHAR)c;
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): return status TRUE\n"));
                    status = TRUE;
                } else {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): set CHECK_INTR_IDLE\n"));
                    InterlockedExchange(&(deviceExtension->chan[c].CheckIntr), CHECK_INTR_IDLE);
                }

                // re-enable interrupts on other channel
                if(deviceExtension->simplexOnly /*||
                   ((WinVer_Id() > WinVer_NT) && BMList[deviceExtension->DevIndex].MasterDev)*/) {
                    AtapiEnableInterrupts(deviceExtension, !c);
                }

            } else {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): set CHECK_INTR_IDLE (2)\n"));
                InterlockedExchange(&(deviceExtension->chan[c].CheckIntr), CHECK_INTR_IDLE);
            }

        }
    }
    KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): exit with status %#x\n", status));
    if(status) {
        deviceExtension->FirstChannelToCheck++;
        if(deviceExtension->FirstChannelToCheck >= deviceExtension->NumberChannels)
            deviceExtension->FirstChannelToCheck = 0;
    }
    return status;
} // end AtapiInterrupt()

//ULONG i2c = 0;
#ifndef UNIATA_CORE

BOOLEAN
NTAPI
AtapiInterrupt2(
    IN PKINTERRUPT Interrupt,
    IN PVOID Isr2HwDeviceExtension
    )
{

    PISR2_DEVICE_EXTENSION Isr2DeviceExtension = (PISR2_DEVICE_EXTENSION)Isr2HwDeviceExtension;
    PHW_DEVICE_EXTENSION deviceExtension = Isr2DeviceExtension->HwDeviceExtension;
    ULONG c;
    BOOLEAN status = FALSE;
    ULONG c_count = 0;
    ULONG i_res;

    // we should never get here for ISA/MCA
    if(!BMList[deviceExtension->DevIndex].Isr2Enable) {
        KdPrint2((PRINT_PREFIX "AtapiInterrupt2: NOT ACTIVE cntrlr %#x chan %#x\n",deviceExtension->DevIndex, deviceExtension->Channel));
        return FALSE;
    }

    for(c=0; c<deviceExtension->NumberChannels; c++) {
        KdPrint2((PRINT_PREFIX "AtapiInterrupt2: cntrlr %#x chan %#x\n",deviceExtension->DevIndex, c));

        if(CrNtInterlockedExchangeAdd(&(deviceExtension->chan[c].DisableIntr), 0)) {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt2: disabled INTR\n"));
            continue;
        }

        if((ULONG)CrNtInterlockedCompareExchange(CRNT_ILK_PTYPE &(deviceExtension->chan[c].CheckIntr),
                                      CRNT_ILK_TYPE CHECK_INTR_CHECK,
                                      CRNT_ILK_TYPE CHECK_INTR_IDLE) != CHECK_INTR_IDLE)
        {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt2: !CHECK_INTR_IDLE\n"));
            // hunt on unexpected intr (Some devices generate double interrupts,
            // some controllers (at least CMD649) interrupt twice with small delay.
            // If interrupts are disabled, they queue interrupt and re-issue it later,
            // when we do not expect it.
            continue;
        }

        c_count++;
        if((i_res = AtapiCheckInterrupt__(deviceExtension, (UCHAR)c))) {

            KdPrint2((PRINT_PREFIX "AtapiInterrupt2: intr\n"));
            if(i_res == 2) {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt2: Catch unexpected\n"));
                InterlockedExchange(&(deviceExtension->chan[c].CheckIntr), CHECK_INTR_IDLE);
                return TRUE;
            }

            status = TRUE;
            InterlockedExchange(&(deviceExtension->chan[c].CheckIntr), CHECK_INTR_DETECTED);
        } else {
            InterlockedExchange(&(deviceExtension->chan[c].CheckIntr), CHECK_INTR_IDLE);
        }
    }
    KdPrint2((PRINT_PREFIX "AtapiInterrupt2: status %d, c_count %d\n", status, c_count));
    if(status && (c_count != deviceExtension->NumberChannels)) {
        // there is an active ISR/DPC for one channel, but
        // we have an interrupt from another one
        // Lets inform current ISR/DPC about new interrupt
        InterlockedExchange(&(deviceExtension->ReCheckIntr), CHECK_INTR_DETECTED);
    } else {
        status = FALSE;
    }
    KdPrint2((PRINT_PREFIX "AtapiInterrupt2: return %d\n", status));
    return status;
    
} // end AtapiInterrupt2()

RETTYPE_XXableInterrupts
NTAPI
AtapiInterruptDpc(
    IN PVOID HwDeviceExtension
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    ULONG c;

    for(c=0; c<deviceExtension->NumberChannels; c++) {
        KdPrint2((PRINT_PREFIX "AtapiInterruptDpc: %#x\n",c));

        if(!(deviceExtension->chan[c].ChannelCtrlFlags & CTRFLAGS_DPC_REQ)) {

            if((ULONG)CrNtInterlockedCompareExchange(CRNT_ILK_PTYPE &(deviceExtension->chan[c].CheckIntr),
                                          CRNT_ILK_TYPE CHECK_INTR_ACTIVE,
                                          CRNT_ILK_TYPE CHECK_INTR_DETECTED) != CHECK_INTR_DETECTED)
            {
                continue;
            }
                        
        } else {
            deviceExtension->chan[c].ChannelCtrlFlags &= ~CTRFLAGS_DPC_REQ;
        }
/*
        if(OldReqState != REQ_STATE_DPC_INTR_REQ) {
            AtapiDisableInterrupts(deviceExtension, lChannel);
        }
*/
        deviceExtension->chan[c].DpcState = DPC_STATE_DPC;
        if(!AtapiInterrupt__(HwDeviceExtension, (UCHAR)c)) {
            InterlockedExchange(&(deviceExtension->chan[c].CheckIntr), CHECK_INTR_IDLE);
        }
    }
    return RETVAL_XXableInterrupts;
} // end AtapiInterruptDpc()


RETTYPE_XXableInterrupts
NTAPI
AtapiEnableInterrupts__(
    IN PVOID HwDeviceExtension
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    KdPrint2((PRINT_PREFIX "AtapiEnableInterrupts__():\n"));
    ULONG c;
    PHW_CHANNEL chan = NULL;

    for(c=0; c<deviceExtension->NumberChannels; c++) {
        KdPrint2((PRINT_PREFIX "AtapiEnableInterrupts__(2): %#x\n",c));
        chan = &(deviceExtension->chan[c]);

        if(chan->ChannelCtrlFlags & CTRFLAGS_ENABLE_INTR_REQ) {
            // enable intrs on requested channel
            chan->ChannelCtrlFlags &= ~CTRFLAGS_ENABLE_INTR_REQ;
            AtapiEnableInterrupts(HwDeviceExtension, c);
            InterlockedExchange(&(chan->CheckIntr),
                                          CHECK_INTR_IDLE);

            // check if current or other channel(s) interrupted
            //AtapiInterrupt(HwDeviceExtension);

            if(deviceExtension->simplexOnly) {
                break;
            }
        } else {
            // check if other channel(s) interrupted
            // must do nothing in simplex mode
            if((ULONG)CrNtInterlockedCompareExchange(CRNT_ILK_PTYPE &(chan->CheckIntr),
                                          CRNT_ILK_TYPE CHECK_INTR_ACTIVE,
                                          CRNT_ILK_TYPE CHECK_INTR_DETECTED) != CHECK_INTR_DETECTED) {
                continue;
            }
            //ASSERT(!deviceExtension->simplexOnly);
            chan->DpcState = DPC_STATE_ISR;
            if(!AtapiInterrupt__(HwDeviceExtension, (UCHAR)c)) {
                InterlockedExchange(&(chan->CheckIntr), CHECK_INTR_IDLE);
            }
        }
    }
    // In simplex mode next command must be sent to device here
    if(deviceExtension->simplexOnly && chan) {
        PSCSI_REQUEST_BLOCK srb;
        chan = UniataGetNextChannel(chan);
        if(chan) {
            srb = UniataGetCurRequest(chan);
        } else {
            srb = NULL;
        }
        if(srb) {
            AtapiStartIo__(HwDeviceExtension, srb, FALSE);
        }
    }

    return RETVAL_XXableInterrupts;

} // end AtapiEnableInterrupts__()

#endif //UNIATA_CORE


VOID
NTAPI
AtapiEnableInterrupts(
    IN PVOID HwDeviceExtension,
    IN ULONG c
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    KdPrint2((PRINT_PREFIX "AtapiEnableInterrupts_%d: %d\n",c, deviceExtension->chan[c].DisableIntr));
    if(c >= deviceExtension->NumberChannels) {
        return;
    }
    if(!InterlockedDecrement(&deviceExtension->chan[c].DisableIntr)) {
        AtapiWritePort1(&deviceExtension->chan[c], IDX_IO2_o_Control,
                               IDE_DC_A_4BIT );
        deviceExtension->chan[c].ChannelCtrlFlags &= ~CTRFLAGS_INTR_DISABLED;
    } else {
        AtapiWritePort1(&deviceExtension->chan[c], IDX_IO2_o_Control,
                               IDE_DC_DISABLE_INTERRUPTS /*| IDE_DC_A_4BIT*/ );
    }
    return;
} // end AtapiEnableInterrupts()

VOID
NTAPI
AtapiDisableInterrupts(
    IN PVOID HwDeviceExtension,
    IN ULONG c
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    KdPrint2((PRINT_PREFIX "AtapiDisableInterrupts_%d: %d\n",c, deviceExtension->chan[c].DisableIntr));
    // mark channel as busy
    if(c >= deviceExtension->NumberChannels) {
        return;
    }
    if(InterlockedIncrement(&deviceExtension->chan[c].DisableIntr)) {
        AtapiWritePort1(&deviceExtension->chan[c], IDX_IO2_o_Control,
                               IDE_DC_DISABLE_INTERRUPTS /*| IDE_DC_A_4BIT*/ );
        deviceExtension->chan[c].ChannelCtrlFlags |= CTRFLAGS_INTR_DISABLED;
    }

    return;
} // end AtapiDisableInterrupts()


/*
    Check hardware for interrupt state
 */
BOOLEAN
NTAPI
AtapiCheckInterrupt__(
    IN PVOID HwDeviceExtension,
    IN UCHAR c // logical channel
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PHW_CHANNEL chan = &(deviceExtension->chan[c]);
    PHW_LU_EXTENSION LunExt;

    ULONG VendorID  = deviceExtension->DevID & 0xffff;
    ULONG ChipType  = deviceExtension->HwFlags & CHIPTYPE_MASK;

    ULONG status;
    ULONG pr_status = 0;
    UCHAR dma_status = 0;
    UCHAR reg8 = 0;
    ULONG reg32 = 0;
    UCHAR statusByte;
    ULONG slotNumber = deviceExtension->slotNumber;
    ULONG SystemIoBusNumber = deviceExtension->SystemIoBusNumber;
    ULONG ChipFlags = deviceExtension->HwFlags & CHIPFLAG_MASK;
    UCHAR Channel;
    UCHAR lChannel;
    BOOLEAN DmaTransfer = FALSE;
    BOOLEAN OurInterrupt = FALSE;
//    ULONG k;
    UCHAR interruptReason;
    BOOLEAN EarlyIntr = FALSE;

    KdPrint2((PRINT_PREFIX "AtapiCheckInterrupt__:\n"));

    lChannel = c;
    Channel = (UCHAR)(deviceExtension->Channel + lChannel);

    if(chan->ChannelCtrlFlags & CTRFLAGS_DMA_ACTIVE) {
        DmaTransfer = TRUE;
        KdPrint2((PRINT_PREFIX "  cntrlr %#x:%#x, lch %#x DmaTransfer = TRUE\n", deviceExtension->DevIndex,
            deviceExtension->Channel + c, c));
    } else {
        KdPrint2((PRINT_PREFIX "  cntrlr %#x:%#x, lch %#x DmaTransfer = FALSE\n", deviceExtension->DevIndex,
            deviceExtension->Channel + c, c));
        dma_status = GetDmaStatus(deviceExtension, lChannel);
        KdPrint2((PRINT_PREFIX "  DMA status %#x\n", dma_status));
    }

    // do controller-specific interrupt servicing staff
    if(deviceExtension->UnknownDev) {
        KdPrint2((PRINT_PREFIX "  UnknownDev\n"));
        goto check_unknown;
    }

    if((ChipFlags & UNIATA_AHCI) &&
        UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
        OurInterrupt = UniataAhciStatus(HwDeviceExtension, lChannel, -1);
        return OurInterrupt;
    }

    // Attention !
    // We can catch (BM_STATUS_ACTIVE + BM_STATUS_INTR) when operation is actually completed
    // Such behavior was observed with Intel ICH-xxx chips
    // This condition shall also be treated as 'our interrupt' because of BM_STATUS_INTR flag

    switch(VendorID) {

    case ATA_PROMISE_ID: {
        switch(ChipType) {
        case PROLD:
        case PRNEW:
            status = AtapiReadPortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressBM_0),0x1c);
            if (!DmaTransfer)
                break;
            if (!(status & 
                  ((Channel) ? 0x00004000 : 0x00000400))) {
                KdPrint2((PRINT_PREFIX "  Promise old/new unexpected\n"));
                return FALSE;
            }
            break;
        case PRTX:
            AtapiWritePort1(chan, IDX_BM_DeviceSpecific0, 0x0b);
            status = AtapiReadPort1(chan, IDX_BM_DeviceSpecific1);
            if (!DmaTransfer)
                break;
            if(!(status & 0x20)) {
                KdPrint2((PRINT_PREFIX "  Promise tx unexpected\n"));
                return FALSE;
            }
            break;
        case PRMIO:
            status = AtapiReadPortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressBM_0),0x0040);
            if(ChipFlags & PRSATA) {
                pr_status = AtapiReadPortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressBM_0),0x006c);
                AtapiWritePortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressBM_0),0x006c, pr_status & 0x000000ff);
            }
            if(pr_status & (0x11 << Channel)) {
                // TODO: reset channel
                KdPrint2((PRINT_PREFIX "  Promise mio unexpected + reset req\n"));
                return FALSE;
            }
            if(!(status & (0x01 << Channel))) {
                KdPrint2((PRINT_PREFIX "  Promise mio unexpected\n"));
                return FALSE;
            }
            AtapiWritePort4(chan, IDX_BM_DeviceSpecific0, 0x00000001);
            break;
        }
        break; }
    case ATA_NVIDIA_ID: {
        if(!(ChipFlags & UNIATA_SATA))
            break;

        KdPrint2((PRINT_PREFIX "NVIDIA\n"));

        ULONG offs = (ChipFlags & NV4OFF) ? 0x0440 : 0x0010;
        ULONG shift = Channel << ((ChipFlags & NVQ) ? 4 : 2);

        /* get and clear interrupt status */
        if(ChipFlags & NVQ) {
            pr_status = AtapiReadPortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs);
            AtapiWritePortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs, (0x0fUL << shift) | 0x00f000f0);
        } else {
            pr_status = AtapiReadPortEx1(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs);
            AtapiWritePortEx1(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs, (0x0f << shift));
        }
        KdPrint2((PRINT_PREFIX "  pr_status %x\n", pr_status));

        /* check for and handle connect events */
        if(((pr_status & (0x0cUL << shift)) == (0x04UL << shift)) ) {
            UniataSataEvent(deviceExtension, lChannel, UNIATA_SATA_EVENT_ATTACH, 0);
        }
        /* check for and handle disconnect events */
        if((pr_status & (0x08UL << shift)) &&
            !((pr_status & (0x04UL << shift) &&
            UniataSataReadPort4(chan, IDX_SATA_SStatus, 0))) ) {
            UniataSataEvent(deviceExtension, lChannel, UNIATA_SATA_EVENT_DETACH, 0);
        }
        /* do we have any device action ? */
        if(!(pr_status & (0x01UL << shift))) {
            KdPrint2((PRINT_PREFIX "  nVidia unexpected\n"));
            if(UniataSataClearErr(HwDeviceExtension, c, UNIATA_SATA_DO_CONNECT, 0)) {
                OurInterrupt = 2;
            } else {
                return FALSE;
            }
        }

        break; }
    case ATA_ATI_ID:
        KdPrint2((PRINT_PREFIX "ATI\n"));
        if(ChipType == SIIMIO) {
            // fall to SiI
        } else {
            break;
        }
    case ATA_SILICON_IMAGE_ID:

        if(ChipType == SIIMIO) {

            reg32 = AtapiReadPort4(chan, IDX_BM_DeviceSpecific0);
            KdPrint2((PRINT_PREFIX "  Sii DS0 %x\n", reg32));
            if(reg32 == 0xffffffff) {
                KdPrint2((PRINT_PREFIX "  Sii mio unexpected\n"));
                return FALSE;
            }
            if(!(reg32 & (BM_DS0_SII_DMA_SATA_IRQ | BM_DS0_SII_DMA_COMPLETE | BM_DS0_SII_IRQ | BM_DS0_SII_DMA_ENABLE | BM_DS0_SII_DMA_ERROR))) {
                KdPrint2((PRINT_PREFIX "  Sii mio unexpected (2)\n"));
                return FALSE;
            }

            if(ChipFlags & UNIATA_SATA) {
                if(reg32 & (BM_DS0_SII_DMA_SATA_IRQ | BM_DS0_SII_IRQ)) {

                    /* SIEN doesn't mask SATA IRQs on some 3112s.  Those
                    * controllers continue to assert IRQ as long as
                    * SError bits are pending.  Clear SError immediately.
                    */
                    if(UniataSataClearErr(HwDeviceExtension, c, UNIATA_SATA_DO_CONNECT, 0)) {
                        OurInterrupt = 2;
                    }
                }
            }

            if (!DmaTransfer)
                break;
            if (!((dma_status = GetDmaStatus(deviceExtension, lChannel)) & BM_STATUS_INTR)) {
                KdPrint2((PRINT_PREFIX "  Sii mio unexpected (3)\n"));
                return OurInterrupt;
            }
            AtapiWritePort1(chan, IDX_BM_Status, dma_status & ~BM_STATUS_ERR);
            goto skip_dma_stat_check;

        } else {
            if(!(deviceExtension->HwFlags & SIIINTR))
                break;
            GetPciConfig1(0x71, reg8);
            KdPrint2((PRINT_PREFIX "  0x71 = %#x\n", reg8));
            if (!(reg8 &
                  (Channel ? 0x08 : 0x04))) {
                return FALSE;
            }
            if (!DmaTransfer) {
                KdPrint2((PRINT_PREFIX "  cmd our\n"));
                OurInterrupt = 2;
            }
            SetPciConfig1(0x71, (Channel ? 0x08 : 0x04));
        }
        break;

    case ATA_ACARD_ID:
        if (!DmaTransfer)
            break;
        //dma_status = GetDmaStatus(deviceExtension, lChannel);
        if (!((dma_status = GetDmaStatus(deviceExtension, lChannel)) & BM_STATUS_INTR)) {
            KdPrint2((PRINT_PREFIX "  Acard unexpected\n"));
            return FALSE;
        }
        AtapiWritePort1(chan, IDX_BM_Status, dma_status | BM_STATUS_INTR);
        AtapiStallExecution(1);
        AtapiWritePort1(chan, IDX_BM_Command,
            AtapiReadPort1(chan, IDX_BM_Command) & ~BM_COMMAND_START_STOP);
        goto skip_dma_stat_check;
    case ATA_INTEL_ID:
        if(UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
            if(ChipFlags & UNIATA_AHCI) {
                // Do nothing here
            } else
            if(ChipFlags & UNIATA_SATA) {
                if(UniataSataClearErr(HwDeviceExtension, c, UNIATA_SATA_DO_CONNECT, 0)) {
                    OurInterrupt = 2;
                }
                if(!(chan->ChannelCtrlFlags & CTRFLAGS_NO_SLAVE)) {
                    if(UniataSataClearErr(chan->DeviceExtension, chan->lChannel, UNIATA_SATA_IGNORE_CONNECT, 1)) {
                        OurInterrupt = 2;
                    }
                }
            }
        }
        break;
    default:
        if(UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
            if(ChipFlags & UNIATA_AHCI) {
                // Do nothing here
            } else
            if(ChipFlags & UNIATA_SATA) {
                if(UniataSataClearErr(HwDeviceExtension, c, UNIATA_SATA_DO_CONNECT, 0)) {
                    OurInterrupt = 2;
                }
            }
        }
    }
check_unknown:
    KdPrint2((PRINT_PREFIX "  perform generic check\n"));
    if (DmaTransfer) {
        if (!((dma_status = GetDmaStatus(deviceExtension, lChannel)) & BM_STATUS_INTR)) {
            KdPrint2((PRINT_PREFIX "  DmaTransfer + !BM_STATUS_INTR (%x)\n", dma_status));
            if(dma_status & BM_STATUS_ERR) {
                KdPrint2((PRINT_PREFIX "  DmaTransfer + BM_STATUS_ERR -> our\n"));
                OurInterrupt = 2;
            } else {
                KdPrint2((PRINT_PREFIX "  getting status...\n"));
                GetStatus(chan, statusByte);
                KdPrint2((PRINT_PREFIX "  status %#x\n", statusByte));
                if(statusByte & IDE_STATUS_ERROR) {
                    KdPrint2((PRINT_PREFIX "  IDE_STATUS_ERROR -> our\n", statusByte));
                    OurInterrupt = 2;
                } else {
                    return FALSE;
                }
            }
        }
    } else {
        if(dma_status & BM_STATUS_INTR) {
            // bullshit, we have DMA interrupt, but had never initiate DMA operation
            KdPrint2((PRINT_PREFIX "  clear unexpected DMA intr\n"));
            AtapiDmaDone(deviceExtension, DEVNUM_NOT_SPECIFIED ,lChannel, NULL);
            // catch it !
            OurInterrupt = 2;
        }
    }
skip_dma_stat_check:
    if(!(ChipFlags & UNIATA_SATA)) {
        AtapiStallExecution(1);
    }

    LunExt = &(deviceExtension->lun[c*2 + chan->cur_cdev]);
    /* if drive is busy it didn't interrupt */
    /* the exception is DCS + BSY state of ATAPI devices */
    KdPrint2((PRINT_PREFIX "  getting status...\n"));
    GetStatus(chan, statusByte);
    if(LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) {
        KdPrint3((PRINT_PREFIX "  ATAPI status %#x\n", statusByte));
    } else {
        KdPrint2((PRINT_PREFIX "  IDE status %#x\n", statusByte));
    }
    if (statusByte == 0xff) {
        // interrupt from empty controller ?
    } else 
    if (statusByte & IDE_STATUS_BUSY) {
        if(!chan->ExpectingInterrupt) {
            KdPrint3((PRINT_PREFIX "  unexpected intr + BUSY\n"));
            return OurInterrupt;
        }

        if(LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) {
            KdPrint2((PRINT_PREFIX "  ATAPI additional check\n"));
        } else {
            KdPrint2((PRINT_PREFIX "  expecting intr + BUSY (3), non ATAPI\n"));
            return FALSE;
        }
        if((statusByte & ~IDE_STATUS_DRQ) != (IDE_STATUS_BUSY | IDE_STATUS_DRDY | IDE_STATUS_DSC)) {
            KdPrint3((PRINT_PREFIX "  unexpected status, seems it is not our\n"));
            return FALSE;
        }
        if(!(LunExt->DeviceFlags & DFLAGS_INT_DRQ) && (statusByte & IDE_STATUS_DRQ)) {
            KdPrint3((PRINT_PREFIX "  unexpected DRQ, seems it is not our\n"));
            return FALSE;
        }

        EarlyIntr = TRUE;

        if(dma_status & BM_STATUS_INTR) {
            KdPrint3((PRINT_PREFIX "  our interrupt with BSY set, try wait in ISR or post to DPC\n"));
            /* clear interrupt and get status */
            GetBaseStatus(chan, statusByte);
            KdPrint3((PRINT_PREFIX "  base status %#x (+BM_STATUS_INTR)\n", statusByte));
            return TRUE;
        }

        if(g_WaitBusyInISR) {
            GetStatus(chan, statusByte);
            KdPrint2((PRINT_PREFIX "  status re-check %#x\n", statusByte));
            reg8 = AtapiReadPort1(chan, IDX_IO1_i_Error);
            KdPrint2((PRINT_PREFIX "  Error reg (%#x)\n", reg8));
            if (!(statusByte & IDE_STATUS_BUSY)) {
                KdPrint2((PRINT_PREFIX "  expecting intr + cleared BUSY\n"));
            }
            if (statusByte & IDE_STATUS_BUSY) {
                KdPrint2((PRINT_PREFIX "  still BUSY, seems it is not our\n"));
                return FALSE;
            }
        }

    }

    /* clear interrupt and get status */
    GetBaseStatus(chan, statusByte);
    KdPrint2((PRINT_PREFIX "  base status %#x\n", statusByte));
    if (statusByte == 0xff) {
        // interrupt from empty controller ?
    } else 
    if(!(statusByte & (IDE_STATUS_DRQ | IDE_STATUS_DRDY))) {
        KdPrint2((PRINT_PREFIX "  no DRQ/DRDY set\n"));
        return OurInterrupt;
    }

#ifndef UNIATA_PIO_ONLY
    if(DmaTransfer) {
        if(!EarlyIntr || g_WaitBusyInISR) {
            dma_status = AtapiDmaDone(HwDeviceExtension, DEVNUM_NOT_SPECIFIED, lChannel, NULL/*srb*/);
        } else {
            PSCSI_REQUEST_BLOCK srb = UniataGetCurRequest(chan);
            PATA_REQ AtaReq = srb ? (PATA_REQ)(srb->SrbExtension) : NULL;

            //ASSERT(AtaReq);

            KdPrint2((PRINT_PREFIX "  set REQ_STATE_EARLY_INTR.\n"));
            if(AtaReq) {
                AtaReq->ReqState = REQ_STATE_EARLY_INTR;
            }
        }
    }
#endif //

    if (!(chan->ExpectingInterrupt)) {

        KdPrint2((PRINT_PREFIX "  Unexpected interrupt.\n"));

        if(LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) {
            KdPrint2((PRINT_PREFIX "  ATAPI additional check\n"));
        } else {
            KdPrint2((PRINT_PREFIX "  OurInterrupt = %d\n", OurInterrupt));
            return OurInterrupt;
        }
        interruptReason = (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason) & 0x3);
        KdPrint3((PRINT_PREFIX "AtapiCheckInterrupt__: ATAPI int reason %x\n", interruptReason));
        return OurInterrupt;
    }
    //ASSERT(!chan->queue_depth || chan->cur_req);

    KdPrint2((PRINT_PREFIX "AtapiCheckInterrupt__: exit with TRUE\n"));
    return TRUE;

} // end AtapiCheckInterrupt__()


BOOLEAN
NTAPI
AtapiInterrupt__(
    IN PVOID HwDeviceExtension,
    IN UCHAR c
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PHW_CHANNEL chan = &(deviceExtension->chan[c]);
    // Get current Srb
    PSCSI_REQUEST_BLOCK srb = UniataGetCurRequest(chan);
    PATA_REQ AtaReq = srb ? (PATA_REQ)(srb->SrbExtension) : NULL;

    ULONG wordCount = 0, wordsThisInterrupt = DEV_BSIZE/2;
    ULONG status = SRB_STATUS_SUCCESS;
    UCHAR dma_status = 0;
    ULONG i;
    ULONG k;
    UCHAR statusByte = 0,interruptReason;

    BOOLEAN atapiDev = FALSE;

    UCHAR Channel;
    UCHAR lChannel;
    UCHAR DeviceNumber;
    BOOLEAN DmaTransfer = FALSE;
    UCHAR error = 0;
    ULONG TimerValue = 1000;
#ifdef UNIATA_USE_XXableInterrupts
    BOOLEAN InDpc = (KeGetCurrentIrql() == DISPATCH_LEVEL);
#else
    BOOLEAN InDpc = (chan->DpcState != DPC_STATE_ISR);
#endif // UNIATA_USE_XXableInterrupts
    BOOLEAN UseDpc = deviceExtension->UseDpc;
//    BOOLEAN RestoreUseDpc = FALSE;
    BOOLEAN DataOverrun = FALSE;
    BOOLEAN NoStartIo = TRUE;

    KdPrint2((PRINT_PREFIX "AtapiInterrupt:\n"));
    if(InDpc) {
        KdPrint2((PRINT_PREFIX "  InDpc = TRUE\n"));
        //ASSERT((chan->ChannelCtrlFlags & CTRFLAGS_INTR_DISABLED));
    }

    UCHAR PathId;
    UCHAR TargetId;
    UCHAR Lun;
    UCHAR OldReqState = REQ_STATE_NONE;
    ULONG ldev;
    PHW_LU_EXTENSION LunExt;

    lChannel = c;
    Channel = (UCHAR)(deviceExtension->Channel + lChannel);

    KdPrint2((PRINT_PREFIX "  cntrlr %#x:%d, irql %#x, c %d\n", deviceExtension->DevIndex, Channel, KeGetCurrentIrql(), c));

    if((chan->ChannelCtrlFlags & CTRFLAGS_DMA_ACTIVE) ||
       (AtaReq && (AtaReq->Flags & REQ_FLAG_DMA_OPERATION)) ) {
        DmaTransfer = TRUE;
        KdPrint2((PRINT_PREFIX "  DmaTransfer = TRUE\n"));
    }

    if (srb) {
        PathId   = srb->PathId;  
        TargetId = srb->TargetId;
        Lun      = srb->Lun;     
    } else {
        PathId = (UCHAR)c;
        TargetId =
        Lun      = 0;
        goto enqueue_next_req;
    }

    ldev = GET_LDEV2(PathId, TargetId, Lun);
    DeviceNumber = (UCHAR)(ldev & 1);
    LunExt = &(deviceExtension->lun[ldev]);
    atapiDev = (LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) ? TRUE : FALSE;
    KdPrint2((PRINT_PREFIX "  dev_type %s\n", atapiDev ? "ATAPI" : "IDE"));

    // check if we are in ISR DPC
    if(InDpc) {
        KdPrint2((PRINT_PREFIX "  InDpc -> CTRFLAGS_INTR_DISABLED\n"));
        goto ServiceInterrupt;
    }

    if (DmaTransfer) {
        dma_status = GetDmaStatus(deviceExtension, lChannel);
    }

    if (!(chan->ExpectingInterrupt)) {

        KdPrint2((PRINT_PREFIX "  Unexpected interrupt for this channel.\n"));
        return FALSE;
    }

    // change request state
    if(AtaReq) {
        OldReqState = AtaReq->ReqState;
        AtaReq->ReqState = REQ_STATE_PROCESSING_INTR;
        KdPrint2((PRINT_PREFIX "  OldReqState = %x\n", OldReqState));
    }

    // We don't want using DPC for fast operations, like
    // DMA completion, sending CDB, short ATAPI transfers, etc.
    // !!!! BUT !!!!
    // We MUST use DPC, because of interprocessor synchronization
    // on multiprocessor platforms

    if(DmaTransfer)
        goto ServiceInterrupt;

    switch(OldReqState) {
    case REQ_STATE_ATAPI_EXPECTING_CMD_INTR:
        KdPrint3((PRINT_PREFIX "  EXPECTING_CMD_INTR\n"));
    case REQ_STATE_ATAPI_EXPECTING_DATA_INTR:
    case REQ_STATE_DPC_WAIT_BUSY0:
    case REQ_STATE_DPC_WAIT_BUSY1:
        KdPrint2((PRINT_PREFIX "  continue service interrupt\n"));
        goto ServiceInterrupt;
    case REQ_STATE_ATAPI_DO_NOTHING_INTR:
        KdPrint2((PRINT_PREFIX "  do nothing on interrupt\n"));
        return TRUE;
    }

    if(!DmaTransfer && !atapiDev) {
        KdPrint2((PRINT_PREFIX "  service PIO HDD\n"));
        UseDpc = FALSE;
    }

#ifndef UNIATA_CORE

    if(!UseDpc)
        goto ServiceInterrupt;

#ifdef UNIATA_USE_XXableInterrupts
    if(InDpc) {
        KdPrint2((PRINT_PREFIX "  Unexpected InDpc\n"));
        ASSERT(FALSE);
        // shall never get here
        TimerValue = 1;
        goto CallTimerDpc;
    }

    KdPrint2((PRINT_PREFIX "  this is direct DPC call on DRQL\n"));
    if(AtaReq) {
        AtaReq->ReqState = REQ_STATE_DPC_INTR_REQ;
        KdPrint2((PRINT_PREFIX "  ReqState -> REQ_STATE_DPC_INTR_REQ\n"));
    } else {
        KdPrint2((PRINT_PREFIX "  DPC without AtaReq!!!\n"));
    }
#else
    KdPrint2((PRINT_PREFIX "call service interrupt\n"));
    goto ServiceInterrupt;
#endif // UNIATA_USE_XXableInterrupts

PostToDpc:

    // Attention !!!
    // AtapiInterruptDpc() is called on DISPATCH_LEVEL
    // We always get here when are called from timer callback, which is invoked on DRQL.
    // It is intended to lower IRQL and let other interrupts to be serviced while we are waiting for BUSY release

    KdPrint2((PRINT_PREFIX "AtapiInterrupt: start DPC init...\n"));
    // disable interrupts for this channel,
    // but avoid recursion and double-disable
    if(OldReqState != REQ_STATE_DPC_WAIT_BUSY1) {
        AtapiDisableInterrupts(deviceExtension, lChannel);
    }
    // go to ISR DPC
    chan->ChannelCtrlFlags |= CTRFLAGS_DPC_REQ;

#ifdef UNIATA_USE_XXableInterrupts
    // Will lower IRQL to DISPATCH_LEVEL
    ScsiPortNotification(CallEnableInterrupts, HwDeviceExtension,
                         /*c ?*/ AtapiInterruptDpc/*_1 : AtapiInterruptDpc_0*/);
    KdPrint2((PRINT_PREFIX "AtapiInterrupt: DPC inited\n"));
#else
    // Will raise IRQL to DIRQL
    AtapiQueueTimerDpc(HwDeviceExtension, c,
                         AtapiInterruptDpc,
                         TimerValue);
    KdPrint2((PRINT_PREFIX "AtapiInterrupt: Timer DPC inited\n"));
#endif // UNIATA_USE_XXableInterrupts
    return TRUE;

#ifndef UNIATA_CORE
CallTimerDpc:
    AtaReq->ReqState = REQ_STATE_PROCESSING_INTR;
CallTimerDpc2:
    // Will raise IRQL to DIRQL
    AtapiQueueTimerDpc(HwDeviceExtension, c,
                         AtapiCallBack_X,
                         TimerValue);
    return TRUE;
#endif //UNIATA_CORE

ServiceInterrupt:

    if(AtaReq && InDpc) {
        switch(AtaReq->ReqState) {
        case REQ_STATE_DPC_WAIT_DRQ0:
            goto PIO_wait_DRQ0;
        case REQ_STATE_DPC_WAIT_BUSY:
            goto PIO_wait_busy;
        case REQ_STATE_DPC_WAIT_DRQ:
            goto PIO_wait_DRQ;
        case REQ_STATE_DPC_WAIT_DRQ_ERR:
            goto continue_err;
        case REQ_STATE_DPC_WAIT_BUSY0:
        case REQ_STATE_DPC_WAIT_BUSY1:
            // continue normal execution
            break;
        }
    }
#else
ServiceInterrupt:
#endif //UNIATA_CORE
/*
    // make additional delay for old devices (if we are not in DPC)
    if((!LunExt->IdentifyData.MajorRevision || (deviceExtension->lun[DeviceNumber].TransferMode < ATA_PIO4))
             &&
       !InDpc &&
       !atapiDev &&
       !(deviceExtension->HwFlags & UNIATA_SATA)
       ) {
        KdPrint2((PRINT_PREFIX "  additional delay 10us for old devices\n"));
        AtapiStallExecution(10);
    }
*/
    /* clear interrupt and get status */
    GetBaseStatus(chan, statusByte);
    if(atapiDev) {
        KdPrint3((PRINT_PREFIX "AtapiInterrupt: ATAPI Entered with status (%#x)\n", statusByte));
    } else {
        KdPrint2((PRINT_PREFIX "AtapiInterrupt: Entered with status (%#x)\n", statusByte));
    }

    if(!UseDpc) {
        KdPrint2((PRINT_PREFIX "  operate like in DPC\n"));
        InDpc = TRUE;
    }
                                                                 	
    if (!atapiDev) {
        // IDE
        if (statusByte & IDE_STATUS_BUSY) {
            if (deviceExtension->DriverMustPoll) {
                // Crashdump is polling and we got caught with busy asserted.
                // Just go away, and we will be polled again shortly.
                KdPrint2((PRINT_PREFIX "  Hit BUSY while polling during crashdump.\n"));
                goto ReturnEnableIntr;
            }
try_dpc_wait:
            // Ensure BUSY is non-asserted.
            // make a very small idle before falling to DPC
            k = (InDpc && UseDpc) ? 1000 : 2;

            for (i = 0; i < k; i++) {

                GetBaseStatus(chan, statusByte);
                if (!(statusByte & IDE_STATUS_BUSY)) {
                    break;
                }
                AtapiStallExecution(10);
            }

            if (!InDpc && UseDpc && i == 2) {

                KdPrint2((PRINT_PREFIX "  BUSY on entry. Status %#x, Base IO %#x\n", statusByte));

                TimerValue = 50;
                AtaReq->ReqState = REQ_STATE_DPC_WAIT_BUSY0;

#ifndef UNIATA_CORE
                goto PostToDpc;
#else //UNIATA_CORE
                AtapiStallExecution(TimerValue);
                goto ServiceInterrupt;
#endif //UNIATA_CORE
            } else 
            if (InDpc && i == k) {
                // reset the controller.
                KdPrint2((PRINT_PREFIX 
                            "  Resetting due to BUSY on entry - %#x.\n",
                            statusByte));
                goto IntrPrepareResetController;
            }
        }
    } else {
        // ATAPI
        if(!LunExt->IdentifyData.MajorRevision &&
            InDpc &&
            !atapiDev &&
            !(deviceExtension->HwFlags & UNIATA_SATA)
            ) {
            KdPrint2((PRINT_PREFIX "  additional delay 10us for old devices (2)\n"));
            AtapiStallExecution(10);
        }
        if (statusByte & IDE_STATUS_BUSY) {
        //if(chan->ChannelCtrlFlags & CTRFLAGS_DSC_BSY) {
            KdPrint3((PRINT_PREFIX "  BUSY on ATAPI device, waiting\n"));
            for(k=20; k; k--) {
                GetStatus(chan, statusByte);
                KdPrint3((PRINT_PREFIX "  status re-check %#x\n", statusByte));
                KdPrint3((PRINT_PREFIX "  Error reg (%#x)\n",
                            AtapiReadPort1(chan, IDX_IO1_i_Error)));
                if (!(statusByte & IDE_STATUS_BUSY)) {
                    KdPrint2((PRINT_PREFIX "  expecting intr + cleared BUSY\n"));
                    break;
                }
                if(k <= 18) {
                    KdPrint3((PRINT_PREFIX "  too long wait -> DPC\n"));
                    if(!InDpc) {
                        KdPrint2((PRINT_PREFIX "  too long wait: ISR -> DPC\n"));
                        TimerValue = 100;
                        AtaReq->ReqState = REQ_STATE_DPC_WAIT_BUSY0;
                    } else {
                        KdPrint2((PRINT_PREFIX "  too long wait: DPC -> DPC\n"));
                        TimerValue = 1000;
                        AtaReq->ReqState = REQ_STATE_DPC_WAIT_BUSY1;
                    }
#ifndef UNIATA_CORE
                    goto CallTimerDpc2;
#else //UNIATA_CORE
                    AtapiStallExecution(TimerValue);
#endif //UNIATA_CORE
                }

                AtapiStallExecution(10);
            }
            if (statusByte & IDE_STATUS_BUSY) {
                KdPrint3((PRINT_PREFIX "  expecting intr + BUSY (2), try DPC wait\n"));
                goto try_dpc_wait;
            }
        }
    }

    if(AtaReq && DmaTransfer) {
        switch(OldReqState) {
        case REQ_STATE_EARLY_INTR:
        case REQ_STATE_DPC_WAIT_BUSY0:

            if(chan->ChannelCtrlFlags & CTRFLAGS_DMA_ACTIVE) {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt: DMA still active\n"));
                dma_status = AtapiDmaDone(HwDeviceExtension, DEVNUM_NOT_SPECIFIED, lChannel, NULL/*srb*/);
            }
            break;
        }
    }

//retry_check:
    // Check for error conditions.
    if ((statusByte & IDE_STATUS_ERROR) ||
        (dma_status & BM_STATUS_ERR)) {

        error = AtapiReadPort1(chan, IDX_IO1_i_Error);
        KdPrint2((PRINT_PREFIX "AtapiInterrupt: Error %#x\n", error));
/*
        if(error & IDE_STATUS_CORRECTED_ERROR) {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: (corrected)\n"));
            statusByte &= ~IDE_STATUS_ERROR;
            goto retry_check;
        }
*/
        if(AtaReq) {
            KdPrint2((PRINT_PREFIX "  Bad Lba %#I64x\n", AtaReq->lba));
        } else {
            KdPrint2((PRINT_PREFIX "  Bad Lba unknown\n"));
        }


        if(!atapiDev) {
            KdPrint2((PRINT_PREFIX "  wait 100 ready after IDE error\n"));
            AtapiStallExecution(100);
        } else {
            KdPrint2((PRINT_PREFIX "  wait 10 ready after ATAPI error\n"));
            AtapiStallExecution(10);
        }
continue_err:

        KdPrint3((PRINT_PREFIX "  Intr on DRQ %x\n",
            LunExt->DeviceFlags & DFLAGS_INT_DRQ));

        for (k = atapiDev ? 0 : 200; k; k--) {
            GetStatus(chan, statusByte);
            if (!(statusByte & IDE_STATUS_DRQ)) {
                AtapiStallExecution(50);
            } else {
                break;
            }
        }

        if (!atapiDev) {
            /* if this is a UDMA CRC error, reinject request */

            AtaReq->retry++;
            if(AtaReq->retry < MAX_RETRIES) {
#ifdef IO_STATISTICS
                chan->lun[DeviceNumber]->ModeErrorCount[AtaReq->retry]++;
#endif //IO_STATISTICS
                if(DmaTransfer /*&&
                   (error & IDE_ERROR_ICRC)*/) {
                    if(AtaReq->retry < MAX_RETRIES) {
//fallback_pio:
                        AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
                        AtaReq->Flags |= REQ_FLAG_FORCE_DOWNRATE;
//                        LunExt->DeviceFlags |= DFLAGS_FORCE_DOWNRATE;
                        AtaReq->ReqState = REQ_STATE_QUEUED;
                        goto reenqueue_req;
                    }
                } else {
                    if(!(AtaReq->Flags & REQ_FLAG_FORCE_DOWNRATE)) {
                        AtaReq->retry++;
                    }
                    KdPrint2((PRINT_PREFIX "Errors in PIO mode\n"));
                }
            }
        } else {
            interruptReason = (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason) & 0x3);
            KdPrint3((PRINT_PREFIX "AtapiInterrupt: ATAPI Error, int reason %x\n", interruptReason));

            if(DmaTransfer && (chan->lun[DeviceNumber]->TransferMode > ATA_UDMA2) &&
               ((error >> 4) == SCSI_SENSE_HARDWARE_ERROR)) {
                if(AtaReq->retry < MAX_RETRIES) {
//fallback_pio:
                    AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
                    AtaReq->Flags |= REQ_FLAG_FORCE_DOWNRATE;
//                        LunExt->DeviceFlags |= DFLAGS_FORCE_DOWNRATE;
                    AtaReq->ReqState = REQ_STATE_QUEUED;
                    goto reenqueue_req;
                }
            } else {
                if(!(AtaReq->Flags & REQ_FLAG_FORCE_DOWNRATE)) {
                    AtaReq->retry++;
                }
                KdPrint3((PRINT_PREFIX "Errors in PIO mode\n"));
            }
        }

        KdPrint3((PRINT_PREFIX "AtapiInterrupt: Error\n"));
        if (srb->Cdb[0] != SCSIOP_REQUEST_SENSE) {
            // Fail this request.
            status = SRB_STATUS_ERROR;
            goto CompleteRequest;
        } else {
            KdPrint2((PRINT_PREFIX "  continue with SCSIOP_REQUEST_SENSE\n"));
        }
    } else
    if(AtaReq->Flags & REQ_FLAG_FORCE_DOWNRATE_LBA48) {
        KdPrint2((PRINT_PREFIX "DMA doesn't work right with LBA48\n"));
        deviceExtension->HbaCtrlFlags |= HBAFLAGS_DMA_DISABLED_LBA48;
    } else
    if(AtaReq->Flags & REQ_FLAG_FORCE_DOWNRATE) {
        KdPrint2((PRINT_PREFIX "Some higher mode doesn't work right :((\n"));
#ifdef IO_STATISTICS
        chan->lun[DeviceNumber]->RecoverCount[AtaReq->retry]++;
        if(chan->lun[DeviceNumber]->RecoverCount[AtaReq->retry] >= chan->lun[DeviceNumber]->IoCount/3 ||
           (deviceExtension->HwFlags & UNIATA_NO80CHK)
           ) {
#else
        if(deviceExtension->HwFlags & UNIATA_NO80CHK) {
#endif //IO_STATISTICS
            KdPrint2((PRINT_PREFIX "Limit transfer rate to %x\n", deviceExtension->lun[DeviceNumber].TransferMode));
            deviceExtension->lun[DeviceNumber].LimitedTransferMode =
                deviceExtension->lun[DeviceNumber].TransferMode;
        }
    }
#ifdef IO_STATISTICS
    chan->lun[DeviceNumber]->IoCount++;
#endif //IO_STATISTICS

continue_PIO:

    // check reason for this interrupt.
    if (atapiDev) {

        KdPrint2((PRINT_PREFIX "AtapiInterrupt: ATAPI branch\n"));
        // ATAPI branch

        interruptReason = (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason) & 0x3);
        KdPrint3((PRINT_PREFIX "AtapiInterrupt: iReason %x\n", interruptReason));
        if(DmaTransfer) {
            wordsThisInterrupt = DEV_BSIZE/2*512;
        } else {
            wordsThisInterrupt = DEV_BSIZE/2;
        }

    } else {

        // ATA branch

        if(DmaTransfer) {
            // simulate DRQ for DMA transfers
            statusByte |= IDE_STATUS_DRQ;
        }
        if (statusByte & IDE_STATUS_DRQ) {

            if(DmaTransfer) {
                wordsThisInterrupt = DEV_BSIZE/2*512;
            } else
            if (LunExt->MaximumBlockXfer) {
                wordsThisInterrupt = DEV_BSIZE/2 * LunExt->MaximumBlockXfer;
            }

            if (srb->SrbFlags & SRB_FLAGS_DATA_IN) {

                interruptReason =  0x2;

            } else if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
                interruptReason = 0x0;

            } else {
                status = SRB_STATUS_ERROR;
                goto CompleteRequest;
            }

        } else if (statusByte & IDE_STATUS_BUSY) {

            //AtapiEnableInterrupts(deviceExtension, lChannel);
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: return FALSE on ATA IDE_STATUS_BUSY\n"));
            return FALSE;

        } else {

            if (AtaReq->WordsLeft) {

                // Funky behaviour seen with PCI IDE (not all, just one).
PIO_wait_DRQ0:
                // The ISR hits with DRQ low, but comes up later.
                for (k = 0; k < 5000; k++) {
                    GetStatus(chan, statusByte);
                    if (statusByte & IDE_STATUS_DRQ) {
                        break;
                    }
                    if(!InDpc) {
                        // goto DPC
                        AtaReq->ReqState = REQ_STATE_DPC_WAIT_DRQ0;
                        TimerValue = 100;
                        KdPrint2((PRINT_PREFIX "AtapiInterrupt: go to DPC (drq0)\n"));
#ifndef UNIATA_CORE
                        goto PostToDpc;
#else //UNIATA_CORE
                        AtapiStallExecution(TimerValue);
                        goto ServiceInterrupt;
#endif //UNIATA_CORE
                    }
                    AtapiStallExecution(100);
                }
                if (k == 5000) {
                    // reset the controller.
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: Resetting due to DRQ not up. Status %#x\n",
                                statusByte));
IntrPrepareResetController:
                    AtapiResetController__(HwDeviceExtension, lChannel, RESET_COMPLETE_CURRENT);
                    goto ReturnEnableIntr;

                } else {
                    interruptReason = (srb->SrbFlags & SRB_FLAGS_DATA_IN) ? 0x2 : 0x0;
                }

            } else {
                // Command complete - verify, write, or the SMART enable/disable.
                // Also get_media_status
                interruptReason = 0x3;
            }
        }
    }

    KdPrint2((PRINT_PREFIX "AtapiInterrupt: i-reason=%d, status=%#x\n", interruptReason, statusByte));
    if (interruptReason == 0x1 && (statusByte & IDE_STATUS_DRQ)) {
        // Write the packet.
        KdPrint3((PRINT_PREFIX "AtapiInterrupt: Writing Atapi packet.\n"));
        // Send CDB to device.
        WriteBuffer(chan, (PUSHORT)srb->Cdb, 6, 0);
        AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_DATA_INTR;

        if(chan->ChannelCtrlFlags & CTRFLAGS_DMA_OPERATION) {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: AtapiDmaStart().\n"));
            AtapiDmaStart(HwDeviceExtension, ldev & 1, lChannel, srb);
        }

        goto ReturnEnableIntr;

    } else if (interruptReason == 0x0 && (statusByte & IDE_STATUS_DRQ)) {

        // Write the data.
        if (atapiDev) {

            // Pick up bytes to transfer and convert to words.
            wordCount =
                AtapiReadPort1(chan, IDX_ATAPI_IO1_i_ByteCountLow);

            wordCount |=
                AtapiReadPort1(chan, IDX_ATAPI_IO1_i_ByteCountHigh) << 8;

            // Covert bytes to words.
            wordCount >>= 1;
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: get W wordCount %#x\n", wordCount));

            if (wordCount != AtaReq->WordsLeft) {
                KdPrint2((PRINT_PREFIX 
                           "AtapiInterrupt: %d words requested; %d words xferred\n",
                           AtaReq->WordsLeft,
                           wordCount));
            }

            // Verify this makes sense.
            if (wordCount > AtaReq->WordsLeft) {
                wordCount = AtaReq->WordsLeft;
                KdPrint2((PRINT_PREFIX 
                           "AtapiInterrupt: Write underrun\n"));
                DataOverrun = TRUE;
            }

        } else {

            // IDE path. Check if words left is at least DEV_BSIZE/2 = 256.
            if (AtaReq->WordsLeft < wordsThisInterrupt) {
               // Transfer only words requested.
               wordCount = AtaReq->WordsLeft;
            } else {
               // Transfer next block.
               wordCount = wordsThisInterrupt;
            }
        }

        if (DmaTransfer && (chan->ChannelCtrlFlags & CTRFLAGS_DMA_OPERATION)) {
            //ASSERT(AtaReq->WordsLeft == wordCount);
            AtaReq->WordsLeft = 0;
            status = SRB_STATUS_SUCCESS;
            chan->ChannelCtrlFlags &= ~CTRFLAGS_DMA_OPERATION;
            goto CompleteRequest;
        }
        // Ensure that this is a write command.
        if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) {

           KdPrint2((PRINT_PREFIX 
                      "AtapiInterrupt: Write interrupt\n"));

           statusByte = WaitOnBusy(chan);

           if (atapiDev || !(LunExt->DeviceFlags & DFLAGS_DWORDIO_ENABLED) /*!deviceExtension->DWordIO*/) {

               WriteBuffer(chan,
                           AtaReq->DataBuffer,
                           wordCount,
                           UniataGetPioTiming(LunExt));
           } else {

               WriteBuffer2(chan,
                           (PULONG)(AtaReq->DataBuffer),
                           wordCount / 2,
                           UniataGetPioTiming(LunExt));
           }
        } else {

            KdPrint3((PRINT_PREFIX 
                        "AtapiInterrupt: Int reason %#x, but srb is for a write %#x.\n",
                        interruptReason,
                        srb));

            // Fail this request.
            status = SRB_STATUS_ERROR;
            goto CompleteRequest;
        }

        // Advance data buffer pointer and bytes left.
        AtaReq->DataBuffer += wordCount;
        AtaReq->WordsLeft -= wordCount;

        if (atapiDev) {
            AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_DATA_INTR;
        }

        goto ReturnEnableIntr;

    } else if (interruptReason == 0x2 && (statusByte & IDE_STATUS_DRQ)) {


        if (atapiDev) {

            // Pick up bytes to transfer and convert to words.
            wordCount =
                AtapiReadPort1(chan, IDX_ATAPI_IO1_i_ByteCountLow) |
                (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_ByteCountHigh) << 8);

            // Covert bytes to words.
            wordCount /= 2;
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: get R wordCount %#x\n", wordCount));

            if (wordCount != AtaReq->WordsLeft) {
                KdPrint2((PRINT_PREFIX 
                           "AtapiInterrupt: %d words requested; %d words xferred\n",
                           AtaReq->WordsLeft,
                           wordCount));
            }

            // Verify this makes sense.
            if (wordCount > AtaReq->WordsLeft) {
                wordCount = AtaReq->WordsLeft;
                DataOverrun = TRUE;
            }

        } else {

            // Check if words left is at least 256.
            if (AtaReq->WordsLeft < wordsThisInterrupt) {
               // Transfer only words requested.
               wordCount = AtaReq->WordsLeft;
            } else {
               // Transfer next block.
               wordCount = wordsThisInterrupt;
            }
        }

        if (DmaTransfer && (chan->ChannelCtrlFlags & CTRFLAGS_DMA_OPERATION)) {
            //ASSERT(AtaReq->WordsLeft == wordCount);
            AtaReq->WordsLeft = 0;
            status = SRB_STATUS_SUCCESS;
            chan->ChannelCtrlFlags &= ~CTRFLAGS_DMA_OPERATION;
            goto CompleteRequest;
        }
        // Ensure that this is a read command.
        if (srb->SrbFlags & SRB_FLAGS_DATA_IN) {

/*           KdPrint2((
                      "AtapiInterrupt: Read interrupt\n"));*/

            statusByte = WaitOnBusy(chan);

            if (atapiDev || !(LunExt->DeviceFlags & DFLAGS_DWORDIO_ENABLED) /*!deviceExtension->DWordIO*/) {
                KdPrint2((PRINT_PREFIX 
                           "IdeIntr: Read %#x words\n", wordCount));

                ReadBuffer(chan,
                          AtaReq->DataBuffer,
                          wordCount,
                          UniataGetPioTiming(LunExt));
                KdPrint2(("IdeIntr: PIO Read AtaReq->DataBuffer %#x, srb->DataBuffer %#x\n", AtaReq->DataBuffer, (srb ? srb->DataBuffer : (void*)-1) ));
                //KdDump(AtaReq->DataBuffer, wordCount*2);
                if(srb && atapiDev && srb->Cdb[0] == SCSIOP_REQUEST_SENSE) {
                    KdDump(AtaReq->DataBuffer, wordCount*2);
                }

                GetStatus(chan, statusByte);
                KdPrint2((PRINT_PREFIX "  status re-check %#x\n", statusByte));

                if(DataOverrun) {
                    KdPrint2((PRINT_PREFIX "  DataOverrun\n"));
                    AtapiSuckPort2(chan);
                }

            } else {
                KdPrint2((PRINT_PREFIX 
                          "IdeIntr: Read %#x Dwords\n", wordCount/2));

                ReadBuffer2(chan,
                           (PULONG)(AtaReq->DataBuffer),
                           wordCount / 2,
                           UniataGetPioTiming(LunExt));
            }
        } else {

            KdPrint3((PRINT_PREFIX 
                        "AtapiInterrupt: Int reason %#x, but srb is for a read %#x.\n",
                        interruptReason,
                        srb));

            // Fail this request.
            status = SRB_STATUS_ERROR;
            goto CompleteRequest;
        }

        // Advance data buffer pointer and bytes left.
        AtaReq->DataBuffer += wordCount;
        AtaReq->WordsLeft -= wordCount;

        // Check for read command complete.
        if (AtaReq->WordsLeft == 0) {

            KdPrint2((PRINT_PREFIX "AtapiInterrupt: all transferred, AtaReq->WordsLeft == 0\n"));
            if (atapiDev) {

                // 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] == SCSIOP_READ_CAPACITY) &&
                    (LunExt->IdentifyData.DeviceType == ATAPI_TYPE_CDROM)) {

                    AtaReq->DataBuffer -= wordCount;
                    if (AtaReq->DataBuffer[0] == 0x00) {

                        *((ULONG *) &(AtaReq->DataBuffer[0])) = 0xFFFFFF7F;

                    }

                    *((ULONG *) &(AtaReq->DataBuffer[2])) = 0x00080000;
                    AtaReq->DataBuffer += wordCount;
                }
            } else {

            /*
                // Completion for IDE drives.
                if (AtaReq->WordsLeft) {
                    status = SRB_STATUS_DATA_OVERRUN;
                } else {
                    status = SRB_STATUS_SUCCESS;
                }

                goto CompleteRequest;
            */
                status = SRB_STATUS_SUCCESS;
                goto CompleteRequest;

            }
        } else {
            if (atapiDev) {
                AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_DATA_INTR;
            }
        }

        goto ReturnEnableIntr;

    } else if (interruptReason == 0x3  && !(statusByte & IDE_STATUS_DRQ)) {

        KdPrint2((PRINT_PREFIX "AtapiInterrupt: interruptReason = CompleteRequest\n"));
        // Command complete.
        if(DmaTransfer) {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: CompleteRequest, was DmaTransfer\n"));
            AtaReq->WordsLeft = 0;
        }
        if (AtaReq->WordsLeft) {
            status = SRB_STATUS_DATA_OVERRUN;
        } else {
            status = SRB_STATUS_SUCCESS;
        }

#ifdef UNIATA_DUMP_ATAPI
        if(srb &&
           srb->SrbFlags & SRB_FLAGS_DATA_IN) {
            UCHAR                   ScsiCommand;
            PCDB                    Cdb;
            PCHAR                   CdbData;
            PCHAR                   ModeSelectData;
            ULONG                   CdbDataLen;
            PSCSI_REQUEST_BLOCK     Srb = srb;

            Cdb = (PCDB)(Srb->Cdb);
            ScsiCommand = Cdb->CDB6.OperationCode;
            CdbData = (PCHAR)(Srb->DataBuffer);
            CdbDataLen = Srb->DataTransferLength;

            if(CdbDataLen > 0x1000) {
                CdbDataLen = 0x1000;
            }

            KdPrint(("--\n"));
            KdPrint2(("VendorID+DeviceID/Rev %#x/%#x\n", deviceExtension->DevID, deviceExtension->RevID));
            KdPrint2(("P:T:D=%d:%d:%d\n",
                                      Srb->PathId,
                                      Srb->TargetId,
                                      Srb->Lun));
            KdPrint(("Complete SCSI Command %2.2x\n", ScsiCommand));
            KdDump(Cdb, 16);

            if(ScsiCommand == SCSIOP_MODE_SENSE) {
                KdPrint(("ModeSense 6\n"));
                PMODE_PARAMETER_HEADER ParamHdr = (PMODE_PARAMETER_HEADER)CdbData;
                ModeSelectData = CdbData+4;
                KdDump(CdbData, CdbDataLen);
            } else
            if(ScsiCommand == SCSIOP_MODE_SENSE10) {
                KdPrint(("ModeSense 10\n"));
                PMODE_PARAMETER_HEADER ParamHdr = (PMODE_PARAMETER_HEADER)CdbData;
                ModeSelectData = CdbData+8;
                KdDump(CdbData, CdbDataLen);
            } else {
                if(srb->SrbFlags & SRB_FLAGS_DATA_IN) {
                    KdPrint(("Read buffer from device:\n"));
                    KdDump(CdbData, CdbDataLen);
                }
            }
            KdPrint(("--\n"));
        }
#endif //UNIATA_DUMP_ATAPI

CompleteRequest:

        KdPrint2((PRINT_PREFIX "AtapiInterrupt: CompleteRequest\n"));
        // Check and see if we are processing our secret (mechanism status/request sense) srb
        if (AtaReq->OriginalSrb) {

            ULONG srbStatus;

            KdPrint2((PRINT_PREFIX "AtapiInterrupt: OriginalSrb != NULL\n"));
            if (srb->Cdb[0] == SCSIOP_MECHANISM_STATUS) {

                KdPrint3((PRINT_PREFIX "AtapiInterrupt: SCSIOP_MECHANISM_STATUS status %#x\n", status));
                if (status == SRB_STATUS_SUCCESS) {
                    // Bingo!!
                    AtapiHwInitializeChanger (HwDeviceExtension,
                                              srb,
                                              (PMECHANICAL_STATUS_INFORMATION_HEADER) srb->DataBuffer);

                    // Get ready to issue the original srb
                    srb = AtaReq->Srb = AtaReq->OriginalSrb;
                    AtaReq->OriginalSrb = NULL;

                } else {
                    // failed!  Get the sense key and maybe try again
                    srb = AtaReq->Srb = BuildRequestSenseSrb (
                                                          HwDeviceExtension,
                                                          AtaReq->OriginalSrb);
                }
/*
                // do not enable interrupts in DPC, do not waste time, do it now!
                if(UseDpc && chan->DisableIntr) {
                    AtapiEnableInterrupts(HwDeviceExtension, c);
                    UseDpc = FALSE;
                    RestoreUseDpc = TRUE;
                }
*/
                srbStatus = AtapiSendCommand(HwDeviceExtension, srb, CMD_ACTION_ALL);

                KdPrint3((PRINT_PREFIX "AtapiInterrupt: chan->ExpectingInterrupt %d (1)\n", chan->ExpectingInterrupt));

                if (srbStatus == SRB_STATUS_PENDING) {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: send orig SRB_STATUS_PENDING (1)\n"));
                    goto ReturnEnableIntr;
                }
/*
                if(RestoreUseDpc) {
                    // restore state on error
                    UseDpc = TRUE;
                    AtapiDisableInterrupts(HwDeviceExtension, c);
                }
*/

            } else { // srb->Cdb[0] == SCSIOP_REQUEST_SENSE)

                PSENSE_DATA senseData = (PSENSE_DATA) srb->DataBuffer;

                KdPrint3((PRINT_PREFIX "AtapiInterrupt: ATAPI command status %#x\n", status));
                if (status == SRB_STATUS_DATA_OVERRUN) {
                    // Check to see if we at least get mininum number of bytes
                    if ((srb->DataTransferLength - AtaReq->WordsLeft) >
                        (offsetof (SENSE_DATA, AdditionalSenseLength) + sizeof(senseData->AdditionalSenseLength))) {
                        status = SRB_STATUS_SUCCESS;
                    }
                }

                if (status == SRB_STATUS_SUCCESS) {
#ifndef UNIATA_CORE
                    if ((senseData->SenseKey != SCSI_SENSE_ILLEGAL_REQUEST) &&
                        chan->MechStatusRetryCount) {

                        // The sense key doesn't say the last request is illegal, so try again
                        chan->MechStatusRetryCount--;
                        srb = AtaReq->Srb = BuildMechanismStatusSrb (
                                                              HwDeviceExtension,
                                                              AtaReq->OriginalSrb);
                    } else {

                        // last request was illegal.  No point trying again
                        AtapiHwInitializeChanger (HwDeviceExtension,
                                                  srb,
                                                  (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL);

                        // Get ready to issue the original srb
                        srb = AtaReq->Srb = AtaReq->OriginalSrb;
                        AtaReq->OriginalSrb = NULL;
                    }
#endif //UNIATA_CORE
/*
                    // do not enable interrupts in DPC, do not waste time, do it now!
                    if(UseDpc && chan->DisableIntr) {
                        AtapiEnableInterrupts(HwDeviceExtension, c);
                        UseDpc = FALSE;
                        RestoreUseDpc = TRUE;
                    }
*/
                    srbStatus = AtapiSendCommand(HwDeviceExtension, srb, CMD_ACTION_ALL);

                    KdPrint3((PRINT_PREFIX "AtapiInterrupt: chan->ExpectingInterrupt %d (2)\n", chan->ExpectingInterrupt));

                    if (srbStatus == SRB_STATUS_PENDING) {
                        KdPrint2((PRINT_PREFIX "AtapiInterrupt: send orig SRB_STATUS_PENDING (2)\n"));
                        goto ReturnEnableIntr;
                    }
/*
                    if(RestoreUseDpc) {
                        // restore state on error
                        UseDpc = TRUE;
                        AtapiDisableInterrupts(HwDeviceExtension, c);
                    }
*/
                }
            }

            // If we get here, it means AtapiSendCommand() has failed
            // Can't recover.  Pretend the original srb has failed and complete it.

            KdPrint3((PRINT_PREFIX "AtapiInterrupt: Error. complete OriginalSrb\n"));

            if (AtaReq->OriginalSrb) {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt: call AtapiHwInitializeChanger()\n"));
                AtapiHwInitializeChanger (HwDeviceExtension,
                                          srb,
                                          (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL);
                srb = AtaReq->Srb = AtaReq->OriginalSrb;
                AtaReq->OriginalSrb = NULL;
            }

            KdPrint2((PRINT_PREFIX "AtapiInterrupt: chan->ExpectingInterrupt %d (3)\n", chan->ExpectingInterrupt));

            // fake an error and read no data
            status = SRB_STATUS_ERROR;
            srb->ScsiStatus = 0;
            AtaReq->DataBuffer = (PUSHORT)(srb->DataBuffer);
            AtaReq->WordsLeft = srb->DataTransferLength;
            chan->RDP = FALSE;

        } else if (status == SRB_STATUS_ERROR) {

            // Map error to specific SRB status and handle request sense.
            KdPrint3((PRINT_PREFIX "AtapiInterrupt: Error. Begin mapping...\n"));
            status = MapError(deviceExtension,
                              srb);

            chan->RDP = FALSE;

        } else if(!DmaTransfer) {

            KdPrint2((PRINT_PREFIX "AtapiInterrupt: PIO completion\n"));
            // Command complete.
PIO_wait_busy:
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: PIO completion, wait BUSY\n"));
            // Wait for busy to drop.
            for (i = 0; i < 5*30; i++) {
                GetStatus(chan, statusByte);
                if (!(statusByte & IDE_STATUS_BUSY)) {
                    break;
                }
                if(!InDpc) {
                    // goto DPC
                    AtaReq->ReqState = REQ_STATE_DPC_WAIT_BUSY;
                    TimerValue = 200;
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: go to DPC (busy)\n"));
#ifndef UNIATA_CORE
                    goto PostToDpc;
#else //UNIATA_CORE
                    AtapiStallExecution(TimerValue);
                    goto ServiceInterrupt;
#endif //UNIATA_CORE
                }
                AtapiStallExecution(100);
            }

            if (i == 5*30) {

                // reset the controller.
                KdPrint2((PRINT_PREFIX 
                            "AtapiInterrupt: Resetting due to BSY still up - %#x.\n",
                            statusByte));
                goto IntrPrepareResetController;
            }
            // Check to see if DRQ is still up.
            if(statusByte & IDE_STATUS_DRQ) {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt: DRQ...\n"));
                if(srb) {
                    if(srb->SrbFlags & SRB_FLAGS_DATA_IN) {
                        KdPrint2((PRINT_PREFIX "srb %x data in\n", srb));
                    } else {
                        KdPrint2((PRINT_PREFIX "srb %x data out\n", srb));
                    }
                } else {
                    KdPrint2((PRINT_PREFIX "srb NULL\n"));
                }
                if(AtaReq) {
                    KdPrint2((PRINT_PREFIX "AtaReq %x AtaReq->WordsLeft=%x\n", AtaReq, AtaReq->WordsLeft));
                } else {
                    KdPrint2((PRINT_PREFIX "AtaReq NULL\n"));
                }
                if(AtaReq && AtaReq->WordsLeft /*&&
                   !(LunExt->DeviceFlags & (DFLAGS_ATAPI_DEVICE | DFLAGS_TAPE_DEVICE | DFLAGS_LBA_ENABLED))*/) {
                    KdPrint2((PRINT_PREFIX "DRQ+AtaReq->WordsLeft -> next portion\n"));
                    goto continue_PIO;
                }
            }
            //if (atapiDev && (statusByte & IDE_STATUS_DRQ)) {}
            //if ((statusByte & IDE_STATUS_DRQ)) {}
            if((statusByte & IDE_STATUS_DRQ) &&
               (LunExt->DeviceFlags & (DFLAGS_ATAPI_DEVICE | DFLAGS_TAPE_DEVICE | DFLAGS_LBA_ENABLED)) ) {

PIO_wait_DRQ:
                KdPrint2((PRINT_PREFIX "AtapiInterrupt: PIO_wait_DRQ\n"));
                for (i = 0; i < 200; i++) {
                    GetStatus(chan, statusByte);
                    if (!(statusByte & IDE_STATUS_DRQ)) {
                        break;
                    }
                    if(!InDpc) {
                        // goto DPC
                        KdPrint2((PRINT_PREFIX "AtapiInterrupt: go to DPC (drq)\n"));
                        AtaReq->ReqState = REQ_STATE_DPC_WAIT_DRQ;
                        TimerValue = 100;
#ifndef UNIATA_CORE
                        goto PostToDpc;
#else //UNIATA_CORE
                        AtapiStallExecution(TimerValue);
                        goto ServiceInterrupt;
#endif //UNIATA_CORE
                    }
                    AtapiStallExecution(100);
                }

                if (i == 200) {
                    // reset the controller.
                    KdPrint2((PRINT_PREFIX   "AtapiInterrupt: Resetting due to DRQ still up - %#x\n",
                                statusByte));
                    goto IntrPrepareResetController;
                }
            }
            if(atapiDev) {
                KdPrint2(("IdeIntr: ATAPI Read AtaReq->DataBuffer %#x, srb->DataBuffer %#x, len %#x\n",
                     AtaReq->DataBuffer, (srb ? srb->DataBuffer : (void*)(-1)), srb->DataTransferLength ));
                //KdDump(srb->DataBuffer, srb->DataTransferLength);
            }
            if(!AtapiDmaPioSync(HwDeviceExtension, srb, (PUCHAR)(srb->DataBuffer), srb->DataTransferLength)) {
                KdPrint2(("IdeIntr: Can't sync DMA and PIO buffers\n"));
            }
        }

        // Clear interrupt expecting flag.
        chan->ExpectingInterrupt = FALSE;
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);

        // Sanity check that there is a current request.
        if(srb != NULL) {
            // Set status in SRB.
            srb->SrbStatus = (UCHAR)status;

            // Check for underflow.
            if(AtaReq->WordsLeft) {

                KdPrint2((PRINT_PREFIX "AtapiInterrupt: Check for underflow, AtaReq->WordsLeft %x\n", AtaReq->WordsLeft));
                // Subtract out residual words and update if filemark hit,
                // setmark hit , end of data, end of media...
                if (!(LunExt->DeviceFlags & DFLAGS_TAPE_DEVICE)) {
                    if (status == SRB_STATUS_DATA_OVERRUN) {
                        srb->DataTransferLength -= AtaReq->WordsLeft*2;
                    } else {
                        srb->DataTransferLength = 0;
                    }
                } else {
                    srb->DataTransferLength -= AtaReq->WordsLeft*2;
                }
            }
            if(status == SRB_STATUS_SUCCESS) {
                AtaReq->WordsTransfered += AtaReq->bcount * DEV_BSIZE/2;
                if(!atapiDev &&
                   AtaReq->WordsTransfered*2 < AtaReq->TransferLength) {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: more I/O required (%x of %x bytes) -> reenqueue\n",
                         AtaReq->WordsTransfered*2, AtaReq->TransferLength));
                    AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
                    AtaReq->ReqState = REQ_STATE_PREPARE_TO_NEXT;
                    goto reenqueue_req;
                } else {
                    KdPrint2((PRINT_PREFIX "   Transfered %x, full size %x\n",
                        AtaReq->WordsTransfered*2, AtaReq->TransferLength));
                }
            }

            if (srb->Function != SRB_FUNCTION_IO_CONTROL) {

CompleteRDP:
                // Indicate command complete.
                if (!(chan->RDP)) {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: RequestComplete\n"));
IntrCompleteReq:

                    if (status == SRB_STATUS_SUCCESS &&
                        srb->SenseInfoBuffer &&
                        srb->SenseInfoBufferLength >= sizeof(SENSE_DATA)) {

                        PSENSE_DATA  senseBuffer = (PSENSE_DATA)srb->SenseInfoBuffer;

                        KdPrint2((PRINT_PREFIX "AtapiInterrupt: set AutoSense\n"));
                        senseBuffer->ErrorCode = 0;
                        senseBuffer->Valid     = 1;
                        senseBuffer->AdditionalSenseLength = 0xb;
                        senseBuffer->SenseKey =  0;
                        senseBuffer->AdditionalSenseCode = 0;
                        senseBuffer->AdditionalSenseCodeQualifier = 0;

                        srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
                    }
                    AtapiDmaDBSync(chan, srb);
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: remove srb %#x, status %x\n", srb, status));
                    UniataRemoveRequest(chan, srb);
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: RequestComplete, srb %#x\n", srb));
                    ScsiPortNotification(RequestComplete,
                                         deviceExtension,
                                         srb);
                }
            } else {

                KdPrint2((PRINT_PREFIX "AtapiInterrupt: IOCTL completion\n"));
                PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)srb->DataBuffer) + sizeof(SRB_IO_CONTROL));

                if (status != SRB_STATUS_SUCCESS) {
                    error = AtapiReadPort1(chan, IDX_IO1_i_Error);
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: error %#x\n", error));
                }

                // Build the SMART status block depending upon the completion status.
                cmdOutParameters->cBufferSize = wordCount;
                cmdOutParameters->DriverStatus.bDriverError = (error) ? SMART_IDE_ERROR : 0;
                cmdOutParameters->DriverStatus.bIDEError = error;

                // If the sub-command is return smart status, jam the value from cylinder low and high, into the
                // data buffer.
                if (chan->SmartCommand == RETURN_SMART_STATUS) {
                    cmdOutParameters->bBuffer[0] = RETURN_SMART_STATUS;
                    cmdOutParameters->bBuffer[1] = AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason);
                    cmdOutParameters->bBuffer[2] = AtapiReadPort1(chan, IDX_ATAPI_IO1_i_Unused1);
                    cmdOutParameters->bBuffer[3] = AtapiReadPort1(chan, IDX_ATAPI_IO1_i_ByteCountLow);
                    cmdOutParameters->bBuffer[4] = AtapiReadPort1(chan, IDX_ATAPI_IO1_i_ByteCountHigh);
                    cmdOutParameters->bBuffer[5] = AtapiReadPort1(chan, IDX_ATAPI_IO1_i_DriveSelect);
                    cmdOutParameters->bBuffer[6] = SMART_CMD;
                    cmdOutParameters->cBufferSize = 8;
                }

                // Indicate command complete.
                goto IntrCompleteReq;
            }

        } else {

            KdPrint2((PRINT_PREFIX "AtapiInterrupt: No SRB!\n"));
        }

        if (chan->RDP) {
            // Check DSC
            for (i = 0; i < 5; i++) {
                GetStatus(chan, statusByte);
                if(!(statusByte & IDE_STATUS_BUSY)) {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: RDP + cleared BUSY\n"));
                    chan->RDP = FALSE;
                    goto CompleteRDP;
                } else
                if (statusByte & IDE_STATUS_DSC) {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: Clear RDP\n"));
                    chan->RDP = FALSE;
                    goto CompleteRDP;
                } 
                AtapiStallExecution(50);
            }
        }
        // RDP can be cleared since previous check
        if (chan->RDP) {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: RequestTimerCall 2000\n"));

            TimerValue = 2000;
#ifndef UNIATA_CORE
            goto CallTimerDpc;
#else //UNIATA_CORE
            AtapiStallExecution(TimerValue);
            goto ServiceInterrupt;
#endif //UNIATA_CORE
        }

//            ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
enqueue_next_req:
        // Get next request
        srb = UniataGetCurRequest(chan);

reenqueue_req:

#ifndef UNIATA_CORE
        KdPrint2((PRINT_PREFIX "AtapiInterrupt: NextRequest, srb=%#x\n",srb));
        if(!srb) {
            ScsiPortNotification(NextRequest,
                                 deviceExtension,
                                 NULL);
        } else {
            ScsiPortNotification(NextLuRequest,
                                 deviceExtension, 
                                 PathId,
                                 TargetId,
                                 Lun);
            // in simplex mode next command must NOT be sent here
            if(!deviceExtension->simplexOnly) {
                AtapiStartIo__(HwDeviceExtension, srb, FALSE);
            }
        }
        // Try to get SRB fron any non-empty queue (later)
        if(deviceExtension->simplexOnly) {
            NoStartIo = FALSE;
        }
#endif //UNIATA_CORE

        goto ReturnEnableIntr;

    } else {

        // Unexpected int. Catch it
        KdPrint2((PRINT_PREFIX "AtapiInterrupt: Unexpected ATAPI interrupt. InterruptReason %#x. Status %#x.\n",
                    interruptReason,
                    statusByte));

    }

ReturnEnableIntr:

    KdPrint2((PRINT_PREFIX "AtapiInterrupt: ReturnEnableIntr\n",srb));
    if(UseDpc) {
        if(CrNtInterlockedExchangeAdd(&(chan->DisableIntr), 0)) {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: call AtapiEnableInterrupts__()\n"));
#ifdef UNIATA_USE_XXableInterrupts
            //ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
            chan->ChannelCtrlFlags |= CTRFLAGS_ENABLE_INTR_REQ;
            // must be called on DISPATCH_LEVEL
            ScsiPortNotification(CallDisableInterrupts, HwDeviceExtension,
                                 AtapiEnableInterrupts__);
#else
            AtapiEnableInterrupts(HwDeviceExtension, c);
            InterlockedExchange(&(chan->CheckIntr),
                                          CHECK_INTR_IDLE);
            // Will raise IRQL to DIRQL
#ifndef UNIATA_CORE
            AtapiQueueTimerDpc(HwDeviceExtension, lChannel,
                                 AtapiEnableInterrupts__,
                                 1);
#endif // UNIATA_CORE
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: Timer DPC inited\n"));
#endif // UNIATA_USE_XXableInterrupts
        }
    }

    InterlockedExchange(&(chan->CheckIntr), CHECK_INTR_IDLE);
    // in simplex mode next command must be sent here if
    // DPC is not used
    KdPrint2((PRINT_PREFIX "AtapiInterrupt: exiting, UseDpc=%d, NoStartIo=%d\n", UseDpc, NoStartIo));

#ifndef UNIATA_CORE
    if(!UseDpc && /*deviceExtension->simplexOnly &&*/ !NoStartIo) {
        chan = UniataGetNextChannel(chan);
        if(chan) {
            srb = UniataGetCurRequest(chan);
        } else {
            srb = NULL;
        }
        KdPrint2((PRINT_PREFIX "AtapiInterrupt: run srb %x\n", srb));
        if(srb) {
            AtapiStartIo__(HwDeviceExtension, srb, FALSE);
        }
    }
#endif //UNIATA_CORE
    return TRUE;

} // end AtapiInterrupt__()

#ifndef UNIATA_CORE

/*++

Routine Description:

    This routine handles SMART enable, disable, read attributes and threshold commands.

Arguments:

    HwDeviceExtension - HBA miniport driver's adapter data storage
    Srb - IO request packet

Return Value:

    SRB status

--*/
ULONG
NTAPI
IdeSendSmartCommand(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    ULONG                c               = GET_CHANNEL(Srb);
    PHW_CHANNEL          chan            = &(deviceExtension->chan[c]);
    PATA_REQ             AtaReq          = (PATA_REQ)(Srb->SrbExtension);
    PSENDCMDOUTPARAMS    cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
    SENDCMDINPARAMS      cmdInParameters = *(PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
    PIDEREGS             regs            = &cmdInParameters.irDriveRegs;
//    ULONG                i;
    UCHAR                statusByte,targetId;


    if (regs->bCommandReg != SMART_CMD) {
        KdPrint2((PRINT_PREFIX 
                    "IdeSendSmartCommand: bCommandReg != SMART_CMD\n"));
        return SRB_STATUS_INVALID_REQUEST;
    }

    targetId = cmdInParameters.bDriveNumber;

    //TODO optimize this check
    if ((!(deviceExtension->lun[targetId].DeviceFlags & DFLAGS_DEVICE_PRESENT)) ||
         (deviceExtension->lun[targetId].DeviceFlags & DFLAGS_ATAPI_DEVICE)) {

        return SRB_STATUS_SELECTION_TIMEOUT;
    }

    chan->SmartCommand = regs->bFeaturesReg;

    // Determine which of the commands to carry out.
    switch(regs->bFeaturesReg) {
    case READ_ATTRIBUTES:
    case READ_THRESHOLDS:

        statusByte = WaitOnBusy(chan);

        if (statusByte & IDE_STATUS_BUSY) {
            KdPrint2((PRINT_PREFIX 
                        "IdeSendSmartCommand: Returning BUSY status\n"));
            return SRB_STATUS_BUSY;
        }

        // Zero the ouput buffer as the input buffer info. has been saved off locally (the buffers are the same).
        RtlZeroMemory(cmdOutParameters, sizeof(SENDCMDOUTPARAMS) + READ_ATTRIBUTE_BUFFER_SIZE - 1);

        // Set data buffer pointer and words left.
        AtaReq->DataBuffer = (PUSHORT)cmdOutParameters->bBuffer;
        AtaReq->WordsLeft = READ_ATTRIBUTE_BUFFER_SIZE / 2;

        statusByte = AtaCommand(deviceExtension, targetId & 0x1, c,
                   regs->bCommandReg,
                   (USHORT)(regs->bCylLowReg) | (((USHORT)(regs->bCylHighReg)) << 8),
                   0,
                   regs->bSectorNumberReg,
                   regs->bSectorCountReg,
                   regs->bFeaturesReg,
                   ATA_IMMEDIATE);

        if(!(statusByte & IDE_STATUS_ERROR)) {
            // Wait for interrupt.
            return SRB_STATUS_PENDING;
        }
        return SRB_STATUS_ERROR;

    case ENABLE_SMART:
    case DISABLE_SMART:
    case RETURN_SMART_STATUS:
    case ENABLE_DISABLE_AUTOSAVE:
    case EXECUTE_OFFLINE_DIAGS:
    case SAVE_ATTRIBUTE_VALUES:

        statusByte = WaitOnBusy(chan);

        if (statusByte & IDE_STATUS_BUSY) {
            KdPrint2((PRINT_PREFIX 
                        "IdeSendSmartCommand: Returning BUSY status\n"));
            return SRB_STATUS_BUSY;
        }

        // Zero the ouput buffer as the input buffer info. has been saved off locally (the buffers are the same).
        RtlZeroMemory(cmdOutParameters, sizeof(SENDCMDOUTPARAMS) - 1);

        // Set data buffer pointer and indicate no data transfer.
        AtaReq->DataBuffer = (PUSHORT)cmdOutParameters->bBuffer;
        AtaReq->WordsLeft = 0;

        statusByte = AtaCommand(deviceExtension, targetId & 0x1, c,
                   regs->bCommandReg,
                   (USHORT)(regs->bCylLowReg) | (((USHORT)(regs->bCylHighReg)) << 8),
                   0,
                   regs->bSectorNumberReg,
                   regs->bSectorCountReg,
                   regs->bFeaturesReg,
                   ATA_IMMEDIATE);

        if(!(statusByte & IDE_STATUS_ERROR)) {
            // Wait for interrupt.
            return SRB_STATUS_PENDING;
        }
        return SRB_STATUS_ERROR;
    } // end switch(regs->bFeaturesReg)

    return SRB_STATUS_INVALID_REQUEST;

} // end IdeSendSmartCommand()

#endif //UNIATA_CORE

ULONGLONG
NTAPI
UniAtaCalculateLBARegs(
    PHW_LU_EXTENSION     LunExt,
    ULONG                startingSector,
    PULONG               max_bcount
    )
{
    UCHAR                drvSelect,sectorNumber;
    USHORT               cylinder;
    ULONG                tmp;

    if(LunExt->DeviceFlags & DFLAGS_LBA_ENABLED) {
        if(LunExt->LimitedTransferMode >= ATA_DMA) {
            if(LunExt->DeviceExtension) {
                (*max_bcount) = LunExt->DeviceExtension->MaximumDmaTransferLength / DEV_BSIZE;
            }
        }
        return startingSector;
    }
    tmp = LunExt->IdentifyData.SectorsPerTrack *
                       LunExt->IdentifyData.NumberOfHeads;
    if(!tmp) {
        KdPrint2((PRINT_PREFIX "UniAtaCalculateLBARegs: 0-sized\n"));
        cylinder     = 0;
        drvSelect    = 0;
        sectorNumber = 1;
        (*max_bcount) = LunExt->IdentifyData.SectorsPerTrack;
    } else {
        cylinder =    (USHORT)(startingSector / tmp);
        drvSelect =   (UCHAR)((startingSector % tmp) / LunExt->IdentifyData.SectorsPerTrack); 
        sectorNumber = (UCHAR)(startingSector % LunExt->IdentifyData.SectorsPerTrack) + 1;
        (*max_bcount) = LunExt->IdentifyData.SectorsPerTrack - sectorNumber + 1;
        KdPrint2((PRINT_PREFIX "UniAtaCalculateLBARegs: C:H:S=%#x:%#x:%#x, max_bc %#x\n",
            cylinder, drvSelect, sectorNumber, (*max_bcount)));
    }
        (*max_bcount) = 0;

    return (ULONG)(sectorNumber&0xff) | (((ULONG)cylinder&0xffff)<<8) | (((ULONG)drvSelect&0xf)<<24);
} // end UniAtaCalculateLBARegs()

ULONGLONG
NTAPI
UniAtaCalculateLBARegsBack(
    PHW_LU_EXTENSION     LunExt,
    ULONGLONG            lba
    )
{
    ULONG                drvSelect,sectorNumber;
    ULONG                cylinder;
    ULONG                tmp;

    if(LunExt->DeviceFlags & DFLAGS_LBA_ENABLED) {
        return lba;
    }
    tmp = LunExt->IdentifyData.SectorsPerTrack *
                       LunExt->IdentifyData.NumberOfHeads;

    cylinder     = (USHORT)((lba >> 8) & 0xffff);
    drvSelect    = (UCHAR)((lba >> 24) & 0xf);
    sectorNumber = (UCHAR)(lba & 0xff);

    lba = sectorNumber-1 +
          (drvSelect*LunExt->IdentifyData.SectorsPerTrack) +
          (cylinder*tmp);

    return lba;
} // end UniAtaCalculateLBARegsBack()


/*++

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

--*/
ULONG
NTAPI
IdeReadWrite(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb,
    IN ULONG CmdAction
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    UCHAR                lChannel = GET_CHANNEL(Srb);
    PHW_CHANNEL          chan = &(deviceExtension->chan[lChannel]);
    PHW_LU_EXTENSION     LunExt;
    PATA_REQ             AtaReq = (PATA_REQ)(Srb->SrbExtension);
    ULONG                ldev = GET_LDEV(Srb);
    UCHAR                DeviceNumber = (UCHAR)(ldev & 1);
    ULONG                startingSector;
    ULONG                max_bcount;
    ULONG                wordCount = 0;
    UCHAR                statusByte,statusByte2;
    UCHAR                cmd;
    ULONGLONG            lba;
    BOOLEAN              use_dma = FALSE;

    AtaReq->Flags |= REQ_FLAG_REORDERABLE_CMD;
    LunExt = &deviceExtension->lun[ldev];

    if((CmdAction & CMD_ACTION_PREPARE) &&
       (AtaReq->ReqState != REQ_STATE_READY_TO_TRANSFER)) {

        if(LunExt->opt_ReadOnly && 
           (Srb->SrbFlags & SRB_FLAGS_DATA_OUT)) {
            if(LunExt->opt_ReadOnly == 1) {
                KdPrint2((PRINT_PREFIX "Abort WRITE (Soft R/O)\n"));
                return SRB_STATUS_ERROR;
            } else {
                KdPrint2((PRINT_PREFIX "Ignore WRITE (Soft R/O)\n"));
                return SRB_STATUS_SUCCESS;
            }
        }

        // Set data buffer pointer and words left.
        AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;

        if(AtaReq->WordsTransfered) {
            AtaReq->DataBuffer = ((PUSHORT)(Srb->DataBuffer)) + AtaReq->WordsTransfered;
            startingSector = (ULONG)(UniAtaCalculateLBARegsBack(LunExt, AtaReq->lba)) /* latest lba */ + AtaReq->bcount /* previous bcount */;
            AtaReq->bcount = (AtaReq->TransferLength - AtaReq->WordsTransfered*2 + DEV_BSIZE-1) / DEV_BSIZE;
            KdPrint2((PRINT_PREFIX "IdeReadWrite (Chained REQ): Starting sector %#x, OrigWordsRequested %#x, WordsTransfered %#x, DevSize %#x\n",
                       startingSector,
                       AtaReq->TransferLength/2,
                       AtaReq->WordsTransfered,
                       AtaReq->bcount));
        } else {
            AtaReq->DataBuffer = (PUSHORT)(Srb->DataBuffer);
            AtaReq->TransferLength = Srb->DataTransferLength;
            // Set up 1st block.
            MOV_DD_SWP(startingSector, ((PCDB)Srb->Cdb)->CDB10.LBA);
            MOV_SWP_DW2DD(AtaReq->bcount, ((PCDB)Srb->Cdb)->CDB10.TransferBlocks);
            KdPrint2((PRINT_PREFIX "IdeReadWrite (Orig REQ): Starting sector %#x, OrigWordsRequested %#x, DevSize %#x\n",
                       startingSector,
                       AtaReq->TransferLength/2,
                       AtaReq->bcount));
        }
        lba = UniAtaCalculateLBARegs(LunExt, startingSector, &max_bcount);

        if(max_bcount) {
            AtaReq->bcount = min(AtaReq->bcount, max_bcount);
        }
        AtaReq->WordsLeft = min(AtaReq->TransferLength - AtaReq->WordsTransfered*2,
                                AtaReq->bcount * DEV_BSIZE) / 2;

        KdPrint2((PRINT_PREFIX "IdeReadWrite (REQ): Starting sector is %#x, Number of WORDS %#x, DevSize %#x\n",
                   startingSector,
                   AtaReq->WordsLeft,
                   AtaReq->bcount));

        AtaReq->lba = lba;

        // assume best case here
        // we cannot reinit Dma until previous request is completed
        if ((LunExt->LimitedTransferMode >= ATA_DMA)) {
            use_dma = TRUE;
            // this will set REQ_FLAG_DMA_OPERATION in AtaReq->Flags on success
            if(!AtapiDmaSetup(HwDeviceExtension, DeviceNumber, lChannel, Srb,
                          (PUCHAR)(AtaReq->DataBuffer),
                          AtaReq->bcount * DEV_BSIZE)) {
                use_dma = FALSE;
            }
        }
        AtaReq->ReqState = REQ_STATE_READY_TO_TRANSFER;
    } else { // exec_only
        KdPrint2((PRINT_PREFIX "IdeReadWrite (ExecOnly): \n"));
        lba = AtaReq->lba;

        if(AtaReq->Flags & REQ_FLAG_DMA_OPERATION) {
            use_dma = TRUE;
        }
    }
    if(!(CmdAction & CMD_ACTION_EXEC)) {
        return SRB_STATUS_PENDING;
    }

    // if this is queued request, reinit DMA and check
    // if DMA mode is still available
    AtapiDmaReinit(deviceExtension, ldev, AtaReq);
    if (/*EnableDma &&*/
        (LunExt->TransferMode >= ATA_DMA)) {
        use_dma = TRUE;
    } else {
        AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
        use_dma = FALSE;
    }

    // Check if write request.
    if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {

        // Prepare read command.
        if(use_dma) {
            cmd = IDE_COMMAND_READ_DMA;
        } else
        if(LunExt->MaximumBlockXfer) {
            cmd = IDE_COMMAND_READ_MULTIPLE;
        } else {
            cmd = IDE_COMMAND_READ;
        }
    } else {

        // Prepare write command.
        if (use_dma) {
            wordCount = AtaReq->bcount*DEV_BSIZE/2;
            cmd = IDE_COMMAND_WRITE_DMA;
        } else
        if (LunExt->MaximumBlockXfer) {
            wordCount = DEV_BSIZE/2 * LunExt->MaximumBlockXfer;

            if (AtaReq->WordsLeft < wordCount) {
               // Transfer only words requested.
               wordCount = AtaReq->WordsLeft;
            }
            cmd = IDE_COMMAND_WRITE_MULTIPLE;

        } else {
            wordCount = DEV_BSIZE/2;
            cmd = IDE_COMMAND_WRITE;
        }
    }

    if(use_dma) {
        chan->ChannelCtrlFlags |= CTRFLAGS_DMA_OPERATION;
    } else {
        chan->ChannelCtrlFlags &= ~CTRFLAGS_DMA_OPERATION;
    }

    // Send IO command.
    KdPrint2((PRINT_PREFIX "IdeReadWrite: Lba %#I64x, Count %#x(%#x)\n", lba, ((Srb->DataTransferLength + 0x1FF) / 0x200),
                                                           ((wordCount*2 + DEV_BSIZE-1) / DEV_BSIZE)));

    if ((Srb->SrbFlags & SRB_FLAGS_DATA_IN) ||
        use_dma) {
        statusByte2 = AtaCommand48(deviceExtension, DeviceNumber, lChannel,
                     cmd, lba,
                     (USHORT)(AtaReq->bcount),
//                     (UCHAR)((wordCount*2 + DEV_BSIZE-1) / DEV_BSIZE),
                     0, ATA_IMMEDIATE);
        if(statusByte2 != 0xff) {
            GetStatus(chan, statusByte2);
        }
        if(statusByte2 & IDE_STATUS_ERROR) {
            statusByte = AtapiReadPort1(chan, IDX_IO1_i_Error);
            KdPrint2((PRINT_PREFIX "IdeReadWrite: status %#x, error %#x\n", statusByte2, statusByte));
            return SRB_STATUS_ERROR;
        }
        if(use_dma) {
            AtapiDmaStart(HwDeviceExtension, DeviceNumber, lChannel, Srb);
        }
        return SRB_STATUS_PENDING;
    }

    statusByte = AtaCommand48(deviceExtension, DeviceNumber, lChannel,
                 cmd, lba,
                 (USHORT)(AtaReq->bcount),
//                 (UCHAR)((wordCount*2 + DEV_BSIZE-1) / DEV_BSIZE),
                 0, ATA_WAIT_INTR);

    if (!(statusByte & IDE_STATUS_DRQ) ||
        statusByte == 0xff) {

        if(statusByte == 0xff) {
            KdPrint2((PRINT_PREFIX 
                       "IdeReadWrite: error sending command (%#x)\n",
                       statusByte));
        } else {
            KdPrint2((PRINT_PREFIX 
                       "IdeReadWrite: DRQ never asserted (%#x)\n",
                       statusByte));
        }

        AtaReq->WordsLeft = 0;

        // Clear interrupt expecting flag.
        chan->ExpectingInterrupt = FALSE;
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);

        // Clear current SRB.
        UniataRemoveRequest(chan, Srb);

        return (statusByte == 0xff) ? SRB_STATUS_ERROR : SRB_STATUS_TIMEOUT;
    }

    chan->ExpectingInterrupt = TRUE;
    InterlockedExchange(&(chan->CheckIntr),
                                  CHECK_INTR_IDLE);

    // Write next DEV_BSIZE/2*N words.
    if (!(LunExt->DeviceFlags & DFLAGS_DWORDIO_ENABLED)) {
        KdPrint2((PRINT_PREFIX 
                   "IdeReadWrite: Write %#x words\n", wordCount));

        WriteBuffer(chan,
                  AtaReq->DataBuffer,
                  wordCount,
                  UniataGetPioTiming(LunExt));

    } else {

        KdPrint2((PRINT_PREFIX 
                   "IdeReadWrite: Write %#x Dwords\n", wordCount/2));

        WriteBuffer2(chan,
                   (PULONG)(AtaReq->DataBuffer),
                   wordCount / 2,
                   UniataGetPioTiming(LunExt));
    }

    // Adjust buffer address and words left count.
    AtaReq->WordsLeft -= wordCount;
    AtaReq->DataBuffer += wordCount;

    // Wait for interrupt.
    return SRB_STATUS_PENDING;

} // end IdeReadWrite()

#ifndef UNIATA_CORE

/*++

Routine Description:
    This routine handles IDE Verify.

Arguments:
    HwDeviceExtension - HBA miniport driver's adapter data storage
    Srb - IO request packet
    `
Return Value:
    SRB status

--*/
ULONG
NTAPI
IdeVerify(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    UCHAR                lChannel = GET_CHANNEL(Srb);
    PHW_CHANNEL          chan = &(deviceExtension->chan[lChannel]);
    PATA_REQ             AtaReq = (PATA_REQ)(Srb->SrbExtension);
    PHW_LU_EXTENSION     LunExt;
    ULONG                ldev = GET_LDEV(Srb);
    UCHAR                statusByte;
    ULONG                startingSector;
    ULONG                max_bcount;
    ULONG                sectors;
    ULONG                endSector;
    USHORT               sectorCount;
    ULONGLONG            lba;

    LunExt = &deviceExtension->lun[ldev];
    // Drive has these number sectors.
    if(!(sectors = (ULONG)(LunExt->NumOfSectors))) {
        sectors = LunExt->IdentifyData.SectorsPerTrack *
                  LunExt->IdentifyData.NumberOfHeads *
                  LunExt->IdentifyData.NumberOfCylinders;
    }

    KdPrint2((PRINT_PREFIX 
                "IdeVerify: Total sectors %#x\n",
                sectors));

    // Get starting sector number from CDB.
    MOV_DD_SWP(startingSector, ((PCDB)Srb->Cdb)->CDB10.LBA);
    MOV_DW_SWP(sectorCount, ((PCDB)Srb->Cdb)->CDB10.TransferBlocks);

    KdPrint2((PRINT_PREFIX 
                "IdeVerify: Starting sector %#x. Number of blocks %#x\n",
                startingSector,
                sectorCount));

    endSector = startingSector + sectorCount;

    KdPrint2((PRINT_PREFIX 
                "IdeVerify: Ending sector %#x\n",
                endSector));

    if (endSector > sectors) {

        // Too big, round down.
        KdPrint2((PRINT_PREFIX 
                    "IdeVerify: Truncating request to %#x blocks\n",
                    sectors - startingSector - 1));

        sectorCount = (USHORT)(sectors - startingSector - 1);

    } else {

        // Set up sector count register. Round up to next block.
        if (sectorCount > 0xFF) {
            sectorCount = (USHORT)0xFF;
        }
    }

    // Set data buffer pointer and words left.
    AtaReq->DataBuffer = (PUSHORT)Srb->DataBuffer;
    AtaReq->WordsLeft = Srb->DataTransferLength / 2;

    // Indicate expecting an interrupt.
    InterlockedExchange(&(chan->CheckIntr),
                                  CHECK_INTR_IDLE);

    lba = UniAtaCalculateLBARegs(LunExt, startingSector, &max_bcount);

    statusByte = AtaCommand48(deviceExtension, ldev & 0x01, GET_CHANNEL(Srb),
                 IDE_COMMAND_VERIFY, lba,
                 sectorCount,
                 0, ATA_IMMEDIATE);

    if(!(statusByte & IDE_STATUS_ERROR)) {
        // Wait for interrupt.
        return SRB_STATUS_PENDING;
    }
    return SRB_STATUS_ERROR;

} // end IdeVerify()

#endif //UNIATA_CORE

/*++

Routine Description:
    Send ATAPI packet command to device.

Arguments:
    HwDeviceExtension - HBA miniport driver's adapter data storage
    Srb - IO request packet

Return Value:

--*/
ULONG
NTAPI
AtapiSendCommand(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb,
    IN ULONG CmdAction
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    UCHAR                lChannel = GET_CHANNEL(Srb);
    PHW_CHANNEL          chan = &(deviceExtension->chan[lChannel]);
    PATA_REQ             AtaReq = (PATA_REQ)(Srb->SrbExtension);
    ULONG                ldev = GET_LDEV(Srb);
    ULONG i;
    ULONG flags;
    UCHAR statusByte,byteCountLow,byteCountHigh;
    BOOLEAN use_dma = FALSE;
    BOOLEAN dma_reinited = FALSE;
    BOOLEAN retried = FALSE;

    KdPrint3((PRINT_PREFIX "AtapiSendCommand: req state %#x, Action %x\n", AtaReq->ReqState, CmdAction));
    if(AtaReq->ReqState < REQ_STATE_PREPARE_TO_TRANSFER)
        AtaReq->ReqState = REQ_STATE_PREPARE_TO_TRANSFER;

#ifdef UNIATA_DUMP_ATAPI
    if(CmdAction & CMD_ACTION_PREPARE) {
        UCHAR                   ScsiCommand;
        PCDB                    Cdb;
        PCHAR                   CdbData;
        PCHAR                   ModeSelectData;
        ULONG                   CdbDataLen;

        Cdb = (PCDB)(Srb->Cdb);
        ScsiCommand = Cdb->CDB6.OperationCode;
        CdbData = (PCHAR)(Srb->DataBuffer);
        CdbDataLen = Srb->DataTransferLength;

        if(CdbDataLen > 0x1000) {
            CdbDataLen = 0x1000;
        }

        KdPrint(("--\n"));
        KdPrint2(("VendorID+DeviceID/Rev %#x/%#x\n", deviceExtension->DevID, deviceExtension->RevID));
        KdPrint2(("P:T:D=%d:%d:%d\n",
                                  Srb->PathId,
                                  Srb->TargetId,
                                  Srb->Lun));
        KdPrint(("SCSI Command %2.2x\n", ScsiCommand));
        KdDump(Cdb, 16);

        if(ScsiCommand == SCSIOP_WRITE_CD) {
            KdPrint(("Write10, LBA %2.2x%2.2x%2.2x%2.2x\n",
                     Cdb->WRITE_CD.LBA[0],
                     Cdb->WRITE_CD.LBA[1],
                     Cdb->WRITE_CD.LBA[2],
                     Cdb->WRITE_CD.LBA[3]
                     ));
        } else
        if(ScsiCommand == SCSIOP_WRITE12) {
            KdPrint(("Write12, LBA %2.2x%2.2x%2.2x%2.2x\n",
                     Cdb->CDB12READWRITE.LBA[0],
                     Cdb->CDB12READWRITE.LBA[1],
                     Cdb->CDB12READWRITE.LBA[2],
                     Cdb->CDB12READWRITE.LBA[3]
                     ));
        } else
        if(ScsiCommand == SCSIOP_MODE_SELECT) {
            KdPrint(("ModeSelect 6\n"));
            PMODE_PARAMETER_HEADER ParamHdr = (PMODE_PARAMETER_HEADER)CdbData;
            ModeSelectData = CdbData+4;
            KdDump(CdbData, CdbDataLen);
        } else
        if(ScsiCommand == SCSIOP_MODE_SELECT10) {
            KdPrint(("ModeSelect 10\n"));
            PMODE_PARAMETER_HEADER ParamHdr = (PMODE_PARAMETER_HEADER)CdbData;
            ModeSelectData = CdbData+8;
            KdDump(CdbData, CdbDataLen);
        } else {
            if(Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
                KdPrint(("Send buffer to device:\n"));
                KdDump(CdbData, CdbDataLen);
            }
        }
        KdPrint(("--\n"));
    }
#endif //UNIATA_DUMP_ATAPI


    if(CmdAction == CMD_ACTION_PREPARE) {
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: CMD_ACTION_PREPARE\n"));
        switch (Srb->Cdb[0]) {
        case SCSIOP_READ:
        case SCSIOP_WRITE:
        case SCSIOP_READ12:
        case SCSIOP_WRITE12:
            // all right
            break;
        default:
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: SRB_STATUS_BUSY\n"));
            return SRB_STATUS_BUSY;
        }
        //
        if (!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_CHANGER_INITED) &&
            !AtaReq->OriginalSrb) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: SRB_STATUS_BUSY (2)\n"));
            return SRB_STATUS_BUSY;
        }
    }

    if((CmdAction & CMD_ACTION_PREPARE) &&
       (AtaReq->ReqState != REQ_STATE_READY_TO_TRANSFER)) {

        KdPrint2((PRINT_PREFIX "AtapiSendCommand: prepare..., ATAPI CMD %x\n", Srb->Cdb[0]));
        // Set data buffer pointer and words left.
        AtaReq->DataBuffer = (PUSHORT)Srb->DataBuffer;
        AtaReq->WordsLeft = Srb->DataTransferLength / 2;
        AtaReq->TransferLength = Srb->DataTransferLength;
        AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;

        // check if reorderable
        switch(Srb->Cdb[0]) {
        case SCSIOP_READ12:
        case SCSIOP_WRITE12:

            MOV_DD_SWP(AtaReq->bcount, ((PCDB)Srb->Cdb)->CDB12READWRITE.NumOfBlocks);
            goto GetLba;

        case SCSIOP_READ:
        case SCSIOP_WRITE:

            MOV_SWP_DW2DD(AtaReq->bcount, ((PCDB)Srb->Cdb)->CDB10.TransferBlocks);
GetLba:
            MOV_DD_SWP(AtaReq->lba, ((PCDB)Srb->Cdb)->CDB10.LBA);

            AtaReq->Flags |= REQ_FLAG_REORDERABLE_CMD;
            AtaReq->Flags &= ~REQ_FLAG_RW_MASK;
            AtaReq->Flags |= (Srb->Cdb[0] == SCSIOP_WRITE || Srb->Cdb[0] == SCSIOP_WRITE12) ?
                              REQ_FLAG_WRITE : REQ_FLAG_READ;
            break;
        }

        // check if DMA read/write
        if(Srb->Cdb[0] == SCSIOP_REQUEST_SENSE) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: SCSIOP_REQUEST_SENSE, no DMA setup\n"));
        } else
        if(AtaReq->TransferLength) {
            // try use DMA
            switch(Srb->Cdb[0]) {
            case SCSIOP_WRITE:
            case SCSIOP_WRITE12:
                if(chan->ChannelCtrlFlags & CTRFLAGS_DMA_RO)
                    break;
                /* FALLTHROUGH */
            case SCSIOP_READ:
            case SCSIOP_READ12:

                if(deviceExtension->opt_AtapiDmaReadWrite) {
call_dma_setup:
                    if(AtapiDmaSetup(HwDeviceExtension, ldev & 1, lChannel, Srb,
                                  (PUCHAR)(AtaReq->DataBuffer),
                                  Srb->DataTransferLength
                                  /*((Srb->DataTransferLength + DEV_BSIZE-1) & ~(DEV_BSIZE-1))*/
                                  )) {
                        KdPrint2((PRINT_PREFIX "AtapiSendCommand: use dma\n"));
                        use_dma = TRUE;
                    }
                }
                break;
            case SCSIOP_READ_CD:
                if(deviceExtension->opt_AtapiDmaRawRead)
                    goto call_dma_setup;
                break;
            default:

                if(deviceExtension->opt_AtapiDmaControlCmd) {
                    if(Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
                        // read operation
                        use_dma = TRUE;
                    } else {
                        // write operation
                        if(chan->ChannelCtrlFlags & CTRFLAGS_DMA_RO) {
                            KdPrint2((PRINT_PREFIX "dma RO\n"));
                            use_dma = FALSE;
                        } else {
                            use_dma = TRUE;
                        }
                    }
                }
                break;
            }
            // try setup DMA
            if(use_dma) {
                if(!AtapiDmaSetup(HwDeviceExtension, ldev & 1, lChannel, Srb,
                              (PUCHAR)(AtaReq->DataBuffer),
                              Srb->DataTransferLength)) {
                    KdPrint2((PRINT_PREFIX "AtapiSendCommand: no dma\n"));
                    use_dma = FALSE;
                } else {
                    KdPrint2((PRINT_PREFIX "AtapiSendCommand: use dma\n"));
                }
            }
        } else {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: zero transfer, no DMA setup\n"));
        }
    
    } else {
        if(AtaReq->Flags & REQ_FLAG_DMA_OPERATION) {
            // if this is queued request, reinit DMA and check
            // if DMA mode is still available
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: AtapiDmaReinit()  (1)\n"));
            AtapiDmaReinit(deviceExtension, ldev, AtaReq);
            if (/*EnableDma &&*/
                (deviceExtension->lun[ldev].TransferMode >= ATA_DMA)) {
                KdPrint2((PRINT_PREFIX "AtapiSendCommand: use dma (2)\n"));
                use_dma = TRUE;
            } else {
                AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
                KdPrint2((PRINT_PREFIX "AtapiSendCommand: no dma (2)\n"));
                use_dma = FALSE;
            }
            dma_reinited = TRUE;
        }
    }

    if(!(CmdAction & CMD_ACTION_EXEC)) {
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: !CMD_ACTION_EXEC => SRB_STATUS_PENDING\n"));
        return SRB_STATUS_PENDING;
    }
    KdPrint3((PRINT_PREFIX "AtapiSendCommand: use_dma=%d\n", use_dma));
    if(AtaReq->Flags & REQ_FLAG_DMA_OPERATION) {
        KdPrint2((PRINT_PREFIX "  REQ_FLAG_DMA_OPERATION\n"));
    }

    if(Srb->Cdb[0] == SCSIOP_REQUEST_SENSE) {
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: SCSIOP_REQUEST_SENSE -> no dma setup (2)\n"));
        use_dma = FALSE;
        AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
        //AtapiDmaReinit(deviceExtension, ldev, AtaReq);
    } if(AtaReq->TransferLength) {
        if(!dma_reinited) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: AtapiDmaReinit()\n"));
            AtapiDmaReinit(deviceExtension, ldev, AtaReq);
            if (/*EnableDma &&*/
                (deviceExtension->lun[ldev].TransferMode >= ATA_DMA)) {
                use_dma = TRUE;
            } else {
                AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
                use_dma = FALSE;
            }
        }
    } else {
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: zero transfer\n"));
        use_dma = FALSE;
        AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
        if(!deviceExtension->opt_AtapiDmaZeroTransfer) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: AtapiDmaReinit() to PIO\n"));
            AtapiDmaReinit(deviceExtension, ldev, AtaReq);
        }
    }
    KdPrint2((PRINT_PREFIX "AtapiSendCommand: use_dma=%d\n", use_dma));
    if(AtaReq->Flags & REQ_FLAG_DMA_OPERATION) {
        KdPrint2((PRINT_PREFIX "  REQ_FLAG_DMA_OPERATION\n"));
    }
    
    KdPrint2((PRINT_PREFIX "AtapiSendCommand: CMD_ACTION_EXEC\n"));

#ifndef UNIATA_CORE
    // We need to know how many platters our atapi cd-rom device might have.
    // Before anyone tries to send a srb to our target for the first time,
    // we must "secretly" send down a separate mechanism status srb in order to
    // initialize our device extension changer data.  That's how we know how
    // many platters our target has.

    if (!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_CHANGER_INITED) &&
        !AtaReq->OriginalSrb) {

        ULONG srbStatus;

        KdPrint3((PRINT_PREFIX "AtapiSendCommand: BuildMechanismStatusSrb()\n"));
        // Set this flag now. If the device hangs on the mech. status
        // command, we will not have the chance to set it.
        deviceExtension->lun[ldev].DeviceFlags |= DFLAGS_CHANGER_INITED;

        chan->MechStatusRetryCount = 3;
        AtaReq->OriginalSrb = Srb;
        AtaReq->Srb = BuildMechanismStatusSrb (
                                        HwDeviceExtension,
                                        Srb);

        KdPrint3((PRINT_PREFIX "AtapiSendCommand: AtapiSendCommand recursive\n"));
        srbStatus = AtapiSendCommand(HwDeviceExtension, AtaReq->Srb, CMD_ACTION_ALL);
        if (srbStatus == SRB_STATUS_PENDING) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: SRB_STATUS_PENDING (2)\n"));
            return srbStatus;
        } else {
            AtaReq->Srb = AtaReq->OriginalSrb;
            AtaReq->OriginalSrb = NULL;
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: AtapiHwInitializeChanger()\n"));
            AtapiHwInitializeChanger (HwDeviceExtension, Srb,
                                      (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL);
            // fall out
        }
    }
#endif //UNIATA_CORE

    KdPrint3((PRINT_PREFIX "AtapiSendCommand: Command %#x to TargetId %d lun %d\n",
               Srb->Cdb[0], Srb->TargetId, Srb->Lun));
    
    // Make sure command is to ATAPI device.
    flags = deviceExtension->lun[ldev].DeviceFlags;
    if(flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) {
        if((Srb->Lun) > (deviceExtension->lun[ldev].DiscsPresent - 1)) {

            // Indicate no device found at this address.
            AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
            return SRB_STATUS_SELECTION_TIMEOUT;
        }
    } else if(Srb->Lun > 0) {
        AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
        return SRB_STATUS_SELECTION_TIMEOUT;
    }

    if(!(flags & DFLAGS_ATAPI_DEVICE)) {
        AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
        return SRB_STATUS_SELECTION_TIMEOUT;
    }
retry:
    // Select device 0 or 1.
    SelectDrive(chan, ldev & 0x1);

    // Verify that controller is ready for next command.
    GetStatus(chan, statusByte);
    KdPrint3((PRINT_PREFIX "AtapiSendCommand: Entered with status %#x\n", statusByte));

    if(statusByte == 0xff) {
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: bad status 0xff on entry\n"));
        goto make_reset;
    }
    if(statusByte & IDE_STATUS_BUSY) {
        if(statusByte & IDE_STATUS_DSC) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: DSC on entry (%#x), try exec\n", statusByte));
        } else {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: Device busy (%#x) -> reset\n", statusByte));
            // We have to make reset here, since we are expecting device to be available
            //return SRB_STATUS_BUSY; // this cause queue freeze
            goto make_reset;
        }
    }
    if(statusByte & IDE_STATUS_ERROR) {
        if (Srb->Cdb[0] != SCSIOP_REQUEST_SENSE) {

            KdPrint3((PRINT_PREFIX "AtapiSendCommand: Error on entry: (%#x)\n", statusByte));
            // Read the error reg. to clear it and fail this request.
            AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
            return MapError(deviceExtension, Srb);
        } else {
            KdPrint2((PRINT_PREFIX "  continue with SCSIOP_REQUEST_SENSE\n", statusByte));
        }
    }
    // If a tape drive doesn't have DSC set and the last command is restrictive, don't send
    // the next command. See discussion of Restrictive Delayed Process commands in QIC-157.
    if((!(statusByte & IDE_STATUS_DSC)) &&
          (flags & (DFLAGS_TAPE_DEVICE | DFLAGS_ATAPI_DEVICE)) && chan->RDP) {

        AtapiStallExecution(200);
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: DSC not set. %#x => SRB_STATUS_PENDING\n",statusByte));
        AtaReq->ReqState = REQ_STATE_QUEUED;
        return SRB_STATUS_PENDING;
    }

    if(IS_RDP(Srb->Cdb[0])) {
        chan->RDP = TRUE;
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: %#x mapped as DSC restrictive\n", Srb->Cdb[0]));
    } else {
        chan->RDP = FALSE;
    }
    if(statusByte & IDE_STATUS_DRQ) {

        KdPrint3((PRINT_PREFIX "AtapiSendCommand: Entered with status (%#x). Attempting to recover.\n",
                    statusByte));
        // Try to drain the data that one preliminary device thinks that it has
        // to transfer. Hopefully this random assertion of DRQ will not be present
        // in production devices.
        for (i = 0; i < 0x10000; i++) {
            GetStatus(chan, statusByte);
            if(statusByte & IDE_STATUS_DRQ) {
                AtapiReadPort2(chan, IDX_IO1_i_Data);
            } else {
                break;
            }
        }

        if (i == 0x10000) {
make_reset:
            KdPrint3((PRINT_PREFIX "AtapiSendCommand: DRQ still asserted.Status (%#x)\n", statusByte));

            AtapiDisableInterrupts(deviceExtension, lChannel);

            AtapiSoftReset(chan, ldev & 1);

            KdPrint2((PRINT_PREFIX "AtapiSendCommand: Issued soft reset to Atapi device. \n"));
            // Re-initialize Atapi device.
            CheckDevice(HwDeviceExtension, GET_CHANNEL(Srb), ldev & 1, TRUE);
/*
            IssueIdentify(HwDeviceExtension, ldev & 1, GET_CHANNEL(Srb),
                          IDE_COMMAND_ATAPI_IDENTIFY, FALSE);
*/
            // Inform the port driver that the bus has been reset.
            ScsiPortNotification(ResetDetected, HwDeviceExtension, 0);
            // Clean up device extension fields that AtapiStartIo won't.
            chan->ExpectingInterrupt = FALSE;
            chan->RDP = FALSE;
            InterlockedExchange(&(deviceExtension->chan[GET_CHANNEL(Srb)].CheckIntr),
                                          CHECK_INTR_IDLE);

            AtapiEnableInterrupts(deviceExtension, lChannel);
/*
            AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
            return SRB_STATUS_BUS_RESET;
*/
            if(!retried) {
                KdPrint3((PRINT_PREFIX "AtapiSendCommand: retry after reset.\n"));
                retried = TRUE;
                goto retry;
            }
            KdPrint3((PRINT_PREFIX "AtapiSendCommand: selection timeout.\n"));
            AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
            return SRB_STATUS_SELECTION_TIMEOUT;
        }
    }

    if(flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) {
        // As the cdrom driver sets the LUN field in the cdb, it must be removed.
        Srb->Cdb[1] &= ~0xE0;
        if((Srb->Cdb[0] == SCSIOP_TEST_UNIT_READY) && (flags & DFLAGS_SANYO_ATAPI_CHANGER)) {
            // Torisan changer. TUR's are overloaded to be platter switches.
            Srb->Cdb[7] = Srb->Lun;
        }
    }

    // SETUP DMA !!!!!

    if(use_dma) {
        chan->ChannelCtrlFlags |= CTRFLAGS_DMA_OPERATION;
    } else {
        chan->ChannelCtrlFlags &= ~CTRFLAGS_DMA_OPERATION;
    }

    statusByte = WaitOnBusy(chan);
    KdPrint3((PRINT_PREFIX "AtapiSendCommand: Entry Status (%#x)\n",
               statusByte));

    AtapiWritePort1(chan, IDX_IO1_o_Feature,
                            use_dma ? ATA_F_DMA : 0);

    // Write transfer byte count to registers.
    byteCountLow = (UCHAR)(Srb->DataTransferLength & 0xFF);
    byteCountHigh = (UCHAR)(Srb->DataTransferLength >> 8);

    if (Srb->DataTransferLength >= 0x10000) {
        byteCountLow = byteCountHigh = 0xFF;
    }

    AtapiWritePort1(chan, IDX_ATAPI_IO1_o_ByteCountLow, byteCountLow);
    AtapiWritePort1(chan, IDX_ATAPI_IO1_o_ByteCountHigh, byteCountHigh);

    if (flags & DFLAGS_INT_DRQ) {

        // This device interrupts when ready to receive the packet.

        KdPrint3((PRINT_PREFIX "AtapiSendCommand: Wait for int. to send packet. Status (%#x)\n",
                   statusByte));

        chan->ExpectingInterrupt = TRUE;
        AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_CMD_INTR;
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);

        // Write ATAPI packet command.
        AtapiWritePort1(chan, IDX_IO1_o_Command, IDE_COMMAND_ATAPI_PACKET);

        KdPrint3((PRINT_PREFIX "AtapiSendCommand: return SRB_STATUS_PENDING (DRQ)\n"));
        return SRB_STATUS_PENDING;

    }

    // This device quickly sets DRQ when ready to receive the packet.

    KdPrint2((PRINT_PREFIX "AtapiSendCommand: Poll for int. to send packet. Status (%#x)\n",
               statusByte));

    chan->ExpectingInterrupt = TRUE;
    AtaReq->ReqState = REQ_STATE_ATAPI_DO_NOTHING_INTR;
    InterlockedExchange(&(chan->CheckIntr),
                                  CHECK_INTR_IDLE);

    if(g_opt_AtapiSendDisableIntr) {
        AtapiDisableInterrupts(deviceExtension, lChannel);
    }

    // Write ATAPI packet command.
    AtapiWritePort1(chan, IDX_IO1_o_Command, IDE_COMMAND_ATAPI_PACKET);

    // Wait for DRQ.
    WaitOnBusy(chan);
    statusByte = WaitForDrq(chan);

    // Need to read status register and clear interrupt (if any)
    GetBaseStatus(chan, statusByte);

    if (!(statusByte & IDE_STATUS_DRQ)) {

        if(g_opt_AtapiSendDisableIntr) {
            AtapiEnableInterrupts(deviceExtension, lChannel);
        }
        KdPrint3((PRINT_PREFIX "AtapiSendCommand: DRQ never asserted (%#x)\n", statusByte));
        AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
        return SRB_STATUS_ERROR;
    }

    GetStatus(chan, statusByte);
    KdPrint3((PRINT_PREFIX "AtapiSendCommand: status (%#x)\n", statusByte));

    // Send CDB to device.
    statusByte = WaitOnBaseBusy(chan);

    // Indicate expecting an interrupt and wait for it.
    chan->ExpectingInterrupt = TRUE;
    InterlockedExchange(&(chan->CheckIntr),
                                  CHECK_INTR_IDLE);
    AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_DATA_INTR;

    GetBaseStatus(chan, statusByte);

    if(g_opt_AtapiSendDisableIntr) {
        AtapiEnableInterrupts(deviceExtension, lChannel);
    }

    WriteBuffer(chan,
                (PUSHORT)Srb->Cdb,
                6,
                0);

    if(chan->ChannelCtrlFlags & CTRFLAGS_DMA_OPERATION) {
        AtapiDmaStart(HwDeviceExtension, ldev & 1, lChannel, Srb);
    }

    KdPrint3((PRINT_PREFIX "AtapiSendCommand: ExpectingInterrupt (%#x)\n", chan->ExpectingInterrupt));

    KdPrint2((PRINT_PREFIX "AtapiSendCommand: return SRB_STATUS_PENDING (3)\n"));
    return SRB_STATUS_PENDING;

} // end AtapiSendCommand()


#ifndef UNIATA_CORE

/*++

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).

--*/

#ifdef _DEBUG
ULONG check_point = 0;
#define SetCheckPoint(cp)  { check_point = (cp) ; }
#else
#define SetCheckPoint(cp)
#endif

ULONG
NTAPI
IdeSendCommand(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb,
    IN ULONG CmdAction
    )
{
    SetCheckPoint(1);
    KdPrint2((PRINT_PREFIX "** Ide: Command: entryway\n"));
    SetCheckPoint(2);

    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    SetCheckPoint(3);
    UCHAR                lChannel;
    PHW_CHANNEL          chan;
    PCDB cdb;

    SetCheckPoint(4);

    UCHAR statusByte,errorByte;
    ULONG status;
    ULONG i;
    PMODE_PARAMETER_HEADER   modeData;
    ULONG ldev;
    PATA_REQ AtaReq;
    SetCheckPoint(5);
    //ULONG __ebp__ = 0;

    SetCheckPoint(0x20);
    KdPrint2((PRINT_PREFIX "** Ide: Command:\n\n"));
/*    __asm {
        mov eax,ebp
        mov __ebp__, eax
    }*/
    /*KdPrint2((PRINT_PREFIX "** Ide: Command EBP %#x, pCdb %#x, cmd %#x\n",
        __ebp__, &(Srb->Cdb[0]), Srb->Cdb[0]));
    KdPrint2((PRINT_PREFIX "** Ide: Command %s\n",
        (CmdAction == CMD_ACTION_PREPARE) ? "Prep " : ""));
    KdPrint2((PRINT_PREFIX "** Ide: Command Srb %#x\n",
        Srb));
    KdPrint2((PRINT_PREFIX "** Ide: Command SrbExt %#x\n",
        Srb->SrbExtension));
    KdPrint2((PRINT_PREFIX "** Ide: Command to device %d\n",
        Srb->TargetId));*/

    SetCheckPoint(0x30);
    AtaReq = (PATA_REQ)(Srb->SrbExtension);

    KdPrint2((PRINT_PREFIX "** Ide: Command &AtaReq %#x\n",
        &AtaReq));
    KdPrint2((PRINT_PREFIX "** Ide: Command AtaReq %#x\n",
        AtaReq));
    KdPrint2((PRINT_PREFIX "** --- **\n"));

    lChannel = GET_CHANNEL(Srb);
    chan = &(deviceExtension->chan[lChannel]);
    ldev = GET_LDEV(Srb);

    SetCheckPoint(0x40);
    if(AtaReq->ReqState < REQ_STATE_PREPARE_TO_TRANSFER)
        AtaReq->ReqState = REQ_STATE_PREPARE_TO_TRANSFER;

    if(CmdAction == CMD_ACTION_PREPARE) {
        switch (Srb->Cdb[0]) {
#ifdef NAVO_TEST
        case SCSIOP_INQUIRY: // now it requires device access
#endif //NAVO_TEST
        case SCSIOP_READ_CAPACITY:
        case SCSIOP_READ:
        case SCSIOP_WRITE:
        case SCSIOP_REQUEST_SENSE:
            // all right
            KdPrint2((PRINT_PREFIX "** Ide: Command continue prep\n"));
            SetCheckPoint(50);
            break;
        default:
            SetCheckPoint(0);
            KdPrint2((PRINT_PREFIX "** Ide: Command break prep\n"));
            return SRB_STATUS_BUSY;
        }
    }

    SetCheckPoint(0x100 | Srb->Cdb[0]);
    switch (Srb->Cdb[0]) {
    case SCSIOP_INQUIRY:

        KdPrint2((PRINT_PREFIX 
                   "IdeSendCommand: SCSIOP_INQUIRY PATH:LUN:TID = %#x:%#x:%#x\n",
                   Srb->PathId, Srb->Lun, Srb->TargetId));
        // Filter out all TIDs but 0 and 1 since this is an IDE interface
        // which support up to two devices.
        if ((Srb->Lun != 0) ||
            (Srb->PathId >= deviceExtension->NumberChannels) ||
            (Srb->TargetId > 2) /*||
            (!deviceExtension->lun[ldev].DeviceFlags & DFLAGS_DEVICE_PRESENT)*/) {

            KdPrint2((PRINT_PREFIX 
                       "IdeSendCommand: SCSIOP_INQUIRY rejected\n"));
            // Indicate no device found at this address.
            status = SRB_STATUS_SELECTION_TIMEOUT;
            break;

        } else {

            KdPrint2((PRINT_PREFIX 
                       "IdeSendCommand: SCSIOP_INQUIRY ok\n"));
            PINQUIRYDATA    inquiryData  = (PINQUIRYDATA)(Srb->DataBuffer);
            PIDENTIFY_DATA2 identifyData = &(deviceExtension->lun[ldev].IdentifyData);

            if (!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                
                if(!CheckDevice(HwDeviceExtension, lChannel, ldev & 1, FALSE)) {
                    KdPrint2((PRINT_PREFIX 
                               "IdeSendCommand: SCSIOP_INQUIRY rejected (2)\n"));
                    // Indicate no device found at this address.
#ifndef NAVO_TEST
                    status = SRB_STATUS_SELECTION_TIMEOUT;
                    break;
                }
            } else {
                if(!UniataAnybodyHome(HwDeviceExtension, lChannel, ldev & 1)) {
                    KdPrint2((PRINT_PREFIX 
                               "IdeSendCommand: SCSIOP_INQUIRY device have gone\n"));
                    // Indicate no device found at this address.
                    UniataForgetDevice(&(deviceExtension->lun[ldev]));
#endif //NAVO_TEST
                    status = SRB_STATUS_SELECTION_TIMEOUT;
                    break;
                }
            }

            // Zero INQUIRY data structure.
            RtlZeroMemory((PCHAR)(Srb->DataBuffer), Srb->DataTransferLength);

            // Standard IDE interface only supports disks.
            inquiryData->DeviceType = DIRECT_ACCESS_DEVICE;

            // Set the removable bit, if applicable.
            if (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_REMOVABLE_DRIVE) {
                KdPrint2((PRINT_PREFIX 
                           "RemovableMedia\n"));
                inquiryData->RemovableMedia = 1;
            }
            // Set the Relative Addressing (LBA) bit, if applicable.
            if (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_LBA_ENABLED) {
                inquiryData->RelativeAddressing = 1;
                KdPrint2((PRINT_PREFIX 
                           "RelativeAddressing\n"));
            }
            // Set the CommandQueue bit
            inquiryData->CommandQueue = 1;

            // Fill in vendor identification fields.
            for (i = 0; i < 24; i += 2) {
                MOV_DW_SWP(inquiryData->VendorId[i], ((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) {
                MOV_DW_SWP(inquiryData->ProductRevisionLevel[i], ((PUCHAR)identifyData->FirmwareRevision)[i]);
            }

            status = SRB_STATUS_SUCCESS;
        }

        break;

    case SCSIOP_MODE_SENSE:

        KdPrint2((PRINT_PREFIX 
                   "IdeSendCommand: SCSIOP_MODE_SENSE PATH:LUN:TID = %#x:%#x:%#x\n",
                   Srb->PathId, Srb->Lun, Srb->TargetId));
        // This is used to determine if the media is write-protected.
        // Since IDE does not support mode sense then we will modify just the portion we need
        // so the higher level driver can determine if media is protected.
        if (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED) {

            SelectDrive(chan, ldev & 0x1);
            AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_GET_MEDIA_STATUS);
            statusByte = WaitOnBusy(chan);

            if (!(statusByte & IDE_STATUS_ERROR)){

                // no error occured return success, media is not protected
                chan->ExpectingInterrupt = FALSE;
                InterlockedExchange(&(chan->CheckIntr),
                                              CHECK_INTR_IDLE);
                status = SRB_STATUS_SUCCESS;

            } else {

                // error occured, handle it locally, clear interrupt
                errorByte = AtapiReadPort1(chan, IDX_IO1_i_Error);

                GetBaseStatus(chan, statusByte);
                chan->ExpectingInterrupt = FALSE;
                InterlockedExchange(&(chan->CheckIntr),
                                              CHECK_INTR_IDLE);
                status = SRB_STATUS_SUCCESS;

                if (errorByte & IDE_ERROR_DATA_ERROR) {

                    //media is write-protected, set bit in mode sense buffer
                    modeData = (PMODE_PARAMETER_HEADER)Srb->DataBuffer;

                    Srb->DataTransferLength = sizeof(MODE_PARAMETER_HEADER);
                    modeData->DeviceSpecificParameter |= MODE_DSP_WRITE_PROTECT;
                }
            }
            status = SRB_STATUS_SUCCESS;
        } else {
            status = SRB_STATUS_INVALID_REQUEST;
        }
        break;

    case SCSIOP_TEST_UNIT_READY:

        KdPrint2((PRINT_PREFIX 
                   "IdeSendCommand: SCSIOP_TEST_UNIT_READY PATH:LUN:TID = %#x:%#x:%#x\n",
                   Srb->PathId, Srb->Lun, Srb->TargetId));
        if (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED) {

            // Select device 0 or 1.
            SelectDrive(chan, ldev & 0x1);
            AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_GET_MEDIA_STATUS);

            // Wait for busy. If media has not changed, return success
            statusByte = WaitOnBusy(chan);

            if (!(statusByte & IDE_STATUS_ERROR)){
                chan->ExpectingInterrupt = FALSE;
                InterlockedExchange(&(chan->CheckIntr),
                                              CHECK_INTR_IDLE);
                status = SRB_STATUS_SUCCESS;
            } else {
                errorByte = AtapiReadPort1(chan, IDX_IO1_i_Error);
                if (errorByte == IDE_ERROR_DATA_ERROR){

                    // Special case: If current media is write-protected,
                    // the 0xDA command will always fail since the write-protect bit
                    // is sticky,so we can ignore this error
                    GetBaseStatus(chan, statusByte);
                    chan->ExpectingInterrupt = FALSE;
                    InterlockedExchange(&(chan->CheckIntr),
                                                  CHECK_INTR_IDLE);
                    status = SRB_STATUS_SUCCESS;

                } else {

                    // Request sense buffer to be build
                    chan->ExpectingInterrupt = TRUE;
                    InterlockedExchange(&(chan->CheckIntr),
                                                  CHECK_INTR_IDLE);
                    status = SRB_STATUS_PENDING;
               }
            }
        } else {
            status = SRB_STATUS_SUCCESS;
        }

        break;

    case SCSIOP_READ_CAPACITY:

        KdPrint2((PRINT_PREFIX 
                   "** IdeSendCommand: SCSIOP_READ_CAPACITY PATH:LUN:TID = %#x:%#x:%#x\n",
                   Srb->PathId, Srb->Lun, Srb->TargetId));
        // Claim 512 byte blocks (big-endian).
        //((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock = 0x20000;
        i = DEV_BSIZE;
        MOV_DD_SWP( ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock, i );

        // Calculate last sector.
        if(!(i = (ULONG)deviceExtension->lun[ldev].NumOfSectors)) {
            i = deviceExtension->lun[ldev].IdentifyData.SectorsPerTrack *
                deviceExtension->lun[ldev].IdentifyData.NumberOfHeads *
                deviceExtension->lun[ldev].IdentifyData.NumberOfCylinders;
        }
        i--;

        //((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress =
        //    (((PUCHAR)&i)[0] << 24) |  (((PUCHAR)&i)[1] << 16) |
        //    (((PUCHAR)&i)[2] << 8) | ((PUCHAR)&i)[3];

        MOV_DD_SWP( ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress, i );

        KdPrint2((PRINT_PREFIX 
                   "** IDE disk %#x - #sectors %#x, #heads %#x, #cylinders %#x\n",
                   Srb->TargetId,
                   deviceExtension->lun[ldev].IdentifyData.SectorsPerTrack,
                   deviceExtension->lun[ldev].IdentifyData.NumberOfHeads,
                   deviceExtension->lun[ldev].IdentifyData.NumberOfCylinders));


        status = SRB_STATUS_SUCCESS;
        break;

    case SCSIOP_VERIFY:

        KdPrint2((PRINT_PREFIX 
                   "IdeSendCommand: SCSIOP_VERIFY PATH:LUN:TID = %#x:%#x:%#x\n",
                   Srb->PathId, Srb->Lun, Srb->TargetId));
        status = IdeVerify(HwDeviceExtension,Srb);

        break;

    case SCSIOP_READ:
    case SCSIOP_WRITE:

        KdPrint2((PRINT_PREFIX 
                   "IdeSendCommand: SCSIOP_%s PATH:LUN:TID = %#x:%#x:%#x\n",
                   (Srb->Cdb[0] == SCSIOP_WRITE) ? "WRITE" : "READ",
                   Srb->PathId, Srb->Lun, Srb->TargetId));
        AtaReq->Flags &= ~REQ_FLAG_RW_MASK;
        AtaReq->Flags |= (Srb->Cdb[0] == SCSIOP_WRITE) ? REQ_FLAG_WRITE : REQ_FLAG_READ;
        status = IdeReadWrite(HwDeviceExtension,
                              Srb, CmdAction);
        break;

    case SCSIOP_START_STOP_UNIT:

        KdPrint2((PRINT_PREFIX 
                   "IdeSendCommand: SCSIOP_START_STOP_UNIT PATH:LUN:TID = %#x:%#x:%#x\n",
                   Srb->PathId, Srb->Lun, Srb->TargetId));
        //Determine what type of operation we should perform
        cdb = (PCDB)Srb->Cdb;

        if (cdb->START_STOP.LoadEject == 1){

            statusByte = WaitOnBaseBusy(chan);
            // Eject media,
            // first select device 0 or 1.
            SelectDrive(chan, ldev & 0x1);
            AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_MEDIA_EJECT);
        }
        status = SRB_STATUS_SUCCESS;
        break;

    case SCSIOP_MEDIUM_REMOVAL:

       cdb = (PCDB)Srb->Cdb;

       statusByte = WaitOnBaseBusy(chan);

       SelectDrive(chan, ldev & 0x1);
       if (cdb->MEDIA_REMOVAL.Prevent == TRUE) {
           AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_DOOR_LOCK);
       } else {
           AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_DOOR_UNLOCK);
       }
       status = SRB_STATUS_SUCCESS;
       break;

    // Note: I don't implement this, because NTFS driver too often issues this command
    // It causes awful performance degrade. However, if somebody wants, I will implement
    // SCSIOP_FLUSH_BUFFER/SCSIOP_SYNCHRONIZE_CACHE optionally.

#if 0
    case SCSIOP_FLUSH_BUFFER:
    case SCSIOP_SYNCHRONIZE_CACHE:

        SelectDrive(chan, ldev & 0x1);
        AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_FLUSH_CACHE);
        status = SRB_STATUS_SUCCESS;
//        status = SRB_STATUS_PENDING;
        statusByte = WaitOnBusy(chan);
        break;
#endif

    case SCSIOP_REQUEST_SENSE:
        // this function makes sense buffers to report the results
        // of the original GET_MEDIA_STATUS command

        KdPrint2((PRINT_PREFIX 
                   "IdeSendCommand: SCSIOP_REQUEST_SENSE PATH:LUN:TID = %#x:%#x:%#x\n",
                   Srb->PathId, Srb->Lun, Srb->TargetId));
        if (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED) {
            status = IdeBuildSenseBuffer(HwDeviceExtension,Srb);
            break;
        }
        status = SRB_STATUS_INVALID_REQUEST;
        break;

    // ATA_PASSTHORUGH
    case SCSIOP_ATA_PASSTHROUGH:
    {
        PIDEREGS_EX regs;
        BOOLEAN use_dma = FALSE;
        ULONG to_lim;
        
        regs = (PIDEREGS_EX) &(Srb->Cdb[2]);

        lChannel = Srb->TargetId >> 1;

        regs->bDriveHeadReg &= 0x0f;
        regs->bDriveHeadReg |= (UCHAR) (((Srb->TargetId & 0x1) << 4) | 0xA0);

        if((regs->bOpFlags & 1) == 0) {      // execute ATA command

            KdPrint2((PRINT_PREFIX 
                       "IdeSendCommand: SCSIOP_START_STOP_UNIT PATH:LUN:TID = %#x:%#x:%#x\n",
                       Srb->PathId, Srb->Lun, Srb->TargetId));


            AtapiDisableInterrupts(deviceExtension, lChannel);

            if((AtaCommandFlags[regs->bCommandReg] & ATA_CMD_FLAG_DMA) || (regs->bOpFlags & UNIATA_SPTI_EX_USE_DMA)) {
                if((chan->lun[Srb->TargetId & 0x1]->LimitedTransferMode >= ATA_DMA)) {
                    use_dma = TRUE;
                    // this will set REQ_FLAG_DMA_OPERATION in AtaReq->Flags on success
                    if(!AtapiDmaSetup(HwDeviceExtension, Srb->TargetId & 0x1, lChannel, Srb,
                                  (PUCHAR)(Srb->DataBuffer),
                                  ((Srb->DataTransferLength + DEV_BSIZE-1) & ~(DEV_BSIZE-1)))) {
                        use_dma = FALSE;
                    }
                }
            }

            AtapiWritePort1(chan, IDX_IO1_o_DriveSelect,  regs->bDriveHeadReg);
            AtapiStallExecution(10);

            if((regs->bOpFlags & ATA_FLAGS_48BIT_COMMAND) == 0) {      // execute ATA command
                AtapiWritePort1(chan, IDX_IO1_o_Feature,      regs->bFeaturesReg);
                AtapiWritePort1(chan, IDX_IO1_o_BlockCount,   regs->bSectorCountReg);
                AtapiWritePort1(chan, IDX_IO1_o_BlockNumber,  regs->bSectorNumberReg);
                AtapiWritePort1(chan, IDX_IO1_o_CylinderLow,  regs->bCylLowReg);
                AtapiWritePort1(chan, IDX_IO1_o_CylinderHigh, regs->bCylHighReg);
            } else {
                AtapiWritePort1(chan, IDX_IO1_o_Feature,      regs->bFeaturesRegH);
                AtapiWritePort1(chan, IDX_IO1_o_Feature,      regs->bFeaturesReg);
                AtapiWritePort1(chan, IDX_IO1_o_BlockCount,   regs->bSectorCountRegH);
                AtapiWritePort1(chan, IDX_IO1_o_BlockCount,   regs->bSectorCountReg);
                AtapiWritePort1(chan, IDX_IO1_o_BlockNumber,  regs->bSectorNumberRegH);
                AtapiWritePort1(chan, IDX_IO1_o_BlockNumber,  regs->bSectorNumberReg);
                AtapiWritePort1(chan, IDX_IO1_o_CylinderLow,  regs->bCylLowRegH);
                AtapiWritePort1(chan, IDX_IO1_o_CylinderLow,  regs->bCylLowReg);
                AtapiWritePort1(chan, IDX_IO1_o_CylinderHigh, regs->bCylHighRegH);
                AtapiWritePort1(chan, IDX_IO1_o_CylinderHigh, regs->bCylHighReg);
            }
            AtapiWritePort1(chan, IDX_IO1_o_Command,      regs->bCommandReg);
            
            if(use_dma) {
                GetBaseStatus(chan, statusByte);
                if(statusByte & IDE_STATUS_ERROR) {
                    goto passthrough_err;
                }
                AtapiDmaStart(HwDeviceExtension, (Srb->TargetId & 0x1), lChannel, Srb);
            }

            ScsiPortStallExecution(1);                  // wait for busy to be set

            if(regs->bOpFlags & UNIATA_SPTI_EX_SPEC_TO) {
                to_lim = Srb->TimeOutValue;
            } else {
                if(Srb->TimeOutValue <= 2) {
                    to_lim = Srb->TimeOutValue*900;
                } else {
                    to_lim = (Srb->TimeOutValue*999) - 500;
                }
            }
            for(i=0; i<to_lim;i+=2) {      // 2 msec from WaitOnBaseBusy()
                statusByte = WaitOnBaseBusy(chan);      // wait for busy to be clear, up to 2 msec
                GetBaseStatus(chan, statusByte);
                if(statusByte & IDE_STATUS_ERROR) {
                    break;
                }
                if(!(statusByte & IDE_STATUS_BUSY)) {
                    break;
                }
            }
            if(i >= to_lim) {
                //if(regs->bOpFlags & UNIATA_SPTI_EX_FREEZE_TO) {
                //}
                AtapiResetController__(HwDeviceExtension, lChannel, RESET_COMPLETE_NONE);
                goto passthrough_err;
            }

            if(use_dma) {
                AtapiCheckInterrupt__(deviceExtension, (UCHAR)lChannel);
            }
            AtapiDmaDone(deviceExtension, (Srb->TargetId & 0x1), lChannel, NULL);
            GetBaseStatus(chan, statusByte);

            if(statusByte & (IDE_STATUS_BUSY | IDE_STATUS_ERROR)) {
                AtapiSuckPort2(chan);
passthrough_err:
                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;

                    Srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID;
                    Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
                }
                status = SRB_STATUS_ERROR;
            } else {

                if(!use_dma) {
                    if (statusByte & IDE_STATUS_DRQ) {
                        if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
                            ReadBuffer(chan,
                                       (PUSHORT) Srb->DataBuffer,
                                       Srb->DataTransferLength / 2,
                                       0);
                        } else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
                            WriteBuffer(chan,
                                        (PUSHORT) Srb->DataBuffer,
                                        Srb->DataTransferLength / 2,
                                        0);
                        }
                    }
                }
                status = SRB_STATUS_SUCCESS;
            }

            AtapiEnableInterrupts(deviceExtension, lChannel);

        } else { // read task register

            regs = (PIDEREGS_EX) Srb->DataBuffer;

            regs->bDriveHeadReg    = AtapiReadPort1(chan, IDX_IO1_i_DriveSelect);

            if((regs->bOpFlags & ATA_FLAGS_48BIT_COMMAND) == 0) {      // execute ATA command
                regs->bFeaturesReg     = AtapiReadPort1(chan, IDX_IO1_i_Error);
                regs->bSectorCountReg  = AtapiReadPort1(chan, IDX_IO1_i_BlockCount);
                regs->bSectorNumberReg = AtapiReadPort1(chan, IDX_IO1_i_BlockNumber);
                regs->bCylLowReg       = AtapiReadPort1(chan, IDX_IO1_i_CylinderLow);
                regs->bCylHighReg      = AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh);
            } else {
                regs->bFeaturesReg     = AtapiReadPort1(chan, IDX_IO1_i_Error);
                regs->bFeaturesRegH    = AtapiReadPort1(chan, IDX_IO1_i_Error);
                regs->bSectorCountReg  = AtapiReadPort1(chan, IDX_IO1_i_BlockCount);
                regs->bSectorCountRegH = AtapiReadPort1(chan, IDX_IO1_i_BlockCount);
                regs->bSectorNumberReg = AtapiReadPort1(chan, IDX_IO1_i_BlockNumber);
                regs->bSectorNumberRegH= AtapiReadPort1(chan, IDX_IO1_i_BlockNumber);
                regs->bCylLowReg       = AtapiReadPort1(chan, IDX_IO1_i_CylinderLow);
                regs->bCylLowRegH      = AtapiReadPort1(chan, IDX_IO1_i_CylinderLow);
                regs->bCylHighReg      = AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh);
                regs->bCylHighRegH     = AtapiReadPort1(chan, IDX_IO1_i_CylinderHigh);
            }
            regs->bCommandReg      = AtapiReadPort1(chan, IDX_IO1_i_Status);
            status = SRB_STATUS_SUCCESS;
        }
        break;
    }

    default:

        KdPrint2((PRINT_PREFIX 
                   "IdeSendCommand: Unsupported command %#x\n",
                   Srb->Cdb[0]));

        status = SRB_STATUS_INVALID_REQUEST;

    } // end switch

    if(status == SRB_STATUS_PENDING) {
        KdPrint2((PRINT_PREFIX "IdeSendCommand: SRB_STATUS_PENDING\n"));
        if(CmdAction & CMD_ACTION_EXEC) {
            KdPrint2((PRINT_PREFIX "IdeSendCommand: REQ_STATE_EXPECTING_INTR\n"));
            AtaReq->ReqState = REQ_STATE_EXPECTING_INTR;
        }
    } else {
        KdPrint2((PRINT_PREFIX "IdeSendCommand: REQ_STATE_TRANSFER_COMPLETE\n"));
        AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
    }

    return status;

} // end IdeSendCommand()


/*++

Routine Description:
    Enables disables media status notification

Arguments:
    HwDeviceExtension - ATAPI driver storage.

--*/
VOID
NTAPI
IdeMediaStatus(
    BOOLEAN EnableMSN,
    IN PVOID HwDeviceExtension,
    UCHAR ldev
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PHW_CHANNEL          chan;
    UCHAR lChannel = ldev >> 1;
    UCHAR statusByte,errorByte;

    chan = &(deviceExtension->chan[lChannel]);

    if (EnableMSN == TRUE){

        // If supported enable Media Status Notification support
        if ((deviceExtension->lun[ldev].DeviceFlags & DFLAGS_REMOVABLE_DRIVE)) {

            // enable
            statusByte = AtaCommand(deviceExtension, ldev & 1, lChannel,
                                IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                0, ATA_C_F_ENAB_MEDIASTAT, ATA_WAIT_BASE_READY);

            if (statusByte & IDE_STATUS_ERROR) {
                // Read the error register.
                errorByte = AtapiReadPort1(chan, IDX_IO1_i_Error);

                KdPrint2((PRINT_PREFIX 
                            "IdeMediaStatus: Error enabling media status. Status %#x, error byte %#x\n",
                             statusByte,
                             errorByte));
            } else {
                deviceExtension->lun[ldev].DeviceFlags |= DFLAGS_MEDIA_STATUS_ENABLED;
                KdPrint2((PRINT_PREFIX "IdeMediaStatus: Media Status Notification Supported\n"));
                chan->ReturningMediaStatus = 0;

            }

        }
    } else { // end if EnableMSN == TRUE

        // disable if previously enabled
        if ((deviceExtension->lun[ldev].DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED)) {

            statusByte = AtaCommand(deviceExtension, ldev & 1, lChannel,
                                IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                0, ATA_C_F_DIS_MEDIASTAT, ATA_WAIT_BASE_READY);
            deviceExtension->lun[ldev].DeviceFlags &= ~DFLAGS_MEDIA_STATUS_ENABLED;
        }


    }


} // end IdeMediaStatus()


/*++

Routine Description:

    Builts an artificial sense buffer to report the results of a GET_MEDIA_STATUS
    command. This function is invoked to satisfy the SCSIOP_REQUEST_SENSE.
Arguments:

    HwDeviceExtension - ATAPI driver storage.
    Srb - System request block.

Return Value:

    SRB status (ALWAYS SUCCESS).

--*/
ULONG
NTAPI
IdeBuildSenseBuffer(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
//    ULONG status;
    PSENSE_DATA  senseBuffer = (PSENSE_DATA)Srb->DataBuffer;
    UCHAR ReturningMediaStatus = deviceExtension->chan[GET_CHANNEL(Srb)].ReturningMediaStatus;

    if (senseBuffer){

        if(ReturningMediaStatus & IDE_ERROR_MEDIA_CHANGE) {

            senseBuffer->ErrorCode = 0x70;
            senseBuffer->Valid     = 1;
            senseBuffer->AdditionalSenseLength = 0xb;
            senseBuffer->SenseKey =  SCSI_SENSE_UNIT_ATTENTION;
            senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
            senseBuffer->AdditionalSenseCodeQualifier = 0;
        } else if(ReturningMediaStatus & IDE_ERROR_MEDIA_CHANGE_REQ) {

            senseBuffer->ErrorCode = 0x70;
            senseBuffer->Valid     = 1;
            senseBuffer->AdditionalSenseLength = 0xb;
            senseBuffer->SenseKey =  SCSI_SENSE_UNIT_ATTENTION;
            senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
            senseBuffer->AdditionalSenseCodeQualifier = 0;
        } else if(ReturningMediaStatus & IDE_ERROR_END_OF_MEDIA) {

            senseBuffer->ErrorCode = 0x70;
            senseBuffer->Valid     = 1;
            senseBuffer->AdditionalSenseLength = 0xb;
            senseBuffer->SenseKey =  SCSI_SENSE_NOT_READY;
            senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
            senseBuffer->AdditionalSenseCodeQualifier = 0;
        } else if(ReturningMediaStatus & IDE_ERROR_DATA_ERROR) {

            senseBuffer->ErrorCode = 0x70;
            senseBuffer->Valid     = 1;
            senseBuffer->AdditionalSenseLength = 0xb;
            senseBuffer->SenseKey =  SCSI_SENSE_DATA_PROTECT;
            senseBuffer->AdditionalSenseCode = 0;
            senseBuffer->AdditionalSenseCodeQualifier = 0;
        }
        return SRB_STATUS_SUCCESS;
    }
    return SRB_STATUS_ERROR;

}// End of IdeBuildSenseBuffer

VOID
NTAPI
UniataUserDeviceReset(
    PHW_DEVICE_EXTENSION deviceExtension,
    PHW_LU_EXTENSION LunExt,
    ULONG PathId,
    ULONG ldev
    )
{
    AtapiDisableInterrupts(deviceExtension, PathId);
    if (LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) {
        KdPrint2((PRINT_PREFIX "UniataUserDeviceReset: Reset ATAPI\n"));
        AtapiSoftReset(&(deviceExtension->chan[PathId]), ldev & 1);
    } else {
        KdPrint2((PRINT_PREFIX "UniataUserDeviceReset: Reset IDE -> reset entire channel\n"));
        AtapiResetController__(deviceExtension, PathId, RESET_COMPLETE_NONE);
        deviceExtension->chan[PathId].lun[0]->DeviceFlags |= DFLAGS_REINIT_DMA;
        deviceExtension->chan[PathId].lun[1]->DeviceFlags |= DFLAGS_REINIT_DMA;
    }
    LunExt->DeviceFlags |= DFLAGS_REINIT_DMA;  // force PIO/DMA reinit
    AtapiEnableInterrupts(deviceExtension, PathId);
    return;
} // end UniataUserDeviceReset()

BOOLEAN
NTAPI
UniataNeedQueueing(
    PHW_DEVICE_EXTENSION deviceExtension,
    PHW_CHANNEL          chan,
    BOOLEAN              TopLevel
    )
{
    BOOLEAN PostReq = FALSE;
    if(TopLevel) {
        KdPrint3((PRINT_PREFIX "UniataNeedQueueing: TopLevel, qd=%x\n", chan->queue_depth));
        if(chan->queue_depth > 0) {
#if 0
            if(atapiDev &&
               ((Srb->Cdb[0] == SCSIOP_TEST_UNIT_READY)/* ||
                (Srb->Cdb[0] == SCSIOP_REQUEST_SENSE)*/) ) {
                KdPrint2((PRINT_PREFIX "spec: SCSIOP_TEST_UNIT_READY\n"));
                //PostReq = FALSE;
                status = SRB_STATUS_BUSY;
                goto skip_exec;
            } else {
                PostReq = TRUE;
            }
#else
            PostReq = TRUE;
#endif
        } else
        if(deviceExtension->simplexOnly && deviceExtension->queue_depth > 0) {
            PostReq = TRUE;
        }
    } else {
        KdPrint3((PRINT_PREFIX "UniataNeedQueueing: qd=%x\n", chan->queue_depth));
    }
    return PostReq;
} // end UniataNeedQueueing()

/*++

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

--*/
BOOLEAN
NTAPI
AtapiStartIo(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    return AtapiStartIo__(HwDeviceExtension, Srb, TRUE);
} // end AtapiStartIo()

BOOLEAN
NTAPI
AtapiStartIo__(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb,
    IN BOOLEAN TopLevel
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    UCHAR                lChannel;
    PHW_CHANNEL          chan;
    ULONG status;
    ULONG ldev;
    UCHAR PathId;
    UCHAR TargetId;
    UCHAR Lun;
    PATA_REQ AtaReq;
    PSCSI_REQUEST_BLOCK tmpSrb;
    BOOLEAN PostReq = FALSE;
    BOOLEAN atapiDev;
    BOOLEAN commPort = FALSE;

    // deviceExtension->Isr2DevObj must always be NULL for non-PCI
    if(deviceExtension->Isr2DevObj && !BMList[deviceExtension->DevIndex].Isr2Enable) {
        KdPrint2((PRINT_PREFIX "Isr2Enable -> 1\n"));
        BMList[deviceExtension->DevIndex].Isr2Enable = TRUE;
    }
//    deviceExtension->QueueNewIrql = max(deviceExtension->QueueNewIrql, KeGetCurrentIrql());

/*                KeBugCheckEx(0xc000000e,
                             (Srb->PathId<<16) | (Srb->TargetId<<8) | (Srb->Lun),
                             Srb->Function, 
                             TopLevel, 0x80000001);
*/
    if(TopLevel && Srb && Srb->SrbExtension) {
        KdPrint2((PRINT_PREFIX "TopLevel\n"));
        RtlZeroMemory(Srb->SrbExtension, sizeof(ATA_REQ));
    }

    do {

        lChannel = GET_CHANNEL(Srb);
        chan = &(deviceExtension->chan[lChannel]);
        ldev = GET_LDEV(Srb);

        //ASSERT(deviceExtension);
        //ASSERT(chan);

        KdPrint2((PRINT_PREFIX 
                   "** AtapiStartIo: Function %#x, PATH:LUN:TID = %#x:%#x:%#x\n",
                   Srb->Function, Srb->PathId, Srb->Lun, Srb->TargetId));
        KdPrint2((PRINT_PREFIX "   VendorID+DeviceID/Rev %#x/%#x\n", deviceExtension->DevID, deviceExtension->RevID));

        if(lChannel == deviceExtension->NumberChannels &&
           !Srb->Lun && !Srb->TargetId &&
           ((Srb->Function == SRB_FUNCTION_IO_CONTROL) ||
            (Srb->Function == SRB_FUNCTION_EXECUTE_SCSI && Srb->Cdb[0] == SCSIOP_INQUIRY))
           ) {
            KdPrint2((PRINT_PREFIX 
                       "AtapiStartIo: Communication port\n"));
            if(Srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {

                PINQUIRYDATA    inquiryData  = (PINQUIRYDATA)(Srb->DataBuffer);

                KdPrint2((PRINT_PREFIX 
                           "  INQUIRY\n"));
                // Zero INQUIRY data structure.
                RtlZeroMemory((PCHAR)(Srb->DataBuffer), Srb->DataTransferLength);

                inquiryData->DeviceType = COMMUNICATION_DEVICE;

                // Fill in vendor identification fields.
                RtlCopyMemory(&inquiryData->VendorId, &uniata_comm_name, 28);

                status = SRB_STATUS_SUCCESS;
                goto complete_req;
            }
            commPort = TRUE;
            /* Pass IOCTL request down */
        } else
        if(GET_CDEV(Srb) >= 2 ||
           ldev >= deviceExtension->NumberChannels*2 ||
           lChannel >= deviceExtension->NumberChannels ||
           Srb->Lun) {

           if(lChannel >= deviceExtension->NumberChannels) {
               chan = NULL;
           }

reject_srb:
            //if(!CheckDevice(HwDeviceExtension, lChannel, ldev & 1, FALSE)) {
                KdPrint3((PRINT_PREFIX 
                           "AtapiStartIo: SRB rejected\n"));
                // Indicate no device found at this address.
                KdPrint2((PRINT_PREFIX "SRB_STATUS_SELECTION_TIMEOUT\n"));
                status = SRB_STATUS_SELECTION_TIMEOUT;
                goto complete_req;
            //}
        }

        atapiDev = (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_ATAPI_DEVICE) ? TRUE : FALSE;

#ifdef _DEBUG
        if(!commPort && !(chan->lun[ldev & 1])) {
#if 0
            PrintNtConsole("de = %#x, chan = %#x , dev %#x, nchan %#x\n",
                deviceExtension,
                chan, ldev & 1,
                deviceExtension->NumberChannels);
            PrintNtConsole("lchan = %#x, ldev %#x, cdev %#x, lun0 %#x\n",
                lChannel, ldev, GET_CDEV(Srb), deviceExtension->chan[0].lun[0]);
            PrintNtConsole("Function %#x, PATH:LUN:TID = %#x:%#x:%#x\n",
                       Srb->Function, Srb->PathId, Srb->Lun, Srb->TargetId);
#endif //0
/*
            int i;
            for(i=0; i<1000; i++) {
                AtapiStallExecution(3*1000);
            }
*/
            goto reject_srb;
        }
#endif //_DEBUG

        // Determine which function.
        switch (Srb->Function) {

        case SRB_FUNCTION_EXECUTE_SCSI:

            if(!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                if(Srb->Cdb[0] == SCSIOP_ATA_PASSTHROUGH) {
                    // let passthrough go
                } else
                if(Srb->Cdb[0] == SCSIOP_INQUIRY) {
                    // let INQUIRY go
                } else {

                //if(!CheckDevice(HwDeviceExtension, lChannel, ldev & 1, FALSE)) {
                    KdPrint2((PRINT_PREFIX 
                               "AtapiStartIo: EXECUTE_SCSI rejected (2)\n"));
                    // Indicate no device found at this address.
                    KdPrint2((PRINT_PREFIX "SRB_STATUS_SELECTION_TIMEOUT\n"));
                    status = SRB_STATUS_SELECTION_TIMEOUT;
                    break;
                //}
                }
            }
/*
            __try {
                if(Srb->DataTransferLength) {
                    UCHAR a;
                    a = ((PUCHAR)(Srb->DataBuffer))[0];
                    g_foo += a;
                }
            } __except(EXCEPTION_EXECUTE_HANDLER) {
                KdPrint3((PRINT_PREFIX 
                           "AtapiStartIo: Bad data buffer -> EXECUTE_SCSI rejected\n"));
                // Indicate no device found at this address.
                KdPrint3((PRINT_PREFIX "SRB_STATUS_ERROR\n"));
                status = SRB_STATUS_ERROR;
                KdPrint2((PRINT_PREFIX "  *** Exception...\n"));
                ASSERT(FALSE);
                break;
            }
*/
            PostReq = UniataNeedQueueing(deviceExtension, chan, TopLevel);

            if(PostReq) {

                KdPrint3((PRINT_PREFIX "Non-empty queue\n"));
                if (atapiDev &&
                    (Srb->Cdb[0] != SCSIOP_ATA_PASSTHROUGH)) {
                    KdPrint3((PRINT_PREFIX "Try ATAPI prepare\n"));

                    status = AtapiSendCommand(HwDeviceExtension, Srb, CMD_ACTION_PREPARE);
                } else {
                    KdPrint2((PRINT_PREFIX "Try IDE prepare\n"));
                    status = IdeSendCommand(HwDeviceExtension, Srb, CMD_ACTION_PREPARE);
                }
                /*KeBugCheckEx(0xc000000e,
                             (Srb->PathId<<16) | (Srb->TargetId<<8) | (Srb->Lun),
                             Srb->Function, 
                             status, 0x80000001);*/
                if(status == SRB_STATUS_BUSY)
                    status = SRB_STATUS_PENDING;
                // Insert requests AFTER they have been initialized on
                // CMD_ACTION_PREPARE stage
                // we should not check TopLevel here (it is always TRUE)
                //ASSERT(chan->lun[GET_LDEV(Srb) & 1]);
                UniataQueueRequest(chan, Srb);

                KdPrint2((PRINT_PREFIX "AtapiStartIo: Already have %d request(s)!\n", chan->queue_depth));

            } else {
                // Send command to device.
                KdPrint2((PRINT_PREFIX "Send to device\n"));
                if(TopLevel) {
                    KdPrint2((PRINT_PREFIX "TopLevel (2), srb %#x\n", Srb));
                    AtaReq = (PATA_REQ)(Srb->SrbExtension);
                    KdPrint2((PRINT_PREFIX "TopLevel (3), AtaReq %#x\n", AtaReq));
                    //ASSERT(!AtaReq->Flags);
                    //ASSERT(chan->lun[GET_LDEV(Srb) & 1]);
                    UniataQueueRequest(chan, Srb);
//                    AtaReq = (PATA_REQ)(Srb->SrbExtension);
                    //ASSERT(!AtaReq->Flags);
                    AtaReq->ReqState = REQ_STATE_QUEUED;
                    //ASSERT(!AtaReq->Flags);
                }

#ifndef NAVO_TEST
                if(!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                    if(Srb->Cdb[0] == SCSIOP_INQUIRY) {
                        if(UniataAnybodyHome(deviceExtension, chan->lChannel, ldev & 1)) {
                            if(!CheckDevice(HwDeviceExtension, chan->lChannel, ldev & 1, TRUE)) {
                                goto reject_srb;
                            }
                        }
                        if(!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                            goto reject_srb;
                        }
                    } else
                    if(Srb->Cdb[0] == SCSIOP_ATA_PASSTHROUGH) {
                        // allow
                    } else {
                        goto reject_srb;
                    }
                }
#endif //NAVO_TEST

                if(atapiDev &&
                   (Srb->Cdb[0] != SCSIOP_ATA_PASSTHROUGH)) {
                    KdPrint3((PRINT_PREFIX "Try ATAPI send\n"));
                    status = AtapiSendCommand(HwDeviceExtension, Srb, CMD_ACTION_ALL);
                } else {
                    KdPrint2((PRINT_PREFIX "Try IDE send\n"));
/*                    {
                        ULONG __ebp__ = 0;
                        ULONG __esp__ = 0;

                        KdPrint2((PRINT_PREFIX "** before IdeSendCommand:\n"));
                        __asm {
                            mov eax,ebp
                            mov __ebp__, eax
                            mov eax,esp
                            mov __esp__, eax
                        }
                        KdPrint2((PRINT_PREFIX "** before Ide: EBP:%#x ESP:%#x\n", __ebp__, __esp__));
                    }*/
                    status = IdeSendCommand(HwDeviceExtension, Srb, CMD_ACTION_ALL);
                }
/*                KeBugCheckEx(0xc000000e,
                             (Srb->PathId<<16) | (Srb->TargetId<<8) | (Srb->Lun),
                             Srb->Function, 
                             status, 0x80000002);*/

            }
//skip_exec:
            TopLevel = FALSE;

            break;

        case SRB_FUNCTION_ABORT_COMMAND:

            tmpSrb = ScsiPortGetSrb(HwDeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun,
                               Srb->QueueTag);
            // Verify that SRB to abort is still outstanding.
            if((tmpSrb != Srb->NextSrb) ||
               !chan->queue_depth) {

                KdPrint2((PRINT_PREFIX "AtapiStartIo: SRB to abort already completed\n"));

                // Complete abort SRB.
                status = SRB_STATUS_ABORT_FAILED;
                break;
            }

            AtaReq = (PATA_REQ)(tmpSrb->SrbExtension);
            if(AtaReq->ReqState > REQ_STATE_READY_TO_TRANSFER) {
                if (!AtapiResetController__(deviceExtension, lChannel, RESET_COMPLETE_CURRENT)) {
                      KdPrint2((PRINT_PREFIX "AtapiStartIo: Abort command failed\n"));
                    // Log reset failure.
                    KdPrint3((PRINT_PREFIX 
                                "ScsiPortLogError: devExt %#x, Srb %#x, P:T:D=%d:%d:%d, MsgId %#x (%d)\n",
                                      HwDeviceExtension, NULL, 0, 0, 0, SP_INTERNAL_ADAPTER_ERROR, 5 << 8
                                ));
                    ScsiPortLogError(HwDeviceExtension, NULL, 0, 0, 0, SP_INTERNAL_ADAPTER_ERROR, 5 << 8);
                    status = SRB_STATUS_ERROR;

                } else {
                    status = SRB_STATUS_SUCCESS;
                }
            } else {
                KdPrint2((PRINT_PREFIX "AtapiInterrupt: remove aborted srb %#x\n", tmpSrb));
                if (tmpSrb->SenseInfoBuffer &&
                    tmpSrb->SenseInfoBufferLength >= sizeof(SENSE_DATA)) {

                    PSENSE_DATA  senseBuffer = (PSENSE_DATA)tmpSrb->SenseInfoBuffer;

                    senseBuffer->ErrorCode = 0;
                    senseBuffer->Valid     = 1;
                    senseBuffer->AdditionalSenseLength = 0xb;
                    senseBuffer->SenseKey =  SCSI_SENSE_ABORTED_COMMAND;
                    senseBuffer->AdditionalSenseCode = 0;
                    senseBuffer->AdditionalSenseCodeQualifier = 0;

                    tmpSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
                }
                AtapiDmaDBSync(chan, tmpSrb);
                UniataRemoveRequest(chan, tmpSrb);
                // Indicate command complete.
                ScsiPortNotification(RequestComplete,
                                     deviceExtension,
                                     tmpSrb);
                status = SRB_STATUS_SUCCESS;
            }
            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_DEVICE:
        case SRB_FUNCTION_RESET_LOGICAL_UNIT:

            // Reset single device.
            // For now we support only Lun=0

            // Note: reset is immediate command, it cannot be queued since it is usually used to
            // revert not- responding device to operational state
            KdPrint2((PRINT_PREFIX "AtapiStartIo: Reset device request received\n"));
            UniataUserDeviceReset(deviceExtension, &(deviceExtension->lun[ldev]), lChannel, ldev);
            status = SRB_STATUS_SUCCESS;
            break;

        case SRB_FUNCTION_RESET_BUS:
do_bus_reset:
            // Reset Atapi and SCSI bus.

            // Note: reset is immediate command, it cannot be queued since it is usually used to
            // revert not- responding device to operational state
            KdPrint2((PRINT_PREFIX "AtapiStartIo: Reset bus request received\n"));
            if (!AtapiResetController__(deviceExtension, lChannel, RESET_COMPLETE_ALL)) {
                  KdPrint2((PRINT_PREFIX "AtapiStartIo: Reset bus failed\n"));
                // Log reset failure.
                KdPrint3((PRINT_PREFIX 
                            "ScsiPortLogError: devExt %#x, Srb %#x, P:T:D=%d:%d:%d, MsgId %#x (%d) - (2)\n",
                                  HwDeviceExtension, NULL, 0, 0, 0, SP_INTERNAL_ADAPTER_ERROR, 5 << 8
                            ));
                ScsiPortLogError(HwDeviceExtension, NULL, 0, 0, 0, SP_INTERNAL_ADAPTER_ERROR, 5 << 8);
                status = SRB_STATUS_ERROR;

            } else {
                status = SRB_STATUS_SUCCESS;
            }

            break;

        case SRB_FUNCTION_SHUTDOWN:

            KdPrint2((PRINT_PREFIX "AtapiStartIo: Shutdown\n"));
            if(!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                KdPrint2((PRINT_PREFIX "AtapiStartIo: Shutdown - no such device\n"));
            }
            if(atapiDev) {
                // FLUSH ATAPI device - do nothing
                KdPrint2((PRINT_PREFIX "AtapiStartIo: Shutdown - ATAPI device\n"));
            } else {
                // FLUSH IDE/ATA device
                KdPrint2((PRINT_PREFIX "AtapiStartIo: Shutdown - IDE device\n"));
                AtapiDisableInterrupts(deviceExtension, lChannel);
                status = AtaCommand(deviceExtension, ldev & 1, GET_CHANNEL(Srb),
                           IDE_COMMAND_FLUSH_CACHE, 0, 0, 0, 0, 0, ATA_WAIT_IDLE);
                // If supported & allowed, reset write cacheing
                if(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_WCACHE_ENABLED) {

                    // Disable write cache
                    status = AtaCommand(deviceExtension, ldev & 1, lChannel,
                                        IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                        0, ATA_C_F_DIS_WCACHE, ATA_WAIT_BASE_READY);
                    // Check for errors.
                    if (status & IDE_STATUS_ERROR) {
                        KdPrint2((PRINT_PREFIX 
                                    "AtapiHwInitialize: Disable write cacheing on Device %d failed\n",
                                    ldev));
                    }
                    deviceExtension->lun[ldev].DeviceFlags &= ~DFLAGS_WCACHE_ENABLED;

                    // Re-enable write cache
                    status = AtaCommand(deviceExtension, ldev & 1, lChannel,
                                        IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                        0, ATA_C_F_ENAB_WCACHE, ATA_WAIT_BASE_READY);
                    // Check for errors.
                    if (status & IDE_STATUS_ERROR) {
                        KdPrint2((PRINT_PREFIX 
                                    "AtapiHwInitialize: Enable write cacheing on Device %d failed\n",
                                    ldev));
                        deviceExtension->lun[ldev].DeviceFlags &= ~DFLAGS_WCACHE_ENABLED;
                    } else {
                        deviceExtension->lun[ldev].DeviceFlags |= DFLAGS_WCACHE_ENABLED;
                    }
                }

                AtapiEnableInterrupts(deviceExtension, lChannel);
            }
            status = SRB_STATUS_SUCCESS;

            break;

        case SRB_FUNCTION_FLUSH:

            KdPrint2((PRINT_PREFIX "AtapiStartIo: Flush (do nothing)\n"));
            status = SRB_STATUS_SUCCESS;
            break;

    /*    case SRB_FUNCTION_SHUTDOWN:
        case SRB_FUNCTION_FLUSH:

            // Flush device's cache.
            KdPrint2((PRINT_PREFIX "AtapiStartIo: Device flush received\n"));

            if (chan->CurrentSrb) {

                KdPrint2((PRINT_PREFIX "AtapiStartIo (SRB_FUNCTION_FLUSH): Already have a request!\n"));
                Srb->SrbStatus = SRB_STATUS_BUSY;
                ScsiPortNotification(RequestComplete,
                                     deviceExtension,
                                     Srb);
                return FALSE;
            }

            if (deviceExtension->lun[ldev].DeviceFlags & DFLAGS_ATAPI_DEVICE) {
                status = SRB_STATUS_SUCCESS;
            } else {
                status = AtaCommand(deviceExtension, ldev & 1, GET_CHANNEL(Srb),
                           IDE_COMMAND_FLUSH_CACHE, 0, 0, 0, 0, 0, ATA_WAIT_INTR);
                if (status & IDE_STATUS_DRQ) {
                    status = SRB_STATUS_SUCCESS;
                } else {
                    status = SRB_STATUS_SELECTION_TIMEOUT;
                }
            }
            break;*/

        case SRB_FUNCTION_IO_CONTROL: {

            ULONG len;

            KdPrint2((PRINT_PREFIX "AtapiStartIo: SRB_FUNCTION_IO_CONTROL\n"));

            len = Srb->DataTransferLength;

            if(!AtapiStringCmp( (PCHAR)(((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature),"SCSIDISK",sizeof("SCSIDISK")-1)) {

                switch (((PSRB_IO_CONTROL)(Srb->DataBuffer))->ControlCode) {
                case IOCTL_SCSI_MINIPORT_SMART_VERSION: {

                    PGETVERSIONINPARAMS versionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
                    UCHAR deviceNumber;

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: IOCTL_SCSI_MINIPORT_SMART_VERSION\n"));

                    // Version and revision per SMART 1.03

                    versionParameters->bVersion = 1;
                    versionParameters->bRevision = 1;
                    versionParameters->bReserved = 0;

                    // Indicate that support for IDE IDENTIFY, ATAPI IDENTIFY and SMART commands.
                    versionParameters->fCapabilities = (CAP_ATA_ID_CMD | CAP_ATAPI_ID_CMD | CAP_SMART_CMD);

                    // This is done because of how the IOCTL_SCSI_MINIPORT
                    // determines 'targetid's'. Disk.sys places the real target id value
                    // in the DeviceMap field. Once we do some parameter checking, the value passed
                    // back to the application will be determined.

                    deviceNumber = versionParameters->bIDEDeviceMap;

                    if(commPort) {
                        KdPrint2((PRINT_PREFIX 
                                   "AtapiStartIo: SCSIDISK IOCTL for commPort -> EXECUTE_SCSI rejected (2)\n"));
                        // Indicate no device found at this address.
                        KdPrint2((PRINT_PREFIX "SRB_STATUS_SELECTION_TIMEOUT\n"));
                        status = SRB_STATUS_SELECTION_TIMEOUT;
                        break;
                    }

                    if (!(deviceExtension->lun[ldev].DeviceFlags & DFLAGS_DEVICE_PRESENT) ||
                        atapiDev) {

                        status = SRB_STATUS_SELECTION_TIMEOUT;
                        break;
                    }

                    // NOTE: This will only set the bit
                    // corresponding to this drive's target id.
                    // The bit mask is as follows:
                    //
                    //     -Sec Pri
                    //     S M S M
                    //     3 2 1 0

                    if (deviceExtension->NumberChannels == 1) {
                        if (chan->PrimaryAddress) {
                            deviceNumber = 1 << ldev;
                        } else {
                            deviceNumber = 4 << ldev;
                        }
                    } else {
                        deviceNumber = 1 << ldev;
                    }

                    versionParameters->bIDEDeviceMap = deviceNumber;

                    status = SRB_STATUS_SUCCESS;
                    break;
                }

                case IOCTL_SCSI_MINIPORT_IDENTIFY: {

                    PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
                    SENDCMDINPARAMS   cmdInParameters = *(PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
                    UCHAR             targetId;

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: IOCTL_SCSI_MINIPORT_IDENTIFY\n"));
                    // Extract the target.
                    targetId = cmdInParameters.bDriveNumber;
                    KdPrint2((PRINT_PREFIX "targetId %d\n", targetId));
                    if((targetId >= deviceExtension->NumberChannels*2) ||
                       !(deviceExtension->lun[targetId].DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                        KdPrint2((PRINT_PREFIX "Error: xxx_ID_CMD for non-existant device\n"));
                        status = SRB_STATUS_SELECTION_TIMEOUT;
                        break;
                    }

                    switch(cmdInParameters.irDriveRegs.bCommandReg) {
                    case ID_CMD:
                        if((deviceExtension->lun[targetId].DeviceFlags & DFLAGS_ATAPI_DEVICE)) {
                            KdPrint2((PRINT_PREFIX "Error: ID_CMD for ATAPI\n"));
                            status = SRB_STATUS_INVALID_REQUEST;
                            break;
                        }
                        /* FALL THROUGH */
                    case ATAPI_ID_CMD:

                        if(!(deviceExtension->lun[targetId].DeviceFlags & DFLAGS_ATAPI_DEVICE) &&
                           (cmdInParameters.irDriveRegs.bCommandReg == ATAPI_ID_CMD)) {
                            KdPrint2((PRINT_PREFIX "Error: ATAPI_ID_CMD for non-ATAPI\n"));
                            status = SRB_STATUS_INVALID_REQUEST;
                            break;
                        }

                        len = min(len, sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE);
                        // Zero the output buffer
                        RtlZeroMemory(cmdOutParameters, len);
/*                        for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1); i++) {
                            ((PUCHAR)cmdOutParameters)[i] = 0;
                        }*/

                        // Build status block.
                        cmdOutParameters->cBufferSize = min(IDENTIFY_BUFFER_SIZE, len - sizeof(SENDCMDOUTPARAMS) + 1);
                        cmdOutParameters->DriverStatus.bDriverError = 0;
                        cmdOutParameters->DriverStatus.bIDEError = 0;

                        // Extract the identify data from the device extension.
                        ScsiPortMoveMemory (cmdOutParameters->bBuffer, &deviceExtension->lun[targetId].IdentifyData,
                            cmdOutParameters->cBufferSize);

                        KdPrint2((PRINT_PREFIX "AtapiStartIo: IOCTL_SCSI_MINIPORT_IDENTIFY Ok\n"));

                        status = SRB_STATUS_SUCCESS;

                        break;
                    default:
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: not supported ID code %x\n",
                            cmdInParameters.irDriveRegs.bCommandReg));
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }
                    break;
                }

                case  IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS:
                case  IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS:
                case  IOCTL_SCSI_MINIPORT_ENABLE_SMART:
                case  IOCTL_SCSI_MINIPORT_DISABLE_SMART:
                case  IOCTL_SCSI_MINIPORT_RETURN_STATUS:
                case  IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE:
                case  IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES:
                case  IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS:

                    if(commPort) {
                        KdPrint2((PRINT_PREFIX 
                                   "AtapiStartIo: SCSIDISK Smart IOCTL for commPort -> EXECUTE_SCSI rejected (3)\n"));
                        // Indicate no device found at this address.
                        KdPrint2((PRINT_PREFIX "SRB_STATUS_SELECTION_TIMEOUT\n"));
                        status = SRB_STATUS_SELECTION_TIMEOUT;
                        break;
                    }

                    PostReq = UniataNeedQueueing(deviceExtension, chan, TopLevel);

                    if(PostReq || TopLevel) {
                        UniataQueueRequest(chan, Srb);
                        AtaReq = (PATA_REQ)(Srb->SrbExtension);
                        AtaReq->ReqState = REQ_STATE_QUEUED;
                    }

                    if(PostReq) {

                        KdPrint2((PRINT_PREFIX "Non-empty queue (SMART)\n"));
                        status = SRB_STATUS_PENDING;

                        KdPrint2((PRINT_PREFIX "AtapiStartIo: Already have %d request(s)!\n", chan->queue_depth));
                    } else {

                        status = IdeSendSmartCommand(HwDeviceExtension,Srb);
                    }
                    break;

                default :
                    KdPrint2((PRINT_PREFIX "AtapiStartIo: invalid IoControl %#x for SCSIDISK signature\n",
                                ((PSRB_IO_CONTROL)(Srb->DataBuffer))->ControlCode ));
                    status = SRB_STATUS_INVALID_REQUEST;
                    break;

                }
            } else
            if(!AtapiStringCmp( (PCHAR)(((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature),"-UNIATA-", sizeof("-UNIATA-")-1)) {

                PUNIATA_CTL AtaCtl = (PUNIATA_CTL)(Srb->DataBuffer);
                ULONG ldev = GET_LDEV2(AtaCtl->addr.PathId, AtaCtl->addr.TargetId, 0);
                PHW_LU_EXTENSION LunExt;
                BOOLEAN bad_ldev;
                ULONG i;
                //chan = &(deviceExtension->chan[lChannel]);

                if(AtaCtl->addr.Lun ||
                   ldev >= deviceExtension->NumberChannels*2 ||
                   AtaCtl->addr.PathId >= deviceExtension->NumberChannels) {

                    bad_ldev = TRUE;
                    LunExt = NULL;

                } else {
                    bad_ldev = FALSE;
                    LunExt = &(deviceExtension->lun[ldev]);
                    lChannel = AtaCtl->addr.PathId;
                    chan = &(deviceExtension->chan[lChannel]);
                }

                KdPrint2((PRINT_PREFIX "AtapiStartIo: -UNIATA- %#x, ldev %#x\n", AtaCtl->hdr.ControlCode, ldev));

                /* check for valid LUN */
                switch (AtaCtl->hdr.ControlCode) {
                case  IOCTL_SCSI_MINIPORT_UNIATA_FIND_DEVICES:
                case  IOCTL_SCSI_MINIPORT_UNIATA_RESET_DEVICE:
                    if(bad_ldev &&
                       (AtaCtl->addr.PathId >= deviceExtension->NumberChannels ||
                        AtaCtl->addr.TargetId != 0xff ||
                        AtaCtl->addr.Lun != 0
                        )) {
                        if(AtaCtl->hdr.ControlCode == IOCTL_SCSI_MINIPORT_UNIATA_FIND_DEVICES &&
                           ldev < deviceExtension->NumberChannels*2) { // AtaCtl->addr.TargetId != 0xff
                            LunExt = &(deviceExtension->lun[ldev]);
                            // OK
                        } else {
                            goto handle_bad_ldev;
                        }
                    }
                    // this would be BUS reset
                    lChannel = AtaCtl->addr.PathId;
                    chan = &(deviceExtension->chan[lChannel]);
                    break;
                case  IOCTL_SCSI_MINIPORT_UNIATA_DELETE_DEVICE:
                case  IOCTL_SCSI_MINIPORT_UNIATA_SET_MAX_MODE:
                case  IOCTL_SCSI_MINIPORT_UNIATA_GET_MODE:
                case  IOCTL_SCSI_MINIPORT_UNIATA_RESETBB:
//                case  IOCTL_SCSI_MINIPORT_UNIATA_REG_IO:
                    if(bad_ldev) {
handle_bad_ldev:
                        KdPrint2((PRINT_PREFIX 
                                   "AtapiStartIo: bad_ldev -> IOCTL SRB rejected\n"));
                        // Indicate no device found at this address.
                        goto reject_srb;
                    }
                }

                /* check if queueing is necessary */
                switch (AtaCtl->hdr.ControlCode) {
                case  IOCTL_SCSI_MINIPORT_UNIATA_RESETBB:
                    if(!LunExt->nBadBlocks) {
                        break;
                    }
                    goto uata_ctl_queue;
                case  IOCTL_SCSI_MINIPORT_UNIATA_SET_MAX_MODE:
                    if(!AtaCtl->SetMode.ApplyImmediately) {
                        break;
                    }
                    goto uata_ctl_queue;
                case  IOCTL_SCSI_MINIPORT_UNIATA_FIND_DEVICES:
                //case  IOCTL_SCSI_MINIPORT_UNIATA_RESET_DEVICE: reset must be processed immediately
uata_ctl_queue:
                    KdPrint2((PRINT_PREFIX "put to queue (UNIATA)\n"));
                    PostReq = UniataNeedQueueing(deviceExtension, chan, TopLevel);

                    if(PostReq || TopLevel) {
                        UniataQueueRequest(chan, Srb);
                        AtaReq = (PATA_REQ)(Srb->SrbExtension);
                        AtaReq->ReqState = REQ_STATE_QUEUED;
                    }
                    if(PostReq) {
                        KdPrint2((PRINT_PREFIX "Non-empty queue (UNIATA)\n"));
                        status = SRB_STATUS_PENDING;

                        KdPrint2((PRINT_PREFIX "AtapiStartIo: Already have %d request(s)!\n", chan->queue_depth));
                        goto complete_req;
                    } 
                } // end switch (AtaCtl->hdr.ControlCode)

                /* process request */
                switch (AtaCtl->hdr.ControlCode) {
                case  IOCTL_SCSI_MINIPORT_UNIATA_FIND_DEVICES:

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: rescan bus\n"));

                    if(AtaCtl->FindDelDev.Flags & UNIATA_ADD_FLAGS_UNHIDE) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: unhide from further detection\n"));
                        if(AtaCtl->addr.TargetId != 0xff) {
                            deviceExtension->lun[ldev].DeviceFlags &= ~DFLAGS_HIDDEN;
                        } else {
                        }
                    }

                    for(i=0; i<AtaCtl->FindDelDev.WaitForPhysicalLink && i<30; i++) {
                        AtapiStallExecution(1000 * 1000);
                    }

                    FindDevices(HwDeviceExtension,
                                ((AtaCtl->addr.TargetId == 0xff) && (AtaCtl->FindDelDev.Flags & UNIATA_ADD_FLAGS_UNHIDE))
                                     ? UNIATA_FIND_DEV_UNHIDE : 0,
                                AtaCtl->addr.PathId);
                    status = SRB_STATUS_SUCCESS;

                    break;

                case  IOCTL_SCSI_MINIPORT_UNIATA_DELETE_DEVICE: {

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: remove %#x:%#x\n", AtaCtl->addr.PathId, AtaCtl->addr.TargetId));

                    deviceExtension->lun[ldev].DeviceFlags = 0;
                    if(AtaCtl->FindDelDev.Flags & UNIATA_REMOVE_FLAGS_HIDE) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: hide from further detection\n"));
                        deviceExtension->lun[ldev].DeviceFlags |= DFLAGS_HIDDEN;
                    }

                    for(i=0; i<AtaCtl->FindDelDev.WaitForPhysicalLink && i<30; i++) {
                        AtapiStallExecution(1000 * 1000);
                    }

                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                case  IOCTL_SCSI_MINIPORT_UNIATA_SET_MAX_MODE: {

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Set transfer mode\n"));

                    if(AtaCtl->SetMode.OrigMode != IOMODE_NOT_SPECIFIED) {
                        LunExt->OrigTransferMode = (UCHAR)(AtaCtl->SetMode.OrigMode);
                    }
                    if(AtaCtl->SetMode.MaxMode != IOMODE_NOT_SPECIFIED) {
                        LunExt->LimitedTransferMode = (UCHAR)(AtaCtl->SetMode.MaxMode);
                        if(LunExt->LimitedTransferMode > 
                           LunExt->OrigTransferMode) {
                            // check for incorrect value
                            LunExt->LimitedTransferMode =
                                LunExt->OrigTransferMode;
                        }
                    }
                    LunExt->TransferMode = min(LunExt->LimitedTransferMode, LunExt->OrigTransferMode);

                    LunExt->DeviceFlags |= DFLAGS_REINIT_DMA;  // force PIO/DMA reinit
                    if(AtaCtl->SetMode.ApplyImmediately) {
                        AtapiDmaInit__(deviceExtension, ldev);
                    }
/*                    deviceExtension->lun[ldev].TransferMode =
                    deviceExtension->lun[ldev].LimitedTransferMode = (UCHAR)(setTransferMode->Mode);*/
                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                case  IOCTL_SCSI_MINIPORT_UNIATA_GET_MODE: {

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Get transfer mode\n"));

                    AtaCtl->GetMode.OrigMode    = LunExt->OrigTransferMode;
                    AtaCtl->GetMode.MaxMode     = LunExt->LimitedTransferMode;
                    AtaCtl->GetMode.CurrentMode = LunExt->TransferMode;

                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                case  IOCTL_SCSI_MINIPORT_UNIATA_ADAPTER_INFO: {

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Get adapter info\n"));

                    AtaCtl->AdapterInfo.HeaderLength = offsetof(ADAPTERINFO, Chan);

                    if(len < AtaCtl->AdapterInfo.HeaderLength + sizeof(AtaCtl->AdapterInfo.Chan)) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: Buffer too small: %#x < %#x\n", len,
                            AtaCtl->AdapterInfo.HeaderLength + sizeof(AtaCtl->AdapterInfo.Chan)));
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    AtaCtl->AdapterInfo.DevID      = deviceExtension->DevID;
                    AtaCtl->AdapterInfo.RevID      = deviceExtension->RevID;
                    AtaCtl->AdapterInfo.slotNumber = deviceExtension->slotNumber;
                    AtaCtl->AdapterInfo.SystemIoBusNumber = deviceExtension->SystemIoBusNumber;
                    AtaCtl->AdapterInfo.DevIndex   = deviceExtension->DevIndex;
                    AtaCtl->AdapterInfo.Channel    = deviceExtension->Channel;
                    AtaCtl->AdapterInfo.HbaCtrlFlags = deviceExtension->HbaCtrlFlags;
                    AtaCtl->AdapterInfo.simplexOnly= deviceExtension->simplexOnly;
                    AtaCtl->AdapterInfo.MemIo      = FALSE;/*deviceExtension->MemIo;*/
                    AtaCtl->AdapterInfo.UnknownDev = deviceExtension->UnknownDev;
                    AtaCtl->AdapterInfo.MasterDev  = deviceExtension->MasterDev;
                    AtaCtl->AdapterInfo.MaxTransferMode = deviceExtension->MaxTransferMode;
                    AtaCtl->AdapterInfo.HwFlags    = deviceExtension->HwFlags;
                    AtaCtl->AdapterInfo.OrigAdapterInterfaceType = deviceExtension->OrigAdapterInterfaceType;
                    AtaCtl->AdapterInfo.BusInterruptLevel = deviceExtension->BusInterruptLevel;
                    AtaCtl->AdapterInfo.InterruptMode = deviceExtension->InterruptMode;
                    AtaCtl->AdapterInfo.BusInterruptVector = deviceExtension->BusInterruptVector;
                    AtaCtl->AdapterInfo.NumberChannels = deviceExtension->NumberChannels;
                    AtaCtl->AdapterInfo.AdapterInterfaceType = deviceExtension->AdapterInterfaceType;
                    if(deviceExtension->FullDevName) {
                        strncpy(AtaCtl->AdapterInfo.DeviceName, deviceExtension->FullDevName, 64);
                    }
                    AtaCtl->AdapterInfo.ChanInfoValid = FALSE;

                    RtlZeroMemory(&AtaCtl->AdapterInfo.Chan, sizeof(AtaCtl->AdapterInfo.Chan));

                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                case  IOCTL_SCSI_MINIPORT_UNIATA_RESETBB: {
                    
                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Forget BB list\n"));

                    ForgetBadBlocks(LunExt);

                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                case  IOCTL_SCSI_MINIPORT_UNIATA_RESET_DEVICE: {

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Reset device\n"));

                    if(bad_ldev) {
                        goto do_bus_reset;
                    } else {
                        UniataUserDeviceReset(deviceExtension, LunExt, AtaCtl->addr.PathId, ldev);
                    }

                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                default :
                    KdPrint2((PRINT_PREFIX "AtapiStartIo: invalid IoControl %#x for -UNIATA- signature\n",
                                AtaCtl->hdr.ControlCode ));
                    status = SRB_STATUS_INVALID_REQUEST;
                    break;
                }

            } else {
                KdPrint2((PRINT_PREFIX "AtapiStartIo: IoControl signature incorrect. Send %s, expected %s or %s\n",
                            ((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature,
                            "SCSIDISK", "-UNIATA-"));

                status = SRB_STATUS_INVALID_REQUEST;
                break;
            }

            break;
        } // end SRB_FUNCTION_IO_CONTROL
        default:

            KdPrint2((PRINT_PREFIX "AtapiStartIo: Unknown IOCTL\n"));
            // Indicate unsupported command.
            status = SRB_STATUS_INVALID_REQUEST;

//            break;

        } // end switch

complete_req:

        PathId   = Srb->PathId;
        TargetId = Srb->TargetId;
        Lun      = Srb->Lun;

        if (status != SRB_STATUS_PENDING) {

            KdPrint2((PRINT_PREFIX 
                       "AtapiStartIo: Srb %#x complete with status %#x\n",
                       Srb,
                       status));

            // Set status in SRB.
            Srb->SrbStatus = (UCHAR)status;

            AtapiDmaDBSync(chan, Srb);
            UniataRemoveRequest(chan, Srb);
            // Indicate command complete.
            ScsiPortNotification(RequestComplete,
                                 deviceExtension,
                                 Srb);

            // Remove current Srb & get next one
            if((Srb = UniataGetCurRequest(chan))) {
                AtaReq = (PATA_REQ)(Srb->SrbExtension);
                if(AtaReq->ReqState > REQ_STATE_QUEUED) {
                    // current request is under precessing, thus
                    // we should do nothing here
                    Srb = NULL;
                }
            }
            if(!chan) {
                //ASSERT(TopLevel);
            }
        }
        KdPrint2((PRINT_PREFIX "AtapiStartIo: next Srb %x\n", Srb));

    } while (Srb && (status != SRB_STATUS_PENDING));

    KdPrint2((PRINT_PREFIX "AtapiStartIo: query PORT for next request\n"));
    // Indicate ready for next request.
    ScsiPortNotification(NextRequest,
                         deviceExtension,
                         NULL);

    ScsiPortNotification(NextLuRequest,
                         deviceExtension, 
                         PathId,
                         TargetId,
                         Lun);

    return TRUE;

} // end AtapiStartIo__()


void
NTAPI
UniataInitAtaCommands()
{
    int i;
    UCHAR command;
    UCHAR flags;

    KdPrint2((PRINT_PREFIX "UniataInitAtaCommands:\n"));

    for(i=0; i<256; i++) {

        flags = 0;
        command = i;

        KdPrint2((PRINT_PREFIX "cmd %2.2x: ", command));

        switch(command) {
        case IDE_COMMAND_READ_DMA48:
        case IDE_COMMAND_READ_DMA_Q48:
        case IDE_COMMAND_READ_STREAM_DMA48:
        case IDE_COMMAND_READ_STREAM48:
        case IDE_COMMAND_WRITE_DMA48:
        case IDE_COMMAND_WRITE_DMA_Q48:
        case IDE_COMMAND_READ_DMA_Q:
        case IDE_COMMAND_READ_DMA:
        case IDE_COMMAND_WRITE_DMA:
        case IDE_COMMAND_WRITE_DMA_Q:
        case IDE_COMMAND_WRITE_STREAM_DMA48:
        case IDE_COMMAND_WRITE_STREAM48:
        case IDE_COMMAND_WRITE_FUA_DMA48:
        case IDE_COMMAND_WRITE_FUA_DMA_Q48:
        case IDE_COMMAND_READ_LOG_DMA48:
        case IDE_COMMAND_WRITE_LOG_DMA48:
        case IDE_COMMAND_TRUSTED_RCV_DMA:
        case IDE_COMMAND_TRUSTED_SEND_DMA:
            KdPrint2((PRINT_PREFIX "DMA "));
            flags |= ATA_CMD_FLAG_DMA;
        }

        switch(command) {
        case IDE_COMMAND_READ48:
        case IDE_COMMAND_READ_DMA48:
        case IDE_COMMAND_READ_DMA_Q48:
        case IDE_COMMAND_READ_MUL48:
        case IDE_COMMAND_READ_STREAM_DMA48:
        case IDE_COMMAND_READ_STREAM48:
        case IDE_COMMAND_WRITE48:
        case IDE_COMMAND_WRITE_DMA48:
        case IDE_COMMAND_WRITE_DMA_Q48:
        case IDE_COMMAND_WRITE_MUL48:
        case IDE_COMMAND_WRITE_STREAM_DMA48:
        case IDE_COMMAND_WRITE_STREAM48:
        case IDE_COMMAND_WRITE_FUA_DMA48:
        case IDE_COMMAND_WRITE_FUA_DMA_Q48:
        case IDE_COMMAND_WRITE_MUL_FUA48:
        case IDE_COMMAND_FLUSH_CACHE48:
        case IDE_COMMAND_VERIFY48:

            KdPrint2((PRINT_PREFIX "48 "));
            flags |= ATA_CMD_FLAG_48;
            /* FALL THROUGH */

        case IDE_COMMAND_READ:
        case IDE_COMMAND_READ_MULTIPLE:
        case IDE_COMMAND_READ_DMA:
        case IDE_COMMAND_READ_DMA_Q:
        case IDE_COMMAND_WRITE:
        case IDE_COMMAND_WRITE_MULTIPLE:
        case IDE_COMMAND_WRITE_DMA:
        case IDE_COMMAND_WRITE_DMA_Q:
        case IDE_COMMAND_FLUSH_CACHE:
        case IDE_COMMAND_VERIFY:

            KdPrint2((PRINT_PREFIX "LBA "));
            flags |= ATA_CMD_FLAG_LBAIOsupp;
        }

        flags |= ATA_CMD_FLAG_48supp;

        switch (command) {
        case IDE_COMMAND_READ:
            command = IDE_COMMAND_READ48; break;
        case IDE_COMMAND_READ_MULTIPLE:
            command = IDE_COMMAND_READ_MUL48; break;
        case IDE_COMMAND_READ_DMA:
            command = IDE_COMMAND_READ_DMA48; break;
        case IDE_COMMAND_READ_DMA_Q:
            command = IDE_COMMAND_READ_DMA_Q48; break;
        case IDE_COMMAND_WRITE:
            command = IDE_COMMAND_WRITE48; break;
        case IDE_COMMAND_WRITE_MULTIPLE:
            command = IDE_COMMAND_WRITE_MUL48; break;
        case IDE_COMMAND_WRITE_DMA:
            command = IDE_COMMAND_WRITE_DMA48; break;
        case IDE_COMMAND_WRITE_DMA_Q:
            command = IDE_COMMAND_WRITE_DMA_Q48; break;
        case IDE_COMMAND_FLUSH_CACHE:
            command = IDE_COMMAND_FLUSH_CACHE48; break;
    //    case IDE_COMMAND_READ_NATIVE_SIZE:
    //            command = IDE_COMMAND_READ_NATIVE_SIZE48; break;
        case IDE_COMMAND_SET_NATIVE_SIZE:
            command = IDE_COMMAND_SET_NATIVE_SIZE48; break;
        case IDE_COMMAND_VERIFY:
            command = IDE_COMMAND_VERIFY48; break;
        default:
            KdPrint2((PRINT_PREFIX "!28->48 "));
            flags &= ~ATA_CMD_FLAG_48supp;
        }

        KdPrint2((PRINT_PREFIX "\t -> %2.2x (%2.2x)\n", command, flags));
        AtaCommands48[i]   = command;
        AtaCommandFlags[i] = flags;
    }
} // end UniataInitAtaCommands()

/*++

Routine Description:

    Installable driver initialization entry point for system.

Arguments:

    Driver Object

Return Value:

    Status from ScsiPortInitialize()

--*/
extern "C"
ULONG
NTAPI
DriverEntry(
    IN PVOID DriverObject,
    IN PVOID Argument2
    )
{
    HW_INITIALIZATION_DATA_COMMON hwInitializationData;
    ULONG                  adapterCount;
    ULONG                  i, c, alt;
    ULONG                  statusToReturn, newStatus;
    PUNICODE_STRING        RegistryPath = (PUNICODE_STRING)Argument2;
    BOOLEAN                ReEnter = FALSE;
    WCHAR                  a;
    NTSTATUS               status;

    PCONFIGURATION_INFORMATION GlobalConfig = IoGetConfigurationInformation();
    BOOLEAN PrimaryClaimed   = FALSE;
    BOOLEAN SecondaryClaimed = FALSE;

    LARGE_INTEGER t0, t1;

    Connect_DbgPrint();
    KdPrint2((PRINT_PREFIX "%s", (PCCHAR)ver_string));
    a = (WCHAR)strlen(ver_string);

    g_opt_Verbose = (BOOLEAN)AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"PrintLogo", 0);
    if(g_opt_Verbose) {
        _PrintNtConsole("Universal ATA driver v 0." UNIATA_VER_STR "\n");
    }

    if(!SavedDriverObject) {
        SavedDriverObject = (PDRIVER_OBJECT)DriverObject;
        // we are here for the 1st time
        // init CrossNT and get OS version
        if(!NT_SUCCESS(status = CrNtInit(SavedDriverObject, RegistryPath))) {
            KdPrint(("UniATA Init: CrNtInit failed with status %#x\n", status));
            //HalDisplayString((PUCHAR)"DbgPrnHkInitialize: CrNtInit failed\n");
            return status;
        }
        KdPrint(("UniATA Init: OS ver %x.%x (%d)\n", MajorVersion, MinorVersion, BuildNumber));

        KeQuerySystemTime(&t0);
        do {
            KeQuerySystemTime(&t1);
        } while(t0.QuadPart == t1.QuadPart);
        t0=t1;
        g_Perf=0;
        do {
            KeQuerySystemTime(&t1);
            g_Perf++;
        } while(t0.QuadPart == t1.QuadPart);
        g_PerfDt = (ULONG)((t1.QuadPart - t0.QuadPart)/10);
        KdPrint(("Performance calibration: dt=%d, counter=%I64d\n", g_PerfDt, g_Perf ));
    } else {
        KdPrint(("UniATA Init: ReEnter\n"));
        ReEnter = TRUE;
    }

    // (re)read bad block list
    InitBadBlocks(NULL);

    if(!ReEnter) {
        // init ATA command translation table
        UniataInitAtaCommands();
        // get registry path to settings
        RtlCopyMemory(&SavedRegPath, RegistryPath, sizeof(UNICODE_STRING));
        SavedRegPath.Buffer = (PWCHAR)&SavedRegPathBuffer;
        SavedRegPath.Length = min(RegistryPath->Length, 255*sizeof(WCHAR));
        SavedRegPath.MaximumLength = 255*sizeof(WCHAR);
        RtlCopyMemory(SavedRegPath.Buffer, RegistryPath->Buffer, SavedRegPath.Length);
        SavedRegPath.Buffer[SavedRegPath.Length/sizeof(WCHAR)] = 0;

        if(AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"VirtualBox", 0)) {
            // adjust options for VirtualBox
            g_opt_WaitBusyCount = 20000;
            g_opt_WaitBusyDelay = 150;
            g_opt_WaitDrqDelay  = 100;
            g_opt_AtapiSendDisableIntr = 0;
            g_opt_AtapiDmaRawRead = FALSE;
        }

        g_opt_WaitBusyCount = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"WaitBusyCount", g_opt_WaitBusyCount); // 200 vs 20000
        g_opt_WaitBusyDelay = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"WaitBusyDelay", g_opt_WaitBusyDelay); // 10 vs 150
        g_opt_WaitDrqDelay  = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"WaitDrqDelay",  g_opt_WaitDrqDelay);  // 10 vs 100
        g_opt_AtapiSendDisableIntr = (BOOLEAN)AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"AtapiSendDisableIntr",  g_opt_AtapiSendDisableIntr);  // 1 vs 0
        g_opt_AtapiDmaRawRead      = (BOOLEAN)AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"AtapiDmaRawRead",       g_opt_AtapiDmaRawRead);       // 1 vs 0

    }

    if(WinVer_Id() >= WinVer_2k) {
        if(AtapiRegCheckParameterValue(NULL, L"Paramaters\\PnpInterface", L"1", 0)) {
            KdPrint(("UniATA: Behave as WDM, mlia (1)\n"));
            WinVer_WDM_Model = TRUE;
        }
        if(AtapiRegCheckParameterValue(NULL, L"Paramaters\\PnpInterface", L"5", 0)) {
            KdPrint(("UniATA: Behave as WDM, mlia (5)\n"));
            WinVer_WDM_Model = TRUE;
        }
    }

    SkipRaids = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"SkipRaids", 1);
    ForceSimplex = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"ForceSimplex", 0);
#ifdef _DEBUG
    g_LogToDisplay = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"LogToDisplay", 0);
#endif //_DEBUG

    statusToReturn = 0xffffffff;

    // Zero out structure.
    RtlZeroMemory(((PCHAR)&hwInitializationData), sizeof(hwInitializationData));

    // Set size of hwInitializationData.
    hwInitializationData.comm.HwInitializationDataSize =
      sizeof(hwInitializationData.comm) +
//      sizeof(hwInitializationData.nt4) +
      ((WinVer_Id() <= WinVer_NT) ? 0 : sizeof(hwInitializationData.w2k));
    KdPrint(("HwInitializationDataSize = %x\n", hwInitializationData.comm.HwInitializationDataSize));

    // Set entry points.
    hwInitializationData.comm.HwInitialize = (PHW_INITIALIZE)AtapiHwInitialize;
    hwInitializationData.comm.HwResetBus = (PHW_RESET_BUS)AtapiResetController;
    hwInitializationData.comm.HwStartIo = (PHW_STARTIO)AtapiStartIo;
    hwInitializationData.comm.HwInterrupt = (PHW_INTERRUPT)AtapiInterrupt;

    // Specify size of extensions.
    hwInitializationData.comm.DeviceExtensionSize     = sizeof(HW_DEVICE_EXTENSION);
    hwInitializationData.comm.SpecificLuExtensionSize = sizeof(HW_LU_EXTENSION);
    hwInitializationData.comm.SrbExtensionSize        = sizeof(ATA_REQ);

    // Indicate PIO device.
    hwInitializationData.comm.MapBuffers = TRUE;
    // Set PnP-specific API
    if(WinVer_Id() > WinVer_NT) {
        KdPrint(("set NeedPhysicalAddresses = TRUE\n"));
        hwInitializationData.comm.NeedPhysicalAddresses = TRUE;
        KdPrint(("set AtapiAdapterControl() ptr\n"));
        hwInitializationData.w2k.HwAdapterControl = (PHW_ADAPTER_CONTROL)AtapiAdapterControl;
    }

    KdPrint2((PRINT_PREFIX "\n\nATAPI IDE enum supported BusMaster Devices\n"));

    if(!ReEnter) {
        UniataEnumBusMasterController(DriverObject, Argument2);
    }

    // Look for legacy ISA-bridged PCI IDE controller (onboard)
    KdPrint2((PRINT_PREFIX "\n\nATAPI IDE: Look for legacy ISA-bridged PCI IDE controller (onboard)\n"));
    KdPrint2((PRINT_PREFIX "\n\nATAPI IDE: BMListLen %d\n", BMListLen));
    for (i=0; i <BMListLen; i++) {

        if(!BMList[i].MasterDev) {
            KdPrint2((PRINT_PREFIX "!BMList[i].MasterDev\n"));
            break;
        }
        if(AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"IgnoreIsaCompatiblePci", 0)) {
            break;
        }
        if(ReEnter) {
            KdPrint2((PRINT_PREFIX "ReEnter, skip it\n"));
            if(BMList[i].ChanInitOk & 0x03) {
                KdPrint2((PRINT_PREFIX "Already initialized, skip it\n"));
                statusToReturn =
                newStatus = STATUS_SUCCESS;
            }
            continue;
        }
        BMList[i].AltInitMasterDev = (UCHAR)0xff;

        if(GlobalConfig->AtDiskPrimaryAddressClaimed)
            PrimaryClaimed = TRUE;
        if(GlobalConfig->AtDiskSecondaryAddressClaimed)
            SecondaryClaimed = TRUE;

        if(g_opt_Verbose) {
            _PrintNtConsole("Init standard Dual-channel PCI ATA controller:");
        }


        for(alt = 0; alt < (ULONG)(WinVer_WDM_Model ? 1 : 2) ; alt++) {

            for(c=0; c<2; c++) {

                if(AtapiRegCheckDevValue(NULL, c, DEVNUM_NOT_SPECIFIED, L"IgnoreIsaCompatiblePci", 0)) {
                    break;
                }
                if(c==0) {
                    if(PrimaryClaimed) {
                        KdPrint2((PRINT_PREFIX "Primary already claimed\n"));
                        continue;
                    }
                } else
                if(c==1) {
                    if(SecondaryClaimed) {
                        KdPrint2((PRINT_PREFIX "Secondary already claimed\n"));
                        continue;
                    }
                }

                if((WinVer_Id() < WinVer_2k)) {
                    // do not even try if already claimed
                    if(c==0) {
                        GlobalConfig->AtDiskPrimaryAddressClaimed = FALSE;
                    } else
                    if(c==1) {
                        GlobalConfig->AtDiskSecondaryAddressClaimed = FALSE;
                    }
                }
                if(!WinVer_WDM_Model) {
                    hwInitializationData.comm.HwFindAdapter = UniataFindBusMasterController;
                } else {
                    // in WDM model things are different....
                    hwInitializationData.comm.HwFindAdapter = (c == 0) ?
                        UniataFindCompatBusMasterController1 : UniataFindCompatBusMasterController2;
                }
                hwInitializationData.comm.NumberOfAccessRanges = 6;
                hwInitializationData.comm.AdapterInterfaceType = Isa;

                if(!WinVer_WDM_Model) {
                    BMList[i].channel = (UCHAR)c;
                }

                KdPrint2((PRINT_PREFIX "Try init channel %d, method %d\n", c, alt));
                newStatus = ScsiPortInitialize(DriverObject,
                                               Argument2,
                                               &hwInitializationData.comm,
                                               (PVOID)(i | (alt ? 0x80000000 : 0)));
                KdPrint2((PRINT_PREFIX "ScsiPortInitialize Status %#x\n", newStatus));
                if (newStatus < statusToReturn) {
                    statusToReturn = newStatus;
                }
                if (newStatus == STATUS_SUCCESS) {
                    BMList[i].ChanInitOk |= 0x01 << c;
/*
                    if(BMList[i].MasterDev && (WinVer_Id() > WinVer_NT)) {
                        c = 1; // this will break our for()
                        BMList[i].ChanInitOk |= 0x01 << c;
                    }
*/
                }
            }
            if(WinVer_Id() >= WinVer_2k) {
                // the following doesn't work under higher OSes
                KdPrint2((PRINT_PREFIX "make still one attempt\n"));
                continue;
            }
            if(BMList[i].ChanInitOk & 0x03) {
                // under NT we receive status immediately, so
                // we can omit alternative init method id STATUS_SUCCESS returned
                KdPrint2((PRINT_PREFIX "Ok, no more retries required\n"));
                break;
            }
            // if (WinVer_Id() == WinVer_NT) and some error occured
            // try alternative init method
        } // for(alt...)
#if 0
        if(WinVer_WDM_Model) {
            hwInitializationData.comm.HwFindAdapter = UniataFindFakeBusMasterController;
            hwInitializationData.comm.NumberOfAccessRanges = 5;
            hwInitializationData.comm.AdapterInterfaceType = PCIBus;

            hwInitializationData.comm.VendorId             = BMList[i].VendorId;
            hwInitializationData.comm.VendorIdLength       = (USHORT) BMList[i].VendorIdLength;
            hwInitializationData.comm.DeviceId             = BMList[i].DeviceId;
            hwInitializationData.comm.DeviceIdLength       = (USHORT) BMList[i].DeviceIdLength;

            //BMList[i].channel = 0/*(UCHAR)c*/;

            KdPrint2((PRINT_PREFIX "Try init fake: %4.4s %4.4s \n",
                                   hwInitializationData.comm.VendorId,
                                   hwInitializationData.comm.DeviceId));
            newStatus = ScsiPortInitialize(DriverObject,
                                           Argument2,
                                           &hwInitializationData.comm,
                                           (PVOID)i);
            KdPrint2((PRINT_PREFIX "ScsiPortInitialize Status %#x\n", newStatus));
        }
#endif //0
        if(g_opt_Verbose) {
            if(BMList[i].ChanInitOk & 0x03) {
                _PrintNtConsole("  OK\n");
            } else {
                _PrintNtConsole("  failed\n");
            }
        }

    }

/*    KeBugCheckEx(0xc000000e,
                 (i << 16) | BMList[0].ChanInitOk,
                 c, 
                 newStatus, statusToReturn);*/

    // Look for PCI IDE controller
    KdPrint2((PRINT_PREFIX "\n\nATAPI IDE: Look for PCI IDE controller\n"));
    KdPrint2((PRINT_PREFIX "\n\nATAPI IDE: i %d, BMListLen %d\n", i, BMListLen));
    for (; i <BMListLen; i++) {

        if(AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"IgnoreNativePci", 0)) {
            break;
        }
/*        if(BMList[i].MasterDev)
            continue;*/
        if(g_opt_Verbose) {
            _PrintNtConsole("Init PCI ATA controller Vendor/Dev %4.4s//%4.4s at PCI Address %d:%d:%d",
                BMList[i].VendorId, BMList[i].DeviceId,
                BMList[i].busNumber,
                BMList[i].slotNumber % PCI_MAX_FUNCTION,
                (BMList[i].slotNumber / PCI_MAX_FUNCTION) % PCI_MAX_DEVICES);
        }

        hwInitializationData.comm.HwFindAdapter = UniataFindBusMasterController;
        hwInitializationData.comm.NumberOfAccessRanges = 6;
        hwInitializationData.comm.AdapterInterfaceType = PCIBus;

        hwInitializationData.comm.VendorId             = BMList[i].VendorId;
        hwInitializationData.comm.VendorIdLength       = (USHORT) BMList[i].VendorIdLength;
        hwInitializationData.comm.DeviceId             = BMList[i].DeviceId;
        hwInitializationData.comm.DeviceIdLength       = (USHORT) BMList[i].DeviceIdLength;

        BMList[i].channel = 0/*(UCHAR)c*/;

        KdPrint2((PRINT_PREFIX "Try init %4.4s %4.4s \n",
                               hwInitializationData.comm.VendorId,
                               hwInitializationData.comm.DeviceId));
        newStatus = ScsiPortInitialize(DriverObject,
                                       Argument2,
                                       &hwInitializationData.comm,
                                       (PVOID)i);
        KdPrint2((PRINT_PREFIX "ScsiPortInitialize Status %#x\n", newStatus));
        if(newStatus == STATUS_DEVICE_DOES_NOT_EXIST && BMList[i].NeedAltInit) {
            KdPrint2((PRINT_PREFIX "STATUS_DEVICE_DOES_NOT_EXIST, try workaround\n"));
            hwInitializationData.comm.AdapterInterfaceType = Isa;
            newStatus = ScsiPortInitialize(DriverObject,
                                           Argument2,
                                           &hwInitializationData.comm,
                                           (PVOID)(i | 0x80000000));
            KdPrint2((PRINT_PREFIX "ScsiPortInitialize Status %#x (2)\n", newStatus));
        }
        if (newStatus < statusToReturn)
            statusToReturn = newStatus;

        if(g_opt_Verbose) {
            if(newStatus == STATUS_SUCCESS) {
                _PrintNtConsole("  OK\n");
            } else {
                _PrintNtConsole("  failed\n");
            }
        }

    }

/*    KeBugCheckEx(0xc000000e,
                 i,
                 c, 
                 newStatus, statusToReturn);*/

    // --------------

    hwInitializationData.comm.VendorId             = 0;
    hwInitializationData.comm.VendorIdLength       = 0;
    hwInitializationData.comm.DeviceId             = 0;
    hwInitializationData.comm.DeviceIdLength       = 0;

    // The adapter count is used by the find adapter routine to track how
    // which adapter addresses have been tested.

    // Indicate 2 access ranges and reset FindAdapter.
    hwInitializationData.comm.NumberOfAccessRanges = 2;
    hwInitializationData.comm.HwFindAdapter = AtapiFindController;

    if(!AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"IgnoreIsa", 0)) {
        // Indicate ISA bustype.
        hwInitializationData.comm.AdapterInterfaceType = Isa;
        adapterCount = 0;

        // Call initialization for ISA bustype.
        KdPrint2((PRINT_PREFIX "\n\nATAPI IDE: Look for ISA Controllers\n"));
        newStatus =  ScsiPortInitialize(DriverObject,
                                        Argument2,
                                        &hwInitializationData.comm,
                                        &adapterCount);
        KdPrint2((PRINT_PREFIX "ScsiPortInitialize Status %#x\n", newStatus));
        if (newStatus < statusToReturn)
            statusToReturn = newStatus;
    }
    if(!AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"IgnoreMca", 0)) {
        // Set up for MCA
        KdPrint2((PRINT_PREFIX "\n\nATAPI IDE: Look for MCA Controllers\n"));
        hwInitializationData.comm.AdapterInterfaceType = MicroChannel;
        adapterCount = 0;

        newStatus =  ScsiPortInitialize(DriverObject,
                                        Argument2,
                                        &hwInitializationData.comm,
                                        &adapterCount);
        KdPrint2((PRINT_PREFIX "ScsiPortInitialize Status %#x\n", newStatus));
        if (newStatus < statusToReturn)
            statusToReturn = newStatus;
    }
    InDriverEntry = FALSE;

    KdPrint2((PRINT_PREFIX "\n\nLeave UNIATA MiniPort DriverEntry with status %#x\n", statusToReturn));

    return statusToReturn;

} // end DriverEntry()


PSCSI_REQUEST_BLOCK
NTAPI
BuildMechanismStatusSrb(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PSCSI_REQUEST_BLOCK srb;
    PCDB cdb;
    PATA_REQ AtaReq = (PATA_REQ)(Srb->SrbExtension);

    srb = &(deviceExtension->chan[GET_CHANNEL(Srb)].InternalSrb);

    RtlZeroMemory((PCHAR) srb, sizeof(SCSI_REQUEST_BLOCK));

    srb->PathId     = (UCHAR)(Srb->PathId);
    srb->TargetId   = (UCHAR)(Srb->TargetId);
    srb->Function   = SRB_FUNCTION_EXECUTE_SCSI;
    srb->Length     = sizeof(SCSI_REQUEST_BLOCK);

    // Set flags to disable synchronous negociation.
    srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;

    // Set timeout to 4 seconds.
    srb->TimeOutValue = 4;

    srb->CdbLength          = 6;
    srb->DataBuffer         = &(deviceExtension->chan[GET_CHANNEL(Srb)].MechStatusData);
    srb->DataTransferLength = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER);
    srb->SrbExtension       = AtaReq;

    // Set CDB operation code.
    cdb = (PCDB)srb->Cdb;
    cdb->MECH_STATUS.OperationCode       = SCSIOP_MECHANISM_STATUS;
    cdb->MECH_STATUS.AllocationLength[1] = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER);

    return srb;
} // end BuildMechanismStatusSrb()

#endif //UNIATA_CORE

PSCSI_REQUEST_BLOCK
NTAPI
BuildRequestSenseSrb (
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PSCSI_REQUEST_BLOCK srb;
    PCDB cdb;
    PATA_REQ AtaReq = (PATA_REQ)(Srb->SrbExtension);

    srb = &(deviceExtension->chan[GET_CHANNEL(Srb)].InternalSrb);

    RtlZeroMemory((PCHAR) srb, sizeof(SCSI_REQUEST_BLOCK));

    srb->PathId     = (UCHAR)(Srb->PathId);
    srb->TargetId   = (UCHAR)(Srb->TargetId);
    srb->Function   = SRB_FUNCTION_EXECUTE_SCSI;
    srb->Length     = sizeof(SCSI_REQUEST_BLOCK);

    // Set flags to disable synchronous negociation.
    srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;

    // Set timeout to 2 seconds.
    srb->TimeOutValue = 4;

    srb->CdbLength          = 6;
    srb->DataBuffer         = &(deviceExtension->chan[GET_CHANNEL(Srb)].MechStatusSense);
    srb->DataTransferLength = sizeof(SENSE_DATA);
    srb->SrbExtension       = AtaReq;

    // Set CDB operation code.
    cdb = (PCDB)srb->Cdb;
    cdb->CDB6INQUIRY.OperationCode    = SCSIOP_REQUEST_SENSE;
    cdb->CDB6INQUIRY.AllocationLength = sizeof(SENSE_DATA);

    return srb;
} // end BuildRequestSenseSrb()

#ifndef UNIATA_CORE

ULONG
NTAPI
AtapiRegCheckDevLunValue(
    IN PVOID HwDeviceExtension,
    IN PCWCH NamePrefix,
    IN ULONG chan,
    IN ULONG dev,
    IN PCWSTR Name,
    IN ULONG Default
    )
{
    WCHAR namex[160];
    ULONG val = Default;

    val = AtapiRegCheckParameterValue(
        HwDeviceExtension, NamePrefix, Name, val);

    if(chan != CHAN_NOT_SPECIFIED) {
        swprintf(namex, L"%s\\Chan_%1.1d", NamePrefix, chan);
        val = AtapiRegCheckParameterValue(
            HwDeviceExtension, namex, Name, val);
        if(dev != DEVNUM_NOT_SPECIFIED) {
            swprintf(namex, L"%s\\Chan_%1.1d\\%s", NamePrefix, chan, (dev & 0x01) ? L"Lun_1" : L"Lun_0");
            val = AtapiRegCheckParameterValue(
                HwDeviceExtension, namex, Name, val);
        }
    }
    return val;
} // end AtapiRegCheckDevLunValue()

ULONG
NTAPI
EncodeVendorStr(
   OUT PWCHAR Buffer,
    IN PUCHAR Str,
    IN ULONG  Length
    )
{
    ULONG i,j;
    WCHAR a;

    for(i=0, j=0; i<Length; i++, j++) {
        // fix byte-order
        a = Str[i ^ 0x01];
        if(!a) {
            Buffer[j] = 0;
            return j;
        } else
        if(a == ' ') {
            Buffer[j] = '_';
        } else
        if((a == '_') ||
           (a == '#') ||
           (a == '\\') ||
           (a == '\"') ||
           (a == '\'') ||
           (a <  ' ') ||
           (a >= 127)) {
            Buffer[j] = '#';
            j++;
            swprintf(Buffer+j, L"%2.2x", a);
            j++;
        } else {
            Buffer[j] = a;
        }
    }
    Buffer[j] = 0;
    return j;
} // end EncodeVendorStr()

ULONG
NTAPI
AtapiRegCheckDevValue(
    IN PVOID HwDeviceExtension,
    IN ULONG chan,
    IN ULONG dev,
    IN PCWSTR Name,
    IN ULONG Default
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
//    WCHAR name0[11];
//    WCHAR name1[11+4+5];
//    WCHAR name2[11+4+4+10];
//    WCHAR name3[11+4+4+5+20];
//    WCHAR name3[11+4+4+5+20+1];
    WCHAR namex[160];

    WCHAR namev[16];
    WCHAR named[16];
    WCHAR names[20];

    IN ULONG VendorID;
    IN ULONG DeviceID;
    IN ULONG SlotNumber;

    ULONG val = Default;

    KdPrint(( " Parameter %ws\n", Name));

    if(deviceExtension) {
        VendorID   =  deviceExtension->DevID        & 0xffff;
        DeviceID   = (deviceExtension->DevID >> 16) & 0xffff;
        SlotNumber = deviceExtension->slotNumber;
    } else {
        VendorID   = 0xffff;
        DeviceID   = 0xffff;
        SlotNumber = 0xffffffff;
    }

    val = AtapiRegCheckDevLunValue(
        HwDeviceExtension, L"Parameters", chan, dev, Name, val);

    if(deviceExtension) {
        if(deviceExtension->AdapterInterfaceType == PCIBus) {
            // PCI
            swprintf(namev, L"\\IDE_%d", deviceExtension->DevIndex);
            swprintf(namex, L"Parameters%s", namev);
            val = AtapiRegCheckDevLunValue(
                HwDeviceExtension, namex, chan, dev, Name, val);


            swprintf(namev, L"\\Ven_%4.4x", VendorID);
            swprintf(named, L"\\Dev_%4.4x", DeviceID);
            swprintf(names, L"\\Slot_%8.8x", SlotNumber);

            swprintf(namex, L"Parameters%s", namev);
            val = AtapiRegCheckDevLunValue(
                HwDeviceExtension, namex, chan, dev, Name, val);

            swprintf(namex, L"Parameters%s%s", namev, named);
            val = AtapiRegCheckDevLunValue(
                HwDeviceExtension, namex, chan, dev, Name, val);

            swprintf(namex, L"Parameters%s%s%s", namev, named, names);
            val = AtapiRegCheckDevLunValue(
                HwDeviceExtension, namex, chan, dev, Name, val);
        } else
        if(deviceExtension->AdapterInterfaceType == Isa) {
            // Isa
            swprintf(namev, L"\\IDE_%d", deviceExtension->DevIndex+BMListLen);
            swprintf(namex, L"Parameters%s", namev);
            val = AtapiRegCheckDevLunValue(
                HwDeviceExtension, namex, chan, dev, Name, val);

            swprintf(namev, L"\\ISA_%d", deviceExtension->DevIndex);
            swprintf(namex, L"Parameters%s", namev);
            val = AtapiRegCheckDevLunValue(
                HwDeviceExtension, namex, chan, dev, Name, val);

        } else
        if(deviceExtension->AdapterInterfaceType == MicroChannel) {
            // MicroChannel
            swprintf(namev, L"\\IDE_%d", deviceExtension->DevIndex+BMListLen+IsaCount);
            swprintf(namex, L"Parameters%s", namev);
            val = AtapiRegCheckDevLunValue(
                HwDeviceExtension, namex, chan, dev, Name, val);

            swprintf(namev, L"\\MCA_%d", deviceExtension->DevIndex);
            swprintf(namex, L"Parameters%s", namev);
            val = AtapiRegCheckDevLunValue(
                HwDeviceExtension, namex, chan, dev, Name, val);

        }
    }

    KdPrint(( " Parameter %ws = %#x\n", Name, val));
    return val;

} // end AtapiRegCheckDevValue()

/*
    The user must specify that Xxx is to run on the platform
    by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
    Services\UniATA\Xxx:REG_DWORD:Zzz.

    The user can override the global setting to enable or disable Xxx on a
    specific cdrom device by setting the key HKEY_LOCAL_MACHINE\System\
    CurrentControlSet\Services\UniATA\Parameters\Device<N>\Xxx:REG_DWORD to one or zero.

    If this registry value does not exist or contains the value zero then
    the timer to check for media change does not run.

    Arguments:

    RegistryPath - pointer to the unicode string inside
                   ...\CurrentControlSet\Services\UniATA
    DeviceNumber - The number of the HBA device object

    Returns:    Registry Key value
 */
ULONG
NTAPI
AtapiRegCheckParameterValue(
    IN PVOID HwDeviceExtension,
    IN PCWSTR PathSuffix,
    IN PCWSTR Name,
    IN ULONG Default
    )
{
#define ITEMS_TO_QUERY 2 // always 1 greater than what is searched 

//    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    NTSTATUS          status;
    LONG              zero = Default;

    RTL_QUERY_REGISTRY_TABLE parameters[ITEMS_TO_QUERY];

//    LONG              tmp = 0;
    LONG              doRun = Default;

    PUNICODE_STRING   RegistryPath = &SavedRegPath;

    UNICODE_STRING    paramPath;

    // <SavedRegPath>\<PathSuffix> -> <Name>
//    KdPrint(( "AtapiCheckRegValue: %ws -> %ws\n", PathSuffix, Name));
//    KdPrint(( "AtapiCheckRegValue: RegistryPath %ws\n", RegistryPath->Buffer));

    paramPath.Length = 0;
    paramPath.MaximumLength = RegistryPath->Length +
        (wcslen(PathSuffix)+2)*sizeof(WCHAR);
    paramPath.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, paramPath.MaximumLength);
    if(!paramPath.Buffer) {
        KdPrint(("AtapiCheckRegValue: couldn't allocate paramPath\n"));
        return Default;
    }

    RtlZeroMemory(paramPath.Buffer, paramPath.MaximumLength);
    RtlAppendUnicodeToString(&paramPath, RegistryPath->Buffer);
    RtlAppendUnicodeToString(&paramPath, L"\\");
    RtlAppendUnicodeToString(&paramPath, (PWCHAR)PathSuffix);

    // Check for the Xxx value.
    RtlZeroMemory(parameters, (sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY));

    parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
    parameters[0].Name          = (PWCHAR)Name;
    parameters[0].EntryContext  = &doRun;
    parameters[0].DefaultType   = REG_DWORD;
    parameters[0].DefaultData   = &zero;
    parameters[0].DefaultLength = sizeof(ULONG);

    status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE /*| RTL_REGISTRY_OPTIONAL*/,
                                    paramPath.Buffer, parameters, NULL, NULL);
    KdPrint(( "AtapiCheckRegValue: %ws -> %ws is %#x\n", PathSuffix, Name, doRun));

    ExFreePool(paramPath.Buffer);

    if(!NT_SUCCESS(status)) {
        doRun = Default;
    }

    return doRun;

#undef ITEMS_TO_QUERY

} // end AtapiRegCheckParameterValue()


SCSI_ADAPTER_CONTROL_STATUS
NTAPI
AtapiAdapterControl(
    IN PVOID HwDeviceExtension,
    IN SCSI_ADAPTER_CONTROL_TYPE ControlType,
    IN PVOID Parameters
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PSCSI_SUPPORTED_CONTROL_TYPE_LIST pControlTypeList;
    ULONG                numberChannels  = deviceExtension->NumberChannels;
    ULONG c;
    NTSTATUS status;

    KdPrint(( "AtapiAdapterControl: %#x\n", ControlType));

    switch(ControlType) {
        case ScsiQuerySupportedControlTypes: {
            BOOLEAN supportedTypes[ScsiAdapterControlMax] = {
                TRUE,       // ScsiQuerySupportedControlTypes
                TRUE,       // ScsiStopAdapter
                TRUE,       // ScsiRestartAdapter
                FALSE,      // ScsiSetBootConfig
                FALSE       // ScsiSetRunningConfig
            };

            ULONG lim = ScsiAdapterControlMax;
            ULONG i;

            pControlTypeList = (PSCSI_SUPPORTED_CONTROL_TYPE_LIST) Parameters;

            if(pControlTypeList->MaxControlType < lim) {
                lim = pControlTypeList->MaxControlType;
            }

            for(i = 0; i < lim; i++) {
                pControlTypeList->SupportedTypeList[i] = supportedTypes[i];
            }

            break;

        }
        case ScsiStopAdapter: {

            KdPrint(( "AtapiAdapterControl: ScsiStopAdapter\n"));
            // Shut down all interrupts on the adapter.  They'll get re-enabled
            // by the initialization routines.
            for (c = 0; c < numberChannels; c++) {
                AtapiResetController(deviceExtension, c);
                AtapiDisableInterrupts(deviceExtension, c);
            }
            if(deviceExtension->AdapterInterfaceType == PCIBus) {
                // we must never get here for non-PCI
                status = UniataDisconnectIntr2(HwDeviceExtension);
                BMList[deviceExtension->DevIndex].Isr2Enable = FALSE;
            }
            break;
        }
        case ScsiRestartAdapter: {

            KdPrint(( "AtapiAdapterControl: ScsiRestartAdapter\n"));
            // Enable all the interrupts on the adapter while port driver call
            // for power up an HBA that was shut down for power management

            AtapiChipInit(HwDeviceExtension, DEVNUM_NOT_SPECIFIED, CHAN_NOT_SPECIFIED);
            status = UniataConnectIntr2(HwDeviceExtension);
            for (c = 0; c < numberChannels; c++) {
                AtapiChipInit(HwDeviceExtension, DEVNUM_NOT_SPECIFIED, c);
                FindDevices(HwDeviceExtension, 0, c);
                AtapiEnableInterrupts(deviceExtension, c);
                AtapiHwInitialize__(deviceExtension, c);
            }
            if(deviceExtension->Isr2DevObj) {
                // we must never get here for non-PCI
                BMList[deviceExtension->DevIndex].Isr2Enable = TRUE;
            }

            break;
        }

        default: {
            KdPrint(( "AtapiAdapterControl: default => return ScsiAdapterControlUnsuccessful\n"));
            return ScsiAdapterControlUnsuccessful;
        }
    }

    return ScsiAdapterControlSuccess;
} // end AtapiAdapterControl()

#endif //UNIATA_CORE

extern "C"
NTHALAPI
VOID
NTAPI
HalDisplayString (
    PUCHAR String
    );

extern "C"
VOID
_cdecl
_PrintNtConsole(
    PCCH DebugMessage,
    ...
    )
{
    int len;
    UCHAR dbg_print_tmp_buff[512];
//    UNICODE_STRING msgBuff;
    va_list ap;
    va_start(ap, DebugMessage);

    len = _vsnprintf((PCHAR)&dbg_print_tmp_buff[0], 511, DebugMessage, ap);

    dbg_print_tmp_buff[511] = 0;

    HalDisplayString(dbg_print_tmp_buff);

    va_end(ap);

} // end PrintNtConsole()

