reactos/drivers/storage/ide/uniata/id_ata.cpp
Hermès Bélusca-Maïto 8a3822c4ea Sync with trunk r63647.
svn path=/branches/condrv_restructure/; revision=63648
2014-06-27 18:05:20 +00:00

10887 lines
402 KiB
C++

/*++
Copyright (c) 2002-2014 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);
} else {
break;
}
}
if(i) {
KdPrint2((PRINT_PREFIX "AtapiSuckPort2: overrun detected (%#x words)\n", i ));
}
return statusByte;
} // AtapiSuckPort2()
UCHAR
DDKFASTAPI
WaitOnBusy(
IN PHW_CHANNEL chan
)
{
ULONG i;
UCHAR Status;
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"));
// 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
// spacifying 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;
// 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));
}
} 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;
UCHAR 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)) {
#ifdef _DEBUG
UniataDumpAhciPortRegs(chan);
#endif
AtapiDisableInterrupts(deviceExtension, j);
UniataAhciReset(HwDeviceExtension, j);
} else {
KdPrint2((PRINT_PREFIX " skip not implemented\n"));
}
} 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: {
KdPrint2((PRINT_PREFIX " SIS/nVidia\n"));
if(!(ChipFlags & UNIATA_SATA))
goto default_reset;
break; }
case ATA_SILICON_IMAGE_ID: {
ULONG offset;
ULONG Channel = deviceExtension->Channel + j;
if(!(ChipFlags & UNIATA_SATA))
goto default_reset;
offset = ((Channel & 1) << 7) + ((Channel & 2) << 8);
/* disable PHY state change interrupt */
AtapiWritePortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x148 + offset, 0);
UniataSataClearErr(HwDeviceExtension, j, UNIATA_SATA_IGNORE_CONNECT, 0);
/* reset controller part for this channel */
AtapiWritePortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x48,
AtapiReadPortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x48) | (0xc0 >> Channel));
AtapiStallExecution(1000);
AtapiWritePortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x48,
AtapiReadPortEx4(NULL, (ULONGIO_PTR)(&deviceExtension->BaseIoAddressSATA_0), 0x48) & ~(0xc0 >> Channel));
break; }
case ATA_PROMISE_ID: {
break; }
default:
if(ChipFlags & UNIATA_SATA) {
KdPrint2((PRINT_PREFIX " SATA generic reset\n"));
UniataSataClearErr(HwDeviceExtension, j, UNIATA_SATA_IGNORE_CONNECT, 0);
}
default_reset:
KdPrint2((PRINT_PREFIX " send reset\n"));
AtapiWritePort1(chan, IDX_IO2_o_Control, IDE_DC_DISABLE_INTERRUPTS |
IDE_DC_RESET_CONTROLLER );
KdPrint2((PRINT_PREFIX " wait a little\n"));
AtapiStallExecution(10000);
// Disable interrupts
KdPrint2((PRINT_PREFIX " disable intr\n"));
AtapiDisableInterrupts(deviceExtension, j);
AtapiStallExecution(100);
KdPrint2((PRINT_PREFIX " re-enable intr\n"));
AtapiEnableInterrupts(deviceExtension, j);
KdPrint2((PRINT_PREFIX " wait a little (2)\n"));
AtapiStallExecution(100000);
KdPrint2((PRINT_PREFIX " done\n"));
break;
} // end switch()
//if(!(ChipFlags & UNIATA_SATA)) {}
if(!UniataIsSATARangeAvailable(deviceExtension, j)) {
// Reset DMA engine if active
KdPrint2((PRINT_PREFIX " check DMA engine\n"));
dma_status = GetDmaStatus(chan->DeviceExtension, chan->lChannel);
KdPrint2((PRINT_PREFIX " DMA status %#x\n", dma_status));
if((ChannelCtrlFlags & CTRFLAGS_DMA_ACTIVE) ||
(dma_status & BM_STATUS_INTR)) {
AtapiDmaDone(HwDeviceExtension, 0, j, NULL);
}
}
} // 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) {
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 {
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\n", pr_status));
/* check for and handle connect events */
if(((pr_status & (0x0cUL << shift)) == (0x04UL << shift)) ) {
UniataSataEvent(deviceExtension, lChannel, UNIATA_SATA_EVENT_ATTACH, 0);
}
/* check for and handle disconnect events */
if((pr_status & (0x08UL << shift)) &&
!((pr_status & (0x04UL << shift) &&
UniataSataReadPort4(chan, IDX_SATA_SStatus, 0))) ) {
UniataSataEvent(deviceExtension, lChannel, UNIATA_SATA_EVENT_DETACH, 0);
}
/* do we have any device action ? */
if(!(pr_status & (0x01UL << shift))) {
KdPrint2((PRINT_PREFIX " nVidia unexpected\n"));
if(UniataSataClearErr(HwDeviceExtension, c, UNIATA_SATA_DO_CONNECT, 0)) {
OurInterrupt = 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
#ifdef _DEBUG
UniataDumpAhciPortRegs(chan);
#endif
if(!UniataAhciAbortOperation(chan)) {
KdPrint2((PRINT_PREFIX "need UniataAhciReset\n"));
}
#ifdef _DEBUG
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
#ifdef _DEBUG
UniataDumpAhciPortRegs(chan);
#endif
if(!UniataAhciAbortOperation(chan)) {
KdPrint2((PRINT_PREFIX "need UniataAhciReset\n"));
}
// clear interrupts again
UniataAhciWaitCommandReady(chan, 10);
#ifdef _DEBUG
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;
}
}
#ifdef _DEBUG
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;
AtaReq->Flags |= REQ_FLAG_FORCE_DOWNRATE;
// LunExt->DeviceFlags |= DFLAGS_FORCE_DOWNRATE;
}
AtaReq->ReqState = REQ_STATE_QUEUED;
goto reenqueue_req;
}
} else {
if(!(AtaReq->Flags & REQ_FLAG_FORCE_DOWNRATE)) {
AtaReq->retry++;
}
KdPrint2((PRINT_PREFIX "Errors in PIO mode\n"));
}
}
} else {
interruptReason = (AtapiReadPort1(chan, IDX_ATAPI_IO1_i_InterruptReason) & ATAPI_IR_Mask);
KdPrint3((PRINT_PREFIX "AtapiInterrupt: ATAPI Error, int reason %x\n", interruptReason));
if(DmaTransfer && (chan->lun[DeviceNumber]->TransferMode > ATA_UDMA2) &&
((error >> 4) == SCSI_SENSE_HARDWARE_ERROR)) {
if(AtaReq->retry < MAX_RETRIES) {
//fallback_pio:
AtaReq->Flags &= ~REQ_FLAG_DMA_OPERATION;
AtaReq->Flags |= REQ_FLAG_FORCE_DOWNRATE;
// LunExt->DeviceFlags |= DFLAGS_FORCE_DOWNRATE;
AtaReq->ReqState = REQ_STATE_QUEUED;
goto reenqueue_req;
}
} else {
if(!(AtaReq->Flags & REQ_FLAG_FORCE_DOWNRATE)) {
AtaReq->retry++;
}
KdPrint3((PRINT_PREFIX "Errors in PIO mode\n"));
}
}
KdPrint3((PRINT_PREFIX "AtapiInterrupt: Error\n"));
if (srb->Cdb[0] != SCSIOP_REQUEST_SENSE) {
// Fail this request.
status = SRB_STATUS_ERROR;
goto CompleteRequest;
} else {
KdPrint2((PRINT_PREFIX " continue with SCSIOP_REQUEST_SENSE\n"));
}
} else
if(AtaReq->Flags & REQ_FLAG_FORCE_DOWNRATE_LBA48) {
KdPrint2((PRINT_PREFIX "DMA doesn't work right with LBA48\n"));
deviceExtension->HbaCtrlFlags |= HBAFLAGS_DMA_DISABLED_LBA48;
} else
if(AtaReq->Flags & REQ_FLAG_FORCE_DOWNRATE) {
#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 {
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));
// Fail this request.
status = SRB_STATUS_ERROR;
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;
}
// 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;
}
// 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;
}
}
}
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 os 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;
}
//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;
// 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;
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:
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;
GetBaseStatus(chan, statusByte);
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_POWER_CONDITION_PAGE)-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 = 0;
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); // 1 vs 0
g_opt_AtapiDmaRawRead = (BOOLEAN)AtapiRegCheckDevValue(NULL, CHAN_NOT_SPECIFIED, DEVNUM_NOT_SPECIFIED, L"AtapiDmaRawRead", g_opt_AtapiDmaRawRead); // 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()