/*++

Copyright (c) 2002-2015 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 significantly changed/updated by
         Alter, Copyright (c) 2002-2014:

    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/SATA3) 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
ULONG  g_opt_WaitBusyLongCount = 2000; // 2000
ULONG  g_opt_WaitBusyLongDelay = 250;  // 250
ULONG  g_opt_MaxIsrWait = 40; //
BOOLEAN g_opt_AtapiSendDisableIntr = 0; // 0
BOOLEAN g_opt_AtapiDmaRawRead = 1; // 0
BOOLEAN hasPCI = FALSE;

ULONG g_opt_VirtualMachine = 0; // Auto

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

VOID
NTAPI
UniataUserDeviceReset(
    PHW_DEVICE_EXTENSION deviceExtension,
    PHW_LU_EXTENSION LunExt,
    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

#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;

    // Assume, proper drive is already seleted
    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);
            UniataNanoSleep(PIO0_TIMING);
        } else {
            break;
        }
    }
    if(i) {
        KdPrint2((PRINT_PREFIX "AtapiSuckPort2: overrun detected (%#x words)\n", i ));
    }
    return statusByte;
} // AtapiSuckPort2()

ULONG
DDKFASTAPI
AtapiSuckPortBuffer2(
    IN PHW_CHANNEL chan,
    IN PUSHORT Buffer,
    IN ULONG Count
    )
{
    UCHAR statusByte;
    ULONG i;
    USHORT data;
    BOOLEAN retry = FALSE;

    // Assume, proper drive is already seleted
    WaitOnBusyLong(chan);
    for (i = 0; i < Count; i++) {

        GetStatus(chan, statusByte);
        if (statusByte & IDE_STATUS_DRQ) {
            // Suck out any remaining bytes and throw away.
            data = AtapiReadPort2(chan, IDX_IO1_i_Data);
            (*Buffer) = data;
            Count--;
            Buffer++;
            UniataNanoSleep(PIO0_TIMING);
            retry = FALSE;
        } else {
            if(i<Count && !retry) {
                KdPrint2((PRINT_PREFIX "  wait...\n"));
                WaitForDrq(chan);
                retry = TRUE;
            }
            break;
        }
    }
    if(i) {
        KdPrint2((PRINT_PREFIX "AtapiSuckPortBuffer2: %#x words\n", i ));
        if(i==Count) {
            AtapiSuckPort2(chan);
        }
    }
    return i;
} // AtapiSuckPortBuffer2()

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

    GetStatus(chan, Status);
    for (i=0; i<g_opt_WaitBusyCount; i++) {
        if (Status & IDE_STATUS_BUSY) {
            AtapiStallExecution(g_opt_WaitBusyDelay);
            GetStatus(chan, Status);
            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<g_opt_WaitBusyLongCount; i++) {
        GetStatus(chan, Status);
        if (Status & IDE_STATUS_BUSY) {
            AtapiStallExecution(g_opt_WaitBusyLongDelay);
            continue;
        } else {
            break;
        }
    }
    return Status;
} // end WaitOnBusyLong()

UCHAR
DDKFASTAPI
WaitOnBaseBusy(
    IN PHW_CHANNEL   chan
    )
{
    ULONG i;
    UCHAR Status = IDE_STATUS_WRONG;
    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 == IDE_STATUS_WRONG) {
        return IDE_STATUS_WRONG;
    }
    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 == IDE_STATUS_WRONG) {
            // 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;

    if(chan->DeviceExtension->HwFlags & UNIATA_AHCI) {
        UniataAhciSoftReset(chan->DeviceExtension, chan->lChannel, DeviceNumber);
        return;
    }

    GetBaseStatus(chan, statusByte2);
    KdPrint2((PRINT_PREFIX "  statusByte2 %x:\n", statusByte2));
    SelectDrive(chan, DeviceNumber);
    if(chan->lun[DeviceNumber]->DeviceFlags & DFLAGS_MANUAL_CHS) {
        // For ESDI/MFM
        AtapiStallExecution(10000);
        for (i = 0; i < 1000; i++) {
            AtapiStallExecution(999);
        }
    } else {
        AtapiStallExecution(500);
        GetBaseStatus(chan, statusByte2);
        AtapiWritePort1(chan, IDX_IO1_o_Command, IDE_COMMAND_ATAPI_RESET);

        // Do not wait for BUSY assertion if it was initially set, jump to
        // BUSY release wait loop
        if(!(statusByte2 & IDE_STATUS_BUSY)) {
            // Wait for BUSY assertion, in some cases delay may occure
            // 100ms should be enough
            i = 10*1000;
            while (!(AtapiReadPort1(chan, IDX_IO1_i_Status) & IDE_STATUS_BUSY) &&
                   i--)
            {
                AtapiStallExecution(10);
            }
        }

        i = 30 * 1000;
        // 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 wait_flags
    )
{
    PHW_CHANNEL          chan = &(deviceExtension->chan[lChannel]);
    UCHAR                statusByte;
    ULONG i;
    PUCHAR plba;

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

    if(deviceExtension->HwFlags & UNIATA_AHCI) {
        //PIDE_AHCI_CMD  AHCI_CMD = &(chan->AhciCtlBlock->cmd);

        KdPrint3(("  (ahci)\n"));

        statusByte = UniataAhciSendPIOCommand(deviceExtension, lChannel, DeviceNumber,
            (PSCSI_REQUEST_BLOCK)NULL,
            NULL,
            0,
            command,
            lba, count,
            feature,
            0 /* ahci flags */ ,
            wait_flags,
            1000 /* timeout 1 sec */
            );

        return statusByte;
    }

    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|ATA_CMD_FLAG_FUA)) == ATA_CMD_FLAG_LBAIOsupp) &&
       CheckIfBadBlock(chan->lun[DeviceNumber], 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 (UniAta_need_lba48(command, lba, count,
        chan->lun[DeviceNumber]->IdentifyData.FeaturesSupport.Address48)) {

        KdPrint2((PRINT_PREFIX "  dev %#x USE_LBA_48\n", DeviceNumber ));
        /* 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: dev %#x USE_LBA48 (2)\n", DeviceNumber ));
        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 ||
        //   (chan->lun[DeviceNumber]->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(chan->lun[DeviceNumber]->DeviceFlags & DFLAGS_LBA_ENABLED) {
            //KdPrint2((PRINT_PREFIX "AtaCommand28: dev %#x USE_LBA\n", DeviceNumber ));
            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: dev %#x USE_CHS\n", DeviceNumber ));
            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 (wait_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 == IDE_STATUS_WRONG) {
                // 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;
        }
        UniataExpectChannelInterrupt(chan, 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 wait_flags
    )
{
    if(!(deviceExtension->HwFlags & UNIATA_AHCI)) {
        return AtaCommand48(deviceExtension, DeviceNumber, lChannel,
                            command,
                            (ULONG)sector | ((ULONG)cylinder << 8) | ((ULONG)(head & 0x0f) << 24),
                            count, feature, wait_flags);
    } else {
        return UniataAhciSendPIOCommand(deviceExtension, lChannel, DeviceNumber,
            (PSCSI_REQUEST_BLOCK)NULL,
            NULL,
            0,
            command,
            (ULONG)sector | ((ULONG)cylinder << 8) | ((ULONG)(head & 0x0f) << 24),
            count,
            feature,
            0 /* ahci flags */ ,
            wait_flags,
            1000 /* timeout 1 sec */
            );

    }
} // 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 IOMODE_NOT_SPECIFIED;
} // 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 IOMODE_NOT_SPECIFIED;
} // end AtaWmode()

LONG
NTAPI
AtaUmode(PIDENTIFY_DATA2 ident)
{
    if (!ident->UdmaModesValid)
        return IOMODE_NOT_SPECIFIED;
    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 IOMODE_NOT_SPECIFIED;
} // end AtaUmode()

LONG
NTAPI
AtaSAmode(PIDENTIFY_DATA2 ident) {
    if(!ident->SataCapabilities || 
       ident->SataCapabilities == 0xffff) {
        return IOMODE_NOT_SPECIFIED;
    }
    if(ident->SataCapabilities & ATA_SATA_GEN3) {
        return ATA_SA600;
    } else
    if(ident->SataCapabilities & ATA_SATA_GEN2) {
        return ATA_SA300;
    } else
    if(ident->SataCapabilities & ATA_SATA_GEN1) {
        return ATA_SA150;
    }
    return IOMODE_NOT_SPECIFIED;
} // end AtaSAmode()

#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

#if DBG
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()
#endif

VOID
NTAPI
UniataSnapAtaRegs(
    IN PHW_CHANNEL chan,
    IN ULONG DeviceNumber,
 IN OUT PIDEREGS_EX regs
    )
{
    if(chan->DeviceExtension->HwFlags & UNIATA_AHCI) {
        // AHCI
        UniataAhciSnapAtaRegs(chan, DeviceNumber, regs);
    } else {
        // SATA/PATA, assume drive is selected
        ULONG                j;
        UCHAR                statusByteAlt;

        if((regs->bOpFlags & ATA_FLAGS_48BIT_COMMAND) == 0) {
            for(j=IDX_IO1_i_Error; j<=IDX_IO1_i_Status; j++) {
                statusByteAlt = AtapiReadPort1(chan, IDX_IO1+j);
                ((PUCHAR)regs)[j-1] = statusByteAlt;
            }
            regs->bOpFlags = 0;
        } else {
            regs->bDriveHeadReg    = AtapiReadPort1(chan, IDX_IO1_i_DriveSelect);
            for(j=IDX_IO1_i_Error; j<IDX_IO1_i_DriveSelect; j++) {
                statusByteAlt = AtapiReadPort1(chan, IDX_IO1+j);
                ((PUCHAR)regs)[j-1] = statusByteAlt;
                statusByteAlt = AtapiReadPort1(chan, IDX_IO1+j);
                ((PUCHAR)regs)[j+8-1] = statusByteAlt;
            }
            regs->bCommandReg      = AtapiReadPort1(chan, IDX_IO1_i_Status);
        }
    }
    return;
} // end UniataSnapAtaRegs()

/*++

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;
    BOOLEAN              use_ahci = FALSE;
    PHW_LU_EXTENSION     LunExt = chan->lun[DeviceNumber];

    use_ahci = UniataIsSATARangeAvailable(deviceExtension, lChannel) &&
        (deviceExtension->HwFlags & UNIATA_AHCI);

    if(chan->ChannelCtrlFlags & CTRFLAGS_AHCI_PM) {
        if(chan->PmLunMap & (1 << DeviceNumber)) {
            // OK
        } else {
            KdPrint2((PRINT_PREFIX "IssueIdentify: PM empty port\n"));
            return FALSE;
        }
    } else
    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;
    }

    if(use_ahci) {
        statusByte = WaitOnBusyLong(chan);
#if DBG
        if(!chan->AhciInternalAtaReq) {
            KdPrint2((PRINT_PREFIX "!AhciInternalAtaReq\n"));
        }
#endif
    } else {
        SelectDrive(chan, DeviceNumber);
        AtapiStallExecution(10);
        statusByte = WaitOnBusyLong(chan);
        // Check that the status register makes sense.
        GetBaseStatus(chan, statusByte);
    }

    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 (dev %d)\n", DeviceNumber));
                    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);
                    if(statusByte == IDE_STATUS_WRONG) {
                        KdPrint2((PRINT_PREFIX "IssueIdentify: IDE_STATUS_WRONG (dev %d)\n", DeviceNumber));
                        return FALSE;
                    }

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

                SelectDrive(chan, DeviceNumber);
            } else {
                GetBaseStatus(chan, statusByte);
            }
            // 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) (dev %d)\n", DeviceNumber));
                // 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 (dev %d)\n", DeviceNumber));
                return FALSE;
            }
        }
    } else {
        KdPrint2((PRINT_PREFIX "IssueIdentify: Checking for ATAPI. Status (%#x)\n", statusByte));
        if(statusByte == IDE_STATUS_WRONG) {
            return FALSE;
        }
        //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(use_ahci) {
        statusByte = UniataAhciSendPIOCommand(HwDeviceExtension, lChannel, DeviceNumber,
            (PSCSI_REQUEST_BLOCK)NULL,
            (PUCHAR)(&deviceExtension->FullIdentifyData),
            DEV_BSIZE,
            Command,
            0, 0,
            0,
            0 /* ahci flags */ ,
            ATA_WAIT_INTR,
            1000 /* timeout 1 sec */
            );
        j = 9; // AHCI is rather different, skip loop at all
    } else
    if(LunExt->DeviceFlags & DFLAGS_MANUAL_CHS) {
        j = 9; // don't send IDENTIFY, assume it is not supported
        KdPrint2((PRINT_PREFIX "IssueIdentify: Manual CHS\n"));
        RtlZeroMemory(&(deviceExtension->FullIdentifyData), sizeof(deviceExtension->FullIdentifyData));
        RtlCopyMemory(&(deviceExtension->FullIdentifyData), &(LunExt->IdentifyData), sizeof(LunExt->IdentifyData));
    } else
    if(UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
        j = 4; // skip old-style checks
    } else {
        j = 0;
    }
    for (; j < 4*2; j++) {
        // Send IDENTIFY command.

        // Load CylinderHigh and CylinderLow with number bytes to transfer for old devices, use 0 for newer.
        
        statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel, Command, (j < 4) ? DEV_BSIZE : 0 /* cyl */, 0, 0, 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) (dev %d)\n", DeviceNumber));
                    // 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) (dev %d)\n", DeviceNumber));
                    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 chance.
            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;
    }

    if(use_ahci) {
        // everything should already be done by controller
    } else
    if(LunExt->DeviceFlags & DFLAGS_MANUAL_CHS) {
        j = 9; // don't send IDENTIFY, assume it is not supported
        KdPrint2((PRINT_PREFIX "IssueIdentify: Manual CHS (2)\n"));
        statusByte = WaitForDrq(chan);
        statusByte = WaitOnBusyLong(chan);
            KdPrint2((PRINT_PREFIX "IssueIdentify: statusByte %#x\n", statusByte));
        GetBaseStatus(chan, statusByte);
    } else {

        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 DBG
            if(atapiDev) {
              j = (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason) & ATAPI_IR_Mask);
              KdPrint3((PRINT_PREFIX "IssueIdentify: iReason %x\n", j));

              j =
                  AtapiReadPort1(chan, IDX_ATAPI_IO1_i_ByteCountLow);

              j |=
                  (USHORT)AtapiReadPort1(chan, IDX_ATAPI_IO1_i_ByteCountHigh) << 8;
              KdPrint3((PRINT_PREFIX "IssueIdentify: wCount %x\n", j));

            }
#endif //DBG
            // 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
            // specifying manual delay in the way you see below.
            ReadBuffer(chan, (PUSHORT)&deviceExtension->FullIdentifyData, 256, PIO0_TIMING);

            // 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, (PULONG)&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/%x\n", deviceExtension->FullIdentifyData.UltraDMAActive, deviceExtension->FullIdentifyData.UltraDMASupport));
    }
    KdPrint2((PRINT_PREFIX "SATA:  %x\n", deviceExtension->FullIdentifyData.SataEnable));
    KdPrint2((PRINT_PREFIX "SATA support: %x, CAPs %#x\n",
        deviceExtension->FullIdentifyData.SataSupport,
        deviceExtension->FullIdentifyData.SataCapabilities));

    LunExt->LimitedTransferMode =
    LunExt->OrigTransferMode =
        (UCHAR)ata_cur_mode_from_ident(&(deviceExtension->FullIdentifyData), IDENT_MODE_MAX);
    LunExt->TransferMode =
        (UCHAR)ata_cur_mode_from_ident(&(deviceExtension->FullIdentifyData), IDENT_MODE_ACTIVE);

    KdPrint2((PRINT_PREFIX "OrigTransferMode: %x, Active: %x\n", LunExt->OrigTransferMode, LunExt->TransferMode));
    KdPrint2((PRINT_PREFIX "Accoustic %d, cur %d\n",
        deviceExtension->FullIdentifyData.VendorAcoustic,
        deviceExtension->FullIdentifyData.CurrentAcoustic
        ));
    KdPrint2((PRINT_PREFIX "AdvPowerMode %d, cur %d\n",
        deviceExtension->FullIdentifyData.CfAdvPowerMode
        ));

    // 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",
                    DeviceNumber,
                    deviceExtension->FullIdentifyData.RemovableStatus));
        LunExt->DeviceFlags |= DFLAGS_REMOVABLE_DRIVE;
    }
    if(use_ahci) {
        // AHCI doesn't recommend using PIO and multiblock
        LunExt->MaximumBlockXfer = 0;
    } else
    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;

        KdPrint2((PRINT_PREFIX "PhysLogSectorSize %#x, %#x, offset %#x\n", 
                deviceExtension->FullIdentifyData.PhysLogSectorSize,
                deviceExtension->FullIdentifyData.LargeSectorSize,
                deviceExtension->FullIdentifyData.LogicalSectorOffset
                ));

        KdPrint2((PRINT_PREFIX "NV PM_Sup %d, PM_En %d, En %d, PM ver %#x ver %#x\n", 
                deviceExtension->FullIdentifyData.NVCache_PM_Supported,
                deviceExtension->FullIdentifyData.NVCache_PM_Enabled,
                deviceExtension->FullIdentifyData.NVCache_Enabled,
                deviceExtension->FullIdentifyData.NVCache_PM_Version,
                deviceExtension->FullIdentifyData.NVCache_Version
                ));

        KdPrint2((PRINT_PREFIX "R-rate %d\n",
                deviceExtension->FullIdentifyData.NominalMediaRotationRate
                ));

        // 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)) {
                    if(use_ahci) {
                        NativeNumOfSectors = chan->AhciInternalAtaReq->ahci.in_lba;
                    } else {
                        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)((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)) {
                            if(use_ahci) {
                                NativeNumOfSectors = chan->AhciInternalAtaReq->ahci.in_lba;
                            } else {
                                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;
                        }
                    }
                } // !error
            }
    
            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)) {
                    if(use_ahci) {
                        NativeNumOfSectors = chan->AhciInternalAtaReq->ahci.in_lba;
                    } else {
                        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;
                }
            }
        }

        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));
        }
        KdPrint2((PRINT_PREFIX "IssueIdentify: AtapiCmdSize %#x\n", deviceExtension->FullIdentifyData.AtapiCmdSize));
    } 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;
    PHW_LU_EXTENSION     LunExt;
//    ULONG i;
    UCHAR statusByte;
    UCHAR errorByte;

    LunExt = deviceExtension->chan[lChannel].lun[DeviceNumber];
    identifyData = &(LunExt->IdentifyData);

    if(LunExt->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
    )
{
    // keep only DFLAGS_HIDDEN flag
    LunExt->DeviceFlags &= DFLAGS_HIDDEN;
    LunExt->AtapiReadyWaitDelay = 0;
} // end UniataForgetDevice()


/*++

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

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(%x)\n", PathId));
    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 MaxLuns;
    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;
    USHORT tmp16;

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

    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]);
        MaxLuns = chan->NumberLuns;
        KdPrint2((PRINT_PREFIX "  CompleteType %#x, Luns %d, chan %#x, sptr %#x\n", CompleteType, MaxLuns, chan, &chan));
        //MaxLuns = (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, chan %#x\n", CurSrb, chan));
                // 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;
                    CurSrb->SrbExtension = NULL;
                    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;
                        KdPrint2((PRINT_PREFIX "  senseBuffer %#x, chan %#x\n", senseBuffer, chan));

                        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;
                    KdPrint2((PRINT_PREFIX "chan %#x\n", chan));

                    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.
        UniataExpectChannelInterrupt(chan, FALSE);
        chan->RDP = FALSE;
        chan->ChannelCtrlFlags = 0;
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);
        
        for (i = 0; i < MaxLuns; i++) {
            chan->lun[i]->PowerState = 0;
        }
        // Reset controller
        if(ChipFlags & UNIATA_AHCI) {
            KdPrint2((PRINT_PREFIX "  AHCI path\n"));
            if(UniataAhciChanImplemented(deviceExtension, j)) {
#if DBG
                UniataDumpAhciPortRegs(chan);
#endif
                AtapiDisableInterrupts(deviceExtension, j);
                UniataAhciReset(HwDeviceExtension, j);
            } else {
                KdPrint2((PRINT_PREFIX "  skip not implemented\n"));
                continue;
            }
        } else {
            KdPrint2((PRINT_PREFIX "  ATA path, chan %#x\n", chan));
            KdPrint2((PRINT_PREFIX "  disable intr (0)\n"));
            AtapiDisableInterrupts(deviceExtension, j);
            KdPrint2((PRINT_PREFIX "  done\n"));
            switch(VendorID) {
            case ATA_INTEL_ID: {
                ULONG mask;
                ULONG pshift;
                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 (MaxLuns > 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)) {
                    pshift = 8;
                } else {
                    pshift = 4;
                }
                while (timeout--) {
                    GetPciConfig2(0x92, tmp16);
                    if (((tmp16 >> pshift) & mask) == mask) {
                        GetBaseStatus(chan, statusByte);
                        if(statusByte != IDE_STATUS_WRONG) {
                            break;
                        }
                    }
                    AtapiStallExecution(10000);
                }
                break; }
            case ATA_SIS_ID:
            case ATA_NVIDIA_ID: {
                ULONG offs;
                ULONG Channel = deviceExtension->Channel + j;
                KdPrint2((PRINT_PREFIX "  SIS/nVidia\n"));
                if(!(ChipFlags & UNIATA_SATA)) {
                    goto default_reset;
                }
                offs = (ChipFlags & NV4OFF) ? 0x0440 : 0x0010;

                KdPrint2((PRINT_PREFIX "  disable Phy intr, offs %#x, c %u\n", offs, Channel));
                /* disable device and PHY state change interrupts */
                if(ChipFlags & NVQ) {
                    KdPrint2((PRINT_PREFIX "  NVQ, 32bits reg\n"));
                    AtapiWritePortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs+4, 
                        AtapiReadPortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs+4) & ((~(ULONG)0x0000000d) << (!Channel*16)) );
                } else {
                    AtapiWritePortEx1(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs+1, 
                        AtapiReadPortEx1(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs+1) & ((~(UCHAR)0x0d) << (!Channel*4)) );
                }
                tmp16 = UniataSataPhyEnable(HwDeviceExtension, j, 0/* dev0*/, UNIATA_SATA_RESET_ENABLE);

                KdPrint2((PRINT_PREFIX "  enable Phy intr, offs %#x\n", offs));
                /* enable device and PHY state change interrupts */
                if(ChipFlags & NVQ) {
                    AtapiWritePortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs+4, 
                        AtapiReadPortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs+4) | (((ULONG)0x0000000d) << (!Channel*16)) );
                } else {
                    AtapiWritePortEx1(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs+1, 
                        AtapiReadPortEx1(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0),offs+1) | (((UCHAR)0x0d) << (!Channel*4)) );
                }

                KdPrint2((PRINT_PREFIX "  dev status %#x\n", tmp16));
                if(tmp16 != IDE_STATUS_WRONG) {
                    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);
                }
            }
        } // ATA vs AHCI

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

            // Check if device present.
            KdPrint2((PRINT_PREFIX "  Chan %#x\n", chan));
            KdPrint2((PRINT_PREFIX "  Lun %#x\n", i));
            KdPrint2((PRINT_PREFIX "  Lun ptr %#x\n", chan->lun[i]));
            if (!(chan->lun[i]->DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                if(ChipFlags & UNIATA_AHCI) {
                    // everything is done in UniataAhciReset()
                    KdPrint2((PRINT_PREFIX "  device have gone\n"));
                    continue;
                }
#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(ChipFlags & UNIATA_AHCI) {
                    // everything is done in UniataAhciReset()
                    KdPrint2((PRINT_PREFIX "  found some device\n"));

                    if(!IssueIdentify(HwDeviceExtension,
                                  i, j,
                             ATAPI_DEVICE(chan, i) ?
                                  IDE_COMMAND_ATAPI_IDENTIFY : IDE_COMMAND_IDENTIFY,
                                  FALSE)) {
                        KdPrint2((PRINT_PREFIX "  identify failed !\n"));
                        UniataForgetDevice(chan->lun[i]);
                    }
                    continue;
                }
                if(!UniataAnybodyHome(HwDeviceExtension, j, i)) {
                    KdPrint2((PRINT_PREFIX "  device have gone\n"));
                    UniataForgetDevice(chan->lun[i]);
                }
#endif //NAVO_TEST
            }

            SelectDrive(chan, i);
            AtapiStallExecution(10);
            statusByte = WaitOnBusyLong(chan);
            statusByte = UniataIsIdle(deviceExtension, statusByte);
            if(statusByte == IDE_STATUS_WRONG) {
                KdPrint2((PRINT_PREFIX 
                           "no drive, status %#x\n",
                           statusByte));
                UniataForgetDevice(chan->lun[i]);
            } else
            // Check for ATAPI disk.
            if (ATAPI_DEVICE(chan, i)) {
                // 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) {
                    ULONG k;
                    k = UniataAnybodyHome(deviceExtension, j, i);
                    if(k == ATA_AT_HOME_HDD) {
                        // device reset in progress, perform additional wait
                        KdPrint2((PRINT_PREFIX "  long reset, wait up to 4.5 s\n"));
                        k = 30 * 1000;
                        while ((AtapiReadPort1(chan, IDX_IO1_i_Status) & IDE_STATUS_BUSY) &&
                               k--)
                        {
                            AtapiStallExecution(150);
                        }
                        KdPrint2((PRINT_PREFIX " exit after %u loops\n", k));
                        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
            chan->lun[i]->DeviceFlags |= DFLAGS_REINIT_DMA;
        }
#endif //0

        // Enable interrupts, note, 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);
    } // for(channel)
    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 = 0;
    UCHAR srbStatus = SRB_STATUS_SUCCESS;
    UCHAR scsiStatus;
    ULONG DeviceNumber = GET_CDEV(Srb);
    PHW_LU_EXTENSION     LunExt = chan->lun[DeviceNumber];

    // Read the error register.

    if(deviceExtension->HwFlags & UNIATA_AHCI) {
        PATA_REQ AtaReq = (PATA_REQ)(Srb->SrbExtension);
        if(AtaReq) {
            errorByte = AtaReq->ahci.in_error;
        } else {
        }
    } else {
        errorByte = AtapiReadPort1(chan, IDX_IO1_i_Error);
    }
    KdPrint2((PRINT_PREFIX 
               "MapError: Error register is %#x\n",
               errorByte));

    if (LunExt->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;
            }

            LunExt->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 (!(LunExt->DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED)){
                LunExt->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;
            }

            LunExt->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 (!(LunExt->DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED)){
                LunExt->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 (LunExt->ErrorCount >= MAX_ERRORS) {
//            deviceExtension->DWordIO = FALSE;

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

            LunExt->DeviceFlags &= ~DFLAGS_DWORDIO_ENABLED;
            LunExt->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 (LunExt->DeviceFlags & DFLAGS_DEVICE_PRESENT &&
                 !(LunExt->DeviceFlags & (DFLAGS_ATAPI_DEVICE | DFLAGS_MANUAL_CHS))) {

                statusByte = AtaCommand(deviceExtension, DeviceNumber, 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.
                    LunExt->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
    ->HwInitialize

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);
    }
    if(deviceExtension->MasterDev) {
        KdPrint2((PRINT_PREFIX "  mark chan %d of master controller [%x] as inited\n",
            deviceExtension->Channel, deviceExtension->DevIndex));
        BMList[deviceExtension->DevIndex].ChanInitOk |= 0x01 << deviceExtension->Channel;
    }

    /* 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;

    if((deviceExtension->HwFlags & UNIATA_AHCI) &&
       !UniataAhciChanImplemented(deviceExtension, lChannel)) {
        return;
    }

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

    for (i = 0; i < chan->NumberLuns; i++) {

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

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

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

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

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

            // If supported, setup Multi-block transfers.
            statusByte = AtaCommand(deviceExtension, i, 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, lChannel,
                                    IDE_COMMAND_SET_MULTIPLE, 0, 0, 0,
                                    0, 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, 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, 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, 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, lChannel,
                                        IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                        0, ATA_C_F_DIS_WCACHE, ATA_WAIT_BASE_READY);
                    LunExt->DeviceFlags &= ~DFLAGS_WCACHE_ENABLED;
                }

                if(LunExt->IdentifyData.FeaturesSupport.PowerMngt ||
                   LunExt->IdentifyData.FeaturesSupport.APM) {

                    if(LunExt->opt_AdvPowerMode) {
                        KdPrint2((PRINT_PREFIX "  Try Enable Adv. Power Mgmt\n"));
                        // setup APM
                        statusByte = AtaCommand(deviceExtension, i, lChannel,
                                            IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                            LunExt->opt_AdvPowerMode, ATA_C_F_ENAB_APM, ATA_WAIT_BASE_READY);
                        // Check for errors.
                        if (statusByte & IDE_STATUS_ERROR) {
                            KdPrint2((PRINT_PREFIX 
                                        "AtapiHwInitialize: Enable APM on Device %d failed\n",
                                        i));
                        }
                    } else {
                        KdPrint2((PRINT_PREFIX "  Disable Adv. Power Mgmt\n"));
                        statusByte = AtaCommand(deviceExtension, i, lChannel,
                                            IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                            0, ATA_C_F_DIS_APM, ATA_WAIT_BASE_READY);
                    }
                }
                if(LunExt->IdentifyData.FeaturesSupport.AutoAcoustic) {
                    if(LunExt->opt_AcousticMode) {
                        KdPrint2((PRINT_PREFIX "  Try Enable Acoustic Mgmt\n"));
                        // setup acoustic mgmt
                        statusByte = AtaCommand(deviceExtension, i, lChannel,
                                            IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                            LunExt->opt_AcousticMode, ATA_C_F_ENAB_ACOUSTIC, ATA_WAIT_BASE_READY);
                        // Check for errors.
                        if (statusByte & IDE_STATUS_ERROR) {
                            KdPrint2((PRINT_PREFIX 
                                        "AtapiHwInitialize: Enable Acoustic Mgmt on Device %d failed\n",
                                        i));
                        }
                    } else {
                        KdPrint2((PRINT_PREFIX "  Disable Acoustic Mgmt\n"));
                        statusByte = AtaCommand(deviceExtension, i, lChannel,
                                            IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                            0, ATA_C_F_DIS_ACOUSTIC, ATA_WAIT_BASE_READY);
                    }
                }
                if(LunExt->IdentifyData.FeaturesSupport.Standby) {
                    KdPrint2((PRINT_PREFIX "  Try init standby timer: %d\n"));
                    // setup standby timer
                    statusByte = AtaCommand(deviceExtension, i, lChannel,
                                        IDE_COMMAND_IDLE, 0, 0, 0,
                                        LunExt->opt_StandbyTimer, 0, ATA_WAIT_BASE_READY);
                    // Check for errors.
                    if (statusByte & IDE_STATUS_ERROR) {
                        KdPrint2((PRINT_PREFIX 
                                    "AtapiHwInitialize: standby timer on Device %d failed\n",
                                    i));
                    }
                }
            }

        } 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) || (PreferedMode > chan->MaxTransferMode)) {
            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->LimitedTransferMode =
        LunExt->TransferMode =
            (CHAR)PreferedMode;

        AtapiDmaInit__(deviceExtension, LunExt);

        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);
            if(statusByte == IDE_STATUS_WRONG) {
                waitCount = 0;
            }
            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
            if(statusByte != IDE_STATUS_WRONG) {
                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 lChannel = GET_CHANNEL(Srb);
    PHW_CHANNEL chan = &(deviceExtension->chan[lChannel]);
    ULONG DeviceNumber = GET_CDEV(Srb);
    PHW_LU_EXTENSION     LunExt = chan->lun[DeviceNumber];

    if (MechanismStatus) {
        LunExt->DiscsPresent = MechanismStatus->NumberAvailableSlots;
        if (LunExt->DiscsPresent > 1) {
            LunExt->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; _c++) {
        c = (_c+deviceExtension->FirstChannelToCheck) % deviceExtension->NumberChannels;

        if(c == lChannel) {
            continue;
        }

        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);
} // end AtapiCallBack_X()

#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];
    ULONG hIS;
    ULONG checked;

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

    if(deviceExtension->HwFlags & UNIATA_AHCI) {
        // AHCI may generate state change notification, never skip this check
        hIS = UniataAhciReadHostPort4(deviceExtension, IDX_AHCI_IS);
        KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): AHCI: hIS=%x cntrlr %#x chan %#x\n",hIS, deviceExtension->DevIndex, deviceExtension->Channel));
        if(!hIS) {
            return FALSE;
        }
        // assume all non-interrupted ports to be already checked
        checked = ~hIS;
        // assume all not implemented ports to be already checked
        checked |= ~deviceExtension->AHCI_PI;
    } else {
        checked = 0; // assume all ports are not checked
    }

    if(!deviceExtension->ExpectingInterrupt) {
        // if we do not expect interrupt, exit now,
        // but keep in mind that it can be unexpected one
        // Note: this is just a hint, not exact counter
        KdPrint2((PRINT_PREFIX "unexpected, 1st chance\n"));
        //deviceExtension->ExpectingInterrupt++;
        //return FALSE;
    }
    // clear this flag now, it can be set again in sub-calls
    deviceExtension->ExpectingInterrupt=0;


//    for(_c=0; _c<deviceExtension->NumberChannels; _c++) {
//        checked[_c] = (UCHAR)((hIS >> _c) & 0x01);
//    }

//    fc = 
    for(pass=0; pass<2; pass++) {
        //KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): pass %d\n", pass));
        if(status && pass) {
            // we catched some expected interrupts now.
            // do not touch unexpected until next ISR call
            break;
        }
        for(_c=0; _c<deviceExtension->NumberChannels; _c++) {

            c = (_c+deviceExtension->FirstChannelToCheck) % deviceExtension->NumberChannels;

            if((checked>>c) & 0x01)
                continue;

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

            checked |= (ULONG)1 << c;

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

            if(CrNtInterlockedExchangeAdd(&(deviceExtension->chan[c].DisableIntr), 0)) {
                // we get here on idle channels or when ISR is posted to DPC
                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 == INTERRUPT_REASON_UNEXPECTED) {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt(base): Catch unexpected\n"));
                    InterlockedExchange(&(deviceExtension->chan[c].CheckIntr), CHECK_INTR_IDLE);
                    //return TRUE;
                    status = TRUE;
                    continue;
                }
                // 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
    )
{
    // This ISR is intended to catch interrupts when we are already in other ISR instance
    // for the same device. This may happen when we have multiple channels,
    // especially on SMP machines

    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;
    ULONG hIS;
    ULONG checked;

    // 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;
    }

    if(deviceExtension->HwFlags & UNIATA_AHCI) {
        // AHCI may generate state change notification, never skip this check
        hIS = UniataAhciReadHostPort4(deviceExtension, IDX_AHCI_IS);
        KdPrint2((PRINT_PREFIX "AtapiInterrupt2: AHCI: hIS=%x cntrlr %#x chan %#x\n",hIS, deviceExtension->DevIndex, deviceExtension->Channel));
        if(!hIS) {
            return FALSE;
        }
        // assume all non-interrupted ports to be already checked
        checked = ~hIS; 
        // assume all not implemented ports to be already checked
        checked |= ~deviceExtension->AHCI_PI;

    } else {
        checked = 0; // assume all ports are not checked
    }
    if(!deviceExtension->ExpectingInterrupt) {
        KdPrint2((PRINT_PREFIX "AtapiInterrupt2: !deviceExtension->ExpectingInterrupt\n"));
        deviceExtension->ExpectingInterrupt++;
        return FALSE;
    }
    //deviceExtension->ExpectingInterrupt = 0;

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

        if((checked>>c) & 0x01)
            continue;

        checked |= (ULONG)1 << 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 == INTERRUPT_REASON_UNEXPECTED) {
                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;
    PHW_CHANNEL chan;
    //UCHAR statusByte;
    
    if(c >= deviceExtension->NumberChannels) {
        KdPrint2((PRINT_PREFIX "AtapiEnableInterrupts_%d: WRONG CHANNEL\n",c));
        return;
    }
    if((deviceExtension->HwFlags & UNIATA_AHCI) &&
       !UniataAhciChanImplemented(deviceExtension, c)) {
        KdPrint2((PRINT_PREFIX "AtapiEnableInterrupts_%d: not imp. CHANNEL\n",c));
        return;
    }

    chan = &(deviceExtension->chan[c]);
    KdPrint2((PRINT_PREFIX "AtapiEnableInterrupts_%d: %d\n",c, chan->DisableIntr));
    if(!InterlockedDecrement(&chan->DisableIntr)) {
        if(deviceExtension->HwFlags & UNIATA_AHCI) {
            UniataAhciWriteChannelPort4(chan, IDX_AHCI_P_IE,
                (ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
                 ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_INF | ATA_AHCI_P_IX_IF | ATA_AHCI_P_IX_OF |
                 ((/*ch->pm_level == */0) ? ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC : 0) |
                 ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC | /* DEBUG */ 
                 ATA_AHCI_P_IX_DI |
                 ATA_AHCI_P_IX_DP | ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB |
                 ATA_AHCI_P_IX_DS | ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR)
                );
        } else {
            //SelectDrive(chan, 0);
            //GetBaseStatus(chan, statusByte);
            AtapiWritePort1(chan, IDX_IO2_o_Control,
                                   IDE_DC_A_4BIT );
            //if(chan->NumberLuns) {
            //    SelectDrive(chan, 1);
            //    GetBaseStatus(chan, statusByte);
            //    AtapiWritePort1(chan, IDX_IO2_o_Control,
            //                           IDE_DC_A_4BIT );
            //    SelectDrive(chan, chan->cur_cdev);
            //}
        }
        chan->ChannelCtrlFlags &= ~CTRFLAGS_INTR_DISABLED;
    } else {
        if(deviceExtension->HwFlags & UNIATA_AHCI) {
            // keep interrupts disabled
            UniataAhciWriteChannelPort4(chan, IDX_AHCI_P_IE, 0);
        } else {
            AtapiWritePort1(chan, 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;
    PHW_CHANNEL chan;
    if(c >= deviceExtension->NumberChannels) {
        KdPrint2((PRINT_PREFIX "AtapiDisableInterrupts_%d: WRONG CHANNEL\n",c));
        return;
    }
    chan = &(deviceExtension->chan[c]);
    KdPrint2((PRINT_PREFIX "AtapiDisableInterrupts_%d: %d\n",c, chan->DisableIntr));
    // mark channel as busy
    if(InterlockedIncrement(&chan->DisableIntr)) {
        if(deviceExtension->HwFlags & UNIATA_AHCI) {
            UniataAhciWriteChannelPort4(chan, IDX_AHCI_P_IE, 0);
        } else {
            //SelectDrive(chan, 0);
            AtapiWritePort1(chan, IDX_IO2_o_Control,
                                   IDE_DC_DISABLE_INTERRUPTS /*| IDE_DC_A_4BIT*/ );
            //if(chan->NumberLuns) {
            //    SelectDrive(chan, 1);
            //    AtapiWritePort1(chan, IDX_IO2_o_Control,
            //                           IDE_DC_DISABLE_INTERRUPTS /*| IDE_DC_A_4BIT*/ );
            //    SelectDrive(chan, chan->cur_cdev);
            //}
        }
        chan->ChannelCtrlFlags |= CTRFLAGS_INTR_DISABLED;
    }

    return;
} // end AtapiDisableInterrupts()

VOID
UniataExpectChannelInterrupt(
    IN struct _HW_CHANNEL* chan,
    IN BOOLEAN Expecting
    )
{
    chan->ExpectingInterrupt = Expecting;
    if(Expecting) {
        chan->DeviceExtension->ExpectingInterrupt++;
    } else
    if(chan->DeviceExtension->ExpectingInterrupt) {
        chan->DeviceExtension->ExpectingInterrupt--;
    }
    return;
} // end UniataExpectChannelInterrupt()

/*
    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 = 0;
    ULONG slotNumber = deviceExtension->slotNumber;
    ULONG SystemIoBusNumber = deviceExtension->SystemIoBusNumber;
    ULONG ChipFlags = deviceExtension->HwFlags & CHIPFLAG_MASK;
    UCHAR Channel;
    UCHAR lChannel;
    BOOLEAN DmaTransfer = FALSE;
    BOOLEAN OurInterrupt = FALSE;
    BOOLEAN StatusValid = FALSE;
//    ULONG k;
    UCHAR interruptReason;
    BOOLEAN EarlyIntr = FALSE;
    BOOLEAN SingleBlockIntr = FALSE;

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

    lChannel = c;
    Channel = (UCHAR)(deviceExtension->Channel + lChannel);
    LunExt = chan->lun[chan->cur_cdev];

    //KdPrint2((PRINT_PREFIX "AtapiCheckInterrupt__ chan %#x:\n", chan));
    //KdPrint2((PRINT_PREFIX "AtapiCheckInterrupt__ (%d/%d):\n", Channel, chan->cur_cdev));

    if((ChipFlags & UNIATA_AHCI) &&
        UniataIsSATARangeAvailable(deviceExtension, lChannel)) {

        if(!UniataAhciChanImplemented(deviceExtension, lChannel)) {
            return OurInterrupt;
        }

        OurInterrupt = UniataAhciStatus(HwDeviceExtension, lChannel, DEVNUM_NOT_SPECIFIED);
        if((OurInterrupt == INTERRUPT_REASON_UNEXPECTED) &&
           (LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE)) {
            UniataAhciWaitCommandReady(chan, 2 /* ms */ );
            statusByte = (UCHAR)UniataAhciWaitReady(chan, 0 /* immediate */);
            if(!(statusByte & (IDE_STATUS_BUSY)) ) {
                KdPrint2((PRINT_PREFIX "ATAPI special case READY\n"));
                //deviceExtension->ExpectingInterrupt++; // will be updated in ISR on ReturnEnableInterrupts
                OurInterrupt = INTERRUPT_REASON_OUR;
            } else
            if((statusByte & (IDE_STATUS_BUSY | IDE_STATUS_DRDY)) == (IDE_STATUS_BUSY | IDE_STATUS_DRDY) ) {
                KdPrint2((PRINT_PREFIX "ATAPI special case pre ERR-READY\n"));
                OurInterrupt = INTERRUPT_REASON_OUR;
            } else
            if(statusByte & IDE_STATUS_ERROR) {
                KdPrint2((PRINT_PREFIX "ATAPI special case ERR-READY\n"));
                OurInterrupt = INTERRUPT_REASON_OUR;
            } else {
                KdPrint2((PRINT_PREFIX "ATAPI special case ? %x\n", statusByte));
                OurInterrupt = INTERRUPT_REASON_OUR;
            }
        }
        return OurInterrupt;
    }

    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;
    }

    // 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 INTERRUPT_REASON_IGNORE;
            }
            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 INTERRUPT_REASON_IGNORE;
            }
            break;
        case PRMIO: {
            ULONG stat_reg = (ChipFlags & PRG2) ? 0x60 : 0x6c;
            status = AtapiReadPortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressBM_0),0x40);
            AtapiWritePortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressBM_0),0x40, status);

            if(status & (1 << (Channel+1))) {
                // our
            } else {
                KdPrint2((PRINT_PREFIX "  Promise mio unexpected\n"));
                return INTERRUPT_REASON_IGNORE;
            }

            if(!(ChipFlags & UNIATA_SATA))
                break;

            pr_status = AtapiReadPortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressBM_0),stat_reg);
            AtapiWritePortEx4(chan, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressBM_0),stat_reg, (pr_status & (0x11 << Channel)));
            if(pr_status & (0x11 << Channel)) {
                // TODO: reset channel
                KdPrint2((PRINT_PREFIX "  Promise mio unexpected + reset req\n"));
                UniataSataEvent(deviceExtension, lChannel, UNIATA_SATA_EVENT_DETACH, 0);
            }
            if(!(status & (0x01 << Channel))) {
                // Connect event
                KdPrint2((PRINT_PREFIX "  Promise mio unexpected attach\n"));
                UniataSataEvent(deviceExtension, lChannel, UNIATA_SATA_EVENT_ATTACH, 0);
            }
            if(UniataSataClearErr(HwDeviceExtension, c, UNIATA_SATA_DO_CONNECT, 0)) {
                OurInterrupt = INTERRUPT_REASON_UNEXPECTED;
            } else {
                return INTERRUPT_REASON_IGNORE;
            }

            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, shift %x\n", pr_status, shift));

        /* 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 = INTERRUPT_REASON_UNEXPECTED;
            } else {
                return INTERRUPT_REASON_IGNORE;
            }
        }

        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 INTERRUPT_REASON_IGNORE;
            }
            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 INTERRUPT_REASON_IGNORE;
            }

            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 = INTERRUPT_REASON_UNEXPECTED;
                    }
                }
            }

            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 INTERRUPT_REASON_IGNORE;
            }
            if (!DmaTransfer) {
                KdPrint2((PRINT_PREFIX "  cmd our\n"));
                OurInterrupt = INTERRUPT_REASON_UNEXPECTED;
            }
            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 INTERRUPT_REASON_IGNORE;
        }
        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 = INTERRUPT_REASON_UNEXPECTED;
                }
                if(!(chan->ChannelCtrlFlags & CTRFLAGS_NO_SLAVE)) {
                    if(UniataSataClearErr(chan->DeviceExtension, chan->lChannel, UNIATA_SATA_IGNORE_CONNECT, 1)) {
                        OurInterrupt = INTERRUPT_REASON_UNEXPECTED;
                    }
                }
            }
        }
        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 = INTERRUPT_REASON_UNEXPECTED;
                }
            }
        }
    }
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 = INTERRUPT_REASON_UNEXPECTED;
            } else {
                KdPrint2((PRINT_PREFIX "  getting status...\n"));
                GetStatus(chan, statusByte);
                StatusValid = 1;
                KdPrint2((PRINT_PREFIX "  status %#x\n", statusByte));
                if(statusByte & IDE_STATUS_ERROR) {
                    KdPrint2((PRINT_PREFIX "  IDE_STATUS_ERROR -> our\n", statusByte));
                    OurInterrupt = INTERRUPT_REASON_UNEXPECTED;
                } else
                if ((statusByte & IDE_STATUS_DSC) &&
                    (LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) &&
                    (dma_status == BM_STATUS_ACTIVE)) {
                    KdPrint2((PRINT_PREFIX "  special case DMA + ATAPI + IDE_STATUS_DSC -> our\n", statusByte));
                    // some devices interrupts on each block transfer even in DMA mode
                    if(LunExt->TransferMode >= ATA_SDMA && LunExt->TransferMode <= ATA_WDMA2) {
                        KdPrint2((PRINT_PREFIX "  wait for completion\n"));
                        ///* clear interrupt and get status */
                        //GetBaseStatus(chan, statusByte);
                        //return INTERRUPT_REASON_IGNORE;
                        SingleBlockIntr = TRUE;
                    }
                } else {
                    return INTERRUPT_REASON_IGNORE;
                }
            }
        }
    } 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 = INTERRUPT_REASON_UNEXPECTED;
        }
    }
skip_dma_stat_check:
    if(!(ChipFlags & UNIATA_SATA) && chan->ExpectingInterrupt) {
        AtapiStallExecution(1);
    }

    /* if drive is busy it didn't interrupt */
    /* the exception is DCS + BSY state of ATAPI devices */
    if(!StatusValid) {
        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 == IDE_STATUS_WRONG) {
        // 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 INTERRUPT_REASON_IGNORE;
        }
        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 INTERRUPT_REASON_IGNORE;
        }
        if(!(LunExt->DeviceFlags & DFLAGS_INT_DRQ) && (statusByte & IDE_STATUS_DRQ)) {
            KdPrint3((PRINT_PREFIX "  unexpected DRQ, seems it is not our\n"));
            return INTERRUPT_REASON_IGNORE;
        }

        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);
            if(!(dma_status & BM_STATUS_ACTIVE)) {
                AtapiDmaDone(deviceExtension, DEVNUM_NOT_SPECIFIED ,lChannel, NULL);
            }
            KdPrint3((PRINT_PREFIX "  base status %#x (+BM_STATUS_INTR)\n", statusByte));
            return INTERRUPT_REASON_OUR;
        }

        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 INTERRUPT_REASON_IGNORE;
            }
        }

    }

    /* clear interrupt and get status */
    GetBaseStatus(chan, statusByte);
    KdPrint2((PRINT_PREFIX "  base status %#x\n", statusByte));
    if (statusByte == IDE_STATUS_WRONG) {
        // 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(!SingleBlockIntr && (!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);

            if(SingleBlockIntr) {
                KdPrint2((PRINT_PREFIX "  set REQ_STATE_ATAPI_EXPECTING_DATA_INTR2.\n"));
            } else {
                KdPrint2((PRINT_PREFIX "  set REQ_STATE_EARLY_INTR.\n"));
            }
            if(AtaReq) {
                AtaReq->ReqState = SingleBlockIntr ? REQ_STATE_ATAPI_EXPECTING_DATA_INTR2 : 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) & ATAPI_IR_Mask);
        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 INTERRUPT_REASON_OUR;

} // 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;

#ifdef DBG
    UCHAR Channel;
#endif //DBG
    UCHAR lChannel;
    UCHAR DeviceNumber;
    BOOLEAN DmaTransfer = FALSE;
    UCHAR error = 0;
    ULONG TimerValue = 1000;
    ULONG TotalTimerValue = 0;
#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;
    BOOLEAN NoRetry = FALSE;

    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;

#ifdef DBG
    Channel = (UCHAR)(deviceExtension->Channel + lChannel);

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

    if((chan->ChannelCtrlFlags & CTRFLAGS_DMA_ACTIVE) ||
       (AtaReq && (AtaReq->Flags & REQ_FLAG_DMA_OPERATION)) ||
       (deviceExtension->HwFlags & UNIATA_AHCI)) {
        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)(TargetId);
    LunExt = chan->lun[DeviceNumber];
    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_ATAPI_EXPECTING_DATA_INTR2:
    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) {
        UniataExpectChannelInterrupt(chan, FALSE);
        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:
    if(!InDpc && OldReqState != REQ_STATE_DPC_WAIT_BUSY1) {
        // we must block interrupts from this channel
        // If device generate new interrupt before we get to DPC,
        // ISR will assume, that it is NOT our interrupt
        AtapiDisableInterrupts(deviceExtension, lChannel);
        // We should not clean ExpectingInterrupt flag on channel, since it is used in DPC
    }
    // 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 */
    if(deviceExtension->HwFlags & UNIATA_AHCI) {
        UniataAhciEndTransaction(HwDeviceExtension, lChannel, DeviceNumber, srb);
        statusByte = (UCHAR)(AtaReq->ahci.in_status & IDE_STATUS_MASK);

        if(chan->AhciLastIS & ~(ATA_AHCI_P_IX_DHR | ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DS | ATA_AHCI_P_IX_SDB)) {
            KdPrint3((PRINT_PREFIX "Err intr (%#x), SE (%#x)\n",
                chan->AhciLastIS & ~(ATA_AHCI_P_IX_DHR | ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DS | ATA_AHCI_P_IX_SDB),
                chan->AhciLastSError));
            if(chan->AhciLastIS & ~ATA_AHCI_P_IX_OF) {
                //KdPrint3((PRINT_PREFIX "Err mask (%#x)\n", chan->AhciLastIS & ~ATA_AHCI_P_IX_OF));
                // We have some other error except Overflow
                // Just signal ERROR, operation will be aborted in ERROR branch.
                statusByte |= IDE_STATUS_ERROR;
                AtaReq->ahci.in_serror = chan->AhciLastSError;
                if(chan->AhciLastSError & (ATA_SE_HANDSHAKE_ERR | ATA_SE_LINKSEQ_ERR | ATA_SE_TRANSPORT_ERR | ATA_SE_UNKNOWN_FIS)) {
                    KdPrint2((PRINT_PREFIX "Unrecoverable\n"));
                    NoRetry = TRUE;
                }
            } else {
                // We have only Overflow. Abort operation and continue
#if DBG
                UniataDumpAhciPortRegs(chan);
#endif
                if(!UniataAhciAbortOperation(chan)) {
                    KdPrint2((PRINT_PREFIX "need UniataAhciReset\n"));
                }
#if DBG
                UniataDumpAhciPortRegs(chan);
#endif
                UniataAhciWaitCommandReady(chan, 10);
            }
        }

    } else {
        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(deviceExtension->HwFlags & UNIATA_AHCI) {
            KdPrint3((PRINT_PREFIX "  AHCI branch (IDE)\n"));
        } else
        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(deviceExtension->HwFlags & UNIATA_AHCI) {
            KdPrint3((PRINT_PREFIX "  AHCI branch (ATAPI)\n"));
        } else {
            interruptReason = (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason) & ATAPI_IR_Mask);
            KdPrint3((PRINT_PREFIX "AtapiInterrupt: iReason %x\n", interruptReason));
        }

        if (statusByte & IDE_STATUS_BUSY) {
        //if(chan->ChannelCtrlFlags & CTRFLAGS_DSC_BSY) {}
/*
#ifndef UNIATA_CORE
            // This is just workaround
            // We should DISABLE interrupts before entering WAIT state
            UniataExpectChannelInterrupt(chan, TRUE);
#endif //UNIATA_CORE
*/
            KdPrint3((PRINT_PREFIX "  BUSY on ATAPI device, waiting %d us\n", LunExt->AtapiReadyWaitDelay));
#ifndef UNIATA_CORE
            if(LunExt->AtapiReadyWaitDelay && (LunExt->AtapiReadyWaitDelay > g_opt_MaxIsrWait) && !InDpc && UseDpc) {
                TimerValue = LunExt->AtapiReadyWaitDelay;
                KdPrint2((PRINT_PREFIX "  too long wait: ISR -> DPC (0)\n"));
                AtaReq->ReqState = REQ_STATE_DPC_WAIT_BUSY0;
                goto CallTimerDpc2;
            }
#endif //UNIATA_CORE
            TimerValue = 10;
            for(k=20; k; k--) {
                GetBaseStatus(chan, statusByte);
                KdPrint3((PRINT_PREFIX "  status re-check %#x\n", statusByte));
                KdPrint3((PRINT_PREFIX "  Error reg (%#x)\n",
                            AtapiReadPort1(chan, IDX_ATAPI_IO1_i_Error)));
                if (!(statusByte & IDE_STATUS_BUSY)) {
                    KdPrint2((PRINT_PREFIX "  expecting intr + cleared BUSY\n"));
                    break;
                }
                TotalTimerValue += TimerValue;
                if(k <= 1) {
                    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
                    if(UseDpc) {
                        if(!LunExt->AtapiReadyWaitDelay) {
                            LunExt->AtapiReadyWaitDelay = TotalTimerValue*2/3;
                        }
                        goto CallTimerDpc2;
                    }
#endif //UNIATA_CORE
                }

                AtapiStallExecution(TimerValue);
                TimerValue += 10;
            }
            if(!LunExt->AtapiReadyWaitDelay) {
                LunExt->AtapiReadyWaitDelay = TotalTimerValue*2/3;
                KdPrint2((PRINT_PREFIX "  store AtapiReadyWaitDelay: %d\n", LunExt->AtapiReadyWaitDelay));
            }
            if (statusByte & IDE_STATUS_BUSY) {
                KdPrint3((PRINT_PREFIX "  expecting intr + BUSY (2), try DPC wait\n"));
                goto try_dpc_wait;
            }
        }
    }

    if(AtaReq && DmaTransfer && !(deviceExtension->HwFlags & UNIATA_AHCI)) {
        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)) {

        if(deviceExtension->HwFlags & UNIATA_AHCI) {
            error = AtaReq->ahci.in_error;
            // wait ready
#if DBG
            UniataDumpAhciPortRegs(chan);
#endif
            if(!UniataAhciAbortOperation(chan)) {
                KdPrint2((PRINT_PREFIX "need UniataAhciReset\n"));
            }
            // clear interrupts again
            UniataAhciWaitCommandReady(chan, 10);
#if DBG
            UniataDumpAhciPortRegs(chan);
#endif
            UniataAhciStatus(HwDeviceExtension, lChannel, DEVNUM_NOT_SPECIFIED);
            if(NoRetry) {
                AtaReq->retry += MAX_RETRIES;
                if(!error && (statusByte & IDE_STATUS_ERROR)) {
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: force error status\n"));
                    error |= IDE_STATUS_ERROR;
                }
            }
#if DBG
            UniataDumpAhciPortRegs(chan);
#endif
        } else {
            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(deviceExtension->HwFlags & UNIATA_AHCI) {
            KdPrint2((PRINT_PREFIX "  no wait ready after error\n"));
        } else
        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--) {
            GetBaseStatus(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)*/) {
                    KdPrint2((PRINT_PREFIX "Errors in DMA mode\n"));
                    if(AtaReq->retry < MAX_RETRIES) {
//fallback_pio:
                        if(!(deviceExtension->HwFlags & UNIATA_AHCI)) {
                            //AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
                            // Downrate will happen in AtapiDmaReinit(), try UDMA-2 for HDD only
                            AtaReq->Flags |= REQ_FLAG_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) & ATAPI_IR_Mask);
            KdPrint3((PRINT_PREFIX "AtapiInterrupt: ATAPI Error, int reason %x\n", interruptReason));

            if(UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
                if(deviceExtension->HwFlags & UNIATA_AHCI) {
                    // Do nothing here
                } else
                if(deviceExtension->HwFlags & UNIATA_SATA) {
                    UniataSataClearErr(HwDeviceExtension, lChannel, UNIATA_SATA_IGNORE_CONNECT, 0);
                }
            }

            if(DmaTransfer && (chan->lun[DeviceNumber]->TransferMode > ATA_UDMA2) &&
               ((error >> 4) == SCSI_SENSE_HARDWARE_ERROR)) {
                if(AtaReq->retry < MAX_RETRIES) {
//fallback_pio:
                    // Downrate will happen in AtapiDmaReinit(), use PIO immediately for ATAPI
                    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) {
#ifdef IO_STATISTICS
        KdPrint2((PRINT_PREFIX "Some higher mode doesn't work right :((\n"));
        KdPrint2((PRINT_PREFIX "Recovery stats[%d]: %d vs %d\n",
              AtaReq->retry,
              LunExt->RecoverCount[AtaReq->retry],
              LunExt->BlockIoCount
              ));
        LunExt->RecoverCount[AtaReq->retry]++;
        if(LunExt->RecoverCount[AtaReq->retry] >= chan->lun[DeviceNumber]->BlockIoCount/3 ||
           (deviceExtension->HwFlags & UNIATA_NO80CHK)
           ) {
#else
        if(deviceExtension->HwFlags & UNIATA_NO80CHK) {
#endif //IO_STATISTICS
            KdPrint2((PRINT_PREFIX "Limit transfer rate to %x\n", LunExt->TransferMode));
            LunExt->LimitedTransferMode =
                LunExt->TransferMode;
        }
    }
#ifdef IO_STATISTICS
    if(AtaReq->bcount) {
        // we need stats for Read/Write operations
        LunExt->BlockIoCount++;
    }
    LunExt->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) & ATAPI_IR_Mask);
        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 = ATAPI_IR_IO_toHost;

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

            } 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 {

            KdPrint2((PRINT_PREFIX "AtapiInterrupt: !DRQ, !BUSY, WordsLeft %#x\n", AtaReq->WordsLeft));
            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++) {
                    GetBaseStatus(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) ? ATAPI_IR_IO_toHost : ATAPI_IR_IO_toDev;
                }

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

    KdPrint2((PRINT_PREFIX "AtapiInterrupt: i-reason=%d, status=%#x\n", interruptReason, statusByte));
    if(deviceExtension->HwFlags & UNIATA_AHCI) {
        KdPrint2((PRINT_PREFIX "  AHCI path, WordsTransfered %x, WordsLeft %x\n", AtaReq->WordsTransfered, AtaReq->WordsLeft));
/*        if(chan->AhciLastIS & ATA_AHCI_P_IX_OF) {
            //status = SRB_STATUS_DATA_OVERRUN;
            DataOverrun = TRUE;
        } else {
            status = SRB_STATUS_SUCCESS;
        }*/
        if(AtaReq->WordsTransfered >= AtaReq->WordsLeft) {
            AtaReq->WordsLeft = 0;
        } else {
            AtaReq->WordsLeft -= AtaReq->WordsTransfered;
        }
        //if(AtaReq->WordsLeft && (status == SRB_STATUS_SUCCESS)) {
        //    status = SRB_STATUS_DATA_OVERRUN;
        //}
        status = SRB_STATUS_SUCCESS;
        chan->ChannelCtrlFlags &= ~CTRFLAGS_DMA_OPERATION;
        goto CompleteRequest;
    } else
    if (interruptReason == ATAPI_IR_COD_Cmd && (statusByte & IDE_STATUS_DRQ)) {
        // Write the packet.
        KdPrint3((PRINT_PREFIX "AtapiInterrupt: Writing Atapi packet.\n"));
        // Send CDB to device.
        WriteBuffer(chan, (PUSHORT)srb->Cdb, 
                          LunExt->IdentifyData.AtapiCmdSize ? 8 : 6,
                          0);
        AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_DATA_INTR;

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

        goto ReturnEnableIntr;

    } else if (interruptReason == ATAPI_IR_IO_toDev && (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 |=
                (USHORT)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);
            if(AtaReq->ReqState == REQ_STATE_ATAPI_EXPECTING_DATA_INTR2) {
                KdPrint2((PRINT_PREFIX 
                          "IdeIntr: DMA tmp INTR %#x vs %#x\n", AtaReq->WordsLeft, wordCount));
                if(AtaReq->WordsLeft > wordCount) {
                    AtaReq->WordsLeft -= wordCount;
                    AtaReq->WordsTransfered += wordCount;
                    AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_DATA_INTR;
                    goto ReturnEnableIntr;
                }
                dma_status = AtapiDmaDone(HwDeviceExtension, DEVNUM_NOT_SPECIFIED, lChannel, NULL/*srb*/);
            }
            AtaReq->WordsTransfered = AtaReq->WordsLeft;
            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 read %#x.\n",
                        interruptReason,
                        srb));

            if(!wordCount && atapiDev && 
                !(statusByte & (IDE_STATUS_ERROR | IDE_STATUS_BUSY)) &&
                 (statusByte & IDE_STATUS_DRQ)) {
                // this can be late Packet interrupt after packet sending
                KdPrint2((PRINT_PREFIX "  unreliable wordCount ?\n"));

                wordCount = AtapiSuckPortBuffer2(chan,
                                                 AtaReq->DataBuffer,
                                                 AtaReq->WordsLeft);
                KdPrint2((PRINT_PREFIX "  transferred %#x\n", wordCount));
                GetBaseStatus(chan, statusByte);
                KdPrint2((PRINT_PREFIX "  status %#x, statusByte\n"));
                if(wordCount) {
                    interruptReason = ATAPI_IR_IO_toHost;
#if DBG
                    UniataDumpATARegs(chan);
#endif
                }
                if(wordCount && AtaReq->WordsLeft) {
                    goto continue_atapi_pio_read;
                }
            }
            // Fail this request.
            status = SRB_STATUS_ERROR;
            if(!wordCount && atapiDev && (srb->Cdb[0] != SCSIOP_REQUEST_SENSE)) {
                // some devices feel bad after incorrect commands and may need reset
                KdPrint2((PRINT_PREFIX 
                          "AtapiInterrupt: Try ATAPI reset\n"));

                AtapiDisableInterrupts(deviceExtension, lChannel);
                AtapiSoftReset(chan, DeviceNumber);
                AtapiEnableInterrupts(deviceExtension, lChannel);
                status = SRB_STATUS_BUS_RESET;
                AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;

//                goto IntrPrepareResetController;
            }
            goto CompleteRequest;
        }
        // Advance data buffer pointer and bytes left.
        AtaReq->DataBuffer += wordCount;
        AtaReq->WordsLeft -= wordCount;
        AtaReq->WordsTransfered += wordCount;

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

        goto ReturnEnableIntr;

    } else if (interruptReason == ATAPI_IR_IO_toHost && (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);

            // Convert bytes to words.
            wordCount >>= 1;
            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)) {
            if(AtaReq->ReqState == REQ_STATE_ATAPI_EXPECTING_DATA_INTR2) {
                KdPrint2((PRINT_PREFIX 
                          "IdeIntr: DMA tmp INTR %#x vs %#x\n", AtaReq->WordsLeft, wordCount));
                if(AtaReq->WordsLeft > wordCount) {
                    AtaReq->WordsLeft -= wordCount;
                    AtaReq->WordsTransfered += wordCount;
                    AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_DATA_INTR;
                    goto ReturnEnableIntr;
                }
                dma_status = AtapiDmaDone(HwDeviceExtension, DEVNUM_NOT_SPECIFIED, lChannel, NULL/*srb*/);
            }
            //ASSERT(AtaReq->WordsLeft == wordCount);
            AtaReq->WordsTransfered = AtaReq->WordsLeft;
            AtaReq->WordsLeft = 0;
            status = SRB_STATUS_SUCCESS;
            chan->ChannelCtrlFlags &= ~CTRFLAGS_DMA_OPERATION;
            goto CompleteRequest;
        }
continue_read_drq:
        // 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);
                }

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

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

                if(statusByte & IDE_STATUS_BUSY) {
                    for (i = 0; i < 2; i++) {
                        AtapiStallExecution(10);
                        GetBaseStatus(chan, statusByte);
                        if (!(statusByte & IDE_STATUS_BUSY)) {
                            break;
                        }
                    }
                }
            } 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;
        }
continue_atapi_pio_read:
        // Advance data buffer pointer and bytes left.
        AtaReq->DataBuffer += wordCount;
        AtaReq->WordsLeft -= wordCount;
        AtaReq->WordsTransfered += 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;
                }

                GetStatus(chan, statusByte);
                if(!(statusByte & IDE_STATUS_BUSY)) {
                    // Assume command is completed if BUSY is cleared
                    // and all data read
                    // Optionally, we may receive COMPLETE interrupt later and
                    // treat it as unexpected
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: early complete ? status %x\n", statusByte));

                    status = SRB_STATUS_SUCCESS;
                    goto CompleteRequest;
                }

            } 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;
                GetStatus(chan, statusByte);
                if(!(statusByte & IDE_STATUS_BUSY)) {
                    // Assume command is completed if BUSY is cleared
                    // even if NOT all data read
                    // Optionally, we may receive COMPLETE interrupt later and
                    // treat it as unexpected
                    KdPrint2((PRINT_PREFIX "AtapiInterrupt: early complete + underrun ? status %x\n", statusByte));

                    status = SRB_STATUS_SUCCESS;
                    goto CompleteRequest;
                }
            } else {
                if(!atapiDev && !DataOverrun && (srb->SrbFlags & SRB_FLAGS_DATA_IN) &&
                    (statusByte == (IDE_STATUS_IDLE | IDE_STATUS_DRQ))) {
                    KdPrint2((PRINT_PREFIX "  HDD read data ready \n"));
                    goto continue_read_drq;
                }
            }
        }

        goto ReturnEnableIntr;

    } else if (interruptReason == (ATAPI_IR_IO_toHost | ATAPI_IR_COD_Cmd) && !(statusByte & IDE_STATUS_DRQ)) {

        KdPrint2((PRINT_PREFIX "AtapiInterrupt: interruptReason = CompleteRequest\n"));
        // Command complete. We exactly know this because of IReason.

        if(DmaTransfer) {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: CompleteRequest, was DmaTransfer\n"));
            AtaReq->WordsTransfered += AtaReq->WordsLeft;
            AtaReq->WordsLeft = 0;
        } else {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: CompleteRequest, was PIO\n"));

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

            KdPrint2((PRINT_PREFIX "AtapiInterrupt: wordCount %#x, WordsTransfered %#x\n", wordCount, AtaReq->WordsTransfered));

        }
        //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, srbstatus %x\n", status));
        // Check and see if we are processing our secret (mechanism status/request sense) srb

        if(AtaReq->WordsLeft && (status == SRB_STATUS_SUCCESS)) {
            KdPrint2((PRINT_PREFIX "WordsLeft %#x -> SRB_STATUS_DATA_OVERRUN\n", AtaReq->WordsLeft));
            status = SRB_STATUS_DATA_OVERRUN;
        }

        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) >
                        (FIELD_OFFSET (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) {

                        KdPrint3((PRINT_PREFIX "AtapiInterrupt: MechStatusRetryCount %#x\n", 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.
                        // Do-nothing call ?
                        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"));
                // Do-nothing call ?
                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++) {
                GetBaseStatus(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++) {
                    GetBaseStatus(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.
        UniataExpectChannelInterrupt(chan, FALSE);
        // clear this flag now, it can be set again in sub-calls
        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) {
                //if(!(deviceExtension->HwFlags & UNIATA_AHCI) && !atapiDev) {
                //    // This should be set in UniataAhciEndTransaction() for AHCI
                //    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"));

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

                if(!AtapiStringCmp( (PCHAR)(((PSRB_IO_CONTROL)(srb->DataBuffer))->Signature),"SCSIDISK",sizeof("SCSIDISK")-1)) {

                    PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
                    // 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) {
                        PIDEREGS_EX regs = (PIDEREGS_EX)&(cmdOutParameters->bBuffer);

                        regs->bOpFlags = 0;
                        UniataSnapAtaRegs(chan, 0, regs);

                        regs->bCommandReg = SMART_CMD;
                        regs->bFeaturesReg = RETURN_SMART_STATUS;

                        cmdOutParameters->cBufferSize = 8;
                    }
                    chan->SmartCommand = 0; // cleanup after execution
                }
                // Indicate command complete.
                goto IntrCompleteReq;
            }

        } else {

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

        if (chan->RDP) {
            // Check DSC
            for (i = 0; i < 5; i++) {
                GetBaseStatus(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));

        if(OldReqState == REQ_STATE_DPC_WAIT_BUSY0 &&
           AtaReq->WordsLeft == 0) {
            KdPrint2((PRINT_PREFIX "AtapiInterrupt: pending WAIT_BUSY0. Complete.\n"));
            status = SRB_STATUS_SUCCESS;
            chan->ChannelCtrlFlags &= ~CTRFLAGS_DMA_OPERATION;
            goto CompleteRequest;
        }
    }

ReturnEnableIntr:

    KdPrint2((PRINT_PREFIX "AtapiInterrupt: ReturnEnableIntr\n",srb));
    //UniataExpectChannelInterrupt(chan, TRUE); // device may interrupt
    deviceExtension->ExpectingInterrupt = TRUE;
    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,
    IN ULONG targetId // assume it is always valid
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    ULONG                c               ; // = GET_CHANNEL(Srb); may be invalid
    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;
    ULONG DeviceNumber;

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

    c = targetId / deviceExtension->NumberLuns;
    DeviceNumber = targetId % deviceExtension->NumberLuns;
    KdPrint2((PRINT_PREFIX "  c %d, dev %d\n", c, DeviceNumber));

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

    chan->SmartCommand = regs->bFeaturesReg;

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

        if(Srb->DataTransferLength < sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDOUTPARAMS) + READ_ATTRIBUTE_BUFFER_SIZE - 1) {
            KdPrint2((PRINT_PREFIX 
                        "IdeSendSmartCommand: wrong buffer size\n"));
            return SRB_STATUS_DATA_OVERRUN;
        }

        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, DeviceNumber, 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:
    case AUTO_OFFLINE:

        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, DeviceNumber, 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.
            UniataExpectChannelInterrupt(chan, TRUE); // device may 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,
    ULONGLONG            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 = GET_CDEV(Srb);;
    ULONGLONG            startingSector=0;
    ULONG                max_bcount;
    ULONG                wordCount = 0;
    UCHAR                statusByte,statusByte2;
    UCHAR                cmd;
    ULONGLONG            lba;
    BOOLEAN              use_dma = FALSE;
    ULONG                fis_size;

    AtaReq->Flags |= REQ_FLAG_REORDERABLE_CMD;
    LunExt = chan->lun[DeviceNumber];

    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 = (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 %I64x, 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.
            switch(Srb->Cdb[0]) {
            case SCSIOP_READ:
            case SCSIOP_WRITE:
                MOV_DD_SWP(startingSector, ((PCDB)Srb->Cdb)->CDB10.LBA);
                MOV_SWP_DW2DD(AtaReq->bcount, ((PCDB)Srb->Cdb)->CDB10.TransferBlocks);
                break;
            case SCSIOP_READ12:
            case SCSIOP_WRITE12:
                MOV_DD_SWP(startingSector, ((PCDB)Srb->Cdb)->CDB12READWRITE.LBA);
                MOV_DD_SWP(AtaReq->bcount, ((PCDB)Srb->Cdb)->CDB12READWRITE.NumOfBlocks);
                break;
            case SCSIOP_READ16:
            case SCSIOP_WRITE16:
                MOV_QD_SWP(startingSector, ((PCDB)Srb->Cdb)->CDB16READWRITE.LBA);
                MOV_DD_SWP(AtaReq->bcount, ((PCDB)Srb->Cdb)->CDB16READWRITE.NumOfBlocks);
                break;
            }
            KdPrint2((PRINT_PREFIX "IdeReadWrite (Orig REQ): Starting sector %I64x, 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 %I64x, 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(deviceExtension->HwFlags & UNIATA_AHCI) {
            UniataAhciSetupCmdPtr(AtaReq);
            if(!AtapiDmaSetup(HwDeviceExtension, DeviceNumber, lChannel, Srb,
                          (PUCHAR)(AtaReq->DataBuffer),
                          AtaReq->bcount * DEV_BSIZE)) {
                KdPrint3((PRINT_PREFIX "IdeReadWrite: AHCI !DMA\n"));
                return SRB_STATUS_ERROR;
            }
        } else
        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;
            }
        }

        if(deviceExtension->HwFlags & UNIATA_AHCI) {
            KdPrint2((PRINT_PREFIX "IdeReadWrite: setup AHCI FIS\n"));
            RtlZeroMemory(&(AtaReq->ahci.ahci_cmd_ptr->cfis), sizeof(AtaReq->ahci_cmd0.cfis));

            fis_size = UniataAhciSetupFIS_H2D(deviceExtension, DeviceNumber, lChannel,
                   &(AtaReq->ahci.ahci_cmd_ptr->cfis[0]),
                    (AtaReq->Flags & REQ_FLAG_READ) ? IDE_COMMAND_READ_DMA : IDE_COMMAND_WRITE_DMA,
                    lba,
                     (USHORT)(AtaReq->bcount),
                    0
                    /*,(AtaReq->Flags & REQ_FLAG_READ) ? 0 : ATA_AHCI_CMD_WRITE*/
                    );

            if(!fis_size) {
                KdPrint3((PRINT_PREFIX "IdeReadWrite: AHCI !FIS\n"));
                return SRB_STATUS_ERROR;
            }

            AtaReq->ahci.io_cmd_flags = UniAtaAhciAdjustIoFlags(0, (AtaReq->Flags & REQ_FLAG_READ) ? 0 : ATA_AHCI_CMD_WRITE, fis_size, DeviceNumber);
            KdPrint2((PRINT_PREFIX "IdeReadWrite ahci io flags %x: \n", AtaReq->ahci.io_cmd_flags));
        }

        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, LunExt, 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;
        }
    }

    // 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(use_dma) {
        chan->ChannelCtrlFlags |= CTRFLAGS_DMA_OPERATION;
    } else {
        chan->ChannelCtrlFlags &= ~CTRFLAGS_DMA_OPERATION;
    }

    if(deviceExtension->HwFlags & UNIATA_AHCI) {
        // AHCI doesn't distinguish DMA and PIO
        //AtapiDmaStart(HwDeviceExtension, DeviceNumber, lChannel, Srb);
        UniataAhciBeginTransaction(HwDeviceExtension, lChannel, DeviceNumber, Srb);
        UniataExpectChannelInterrupt(chan, TRUE); // device may interrupt
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);
        return SRB_STATUS_PENDING;
    }

    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 != IDE_STATUS_WRONG) {
            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 == IDE_STATUS_WRONG) {

        if(statusByte == IDE_STATUS_WRONG) {
            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.
        UniataExpectChannelInterrupt(chan, FALSE);
        InterlockedExchange(&(chan->CheckIntr),
                                      CHECK_INTR_IDLE);

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

        return (statusByte == IDE_STATUS_WRONG) ? SRB_STATUS_ERROR : SRB_STATUS_TIMEOUT;
    }

    UniataExpectChannelInterrupt(chan, 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;
    AtaReq->WordsTransfered += 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);
    ULONG                DeviceNumber = GET_CDEV(Srb);
    UCHAR                statusByte;
    ULONGLONG            startingSector=0;
    ULONG                max_bcount;
    ULONGLONG            sectors;
    ULONGLONG            endSector;
    ULONG                sectorCount=0;
    ULONGLONG            lba;

    LunExt = chan->lun[DeviceNumber];
    // 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.
    switch(Srb->Cdb[0]) {
    case SCSIOP_VERIFY:
        MOV_DD_SWP(startingSector, ((PCDB)Srb->Cdb)->CDB10.LBA);
        MOV_SWP_DW2DD(sectorCount, ((PCDB)Srb->Cdb)->CDB10.TransferBlocks);
        break;
    case SCSIOP_VERIFY12:
        MOV_DD_SWP(startingSector, ((PCDB)Srb->Cdb)->CDB12READWRITE.LBA);
        MOV_DD_SWP(sectorCount, ((PCDB)Srb->Cdb)->CDB12READWRITE.NumOfBlocks);
        break;
    case SCSIOP_VERIFY16:
        MOV_QD_SWP(startingSector, ((PCDB)Srb->Cdb)->CDB16READWRITE.LBA);
        MOV_DD_SWP(sectorCount, ((PCDB)Srb->Cdb)->CDB16READWRITE.NumOfBlocks);
        break;
    }

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

    endSector = startingSector + sectorCount;

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

    if (endSector > sectors) {

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

        sectorCount = (ULONG)(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, LunExt->Lun, GET_CHANNEL(Srb),
                 IDE_COMMAND_VERIFY, lba,
                 (USHORT)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);
    PHW_LU_EXTENSION     LunExt;
    //ULONG                ldev = GET_LDEV(Srb);
    ULONG                DeviceNumber = GET_CDEV(Srb);
    ULONG flags;
    UCHAR statusByte,statusByte0,byteCountLow,byteCountHigh;
    BOOLEAN use_dma = FALSE;
    BOOLEAN dma_reinited = FALSE;
    BOOLEAN retried = FALSE;
    ULONG                fis_size;
    UCHAR FeatureReg=0;

    LunExt = chan->lun[DeviceNumber];

    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_WRITE16) {
            KdPrint(("Write16, LBA %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
                     Cdb->CDB16READWRITE.LBA[0],
                     Cdb->CDB16READWRITE.LBA[1],
                     Cdb->CDB16READWRITE.LBA[2],
                     Cdb->CDB16READWRITE.LBA[3],
                     Cdb->CDB16READWRITE.LBA[4],
                     Cdb->CDB16READWRITE.LBA[5],
                     Cdb->CDB16READWRITE.LBA[6],
                     Cdb->CDB16READWRITE.LBA[7]
                     ));
        } 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, Cdb %x\n", &(Srb->Cdb)));

        switch (Srb->Cdb[0]) {
        case SCSIOP_RECEIVE:
        case SCSIOP_SEND:
        case SCSIOP_READ:
        case SCSIOP_WRITE:
        case SCSIOP_READ12:
        case SCSIOP_WRITE12:
        case SCSIOP_READ16:
        case SCSIOP_WRITE16:
            // all right
            break;
        case SCSIOP_READ_CD:
        case SCSIOP_READ_CD_MSF:
            if(deviceExtension->opt_AtapiDmaRawRead) {
                // all right
                break;
            }
            /* FALL THROUGH */
        default:
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: SRB_STATUS_BUSY\n"));
            return SRB_STATUS_BUSY;
        }
        //
        if (!(LunExt->DeviceFlags & DFLAGS_CHANGER_INITED) &&
            !AtaReq->OriginalSrb) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: SRB_STATUS_BUSY (2)\n"));
            return SRB_STATUS_BUSY;
        }
    }

#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 (!(LunExt->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.
        LunExt->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 {

            // failed!  Get the sense key and maybe try again
            AtaReq->Srb = BuildRequestSenseSrb (  HwDeviceExtension,
                                                  AtaReq->OriginalSrb);

            srbStatus = AtapiSendCommand(HwDeviceExtension, AtaReq->Srb, CMD_ACTION_ALL);

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

            if (srbStatus == SRB_STATUS_PENDING) {
                KdPrint2((PRINT_PREFIX "AtapiSendCommand: send orig SRB_STATUS_PENDING (2.1)\n"));
                return srbStatus;
            }

            // failed again ? should not get here

            AtaReq->Srb = AtaReq->OriginalSrb;
            AtaReq->OriginalSrb = NULL;

            KdPrint2((PRINT_PREFIX "AtapiSendCommand: AtapiHwInitializeChanger()\n"));
            // Do-nothing call ?
            AtapiHwInitializeChanger (HwDeviceExtension, Srb,
                                      (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL);
            // fall out
        }
    }
#endif //UNIATA_CORE

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

        KdPrint2((PRINT_PREFIX "AtapiSendCommand: prepare..., ATAPI CMD %x (Cdb %x)\n", Srb->Cdb[0], &(Srb->Cdb)));

        if(!LunExt->IdentifyData.AtapiCmdSize &&
            (Srb->CdbLength > 12)) {
            KdPrint2((PRINT_PREFIX "Cdb16 not supported\n"));
            return SRB_STATUS_INVALID_REQUEST;
        }

        // 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;
        // reset this to force PRD init. May be already setup by recursive SRB
        AtaReq->dma_entries = 0;

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

            MOV_DD_SWP(AtaReq->bcount, ((PCDB)Srb->Cdb)->CDB16READWRITE.NumOfBlocks);
            MOV_QD_SWP(AtaReq->lba, ((PCDB)Srb->Cdb)->CDB16READWRITE.LBA);
            goto GetLba2;

        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);
GetLba2:
            AtaReq->Flags |= REQ_FLAG_REORDERABLE_CMD;
            AtaReq->Flags &= ~REQ_FLAG_RW_MASK;
            AtaReq->Flags |= (Srb->Cdb[0] == SCSIOP_WRITE ||
                              Srb->Cdb[0] == SCSIOP_WRITE12 ||
                              Srb->Cdb[0] == SCSIOP_WRITE16) ?
                              REQ_FLAG_WRITE : REQ_FLAG_READ;
            break;
        default:
            AtaReq->Flags &= ~REQ_FLAG_RW_MASK;
            if(!AtaReq->TransferLength) {
                KdPrint(("  assume 0-transfer\n"));
            } else
            if(Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
                KdPrint(("  assume OUT\n"));
                AtaReq->Flags |= REQ_FLAG_WRITE;
            } else
            if(Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
                KdPrint(("  assume IN\n"));
                AtaReq->Flags |= REQ_FLAG_READ;
            }
            break;
        }

        // check if DMA read/write
        if(deviceExtension->HwFlags & UNIATA_AHCI) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: force use dma (ahci)\n"));
            use_dma = TRUE;
            goto setup_dma;
        } else
/*        if((deviceExtension->HwFlags & UNIATA_SATA) && (LunExt->OrigTransferMode >= ATA_DMA)) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: force use dma (sata)\n"));
            use_dma = TRUE;
            goto setup_dma;
        } else*/
        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:
            case SCSIOP_WRITE16:
            case SCSIOP_SEND:
                if(chan->ChannelCtrlFlags & CTRFLAGS_DMA_RO)
                    break;
                /* FALLTHROUGH */
            case SCSIOP_RECEIVE:
            case SCSIOP_READ:
            case SCSIOP_READ12:
            case SCSIOP_READ16:

                if(deviceExtension->opt_AtapiDmaReadWrite) {
call_dma_setup:
                    if(deviceExtension->HwFlags & UNIATA_AHCI) {
                        KdPrint2((PRINT_PREFIX "AtapiSendCommand: use dma (ahci)\n"));
                        use_dma = TRUE;
                    } else
                    if(AtapiDmaSetup(HwDeviceExtension, DeviceNumber, 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:
            case SCSIOP_READ_CD_MSF:
                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
setup_dma:
            if(use_dma) {
                if(deviceExtension->HwFlags & UNIATA_AHCI) {
                    KdPrint2((PRINT_PREFIX "AtapiSendCommand: use dma (ahci)\n"));
                    //use_dma = TRUE;
                } else
                if(!AtapiDmaSetup(HwDeviceExtension, DeviceNumber, 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"));
        }


        if(deviceExtension->HwFlags & UNIATA_AHCI) {

            UniataAhciSetupCmdPtr(AtaReq);

            if(!Srb->DataTransferLength) {
                KdPrint2((PRINT_PREFIX "zero-transfer\n"));
                use_dma = FALSE;
            } else
            if(!AtapiDmaSetup(HwDeviceExtension, DeviceNumber, lChannel, Srb,
                          (PUCHAR)(AtaReq->DataBuffer),
                          Srb->DataTransferLength)) {
                KdPrint2((PRINT_PREFIX "AtapiSendCommand: no AHCI dma!\n"));
                return SRB_STATUS_ERROR;
            }
            if(!use_dma) {
                AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
            } else {
                FeatureReg |= ATA_F_DMA;
                if(LunExt->IdentifyData.AtapiDMA.DMADirRequired &&
                   (Srb->SrbFlags & SRB_FLAGS_DATA_IN)) {
                    FeatureReg |= ATA_F_DMAREAD;
                }
            }

            KdPrint2((PRINT_PREFIX "AtapiSendCommand: setup AHCI FIS\n"));
            // this is done in UniataAhciSetupFIS_H2D()
            //RtlZeroMemory(&(AtaReq->ahci.ahci_cmd_ptr->cfis), sizeof(AtaReq->ahci_cmd0.cfis)); 
            RtlCopyMemory(&(AtaReq->ahci.ahci_cmd_ptr->acmd), Srb->Cdb, Srb->CdbLength);

            fis_size = UniataAhciSetupFIS_H2D(deviceExtension, DeviceNumber, lChannel,
                   &(AtaReq->ahci.ahci_cmd_ptr->cfis[0]),
                    IDE_COMMAND_ATAPI_PACKET /* command */,
                    0 /* lba */,
                    (Srb->DataTransferLength >= 0x10000) ? (USHORT)(0xffff) : (USHORT)(Srb->DataTransferLength),
                    FeatureReg/* feature */
                    );

            if(!fis_size) {
                KdPrint3((PRINT_PREFIX "AtapiSendCommand: AHCI !FIS\n"));
                return SRB_STATUS_ERROR;
            }

            AtaReq->ahci.io_cmd_flags = UniAtaAhciAdjustIoFlags(0,
                ((Srb->DataTransferLength && (Srb->SrbFlags & SRB_FLAGS_DATA_OUT)) ? ATA_AHCI_CMD_WRITE : 0) |
                (ATA_AHCI_CMD_ATAPI | ATA_AHCI_CMD_PREFETCH),
                fis_size, DeviceNumber);

            KdPrint2((PRINT_PREFIX "AtapiSendCommand ahci io flags %x: \n", AtaReq->ahci.io_cmd_flags));
        }
    
    } 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, LunExt, AtaReq);
            if (/*EnableDma &&*/
                (LunExt->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, Cmd %x\n", use_dma, Srb->Cdb[0]));
    if(AtaReq->Flags & REQ_FLAG_DMA_OPERATION) {
        KdPrint2((PRINT_PREFIX "  REQ_FLAG_DMA_OPERATION\n"));
    }

    if((Srb->Cdb[0] == SCSIOP_REQUEST_SENSE) && !(deviceExtension->HwFlags & UNIATA_SATA)) {
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: SCSIOP_REQUEST_SENSE -> no dma setup (2)\n"));
        use_dma = FALSE;
        AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
        AtapiDmaReinit(deviceExtension, LunExt, AtaReq);
    } if(AtaReq->TransferLength) {
        if(!dma_reinited) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: AtapiDmaReinit()\n"));
            AtapiDmaReinit(deviceExtension, LunExt, AtaReq);
            if (/*EnableDma &&*/
                (LunExt->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 && !(deviceExtension->HwFlags & UNIATA_SATA)) {
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: AtapiDmaReinit() to PIO\n"));
            AtapiDmaReinit(deviceExtension, LunExt, 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"));

    KdPrint3((PRINT_PREFIX "AtapiSendCommand: Cdb %x Command %#x to TargetId %d lun %d\n",
               &(Srb->Cdb), Srb->Cdb[0], Srb->TargetId, Srb->Lun));
    
    // Make sure command is to ATAPI device.
    flags = LunExt->DeviceFlags;
    if(flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) {
        if((Srb->Lun) > (LunExt->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. Or more for PM
    SelectDrive(chan, DeviceNumber);

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

    if(statusByte == IDE_STATUS_WRONG) {
        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(deviceExtension->HwFlags & UNIATA_AHCI) {
        ULONG CI;
        // Check if command list is free
        CI = UniataAhciReadChannelPort4(chan, IDX_AHCI_P_CI);
        if(CI) {
            // controller is busy, however we expect it to be free
            KdPrint2((PRINT_PREFIX "AtapiSendCommand: Controller busy (CI=%#x) -> reset\n", CI));
            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.
        statusByte = AtapiSuckPort2(chan);
/*
        for (i = 0; i < 0x10000; i++) {
            GetStatus(chan, statusByte);
            if(statusByte & IDE_STATUS_DRQ) {
                AtapiReadPort2(chan, IDX_IO1_i_Data);
            } else {
                break;
            }
        }
*/
        if (statusByte & IDE_STATUS_DRQ) {
make_reset:
            KdPrint3((PRINT_PREFIX "AtapiSendCommand: DRQ still asserted.Status (%#x)\n", statusByte));

            AtapiDisableInterrupts(deviceExtension, lChannel);

            AtapiSoftReset(chan, DeviceNumber);

            KdPrint2((PRINT_PREFIX "AtapiSendCommand: Issued soft reset to Atapi device. \n"));
            // Re-initialize Atapi device.
            CheckDevice(HwDeviceExtension, GET_CHANNEL(Srb), DeviceNumber, TRUE);
/*
            IssueIdentify(HwDeviceExtension, DeviceNumber, 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.
            UniataExpectChannelInterrupt(chan, 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;
    }

    if(deviceExtension->HwFlags & UNIATA_AHCI) {
        KdPrint2((PRINT_PREFIX "AtapiSendCommand: AHCI, begin transaction\n"));
        //AtaReq->Flags = ~REQ_FLAG_DMA_OPERATION; // keep proped DMA flag for proper RETRY handling
        UniataExpectChannelInterrupt(chan, TRUE);
        UniataAhciBeginTransaction(HwDeviceExtension, lChannel, DeviceNumber, Srb);
        return SRB_STATUS_PENDING;
    }

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

    if(use_dma) {
        FeatureReg |= ATA_F_DMA;
        if(LunExt->IdentifyData.AtapiDMA.DMADirRequired &&
           (Srb->SrbFlags & SRB_FLAGS_DATA_IN)) {
            FeatureReg |= ATA_F_DMAREAD;
        }
    }

    AtapiWritePort1(chan, IDX_IO1_o_Feature, FeatureReg);

    // 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));

        UniataExpectChannelInterrupt(chan, 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));

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

    if(g_opt_AtapiSendDisableIntr) {
        AtapiDisableInterrupts(deviceExtension, lChannel);
    }
    // remember status. Later we may check if error appeared after cmd packet 
    statusByte0 = statusByte;

    // 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.
    UniataExpectChannelInterrupt(chan, TRUE);
    InterlockedExchange(&(chan->CheckIntr),
                                  CHECK_INTR_IDLE);
    AtaReq->ReqState = REQ_STATE_ATAPI_EXPECTING_DATA_INTR;

    // clear interrupt
    GetBaseStatus(chan, statusByte);

#if DBG
    statusByte = (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason) & ATAPI_IR_Mask);
    KdPrint3((PRINT_PREFIX "AtapiSendCommand: iReason %x\n", statusByte));
#endif //DBG

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

    WriteBuffer(chan,
                (PUSHORT)Srb->Cdb,
                LunExt->IdentifyData.AtapiCmdSize ? 8 : 6,
                0);

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

    // When we operate in DMA mode, we should not start transfer when there is an error on entry
    // Interrupt may never come in such case.
    if(statusByte & IDE_STATUS_ERROR) {
        UCHAR interruptReason;

        GetBaseStatus(chan, statusByte);
        KdPrint3((PRINT_PREFIX "AtapiSendCommand: Error on cmd: (%#x)\n", statusByte));

        interruptReason = (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason) & ATAPI_IR_Mask);
        KdPrint3((PRINT_PREFIX "AtapiSendCommand: iReason %x\n", interruptReason));

        // TODO:  we should check interruptReason and decide what to do now

        // Read the error reg. to clear it and fail this request.
        AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
        return MapError(deviceExtension, Srb);
    }
/*    if(statusByte & IDE_STATUS_DSC) {
        KdPrint3((PRINT_PREFIX "AtapiSendCommand: DSC on cmd: (%#x)\n", statusByte));
        // Read the error reg. to clear it and fail this request.
        statusByte = AtapiReadPort1(chan, IDX_IO1_i_Error);
        KdPrint3((PRINT_PREFIX "AtapiSendCommand: Err on cmd: (%#x)\n", statusByte));
        if(statusByte >> 4) {
            GetBaseStatus(chan, statusByte);
            AtaReq->ReqState = REQ_STATE_TRANSFER_COMPLETE;
            return MapError(deviceExtension, Srb);
        }
    }
*/
    if(chan->ChannelCtrlFlags & CTRFLAGS_DMA_OPERATION) {
        AtapiDmaStart(HwDeviceExtension, DeviceNumber, 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;
    PHW_LU_EXTENSION     LunExt;

    SetCheckPoint(4);

    UCHAR statusByte,errorByte;
    ULONG status = SRB_STATUS_INVALID_REQUEST;
    ULONG i;
    ULONGLONG lba;
    PMODE_PARAMETER_HEADER   modeData;
    //ULONG ldev;
    ULONG DeviceNumber;
    PATA_REQ AtaReq;
    UCHAR command;

    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);
    DeviceNumber = GET_CDEV(Srb);
    LunExt = chan->lun[DeviceNumber];

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

    cdb = (PCDB)(Srb->Cdb);

    if(CmdAction == CMD_ACTION_PREPARE) {
        switch (Srb->Cdb[0]) {
        case SCSIOP_SERVICE_ACTION16:
            if( cdb->SERVICE_ACTION16.ServiceAction==SCSIOP_SA_READ_CAPACITY16 ) {
                // ok
            } else {
                goto default_no_prep;
            }
#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_READ12:
        case SCSIOP_WRITE12:
        case SCSIOP_READ16:
        case SCSIOP_WRITE16:
        case SCSIOP_REQUEST_SENSE:
            // all right
            KdPrint2((PRINT_PREFIX "** Ide: Command continue prep\n"));
            SetCheckPoint(50);
            break;
        default:
default_no_prep:
            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 wrong TIDs.
        if ((Srb->Lun != 0) ||
            (Srb->PathId >= deviceExtension->NumberChannels) ||
            (Srb->TargetId >= deviceExtension->NumberLuns)) {

            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 = &(LunExt->IdentifyData);

            if (!(chan->lun[DeviceNumber]->DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                
                if(!CheckDevice(HwDeviceExtension, lChannel, DeviceNumber, 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, DeviceNumber)) {
                    KdPrint2((PRINT_PREFIX 
                               "IdeSendCommand: SCSIOP_INQUIRY device have gone\n"));
                    // Indicate no device found at this address.
                    UniataForgetDevice(chan->lun[DeviceNumber]);
#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 (LunExt->DeviceFlags & DFLAGS_REMOVABLE_DRIVE) {
                KdPrint2((PRINT_PREFIX 
                           "RemovableMedia\n"));
                inquiryData->RemovableMedia = 1;
            }
            // Set the Relative Addressing (LBA) bit, if applicable.
            if (LunExt->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->DeviceIdentificationString[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_REPORT_LUNS: {

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

        MOV_DD_SWP(alen, cdb->REPORT_LUNS.AllocationLength);

        if(alen < 16) {
            goto invalid_cdb;
        }
        alen = 8;

        LunInfo = (PREPORT_LUNS_INFO_HDR)(Srb->DataBuffer);
        RtlZeroMemory(LunInfo, 16);

        MOV_DD_SWP( LunInfo->ListLength, alen );
        Srb->DataTransferLength = 16;
        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));
        
        if(cdb->MODE_SENSE.PageCode == MODE_PAGE_POWER_CONDITION) {
            PMODE_POWER_CONDITION_PAGE modeData;

            KdPrint2((PRINT_PREFIX "MODE_PAGE_POWER_CONDITION\n"));
            modeData = (PMODE_POWER_CONDITION_PAGE)(Srb->DataBuffer);
            if(cdb->MODE_SENSE.AllocationLength < sizeof(MODE_POWER_CONDITION_PAGE)) {
                status = SRB_STATUS_DATA_OVERRUN;
            } else {
                RtlZeroMemory(modeData, sizeof(MODE_POWER_CONDITION_PAGE));
                modeData->PageCode = MODE_PAGE_POWER_CONDITION;
                modeData->PageLength = sizeof(MODE_PAGE_POWER_CONDITION)-sizeof(MODE_PARAMETER_HEADER);
                modeData->Byte3.Fields.Idle = LunExt->PowerState <= StartStop_Power_Idle;
                modeData->Byte3.Fields.Standby = LunExt->PowerState == StartStop_Power_Standby;
                Srb->DataTransferLength = sizeof(MODE_POWER_CONDITION_PAGE);
                status = SRB_STATUS_SUCCESS;
            }
        } else
        if(cdb->MODE_SENSE.PageCode == MODE_PAGE_CACHING) {
            PMODE_CACHING_PAGE modeData;

            KdPrint2((PRINT_PREFIX "MODE_PAGE_CACHING\n"));
            modeData = (PMODE_CACHING_PAGE)(Srb->DataBuffer);
            if(cdb->MODE_SENSE.AllocationLength < sizeof(MODE_CACHING_PAGE)) {
                status = SRB_STATUS_DATA_OVERRUN;
            } else {
                RtlZeroMemory(modeData, sizeof(MODE_CACHING_PAGE));
                modeData->PageCode = MODE_PAGE_CACHING;
                modeData->PageLength = sizeof(MODE_CACHING_PAGE)-sizeof(MODE_PARAMETER_HEADER);
                modeData->ReadDisableCache = (LunExt->DeviceFlags & DFLAGS_RCACHE_ENABLED) ? 0 : 1;
                modeData->WriteCacheEnable = (LunExt->DeviceFlags & DFLAGS_WCACHE_ENABLED) ? 1 : 0;
                Srb->DataTransferLength = sizeof(MODE_CACHING_PAGE);
                status = SRB_STATUS_SUCCESS;
            }
        } else
        if (LunExt->DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED) {

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

            //SelectDrive(chan, DeviceNumber);
            //AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_GET_MEDIA_STATUS);
            //statusByte = WaitOnBusy(chan);
            statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel, IDE_COMMAND_GET_MEDIA_STATUS, 0, 0, 0, 0, 0, ATA_WAIT_READY);

            if (!(statusByte & IDE_STATUS_ERROR)) {

                // no error occured return success, media is not protected
                UniataExpectChannelInterrupt(chan, 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);
                UniataExpectChannelInterrupt(chan, 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 (chan->lun[DeviceNumber]->DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED) {

            // Select device 0 or 1.
            //SelectDrive(chan, DeviceNumber);
            //AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_GET_MEDIA_STATUS);
            // Wait for busy. If media has not changed, return success
            //statusByte = WaitOnBusy(chan);
            statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel, IDE_COMMAND_GET_MEDIA_STATUS, 0, 0, 0, 0, 0, ATA_WAIT_READY);

            if (!(statusByte & IDE_STATUS_ERROR)){
                UniataExpectChannelInterrupt(chan, 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);
                    UniataExpectChannelInterrupt(chan, FALSE);
                    InterlockedExchange(&(chan->CheckIntr),
                                                  CHECK_INTR_IDLE);
                    status = SRB_STATUS_SUCCESS;

                } else {

                    // Request sense buffer to be build
                    UniataExpectChannelInterrupt(chan, 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;
        RtlZeroMemory(Srb->DataBuffer, sizeof(READ_CAPACITY_DATA));
        MOV_DD_SWP( ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock, i );

        // Calculate last sector.
        if(!(i = (ULONG)LunExt->NumOfSectors)) {
            i = LunExt->IdentifyData.SectorsPerTrack *
                LunExt->IdentifyData.NumberOfHeads *
                LunExt->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,
                   LunExt->IdentifyData.SectorsPerTrack,
                   LunExt->IdentifyData.NumberOfHeads,
                   LunExt->IdentifyData.NumberOfCylinders));


        status = SRB_STATUS_SUCCESS;
        break;

    case SCSIOP_SERVICE_ACTION16:

        if( cdb->SERVICE_ACTION16.ServiceAction==SCSIOP_SA_READ_CAPACITY16 ) {
            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;
            RtlZeroMemory(Srb->DataBuffer, sizeof(READ_CAPACITY16_DATA));
            MOV_DD_SWP( ((PREAD_CAPACITY16_DATA)Srb->DataBuffer)->BytesPerBlock, i );

            // Calculate last sector.
            if(!(lba = LunExt->NumOfSectors)) {
                lba = LunExt->IdentifyData.SectorsPerTrack *
                    LunExt->IdentifyData.NumberOfHeads *
                    LunExt->IdentifyData.NumberOfCylinders;
            }
            lba--;
            MOV_QD_SWP( ((PREAD_CAPACITY16_DATA)Srb->DataBuffer)->LogicalBlockAddress, lba );

            KdPrint2((PRINT_PREFIX 
                       "** IDE disk %#x - #sectors %#x, #heads %#x, #cylinders %#x (16)\n",
                       Srb->TargetId,
                       LunExt->IdentifyData.SectorsPerTrack,
                       LunExt->IdentifyData.NumberOfHeads,
                       LunExt->IdentifyData.NumberOfCylinders));

            status = SRB_STATUS_SUCCESS;
        } else {
            goto default_abort;
        }
        break;

    case SCSIOP_VERIFY:
    case SCSIOP_VERIFY12:
    case SCSIOP_VERIFY16:

        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:
    case SCSIOP_READ12:
    case SCSIOP_WRITE12:
    case SCSIOP_READ16:
    case SCSIOP_WRITE16:

        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 ||
                          Srb->Cdb[0] == SCSIOP_WRITE12 ||
                          Srb->Cdb[0] == SCSIOP_WRITE16) ? 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 immed %d PATH:LUN:TID = %#x:%#x:%#x\n",
                   cdb->START_STOP.Immediate, Srb->PathId, Srb->Lun, Srb->TargetId));
        //Determine what type of operation we should perform

        command = 0;

        if(cdb->START_STOP.FL ||
           cdb->START_STOP.FormatLayerNumber ||
           cdb->START_STOP.Reserved2 ||
           cdb->START_STOP.Reserved2_2 ||
           cdb->START_STOP.Reserved3 ||
           FALSE) {
            goto invalid_cdb;
        }

        if (cdb->START_STOP.PowerConditions) {
            KdPrint2((PRINT_PREFIX "START_STOP Power %d\n", cdb->START_STOP.PowerConditions));
            switch(cdb->START_STOP.PowerConditions) {
            case StartStop_Power_Idle:
                command = IDE_COMMAND_IDLE_IMMED;
                break;
            case StartStop_Power_Standby:
                command = IDE_COMMAND_STANDBY_IMMED;
                break;
            case StartStop_Power_Sleep:
                // TODO: we should save power state in order to know
                // that RESET sould be issued to revert device into
                // operable state

                command = IDE_COMMAND_SLEEP;
                break;
            default:
                goto invalid_cdb;
            }
            LunExt->PowerState = cdb->START_STOP.PowerConditions;
        } else
        if (cdb->START_STOP.LoadEject == 1) {
            KdPrint2((PRINT_PREFIX "START_STOP eject\n"));
            // Eject media,
            // first select device 0 or 1.
            //SelectDrive(chan, DeviceNumber);
            //AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_MEDIA_EJECT);
            command = IDE_COMMAND_MEDIA_EJECT;
        } else
        if (cdb->START_STOP.Start == 0) {
            KdPrint2((PRINT_PREFIX "START_STOP standby\n"));
            command = IDE_COMMAND_STANDBY_IMMED;
        } else {
            // TODO: we may need to perform hard reset (after sleep) or
            // issue IDE_COMMAND_IDLE_IMMED in order to activate device
            KdPrint2((PRINT_PREFIX "START_STOP activate\n"));

            if(LunExt->PowerState == StartStop_Power_Sleep) {
                UniataUserDeviceReset(deviceExtension, LunExt, lChannel);
                status = SRB_STATUS_SUCCESS;
                break;
            } else
            if(LunExt->PowerState > StartStop_Power_Idle) {
                KdPrint2((PRINT_PREFIX "  issue IDLE\n"));
                command = IDE_COMMAND_IDLE_IMMED;
            } else {
                KdPrint2((PRINT_PREFIX "  do nothing\n"));
                status = SRB_STATUS_SUCCESS;
                break;
            }
        }
        if(command) {
            statusByte = WaitOnBaseBusy(chan);
            statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel, command, 0, 0, 0, 0, 0, 
                cdb->START_STOP.Immediate ? ATA_IMMEDIATE : ATA_WAIT_READY);
            status = (statusByte & IDE_STATUS_ERROR) ? SRB_STATUS_ERROR : SRB_STATUS_SUCCESS;
            //UniataExpectChannelInterrupt(chan, TRUE); // device may interrupt

        } else {
invalid_cdb:
            KdPrint2((PRINT_PREFIX "START_STOP invalid\n"));
            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_CDB;
                senseBuffer->AdditionalSenseCodeQualifier = 0;

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

    case SCSIOP_MEDIUM_REMOVAL:

        cdb = (PCDB)Srb->Cdb;

        if(LunExt->IdentifyData.Removable) {
            statusByte = WaitOnBaseBusy(chan);

            //SelectDrive(chan, DeviceNumber);
            if (cdb->MEDIA_REMOVAL.Prevent == TRUE) {
                //AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_DOOR_LOCK);
                statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel, IDE_COMMAND_DOOR_LOCK, 0, 0, 0, 0, 0, ATA_IMMEDIATE);
            } else {
                //AtapiWritePort1(chan, IDX_IO1_o_Command,IDE_COMMAND_DOOR_UNLOCK);
                statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel, IDE_COMMAND_DOOR_UNLOCK, 0, 0, 0, 0, 0, ATA_IMMEDIATE);
            }
            status = SRB_STATUS_SUCCESS;
        } else {
            status = SRB_STATUS_INVALID_REQUEST;
        }
        break;

#if 0
    // 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.
    case SCSIOP_FLUSH_BUFFER:
    case SCSIOP_SYNCHRONIZE_CACHE:

        SelectDrive(chan, DeviceNumber);
        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 (LunExt->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]);

        if(chan->DeviceExtension->HwFlags & UNIATA_SATA) {
            //lChannel = Srb->TargetId >> 1;
        } else {
            DeviceNumber = max(DeviceNumber, 1);
            regs->bDriveHeadReg &= 0x0f;
            regs->bDriveHeadReg |= (UCHAR) (((DeviceNumber & 0x1) << 4) | 0xA0);
        }

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

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

            if((regs->bOpFlags & UNIATA_SPTI_EX_SPEC_TO) == 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;
                }
            }

            AtapiDisableInterrupts(deviceExtension, lChannel);

            if(chan->DeviceExtension->HwFlags & UNIATA_AHCI) {
                // AHCI
                statusByte = UniataAhciSendPIOCommandDirect(
                        deviceExtension,
                        lChannel,
                        DeviceNumber,
                        Srb,
                        regs,
                        ATA_WAIT_INTR,
                        to_lim
                        );
                if(statusByte == IDE_STATUS_WRONG) {
                    goto passthrough_err;
                }
                if(statusByte & (IDE_STATUS_BUSY | IDE_STATUS_ERROR)) {
                    UniataAhciAbortOperation(chan);
                    goto passthrough_err;
                }
                goto passthrough_done;
            }

            // SATA/PATA
            if((AtaCommandFlags[regs->bCommandReg] & ATA_CMD_FLAG_DMA) || (regs->bOpFlags & UNIATA_SPTI_EX_USE_DMA)) {
                if((chan->lun[DeviceNumber]->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)(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, DeviceNumber, lChannel, Srb);
            }

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

            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, DeviceNumber, 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;
            }
passthrough_done:;
            AtapiEnableInterrupts(deviceExtension, lChannel);

        } else { // read task register

            BOOLEAN use48;
            regs = (PIDEREGS_EX) Srb->DataBuffer;

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

            if((Srb->DataTransferLength >= sizeof(IDEREGS_EX)) &&
               (regs->bOpFlags & ATA_FLAGS_48BIT_COMMAND)) {
                use48 = TRUE;
            } else
            if(Srb->DataTransferLength >= sizeof(IDEREGS)) {
                use48 = FALSE;
            } else {
                KdPrint2((PRINT_PREFIX " buffer too small \n"));
                status = SRB_STATUS_DATA_OVERRUN;
                break;
            }
            RtlZeroMemory(regs, use48 ? sizeof(IDEREGS_EX) : sizeof(IDEREGS));
            regs->bOpFlags = use48 ? ATA_FLAGS_48BIT_COMMAND : 0;
            UniataSnapAtaRegs(chan, 0, regs);

            status = SRB_STATUS_SUCCESS;
        }
        break;
    }

    default:
default_abort:
        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,
    IN ULONG lChannel,
    IN ULONG DeviceNumber
    )
{
    PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
    PHW_CHANNEL          chan;
    UCHAR statusByte,errorByte;

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

    if (EnableMSN == TRUE){

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

            // enable
            statusByte = AtaCommand(deviceExtension, DeviceNumber, 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 {
                chan->lun[DeviceNumber]->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 ((chan->lun[DeviceNumber]->DeviceFlags & DFLAGS_MEDIA_STATUS_ENABLED)) {

            statusByte = AtaCommand(deviceExtension, DeviceNumber, lChannel,
                                IDE_COMMAND_SET_FEATURES, 0, 0, 0,
                                0, ATA_C_F_DIS_MEDIASTAT, ATA_WAIT_BASE_READY);
            chan->lun[DeviceNumber]->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 lChannel
    )
{
    ULONG i;
    AtapiDisableInterrupts(deviceExtension, lChannel);
    if ((LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) &&
        (LunExt->PowerState != StartStop_Power_Sleep)) {
        KdPrint2((PRINT_PREFIX "UniataUserDeviceReset: Reset ATAPI\n"));
        AtapiSoftReset(&(deviceExtension->chan[lChannel]), LunExt->Lun);
    } else {
        KdPrint2((PRINT_PREFIX "UniataUserDeviceReset: Reset IDE -> reset entire channel\n"));
        AtapiResetController__(deviceExtension, lChannel, RESET_COMPLETE_NONE);
        for(i=0; i<deviceExtension->NumberLuns; i++) {
            deviceExtension->chan[lChannel].lun[i]->DeviceFlags |= DFLAGS_REINIT_DMA;
        }
    }
    LunExt->DeviceFlags |= DFLAGS_REINIT_DMA;  // force PIO/DMA reinit
    AtapiEnableInterrupts(deviceExtension, lChannel);
    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.
    ->HwStartIo

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;
    PHW_LU_EXTENSION     LunExt;
    ULONG status;
    //ULONG ldev;
    ULONG DeviceNumber;
    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));
        UniAtaClearAtaReq(Srb->SrbExtension);
    }

    do { // fetch all queued commands for the channel (if valid)

        lChannel = GET_CHANNEL(Srb);
        //ldev = GET_LDEV(Srb);
        chan = NULL;
        LunExt = NULL;
        DeviceNumber = GET_CDEV(Srb);
        commPort = FALSE;

        //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))
           ) {
            // This is our virtual device
            KdPrint2((PRINT_PREFIX 
                       "AtapiStartIo: Communication port\n"));
            if(Srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {

                if(Srb->DataTransferLength < sizeof(PINQUIRYDATA)) {
                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Buffer too small: %#x < %#x\n", Srb->DataTransferLength,
                        sizeof(PINQUIRYDATA) ));
wrong_buffer_size:
                    status = SRB_STATUS_DATA_OVERRUN;
                    goto complete_req;
                }

                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(lChannel >= deviceExtension->NumberChannels ||
            Srb->TargetId /*DeviceNumber*/ >= deviceExtension->NumberLuns ||
            Srb->Lun) {

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

reject_srb:
            //if(!CheckDevice(HwDeviceExtension, lChannel, DeviceNumber, 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;
            //}
        } else
        if((deviceExtension->HwFlags & UNIATA_AHCI) &&
           !UniataAhciChanImplemented(deviceExtension, lChannel)) {
            chan = NULL;
        }

        if(!commPort) {
            chan = &(deviceExtension->chan[lChannel]);
            LunExt = chan->lun[DeviceNumber];
            if(!LunExt) {
                goto reject_srb;
            }
            atapiDev = (LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) ? TRUE : FALSE;
        } else {
            atapiDev = FALSE;
        }

#ifdef _DEBUG
        if(!commPort && !LunExt) {
#if 0
            PrintNtConsole("de = %#x, chan = %#x , dev %#x, nchan %#x\n",
                deviceExtension,
                chan, DeviceNumber,
                deviceExtension->NumberChannels);
            PrintNtConsole("lchan = %#x, cdev %#x, lun0 %#x\n",
                lChannel, 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(!LunExt || !(LunExt->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, DeviceNumber, 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;
                //}
                }
            } else {
                KdPrint2((PRINT_PREFIX 
                           "  SRB %#x, CDB %#x, AtaReq %#x, SCmd %#x\n", Srb, &(Srb->Cdb), Srb->SrbExtension, Srb->Cdb[0]));
            }
/*
            __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_CDEV(Srb)]);
                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 %x\n", Srb->Cdb[0]));
                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_CDEV(Srb)]);
                    UniataQueueRequest(chan, Srb);
//                    AtaReq = (PATA_REQ)(Srb->SrbExtension);
                    //ASSERT(!AtaReq->Flags);
                    AtaReq->ReqState = REQ_STATE_QUEUED;
                    //ASSERT(!AtaReq->Flags);
                }

#ifndef NAVO_TEST
                if(!LunExt || !(LunExt->DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                    if(!LunExt) {
                        goto reject_srb;
                    }
                    if(Srb->Cdb[0] == SCSIOP_INQUIRY) {
                        if(UniataAnybodyHome(deviceExtension, chan->lChannel, DeviceNumber)) {
                            if(!CheckDevice(HwDeviceExtension, chan->lChannel, DeviceNumber, TRUE)) {
                                goto reject_srb;
                            }
                        }
                        if(!(LunExt->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)/* &&
                   (Srb->Cdb[0] != SCSIOP_REPORT_LUNS)*/) {
                    KdPrint3((PRINT_PREFIX "Try ATAPI send %x\n", Srb->Cdb[0]));
                    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, LunExt, lChannel);
            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(!LunExt || !(LunExt->DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                KdPrint2((PRINT_PREFIX "AtapiStartIo: Shutdown - no such device\n"));
            } else
            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, DeviceNumber, GET_CHANNEL(Srb),
                           IDE_COMMAND_FLUSH_CACHE, 0, 0, 0, 0, 0, ATA_WAIT_IDLE);
                // If supported & allowed, reset write cacheing
                if(LunExt->DeviceFlags & DFLAGS_WCACHE_ENABLED) {

                    // Disable write cache
                    status = AtaCommand(deviceExtension, DeviceNumber, 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",
                                    DeviceNumber));
                    }
                    LunExt->DeviceFlags &= ~DFLAGS_WCACHE_ENABLED;

                    // Re-enable write cache
                    status = AtaCommand(deviceExtension, DeviceNumber, 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",
                                    DeviceNumber));
                        LunExt->DeviceFlags &= ~DFLAGS_WCACHE_ENABLED;
                    } else {
                        LunExt->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_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)) {

                ULONG targetId = (ULONG)(-1);

                if(len < sizeof(SRB_IO_CONTROL)) {
                    goto wrong_buffer_size;
                }

                // extract bogus bus address
                switch (((PSRB_IO_CONTROL)(Srb->DataBuffer))->ControlCode) {
                case IOCTL_SCSI_MINIPORT_SMART_VERSION: {
                    PGETVERSIONINPARAMS versionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));

                    if(len < sizeof(SRB_IO_CONTROL)+sizeof(GETVERSIONINPARAMS)) {
                        goto wrong_buffer_size;
                    }

                    targetId = versionParameters->bIDEDeviceMap;
                    KdPrint2((PRINT_PREFIX "targetId (smart ver) %d\n", targetId));
                    break; }
                case IOCTL_SCSI_MINIPORT_IDENTIFY:
                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:
                case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE:
                case IOCTL_SCSI_MINIPORT_READ_SMART_LOG:
                case IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG:
                    {
                    PSENDCMDINPARAMS   cmdInParameters = (PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));

                    if(len < sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS) - 1) {
                        goto wrong_buffer_size;
                    }

                    targetId = cmdInParameters->bDriveNumber;
                    KdPrint2((PRINT_PREFIX "targetId (smart/ident) %d\n", targetId));
                    break; }
                default:
invalid_request:
                    KdPrint2((PRINT_PREFIX "AtapiStartIo: invalid IoControl %#x for SCSIDISK signature\n",
                                ((PSRB_IO_CONTROL)(Srb->DataBuffer))->ControlCode ));
                    status = SRB_STATUS_INVALID_REQUEST;
                    goto complete_req;
                } // end switch()

                // adjust (if necessary) bus address
                if(targetId != (ULONG)(-1)) {

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

                    if (deviceExtension->NumberChannels == 1) {
                        // do this for legacy controllers and legacy callers
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: legacy call\n"));
                        DeviceNumber = (targetId & 0x01);
                        lChannel = 0;
                    } else
                    if(commPort) {
                        // do this for smartmontools, sending IOCTLs to PhysicalDrive%d
                        // due to DISK.SYS design bug, we have invalid SCSI address in SRB
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: legacy call (2)\n"));
                        if(deviceExtension->HwFlags & UNIATA_AHCI) {
                            lChannel = (UCHAR)targetId / 2;
                            DeviceNumber = 0;
                        } else {
                            lChannel = (UCHAR)(targetId / 2);
                            DeviceNumber = targetId & 0x01;
                        }
                    } else {
                        // otherwise assume lChannel and DeviceNumber from Srb are ok
                    }
                    if(lChannel >= deviceExtension->NumberChannels ||
                        DeviceNumber >= deviceExtension->NumberLuns) {
                        KdPrint2((PRINT_PREFIX 
                                   "AtapiStartIo: SCSIDISK IOCTL for non-exestent drive %d -> EXECUTE_SCSI rejected (2)\n",
                                       targetId));
                        // Indicate no device found at this address.
                        goto reject_srb;
                    }
                    targetId = lChannel*deviceExtension->NumberLuns+DeviceNumber;
                    chan = &(deviceExtension->chan[lChannel]);
                    LunExt = chan->lun[DeviceNumber];
                    if(!LunExt) {
                        goto reject_srb;
                    }
                    atapiDev = (LunExt->DeviceFlags & DFLAGS_ATAPI_DEVICE) ? TRUE : FALSE;

                    if (!(LunExt->DeviceFlags & DFLAGS_DEVICE_PRESENT)) {
                        goto reject_srb;
                    }
                }

                switch (((PSRB_IO_CONTROL)(Srb->DataBuffer))->ControlCode) {
                case IOCTL_SCSI_MINIPORT_SMART_VERSION: {

                    PGETVERSIONINPARAMS versionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
                    UCHAR deviceNumberMap;

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

                    if (atapiDev) {
                        goto invalid_request;
                    }

                    // 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(chan->DeviceExtension->HwFlags & UNIATA_AHCI) {
                        deviceNumberMap = 1 << lChannel;
                        DeviceNumber = 0;
                    } else
                    if (deviceExtension->NumberChannels == 1) {
                        if (chan->PrimaryAddress) {
                            deviceNumberMap = 1 << DeviceNumber;
                        } else {
                            deviceNumberMap = 4 << DeviceNumber;
                        }
                    } else {
                        deviceNumberMap = 1 << (DeviceNumber+lChannel*2);
                    }

                    versionParameters->bIDEDeviceMap = deviceNumberMap;

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

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: IOCTL_SCSI_MINIPORT_IDENTIFY\n"));
                    // Extract the target.
                    KdPrint2((PRINT_PREFIX "targetId %d\n", targetId));

                    switch(cmdInParameters.irDriveRegs.bCommandReg) {
                    case ID_CMD:
                        if(atapiDev) {
                            KdPrint2((PRINT_PREFIX "Error: ID_CMD for ATAPI\n"));
                            goto invalid_request;
                        }
                        /* FALL THROUGH */
                    case ATAPI_ID_CMD:

                        if(!atapiDev &&
                           (cmdInParameters.irDriveRegs.bCommandReg == ATAPI_ID_CMD)) {
                            KdPrint2((PRINT_PREFIX "Error: ATAPI_ID_CMD for non-ATAPI\n"));
                            goto invalid_request;
                        }

                        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, &(LunExt->IdentifyData),
                            cmdOutParameters->cBufferSize);

                        if((cmdOutParameters->cBufferSize == IDENTIFY_BUFFER_SIZE) &&
                           (LunExt->IdentifyData.ChecksumValid == ATA_ChecksumValid)) {
                            // adjust checksum if it is possible
                            CHAR csum = 0;
                            ULONG i;

                            for(i=0; i < IDENTIFY_BUFFER_SIZE-1; i++) {
                                csum += (CHAR)(cmdOutParameters->bBuffer[i]);
                            }
                            cmdOutParameters->bBuffer[i] = -csum;
                            KdPrint2((PRINT_PREFIX "AtapiStartIo: adjust checksum %d\n"));
                        }
                        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:
*/
                default:
                    // *all* IOCTLs here are SMART
                    if(commPort) {
                        KdPrint2((PRINT_PREFIX 
                                   "AtapiStartIo: SCSIDISK Smart IOCTL for commPort -> EXECUTE_SCSI rejected (3)\n"));
                    }
                    if (atapiDev) {
                        goto invalid_request;
                    }

                    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, targetId);
                    }
                    break;

                // we should not get here, checked above
/*                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);
                ULONG DeviceNumber = AtaCtl->addr.TargetId;
                BOOLEAN bad_ldev;
                ULONG i, pos;

                pos = FIELD_OFFSET(UNIATA_CTL, RawData);
                //chan = &(deviceExtension->chan[lChannel]);
                if(len < pos) {
                    KdPrint2((PRINT_PREFIX "AtapiStartIo: AtaCtl Buffer too small: %#x < %#x\n", len,
                        FIELD_OFFSET(UNIATA_CTL, RawData) ));
                    goto wrong_buffer_size;
                }

                if(AtaCtl->addr.Lun ||
                   AtaCtl->addr.TargetId >= deviceExtension->NumberLuns || 
                   AtaCtl->addr.PathId >= deviceExtension->NumberChannels) {

                    chan = NULL;
                    bad_ldev = TRUE;
                    LunExt = NULL;

                } else {
                    bad_ldev = FALSE;
                    lChannel = AtaCtl->addr.PathId;
                    chan = &(deviceExtension->chan[lChannel]);
                    LunExt = chan->lun[DeviceNumber];
                }

                KdPrint2((PRINT_PREFIX "AtapiStartIo: -UNIATA- %#x, dev %#x\n", AtaCtl->hdr.ControlCode, DeviceNumber));

                /* check for valid LUN */
                switch (AtaCtl->hdr.ControlCode) {
                case  IOCTL_SCSI_MINIPORT_UNIATA_FIND_DEVICES:
                case  IOCTL_SCSI_MINIPORT_UNIATA_RESET_DEVICE:
                    // this would be BUS reset
                    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 &&
                           DeviceNumber < deviceExtension->NumberLuns) { // AtaCtl->addr.TargetId != 0xff
                            lChannel = AtaCtl->addr.PathId;
                            chan = &(deviceExtension->chan[lChannel]);
                            LunExt = chan->lun[DeviceNumber];
                            // OK
                        } else {
                            goto handle_bad_ldev;
                        }
                    } else {
                        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(len < pos+sizeof(AtaCtl->SetMode)) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: AtaCtl Buffer too small: %#x < %#x\n", len,
                            pos+sizeof(AtaCtl->SetMode) ));
                        goto wrong_buffer_size;
                    }
                    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(len < pos+sizeof(AtaCtl->FindDelDev)) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: AtaCtl Buffer too small: %#x < %#x\n", len,
                            pos+sizeof(AtaCtl->FindDelDev) ));
                        goto wrong_buffer_size;
                    }
                    if(AtaCtl->FindDelDev.Flags & UNIATA_ADD_FLAGS_UNHIDE) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: unhide from further detection\n"));
                        if(AtaCtl->addr.TargetId != 0xff) {
                            LunExt->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));

                    if(len < pos+sizeof(AtaCtl->FindDelDev)) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: AtaCtl Buffer too small: %#x < %#x\n", len,
                            pos+sizeof(AtaCtl->FindDelDev) ));
                        goto wrong_buffer_size;
                    }
                    LunExt->DeviceFlags = 0;
                    if(AtaCtl->FindDelDev.Flags & UNIATA_REMOVE_FLAGS_HIDE) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: hide from further detection\n"));
                        //LunExt->DeviceFlags |= DFLAGS_HIDDEN;
                        UniataForgetDevice(LunExt);
                    }

                    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(len < pos+sizeof(AtaCtl->SetMode)) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: AtaCtl Buffer too small: %#x < %#x\n", len,
                            pos+sizeof(AtaCtl->SetMode) ));
                        goto wrong_buffer_size;
                    }
                    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, LunExt);
                    }
/*                    LunExt->TransferMode =
                    LunExt->LimitedTransferMode = (UCHAR)(setTransferMode->Mode);*/
                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                case  IOCTL_SCSI_MINIPORT_UNIATA_GET_MODE: {

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Get transfer mode\n"));

                    if(len < pos+sizeof(AtaCtl->GetMode)) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: AtaCtl Buffer too small: %#x < %#x\n", len,
                            pos+sizeof(AtaCtl->GetMode) ));
                        goto wrong_buffer_size;
                    }
                    AtaCtl->GetMode.OrigMode    = LunExt->OrigTransferMode;
                    AtaCtl->GetMode.MaxMode     = LunExt->LimitedTransferMode;
                    AtaCtl->GetMode.CurrentMode = LunExt->TransferMode;
                    AtaCtl->GetMode.PhyMode     = LunExt->PhyTransferMode;

                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                case  IOCTL_SCSI_MINIPORT_UNIATA_GET_VERSION: {

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Get version\n"));

                    if(len < pos+sizeof(AtaCtl->Version)) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: AtaCtl Buffer too small: %#x < %#x\n", len,
                            pos+sizeof(AtaCtl->Version) ));
                        goto wrong_buffer_size;
                    }
                    AtaCtl->Version.Length      = sizeof(GETDRVVERSION);
                    AtaCtl->Version.VersionMj   = UNIATA_VER_MJ;
                    AtaCtl->Version.VersionMn   = UNIATA_VER_MN;
                    AtaCtl->Version.SubVerMj    = UNIATA_VER_SUB_MJ;
                    AtaCtl->Version.SubVerMn    = UNIATA_VER_SUB_MN;

                    status = SRB_STATUS_SUCCESS;
                    break;
                }
                case  IOCTL_SCSI_MINIPORT_UNIATA_ADAPTER_INFO: {

                    KdPrint2((PRINT_PREFIX "AtapiStartIo: Get adapter info\n"));

                    if(len < pos+sizeof(AtaCtl->AdapterInfo)) {
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: AtaCtl Buffer too small: %#x < %#x\n", len,
                            pos+sizeof(AtaCtl->AdapterInfo) ));
                        goto wrong_buffer_size;
                    }
                    AtaCtl->AdapterInfo.HeaderLength = sizeof(ADAPTERINFO);

                    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.NumberLuns = (UCHAR)deviceExtension->NumberLuns;
                    AtaCtl->AdapterInfo.AdapterInterfaceType = deviceExtension->AdapterInterfaceType;
                    if(deviceExtension->FullDevName) {
                        strncpy(AtaCtl->AdapterInfo.DeviceName, deviceExtension->FullDevName, 64);
                    }
                    AtaCtl->AdapterInfo.ChanInfoValid = FALSE;
                    AtaCtl->AdapterInfo.LunInfoValid = FALSE;
                    AtaCtl->AdapterInfo.ChanHeaderLengthValid = TRUE;

                    pos += AtaCtl->AdapterInfo.HeaderLength;

                    // zero tail
                    RtlZeroMemory(((PCHAR)AtaCtl)+pos,
                        len-pos);

                    if(len >= pos+AtaCtl->AdapterInfo.NumberChannels*sizeof(CHANINFO)) {
                        PCHANINFO ChanInfo = (PCHANINFO)( ((PCHAR)AtaCtl)+pos );
                        PHW_CHANNEL cur_chan;
                        KdPrint2((PRINT_PREFIX "AtapiStartIo: Fill channel info\n"));
                        for(i=0;i<AtaCtl->AdapterInfo.NumberChannels;i++) {
                            KdPrint2((PRINT_PREFIX "chan[%d] %x\n", i, cur_chan));
                            cur_chan = &(deviceExtension->chan[i]);
                            ChanInfo->MaxTransferMode = cur_chan->MaxTransferMode;
                            ChanInfo->ChannelCtrlFlags = cur_chan->ChannelCtrlFlags;
                            RtlCopyMemory(&(ChanInfo->QueueStat), &(cur_chan->QueueStat), sizeof(ChanInfo->QueueStat));
                            ChanInfo->ReorderCount        = cur_chan->ReorderCount;
                            ChanInfo->IntersectCount      = cur_chan->IntersectCount;
                            ChanInfo->TryReorderCount     = cur_chan->TryReorderCount;
                            ChanInfo->TryReorderHeadCount = cur_chan->TryReorderHeadCount;
                            ChanInfo->TryReorderTailCount = cur_chan->TryReorderTailCount;
                            //ChanInfo->opt_MaxTransferMode = cur_chan->opt_MaxTransferMode;
                            ChanInfo++;
                        }
                        AtaCtl->AdapterInfo.ChanInfoValid = TRUE;
                        AtaCtl->AdapterInfo.ChanHeaderLength = sizeof(*ChanInfo);
                    }

                    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);
                    }

                    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;

            if(chan && Srb) {
                KdPrint2((PRINT_PREFIX "AtapiStartIo: AtapiDmaDBSync(%x, %x)\n", chan, Srb));
                AtapiDmaDBSync(chan, Srb);
            }
            KdPrint2((PRINT_PREFIX "AtapiStartIo: UniataRemoveRequest(%x, %x)\n", chan, Srb));
            UniataRemoveRequest(chan, Srb);
            // Indicate command complete.
            KdPrint2((PRINT_PREFIX "AtapiStartIo: ScsiPortNotification\n"));
            ScsiPortNotification(RequestComplete,
                                 deviceExtension,
                                 Srb);

            KdPrint2((PRINT_PREFIX "AtapiStartIo: UniataGetCurRequest\n"));
            // 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;
                }
            }
            KdPrint2((PRINT_PREFIX "AtapiStartIo: chan %x, Src %x\n", chan, Srb));
            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:
        case IDE_COMMAND_DATA_SET_MGMT:
            //KdPrint2((PRINT_PREFIX "DMA "));
            flags |= ATA_CMD_FLAG_DMA;
        }

        switch(command) {
        case IDE_COMMAND_WRITE_FUA_DMA48:
        case IDE_COMMAND_WRITE_FUA_DMA_Q48:
        case IDE_COMMAND_WRITE_MUL_FUA48:

            flags |= ATA_CMD_FLAG_FUA;
            /* FALL THROUGH */

        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_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;
        }

        switch(command) {
        case IDE_COMMAND_READ_NATIVE_SIZE48:
        case IDE_COMMAND_SET_NATIVE_SIZE48:
            // we cannot set LBA flag for these commands to avoid BadBlock handling
            //flags |= ATA_CMD_FLAG_LBAIOsupp;
            flags |= ATA_CMD_FLAG_48;

        case IDE_COMMAND_READ_NATIVE_SIZE:
        case IDE_COMMAND_SET_NATIVE_SIZE:

            flags |= ATA_CMD_FLAG_LBAIOsupp | ATA_CMD_FLAG_FUA;
        }

        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;
        }

        switch (command) {
        case IDE_COMMAND_READ:
        case IDE_COMMAND_READ_MULTIPLE:
        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_READ_DMA_Q:
        case IDE_COMMAND_READ_DMA:
        case IDE_COMMAND_READ_LOG_DMA48:
        case IDE_COMMAND_TRUSTED_RCV_DMA:
        case IDE_COMMAND_IDENTIFY:
        case IDE_COMMAND_ATAPI_IDENTIFY:
            //KdPrint2((PRINT_PREFIX "RD "));
            flags |= ATA_CMD_FLAG_In;
            break;
        case IDE_COMMAND_WRITE:
        case IDE_COMMAND_WRITE_MULTIPLE:
        case IDE_COMMAND_WRITE_DMA48:
        case IDE_COMMAND_WRITE_DMA_Q48:
        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:
            //KdPrint2((PRINT_PREFIX "WR "));
            flags |= ATA_CMD_FLAG_Out;
            break;
        }

        //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;
#ifndef USE_REACTOS_DDK
    NTSTATUS               status;
#endif

    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;
#ifdef USE_REACTOS_DDK
        KdPrint(("UniATA Init: OS should be ReactOS\n"));
        MajorVersion=0x04;
        MinorVersion=0x01;
        BuildNumber=1;
#else
        // 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;
        }
#endif // USE_REACTOS_DDK
        KdPrint(("UniATA Init: OS ver %x.%x (%d), %d CPU(s)\n", MajorVersion, MinorVersion, BuildNumber, *KeNumberProcessors));

        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(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) {

        g_opt_VirtualMachine = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"VirtualMachineType", g_opt_VirtualMachine);
        if(g_opt_VirtualMachine > VM_MAX_KNOWN) {
            g_opt_VirtualMachine = 0;
        }
        if(AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"VirtualBox", (g_opt_VirtualMachine == VM_VBOX))) {
            g_opt_VirtualMachine = VM_VBOX;
        }
        // Pre-scan PCI bus, also check if we are under VM
        UniataEnumBusMasterController(DriverObject, Argument2);

        switch(g_opt_VirtualMachine) {
        case VM_VBOX:
            KdPrint2((PRINT_PREFIX "adjust options for VirtualBox\n"));
            // adjust options for VirtualBox
            g_opt_WaitBusyCount = 20000;
            g_opt_WaitBusyDelay = 150;
            g_opt_WaitDrqDelay  = 100;
            g_opt_WaitBusyLongCount = 20000;
            g_opt_MaxIsrWait = 200;
            g_opt_AtapiSendDisableIntr = FALSE;
            g_opt_AtapiDmaRawRead = FALSE;
            break;
        }

        if(!hasPCI) {
            KdPrint2((PRINT_PREFIX "old slow machine, adjust timings\n"));
            // old slow machine, adjust timings
            g_opt_WaitBusyCount = 20000;
            g_opt_WaitBusyDelay = 150;
            g_opt_WaitDrqDelay  = 100;
            g_opt_WaitBusyLongCount = 20000;
            g_opt_MaxIsrWait = 200;
        }

        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_WaitBusyLongCount = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"WaitBusyLongCount", g_opt_WaitBusyLongCount); // 2000 vs 20000
        g_opt_WaitBusyLongDelay = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"WaitBusyLongDelay", g_opt_WaitBusyLongDelay); // 250 vs 250
        g_opt_AtapiSendDisableIntr = (BOOLEAN)AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"AtapiSendDisableIntr",  g_opt_AtapiSendDisableIntr) ? TRUE : FALSE;  // 1 vs 0
        g_opt_AtapiDmaRawRead      = (BOOLEAN)AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"AtapiDmaRawRead",       g_opt_AtapiDmaRawRead) ? TRUE : FALSE;       // 1 vs 0
        g_opt_MaxIsrWait    = AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"MaxIsrWait",       g_opt_MaxIsrWait);       // 40 vs xxx
    }

    // 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(!WinVer_WDM_Model && !PrimaryClaimed && !SecondaryClaimed &&
            !(BMList[i].ChanInitOk & 0x80)) {
            newStatus = UniataClaimLegacyPCIIDE(i);
            if(newStatus != STATUS_SUCCESS) {
                KdPrint2((PRINT_PREFIX "Can't acquire PCI part of BusMaster, try as pure ISA later.\n"));
                break;
            }
        }

        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) {
                    if(WinVer_Id() < WinVer_2k) {
                        // This should be done in HwInitialize under w2k+ to ensure that 
                        // channel is actually initialized
                        BMList[i].ChanInitOk |= 0x01 << c;
                    } else {
                        if(BMList[i].ChanInitOk & (0x01 << c)) {
                            KdPrint2((PRINT_PREFIX "HwInit passed\n"));
                        }
                    }
/*
                    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 didn't work under higher OSes,
                // until we move setting of FLAGS to HwInit
                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 if STATUS_SUCCESS returned.
                // Under w2k+ we relay on flags, set in HwInitialize.
                KdPrint2((PRINT_PREFIX "Ok, no more retries required\n"));
                break;
            } else
            if(WinVer_Id() >= WinVer_2k) {
                // try AltInit if HwInit was not called immediately under w2k+
                KdPrint2((PRINT_PREFIX "make still one attempt w2k+\n"));
            } else {
                // if (WinVer_Id() == WinVer_NT) and some error occured
                // try alternative init method
                KdPrint2((PRINT_PREFIX "make still one attempt w2k+\n"));
            }
        } // for(alt...)
        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             = (PVOID)BMList[i].VendorId;
        hwInitializationData.comm.VendorIdLength       = (USHORT) BMList[i].VendorIdLength;
        hwInitializationData.comm.DeviceId             = (PVOID)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 == (ULONG)STATUS_DEVICE_DOES_NOT_EXIST && BMList[i].NeedAltInit) {
            // Note: this is actually a BUG in scsiport.sys
            // It stops scanning PCI bus when reaches empty PCI Function inside Slot
            // However, this PCI Slot may have higher non-empty Functions
            // UniATA will perform all staff instead of ScsiPort under NT,
            // but for ReactOS it is better to patch ScsiPort.
            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;

    if(!BMListLen) {
        hwInitializationData.comm.SrbExtensionSize        = //FIELD_OFFSET(ATA_REQ, ata);
                                                            sizeof(ATA_REQ);
        KdPrint2((PRINT_PREFIX "using AtaReq sz %x\n", hwInitializationData.comm.SrbExtensionSize));
    }

    // 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);

    KdPrint2((PRINT_PREFIX " MechanismStatusSrb %#x\n", srb));

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

    KdPrint2((PRINT_PREFIX " RequestSenseSrb %#x\n", srb));

    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, REGRTL_STR_PTYPE 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          = REGRTL_STR_PTYPE 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);
    if(NT_SUCCESS(status)) {
        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);
            if(NT_SUCCESS(status)) {
                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
    );

#define DEBUG_MSG_BUFFER_SIZE   512

extern "C"
VOID
_cdecl
_PrintNtConsole(
    PCCH DebugMessage,
    ...
    )
{
    //int len;
    UCHAR dbg_print_tmp_buff[DEBUG_MSG_BUFFER_SIZE];
//    UNICODE_STRING msgBuff;
    va_list ap;
    va_start(ap, DebugMessage);

    /*len =*/ _vsnprintf((PCHAR)&dbg_print_tmp_buff[0], DEBUG_MSG_BUFFER_SIZE-1, DebugMessage, ap);

    dbg_print_tmp_buff[DEBUG_MSG_BUFFER_SIZE-1] = 0;

    //DbgPrint(((PCHAR)&(dbg_print_tmp_buff[0]))); // already done in KdPrint macro
    HalDisplayString(dbg_print_tmp_buff);

#ifdef _DEBUG
    if(g_LogToDisplay > 1) {
        AtapiStallExecution(g_LogToDisplay*1000);
    }
#endif // _DEBUG

    va_end(ap);

} // end PrintNtConsole()

