windows-nt/Source/XPSP1/NT/drivers/storage/ide/atapi/hack.c
2020-09-26 16:20:57 +08:00

1591 lines
39 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) 1997-99 Microsoft Corporation
Module Name:
hack.c
Abstract:
--*/
#include "ideport.h"
#include "hack.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, IdePortSlaveIsGhost)
#pragma alloc_text(PAGE, IdePortGetFlushCommand)
#pragma alloc_text(PAGE, IdePortMustBePio)
#pragma alloc_text(PAGE, IdePortPioByDefaultDevice)
#pragma alloc_text(PAGE, IdePortDeviceHasNonRemovableMedia)
#pragma alloc_text(PAGE, IdePortDeviceIsLs120)
#pragma alloc_text(PAGE, IdePortNoPowerDown)
#pragma alloc_text(PAGE, IdePortVerifyDma)
#pragma alloc_text(NONPAGE, IdePortFudgeAtaIdentifyData)
#pragma alloc_text(PAGE, IdePortIsThisAPanasonicPCMCIACard)
#pragma alloc_text(PAGE, IdeFindSpecialDevice)
/*
#pragma alloc_text(PAGE, IdePortIsThisASonyMemorystickPCMCIACard)
#pragma alloc_text(PAGE, IdePortSonyMemoryStick)
#pragma alloc_text(PAGE, IdePortReuseIdent)
#pragma alloc_text(PAGE, IdePortBadCdrom)
*/
#endif // ALLOC_PRAGMA
#if defined (FAKE_BMSETUP_FAILURE)
ULONG FailBmSetupCount = 0;
#endif // FAKE_BMSETUP_FAILURE
#if DBG
ULONG IdeDebugRescanBusFreq = 0;
ULONG IdeDebugRescanBusCounter = 0;
ULONG IdeDebugHungControllerFreq = 0;
ULONG IdeDebugHungControllerCounter = 0;
ULONG IdeDebugTimeoutAllCacheFlush = 0;
ULONG IdeDebugForceSmallCrashDumpBlockSize = 0;
PDEVICE_OBJECT IdeDebugDevObjTimeoutAllDmaSrb = 0;
ULONG IdeDebug = 0;
ULONG IdeDebugPrintControl = DBG_ALWAYS;
UCHAR IdeBuffer[0x1000];
VOID
IdeDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
/*++
Routine Description:
Debug print for all SCSI drivers
Arguments:
Debug print level between 0 and 3, with 3 being the most verbose.
Return Value:
None
--*/
{
BOOLEAN print = FALSE;
va_list ap;
va_start(ap, DebugMessage);
if (DebugPrintLevel & DBG_BIT_CONTROL) {
if (DebugPrintLevel == DBG_ALWAYS) {
print = TRUE;
} else if ((DebugPrintLevel & ~DBG_BIT_CONTROL) & IdeDebugPrintControl) {
print = TRUE;
}
} else {
if (DebugPrintLevel <= IdeDebug) {
print = TRUE;
}
}
if (print) {
vsprintf(IdeBuffer, DebugMessage, ap);
#ifdef ENABLE_DBG_PRINT
DbgPrint(IdeBuffer);
#else
DbgPrintEx(DPFLTR_IDEP_ID,
DPFLTR_INFO_LEVEL,
IdeBuffer
);
#endif
}
va_end(ap);
} // end IdeDebugPrint()
#endif
//
// if we see one of these slave device that looks like
// the master device, we will ignore the slave device
//
BOOLEAN
IdePortSlaveIsGhost (
IN OUT PFDO_EXTENSION FdoExtension,
IN PIDENTIFY_DATA MasterIdentifyData,
IN PIDENTIFY_DATA SlaveIdentifyData
)
{
ULONG length;
ULONG i;
PAGED_CODE();
length = sizeof (MasterIdentifyData->ModelNumber);
if (length == RtlCompareMemory (
MasterIdentifyData->ModelNumber,
SlaveIdentifyData->ModelNumber,
length)) {
if (IdePortSearchDeviceInRegMultiSzList (
FdoExtension,
MasterIdentifyData,
GHOST_SLAVE_DEVICE)) {
DebugPrint ((DBG_WARNING, "ATAPI: Found a ghost slave\n"));
return TRUE;
}
}
return FALSE;
}
UCHAR
IdePortGetFlushCommand (
IN OUT PFDO_EXTENSION FdoExtension,
IN OUT PPDO_EXTENSION PdoExtension,
IN PIDENTIFY_DATA IdentifyData
)
{
ULONG i;
UCHAR flushCommand;
BOOLEAN done;
PAGED_CODE();
ASSERT (FdoExtension);
ASSERT (PdoExtension);
ASSERT (IdentifyData);
done = FALSE;
//
// in hall of shame list?
//
if (IdePortSearchDeviceInRegMultiSzList (
FdoExtension,
IdentifyData,
NO_FLUSH_DEVICE)) {
DebugPrint ((DBG_WARNING, "ATAPI: found a device that couldn't handle any flush command\n"));
flushCommand = IDE_COMMAND_NO_FLUSH;
done = TRUE;
} else if (IdePortSearchDeviceInRegMultiSzList (
FdoExtension,
IdentifyData,
CHECK_POWER_FLUSH_DEVICE)) {
DebugPrint ((DBG_WARNING, "ATAPI: found a device that has to use check power mode command to flush\n"));
flushCommand = IDE_COMMAND_CHECK_POWER;
done = TRUE;
}
if (!done) {
//
// real ATA-4 drive?
//
if ((IdentifyData->MajorRevision != 0x0000) &&
(IdentifyData->MajorRevision != 0xffff)) {
USHORT version;
version = IdentifyData->MajorRevision & ATA_VERSION_MASK;
if (version & ~(ATA1_COMPLIANCE | ATA2_COMPLIANCE | ATA3_COMPLIANCE)) {
//
// ATA-4 Flush Command
//
flushCommand = IDE_COMMAND_FLUSH_CACHE;
done = TRUE;
}
}
}
if (!done) {
ATA_PASS_THROUGH ataPassThroughData;
NTSTATUS status;
//
// try the ATA-4 flush command. maybe it will work.
//
RtlZeroMemory (&ataPassThroughData, sizeof (ataPassThroughData));
ataPassThroughData.IdeReg.bCommandReg = IDE_COMMAND_FLUSH_CACHE;
ataPassThroughData.IdeReg.bReserved = ATA_PTFLAGS_STATUS_DRDY_REQUIRED | ATA_PTFLAGS_ENUM_PROBING;
status = IssueSyncAtaPassThroughSafe (
FdoExtension,
PdoExtension,
&ataPassThroughData,
FALSE,
FALSE,
15,
FALSE
);
if (NT_SUCCESS(status)) {
if (!(ataPassThroughData.IdeReg.bCommandReg & IDE_STATUS_ERROR)) {
flushCommand = IDE_COMMAND_FLUSH_CACHE;
done = TRUE;
}
}
}
if (!done) {
// out of idea!
// choose the default
flushCommand = IDE_COMMAND_CHECK_POWER;
}
return flushCommand;
}
#if 0
BOOLEAN
IdePortReuseIdent(
IN PFDO_EXTENSION FdoExtension,
IN PIDENTIFY_DATA IdentifyData
)
{
PAGED_CODE();
//
// Determine if we can re-use the identify data
//
if (IdePortSearchDeviceInRegMultiSzList (
FdoExtension,
IdentifyData,
NEED_IDENT_DEVICE)) {
return TRUE;
}
return FALSE;
}
#endif
BOOLEAN
IdePortMustBePio (
IN PFDO_EXTENSION FdoExtension,
IN PIDENTIFY_DATA IdentifyData
)
{
PAGED_CODE();
//
// query pio only device from the registry
//
if (IdePortSearchDeviceInRegMultiSzList (
FdoExtension,
IdentifyData,
PIO_ONLY_DEVICE)) {
return TRUE;
}
return FALSE;
} // IdePortMustBePio
BOOLEAN
IdePortPioByDefaultDevice (
IN PFDO_EXTENSION FdoExtension,
IN PIDENTIFY_DATA IdentifyData
)
{
PAGED_CODE();
//
// query pio only device from the registry
//
if (IdePortSearchDeviceInRegMultiSzList (
FdoExtension,
IdentifyData,
DEFAULT_PIO_DEVICE)) {
return TRUE;
}
return FALSE;
} // IdePortMustBePio
BOOLEAN
IdePortDeviceHasNonRemovableMedia (
IN OUT PFDO_EXTENSION FdoExtension,
IN PIDENTIFY_DATA IdentifyData
)
{
BOOLEAN removableMediaOverride;
PAGED_CODE();
if (IsNEC_98) {
return ((IdentifyData->GeneralConfiguration & (1 << 7))? TRUE :
(!Is98LegacyIde(&FdoExtension->HwDeviceExtension->BaseIoAddress1)? TRUE : FALSE));
}
return (IdentifyData->GeneralConfiguration & (1 << 7)) ? TRUE : FALSE;
/*
removableMediaOverride = FALSE;
if (IdePortSearchDeviceInRegMultiSzList (
FdoExtension,
IdentifyData,
NONREMOVABLE_MEDIA_OVERRIDE)) {
removableMediaOverride = TRUE;
}
if (removableMediaOverride) {
return FALSE;
} else {
return (IdentifyData->GeneralConfiguration & (1 << 7)) ? TRUE : FALSE;
}
*/
}
BOOLEAN
IdePortDeviceIsLs120 (
IN PFDO_EXTENSION FdoExtension,
IN PIDENTIFY_DATA IdentifyData
)
{
UCHAR modelNumber[41];
ULONG i;
UCHAR ls120NameString[] = "LS-120";
PAGED_CODE();
//
// byte swap model number
//
for (i=0; i<40; i+=2) {
modelNumber[i + 0] = IdentifyData->ModelNumber[i + 1];
modelNumber[i + 1] = IdentifyData->ModelNumber[i + 0];
}
modelNumber[i] = 0;
return strstr(_strupr(modelNumber), ls120NameString) ? TRUE : FALSE;
} // IdePortDeviceIsLs120
BOOLEAN
IdePortNoPowerDown (
IN PFDO_EXTENSION FdoExtension,
IN PIDENTIFY_DATA IdentifyData
)
{
PAGED_CODE();
//
// query no power down device from the registry
//
if (IdePortSearchDeviceInRegMultiSzList (
FdoExtension,
IdentifyData,
NO_POWER_DOWN_DEVICE)) {
return TRUE;
}
return FALSE;
} // IdePortNoPowerDown
BOOLEAN
IdePortVerifyDma (
IN PPDO_EXTENSION pdoExtension,
IN IDE_DEVICETYPE ideDeviceType
)
{
NTSTATUS status;
ULONG oldDmaTransferTimeoutCount;
BOOLEAN dmaOk;
dmaOk = TRUE;
if (pdoExtension->DmaTransferTimeoutCount >= PDO_DMA_TIMEOUT_LIMIT) {
dmaOk = FALSE;
} else if (ideDeviceType == DeviceIsAtapi) {
INQUIRYDATA DmaInquiryData;
INQUIRYDATA PioInquiryData;
status = IssueInquirySafe(
pdoExtension->ParentDeviceExtension,
pdoExtension,
&DmaInquiryData,
FALSE);
if (NT_SUCCESS(status)) {
//
// force a pio transfer
//
oldDmaTransferTimeoutCount = InterlockedExchange(
&pdoExtension->DmaTransferTimeoutCount,
PDO_DMA_TIMEOUT_LIMIT
);
status = IssueInquirySafe(
pdoExtension->ParentDeviceExtension,
pdoExtension,
&PioInquiryData,
FALSE);
if (NT_SUCCESS(status) &&
(RtlCompareMemory (&DmaInquiryData, &PioInquiryData,
sizeof(DmaInquiryData)) != sizeof(DmaInquiryData))) {
dmaOk = FALSE;
//
// dma is not ok, leave the dma error count as PDO_DMA_TIMEOUT_LIMIT
// so that we are not going to use dma with this device
//
} else {
InterlockedExchange(
&pdoExtension->DmaTransferTimeoutCount,
oldDmaTransferTimeoutCount
);
}
}
} else if (ideDeviceType == DeviceIsAta) {
PUCHAR dmaDataBuffer;
PUCHAR pioDataBuffer;
CDB cdb;
//
// the only non-desctrutive way to test dma on a
// ata device is to perform a pio read and a dma read and
// compare the data.
//
// this technique does not work if the device has a removable
// media and it is removed.
//
dmaDataBuffer = ExAllocatePool (
NonPagedPool,
512 * 2
);
if (dmaDataBuffer) {
pioDataBuffer = dmaDataBuffer + 512;
//
// setup dma pass through
//
RtlZeroMemory(&cdb, sizeof(CDB));
cdb.CDB10.OperationCode = SCSIOP_READ;
cdb.CDB10.TransferBlocksLsb = 1;
status = IssueSyncAtapiCommandSafe (
pdoExtension->ParentDeviceExtension,
pdoExtension,
&cdb,
dmaDataBuffer,
512,
TRUE,
2,
FALSE
);
if (NT_SUCCESS(status)) {
//
// setup pio pass through
//
RtlZeroMemory(&cdb, sizeof(CDB));
cdb.CDB10.OperationCode = SCSIOP_READ;
cdb.CDB10.TransferBlocksLsb = 1;
//
// force a pio transfer
//
oldDmaTransferTimeoutCount = InterlockedExchange(
&pdoExtension->DmaTransferTimeoutCount,
PDO_DMA_TIMEOUT_LIMIT
);
status = IssueSyncAtapiCommand (
pdoExtension->ParentDeviceExtension,
pdoExtension,
&cdb,
pioDataBuffer,
512,
TRUE,
2,
FALSE
);
if (NT_SUCCESS(status) &&
(RtlCompareMemory (
dmaDataBuffer,
pioDataBuffer,
512) != 512)) {
dmaOk = FALSE;
//
// dma is not ok, leave the dma error count as PDO_DMA_TIMEOUT_LIMIT
// so that we are not going to use dma with this device
//
} else {
InterlockedExchange(
&pdoExtension->DmaTransferTimeoutCount,
oldDmaTransferTimeoutCount
);
}
}
}
if (dmaDataBuffer) {
ExFreePool (dmaDataBuffer);
}
}
#if DBG
#if defined (FAKE_BROKEN_DMA_DEVICE)
InterlockedExchange(
&pdoExtension->DmaTransferTimeoutCount,
PDO_DMA_TIMEOUT_LIMIT
);
dmaOk = FALSE;
#endif // FAKE_BROKEN_DMA_DEVICE
#endif // DBG
if (!dmaOk) {
ERROR_LOG_ENTRY errorLogEntry;
errorLogEntry.ErrorCode = SP_BAD_FW_ERROR;
errorLogEntry.MajorFunctionCode = IRP_MJ_SCSI;
errorLogEntry.PathId = pdoExtension->PathId;
errorLogEntry.TargetId = pdoExtension->TargetId;
errorLogEntry.Lun = pdoExtension->Lun;
errorLogEntry.UniqueId = ERRLOGID_LYING_DMA_SYSTEM;
errorLogEntry.ErrorLogRetryCount = 0;
errorLogEntry.SequenceNumber = 0;
LogErrorEntry(
pdoExtension->ParentDeviceExtension,
&errorLogEntry
);
DebugPrint ((
DBG_ALWAYS,
"ATAPI: system and/or device lies about its dma capability. pdoe = 0x%x\n",
pdoExtension
));
}
return dmaOk;
}
VOID
IdePortFudgeAtaIdentifyData(
IN OUT PIDENTIFY_DATA IdentifyData
)
{
if (IdentifyData->GeneralConfiguration == 0xffff) {
//
// guessing we have a really old ATA drive
// fake the GeneralConfiguration value
//
CLRMASK (
IdentifyData->GeneralConfiguration,
(IDE_IDDATA_REMOVABLE | (1 << 15))
);
}
}
#define PANASONIC_PCMCIA_IDE_DEVICE L"PCMCIA\\KME-KXLC005-A99E"
BOOLEAN
IdePortIsThisAPanasonicPCMCIACard(
IN PFDO_EXTENSION FdoExtension
)
{
IO_STATUS_BLOCK ioStatus;
KEVENT pnpEvent;
NTSTATUS status;
PDEVICE_OBJECT targetObject;
PIO_STACK_LOCATION irpStack;
PIRP pnpIrp;
BOOLEAN result = FALSE;
PAGED_CODE();
targetObject = FdoExtension->AttacheeDeviceObject;
//
// Initialize the event
//
KeInitializeEvent( &pnpEvent, SynchronizationEvent, FALSE );
//
// Build an Irp
//
pnpIrp = IoBuildSynchronousFsdRequest(
IRP_MJ_PNP,
FdoExtension->AttacheeDeviceObject,
NULL,
0,
NULL,
&pnpEvent,
&ioStatus
);
if (pnpIrp == NULL) {
return FALSE;
}
//
// Pnp Irps all begin life as STATUS_NOT_SUPPORTED;
//
pnpIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
pnpIrp->IoStatus.Information = 0;
//
// Set the top of stack
//
irpStack = IoGetNextIrpStackLocation( pnpIrp );
RtlZeroMemory( irpStack, sizeof(IO_STACK_LOCATION ) );
irpStack->MajorFunction = IRP_MJ_PNP;
irpStack->MinorFunction = IRP_MN_QUERY_ID;
irpStack->Parameters.QueryId.IdType = BusQueryDeviceID;
//
// Make sure that there are no completion routines set
//
IoSetCompletionRoutine(
pnpIrp,
NULL,
NULL,
FALSE,
FALSE,
FALSE
);
//
// Call the driver
//
status = IoCallDriver( targetObject, pnpIrp );
if (status == STATUS_PENDING) {
//
// Block until the irp comes back
//
KeWaitForSingleObject(
&pnpEvent,
Executive,
KernelMode,
FALSE,
NULL
);
status = ioStatus.Status;
}
if (NT_SUCCESS(status)) {
UNICODE_STRING panasonicDeviceId;
UNICODE_STRING deviceId;
RtlInitUnicodeString (&panasonicDeviceId, PANASONIC_PCMCIA_IDE_DEVICE);
RtlInitUnicodeString (&deviceId, (PWCHAR) ioStatus.Information);
if (!RtlCompareUnicodeString(
&deviceId,
&panasonicDeviceId,
TRUE)) {
result = TRUE;
}
ExFreePool ((PVOID) ioStatus.Information);
}
return result;
}
//
// Timing Code
//
#if defined (ENABLE_TIME_LOG)
TIME_LOG TimeLog[TimeMax] = {0};
VOID
LogStartTime(
TIME_ID id,
PLARGE_INTEGER timer
)
{
*timer = KeQueryPerformanceCounter(NULL);
}
VOID
LogStopTime(
TIME_ID id,
PLARGE_INTEGER timer,
ULONG waterMark
)
{
LARGE_INTEGER freq;
LARGE_INTEGER stopTime;
LARGE_INTEGER diffTime;
LARGE_INTEGER diffTimeInMicroSec;
stopTime = KeQueryPerformanceCounter(&freq);
diffTime.QuadPart = stopTime.QuadPart - timer->QuadPart;
diffTimeInMicroSec.QuadPart = (diffTime.QuadPart * 1000000) / freq.QuadPart;
// need a spinlock
if (TimeLog[id].min.QuadPart == 0) {
TimeLog[id].min.QuadPart = 0x7fffffffffffffffL;
}
if (diffTime.QuadPart < TimeLog[id].min.QuadPart) {
TimeLog[id].min = diffTime;
}
if (diffTime.QuadPart > TimeLog[id].max.QuadPart) {
TimeLog[id].max = diffTime;
}
TimeLog[id].totalTimeInMicroSec.QuadPart += diffTimeInMicroSec.QuadPart;
TimeLog[id].numLog.QuadPart++;
if (waterMark) {
if (diffTimeInMicroSec.LowPart > waterMark) {
DebugPrint ((DBG_ALWAYS, "IdePort: timerID 0x%d took %d us\n", id, (ULONG) diffTimeInMicroSec.LowPart));
}
}
}
#endif // ENABLE_TIME_LOG
#if defined (IDE_BUS_TRACE)
ULONG IdePortBusTraceTableMaxEntries = 0x20000;
BUS_TRACE_LOG IdePortBusTaceLog = {0, 0, 0, FALSE};
VOID InitBusTraceLogTable (
VOID
)
{
ASSERT (IdePortBusTaceLog.LogTable == NULL);
//
// make sure MAX_ULONG + 1 is a multiple of total log entries
// so that when the index wraps, we don't skin any log entry
//
ASSERT(!((((ULONGLONG) 0xffffffff) + 1) % IdePortBusTraceTableMaxEntries));
IdePortBusTaceLog.LogTable =
ExAllocatePool (NonPagedPool, IdePortBusTraceTableMaxEntries * sizeof(BUS_TRACE_RECORD));
if (IdePortBusTaceLog.LogTable) {
IdePortBusTaceLog.NumLogTableEntries = IdePortBusTraceTableMaxEntries;
IdePortBusTaceLog.LastLogTableEntry = -1;
IdePortBusTaceLog.TableWrapped = FALSE;
KeInitializeSpinLock(&IdePortBusTaceLog.SpinLock);
}
}
VOID FreeBusTraceLogTable (
VOID
)
{
if (IdePortBusTaceLog.LogTable) {
ExFreePool (IdePortBusTaceLog.LogTable);
RtlZeroMemory(&IdePortBusTaceLog, sizeof(IdePortBusTaceLog));
}
}
VOID
IdepUpdateTraceLog (
IO_TYPE IoType,
PVOID PortAddress,
ULONG Data
)
{
KIRQL currentIrql;
ULONG lastEntry;
if (IdePortBusTaceLog.LogTable) {
lastEntry = InterlockedIncrement(&IdePortBusTaceLog.LastLogTableEntry);
lastEntry--;
lastEntry %= IdePortBusTaceLog.NumLogTableEntries;
IdePortBusTaceLog.LogTable[lastEntry].IoType = IoType;
IdePortBusTaceLog.LogTable[lastEntry].Address = PortAddress;
IdePortBusTaceLog.LogTable[lastEntry].Data = Data;
IdePortBusTaceLog.LogTable[lastEntry].Count = 1;
}
}
UCHAR
IdepPortInPortByte (
PUCHAR PortAddress
)
{
KIRQL currentIrql;
UCHAR data;
data = READ_PORT_UCHAR(PortAddress);
IdepUpdateTraceLog (InPortByte, PortAddress, (ULONG) data);
return data;
}
VOID
IdepPortOutPortByte (
PUCHAR PortAddress,
UCHAR Data
)
{
WRITE_PORT_UCHAR(PortAddress, Data);
IdepUpdateTraceLog (OutPortByte, PortAddress, (ULONG) Data);
return;
}
USHORT
IdepPortInPortWord (
PUSHORT PortAddress
)
{
KIRQL currentIrql;
USHORT data;
data = READ_PORT_USHORT(PortAddress);
IdepUpdateTraceLog (InPortWord, PortAddress, (ULONG) data);
return data;
}
VOID
IdepPortOutPortWord (
PUSHORT PortAddress,
USHORT Data
)
{
WRITE_PORT_USHORT(PortAddress, Data);
IdepUpdateTraceLog (OutPortWord, PortAddress, (ULONG) Data);
return;
}
VOID
IdepPortInPortWordBuffer (
PUSHORT PortAddress,
PUSHORT Buffer,
ULONG Count
)
{
ULONG i;
for (i=0; i<Count; i++) {
Buffer[i] = IdepPortInPortWord (PortAddress);
}
return;
}
VOID
IdepPortOutPortWordBuffer (
PUSHORT PortAddress,
PUSHORT Buffer,
ULONG Count
)
{
ULONG i;
for (i=0; i<Count; i++) {
IdepPortOutPortWord (PortAddress, Buffer[i]);
}
return;
}
#endif // IDE_BUS_TRACE
SPECIAL_ACTION_FLAG
IdeFindSpecialDevice(
IN PUCHAR VendorProductId,
IN PUCHAR ProductRevisionId
)
/*++
Routine Description:
This routine will search the IDE special device table to determine whether
any special behavior should be enabled for this device. The match is made upto
the strlen of VendorProductId in the table.
Arguments:
VendorProductId - the full vendor & product ID of the device in question.
ProductRevisionId - the full product revision ID of the device in question.
ReturnValue:
an ulong which describes the limitations of the device
in question.
--*/
{
IDE_SPECIAL_DEVICE IdeSpecialDeviceTable[] = {
{"TOSHIBA CD-ROM XM-1702B", NULL, disableSerialNumber},
{"TOSHIBA CD-ROM XM-6202B", NULL, disableSerialNumber},
{"COMPAQ DVD-ROM DRD-U424", NULL, disableSerialNumber},
{" " , NULL, disableSerialNumber},
{"KENWOOD CD-ROM", NULL, skipModeSense},
{"MEMORYSTICK", NULL, setFlagSonyMemoryStick},
{NULL, NULL, noSpecialAction}
};
ULONG i;
ULONG length;
PAGED_CODE();
//
// if both the arguments are null, then just return no special action
//
if (VendorProductId == NULL &&
ProductRevisionId == NULL) {
return noSpecialAction;
}
for(i = 0; IdeSpecialDeviceTable[i].VendorProductId != NULL; i++) {
//
// Match only upto the strlen of the productID in the table
// This will allow special action for all the models from a particular vendor.
//
length=strlen(IdeSpecialDeviceTable[i].VendorProductId);
if (length != RtlCompareMemory(IdeSpecialDeviceTable[i].VendorProductId,
VendorProductId, length)) {
continue;
}
//
// Partial matches are not acceptable for revision Ids.
//
if((IdeSpecialDeviceTable[i].Revision != NULL) &&
(strcmp(IdeSpecialDeviceTable[i].Revision,
ProductRevisionId) != 0)) {
continue;
}
//
// We've got a match. Break out.
//
break;
}
//
// Return whatever entry we're pointing at. If we matched based on the
// id's then this will be the matching entry. If we broke out of the
// loop then this will be the last entry in the list which is the
// benign, "nothing special about this device" entry that we return
// for a failed match.
//
return (IdeSpecialDeviceTable[i].RequiredAction);
}
#ifdef ENABLE_COMMAND_LOG
VOID
IdeLogOpenCommandLog(
PSRB_DATA SrbData
)
{
if (SrbData->IdeCommandLog == NULL) {
SrbData->IdeCommandLog = ExAllocatePool(
NonPagedPool,
MAX_COMMAND_LOG_ENTRIES*sizeof(COMMAND_LOG)
);
if (SrbData->IdeCommandLog != NULL) {
RtlZeroMemory(SrbData->IdeCommandLog, MAX_COMMAND_LOG_ENTRIES*sizeof(COMMAND_LOG));
}
SrbData->IdeCommandLogIndex = 0;
}
return;
}
VOID
IdeLogStartCommandLog(
PSRB_DATA SrbData
)
{
PCOMMAND_LOG cmdLog = &(SrbData->IdeCommandLog[SrbData->IdeCommandLogIndex]);
PSCSI_REQUEST_BLOCK srb = SrbData->CurrentSrb;
ASSERT(srb);
if (cmdLog == NULL) {
return;
}
UpdateStartTimeStamp(cmdLog);
if (srb->Function == SRB_FUNCTION_ATA_PASS_THROUGH ||
srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH) {
PATA_PASS_THROUGH ataPassThroughData = srb->DataBuffer;
cmdLog->Cdb[0]= srb->Function;
RtlCopyMemory(&(cmdLog->Cdb[1]), &(ataPassThroughData->IdeReg), sizeof(IDEREGS));
} else {
RtlCopyMemory(&(cmdLog->Cdb), &(srb->Cdb), sizeof(CDB));
}
return;
}
VOID
IdeLogStopCommandLog(
PSRB_DATA SrbData
)
{
PCOMMAND_LOG cmdLog = &(SrbData->IdeCommandLog[SrbData->IdeCommandLogIndex]);
PSCSI_REQUEST_BLOCK srb = SrbData->CurrentSrb;
PSENSE_DATA senseBuffer = NULL;
ULONG senseInfoBufferLength;
ASSERT(srb);
if (cmdLog == NULL) {
return;
}
UpdateStopTimeStamp(cmdLog);
if (srb->Cdb[0] == SCSIOP_REQUEST_SENSE) {
senseBuffer = srb->DataBuffer;
senseInfoBufferLength = srb->DataTransferLength;
} else if (srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
senseBuffer = srb->SenseInfoBuffer;
senseInfoBufferLength = (ULONG) srb->SenseInfoBufferLength;
}
if (senseBuffer && (senseInfoBufferLength > FIELD_OFFSET(SENSE_DATA, AdditionalSenseCodeQualifier))) {
cmdLog->SenseData[0] = senseBuffer->SenseKey;
cmdLog->SenseData[1] = senseBuffer->AdditionalSenseCode;
cmdLog->SenseData[2] = senseBuffer->AdditionalSenseCodeQualifier;
}else {
cmdLog->SenseData[0] = 0;
cmdLog->SenseData[1] = 0;
cmdLog->SenseData[2] = 0;
}
SrbData->IdeCommandLogIndex = ( SrbData->IdeCommandLogIndex + 1) % MAX_COMMAND_LOG_ENTRIES;
return;
}
VOID
IdeLogSaveTaskFile(
PSRB_DATA SrbData,
PIDE_REGISTERS_1 BaseIoAddress
)
{
PCOMMAND_LOG cmdLog = &(SrbData->IdeCommandLog[SrbData->IdeCommandLogIndex]);
if (cmdLog == NULL) {
return;
}
AtapiTaskRegisterSnapshot(BaseIoAddress, &(cmdLog->FinalTaskFile));
return;
}
VOID
IdeLogBmStatus(
PSCSI_REQUEST_BLOCK Srb,
BMSTATUS BmStatus
)
{
PSRB_DATA srbData = IdeGetSrbData(NULL, Srb);
PCOMMAND_LOG cmdLog;
if (srbData == NULL) {
return;
}
cmdLog = &(srbData->IdeCommandLog[srbData->IdeCommandLogIndex]);
if (cmdLog == NULL) {
return;
}
cmdLog->BmStatus = BmStatus;
return;
}
VOID
IdeLogFreeCommandLog(
PSRB_DATA SrbData
)
{
PCOMMAND_LOG cmdLog = SrbData->IdeCommandLog;
if (cmdLog) {
ExFreePool(cmdLog);
}
SrbData->IdeCommandLog = NULL;
SrbData->IdeCommandLogIndex = 0;
}
#endif
#ifdef ENABLE_ATAPI_VERIFIER
PVOID ViIdeExtensionTable[2];
#define VFLAGS_FORCE_TIMEOUT (1<<0)
#define VFLAGS_DMA_TIMEOUT (1<<1)
#define VFLAGS_CFLUSH_TIMEOUT (1<<2)
#define VFLAGS_DEVICE_CHANGE (1<<3)
#define VFLAGS_MISSING_DEVICE (1<<4)
#define VFLAGS_ACTUAL_ERROR (1<<5)
#define VFLAGS_CRC_ERROR (1<<6)
#define VFLAGS_BUSY_ERROR (1<<7)
#define VFLAGS_RW_ERROR (1<<8)
VOID
ViIdeInitVerifierSettings(
IN PFDO_EXTENSION FdoExtension
)
{
}
BOOLEAN
ViIdeGenerateDmaTimeout(
IN PVOID HwDeviceExtension,
IN BOOLEAN DmaInProgress
)
{
PHW_DEVICE_EXTENSION hwDeviceExtension = HwDeviceExtension;
PFDO_EXTENSION fdoExtension = ((PFDO_EXTENSION)HwDeviceExtension - 1);
PSCSI_REQUEST_BLOCK srb = hwDeviceExtension->CurrentSrb;
ULONG ideInternalVerifierFlags ;
ASSERT(srb);
ideInternalVerifierFlags = fdoExtension->IdeInternalVerifierFlags[srb->TargetId];
if (ideInternalVerifierFlags & VFLAGS_FORCE_TIMEOUT) {
return TRUE;
}
if (DmaInProgress && (ideInternalVerifierFlags & VFLAGS_DMA_TIMEOUT)) {
return TRUE;
}
if ((srb->Function == SRB_FUNCTION_ATA_PASS_THROUGH) ||
(srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH)) {
PATA_PASS_THROUGH ataPassThroughData;
PIDEREGS pIdeReg;
ataPassThroughData = srb->DataBuffer;
pIdeReg = &ataPassThroughData->IdeReg;
if ((ideInternalVerifierFlags & VFLAGS_CFLUSH_TIMEOUT) &&
(pIdeReg->bCommandReg == hwDeviceExtension->DeviceParameters[srb->TargetId].IdeFlushCommand )) {
return TRUE;
}
}
return FALSE;
}
ULONG
ViIdeFakeDeviceChange(
IN PFDO_EXTENSION FdoExtension,
ULONG Target
)
{
ULONG ideInternalVerifierFlags = FdoExtension->IdeDebugVerifierFlags[Target];
if (ideInternalVerifierFlags & VFLAGS_DEVICE_CHANGE) {
return 1;
}
return 0;
}
BOOLEAN
ViIdeFakeMissingDevice(
IN PFDO_EXTENSION FdoExtension,
ULONG Target
)
{
ULONG ideInternalVerifierFlags = FdoExtension->IdeDebugVerifierFlags[Target];
if (ideInternalVerifierFlags & VFLAGS_MISSING_DEVICE) {
return TRUE;
}
return FALSE;
}
VOID
ViAtapiInterrupt(
IN PFDO_EXTENSION FdoExtension
)
{
PHW_DEVICE_EXTENSION hwDeviceExtension = FdoExtension->HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress = &(hwDeviceExtension->BaseIoAddress1);
PSCSI_REQUEST_BLOCK srb = hwDeviceExtension->CurrentSrb;
ULONG target;
//DebugPrint((0, "verifier interrupt fdoe = %x, b=%x\n", FdoExtension, baseIoAddress->RegistersBaseAddress));
if ((ULONG)(baseIoAddress->RegistersBaseAddress) == 0x1f0) {
ViIdeExtensionTable[0]=FdoExtension;
} else {
ViIdeExtensionTable[1]=FdoExtension;
}
if (srb == NULL) {
return ;
}
target = srb->TargetId;
//
// Generate timeouts
//
if (FdoExtension->IdeDebugVerifierFlags[target] & VFLAGS_DMA_TIMEOUT) {
FdoExtension->IdeInternalVerifierFlags[target] |= VFLAGS_DMA_TIMEOUT;
return;
}
//
// Generate CRC errors
//
if (FdoExtension->IdeDebugVerifierFlags[target] & VFLAGS_CRC_ERROR) {
if (FdoExtension->IdeVerifierEventCount[target][CrcEvent] >= FdoExtension->IdeVerifierEventFrequency[target][CrcEvent]) {
FdoExtension->IdeInternalVerifierFlags[target] |= VFLAGS_CRC_ERROR;
FdoExtension->IdeVerifierEventCount[target][CrcEvent]=0;
return;
} else {
FdoExtension->IdeVerifierEventCount[target][RwEvent]++;
}
}
//
// Generate Busy errors
//
if (FdoExtension->IdeDebugVerifierFlags[target] & VFLAGS_BUSY_ERROR) {
if (FdoExtension->IdeVerifierEventCount[target][BusyEvent] >= FdoExtension->IdeVerifierEventFrequency[target][BusyEvent]) {
FdoExtension->IdeInternalVerifierFlags[target] |= VFLAGS_BUSY_ERROR;
FdoExtension->IdeVerifierEventCount[target][BusyEvent]=0;
return;
} else {
FdoExtension->IdeVerifierEventCount[target][BusyEvent]++;
}
}
//
// Generate Read write errors
//
if (FdoExtension->IdeDebugVerifierFlags[target] & VFLAGS_RW_ERROR) {
if (FdoExtension->IdeVerifierEventCount[target][RwEvent] >= FdoExtension->IdeVerifierEventFrequency[target][RwEvent]) {
FdoExtension->IdeInternalVerifierFlags[target] |= VFLAGS_RW_ERROR;
FdoExtension->IdeVerifierEventCount[target][RwEvent]=0;
return;
} else {
FdoExtension->IdeVerifierEventCount[target][RwEvent]++;
}
}
// ViIdeGenerateReadWriteErrors(FdoExtension);
// ViIdeGenerateDmaErrors(FdoExtension);
// ViIdeFakeHungController(FdoExtension);
return ;
}
UCHAR
ViIdeGetBaseStatus(
PIDE_REGISTERS_1 BaseIoAddress
)
{
UCHAR status = IdePortInPortByte((BaseIoAddress)->Command);
/*
UCHAR deviceSelect = IdePortInPortByte(BaseIoAddress->DriveSelect);
UCHAR channel = ((ULONG)(BaseIoAddress->RegistersBaseAddress) == 0x1f0) ? 0: 1;
PFDO_EXTENSION fdoExtension = ViIdeExtensionTable[channel];
ULONG target = (deviceSelect == 0xA0)? 0: 1;
ULONG ideInternalVerifierFlags;
ULONG dFlags;
if (fdoExtension == NULL) {
return status;
}
ideInternalVerifierFlags = fdoExtension->IdeInternalVerifierFlags[target];
dFlags = fdoExtension->HwDeviceExtension->DeviceFlags[target];
if (status & IDE_STATUS_ERROR) {
SETMASK(fdoExtension->IdeInternalVerifierFlags[target], VFLAGS_ACTUAL_ERROR);
return status;
}
if (ideInternalVerifierFlags & VFLAGS_CRC_ERROR) {
return IDE_STATUS_ERROR;
}
if (ideInternalVerifierFlags & VFLAGS_BUSY_ERROR) {
return IDE_STATUS_BUSY;
}
if (ideInternalVerifierFlags & VFLAGS_RW_ERROR) {
return IDE_STATUS_ERROR;
}
*/
return status;
}
UCHAR
ViIdeGetErrorByte(
PIDE_REGISTERS_1 BaseIoAddress
)
/**++
Description:
Depending on the internalVerifier flag (set by the other verifier routines),
this function will return the appropriate error value. However, if the device
reports an actual error (as indicated by the internalverifier flag),
it is returned unchanged.
Arguments:
BaseIoAddress : Task file registers
Return Value:
The error byte.
--**/
{
UCHAR error = IdePortInPortByte(BaseIoAddress->Error);
/*
UCHAR deviceSelect = IdePortInPortByte(BaseIoAddress->DriveSelect);
UCHAR channel = ((ULONG)(BaseIoAddress->RegistersBaseAddress) == 0x1f0) ? 0: 1;
PFDO_EXTENSION fdoExtension = ViIdeExtensionTable[channel];
ULONG target = (deviceSelect == 0xA0)? 0: 1;
ULONG ideInternalVerifierFlags;
ULONG dFlags;
if (fdoExtension == NULL ) {
return error;
}
ideInternalVerifierFlags = fdoExtension->IdeInternalVerifierFlags[target];
dFlags = fdoExtension->HwDeviceExtension->DeviceFlags[target];
//
// return error if an actual error was reproted
//
if (ideInternalVerifierFlags & VFLAGS_ACTUAL_ERROR) {
CLRMASK(fdoExtension->IdeInternalVerifierFlags[target], VFLAGS_ACTUAL_ERROR);
return error;
}
if (ideInternalVerifierFlags & VFLAGS_CRC_ERROR) {
if (dFlags & DFLAGS_ATAPI_DEVICE) {
error = SCSI_SENSE_HARDWARE_ERROR << 4;
} else {
error = IDE_ERROR_CRC_ERROR | IDE_ERROR_COMMAND_ABORTED;
}
return error;
}
*/
return error;
}
#endif
#ifdef IDE_MEASURE_BUSSCAN_SPEED
VOID
LogBusScanStartTimer(
PLARGE_INTEGER TickCount
)
{
KeQueryTickCount(TickCount);
return;
}
ULONG
LogBusScanStopTimer(
PLARGE_INTEGER TickCount
)
{
LARGE_INTEGER tickCount2;
LARGE_INTEGER numMs;
KeQueryTickCount(&tickCount2);
numMs.QuadPart = ((tickCount2.QuadPart - TickCount->QuadPart) * KeQueryTimeIncrement()) / (10 * 1000);
return(numMs.u.LowPart);
}
#endif
VOID
FASTCALL
IdePortLogNoMemoryErrorFn(
IN PVOID DeviceExtension,
IN ULONG TargetId,
IN POOL_TYPE PoolType,
IN SIZE_T Size,
IN ULONG FailureLocationId,
IN ULONG Tag
)
/*++
Routine Description:
This routine writes a message to the event log indicating that an
allocation failure has occurred.
Arguments:
DeviceExtension - Fdo Extension
TargetId - The target Id of the device that the request was to be sent to
PoolType - identifies the pool the failed allocation attempt was from.
Size - indicates the number of bytes that the failed allocation
attempt tried to obtain.
Tag - identifies the pool tag associated with the failed
allocation.
LocationId - identifies the location in the source code where it failed
Return Value:
VOID
--*/
{
NTSTATUS status;
PFDO_EXTENSION deviceExtension = (PFDO_EXTENSION) (DeviceExtension);
PIO_ERROR_LOG_PACKET errorLogEntry;
PIO_ERROR_LOG_PACKET currentValue;
InterlockedIncrement(&deviceExtension->NumMemoryFailure);
//
// Try to allocate a new error log event.
//
errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
deviceExtension->DeviceObject,
ALLOC_FAILURE_LOGSIZE
);
//
// If we could not allocate a log event, we check the device extension to
// see if it has a reserve event we can use. If we cannot get the device
// extension or if it does not contain a reserve event, we return
// without logging the allocation failure.
//
if (errorLogEntry == NULL) {
//
// Get the reserve event in the device extension. The reserve event
// may have already been used, so it's possible that it is NULL. If
// this is the case, we give up and return.
//
errorLogEntry = (PIO_ERROR_LOG_PACKET)
deviceExtension->ReserveAllocFailureLogEntry[TargetId];
if (errorLogEntry == NULL) {
DebugPrint((1, "IdePortLogAllocationFailureFn: no reserve packet\n"));
return;
}
//
// We have to ensure that we are the only instance to use this
// event. To do so, we attempt to NULL the event in the driver
// extension. If somebody else beats us to it, they own the
// event and we have to give up.
//
currentValue = InterlockedCompareExchangePointer(
&(deviceExtension->ReserveAllocFailureLogEntry[TargetId]),
NULL,
errorLogEntry
);
if (errorLogEntry != currentValue) {
DebugPrint((1, "IdePortLogAllocationFailureFn: someone already owns packet\n"));
return;
}
}
//
// Log the error
//
errorLogEntry->ErrorCode = IO_WARNING_ALLOCATION_FAILED;
errorLogEntry->SequenceNumber = 0;
errorLogEntry->MajorFunctionCode = 0;
errorLogEntry->RetryCount = 0;
errorLogEntry->UniqueErrorValue = 0x10;
errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES;
errorLogEntry->DumpDataSize = 4 * sizeof(ULONG);
errorLogEntry->DumpData[0] = TargetId;
errorLogEntry->DumpData[1] = FailureLocationId;
errorLogEntry->DumpData[2] = PtrToUlong((PVOID)Size);
errorLogEntry->DumpData[3] = Tag;
IoWriteErrorLogEntry(errorLogEntry);
}