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

6619 lines
188 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) 1993-99 Microsoft Corporation
Module Name:
atapi.c
Abstract:
This is the miniport driver for IDE controllers.
Author:
Mike Glass (MGlass)
Chuck Park (ChuckP)
Joe Dai (joedai)
Environment:
kernel mode only
Notes:
Revision History:
george C.(georgioc) Merged wtih Compaq code to make miniport driver function
with the 120MB floppy drive
Added support for MEDIA STATUS NOTIFICATION
Added support for SCSIOP_START_STOP_UNIT (eject media)
joedai PCI Bus Master IDE Support
ATA Passthrough (temporary solution)
LBA with ATA drive > 8G
PCMCIA IDE support
Native mode support
--*/
#include "ideport.h"
#if DBG
ULONG __DebugForceTimeout = 0;
static ULONG __DebugResetCounter = 0;
#endif // DBG
#ifdef ALLOC_PRAGMA
#pragma alloc_text(NONPAGE, InitHwExtWithIdentify)
#endif // ALLOC_PRAGMA
BOOLEAN
IssueIdentify(
PIDE_REGISTERS_1 CmdBaseAddr,
PIDE_REGISTERS_2 CtrlBaseAddr,
IN ULONG DeviceNumber,
IN UCHAR Command,
IN BOOLEAN InterruptOff,
OUT PIDENTIFY_DATA IdentifyData
)
/*++
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.
InterruptOff - should leave interrupt disabled
Return Value:
TRUE if all goes well.
--*/
{
ULONG waitCount = 20000;
ULONG i,j;
UCHAR statusByte;
UCHAR signatureLow,
signatureHigh;
IDENTIFY_DATA fullIdentifyData;
RtlZeroMemory (IdentifyData, sizeof (IdentifyData));
//
// Select device 0 or 1.
//
SelectIdeDevice(CmdBaseAddr, DeviceNumber, 0);
//
// Check that the status register makes sense.
//
GetBaseStatus(CmdBaseAddr, statusByte);
if (Command == IDE_COMMAND_IDENTIFY) {
//
// Mask status byte ERROR bits.
//
CLRMASK (statusByte, IDE_STATUS_ERROR | IDE_STATUS_INDEX);
DebugPrint((1,
"IssueIdentify: Checking for IDE. Status (%x)\n",
statusByte));
//
// Check if register value is reasonable.
//
if (statusByte != IDE_STATUS_IDLE) {
//
// Reset the controller.
//
IdeHardReset (
CmdBaseAddr,
CtrlBaseAddr,
InterruptOff,
TRUE
);
SelectIdeDevice(CmdBaseAddr, DeviceNumber, 0);
//
// Another check for signature, to deal with one model Atapi that doesn't assert signature after
// a soft reset.
//
signatureLow = IdePortInPortByte(CmdBaseAddr->CylinderLow);
signatureHigh = IdePortInPortByte(CmdBaseAddr->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
//
// Device is Atapi.
//
return FALSE;
}
if (Is98LegacyIde(CmdBaseAddr)) {
AtapiSoftReset(CmdBaseAddr, CtrlBaseAddr, DeviceNumber, TRUE);
WaitOnBusy(CmdBaseAddr, statusByte);
if (!(statusByte & IDE_STATUS_ERROR)) {
//
// Device is WD-Mode cdrom.
//
return FALSE;
}
}
GetBaseStatus(CmdBaseAddr, statusByte);
CLRMASK (statusByte, IDE_STATUS_INDEX);
if (statusByte != IDE_STATUS_IDLE) {
//
// Give up on this.
//
return FALSE;
}
}
} else {
DebugPrint((1,
"IssueIdentify: Checking for ATAPI. Status (%x)\n",
statusByte));
}
//
// Load CylinderHigh and CylinderLow with number bytes to transfer.
//
IdePortOutPortByte(CmdBaseAddr->CylinderHigh, (0x200 >> 8));
IdePortOutPortByte(CmdBaseAddr->CylinderLow, (0x200 & 0xFF));
for (j = 0; j < 2; j++) {
//
// Send IDENTIFY command.
//
WaitOnBusy(CmdBaseAddr,statusByte);
IdePortOutPortByte(CmdBaseAddr->Command, Command);
WaitOnBusy(CmdBaseAddr,statusByte);
if (statusByte & IDE_STATUS_ERROR) {
continue;
}
//
// Wait for DRQ.
//
for (i = 0; i < 4; i++) {
WaitForDrq(CmdBaseAddr, statusByte);
if (statusByte & IDE_STATUS_DRQ) {
//
// Read status to acknowledge any interrupts generated.
//
GetBaseStatus(CmdBaseAddr, statusByte);
//
// One last check for Atapi.
//
signatureLow = IdePortInPortByte(CmdBaseAddr->CylinderLow);
signatureHigh = IdePortInPortByte(CmdBaseAddr->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
//
// Device is Atapi.
//
return FALSE;
}
break;
}
if (Command == IDE_COMMAND_IDENTIFY) {
//
// Check the signature. If DRQ didn't come up it's likely Atapi.
//
signatureLow = IdePortInPortByte(CmdBaseAddr->CylinderLow);
signatureHigh = IdePortInPortByte(CmdBaseAddr->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
//
// Device is Atapi.
//
return FALSE;
}
if (Is98LegacyIde(CmdBaseAddr)) {
AtapiSoftReset(CmdBaseAddr, CtrlBaseAddr, DeviceNumber, TRUE);
WaitOnBusy(CmdBaseAddr, statusByte);
if (!(statusByte & IDE_STATUS_ERROR)) {
//
// Device is WD-Mode cdrom.
//
return FALSE;
}
}
}
WaitOnBusy(CmdBaseAddr,statusByte);
}
if (i == 4 && j == 0) {
//
// Device didn't respond correctly. It will be given one more chances.
//
DebugPrint((1,
"IssueIdentify: DRQ never asserted (%x). Error reg (%x)\n",
statusByte,
IdePortInPortByte(CmdBaseAddr->Error)));
AtapiSoftReset(CmdBaseAddr, CtrlBaseAddr, DeviceNumber, InterruptOff);
GetStatus(CmdBaseAddr,statusByte);
DebugPrint((1,
"IssueIdentify: Status after soft reset (%x)\n",
statusByte));
} else {
break;
}
}
//
// Check for error on really bad master devices that assert random
// patterns of bits in the status register at the slave address.
//
if (statusByte & IDE_STATUS_ERROR) {
return FALSE;
}
DebugPrint((1,
"IssueIdentify: Status before read words %x\n",
statusByte));
//
// pull out 256 words. After waiting for one model that asserts busy
// after receiving the Packet Identify command.
//
WaitOnBusy(CmdBaseAddr,statusByte);
if (!(statusByte & IDE_STATUS_DRQ)) {
return FALSE;
}
if (Is98LegacyIde(CmdBaseAddr)) {
//
// Delay(10ms) for ATAPI CD-ROM device.
//
if (Command == IDE_COMMAND_ATAPI_IDENTIFY) {
for (i = 0; i < 100; i++) {
KeStallExecutionProcessor(100);
}
}
}
ReadBuffer(CmdBaseAddr,
(PUSHORT)&fullIdentifyData,
sizeof (fullIdentifyData) / 2);
RtlMoveMemory(IdentifyData,&fullIdentifyData,sizeof(*IdentifyData));
WaitOnBusy(CmdBaseAddr,statusByte);
for (i = 0; i < 0x10000; i++) {
GetStatus(CmdBaseAddr,statusByte);
if (statusByte & IDE_STATUS_DRQ) {
//
// pull out any remaining bytes and throw away.
//
READ_PORT_USHORT(CmdBaseAddr->Data);
} else {
break;
}
}
DebugPrint((3,
"IssueIdentify: Status after read words (%x)\n",
statusByte));
return TRUE;
} // end IssueIdentify()
VOID
InitDeviceGeometry(
PHW_DEVICE_EXTENSION HwDeviceExtension,
ULONG Device,
ULONG NumberOfCylinders,
ULONG NumberOfHeads,
ULONG SectorsPerTrack
)
{
ASSERT (HwDeviceExtension);
ASSERT (Device < HwDeviceExtension->MaxIdeDevice);
ASSERT (NumberOfCylinders);
ASSERT (NumberOfHeads);
ASSERT (SectorsPerTrack);
HwDeviceExtension->NumberOfCylinders[Device] = NumberOfCylinders;
HwDeviceExtension->NumberOfHeads[Device] = NumberOfHeads;
HwDeviceExtension->SectorsPerTrack[Device] = SectorsPerTrack;
return;
}
VOID
InitHwExtWithIdentify(
IN PVOID HwDeviceExtension,
IN ULONG DeviceNumber,
IN UCHAR Command,
IN PIDENTIFY_DATA IdentifyData,
IN BOOLEAN RemovableMedia
)
/*++
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.
InterruptOff - should leave interrupt disabled
Return Value:
TRUE if all goes well.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
ULONG i,j;
//
// Check out a few capabilities / limitations of the device.
//
if (IdentifyData->MediaStatusNotification == IDENTIFY_MEDIA_STATUS_NOTIFICATION_SUPPORTED) {
//
// Determine if this drive supports the MSN functions.
//
DebugPrint((2,"InitHwExtWithIdentify: Marking drive %d as removable. SFE = %d\n",
DeviceNumber,
IdentifyData->MediaStatusNotification));
deviceExtension->DeviceFlags[DeviceNumber] |= DFLAGS_MSN_SUPPORT;
}
if (RemovableMedia) {
//
// This device has removable media
//
deviceExtension->DeviceFlags[DeviceNumber] |= DFLAGS_REMOVABLE_DRIVE;
CLRMASK (deviceExtension->DeviceFlags[DeviceNumber], DFLAGS_IDENTIFY_VALID);
DebugPrint((2,
"InitHwExtWithIdentify: Device media is removable\n"));
} else {
DebugPrint((2,
"InitHwExtWithIdentify: Device media is NOT removable\n"));
}
if (IdentifyData->GeneralConfiguration & 0x20 &&
Command != IDE_COMMAND_IDENTIFY) {
//
// This device interrupts with the assertion of DRQ after receiving
// Atapi Packet Command
//
deviceExtension->DeviceFlags[DeviceNumber] |= DFLAGS_INT_DRQ;
DebugPrint((2,
"InitHwExtWithIdentify: Device interrupts on assertion of DRQ.\n"));
} else {
DebugPrint((2,
"InitHwExtWithIdentify: Device does not interrupt on assertion of DRQ.\n"));
}
if (((IdentifyData->GeneralConfiguration & 0xF00) == 0x100) &&
Command != IDE_COMMAND_IDENTIFY) {
//
// This is a tape.
//
deviceExtension->DeviceFlags[DeviceNumber] |= DFLAGS_TAPE_DEVICE;
DebugPrint((2,
"InitHwExtWithIdentify: Device is a tape drive.\n"));
} else {
DebugPrint((2,
"InitHwExtWithIdentify: Device is not a tape drive.\n"));
}
return;
} // InitHwExtWithIdentify
BOOLEAN
SetDriveParameters(
IN PVOID HwDeviceExtension,
IN ULONG DeviceNumber,
IN BOOLEAN Sync
)
/*++
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.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
ULONG i;
UCHAR statusByte;
DebugPrint ((DBG_BUSSCAN, "SetDriveParameters: %s %d\n", __FILE__, __LINE__));
DebugPrint((1,
"SetDriveParameters: Number of heads %x\n",
deviceExtension->NumberOfHeads[DeviceNumber]));
DebugPrint((1,
"SetDriveParameters: Sectors per track %x\n",
deviceExtension->SectorsPerTrack[DeviceNumber]));
if (deviceExtension->DeviceFlags[DeviceNumber] & DFLAGS_IDENTIFY_VALID) {
ASSERT(!(deviceExtension->DeviceFlags[DeviceNumber] & DFLAGS_REMOVABLE_DRIVE));
SETMASK(deviceExtension->DeviceFlags[DeviceNumber], DFLAGS_IDENTIFY_INVALID);
CLRMASK(deviceExtension->DeviceFlags[DeviceNumber], DFLAGS_IDENTIFY_VALID);
}
//
// Set up registers for SET PARAMETER command.
//
SelectIdeDevice(baseIoAddress1, DeviceNumber, (deviceExtension->NumberOfHeads[DeviceNumber] - 1));
IdePortOutPortByte(baseIoAddress1->BlockCount,
(UCHAR)deviceExtension->SectorsPerTrack[DeviceNumber]);
DebugPrint ((DBG_BUSSCAN, "SetDriveParameters: %s %d\n", __FILE__, __LINE__));
//
// Send SET PARAMETER command.
//
IdePortOutPortByte(baseIoAddress1->Command,
IDE_COMMAND_SET_DRIVE_PARAMETERS);
DebugPrint ((DBG_BUSSCAN, "SetDriveParameters: %s %d\n", __FILE__, __LINE__));
if (Sync) {
DebugPrint ((DBG_BUSSCAN, "SetDriveParameters: %s %d\n", __FILE__, __LINE__));
//
// Wait for ERROR or command complete.
//
WaitOnBusy(baseIoAddress1,statusByte);
DebugPrint ((DBG_BUSSCAN, "SetDriveParameters: %s %d\n", __FILE__, __LINE__));
if (statusByte & IDE_STATUS_BUSY) {
return FALSE;
} else {
if (statusByte & IDE_STATUS_ERROR) {
UCHAR errorByte;
errorByte = IdePortInPortByte(baseIoAddress1->Error);
DebugPrint((1,
"SetDriveParameters: Error bit set. Status %x, error %x\n",
errorByte,
statusByte));
return FALSE;
} else {
return TRUE;
}
}
} else {
return TRUE;
}
} // end SetDriveParameters()
BOOLEAN
AtapiResetController(
IN PVOID HwDeviceExtension,
IN ULONG PathId,
IN PULONG CallAgain
)
/*++
Routine Description:
Reset IDE controller and/or Atapi device.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Return Value:
Nothing.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2;
BOOLEAN result = FALSE;
ULONG i;
UCHAR statusByte;
ULONG numStates;
BOOLEAN enumProbing = FALSE;
ULONG lineNumber, deviceNumber;
BOOLEAN setResetFinalState;
baseIoAddress1 = &deviceExtension->BaseIoAddress1;
baseIoAddress2 = &deviceExtension->BaseIoAddress2;
if (*CallAgain == 0) {
#if DBG
__DebugResetCounter = 0;
#endif
// build state table
numStates = 0;
setResetFinalState = FALSE;
for (lineNumber = 0; lineNumber < (deviceExtension->MaxIdeDevice/MAX_IDE_DEVICE); lineNumber++) {
if (Is98LegacyIde(baseIoAddress1)) {
if (!(deviceExtension->DeviceFlags[lineNumber * MAX_IDE_DEVICE] & DFLAGS_DEVICE_PRESENT)) {
continue;
}
}
deviceExtension->ResetState.DeviceNumber[numStates] = lineNumber * MAX_IDE_DEVICE;
deviceExtension->ResetState.State[numStates++] = IdeResetBegin;
deviceExtension->ResetState.DeviceNumber[numStates] = lineNumber * MAX_IDE_DEVICE;
deviceExtension->ResetState.State[numStates++] = ideResetBusResetInProgress;
for (i = 0; i < MAX_IDE_DEVICE; i++) {
deviceNumber = (lineNumber * MAX_IDE_DEVICE) + i;
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) {
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_ATAPI_DEVICE) {
deviceExtension->ResetState.DeviceNumber[numStates] = deviceNumber;
deviceExtension->ResetState.State[numStates++] = ideResetAtapiReset;
deviceExtension->ResetState.DeviceNumber[numStates] = deviceNumber;
deviceExtension->ResetState.State[numStates++] = ideResetAtapiResetInProgress;
deviceExtension->ResetState.DeviceNumber[numStates] = deviceNumber;
deviceExtension->ResetState.State[numStates++] = ideResetAtapiIdentifyData;
} else {
deviceExtension->ResetState.DeviceNumber[numStates] = deviceNumber;
deviceExtension->ResetState.State[numStates++] = ideResetAtaIDP;
deviceExtension->ResetState.DeviceNumber[numStates] = deviceNumber;
deviceExtension->ResetState.State[numStates++] = ideResetAtaIDPInProgress;
deviceExtension->ResetState.DeviceNumber[numStates] = deviceNumber;
deviceExtension->ResetState.State[numStates++] = ideResetAtaMSN;
}
}
setResetFinalState = TRUE;
}
}
if (setResetFinalState) {
deviceExtension->ResetState.DeviceNumber[numStates] = 0;
deviceExtension->ResetState.State[numStates++] = ideResetFinal;
}
ASSERT (numStates <= RESET_STATE_TABLE_LEN);
}
SelectIdeLine (baseIoAddress1,((deviceExtension->ResetState.DeviceNumber[*CallAgain] >> 1) == 0)?0:1);
DebugPrint ((DBG_RESET,
"AtapiResetController CallAgain = 0x%x, device = 0x%x, busyCount = 0x%x\n",
*CallAgain,
deviceExtension->ResetState.DeviceNumber[*CallAgain],
deviceExtension->ResetState.WaitBusyCount
));
switch (deviceExtension->ResetState.State[*CallAgain]) {
case IdeResetBegin:
DebugPrint((DBG_RESET,
"AtapiResetController: Reset 0x%x IDE...\n",
deviceExtension->BaseIoAddress1.RegistersBaseAddress));
if (Is98LegacyIde(baseIoAddress1) && !(deviceExtension->ResetState.DeviceNumber[*CallAgain] & 0x2)) {
UCHAR driveHeadReg;
SelectIdeDevice(baseIoAddress1, deviceExtension->ResetState.DeviceNumber[*CallAgain], 0);
driveHeadReg = IdePortInPortByte(baseIoAddress1->DriveSelect);
if (driveHeadReg != ((deviceExtension->ResetState.DeviceNumber[*CallAgain] & 0x1) << 4 | 0xA0)) {
//
// Select the secondary line, when there is no primary line.
//
SelectIdeLine (baseIoAddress1, 1);
GetStatus(baseIoAddress1,statusByte);
}
}
//
// Check if request is in progress.
//
if (deviceExtension->CurrentSrb) {
enumProbing = TestForEnumProbing (deviceExtension->CurrentSrb);
if ((deviceExtension->CurrentSrb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH) ||
(deviceExtension->CurrentSrb->Function == SRB_FUNCTION_ATA_PASS_THROUGH)) {
PATA_PASS_THROUGH ataPassThroughData;
ataPassThroughData = deviceExtension->CurrentSrb->DataBuffer;
AtapiTaskRegisterSnapshot (baseIoAddress1, &ataPassThroughData->IdeReg);
}
//
// Complete outstanding request with SRB_STATUS_BUS_RESET.
//
IdePortCompleteRequest(deviceExtension,
deviceExtension->CurrentSrb,
(ULONG)SRB_STATUS_BUS_RESET);
//
// Clear request tracking fields.
//
deviceExtension->CurrentSrb = NULL;
deviceExtension->BytesLeft = 0;
deviceExtension->DataBuffer = NULL;
//
// Indicate ready for next request.
//
IdePortNotification(IdeNextRequest,
deviceExtension,
NULL);
}
//
// Clear DMA
//
if (deviceExtension->DMAInProgress) {
deviceExtension->DMAInProgress = FALSE;
deviceExtension->BusMasterInterface.BmDisarm (deviceExtension->BusMasterInterface.Context);
}
//
// Clear expecting interrupt flag.
//
deviceExtension->ExpectingInterrupt = FALSE;
deviceExtension->RDP = FALSE;
if (!enumProbing) {
//
// ATA soft reset. reset only ATA devices
//
IdeHardReset (
baseIoAddress1,
baseIoAddress2,
TRUE,
FALSE
);
(*CallAgain)++;
deviceExtension->ResetState.WaitBusyCount = 0;
//
// go to the next stage if a short wait satisfies the
// requirement
//
GetStatus(baseIoAddress1,statusByte);
WaitOnBusyUntil(baseIoAddress1, statusByte, 200);
if (statusByte & IDE_STATUS_BUSY) {
return TRUE;
} else {
return AtapiResetController(
HwDeviceExtension,
PathId,
CallAgain
);
}
} else {
//
// timeout is caused by a command sent to a non-existing device
// no need to reset
//
*CallAgain = 0;
}
return TRUE;
break;
//
// all these states waits for BUSY to go clear
// then go to the next state
//
case ideResetBusResetInProgress:
if (Is98LegacyIde(baseIoAddress1) && !(deviceExtension->ResetState.DeviceNumber[*CallAgain] & 0x2)) {
UCHAR driveHeadReg;
SelectIdeDevice(baseIoAddress1, deviceExtension->ResetState.DeviceNumber[*CallAgain], 0);
driveHeadReg = IdePortInPortByte(baseIoAddress1->DriveSelect);
if (driveHeadReg != ((deviceExtension->ResetState.DeviceNumber[*CallAgain] & 0x1) << 4 | 0xA0)) {
//
// Select the secondary line, when there is no primary line.
//
SelectIdeLine (baseIoAddress1, 1);
}
}
//
// select the slave when there is no master
//
if (!(deviceExtension->DeviceFlags[deviceExtension->ResetState.DeviceNumber[*CallAgain]] &
DFLAGS_DEVICE_PRESENT)) {
SelectIdeDevice(baseIoAddress1, (deviceExtension->ResetState.DeviceNumber[*CallAgain] | 0x1), 0);
}
case ideResetAtapiResetInProgress:
case ideResetAtaIDPInProgress:
GetStatus(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_BUSY) {
deviceExtension->ResetState.WaitBusyCount++;
if ((deviceExtension->ResetState.WaitBusyCount > 30) ||
(statusByte == 0xff)) {
UCHAR deviceSelect = IdePortInPortByte(baseIoAddress1->DriveSelect);
//
// reset fails
//
DebugPrint ((DBG_ALWAYS, "ATAPI ResetController: ATA soft reset fails\n"));
// if ((statusByte == 0xff) && (deviceSelect == 0xff)) {
//
// ULONG i;
//
// DebugPrint ((
// DBG_ALWAYS,
// "ATAPI: ide Channel 0x%x is gone!!\n",
// baseIoAddress1->RegistersBaseAddress
// ));
//
// for (i=0; i<deviceExtension->MaxIdeDevice; i++) {
//
// deviceExtension->DeviceFlags[i] &= ~DFLAGS_DEVICE_PRESENT;
// }
// }
if (IdePortChannelEmpty (baseIoAddress1,
baseIoAddress2, deviceExtension->MaxIdeDevice)) {
IdePortNotification(IdeAllDeviceMissing,
deviceExtension,
NULL);
*CallAgain = 0;
IdePortOutPortByte (baseIoAddress2->DeviceControl, IDE_DC_REENABLE_CONTROLLER);
return TRUE;
}
//
// no choice, but continue with the reset
//
// *CallAgain = 0;
// IdePortOutPortByte (baseIoAddress2, IDE_DC_REENABLE_CONTROLLER);
// return FALSE;
} else {
DebugPrint ((DBG_ALWAYS, "ATAPI: ResetController not ready (status = 0x%x) ...wait for 1 sec\n", statusByte));
return TRUE;
}
}
(*CallAgain)++;
return AtapiResetController(
HwDeviceExtension,
PathId,
CallAgain
);
break;
case ideResetAtapiReset:
SelectIdeDevice(baseIoAddress1, deviceExtension->ResetState.DeviceNumber[*CallAgain], 0);
IdePortOutPortByte(baseIoAddress1->Command, IDE_COMMAND_ATAPI_RESET);
deviceExtension->ResetState.WaitBusyCount = 0;
(*CallAgain)++;
//
// go to the next stage if a short wait satisfies the
// requirement
//
GetStatus(baseIoAddress1,statusByte);
WaitOnBusyUntil(baseIoAddress1, statusByte, 200);
if (statusByte & IDE_STATUS_BUSY) {
return TRUE;
} else {
return AtapiResetController(
HwDeviceExtension,
PathId,
CallAgain
);
}
return TRUE;
break;
case ideResetAtaIDP:
if (!Is98LegacyIde(baseIoAddress1)) {
SetDriveParameters(HwDeviceExtension,
deviceExtension->ResetState.DeviceNumber[*CallAgain],
FALSE);
}
deviceExtension->ResetState.WaitBusyCount = 0;
(*CallAgain)++;
//
// go to the next stage if a short wait satisfies the
// requirement
//
GetStatus(baseIoAddress1,statusByte);
WaitOnBusyUntil(baseIoAddress1, statusByte, 200);
if (statusByte & IDE_STATUS_BUSY) {
return TRUE;
} else {
return AtapiResetController(
HwDeviceExtension,
PathId,
CallAgain
);
}
return TRUE;
break;
case ideResetAtapiIdentifyData:
//
// the device shouldn't be busy at this point
//
SelectIdeDevice(&deviceExtension->BaseIoAddress1,
deviceExtension->ResetState.DeviceNumber[*CallAgain],
0
);
WaitOnBusyUntil(&deviceExtension->BaseIoAddress1,
statusByte,
100
);
//
// issue identify command only if the device is not
// already busy
//
if (!(statusByte & IDE_STATUS_BUSY)) {
GetAtapiIdentifyQuick( &deviceExtension->BaseIoAddress1,
&deviceExtension->BaseIoAddress2,
deviceExtension->ResetState.DeviceNumber[*CallAgain],
&deviceExtension->IdentifyData[deviceExtension->ResetState.DeviceNumber[*CallAgain]]
);
/*
IssueIdentify(
&deviceExtension->BaseIoAddress1,
&deviceExtension->BaseIoAddress2,
deviceExtension->ResetState.DeviceNumber[*CallAgain],
IDE_COMMAND_ATAPI_IDENTIFY,
FALSE,
&deviceExtension->IdentifyData[deviceExtension->ResetState.DeviceNumber[*CallAgain]]
);
*/
}
// InitHwExtWithIdentify(
// HwDeviceExtension,
// deviceExtension->ResetState.DeviceNumber[*CallAgain],
// IDE_COMMAND_ATAPI_IDENTIFY,
// &deviceExtension->IdentifyData[deviceExtension->ResetState.DeviceNumber[*CallAgain]]
// );
(*CallAgain)++;
return AtapiResetController(
HwDeviceExtension,
PathId,
CallAgain
);
break;
case ideResetAtaMSN:
IdeMediaStatus(
TRUE,
HwDeviceExtension,
deviceExtension->ResetState.DeviceNumber[*CallAgain]
);
(*CallAgain)++;
return AtapiResetController(
HwDeviceExtension,
PathId,
CallAgain
);
break;
case ideResetFinal:
//
// HACK: if any of the devices are busy at this point, then they should
// be marked dead. This will anyway happen because of the timeout count.
// However, the waitOnBusy macros in all the routines would consume upto
// 30s leaving the sytem practically hung until the reset completes. Instead,
// we just clear the device present flag before the routine is called and
// restore it after that.
//
for (i = 0; i < (deviceExtension->MaxIdeDevice/MAX_IDE_DEVICE); i++) {
if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) {
SelectIdeDevice(baseIoAddress1, i, 0);
WaitOnBusyUntil(baseIoAddress1, statusByte, 100);
if (statusByte & IDE_STATUS_BUSY) {
CLRMASK(deviceExtension->DeviceFlags[i], DFLAGS_DEVICE_PRESENT);
SETMASK(deviceExtension->DeviceFlags[i], DFLAGS_DEVICE_ERASED);
}
}
}
AtapiHwInitialize(HwDeviceExtension, NULL);
//
// Restore the device present flag
//
for (i = 0; i < (deviceExtension->MaxIdeDevice/MAX_IDE_DEVICE); i++) {
if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_ERASED) {
SETMASK(deviceExtension->DeviceFlags[i], DFLAGS_DEVICE_PRESENT);
}
}
if (IdePortChannelEmpty (baseIoAddress1,
baseIoAddress2, deviceExtension->MaxIdeDevice)) {
IdePortNotification(IdeAllDeviceMissing,
deviceExtension,
NULL);
}
*CallAgain = 0;
for (i = 0; i < (deviceExtension->MaxIdeDevice/MAX_IDE_DEVICE); i++) {
SelectIdeLine(baseIoAddress1, i);
IdePortOutPortByte (baseIoAddress2->DeviceControl, IDE_DC_REENABLE_CONTROLLER);
}
return TRUE;
break;
default:
ASSERT(FALSE);
*CallAgain = 0;
for (i = 0; i < (deviceExtension->MaxIdeDevice/MAX_IDE_DEVICE); i++) {
SelectIdeLine(baseIoAddress1, i);
IdePortOutPortByte (baseIoAddress2->DeviceControl, IDE_DC_REENABLE_CONTROLLER);
}
return FALSE;
break;
}
} // end AtapiResetController()
ULONG
MapError(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine maps ATAPI and IDE errors to specific SRB statuses.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Srb - IO request packet
Return Value:
SRB status
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
ULONG i;
UCHAR errorByte;
UCHAR srbStatus;
UCHAR scsiStatus;
SENSE_DATA tempSenseBuffer;
PSENSE_DATA senseBuffer = (PSENSE_DATA)&tempSenseBuffer;
//
// Read the error register.
//
//errorByte = IdePortInPortByte(baseIoAddress1->Error);
GetErrorByte(baseIoAddress1, errorByte);
DebugPrint((DBG_IDE_DEVICE_ERROR,
"MapError: cdb %x and Error register is %x\n",
Srb->Cdb[0],
errorByte));
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE) {
switch (errorByte >> 4) {
case SCSI_SENSE_NO_SENSE:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: No sense information\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_RECOVERED_ERROR:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Recovered error\n"));
scsiStatus = 0;
srbStatus = SRB_STATUS_SUCCESS;
break;
case SCSI_SENSE_NOT_READY:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Device not ready\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_MEDIUM_ERROR:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Media error\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_HARDWARE_ERROR:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Hardware error\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_ILLEGAL_REQUEST:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Illegal request\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_UNIT_ATTENTION:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Unit attention\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_DATA_PROTECT:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Data protect\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_BLANK_CHECK:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Blank check\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_ABORTED_COMMAND:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"Atapi: Command Aborted\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
default:
DebugPrint((DBG_IDE_DEVICE_ERROR,
"ATAPI: Invalid sense information\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
}
} else {
scsiStatus = 0;
//
// Save errorByte,to be used by SCSIOP_REQUEST_SENSE.
//
deviceExtension->ReturningMediaStatus = errorByte;
RtlZeroMemory(senseBuffer, sizeof(SENSE_DATA));
if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) {
DebugPrint((DBG_IDE_DEVICE_ERROR,
"IDE: Media change\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
if (Srb->SenseInfoBuffer) {
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION;
senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_OPERATOR_REQUEST;
senseBuffer->AdditionalSenseCodeQualifier = SCSI_SENSEQ_MEDIUM_REMOVAL;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
} else if (errorByte & IDE_ERROR_COMMAND_ABORTED) {
DebugPrint((DBG_IDE_DEVICE_ERROR, "IDE: Command abort\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
if ((errorByte & IDE_ERROR_CRC_ERROR) &&
(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_UDMA) &&
SRB_USES_DMA(Srb)) {
DebugPrint((1, "Srb 0x%x had a CRC error using UDMA\n", Srb));
srbStatus = SRB_STATUS_PARITY_ERROR;
if (Srb->SenseInfoBuffer) {
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_HARDWARE_ERROR;
senseBuffer->AdditionalSenseCode = 0x8;
senseBuffer->AdditionalSenseCodeQualifier = 0x3;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
} else {
srbStatus = SRB_STATUS_ABORTED;
if (Srb->SenseInfoBuffer) {
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND;
senseBuffer->AdditionalSenseCode = 0;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
}
deviceExtension->ErrorCount++;
} else if (errorByte & IDE_ERROR_END_OF_MEDIA) {
DebugPrint((DBG_IDE_DEVICE_ERROR,
"IDE: End of media\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
if (Srb->SenseInfoBuffer) {
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;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED)) {
deviceExtension->ErrorCount++;
}
} else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) {
DebugPrint((DBG_IDE_DEVICE_ERROR,
"IDE: Illegal length\n"));
srbStatus = SRB_STATUS_INVALID_REQUEST;
} else if (errorByte & IDE_ERROR_BAD_BLOCK) {
DebugPrint((DBG_IDE_DEVICE_ERROR,
"IDE: Bad block\n"));
srbStatus = SRB_STATUS_ERROR;
scsiStatus = SCSISTAT_CHECK_CONDITION;
if (Srb->SenseInfoBuffer) {
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_HARDWARE_ERROR;
senseBuffer->AdditionalSenseCode = 0;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
} else if (errorByte & IDE_ERROR_ID_NOT_FOUND) {
DebugPrint((DBG_IDE_DEVICE_ERROR,
"IDE: Id not found\n"));
srbStatus = SRB_STATUS_ERROR;
scsiStatus = SCSISTAT_CHECK_CONDITION;
if (Srb->SenseInfoBuffer) {
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_ILLEGAL_REQUEST;
senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_ILLEGAL_BLOCK;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
deviceExtension->ErrorCount++;
} else if (errorByte & IDE_ERROR_MEDIA_CHANGE) {
DebugPrint((DBG_IDE_DEVICE_ERROR,
"IDE: Media change\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
if (Srb->SenseInfoBuffer) {
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION;
senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
} else if (errorByte & IDE_ERROR_DATA_ERROR) {
DebugPrint((DBG_IDE_DEVICE_ERROR,
"IDE: Data error\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED)) {
deviceExtension->ErrorCount++;
}
//
// Build sense buffer
//
if (Srb->SenseInfoBuffer) {
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = (deviceExtension->
DeviceFlags[Srb->TargetId] & DFLAGS_REMOVABLE_DRIVE)? SCSI_SENSE_DATA_PROTECT : SCSI_SENSE_MEDIUM_ERROR;
senseBuffer->AdditionalSenseCode = 0;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
} else { // no sense info
DebugPrint((DBG_IDE_DEVICE_ERROR,
"IdePort: No sense information\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
}
if (senseBuffer->Valid == 1) {
ULONG length = sizeof(SENSE_DATA);
if (Srb->SenseInfoBufferLength < length ) {
length = Srb->SenseInfoBufferLength;
}
ASSERT(length);
ASSERT(Srb->SenseInfoBuffer);
RtlCopyMemory(Srb->SenseInfoBuffer, (PVOID) senseBuffer, length);
}
if ((deviceExtension->ErrorCount >= MAX_ERRORS) &&
(!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA))) {
deviceExtension->MaximumBlockXfer[Srb->TargetId] = 0;
DebugPrint((DBG_ALWAYS,
"MapError: Disabling 32-bit PIO and Multi-sector IOs\n"));
if (deviceExtension->ErrorCount == MAX_ERRORS) {
//
// Log the error.
//
IdePortLogError( HwDeviceExtension,
Srb,
Srb->PathId,
Srb->TargetId,
Srb->Lun,
SP_BAD_FW_WARNING,
4);
}
//
// Reprogram to not use Multi-sector.
//
for (i = 0; i < deviceExtension->MaxIdeDevice; i++) {
UCHAR statusByte;
if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT &&
!(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) {
//
// Select the device.
//
SelectIdeDevice(baseIoAddress1, i, 0);
//
// Setup sector count to reflect the # of blocks.
//
IdePortOutPortByte(baseIoAddress1->BlockCount,
0);
//
// Issue the command.
//
IdePortOutPortByte(baseIoAddress1->Command,
IDE_COMMAND_SET_MULTIPLE);
//
// Wait for busy to drop.
//
WaitOnBaseBusy(baseIoAddress1,statusByte);
//
// Check for errors. Reset the value to 0 (disable MultiBlock) if the
// command was aborted.
//
if (statusByte & IDE_STATUS_ERROR) {
//
// Read the error register.
//
errorByte = IdePortInPortByte(baseIoAddress1->Error);
DebugPrint((DBG_ALWAYS,
"AtapiHwInitialize: Error setting multiple mode. Status %x, error byte %x\n",
statusByte,
errorByte));
//
// Adjust the devExt. value, if necessary.
//
deviceExtension->MaximumBlockXfer[i] = 0;
}
deviceExtension->DeviceParameters[i].IdePioReadCommand = IDE_COMMAND_READ;
deviceExtension->DeviceParameters[i].IdePioWriteCommand = IDE_COMMAND_WRITE;
#ifdef ENABLE_48BIT_LBA
if (deviceExtension->DeviceFlags[i] & DFLAGS_48BIT_LBA) {
deviceExtension->DeviceParameters[i].IdePioReadCommandExt = IDE_COMMAND_READ_EXT;
deviceExtension->DeviceParameters[i].IdePioWriteCommandExt = IDE_COMMAND_WRITE_EXT;
}
#endif
deviceExtension->DeviceParameters[i].MaxBytePerPioInterrupt = 512;
deviceExtension->MaximumBlockXfer[i] = 0;
}
}
}
}
//
// Set SCSI status to indicate a check condition.
//
Srb->ScsiStatus = scsiStatus;
return srbStatus;
} // end MapError()
NTSTATUS
AtapiSetTransferMode (
PHW_DEVICE_EXTENSION DeviceExtension,
ULONG DeviceNumber,
UCHAR ModeValue
)
{
PIDE_REGISTERS_1 baseIoAddress1 = &DeviceExtension->BaseIoAddress1;
UCHAR ideStatus;
if (DeviceExtension->CurrentSrb) {
DebugPrint ((DBG_ALWAYS, "DeviceExtension->CurrentSrb = 0x%x\n", DeviceExtension->CurrentSrb));
ASSERT(DeviceExtension->CurrentSrb == NULL);
}
ASSERT (DeviceExtension->ExpectingInterrupt == FALSE);
if (Is98LegacyIde(baseIoAddress1)) {
if ( !EnhancedIdeSupport() ) {
DebugPrint((1,"atapi: AtapiSetTransferMode - not enhanced-ide.\n"));
return STATUS_INVALID_DEVICE_REQUEST;
}
if (DeviceExtension->DeviceFlags[DeviceNumber] & DFLAGS_WD_MODE) {
//
// WD-Mode cd-rom can not set transfer mode.
//
DebugPrint((1,"atapi: AtapiSetTransferMode - not enhanced-ide.\n"));
return STATUS_INVALID_DEVICE_REQUEST;
}
}
SelectIdeDevice(baseIoAddress1, DeviceNumber, 0);
WaitOnBusy(baseIoAddress1, ideStatus);
IdePortOutPortByte(
baseIoAddress1->Error,
IDE_SET_FEATURE_SET_TRANSFER_MODE
);
IdePortOutPortByte(
baseIoAddress1->BlockCount,
ModeValue
);
IdePortOutPortByte(
baseIoAddress1->Command,
IDE_COMMAND_SET_FEATURE
);
WaitOnBusy(baseIoAddress1, ideStatus);
if (ideStatus & (IDE_STATUS_BUSY | IDE_STATUS_ERROR)) {
return STATUS_INVALID_DEVICE_REQUEST;
} else {
return STATUS_SUCCESS;
}
}
VOID
AtapiProgramTransferMode (
PHW_DEVICE_EXTENSION DeviceExtension
)
{
ULONG i;
for (i=0; i<DeviceExtension->MaxIdeDevice; i++) {
UCHAR ideCommand;
ULONG pioModeStatus;
ULONG dmaModeStatus;
ULONG xferMode;
if (!(DeviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT)) {
continue;
}
DebugPrint((DBG_XFERMODE, "ATAPI: ProgramTransferMode device %x- TMSelected = 0x%x\n",
i,
DeviceExtension->DeviceParameters[i].TransferModeSelected));
CLRMASK (DeviceExtension->DeviceFlags[i], DFLAGS_USE_DMA | DFLAGS_USE_UDMA);
if (!DeviceExtension->NoPioSetTransferMode) {
//
// many old devices just don't support set transfer mode
// they act unpredictably after receiving one
// e.g. "SAMSUNG SCR-730 REV D-05" cdrom will start returning
// no error on TEST_UNIT_READY even if it doesn't have a media
//
// we are going to apply some magic code here!!
// we would not set transfer mode if the device doesn't support
// timing faster than mode2
//
GetHighestPIOTransferMode(DeviceExtension->DeviceParameters[i].TransferModeSelected, xferMode);
if (xferMode > PIO2) {
DebugPrint((DBG_XFERMODE, "ATAPI: device %x, setting PIOmode 0x%x\n",
i,
xferMode));
ideCommand = IDE_SET_ADVANCE_PIO_MODE(xferMode-PIO0);
pioModeStatus = AtapiSetTransferMode (
DeviceExtension,
i,
ideCommand
);
if (!NT_SUCCESS(pioModeStatus)) {
DebugPrint ((DBG_ALWAYS,
"ATAPI: Unable to set pio xfer mode %d for 0x%x device %d\n",
xferMode,
DeviceExtension->BaseIoAddress1.RegistersBaseAddress,
i));
}
}
}
//
// Program DMA mode
//
GetHighestDMATransferMode(DeviceExtension->DeviceParameters[i].TransferModeSelected, xferMode);
if (xferMode >= UDMA0) {
ideCommand = IDE_SET_UDMA_MODE(xferMode-UDMA0);
} else if (xferMode >= MWDMA0) {
ideCommand = IDE_SET_MWDMA_MODE(xferMode-MWDMA0);
} else if (xferMode >= SWDMA0) {
ideCommand = IDE_SET_SWDMA_MODE(xferMode-SWDMA0);
}
//
// Issue the set features command only if we support
// any of the DMA modes
//
if (xferMode >= SWDMA0) {
DebugPrint((DBG_XFERMODE, "ATAPI: device %x, setting DMAmode 0x%x\n",
i,
xferMode));
dmaModeStatus = AtapiSetTransferMode (
DeviceExtension,
i,
ideCommand
);
if (NT_SUCCESS(dmaModeStatus)) {
DeviceExtension->DeviceFlags[i] |= DFLAGS_USE_DMA;
if (xferMode >= UDMA0) {
DeviceExtension->DeviceFlags[i] |= DFLAGS_USE_UDMA;
}
} else {
DebugPrint ((DBG_ALWAYS,
"ATAPI: Unable to set DMA mode %d for 0x%x device %d\n",
xferMode,
DeviceExtension->BaseIoAddress1.RegistersBaseAddress,
i));
}
}
}
return;
}
BOOLEAN
AtapiHwInitialize(
IN PVOID HwDeviceExtension,
IN UCHAR FlushCommand[MAX_IDE_DEVICE * MAX_IDE_LINE]
)
/*++
Routine Description:
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Return Value:
TRUE - if initialization successful.
FALSE - if initialization unsuccessful.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress;
ULONG i;
UCHAR statusByte, errorByte;
baseIoAddress = &deviceExtension->BaseIoAddress1;
for (i = 0; i < deviceExtension->MaxIdeDevice; i++) {
if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) {
//
// Select device
//
SelectIdeDevice(baseIoAddress, i, 0);
//
// Make sure device is ready for any command
//
if (!(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) {
WaitForDRDY(baseIoAddress, statusByte);
}
if (!(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) {
//
// Enable media status notification
//
IdeMediaStatus(TRUE,HwDeviceExtension,i);
//
// If supported, setup Multi-block transfers.
//
if (deviceExtension->MaximumBlockXfer[i]) {
//
// Select the device.
//
SelectIdeDevice(baseIoAddress, i, 0);
//
// Setup sector count to reflect the # of blocks.
//
IdePortOutPortByte(baseIoAddress->BlockCount,
deviceExtension->MaximumBlockXfer[i]);
//
// Issue the command.
//
IdePortOutPortByte(baseIoAddress->Command,
IDE_COMMAND_SET_MULTIPLE);
//
// Wait for busy to drop.
//
WaitOnBaseBusy(baseIoAddress,statusByte);
//
// Check for errors. Reset the value to 0 (disable MultiBlock) if the
// command was aborted.
//
if (statusByte & IDE_STATUS_ERROR) {
//
// Read the error register.
//
errorByte = IdePortInPortByte(baseIoAddress->Error);
DebugPrint((1,
"AtapiHwInitialize: Error setting multiple mode. Status %x, error byte %x\n",
statusByte,
errorByte));
//
// Adjust the devExt. value, if necessary.
//
deviceExtension->MaximumBlockXfer[i] = 0;
} else {
DebugPrint((2,
"AtapiHwInitialize: Using Multiblock on Device %d. Blocks / int - %d\n",
i,
deviceExtension->MaximumBlockXfer[i]));
}
}
}
// IdeMediaStatus(TRUE,HwDeviceExtension,i);
//
// 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! At
// least for the older ATAPI changers.
//
if (deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE) {
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
ULONG waitCount;
// have to get out of the loop sometime!
// 10000 * 100us = 1000,000us = 1000ms = 1s
waitCount = 10000;
GetStatus(baseIoAddress1, statusByte);
while ((statusByte & IDE_STATUS_BUSY) && waitCount) {
//
// Wait for Busy to drop.
//
KeStallExecutionProcessor(100);
GetStatus(baseIoAddress1, statusByte);
waitCount--;
}
}
}
}
AtapiProgramTransferMode (deviceExtension);
InitDeviceParameters (HwDeviceExtension, FlushCommand);
return TRUE;
} // end AtapiHwInitialize()
VOID
AtapiHwInitializeMultiLun (
IN PVOID HwDeviceExtension,
IN ULONG TargetId,
IN ULONG numSlot
)
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
deviceExtension->DeviceFlags[TargetId] |= DFLAGS_MULTI_LUN_INITED;
deviceExtension->LastLun[TargetId] = (numSlot == 0) ? 0 : (numSlot - 1);
return;
}
#ifdef DRIVER_PARAMETER_REGISTRY_SUPPORT
ULONG
AtapiParseArgumentString(
IN PCHAR String,
IN PCHAR KeyWord
)
/*++
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.
--*/
{
PCHAR cptr;
PCHAR 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) {
if (*cptr >= 'A' && *cptr <= 'Z') {
*cptr = *cptr + ('a' - 'A');
}
cptr++;
stringLength++;
}
//
// Calculate the keyword length and lower case all characters.
//
cptr = KeyWord;
while (*cptr) {
if (*cptr >= 'A' && *cptr <= 'Z') {
*cptr = *cptr + ('a' - 'A');
}
cptr++;
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++) {
if (*(cptr - 1) == '\0') {
//
// end of string
//
return 0;
}
}
if (*(kptr - 1) == '\0') {
//
// May have a match backup and check for blank or equals.
//
cptr--;
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')) {
//
// 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 {
//
// 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;
}
}
#endif
VOID
InitDeviceParameters (
IN PVOID HwDeviceExtension,
IN UCHAR FlushCommand[MAX_IDE_DEVICE * MAX_IDE_LINE]
)
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
ULONG deviceNumber;
//
// pick out the ATA or ATAPI r/w command we are going to use
//
for (deviceNumber = 0; deviceNumber < deviceExtension->MaxIdeDevice; deviceNumber++) {
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) {
DebugPrint ((DBG_BUSSCAN, "ATAPI: Base=0x%x Device %d is going to do ", deviceExtension->BaseIoAddress1.RegistersBaseAddress, deviceNumber));
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_USE_DMA) {
DebugPrint ((DBG_BUSSCAN, "DMA\n"));
} else {
DebugPrint ((DBG_BUSSCAN, "PIO\n"));
}
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_ATAPI_DEVICE) {
deviceExtension->DeviceParameters[deviceNumber].MaxBytePerPioInterrupt = 512;
} else {
if (deviceExtension->MaximumBlockXfer[deviceNumber]) {
DebugPrint ((DBG_BUSSCAN, "ATAPI: ATA Device (%d) is going to do PIO Multiple\n", deviceNumber));
deviceExtension->DeviceParameters[deviceNumber].IdePioReadCommand = IDE_COMMAND_READ_MULTIPLE;
deviceExtension->DeviceParameters[deviceNumber].IdePioWriteCommand = IDE_COMMAND_WRITE_MULTIPLE;
#ifdef ENABLE_48BIT_LBA
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_48BIT_LBA) {
deviceExtension->DeviceParameters[deviceNumber].IdePioReadCommandExt = IDE_COMMAND_READ_MULTIPLE_EXT;
deviceExtension->DeviceParameters[deviceNumber].IdePioWriteCommandExt = IDE_COMMAND_WRITE_MULTIPLE_EXT;
}
#endif
deviceExtension->DeviceParameters[deviceNumber].MaxBytePerPioInterrupt =
deviceExtension->MaximumBlockXfer[deviceNumber] * 512;
} else {
DebugPrint ((DBG_BUSSCAN, "ATAPI: ATA Device (%d) is going to do PIO Single\n", deviceNumber));
deviceExtension->DeviceParameters[deviceNumber].IdePioReadCommand = IDE_COMMAND_READ;
deviceExtension->DeviceParameters[deviceNumber].IdePioWriteCommand = IDE_COMMAND_WRITE;
#ifdef ENABLE_48BIT_LBA
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_48BIT_LBA) {
deviceExtension->DeviceParameters[deviceNumber].IdePioReadCommandExt = IDE_COMMAND_READ_EXT;
deviceExtension->DeviceParameters[deviceNumber].IdePioWriteCommandExt = IDE_COMMAND_WRITE_EXT;
}
#endif
deviceExtension->DeviceParameters[deviceNumber].MaxBytePerPioInterrupt = 512;
}
if (FlushCommand) {
deviceExtension->DeviceParameters[deviceNumber].IdeFlushCommand = FlushCommand[deviceNumber];
#ifdef ENABLE_48BIT_LBA
//
// if the flush command worked then we are going to assume that the flush command
// for the 48 bit LBA feature set is supported.
//
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_48BIT_LBA) {
deviceExtension->DeviceParameters[deviceNumber].IdeFlushCommandExt = IDE_COMMAND_FLUSH_CACHE_EXT;
deviceExtension->DeviceParameters[deviceNumber].IdeFlushCommand = IDE_COMMAND_NO_FLUSH;
}
#endif
}
}
}
}
}
ULONG
Atapi2Scsi(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb,
IN char *DataBuffer,
IN ULONG ByteCount
)
/*++
Routine Description:
Convert atapi cdb and mode sense data to scsi format
Arguments:
Srb - SCSI request block
DataBuffer - mode sense data
ByteCount - mode sense data length
Return Value:
byte adjust
--*/
{
ULONG bytesAdjust = 0;
if (DeviceExtension->scsi2atapi) {
if (Srb->Cdb[0] == ATAPI_MODE_SENSE) {
ASSERT(FALSE);
} else if (Srb->Cdb[0] == ATAPI_LS120_FORMAT_UNIT) {
Srb->Cdb[0] = SCSIOP_FORMAT_UNIT;
}
RESTORE_ORIGINAL_CDB(DeviceExtension, Srb);
DeviceExtension->scsi2atapi = FALSE;
}
return bytesAdjust;
}
VOID
AtapiCallBack(
IN PVOID HwDeviceExtension
)
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PSCSI_REQUEST_BLOCK srb = deviceExtension->CurrentSrb;
PATAPI_REGISTERS_1 baseIoAddress1;
UCHAR statusByte;
//
// 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 (srb && (!(deviceExtension->ExpectingInterrupt))) {
#if DBG
if (!SRB_IS_RDP(srb)) {
DebugPrint((1,
"AtapiCallBack: Invalid CDB marked as RDP - %x\n",
srb->Cdb[0]));
}
#endif
baseIoAddress1 = (PATAPI_REGISTERS_1)&deviceExtension->BaseIoAddress1;
if (deviceExtension->RDP) {
GetStatus(baseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_DSC) {
IdePortNotification(IdeRequestComplete,
deviceExtension,
srb);
//
// Clear current SRB.
//
deviceExtension->CurrentSrb = NULL;
deviceExtension->RDP = FALSE;
//
// Ask for next request.
//
IdePortNotification(IdeNextRequest,
deviceExtension,
NULL);
return;
} else {
DebugPrint((3,
"AtapiCallBack: Requesting another timer for Op %x\n",
deviceExtension->CurrentSrb->Cdb[0]));
IdePortNotification(IdeRequestTimerCall,
HwDeviceExtension,
AtapiCallBack,
1000);
return;
}
}
}
DebugPrint((2,
"AtapiCallBack: Calling ISR directly due to BUSY\n"));
AtapiInterrupt(HwDeviceExtension);
}
//#define IdeCrashDumpLogIsrStatus(hwExtension, isrStatus) hwExtension->CrashDumpIsrStatus[hwExtension->CrashDumpLogIndex]=isrStatus;
BOOLEAN
AtapiInterrupt(
IN PVOID HwDeviceExtension
)
/*++
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.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PSCSI_REQUEST_BLOCK srb = deviceExtension->CurrentSrb;
PATAPI_REGISTERS_1 baseIoAddress1;
PATAPI_REGISTERS_2 baseIoAddress2;
ULONG byteCount = 0, bytesThisInterrupt = 512;
ULONG status;
ULONG i;
UCHAR statusByte,interruptReason;
BOOLEAN commandComplete = FALSE;
BOOLEAN atapiDev = FALSE;
UCHAR dmaStatus;
BOOLEAN fakeStatus = FALSE;
BOOLEAN packetBasedSrb;
BOOLEAN wdModeCdRom;
BMSTATUS bmStatus=0;
BOOLEAN dmaInProgress = FALSE;
BOOLEAN alwaysClearBusMasterInterrupt;
UCHAR savedCmd;
BOOLEAN interruptCleared = FALSE;
alwaysClearBusMasterInterrupt = deviceExtension->BusMasterInterface.AlwaysClearBusMasterInterrupt;
//
// if this flag is set, we must try to clear the busmaster interrupt
// this is to overcome a bug in the cpq controller
// this is set for native mode contollers
//
if (alwaysClearBusMasterInterrupt) {
if (deviceExtension->BusMasterInterface.BmStatus) {
bmStatus = deviceExtension->BusMasterInterface.BmStatus (deviceExtension->BusMasterInterface.Context);
if (bmStatus & BMSTATUS_INTERRUPT) {
deviceExtension->BusMasterInterface.BmDisarm (deviceExtension->BusMasterInterface.Context);
interruptCleared = TRUE;
}
}
}
if (srb) {
baseIoAddress1 = (PATAPI_REGISTERS_1)&deviceExtension->BaseIoAddress1;
baseIoAddress2 = (PATAPI_REGISTERS_2)&deviceExtension->BaseIoAddress2;
} else {
DebugPrint((1,
"AtapiInterrupt: CurrentSrb is NULL. Bogus Interrupt\n"));
if (deviceExtension->InterruptMode == LevelSensitive) {
if (deviceExtension->BaseIoAddress1.RegistersBaseAddress != NULL) {
baseIoAddress1 = (PATAPI_REGISTERS_1)&deviceExtension->BaseIoAddress1;
GetBaseStatus(baseIoAddress1, statusByte);
/*
GetSelectedIdeDevice(baseIoAddress1, savedCmd);
SelectIdeDevice(baseIoAddress1, 0, 0);
GetBaseStatus(baseIoAddress1, statusByte);
SelectIdeDevice(baseIoAddress1, 1, 0);
GetBaseStatus(baseIoAddress1, statusByte);
ReSelectIdeDevice(baseIoAddress1, savedCmd);
*/
}
}
return interruptCleared;
}
if (!(deviceExtension->ExpectingInterrupt)) {
DebugPrint((1,
"AtapiInterrupt: Unexpected interrupt.\n"));
return interruptCleared;
}
if (!alwaysClearBusMasterInterrupt) {
if (deviceExtension->BusMasterInterface.BmStatus) {
bmStatus = deviceExtension->BusMasterInterface.BmStatus (deviceExtension->BusMasterInterface.Context);
if (bmStatus & BMSTATUS_INTERRUPT) {
deviceExtension->BusMasterInterface.BmDisarm (deviceExtension->BusMasterInterface.Context);
}
}
}
//
// For SiS IDE Controller, we have to read the bm status register first
//
if (deviceExtension->DMAInProgress) {
// PCI Busmaster IDE Controller spec defines a bit in its status
// register which indicates pending interrupt. However,
// CMD 646 (maybe some other one, too) doesn't always do that if
// the interrupt is from a atapi device. (strange, but true!)
// Since we can look at this interrupt bit only if we are sharing
// interrupt, we will do just that
//
// Doesn't look like it is our interrupt
// If we are called from crashdmp (polling mode) then prociess the interrupt
// even if the bit is not set. It is checked in the crashdmp routine.
//
if (!(bmStatus & BMSTATUS_INTERRUPT) &&
!(deviceExtension->DriverMustPoll)) {
DebugPrint((1, "No BusMaster Interrupt\n"));
ASSERT(interruptCleared == FALSE);
return FALSE;
}
dmaInProgress = deviceExtension->DMAInProgress;
deviceExtension->DMAInProgress = FALSE;
if (deviceExtension->BusMasterInterface.IgnoreActiveBitForAtaDevice) {
if (!(deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE)) {
CLRMASK (bmStatus, BMSTATUS_NOT_REACH_END_OF_TRANSFER);
}
}
}
//
// should we check for the interrupt bit for PIO transfers?
//
//
// Select IDE line(Primary or Secondary).
//
SelectIdeLine(baseIoAddress1, srb->TargetId >> 1);
//
// Clear interrupt by reading status.
//
GetBaseStatus(baseIoAddress1, statusByte);
#ifdef ENABLE_ATAPI_VERIFIER
if (ViIdeGenerateDmaTimeout(deviceExtension, dmaInProgress)) {
deviceExtension->ExpectingInterrupt = FALSE;
return TRUE;
}
#endif
//
// Log the bus master status
//
if (!deviceExtension->DriverMustPoll) {
IdeLogBmStatus(srb, bmStatus);
}
//IdeCrashDumpLogIsrStatus(deviceExtension, bmStatus);
//
// check the type of srb we have
//
if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) {
packetBasedSrb = TRUE;
} else {
packetBasedSrb = FALSE;
}
if ((srb->Function == SRB_FUNCTION_ATA_PASS_THROUGH) ||
(srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH)) {
PATA_PASS_THROUGH ataPassThroughData;
PIDEREGS pIdeReg;
packetBasedSrb = FALSE;
ataPassThroughData = srb->DataBuffer;
pIdeReg = &ataPassThroughData->IdeReg;
//
// if the last command we issued was a SLEEP command,
// the device interface (task registers) is invalid.
// In order to complete the interrupt, will fake the
// a good status
//
if (pIdeReg->bCommandReg == IDE_COMMAND_SLEEP) {
fakeStatus = TRUE;
}
}
if (fakeStatus) {
statusByte = IDE_STATUS_IDLE;
}
DebugPrint((1,
"AtapiInterrupt: Entered with status (%x)\n",
statusByte));
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.
//
DebugPrint((1,
"AtapiInterrupt: Hit status=0x%x while polling during crashdump.\n",
statusByte
));
deviceExtension->DMAInProgress = TRUE;
return TRUE;
}
if (dmaInProgress) {
//
// this is really bad since we already disabled
// dma at this point, but the device is still busy
// can't really recover. just return from now.
// the timeout code will kick in and save the world
//
DebugPrint((DBG_ALWAYS,
"AtapiInterrupt: End of DMA transfer but device is still BUSY. status = 0x%x\n",
statusByte));
//
// we are not expecting interrupt anymore. Clear the flag.
//
deviceExtension->ExpectingInterrupt = FALSE;
return interruptCleared;
}
//
// Ensure BUSY is non-asserted.
//
for (i = 0; i < 10; i++) {
GetBaseStatus(baseIoAddress1, statusByte);
if (!(statusByte & IDE_STATUS_BUSY)) {
break;
}
}
if (i == 10) {
DebugPrint((2,
"AtapiInterrupt: BUSY on entry. Status %x, Base IO %x\n",
statusByte,
baseIoAddress1));
IdePortNotification(IdeRequestTimerCall,
HwDeviceExtension,
AtapiCallBack,
500);
return interruptCleared;
}
}
//
// Check for error conditions.
//
if (statusByte & IDE_STATUS_ERROR) {
if (srb->Cdb[0] != SCSIOP_REQUEST_SENSE) {
//
// Fail this request.
//
status = SRB_STATUS_ERROR;
goto CompleteRequest;
}
}
wdModeCdRom = FALSE;
if (Is98LegacyIde(baseIoAddress1)) {
if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_WD_MODE) {
if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) {
wdModeCdRom = TRUE;
} else {
status = SRB_STATUS_ERROR;
goto CompleteRequest;
}
}
}
//
// check reason for this interrupt.
//
if (packetBasedSrb && !wdModeCdRom) {
interruptReason = (IdePortInPortByte(baseIoAddress1->InterruptReason) & 0x3);
atapiDev = TRUE;
bytesThisInterrupt = 512;
if (dmaInProgress) {
if (interruptReason != 0x3) {
//
// the device causes an interrupt in the middle of a
// dma transfer! bad bad bad device!
// do nothing and just return. this will get translated
// to a timeout and we will retry.
//
DebugPrint((1,
"Interrupt during DMA transfer, reason %x != 0x3\n",
interruptReason
));
deviceExtension->ExpectingInterrupt = FALSE;
// ISSUE: should we return TRUE?
return interruptCleared;
}
}
} else {
if (dmaInProgress) {
interruptReason = 0x3;
} else if (statusByte & IDE_STATUS_DRQ) {
if (deviceExtension->MaximumBlockXfer[srb->TargetId]) {
bytesThisInterrupt = 512 * deviceExtension->MaximumBlockXfer[srb->TargetId];
}
if (srb->SrbFlags & SRB_FLAGS_DATA_IN) {
interruptReason = 0x2;
} else if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
interruptReason = 0x0;
} else {
status = SRB_STATUS_ERROR;
goto CompleteRequest;
}
} else if (statusByte & IDE_STATUS_BUSY) {
ASSERT(interruptCleared == FALSE);
return FALSE;
} else {
if (deviceExtension->BytesLeft && (!Is98LegacyIde(baseIoAddress1))) {
//
// We should return interruptCleared.
//
return interruptCleared;
} else {
//
// Command complete - verify, write, or the SMART enable/disable.
//
// Also get_media_status
interruptReason = 0x3;
}
}
}
if (interruptReason == 0x1 && (statusByte & IDE_STATUS_DRQ)) {
//
// Write the packet.
//
DebugPrint((2,
"AtapiInterrupt: Writing Atapi packet.\n"));
//
// Send CDB to device.
//
WriteBuffer(baseIoAddress1,
(PUSHORT)srb->Cdb,
6);
if (SRB_USES_DMA(srb)) {
deviceExtension->DMAInProgress = TRUE;
deviceExtension->BusMasterInterface.BmArm (deviceExtension->BusMasterInterface.Context);
}
return interruptCleared;
} else if (interruptReason == 0x0 && (statusByte & IDE_STATUS_DRQ)) {
//
// Write the data.
//
if (packetBasedSrb) {
//
// Pick up bytes to transfer and convert to words.
//
byteCount =
IdePortInPortByte(baseIoAddress1->ByteCountLow);
byteCount |=
IdePortInPortByte(baseIoAddress1->ByteCountHigh) << 8;
if (byteCount != deviceExtension->BytesLeft) {
DebugPrint((3,
"AtapiInterrupt: %d bytes requested; %d bytes xferred\n",
deviceExtension->BytesLeft,
byteCount));
}
//
// Verify this makes sense.
//
if (byteCount > deviceExtension->BytesLeft) {
byteCount = deviceExtension->BytesLeft;
}
} else {
//
// IDE path. Check if words left is at least 256.
//
if (deviceExtension->BytesLeft < bytesThisInterrupt) {
//
// Transfer only words requested.
//
byteCount = deviceExtension->BytesLeft;
} else {
//
// Transfer next block.
//
byteCount = bytesThisInterrupt;
}
}
//
// Ensure that this is a write command.
//
if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
DebugPrint((3,
"AtapiInterrupt: Write interrupt\n"));
WaitOnBusy(baseIoAddress1,statusByte);
WriteBuffer(baseIoAddress1,
(PUSHORT)deviceExtension->DataBuffer,
byteCount / sizeof(USHORT));
if (byteCount & 1) {
//
// grab the last byte
//
IdePortOutPortByte(
(PUCHAR)(baseIoAddress1->Data),
deviceExtension->DataBuffer[byteCount - 1]
);
}
} else {
DebugPrint((1,
"AtapiInterrupt: Int reason %x, but srb is for a write %x.\n",
interruptReason,
srb));
//
// Fail this request.
//
status = SRB_STATUS_ERROR;
goto CompleteRequest;
}
//
// Advance data buffer pointer and bytes left.
//
deviceExtension->DataBuffer += byteCount;
deviceExtension->BytesLeft -= byteCount;
return interruptCleared;
} else if (interruptReason == 0x2 && (statusByte & IDE_STATUS_DRQ)) {
if (packetBasedSrb) {
//
// Pick up bytes to transfer
//
byteCount =
IdePortInPortByte(baseIoAddress1->ByteCountLow);
byteCount |=
IdePortInPortByte(baseIoAddress1->ByteCountHigh) << 8;
if (byteCount != deviceExtension->BytesLeft) {
DebugPrint((3,
"AtapiInterrupt: %d bytes requested; %d bytes xferred\n",
deviceExtension->BytesLeft,
byteCount));
}
//
// Verify this makes sense.
//
if (byteCount > deviceExtension->BytesLeft) {
byteCount = deviceExtension->BytesLeft;
}
} else {
//
// Check if words left is at least 256.
//
if (deviceExtension->BytesLeft < bytesThisInterrupt) {
//
// Transfer only words requested.
//
byteCount = deviceExtension->BytesLeft;
} else {
//
// Transfer next block.
//
byteCount = bytesThisInterrupt;
}
}
//
// Ensure that this is a read command.
//
if (srb->SrbFlags & SRB_FLAGS_DATA_IN) {
DebugPrint((3,
"AtapiInterrupt: Read interrupt\n"));
WaitOnBusy(baseIoAddress1,statusByte);
ReadBuffer(baseIoAddress1,
(PUSHORT)deviceExtension->DataBuffer,
byteCount / sizeof(USHORT));
if (byteCount & 1) {
//
// grab the last byte
//
deviceExtension->DataBuffer[byteCount - 1] = IdePortInPortByte((PUCHAR)(baseIoAddress1->Data));
}
} else {
DebugPrint((1,
"AtapiInterrupt: Int reason %x, but srb is for a read %x.\n",
interruptReason,
srb));
//
// Fail this request.
//
status = SRB_STATUS_ERROR;
goto CompleteRequest;
}
//
// Translate ATAPI data back to SCSI data if needed
//
if (deviceExtension->scsi2atapi) {
//
//convert and adjust the wordCount
//
byteCount -= Atapi2Scsi(
deviceExtension,
srb,
deviceExtension->DataBuffer,
byteCount
);
}
//
// Advance data buffer pointer and bytes left.
//
deviceExtension->DataBuffer += byteCount;
deviceExtension->BytesLeft -= byteCount;
//
// Check for read command complete.
//
if (deviceExtension->BytesLeft == 0) {
if (packetBasedSrb) {
//
// 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 (!(deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_MULTI_LUN_INITED)) {
if ((srb->Cdb[0] == 0x25) &&
((deviceExtension->IdentifyData[srb->TargetId].GeneralConfiguration >> 8) & 0x1f) == 0x05) {
deviceExtension->DataBuffer -= byteCount;
if (deviceExtension->DataBuffer[0] == 0x00) {
*((ULONG *) &(deviceExtension->DataBuffer[0])) = 0xFFFFFF7F;
}
*((ULONG *) &(deviceExtension->DataBuffer[2])) = 0x00080000;
deviceExtension->DataBuffer += byteCount;
}
}
} else {
//
// Completion for IDE drives.
//
if (deviceExtension->BytesLeft) {
status = SRB_STATUS_DATA_OVERRUN;
} else {
status = SRB_STATUS_SUCCESS;
}
goto CompleteRequest;
}
}
return interruptCleared;
} else if (interruptReason == 0x3) { // && !(statusByte & IDE_STATUS_DRQ)) {
if (dmaInProgress) {
deviceExtension->BytesLeft = 0;
ASSERT (interruptReason == 3);
//
// bmStatus is initalized eariler.
//
if (!BMSTATUS_SUCCESS(bmStatus)) {
if (bmStatus & BMSTATUS_ERROR_TRANSFER) {
status = SRB_STATUS_ERROR;
}
if (bmStatus & BMSTATUS_NOT_REACH_END_OF_TRANSFER) {
status = SRB_STATUS_DATA_OVERRUN;
}
} else {
status = SRB_STATUS_SUCCESS;
}
} else {
//
// Command complete.
//
if (deviceExtension->BytesLeft) {
status = SRB_STATUS_DATA_OVERRUN;
} else {
status = SRB_STATUS_SUCCESS;
}
}
CompleteRequest:
if (status == SRB_STATUS_ERROR) {
DebugPrint ((1,
"AtapiInterrupt: last command return status byte = 0x%x and error byte = 0x%x\n",
statusByte,
IdePortInPortByte(baseIoAddress1->Error)));
if (deviceExtension->scsi2atapi) {
RESTORE_ORIGINAL_CDB(deviceExtension, srb);
deviceExtension->scsi2atapi = FALSE;
}
//
// Map error to specific SRB status and handle request sense.
//
if ((srb->Function == SRB_FUNCTION_FLUSH) ||
(srb->Function == SRB_FUNCTION_SHUTDOWN)) {
//
// return status success even if a flush fails
//
status = SRB_STATUS_SUCCESS;
} else {
//
// log only the error that is caused by normal reuqest that
// fails
//
if ((srb->Function != SRB_FUNCTION_ATA_PASS_THROUGH) &&
(srb->Function != SRB_FUNCTION_ATA_POWER_PASS_THROUGH)) {
status = MapError(deviceExtension,
srb);
}
}
deviceExtension->RDP = FALSE;
#if DBG
//#define ATAPI_RANDOM_RW_ERROR_FREQUENCY 50
#if ATAPI_RANDOM_RW_ERROR_FREQUENCY
} else if (status == SRB_STATUS_SUCCESS) {
static ULONG _____RWCount = 0;
if ((srb->Cdb[0] == SCSIOP_READ) || (srb->Cdb[0] == SCSIOP_WRITE)) {
_____RWCount++;
// if (baseIoAddress1 == (PATAPI_REGISTERS_1)0x170) {
{
if ((_____RWCount % ATAPI_RANDOM_RW_ERROR_FREQUENCY) == 0) {
DebugPrint ((1, "ATAPI: Forcing R/W error\n"));
srb->SrbStatus = SRB_STATUS_ERROR;
srb->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_HARDWARE_ERROR;
senseBuffer->AdditionalSenseCode = 0;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
status = srb->SrbStatus;
}
}
}
#endif // DBG
#endif // ATAPI_GENERATE_RANDOM_RW_ERROR
} else {
//
// Wait for busy to drop.
//
for (i = 0; i < 60; i++) {
if (fakeStatus) {
statusByte = IDE_STATUS_IDLE;
} else {
GetStatus(baseIoAddress1,statusByte);
}
if (!(statusByte & IDE_STATUS_BUSY)) {
break;
}
KeStallExecutionProcessor(500);
}
if (i == 60) {
//
// reset the controller.
//
DebugPrint((0,
"AtapiInterrupt: Resetting due to BSY still up - %x. Base Io %x\n",
statusByte,
baseIoAddress1));
if (deviceExtension->DriverMustPoll) {
//
// When we are polling, no dpc gets enqueued.
// Try a quick reset...
//
//AtapiSyncResetController (HwDeviceExtension,srb->PathId);
status = SRB_STATUS_BUS_RESET;
} else {
//
// Reset the controller in the completion DPC
//
//AtapiSyncResetController (HwDeviceExtension,srb->PathId);
IdePortNotification(IdeResetRequest,
deviceExtension,
NULL);
return interruptCleared;
}
}
//
// Check to see if DRQ is still up.
//
if (statusByte & IDE_STATUS_DRQ) {
for (i = 0; i < 500; i++) {
GetStatus(baseIoAddress1,statusByte);
if (!(statusByte & IDE_STATUS_DRQ)) {
break;
}
KeStallExecutionProcessor(100);
}
if (i == 500) {
//
// reset the controller.
//
DebugPrint((0,
"AtapiInterrupt: Resetting due to DRQ still up - %x\n",
statusByte));
if (deviceExtension->DriverMustPoll) {
//
// When we are polling, no dpc gets enqueued.
// Try a quick reset...
//
//AtapiSyncResetController (HwDeviceExtension,srb->PathId);
status = SRB_STATUS_BUS_RESET;
} else {
//
// Reset the controller in the completion DPC
//
//AtapiSyncResetController (HwDeviceExtension,srb->PathId);
IdePortNotification(IdeResetRequest,
deviceExtension,
NULL);
return interruptCleared;
}
}
}
}
//
// Clear interrupt expecting and dmaInProgress flag.
//
deviceExtension->ExpectingInterrupt = FALSE;
//
// Sanity check that there is a current request.
//
if (srb != NULL) {
//
// Set status in SRB.
//
srb->SrbStatus = (UCHAR)status;
//
// Check for underflow.
//
if (deviceExtension->BytesLeft) {
//
// Subtract out residual words and update if filemark hit,
// setmark hit , end of data, end of media...
//
if (!(deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_TAPE_DEVICE)) {
if (status == SRB_STATUS_DATA_OVERRUN) {
srb->DataTransferLength -= deviceExtension->BytesLeft;
} else {
srb->DataTransferLength = 0;
}
} else {
srb->DataTransferLength -= deviceExtension->BytesLeft;
}
}
if ((srb->Function == SRB_FUNCTION_ATA_PASS_THROUGH) ||
(srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH)) {
PATA_PASS_THROUGH ataPassThroughData = srb->DataBuffer;
AtapiTaskRegisterSnapshot ((PIDE_REGISTERS_1)baseIoAddress1, &ataPassThroughData->IdeReg);
}
if (srb->Function != SRB_FUNCTION_IO_CONTROL) {
//
// Indicate command complete.
//
if (!(deviceExtension->RDP) &&
!(deviceExtension->DriverMustPoll)) {
IdePortNotification(IdeRequestComplete,
deviceExtension,
srb);
}
} else {
PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
UCHAR error = 0;
if (status != SRB_STATUS_SUCCESS) {
error = IdePortInPortByte(baseIoAddress1->Error);
}
//
// Build the SMART status block depending upon the completion status.
//
cmdOutParameters->cBufferSize = byteCount;
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 (deviceExtension->SmartCommand == RETURN_SMART_STATUS) {
cmdOutParameters->bBuffer[0] = RETURN_SMART_STATUS;
cmdOutParameters->bBuffer[1] = IdePortInPortByte(baseIoAddress1->InterruptReason);
cmdOutParameters->bBuffer[2] = IdePortInPortByte(baseIoAddress1->Unused1);
cmdOutParameters->bBuffer[3] = IdePortInPortByte(baseIoAddress1->ByteCountLow);
cmdOutParameters->bBuffer[4] = IdePortInPortByte(baseIoAddress1->ByteCountHigh);
cmdOutParameters->bBuffer[5] = IdePortInPortByte(baseIoAddress1->DriveSelect);
cmdOutParameters->bBuffer[6] = SMART_CMD;
cmdOutParameters->cBufferSize = 8;
}
//
// Indicate command complete.
//
IdePortNotification(IdeRequestComplete,
deviceExtension,
srb);
}
} else {
DebugPrint((1,
"AtapiInterrupt: No SRB!\n"));
}
//
// Indicate ready for next request.
//
if (!(deviceExtension->RDP)) {
//
// Clear current SRB.
//
deviceExtension->CurrentSrb = NULL;
if (!deviceExtension->DriverMustPoll) {
IdePortNotification(IdeNextRequest,
deviceExtension,
NULL);
}
} else {
ASSERT(!deviceExtension->DriverMustPoll);
IdePortNotification(IdeRequestTimerCall,
HwDeviceExtension,
AtapiCallBack,
2000);
}
return interruptCleared;
} else {
//
// Unexpected int.
//
DebugPrint((0,
"AtapiInterrupt: Unexpected interrupt. InterruptReason %x. Status %x.\n",
interruptReason,
statusByte));
ASSERT(interruptCleared == FALSE);
return FALSE;
}
return interruptCleared;
} // end AtapiInterrupt()
ULONG
IdeSendSmartCommand(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
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
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
PSENDCMDINPARAMS pCmdInParameters = (PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
SENDCMDINPARAMS cmdInParameters = *(PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
PIDEREGS regs = &cmdInParameters.irDriveRegs;
ULONG i;
UCHAR statusByte,targetId;
ULONG byteCount;
if (cmdInParameters.irDriveRegs.bCommandReg == SMART_CMD) {
targetId = cmdInParameters.bDriveNumber;
//TODO optimize this check
if ((!(deviceExtension->DeviceFlags[targetId] & DFLAGS_DEVICE_PRESENT)) ||
(deviceExtension->DeviceFlags[targetId] & DFLAGS_ATAPI_DEVICE)) {
return SRB_STATUS_SELECTION_TIMEOUT;
}
deviceExtension->SmartCommand = cmdInParameters.irDriveRegs.bFeaturesReg;
//
// fudge the target Id field in the srb
// atapi interrupt will use this field.
//
Srb->TargetId = targetId;
//
// Determine which of the commands to carry out.
//
#ifdef ENABLE_SMARTLOG_SUPPORT
if ((cmdInParameters.irDriveRegs.bFeaturesReg == READ_ATTRIBUTES) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == READ_THRESHOLDS) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == SMART_READ_LOG)) {
#else
if ((cmdInParameters.irDriveRegs.bFeaturesReg == READ_ATTRIBUTES) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == READ_THRESHOLDS)) {
#endif
ULONG dataLength = 0;
SelectIdeLine(baseIoAddress1, targetId >> 1);
WaitOnBusy(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_BUSY) {
DebugPrint((1,
"IdeSendSmartCommand: Returning BUSY status\n"));
return SRB_STATUS_BUSY;
}
#ifdef ENABLE_SMARTLOG_SUPPORT
if (cmdInParameters.irDriveRegs.bFeaturesReg == SMART_READ_LOG) {
dataLength = cmdInParameters.irDriveRegs.bSectorCountReg* SMART_LOG_SECTOR_SIZE;
} else {
dataLength = READ_ATTRIBUTE_BUFFER_SIZE;
}
#else
dataLength = READ_ATTRIBUTE_BUFFER_SIZE;
#endif
//
// Zero the ouput buffer as the input buffer info. has been saved off locally (the buffers are the same).
//
for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) + dataLength - 1); i++) {
((PUCHAR)cmdOutParameters)[i] = 0;
}
//
// Set data buffer pointer and words left.
//
deviceExtension->DataBuffer = (PUCHAR)cmdOutParameters->bBuffer;
deviceExtension->BytesLeft = dataLength;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
SelectIdeDevice(baseIoAddress1, targetId, 0);
IdePortOutPortByte(baseIoAddress1->Error,regs->bFeaturesReg);
IdePortOutPortByte(baseIoAddress1->BlockCount,regs->bSectorCountReg);
IdePortOutPortByte(baseIoAddress1->BlockNumber,regs->bSectorNumberReg);
IdePortOutPortByte(baseIoAddress1->CylinderLow,regs->bCylLowReg);
IdePortOutPortByte(baseIoAddress1->CylinderHigh,regs->bCylHighReg);
IdePortOutPortByte(baseIoAddress1->Command,regs->bCommandReg);
//
// Wait for interrupt.
//
return SRB_STATUS_PENDING;
} else if ((cmdInParameters.irDriveRegs.bFeaturesReg == ENABLE_SMART) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == DISABLE_SMART) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == ENABLE_DISABLE_AUTOSAVE) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == EXECUTE_OFFLINE_DIAGS) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == SAVE_ATTRIBUTE_VALUES) ||
(cmdInParameters.irDriveRegs.bFeaturesReg == ENABLE_DISABLE_AUTO_OFFLINE)) {
#ifdef ENABLE_SMARTLOG_SUPPORT
//
// Allow only the non-captive tests, for now.
//
if (cmdInParameters.irDriveRegs.bFeaturesReg == EXECUTE_OFFLINE_DIAGS) {
UCHAR sectorNumber = regs->bSectorNumberReg;
if ((sectorNumber == SMART_OFFLINE_ROUTINE_OFFLINE) ||
(sectorNumber == SMART_SHORT_SELFTEST_OFFLINE) ||
(sectorNumber == SMART_EXTENDED_SELFTEST_OFFLINE) ||
(sectorNumber == SMART_ABORT_OFFLINE_SELFTEST)) {
DebugPrint((1,
"The SMART offline command %x is allowed\n",
sectorNumber));
} else if ((sectorNumber == SMART_SHORT_SELFTEST_CAPTIVE) ||
(sectorNumber == SMART_EXTENDED_SELFTEST_CAPTIVE)) {
//
// Don't allow captive mode requests, if you have a slave(another)
// device
//
if (HasSlaveDevice(deviceExtension, targetId)) {
return SRB_STATUS_INVALID_REQUEST;
}
}
}
#endif
SelectIdeLine(baseIoAddress1, targetId >> 1);
WaitOnBusy(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_BUSY) {
DebugPrint((1,
"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).
//
for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) - 1); i++) {
((PUCHAR)cmdOutParameters)[i] = 0;
}
//
// Set data buffer pointer and indicate no data transfer.
//
deviceExtension->DataBuffer = (PUCHAR)cmdOutParameters->bBuffer;
deviceExtension->BytesLeft = 0;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
SelectIdeDevice(baseIoAddress1, targetId, 0);
IdePortOutPortByte(baseIoAddress1->Error,regs->bFeaturesReg);
IdePortOutPortByte(baseIoAddress1->BlockCount,regs->bSectorCountReg);
IdePortOutPortByte(baseIoAddress1->BlockNumber,regs->bSectorNumberReg);
IdePortOutPortByte(baseIoAddress1->CylinderLow,regs->bCylLowReg);
IdePortOutPortByte(baseIoAddress1->CylinderHigh,regs->bCylHighReg);
IdePortOutPortByte(baseIoAddress1->Command,regs->bCommandReg);
//
// Wait for interrupt.
//
return SRB_STATUS_PENDING;
}
#ifdef ENABLE_SMARTLOG_SUPPORT
else if (cmdInParameters.irDriveRegs.bFeaturesReg == SMART_WRITE_LOG) {
SelectIdeLine(baseIoAddress1, targetId >> 1);
WaitOnBusy(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_BUSY) {
DebugPrint((1,
"IdeSendSmartCommand: Returning BUSY status\n"));
return SRB_STATUS_BUSY;
}
//
// we are assuming that the drive will return an error if we try to
// write multiple sectors when it is not supported.
//
//
// set the input buffer and the datalength fields.
//
deviceExtension->DataBuffer = (PUCHAR)pCmdInParameters->bBuffer;
deviceExtension->BytesLeft = cmdInParameters.irDriveRegs.bSectorCountReg* SMART_LOG_SECTOR_SIZE;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
SelectIdeDevice(baseIoAddress1, targetId, 0);
IdePortOutPortByte(baseIoAddress1->Error,regs->bFeaturesReg);
IdePortOutPortByte(baseIoAddress1->BlockCount,regs->bSectorCountReg);
IdePortOutPortByte(baseIoAddress1->BlockNumber,regs->bSectorNumberReg);
IdePortOutPortByte(baseIoAddress1->CylinderLow,regs->bCylLowReg);
IdePortOutPortByte(baseIoAddress1->CylinderHigh,regs->bCylHighReg);
IdePortOutPortByte(baseIoAddress1->Command,regs->bCommandReg);
ASSERT(!SRB_USES_DMA(Srb));
if (!SRB_USES_DMA(Srb)) {
if (deviceExtension->BytesLeft <
deviceExtension->DeviceParameters[Srb->TargetId].MaxBytePerPioInterrupt) {
byteCount = deviceExtension->BytesLeft;
} else {
byteCount = deviceExtension->DeviceParameters[Srb->TargetId].MaxBytePerPioInterrupt;
}
//
// Wait for BSY and DRQ.
//
WaitOnBaseBusy(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_BUSY) {
DebugPrint((1,
"IdeSendSmartCommand: Returning BUSY status %x\n",
statusByte));
return SRB_STATUS_BUSY;
}
if (statusByte & IDE_STATUS_ERROR) {
DebugPrint((1,
"IdeSendSmartCommand: Returning ERROR status %x\n",
statusByte));
deviceExtension->BytesLeft = 0;
//
// Clear interrupt expecting flag.
//
deviceExtension->ExpectingInterrupt = FALSE;
return MapError(deviceExtension, Srb);
}
for (i = 0; i < 1000; i++) {
GetBaseStatus(baseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_DRQ) {
break;
}
KeStallExecutionProcessor(200);
}
if (!(statusByte & IDE_STATUS_DRQ)) {
DebugPrint((1,
"IdeSmartCommand: DRQ never asserted (%x)\n",
statusByte));
deviceExtension->BytesLeft = 0;
//
// Clear interrupt expecting flag.
//
deviceExtension->ExpectingInterrupt = FALSE;
//
// Clear current SRB.
//
deviceExtension->CurrentSrb = NULL;
return SRB_STATUS_SELECTION_TIMEOUT;
}
//
// Write next 256 words.
//
WriteBuffer(baseIoAddress1,
(PUSHORT)deviceExtension->DataBuffer,
byteCount / sizeof(USHORT));
//
// Adjust buffer address and words left count.
//
deviceExtension->BytesLeft -= byteCount;
deviceExtension->DataBuffer += byteCount;
}
//
// Wait for interrupt.
//
return SRB_STATUS_PENDING;
}
#endif
}
return SRB_STATUS_INVALID_REQUEST;
} // end IdeSendSmartCommand()
#ifdef ENABLE_48BIT_LBA
ULONG
IdeReadWriteExt(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine handles IDE read and writes.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Srb - IO request packet
Return Value:
SRB status
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
ULONG i;
ULONG byteCount;
UCHAR statusByte,statusByte2;
UCHAR cylinderHigh,cylinderLow,drvSelect;
ULONG sectorCount;
LARGE_INTEGER startingSector;
//
// the device should support 48 bit LBA
//
ASSERT(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_48BIT_LBA);
ASSERT(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LBA);
//
// Select device 0 or 1.
//
SelectIdeDevice(baseIoAddress1, Srb->TargetId, 0);
GetStatus(baseIoAddress1, statusByte2);
if (statusByte2 & IDE_STATUS_BUSY) {
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite: Returning BUSY status\n"));
return SRB_STATUS_BUSY;
}
if (!(statusByte2 & IDE_STATUS_DRDY)) {
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite: IDE_STATUS_DRDY not set\n"));
return SRB_STATUS_BUSY;
}
//
// Set data buffer pointer and words left.
// BytesLeft should be 64-bit.
//
deviceExtension->DataBuffer = (PUCHAR)Srb->DataBuffer;
deviceExtension->BytesLeft = Srb->DataTransferLength;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
//
// Set up sector count register. Round up to next block.
//
sectorCount = (Srb->DataTransferLength + 0x1FF) / 0x200;
ASSERT(sectorCount != 0);
//
// Get starting sector number from CDB.
//
startingSector.QuadPart = 0;
startingSector.LowPart = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;
DebugPrint((1,
"startingSector = 0x%x, length = 0x%x\n",
startingSector.LowPart,
sectorCount
));
//
// the device shall support LBA. We will not use CHS
//
SelectIdeDevice(baseIoAddress1,
Srb->TargetId,
IDE_LBA_MODE);
//
// load the higher order bytes
//
IdePortOutPortByte (
baseIoAddress1->BlockCount,
(UCHAR)((sectorCount & 0x0000ff00) >> 8));
IdePortOutPortByte (
baseIoAddress1->BlockNumber,
(UCHAR) (((startingSector.LowPart) & 0xff000000) >> 24));
IdePortOutPortByte (
baseIoAddress1->CylinderLow,
(UCHAR) (((startingSector.HighPart) & 0x000000ff) >> 0));
IdePortOutPortByte (
baseIoAddress1->CylinderHigh,
(UCHAR) (((startingSector.HighPart) & 0x0000ff00) >> 8));
//
// load the lower order bytes
//
IdePortOutPortByte (
baseIoAddress1->BlockCount,
(UCHAR)((sectorCount & 0x000000ff) >> 0));
IdePortOutPortByte (
baseIoAddress1->BlockNumber,
(UCHAR) (((startingSector.LowPart) & 0x000000ff) >> 0));
IdePortOutPortByte (
baseIoAddress1->CylinderLow,
(UCHAR) (((startingSector.LowPart) & 0x0000ff00) >> 8));
IdePortOutPortByte (
baseIoAddress1->CylinderHigh,
(UCHAR) (((startingSector.LowPart) & 0x00ff0000) >> 16));
//
// Check if write request.
//
if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
//
// Send read command.
//
if (SRB_USES_DMA(Srb)) {
IdePortOutPortByte (
baseIoAddress1->Command,
IDE_COMMAND_READ_DMA_EXT);
} else {
ASSERT (deviceExtension->DeviceParameters[Srb->TargetId].IdePioReadCommandExt);
IdePortOutPortByte (
baseIoAddress1->Command,
deviceExtension->DeviceParameters[Srb->TargetId].IdePioReadCommandExt);
}
} else {
//
// Send write command.
//
if (SRB_USES_DMA(Srb)) {
IdePortOutPortByte (
baseIoAddress1->Command,
IDE_COMMAND_WRITE_DMA_EXT);
} else {
ASSERT(deviceExtension->DeviceParameters[Srb->TargetId].IdePioWriteCommandExt);
IdePortOutPortByte (
baseIoAddress1->Command,
deviceExtension->DeviceParameters[Srb->TargetId].IdePioWriteCommandExt);
}
if (!SRB_USES_DMA(Srb)) {
if (deviceExtension->BytesLeft <
deviceExtension->DeviceParameters[Srb->TargetId].MaxBytePerPioInterrupt) {
byteCount = deviceExtension->BytesLeft;
} else {
byteCount = deviceExtension->DeviceParameters[Srb->TargetId].MaxBytePerPioInterrupt;
}
//
// Wait for BSY and DRQ.
//
WaitOnBaseBusy(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_BUSY) {
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite 2: Returning BUSY status %x\n",
statusByte));
return SRB_STATUS_BUSY;
}
for (i = 0; i < 1000; i++) {
GetBaseStatus(baseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_DRQ) {
break;
}
KeStallExecutionProcessor(200);
}
if (!(statusByte & IDE_STATUS_DRQ)) {
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite: DRQ never asserted (%x) original status (%x)\n",
statusByte,
statusByte2));
deviceExtension->BytesLeft = 0;
//
// Clear interrupt expecting flag.
//
deviceExtension->ExpectingInterrupt = FALSE;
//
// Clear current SRB.
//
deviceExtension->CurrentSrb = NULL;
return SRB_STATUS_TIMEOUT;
}
//
// Write next 256 words.
//
WriteBuffer(baseIoAddress1,
(PUSHORT)deviceExtension->DataBuffer,
byteCount / sizeof(USHORT));
//
// Adjust buffer address and words left count.
//
deviceExtension->BytesLeft -= byteCount;
deviceExtension->DataBuffer += byteCount;
}
}
if (SRB_USES_DMA(Srb)) {
deviceExtension->DMAInProgress = TRUE;
deviceExtension->BusMasterInterface.BmArm (deviceExtension->BusMasterInterface.Context);
}
//
// Wait for interrupt.
//
return SRB_STATUS_PENDING;
} // end IdeReadWriteExt()
#endif
ULONG
IdeReadWrite(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine handles IDE read and writes.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Srb - IO request packet
Return Value:
SRB status
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
ULONG startingSector,i;
ULONG byteCount;
UCHAR statusByte,statusByte2;
UCHAR cylinderHigh,cylinderLow,drvSelect,sectorNumber;
//
// Select device 0 or 1.
//
SelectIdeDevice(baseIoAddress1, Srb->TargetId, 0);
GetStatus(baseIoAddress1, statusByte2);
if (statusByte2 & IDE_STATUS_BUSY) {
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite: Returning BUSY status\n"));
return SRB_STATUS_BUSY;
}
if (!(statusByte2 & IDE_STATUS_DRDY)) {
if ((statusByte2 == 0) &&
(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_SONY_MEMORYSTICK)) {
statusByte2=IDE_STATUS_DRDY;
} else {
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite: IDE_STATUS_DRDY not set\n"));
return SRB_STATUS_BUSY;
}
}
//
// returns status busy when atapi verifier is enabled
//
//ViIdeFakeHungController(HwDeviceExtension);
//
// Set data buffer pointer and words left.
//
deviceExtension->DataBuffer = (PUCHAR)Srb->DataBuffer;
deviceExtension->BytesLeft = Srb->DataTransferLength;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
//
// Set up sector count register. Round up to next block.
//
IdePortOutPortByte (
baseIoAddress1->BlockCount,
(UCHAR)((Srb->DataTransferLength + 0x1FF) / 0x200));
//
// Get starting sector number from CDB.
//
startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite: Starting sector is %x, Number of bytes %x\n",
startingSector,
Srb->DataTransferLength));
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LBA) {
SelectIdeDevice(baseIoAddress1,
Srb->TargetId,
(IDE_LBA_MODE | ((startingSector & 0x0f000000) >> 24)));
IdePortOutPortByte (
baseIoAddress1->BlockNumber,
(UCHAR) ((startingSector & 0x000000ff) >> 0));
IdePortOutPortByte (
baseIoAddress1->CylinderLow,
(UCHAR) ((startingSector & 0x0000ff00) >> 8));
IdePortOutPortByte (
baseIoAddress1->CylinderHigh,
(UCHAR) ((startingSector & 0x00ff0000) >> 16));
} else { //CHS
//
// Set up sector number register.
//
sectorNumber = (UCHAR)((startingSector % deviceExtension->SectorsPerTrack[Srb->TargetId]) + 1);
IdePortOutPortByte (
baseIoAddress1->BlockNumber,
sectorNumber);
//
// Set up cylinder low register.
//
cylinderLow = (UCHAR)(startingSector / (deviceExtension->SectorsPerTrack[Srb->TargetId] *
deviceExtension->NumberOfHeads[Srb->TargetId]));
IdePortOutPortByte (
baseIoAddress1->CylinderLow,
cylinderLow);
//
// Set up cylinder high register.
//
cylinderHigh = (UCHAR)((startingSector / (deviceExtension->SectorsPerTrack[Srb->TargetId] *
deviceExtension->NumberOfHeads[Srb->TargetId])) >> 8);
IdePortOutPortByte (
baseIoAddress1->CylinderHigh,
cylinderHigh);
//
// Set up head and drive select register.
//
drvSelect = (UCHAR)(((startingSector / deviceExtension->SectorsPerTrack[Srb->TargetId]) %
deviceExtension->NumberOfHeads[Srb->TargetId]));
SelectIdeDevice(baseIoAddress1, Srb->TargetId, drvSelect);
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite: Cylinder %x Head %x Sector %x\n",
startingSector /
(deviceExtension->SectorsPerTrack[Srb->TargetId] *
deviceExtension->NumberOfHeads[Srb->TargetId]),
(startingSector /
deviceExtension->SectorsPerTrack[Srb->TargetId]) %
deviceExtension->NumberOfHeads[Srb->TargetId],
startingSector %
deviceExtension->SectorsPerTrack[Srb->TargetId] + 1));
}
//
// Check if write request.
//
if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
//
// Send read command.
//
if (SRB_USES_DMA(Srb)) {
IdePortOutPortByte (
baseIoAddress1->Command,
IDE_COMMAND_READ_DMA);
} else {
IdePortOutPortByte (
baseIoAddress1->Command,
deviceExtension->DeviceParameters[Srb->TargetId].IdePioReadCommand);
}
} else {
//
// Send write command.
//
if (SRB_USES_DMA(Srb)) {
IdePortOutPortByte (
baseIoAddress1->Command,
IDE_COMMAND_WRITE_DMA);
} else {
IdePortOutPortByte (
baseIoAddress1->Command,
deviceExtension->DeviceParameters[Srb->TargetId].IdePioWriteCommand);
}
if (!SRB_USES_DMA(Srb)) {
if (deviceExtension->BytesLeft <
deviceExtension->DeviceParameters[Srb->TargetId].MaxBytePerPioInterrupt) {
byteCount = deviceExtension->BytesLeft;
} else {
byteCount = deviceExtension->DeviceParameters[Srb->TargetId].MaxBytePerPioInterrupt;
}
//
// Wait for BSY and DRQ.
//
WaitOnBaseBusy(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_BUSY) {
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite 2: Returning BUSY status %x\n",
statusByte));
return SRB_STATUS_BUSY;
}
for (i = 0; i < 1000; i++) {
GetBaseStatus(baseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_DRQ) {
break;
}
KeStallExecutionProcessor(200);
}
if (!(statusByte & IDE_STATUS_DRQ)) {
DebugPrint((DBG_CRASHDUMP | DBG_READ_WRITE,
"IdeReadWrite: DRQ never asserted (%x) original status (%x)\n",
statusByte,
statusByte2));
deviceExtension->BytesLeft = 0;
//
// Clear interrupt expecting flag.
//
deviceExtension->ExpectingInterrupt = FALSE;
//
// Clear current SRB.
//
deviceExtension->CurrentSrb = NULL;
return SRB_STATUS_TIMEOUT;
}
//
// Write next 256 words.
//
WriteBuffer(baseIoAddress1,
(PUSHORT)deviceExtension->DataBuffer,
byteCount / sizeof(USHORT));
//
// Adjust buffer address and words left count.
//
deviceExtension->BytesLeft -= byteCount;
deviceExtension->DataBuffer += byteCount;
}
}
if (SRB_USES_DMA(Srb)) {
deviceExtension->DMAInProgress = TRUE;
deviceExtension->BusMasterInterface.BmArm (deviceExtension->BusMasterInterface.Context);
}
//
// Wait for interrupt.
//
return SRB_STATUS_PENDING;
} // end IdeReadWrite()
#ifdef ENABLE_48BIT_LBA
ULONG
IdeVerifyExt(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine handles IDE Verify.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Srb - IO request packet
Return Value:
SRB status
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
LARGE_INTEGER startingSector;
ULONG sectors;
ULONG endSector;
ULONG sectorCount;
//
// the device should support 48 bit LBA
//
ASSERT(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_48BIT_LBA);
ASSERT(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LBA);
//
// Get starting sector number from CDB.
//
startingSector.QuadPart = 0;
startingSector.LowPart = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;
sectorCount = (USHORT)((((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb );
DebugPrint((3,
"IdeVerify: Starting sector %x. Number of blocks %x\n",
startingSector.LowPart,
sectorCount));
if (sectorCount > 0x10000) {
DebugPrint((DBG_ALWAYS,
"IdeVerify: verify too many sectors 0x%x\n",
sectorCount));
return SRB_STATUS_INVALID_REQUEST;
}
//
// Set data buffer pointer and words left.
//
deviceExtension->DataBuffer = Srb->DataBuffer;
deviceExtension->BytesLeft = Srb->DataTransferLength;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
SelectIdeDevice(baseIoAddress1,
Srb->TargetId,
IDE_LBA_MODE);
//
// Load the higer order bytes
//
IdePortOutPortByte(baseIoAddress1->BlockNumber,
(UCHAR) (((startingSector.LowPart) & 0xff000000) >> 24));
IdePortOutPortByte(baseIoAddress1->CylinderLow,
(UCHAR) (((startingSector.HighPart) & 0x000000ff) >> 0));
IdePortOutPortByte(baseIoAddress1->CylinderHigh,
(UCHAR) (((startingSector.HighPart) & 0x0000ff00) >> 8));
IdePortOutPortByte(baseIoAddress1->BlockCount,
(UCHAR)((sectorCount & 0x0000ff00) >> 8));
//
// Load the lower order bytes
//
IdePortOutPortByte(baseIoAddress1->BlockNumber,
(UCHAR) (((startingSector.LowPart) & 0x000000ff) >> 0));
IdePortOutPortByte(baseIoAddress1->CylinderLow,
(UCHAR) (((startingSector.LowPart) & 0x0000ff00) >> 8));
IdePortOutPortByte(baseIoAddress1->CylinderHigh,
(UCHAR) (((startingSector.LowPart) & 0x00ff0000) >> 16));
IdePortOutPortByte(baseIoAddress1->BlockCount,
(UCHAR)((sectorCount & 0x000000ff) >> 0));
//
// Send verify command.
//
IdePortOutPortByte(baseIoAddress1->Command,
IDE_COMMAND_VERIFY_EXT);
//
// Wait for interrupt.
//
return SRB_STATUS_PENDING;
} // end IdeVerifyExt()
#endif
ULONG
IdeVerify(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine handles IDE Verify.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Srb - IO request packet
Return Value:
SRB status
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
ULONG startingSector;
ULONG sectors;
ULONG endSector;
USHORT sectorCount;
//
// Drive has these number sectors.
//
#if DBG
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LBA) { // LBA
sectors = deviceExtension->IdentifyData[Srb->TargetId].UserAddressableSectors;
} else {
sectors = deviceExtension->SectorsPerTrack[Srb->TargetId] *
deviceExtension->NumberOfHeads[Srb->TargetId] *
deviceExtension->NumberOfCylinders[Srb->TargetId];
}
#endif
DebugPrint((3,
"IdeVerify: Total sectors %x\n",
sectors));
//
// Get starting sector number from CDB.
//
startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;
DebugPrint((3,
"IdeVerify: Starting sector %x. Number of blocks %x\n",
startingSector,
((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb));
sectorCount = (USHORT)(((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8 |
((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb );
endSector = startingSector + sectorCount;
DebugPrint((3,
"IdeVerify: Ending sector %x\n",
endSector));
if (sectorCount > 0x100) {
DebugPrint((DBG_ALWAYS,
"IdeVerify: verify too many sectors 0x%x\n",
sectorCount));
return SRB_STATUS_INVALID_REQUEST;
}
//
// Set data buffer pointer and words left.
//
deviceExtension->DataBuffer = Srb->DataBuffer;
deviceExtension->BytesLeft = Srb->DataTransferLength;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LBA) { // LBA
SelectIdeDevice(baseIoAddress1,
Srb->TargetId,
(IDE_LBA_MODE |((startingSector & 0x0f000000) >> 24)));
IdePortOutPortByte(baseIoAddress1->BlockNumber,
(UCHAR) ((startingSector & 0x000000ff) >> 0));
IdePortOutPortByte(baseIoAddress1->CylinderLow,
(UCHAR) ((startingSector & 0x0000ff00) >> 8));
IdePortOutPortByte(baseIoAddress1->CylinderHigh,
(UCHAR) ((startingSector & 0x00ff0000) >> 16));
DebugPrint((2,
"IdeVerify: LBA: startingSector %x\n",
startingSector));
} else { //CHS
//
// Set up head and drive select register.
//
SelectIdeDevice(baseIoAddress1,
Srb->TargetId,
(UCHAR)((startingSector /
deviceExtension->SectorsPerTrack[Srb->TargetId]) %
deviceExtension->NumberOfHeads[Srb->TargetId]));
//
// Set up sector number register.
//
IdePortOutPortByte(baseIoAddress1->BlockNumber,
(UCHAR)((startingSector %
deviceExtension->SectorsPerTrack[Srb->TargetId]) + 1));
//
// Set up cylinder low register.
//
IdePortOutPortByte(baseIoAddress1->CylinderLow,
(UCHAR)(startingSector /
(deviceExtension->SectorsPerTrack[Srb->TargetId] *
deviceExtension->NumberOfHeads[Srb->TargetId])));
//
// Set up cylinder high register.
//
IdePortOutPortByte(baseIoAddress1->CylinderHigh,
(UCHAR)((startingSector /
(deviceExtension->SectorsPerTrack[Srb->TargetId] *
deviceExtension->NumberOfHeads[Srb->TargetId])) >> 8));
DebugPrint((2,
"IdeVerify: CHS: Cylinder %x Head %x Sector %x\n",
startingSector /
(deviceExtension->SectorsPerTrack[Srb->TargetId] *
deviceExtension->NumberOfHeads[Srb->TargetId]),
(startingSector /
deviceExtension->SectorsPerTrack[Srb->TargetId]) %
deviceExtension->NumberOfHeads[Srb->TargetId],
startingSector %
deviceExtension->SectorsPerTrack[Srb->TargetId] + 1));
}
/********
if (endSector > sectors) {
//
// Too big, round down.
//
DebugPrint((1,
"IdeVerify: Truncating request to %x blocks\n",
sectors - startingSector - 1));
IdePortOutPortByte(baseIoAddress1->BlockCount,
(UCHAR)(sectors - startingSector - 1));
} else {
IdePortOutPortByte(baseIoAddress1->BlockCount,(UCHAR)sectorCount);
}
******/
IdePortOutPortByte(baseIoAddress1->BlockCount,(UCHAR)sectorCount);
//
// Send verify command.
//
IdePortOutPortByte(baseIoAddress1->Command,
IDE_COMMAND_VERIFY);
//
// Wait for interrupt.
//
return SRB_STATUS_PENDING;
} // end IdeVerify()
VOID
Scsi2Atapi(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Convert SCSI packet command to Atapi packet command.
Arguments:
Srb - IO request packet
Return Value:
None
--*/
{
SAVE_ORIGINAL_CDB(DeviceExtension, Srb);
DeviceExtension->scsi2atapi = FALSE;
if (!(DeviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_TAPE_DEVICE)) {
//
// Change the cdb length
//
//Srb->CdbLength = 12;
switch (Srb->Cdb[0]) {
case SCSIOP_MODE_SENSE: {
ASSERT(FALSE);
break;
}
case SCSIOP_MODE_SELECT: {
ASSERT (FALSE);
break;
}
case SCSIOP_START_STOP_UNIT: {
//
// Bad Cd-roms
// STOP command (1B) hangs during shutdown/hibernation on
// some cd-rom drives. Setting the Immediate bit to 0 seems
// to work
//
PCDB cdb = (PCDB)Srb->Cdb;
if ((cdb->START_STOP.Immediate == 1) &&
(cdb->START_STOP.LoadEject == 0) &&
(cdb->START_STOP.Start == 0))
cdb->START_STOP.Immediate=0;
DeviceExtension->scsi2atapi = TRUE;
break;
}
case SCSIOP_FORMAT_UNIT:
if (DeviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_LS120_FORMAT) {
Srb->Cdb[0] = ATAPI_LS120_FORMAT_UNIT;
DeviceExtension->scsi2atapi = TRUE;
}
break;
}
}
return;
} // Scsi2Atapi
ULONG
AtapiSendCommand(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Send ATAPI packet command to device.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Srb - IO request packet
Return Value:
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PATAPI_REGISTERS_1 baseIoAddress1 = (PATAPI_REGISTERS_1)&deviceExtension->BaseIoAddress1;
PATAPI_REGISTERS_2 baseIoAddress2 = (PATAPI_REGISTERS_2)&deviceExtension->BaseIoAddress2;
ULONG i;
ULONG flags;
UCHAR statusByte,byteCountLow,byteCountHigh;
#ifdef ENABLE_48BIT_LBA
ASSERT(!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_48BIT_LBA));
#endif
DebugPrint((DBG_ATAPI_DEVICES,
"AtapiSendCommand: Command %x to TargetId %d lun %d\n",
Srb->Cdb[0],
Srb->TargetId,
Srb->Lun));
if (Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) {
DebugPrint((DBG_ATAPI_DEVICES,
"AtapiSendCommand: xferLength=%x, LBA=%x\n",
Srb->DataTransferLength,
(((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 |
(((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 8) |
(((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 16) |
(((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 << 24))
));
}
//
// Make sure command is to ATAPI device.
//
flags = deviceExtension->DeviceFlags[Srb->TargetId];
if (Srb->Lun > deviceExtension->LastLun[Srb->TargetId]) {
return SRB_STATUS_SELECTION_TIMEOUT;
}
if (!(flags & DFLAGS_ATAPI_DEVICE)) {
return SRB_STATUS_SELECTION_TIMEOUT;
}
//
// Select device 0 or 1.
//
SelectIdeDevice(baseIoAddress1, Srb->TargetId, 0);
//
// Verify that controller is ready for next command.
//
GetStatus(baseIoAddress1,statusByte);
DebugPrint((2,
"AtapiSendCommand: Entered with status %x\n",
statusByte));
if (statusByte & IDE_STATUS_BUSY) {
DebugPrint((1,
"AtapiSendCommand: Device busy (%x)\n",
statusByte));
return SRB_STATUS_BUSY;
}
//
// If a tape drive has 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 0
if ((!(statusByte & IDE_STATUS_DSC)) &&
(flags & DFLAGS_TAPE_DEVICE) && deviceExtension->RDP) {
KeStallExecutionProcessor(1000);
DebugPrint((2,"AtapiSendCommand: DSC not set. %x\n",statusByte));
return SRB_STATUS_BUSY;
}
#endif
//
// Extended RDP to include SEEK commands for CD-ROMS.
//
if ((!(statusByte & IDE_STATUS_DSC)) && deviceExtension->RDP &&
(flags & DFLAGS_RDP_SET)) {
KeStallExecutionProcessor(1000);
DebugPrint((DBG_ATAPI_DEVICES,
"AtapiSendCommand: DSC not set. %x\n",
statusByte
));
return SRB_STATUS_BUSY;
}
if (SRB_IS_RDP(Srb)) {
deviceExtension->RDP = TRUE;
SETMASK(deviceExtension->DeviceFlags[Srb->TargetId], DFLAGS_RDP_SET);
DebugPrint((3,
"AtapiSendCommand: %x mapped as DSC restrictive\n",
Srb->Cdb[0]));
} else {
deviceExtension->RDP = FALSE;
CLRMASK(deviceExtension->DeviceFlags[Srb->TargetId], DFLAGS_RDP_SET);
}
//
// Convert SCSI to ATAPI commands if needed
//
Scsi2Atapi(deviceExtension, Srb);
//
// Set data buffer pointer and words left.
//
deviceExtension->DataBuffer = Srb->DataBuffer;
deviceExtension->BytesLeft = Srb->DataTransferLength;
WaitOnBusy(baseIoAddress1,statusByte);
//
// Write transfer byte count to registers.
//
byteCountLow = (UCHAR)(Srb->DataTransferLength & 0xFF);
byteCountHigh = (UCHAR)(Srb->DataTransferLength >> 8);
if (Srb->DataTransferLength >= 0x10000) {
byteCountLow = byteCountHigh = 0xFF;
}
IdePortOutPortByte(baseIoAddress1->ByteCountLow,byteCountLow);
IdePortOutPortByte(baseIoAddress1->ByteCountHigh, byteCountHigh);
if (SRB_USES_DMA(Srb)) {
IdePortOutPortByte(baseIoAddress1->Error, 0x1);
} else {
IdePortOutPortByte(baseIoAddress1->Error, 0x0);
}
if (flags & DFLAGS_INT_DRQ) {
//
// This device interrupts when ready to receive the packet.
//
// Write ATAPI packet command.
//
deviceExtension->ExpectingInterrupt = TRUE;
IdePortOutPortByte(baseIoAddress1->Command,
IDE_COMMAND_ATAPI_PACKET);
DebugPrint((3,
"AtapiSendCommand: Wait for int. to send packet. Status (%x)\n",
statusByte));
return SRB_STATUS_PENDING;
} else {
//
// Write ATAPI packet command.
//
IdePortOutPortByte(baseIoAddress1->Command,
IDE_COMMAND_ATAPI_PACKET);
//
// Wait for DRQ.
//
WaitOnBusy(baseIoAddress1, statusByte);
WaitForDrq(baseIoAddress1, statusByte);
if (!(statusByte & IDE_STATUS_DRQ)) {
DebugPrint((1,
"AtapiSendCommand: DRQ never asserted (%x)\n",
statusByte));
return SRB_STATUS_ERROR;
}
}
//
// Need to read status register.
//
GetBaseStatus(baseIoAddress1, statusByte);
//
// Indicate expecting an interrupt and wait for it.
//
deviceExtension->ExpectingInterrupt = TRUE;
//
// Send CDB to device.
//
WaitOnBusy(baseIoAddress1,statusByte);
WriteBuffer(baseIoAddress1,
(PUSHORT)Srb->Cdb,
6);
if (SRB_USES_DMA(Srb)) {
deviceExtension->DMAInProgress = TRUE;
deviceExtension->BusMasterInterface.BmArm (deviceExtension->BusMasterInterface.Context);
}
return SRB_STATUS_PENDING;
} // end AtapiSendCommand()
ULONG
IdeSendFlushCommand(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Program ATA registers for IDE flush command.
Arguments:
HwDeviceExtension - ATAPI driver storage.
Srb - System request block.
Return Value:
SRB status (pending if all goes well).
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
UCHAR flushCommand;
ULONG status;
//
// Get the flush command
//
flushCommand = deviceExtension->DeviceParameters[Srb->TargetId].IdeFlushCommand;
//
// We should check for the case where we have already queued a
// flush cache command.
//
if (flushCommand == IDE_COMMAND_NO_FLUSH) {
return SRB_STATUS_SUCCESS;
}
//
// if we reached this stage then the device should support flush command
//
ASSERT (flushCommand != IDE_COMMAND_NO_FLUSH);
DebugPrint((1,
"IdeSendFlushCommand: device %d, srb 0x%x\n",
Srb->TargetId,
Srb
));
//
// Select the right device
//
SelectIdeDevice(baseIoAddress1, Srb->TargetId, 0);
//
// Set data buffer pointer and words left.
//
deviceExtension->DataBuffer = (PUCHAR)Srb->DataBuffer;
deviceExtension->BytesLeft = Srb->DataTransferLength;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
status = SRB_STATUS_PENDING;
//
// Program the TaskFile registers
//
IdePortOutPortByte(baseIoAddress1->Error, 0);
IdePortOutPortByte(baseIoAddress1->BlockCount, 0);
IdePortOutPortByte(baseIoAddress1->BlockNumber, 0);
IdePortOutPortByte(baseIoAddress1->CylinderLow, 0);
IdePortOutPortByte(baseIoAddress1->CylinderHigh, 0);
IdePortOutPortByte(baseIoAddress1->Command, flushCommand);
return status;
}
#ifdef ENABLE_48BIT_LBA
ULONG
IdeSendFlushCommandExt(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Program ATA registers for IDE flush ext command.
Arguments:
HwDeviceExtension - ATAPI driver storage.
Srb - System request block.
Return Value:
SRB status (pending if all goes well).
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
UCHAR flushCommand;
ULONG status;
UCHAR statusByte;
//
// the device should support 48 bit LBA
//
ASSERT(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_48BIT_LBA);
//
// Get the flush command
//
flushCommand = deviceExtension->DeviceParameters[Srb->TargetId].IdeFlushCommandExt;
//
// We should check for the case where we have already queued a
// flush cache command.
//
if (flushCommand == IDE_COMMAND_NO_FLUSH) {
return SRB_STATUS_SUCCESS;
}
//
// if we reached this stage then the device should support flush command
//
ASSERT (flushCommand != IDE_COMMAND_NO_FLUSH);
DebugPrint((1,
"IdeSendFlushCommand: device %d, srb 0x%x\n",
Srb->TargetId,
Srb
));
//
// Select the right device
//
SelectIdeDevice(baseIoAddress1, Srb->TargetId, 0);
//
// Set data buffer pointer and words left.
//
deviceExtension->DataBuffer = (PUCHAR)Srb->DataBuffer;
deviceExtension->BytesLeft = Srb->DataTransferLength;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
status = SRB_STATUS_PENDING;
//
// Program the TaskFile registers (previous content)
//
IdePortOutPortByte(baseIoAddress1->Error, 0);
IdePortOutPortByte(baseIoAddress1->BlockCount, 0);
IdePortOutPortByte(baseIoAddress1->BlockNumber, 0);
IdePortOutPortByte(baseIoAddress1->CylinderLow, 0);
IdePortOutPortByte(baseIoAddress1->CylinderHigh, 0);
//
// Program the TaskFile registers (current content)
//
IdePortOutPortByte(baseIoAddress1->Error, 0);
IdePortOutPortByte(baseIoAddress1->BlockCount, 0);
IdePortOutPortByte(baseIoAddress1->BlockNumber, 0);
IdePortOutPortByte(baseIoAddress1->CylinderLow, 0);
IdePortOutPortByte(baseIoAddress1->CylinderHigh, 0);
IdePortOutPortByte(baseIoAddress1->Command, flushCommand);
return status;
}
#endif
ULONG
IdeSendCommand(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Program ATA registers for IDE disk transfer.
Arguments:
HwDeviceExtension - ATAPI driver storage.
Srb - System request block.
Return Value:
SRB status (pending if all goes well).
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
PCDB cdb;
UCHAR statusByte,errorByte;
ULONG status;
ULONG i;
PMODE_PARAMETER_HEADER modeData;
DebugPrint((2,
"IdeSendCommand: Command %x to device %d\n",
Srb->Cdb[0],
Srb->TargetId));
switch (Srb->Cdb[0]) {
case SCSIOP_INQUIRY:
//
// Filter out all TIDs but 0 and 1 since this is an IDE interface
// which support up to two devices.
//
if ((Srb->Lun != 0) ||
(!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT))) {
//
// Indicate no device found at this address.
//
status = SRB_STATUS_SELECTION_TIMEOUT;
break;
} else {
INQUIRYDATA inquiryData;
PIDENTIFY_DATA identifyData = &deviceExtension->IdentifyData[Srb->TargetId];
//
// Zero INQUIRY data structure.
//
RtlZeroMemory(Srb->DataBuffer, Srb->DataTransferLength);
RtlZeroMemory((PUCHAR) &inquiryData, sizeof(INQUIRYDATA));
inquiryData.DeviceType = DIRECT_ACCESS_DEVICE;
//
// Set the removable bit, if applicable.
//
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_REMOVABLE_DRIVE) {
inquiryData.RemovableMedia = 1;
}
//
// Fill in vendor identification fields.
//
for (i = 0; i < 20; i += 2) {
inquiryData.VendorId[i] =
((PUCHAR)identifyData->ModelNumber)[i + 1];
inquiryData.VendorId[i+1] =
((PUCHAR)identifyData->ModelNumber)[i];
}
//
// Initialize unused portion of product id.
//
for (i = 0; i < 4; i++) {
inquiryData.ProductId[12+i] = ' ';
}
//
// Move firmware revision from IDENTIFY data to
// product revision in INQUIRY data.
//
for (i = 0; i < 4; i += 2) {
inquiryData.ProductRevisionLevel[i] =
((PUCHAR)identifyData->FirmwareRevision)[i+1];
inquiryData.ProductRevisionLevel[i+1] =
((PUCHAR)identifyData->FirmwareRevision)[i];
}
//
// Copy as much the return data as possible
//
RtlMoveMemory (
Srb->DataBuffer,
&inquiryData,
Srb->DataTransferLength > sizeof (INQUIRYDATA) ?
sizeof (INQUIRYDATA) :
Srb->DataTransferLength
);
status = SRB_STATUS_SUCCESS;
}
break;
case SCSIOP_TEST_UNIT_READY:
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED) {
//
// Select device 0 or 1.
//
SelectIdeDevice(baseIoAddress1, Srb->TargetId, 0);
IdePortOutPortByte(baseIoAddress1->Command,IDE_COMMAND_GET_MEDIA_STATUS);
//
// Wait for busy. If media has not changed, return success
//
WaitOnBusy(baseIoAddress1,statusByte);
if (!(statusByte & IDE_STATUS_ERROR)) {
deviceExtension->ExpectingInterrupt = FALSE;
status = SRB_STATUS_SUCCESS;
} else {
errorByte = IdePortInPortByte(baseIoAddress1->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(baseIoAddress1, statusByte);
status = SRB_STATUS_SUCCESS;
} else {
deviceExtension->ReturningMediaStatus = errorByte;
//
// we need to set the scsi status here. Otherwise we
// won't issue a request sense
//
Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
Srb->SrbStatus = SRB_STATUS_ERROR;
status = SRB_STATUS_ERROR;
}
}
} else {
status = SRB_STATUS_SUCCESS;
}
break;
case SCSIOP_VERIFY:
#ifdef ENABLE_48BIT_LBA
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_48BIT_LBA) {
status = IdeVerifyExt(HwDeviceExtension, Srb);
break;
}
#endif
status = IdeVerify(HwDeviceExtension,Srb);
break;
#ifdef DIDE_CPQ_BM
case SCSIOP_DVD_READ:
case SCSIOP_REPORT_KEY:
case SCSIOP_SEND_KEY:
case SCSIOP_READ_DVD_STRUCTURE:
#endif
case SCSIOP_READ:
case SCSIOP_WRITE:
#ifdef ENABLE_48BIT_LBA
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_48BIT_LBA) {
status = IdeReadWriteExt(HwDeviceExtension,
Srb);
break;
}
#endif
status = IdeReadWrite(HwDeviceExtension,
Srb);
break;
case SCSIOP_START_STOP_UNIT:
//
//Determine what type of operation we should perform
//
cdb = (PCDB)Srb->Cdb;
if (cdb->START_STOP.LoadEject == 1) {
SelectIdeLine(baseIoAddress1, Srb->TargetId >> 1);
//
// Eject media,
// first select device 0 or 1.
//
WaitOnBusy(baseIoAddress1,statusByte);
SelectIdeDevice(baseIoAddress1, Srb->TargetId, 0);
IdePortOutPortByte(baseIoAddress1->Command,IDE_COMMAND_MEDIA_EJECT);
}
status = SRB_STATUS_SUCCESS;
break;
case SCSIOP_MEDIUM_REMOVAL:
cdb = (PCDB)Srb->Cdb;
SelectIdeLine(baseIoAddress1, Srb->TargetId >> 1);
WaitOnBusy(baseIoAddress1,statusByte);
SelectIdeDevice(baseIoAddress1, Srb->TargetId, 0);
if (cdb->MEDIA_REMOVAL.Prevent == TRUE) {
IdePortOutPortByte(baseIoAddress1->Command,IDE_COMMAND_DOOR_LOCK);
} else {
IdePortOutPortByte(baseIoAddress1->Command,IDE_COMMAND_DOOR_UNLOCK);
}
status = SRB_STATUS_SUCCESS;
WaitOnBusy(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_ERROR) {
errorByte = IdePortInPortByte(baseIoAddress1->Error);
status = MapError(HwDeviceExtension, Srb);
}
break;
case SCSIOP_REQUEST_SENSE:
// this function makes sense buffers to report the results
// of the original GET_MEDIA_STATUS command
if ((deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED) &&
(Srb->DataTransferLength >= sizeof(SENSE_DATA))) {
status = IdeBuildSenseBuffer(HwDeviceExtension, Srb);
} else {
status = SRB_STATUS_INVALID_REQUEST;
}
break;
case SCSIOP_SYNCHRONIZE_CACHE:
DebugPrint((1,
"Flush the cache for IDE device %d\n",
Srb->TargetId
));
status = SRB_STATUS_SUCCESS;
//
// Send the flush command if one exists
//
if (deviceExtension->DeviceParameters[Srb->TargetId].IdeFlushCommand !=
IDE_COMMAND_NO_FLUSH) {
#ifdef ENABLE_48BIT_LBA
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_48BIT_LBA) {
status = IdeSendFlushCommandExt(HwDeviceExtension,
Srb);
} else
#endif
status = IdeSendFlushCommand(deviceExtension, Srb);
}
break;
case SCSIOP_FORMAT_UNIT:
if ( IsNEC_98 ) {
//
// Support physical format of fixed disk.
// It is meaningful for SCSI device.
// So, we do not execute it on IDE device.
// But we need to return the success in order to fit with SCSI
//
status = SRB_STATUS_SUCCESS;
break;
}
default:
DebugPrint((1,
"IdeSendCommand: Unsupported command %x\n",
Srb->Cdb[0]));
status = SRB_STATUS_INVALID_REQUEST;
} // end switch
return status;
} // end IdeSendCommand()
ULONG
IdeSendPassThroughCommand(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Program ATA registers for IDE disk transfer.
Arguments:
HwDeviceExtension - ATAPI driver storage.
Srb - System request block.
Return Value:
SRB status (pending if all goes well).
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = &deviceExtension->BaseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2 = &deviceExtension->BaseIoAddress2;
PCDB cdb;
UCHAR statusByte,errorByte;
ULONG status;
PATA_PASS_THROUGH ataPassThroughData;
PIDEREGS pIdeReg;
ataPassThroughData = Srb->DataBuffer;
pIdeReg = &ataPassThroughData->IdeReg;
//
// select the right device
//
CLRMASK (pIdeReg->bDriveHeadReg, 0xb0);
pIdeReg->bDriveHeadReg |= (UCHAR) (((Srb->TargetId & 0x1) << 4) | 0xA0);
SelectIdeDevice(baseIoAddress1, Srb->TargetId, pIdeReg->bDriveHeadReg);
//
// check to see if this is a "no-op" SRB
//
if (pIdeReg->bReserved & ATA_PTFLAGS_NO_OP) {
ULONG repeatCount = (ULONG)ataPassThroughData->IdeReg.bSectorCountReg;
UCHAR busyWait = pIdeReg->bSectorNumberReg;
//
// wait for busy if this is set
//
if (busyWait != 0) {
ULONG busyWaitTime;
if (busyWait > 30) {
busyWait = 30;
}
busyWaitTime = busyWait * 1000;
WaitOnBusyUntil(baseIoAddress1, statusByte, busyWaitTime);
}
if (repeatCount <= 0) {
repeatCount = 1;
}
while (repeatCount) {
repeatCount--;
KeStallExecutionProcessor(100);
//
// get a copy of the task file registers
//
AtapiTaskRegisterSnapshot (
baseIoAddress1,
pIdeReg
);
}
return SRB_STATUS_SUCCESS;
}
if (pIdeReg->bReserved & ATA_PTFLAGS_EMPTY_CHANNEL_TEST) {
#ifdef DPC_FOR_EMPTY_CHANNEL
if (status=IdePortChannelEmptyQuick(baseIoAddress1, baseIoAddress2,
deviceExtension->MaxIdeDevice, &deviceExtension->CurrentIdeDevice,
&deviceExtension->MoreWait, &deviceExtension->NoRetry)) {
if (status==STATUS_RETRY) {
return SRB_STATUS_PENDING;
}
return SRB_STATUS_SUCCESS;
} else {
return SRB_STATUS_ERROR;
}
#endif
if (IdePortChannelEmpty(baseIoAddress1, baseIoAddress2, deviceExtension->MaxIdeDevice)) {
return SRB_STATUS_SUCCESS;
} else {
return SRB_STATUS_ERROR;
}
}
if (pIdeReg->bReserved & ATA_PTFLAGS_INLINE_HARD_RESET) {
IdeHardReset (
baseIoAddress1,
baseIoAddress2,
FALSE,
TRUE
);
//
// re-select the right device
//
SelectIdeDevice(baseIoAddress1, Srb->TargetId, pIdeReg->bDriveHeadReg);
}
GetStatus(baseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_BUSY) {
DebugPrint((1,
"IdeSendPassThroughCommand: Returning BUSY status\n"));
return SRB_STATUS_BUSY;
}
if (pIdeReg->bReserved & ATA_PTFLAGS_STATUS_DRDY_REQUIRED) {
if (!(statusByte & IDE_STATUS_DRDY)) {
if ((statusByte == 0) &&
(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_SONY_MEMORYSTICK)) {
statusByte = IDE_STATUS_DRDY;
} else {
DebugPrint((1,
"IdeSendPassThroughCommand: DRDY not ready\n"));
return SRB_STATUS_BUSY;
}
}
}
if (pIdeReg->bCommandReg != IDE_COMMAND_ATAPI_RESET) {
// if identifydata in device extension is valid, use it
#if 1
if ((deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_IDENTIFY_VALID) &&
(pIdeReg->bCommandReg == IDE_COMMAND_IDENTIFY)) {
ASSERT(!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_REMOVABLE_DRIVE));
DebugPrint((1, "Bypassing identify command\n"));
RtlMoveMemory(ataPassThroughData->DataBuffer, &(deviceExtension->IdentifyData[Srb->TargetId]),
ataPassThroughData->DataBufferSize);
return SRB_STATUS_SUCCESS;
}
#endif
//
// Set data buffer pointer and bytes left.
//
deviceExtension->DataBuffer = ataPassThroughData->DataBuffer;
deviceExtension->BytesLeft = ataPassThroughData->DataBufferSize;
//
// Indicate expecting an interrupt.
//
deviceExtension->ExpectingInterrupt = TRUE;
status = SRB_STATUS_PENDING;
IdePortOutPortByte(baseIoAddress1->Error, pIdeReg->bFeaturesReg);
IdePortOutPortByte(baseIoAddress1->BlockCount, pIdeReg->bSectorCountReg);
IdePortOutPortByte(baseIoAddress1->BlockNumber, pIdeReg->bSectorNumberReg);
IdePortOutPortByte(baseIoAddress1->CylinderLow, pIdeReg->bCylLowReg);
IdePortOutPortByte(baseIoAddress1->CylinderHigh, pIdeReg->bCylHighReg);
IdePortOutPortByte(baseIoAddress1->Command, pIdeReg->bCommandReg);
} else {
//
// perform sync. atapi soft reset because this command doesn't generate interrupts
//
AtapiSoftReset(baseIoAddress1, baseIoAddress2, Srb->TargetId & 0x1, FALSE);
status = SRB_STATUS_SUCCESS;
}
DebugPrint ((1, "IdeSendPassThroughCommand: 0x%x 0x%x command = 0x%x\n", baseIoAddress1->RegistersBaseAddress, Srb->TargetId, pIdeReg->bCommandReg));
return status;
} // end IdeSendPassThroughCommand()
VOID
IdeMediaStatus(
BOOLEAN EnableMSN,
IN PVOID HwDeviceExtension,
ULONG DeviceNumber
)
/*++
Routine Description:
Enables disables media status notification
Arguments:
HwDeviceExtension - ATAPI driver storage.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress = &deviceExtension->BaseIoAddress1;
UCHAR statusByte,errorByte;
if (EnableMSN == TRUE) {
//
// If supported enable Media Status Notification support
//
if ((deviceExtension->DeviceFlags[DeviceNumber] & DFLAGS_MSN_SUPPORT)) {
//
// enable
//
SelectIdeDevice(baseIoAddress, DeviceNumber, 0);
IdePortOutPortByte(baseIoAddress->Error,(UCHAR) (0x95));
IdePortOutPortByte(baseIoAddress->Command,
IDE_COMMAND_SET_FEATURE);
WaitOnBaseBusy(baseIoAddress,statusByte);
if (statusByte & IDE_STATUS_ERROR) {
//
// Read the error register.
//
errorByte = IdePortInPortByte(baseIoAddress->Error);
DebugPrint((1,
"IdeMediaStatus: Error enabling media status. Status %x, error byte %x\n",
statusByte,
errorByte));
} else {
deviceExtension->DeviceFlags[DeviceNumber] |= DFLAGS_MEDIA_STATUS_ENABLED;
DebugPrint((1,"IdeMediaStatus: Media Status Notification Supported\n"));
deviceExtension->ReturningMediaStatus = 0;
}
}
} else { // end if EnableMSN == TRUE
//
// disable if previously enabled
//
if ((deviceExtension->DeviceFlags[DeviceNumber] & DFLAGS_MEDIA_STATUS_ENABLED)) {
SelectIdeDevice(baseIoAddress, DeviceNumber, 0);
IdePortOutPortByte(baseIoAddress->Error,(UCHAR) (0x31));
IdePortOutPortByte(baseIoAddress->Command,
IDE_COMMAND_SET_FEATURE);
WaitOnBaseBusy(baseIoAddress,statusByte);
CLRMASK (deviceExtension->DeviceFlags[DeviceNumber], DFLAGS_MEDIA_STATUS_ENABLED);
}
}
}
ULONG
IdeBuildSenseBuffer(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
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).
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
ULONG status;
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->DataBuffer;
if (senseBuffer) {
if (deviceExtension->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 (deviceExtension->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_OPERATOR_REQUEST;
senseBuffer->AdditionalSenseCodeQualifier = SCSI_SENSEQ_MEDIUM_REMOVAL;
} else if (deviceExtension->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 (deviceExtension->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
BOOLEAN
AtapiStartIo(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine is called from the port driver synchronized
with the kernel to start an IO request.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Srb - IO request packet
Return Value:
TRUE
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
ULONG status;
//
// Determine which function.
//
switch (Srb->Function) {
case SRB_FUNCTION_ATA_POWER_PASS_THROUGH:
case SRB_FUNCTION_FLUSH:
case SRB_FUNCTION_SHUTDOWN:
case SRB_FUNCTION_ATA_PASS_THROUGH:
case SRB_FUNCTION_EXECUTE_SCSI:
//
// Sanity check. Only one request can be outstanding on a
// controller.
//
if (deviceExtension->CurrentSrb) {
DebugPrint((1,
"AtapiStartIo: Already have a request!\n"));
Srb->SrbStatus = SRB_STATUS_BUSY;
IdePortNotification(IdeRequestComplete,
deviceExtension,
Srb);
return FALSE;
}
//
// Indicate that a request is active on the controller.
//
deviceExtension->CurrentSrb = Srb;
//
// Send command to device.
//
// ATA_PASSTHORUGH
if ((Srb->Function == SRB_FUNCTION_ATA_PASS_THROUGH) ||
(Srb->Function == SRB_FUNCTION_ATA_POWER_PASS_THROUGH)) {
status = IdeSendPassThroughCommand(HwDeviceExtension,
Srb);
} else if ((deviceExtension->DeviceFlags[Srb->TargetId] &
(DFLAGS_ATAPI_DEVICE | DFLAGS_DEVICE_PRESENT)) ==
(DFLAGS_ATAPI_DEVICE | DFLAGS_DEVICE_PRESENT)) {
status = AtapiSendCommand(HwDeviceExtension,
Srb);
} else if ((Srb->Function == SRB_FUNCTION_FLUSH) ||
(Srb->Function == SRB_FUNCTION_SHUTDOWN)) {
#ifdef ENABLE_48BIT_LBA
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_48BIT_LBA) {
status = IdeSendFlushCommandExt(HwDeviceExtension,
Srb);
} else {
#endif
status = IdeSendFlushCommand(HwDeviceExtension,
Srb);
#ifdef ENABLE_48BIT_LBA
}
#endif
} else if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT) {
status = IdeSendCommand(HwDeviceExtension,
Srb);
} else {
status = SRB_STATUS_SELECTION_TIMEOUT;
}
break;
case SRB_FUNCTION_ABORT_COMMAND:
//
// Verify that SRB to abort is still outstanding.
//
if (!deviceExtension->CurrentSrb) {
DebugPrint((1, "AtapiStartIo: SRB to abort already completed\n"));
//
// Complete abort SRB.
//
status = SRB_STATUS_ABORT_FAILED;
break;
}
//
// Abort function indicates that a request timed out.
// Call reset routine. Card will only be reset if
// status indicates something is wrong.
// Fall through to reset code.
//
case SRB_FUNCTION_RESET_BUS:
//
// Reset Atapi and SCSI bus.
//
DebugPrint((1, "AtapiStartIo: Reset bus request received\n"));
SelectIdeLine(&deviceExtension->BaseIoAddress1, Srb->TargetId >> 1);
if (!AtapiSyncResetController(deviceExtension,
Srb->PathId)) {
DebugPrint((1,"AtapiStartIo: Reset bus failed\n"));
//
// Log reset failure.
//
IdePortLogError(
HwDeviceExtension,
NULL,
0,
0,
0,
SP_INTERNAL_ADAPTER_ERROR,
5 << 8
);
status = SRB_STATUS_ERROR;
} else {
status = SRB_STATUS_SUCCESS;
}
break;
case SRB_FUNCTION_IO_CONTROL:
if (deviceExtension->CurrentSrb) {
DebugPrint((1,
"AtapiStartIo: Already have a request!\n"));
Srb->SrbStatus = SRB_STATUS_BUSY;
IdePortNotification(IdeRequestComplete,
deviceExtension,
Srb);
return FALSE;
}
//
// Indicate that a request is active on the controller.
//
deviceExtension->CurrentSrb = Srb;
if (strlen("SCSIDISK") != RtlCompareMemory(((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature,"SCSIDISK",strlen("SCSIDISK"))) {
DebugPrint((1,
"AtapiStartIo: IoControl signature incorrect. Send %s, expected %s\n",
((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature,
"SCSIDISK"));
status = SRB_STATUS_INVALID_REQUEST;
break;
}
switch (((PSRB_IO_CONTROL)(Srb->DataBuffer))->ControlCode) {
case IOCTL_SCSI_MINIPORT_SMART_VERSION: {
PGETVERSIONINPARAMS versionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
UCHAR deviceNumber;
UCHAR channelNo;
//
// Version and revision per SMART 1.03
//
versionParameters->bVersion = 1;
versionParameters->bRevision = 1;
versionParameters->bReserved = 0;
//
// Indicate that support for IDE IDENTIFY, ATAPI IDENTIFY and SMART commands.
//
versionParameters->fCapabilities = (CAP_ATA_ID_CMD | CAP_ATAPI_ID_CMD | CAP_SMART_CMD);
//
// This is done because of how the IOCTL_SCSI_MINIPORT
// determines 'targetid's'. Disk.sys places the real target id value
// in the DeviceMap field. Once we do some parameter checking, the value passed
// back to the application will be determined.
//
//
// HACK: atapi doesn't have the channel number. So it uses the hack below.
// this should work on non native mode IDE controllers
//
channelNo = (deviceExtension->PrimaryAddress)? 0:1;
//
// the bIDEDeviceMap is a bit map, with the bits defined as follows
// bit 0 - IDE drive as master on Primary channel
// bit 1 - IDE drive as slave on Primary channel
// bit 2 - IDE drive as master on Secondary channel
// bit 3 - IDE drive as slave on Secondary Channel
// bit 4 - ATAPI drive as master on Primary Channle
// bit 5 - ATAPI drive as slave on Primary Channle
// bit 6 - ATAPI drive as master on secondary Channle
// bit 7 - ATAPI drive as slave on secondary Channle
//
// since we have an FDO per channel, we can only fill in the fields
// pertinent to this channel.
//
versionParameters->bIDEDeviceMap = 0;
//
// Master device
//
deviceNumber = 0;
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) {
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_ATAPI_DEVICE) {
deviceNumber += channelNo*2 + 4;
} else {
deviceNumber += channelNo*2;
}
versionParameters->bIDEDeviceMap |= (1 << deviceNumber);
}
//
// slave device
//
deviceNumber = 1;
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_DEVICE_PRESENT) {
if (deviceExtension->DeviceFlags[deviceNumber] & DFLAGS_ATAPI_DEVICE) {
deviceNumber += channelNo*2 + 4;
} else {
deviceNumber += channelNo*2;
}
versionParameters->bIDEDeviceMap |= (1 << deviceNumber);
}
status = SRB_STATUS_SUCCESS;
break;
}
case IOCTL_SCSI_MINIPORT_IDENTIFY: {
PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
SENDCMDINPARAMS cmdInParameters = *(PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
ULONG i;
UCHAR targetId;
if (cmdInParameters.irDriveRegs.bCommandReg == ID_CMD) {
//
// Extract the target.
//
targetId = cmdInParameters.bDriveNumber;
if (!(deviceExtension->DeviceFlags[targetId] & DFLAGS_DEVICE_PRESENT) ||
(deviceExtension->DeviceFlags[targetId] & DFLAGS_ATAPI_DEVICE)) {
status = SRB_STATUS_SELECTION_TIMEOUT;
break;
}
//
// Zero the output buffer
//
for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1); i++) {
((PUCHAR)cmdOutParameters)[i] = 0;
}
//
// Build status block.
//
cmdOutParameters->cBufferSize = IDENTIFY_BUFFER_SIZE;
cmdOutParameters->DriverStatus.bDriverError = 0;
cmdOutParameters->DriverStatus.bIDEError = 0;
//
// Extract the identify data from the device extension.
//
RtlMoveMemory (cmdOutParameters->bBuffer, &deviceExtension->IdentifyData[targetId], IDENTIFY_DATA_SIZE);
status = SRB_STATUS_SUCCESS;
} else {
status = SRB_STATUS_INVALID_REQUEST;
}
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:
case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE:
#ifdef ENABLE_SMARTLOG_SUPPORT
case IOCTL_SCSI_MINIPORT_READ_SMART_LOG:
case IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG:
#endif
status = IdeSendSmartCommand(HwDeviceExtension,Srb);
break;
default :
status = SRB_STATUS_INVALID_REQUEST;
break;
}
break;
default:
//
// Indicate unsupported command.
//
status = SRB_STATUS_INVALID_REQUEST;
break;
} // end switch
//
// Check if command complete.
//
if (status != SRB_STATUS_PENDING) {
DebugPrint((2,
"AtapiStartIo: Srb %x complete with status %x\n",
Srb,
status));
//
// Clear current SRB.
//
deviceExtension->CurrentSrb = NULL;
//
// Set status in SRB.
//
Srb->SrbStatus = (UCHAR)status;
//
// Indicate command complete.
//
IdePortNotification(IdeRequestComplete,
deviceExtension,
Srb);
//
// Indicate ready for next request.
//
IdePortNotification(IdeNextRequest,
deviceExtension,
NULL);
}
return TRUE;
} // end AtapiStartIo()
BOOLEAN
AtapiSyncResetController(
IN PVOID HwDeviceExtension,
IN ULONG PathId
)
{
ULONG callAgain = 0;
BOOLEAN result;
do {
result = AtapiResetController(
HwDeviceExtension,
PathId,
&callAgain
);
} while (callAgain);
return result;
}
NTSTATUS
IdeHardReset (
PIDE_REGISTERS_1 BaseIoAddress1,
PIDE_REGISTERS_2 BaseIoAddress2,
BOOLEAN InterruptOff,
BOOLEAN Sync
)
{
UCHAR resetByte;
DebugPrint((1,
"IdeHardReset: Resetting controller.\n"));
//
// Kingston DP-ATA/20 pcmcia flash card
//
// if we don't make sure we select master device,
// later when we check for busy status, we will
// get the non-existing slave status
//
IdePortOutPortByte (BaseIoAddress1->DriveSelect, 0xA0);
IdePortOutPortByte (BaseIoAddress2->DeviceControl, IDE_DC_RESET_CONTROLLER | IDE_DC_DISABLE_INTERRUPTS);
//
// ATA-2 spec requires a minimum of 5 microsec stall here
//
KeStallExecutionProcessor (10);
if (InterruptOff) {
resetByte = IDE_DC_DISABLE_INTERRUPTS;
} else {
resetByte = IDE_DC_REENABLE_CONTROLLER;
}
IdePortOutPortByte (BaseIoAddress2->DeviceControl, resetByte);
//
// ATA-2 spec requires a minimum of 400 ns stall here
//
KeStallExecutionProcessor (1);
if (Sync) {
UCHAR deviceSelect;
UCHAR status;
ULONG sec;
ULONG i;
UCHAR statusByte;
WaitOnBusyUntil(BaseIoAddress1, statusByte, 500);
IdePortOutPortByte (BaseIoAddress1->DriveSelect, 0xa0);
deviceSelect = IdePortInPortByte(BaseIoAddress1->DriveSelect);
if (deviceSelect != 0xa0) {
//
// slave only channel
//
KeStallExecutionProcessor(1000);
IdePortOutPortByte (BaseIoAddress1->DriveSelect, 0xb0);
}
//
// ATA-2 spec allows a maximum of 31s for both master and slave device to come back from reset
//
for (sec=0; sec<31; sec++) {
/**/
/* one second loop */
/**/
for (i=0; i<2500; i++) {
GetStatus(BaseIoAddress1, status);
if (status & IDE_STATUS_BUSY) {
KeStallExecutionProcessor(400);
continue;
} else {
break;
}
}
if (status == 0xff) {
break;
} else if (status & IDE_STATUS_BUSY) {
DebugPrint ((0, "ATAPI: IdeHardReset WaitOnBusy failed. status = 0x%x\n", (ULONG) (status)));
} else {
break;
}
}
if (status & IDE_STATUS_BUSY) {
DebugPrint ((0, "ATAPI: IdeHardReset WaitOnBusy failed. status = 0x%x\n", (ULONG) (status)));
return STATUS_UNSUCCESSFUL;
} else {
return STATUS_SUCCESS;
}
} else {
return STATUS_SUCCESS;
}
}
VOID
AtapiTaskRegisterSnapshot (
IN PIDE_REGISTERS_1 CmdRegBase,
IN OUT PIDEREGS IdeReg
)
{
ASSERT(IdeReg);
IdeReg->bFeaturesReg = IdePortInPortByte(CmdRegBase->Error);
IdeReg->bSectorCountReg = IdePortInPortByte(CmdRegBase->BlockCount);
IdeReg->bSectorNumberReg = IdePortInPortByte(CmdRegBase->BlockNumber);
IdeReg->bCylLowReg = IdePortInPortByte(CmdRegBase->CylinderLow);
IdeReg->bCylHighReg = IdePortInPortByte(CmdRegBase->CylinderHigh);
IdeReg->bDriveHeadReg = IdePortInPortByte(CmdRegBase->DriveSelect);
IdeReg->bCommandReg = IdePortInPortByte(CmdRegBase->Command);
return;
} // AtapiTaskFileSnapshot
BOOLEAN
GetAtapiIdentifyQuick (
IN PIDE_REGISTERS_1 BaseIoAddress1,
IN PIDE_REGISTERS_2 BaseIoAddress2,
IN ULONG DeviceNumber,
OUT PIDENTIFY_DATA IdentifyData
)
{
UCHAR statusByte;
ULONG i;
SelectIdeDevice(BaseIoAddress1, DeviceNumber, 0);
GetStatus(BaseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_BUSY) {
return FALSE;
}
IdePortOutPortByte(BaseIoAddress1->Command, IDE_COMMAND_ATAPI_IDENTIFY);
WaitOnBusyUntil(BaseIoAddress1, statusByte, 500);
if ((statusByte & IDE_STATUS_BUSY) ||
(statusByte & IDE_STATUS_ERROR)) {
return FALSE;
}
if (statusByte & IDE_STATUS_DRQ) {
ReadBuffer(BaseIoAddress1,
(PUSHORT)IdentifyData,
sizeof (IDENTIFY_DATA) / 2);
}
GetStatus(BaseIoAddress1, statusByte);
//
// pull out any remaining bytes and throw away.
//
i=0;
while ((statusByte & IDE_STATUS_DRQ) && (i < 100)) {
READ_PORT_USHORT(BaseIoAddress1->Data);
GetStatus(BaseIoAddress1, statusByte);
KeStallExecutionProcessor(50);
i++;
}
return TRUE;
}