/*++
Copyright (c) 1993-6 Microsoft Corporation
Module Name:
atapi.c
Abstract:
This is the miniport driver for ATAPI 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 "miniport.h"
#include "atapi.h" // includes scsi.h
#include "ntdddisk.h"
#include "ntddscsi.h"
#include "intel.h"
//
// Logical unit extension
//
typedef struct _HW_LU_EXTENSION {
ULONG Reserved;
} HW_LU_EXTENSION, *PHW_LU_EXTENSION;
//
// control DMA detection
//
ULONG AtapiPlaySafe = 0;
//
// PCI IDE Controller List
//
CONTROLLER_PARAMETERS
PciControllerParameters[] = {
{ PCIBus,
"8086", // Intel
4,
"7111", // PIIX4 82371
4,
2, // NumberOfIdeBus
FALSE, // Dual FIFO
NULL,
IntelIsChannelEnabled
},
{ PCIBus,
"8086", // Intel
4,
"7010", // PIIX3 82371
4,
2, // NumberOfIdeBus
FALSE, // Dual FIFO
NULL,
IntelIsChannelEnabled
},
{ PCIBus,
"8086", // Intel
4,
"1230", // PIIX 82371
4,
2, // NumberOfIdeBus
FALSE, // Dual FIFO
NULL,
IntelIsChannelEnabled
},
{ PCIBus,
"1095", // CMD
4,
"0646", // 646
4,
2, // NumberOfIdeBus
FALSE, // Dual FIFO
NULL,
ChannelIsAlwaysEnabled
},
{ PCIBus,
"10b9", // ALi (Acer)
4,
"5219", // 5219
4,
2, // NumberOfIdeBus
FALSE, // Dual FIFO
NULL,
ChannelIsAlwaysEnabled
},
{ PCIBus,
"1039", // SiS
4,
"5513", // 5513
4,
2, // NumberOfIdeBus
FALSE, // Dual FIFO
NULL,
ChannelIsAlwaysEnabled
},
{ PCIBus,
"0e11", // Compaq
4,
"ae33", //
4,
2, // NumberOfIdeBus
FALSE, // Dual FIFO
NULL,
ChannelIsAlwaysEnabled
},
{ PCIBus,
"10ad", // WinBond
4,
"0105", // 105
4,
2, // NumberOfIdeBus
FALSE, // Dual FIFO
NULL,
ChannelIsAlwaysEnabled
},
// Broken PCI controllers
{ PCIBus,
"1095", // CMD
4,
"0640", // 640
4,
2, // NumberOfIdeBus
TRUE, // Single FIFO
NULL,
ChannelIsAlwaysEnabled
},
{ PCIBus,
"1039", // SiS
4,
"0601", // ????
4,
2, // NumberOfIdeBus
TRUE, // Single FIFO
NULL,
ChannelIsAlwaysEnabled
}
};
#define NUMBER_OF_PCI_CONTROLLER (sizeof(PciControllerParameters) / sizeof(CONTROLLER_PARAMETERS))
PSCSI_REQUEST_BLOCK
BuildMechanismStatusSrb (
IN PVOID HwDeviceExtension,
IN ULONG PathId,
IN ULONG TargetId
);
PSCSI_REQUEST_BLOCK
BuildRequestSenseSrb (
IN PVOID HwDeviceExtension,
IN ULONG PathId,
IN ULONG TargetId
);
VOID
AtapiHwInitializeChanger (
IN PVOID HwDeviceExtension,
IN ULONG TargetId,
IN PMECHANICAL_STATUS_INFORMATION_HEADER MechanismStatus
);
ULONG
AtapiSendCommand(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
VOID
AtapiZeroMemory(
IN PCHAR Buffer,
IN ULONG Count
);
VOID
AtapiHexToString (
ULONG Value,
PCHAR *Buffer
);
LONG
AtapiStringCmp (
PCHAR FirstStr,
PCHAR SecondStr,
ULONG Count
);
BOOLEAN
AtapiInterrupt(
IN PVOID HwDeviceExtension
);
BOOLEAN
AtapiHwInitialize(
IN PVOID HwDeviceExtension
);
ULONG
IdeBuildSenseBuffer(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
VOID
IdeMediaStatus(
IN BOOLEAN EnableMSN,
IN PVOID HwDeviceExtension,
IN ULONG Channel
);
VOID
DeviceSpecificInitialize(
IN PVOID HwDeviceExtension
);
BOOLEAN
PrepareForBusMastering(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
BOOLEAN
EnableBusMastering(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
VOID
SetBusMasterDetectionLevel (
IN PVOID HwDeviceExtension,
IN PCHAR userArgumentString
);
BOOLEAN
AtapiDeviceDMACapable (
IN PVOID HwDeviceExtension,
IN ULONG deviceNumber
);
#if defined (xDBG)
// Need to link to nt kernel
void KeQueryTickCount(PLARGE_INTEGER c);
LONGLONG lastTickCount = 0;
#define DebugPrintTickCount() _DebugPrintTickCount (__LINE__)
//
// for performance tuning
//
void _DebugPrintTickCount (ULONG lineNumber)
{
LARGE_INTEGER tickCount;
KeQueryTickCount(&tickCount);
DebugPrint ((1, "Line %u: CurrentTick = %u (%u ticks since last check)\n", lineNumber, tickCount.LowPart, (ULONG) (tickCount.QuadPart - lastTickCount)));
lastTickCount = tickCount.QuadPart;
}
#else
#define DebugPrintTickCount()
#endif //DBG
BOOLEAN
IssueIdentify(
IN PVOID HwDeviceExtension,
IN ULONG DeviceNumber,
IN ULONG Channel,
IN UCHAR Command,
IN BOOLEAN InterruptOff
)
/*++
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;
PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Channel] ;
PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel];
ULONG waitCount = 20000;
ULONG i,j;
UCHAR statusByte;
UCHAR signatureLow,
signatureHigh;
IDENTIFY_DATA fullIdentifyData;
DebugPrintTickCount();
//
// Select device 0 or 1.
//
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)((DeviceNumber << 4) | 0xA0));
//
// Check that the status register makes sense.
//
GetBaseStatus(baseIoAddress1, statusByte);
if (Command == IDE_COMMAND_IDENTIFY) {
//
// Mask status byte ERROR bits.
//
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.
//
AtapiSoftReset(baseIoAddress1,DeviceNumber, InterruptOff);
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)((DeviceNumber << 4) | 0xA0));
WaitOnBusy(baseIoAddress1,statusByte);
signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow);
signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
//
// Device is Atapi.
//
DebugPrintTickCount();
return FALSE;
}
DebugPrint((1,
"IssueIdentify: Resetting controller.\n"));
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl,IDE_DC_RESET_CONTROLLER | IDE_DC_DISABLE_INTERRUPTS);
ScsiPortStallExecution(500 * 1000);
if (InterruptOff) {
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_DISABLE_INTERRUPTS);
} else {
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_REENABLE_CONTROLLER);
}
// We really should wait up to 31 seconds
// The ATA spec. allows device 0 to come back from BUSY in 31 seconds!
// (30 seconds for device 1)
do {
//
// Wait for Busy to drop.
//
ScsiPortStallExecution(100);
GetStatus(baseIoAddress1, statusByte);
} while ((statusByte & IDE_STATUS_BUSY) && waitCount--);
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)((DeviceNumber << 4) | 0xA0));
//
// Another check for signature, to deal with one model Atapi that doesn't assert signature after
// a soft reset.
//
signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow);
signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
//
// Device is Atapi.
//
DebugPrintTickCount();
return FALSE;
}
statusByte &= ~IDE_STATUS_INDEX;
if (statusByte != IDE_STATUS_IDLE) {
//
// Give up on this.
//
DebugPrintTickCount();
return FALSE;
}
}
} else {
DebugPrint((1,
"IssueIdentify: Checking for ATAPI. Status (%x)\n",
statusByte));
}
//
// Load CylinderHigh and CylinderLow with number bytes to transfer.
//
ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh, (0x200 >> 8));
ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow, (0x200 & 0xFF));
for (j = 0; j < 2; j++) {
//
// Send IDENTIFY command.
//
WaitOnBusy(baseIoAddress1,statusByte);
ScsiPortWritePortUchar(&baseIoAddress1->Command, Command);
WaitOnBusy(baseIoAddress1,statusByte);
//
// Wait for DRQ.
//
for (i = 0; i < 4; i++) {
WaitForDrq(baseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_DRQ) {
//
// Read status to acknowledge any interrupts generated.
//
GetBaseStatus(baseIoAddress1, statusByte);
//
// One last check for Atapi.
//
signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow);
signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
//
// Device is Atapi.
//
DebugPrintTickCount();
return FALSE;
}
break;
}
if (Command == IDE_COMMAND_IDENTIFY) {
//
// Check the signature. If DRQ didn't come up it's likely Atapi.
//
signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow);
signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
//
// Device is Atapi.
//
DebugPrintTickCount();
return FALSE;
}
}
WaitOnBusy(baseIoAddress1,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,
ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1)));
AtapiSoftReset(baseIoAddress1, DeviceNumber, InterruptOff);
GetStatus(baseIoAddress1,statusByte);
DebugPrint((1,
"IssueIdentify: Status after soft reset (%x)\n",
statusByte));
} else {
break;
}
}
//
// Check for error on really stupid master devices that assert random
// patterns of bits in the status register at the slave address.
//
if ((Command == IDE_COMMAND_IDENTIFY) && (statusByte & IDE_STATUS_ERROR)) {
DebugPrintTickCount();
return FALSE;
}
DebugPrint((1,
"IssueIdentify: Status before read words %x\n",
statusByte));
//
// Suck out 256 words. After waiting for one model that asserts busy
// after receiving the Packet Identify command.
//
WaitOnBusy(baseIoAddress1,statusByte);
if (!(statusByte & IDE_STATUS_DRQ)) {
DebugPrintTickCount();
return FALSE;
}
ReadBuffer(baseIoAddress1,
(PUSHORT)&fullIdentifyData,
sizeof (fullIdentifyData) / 2);
//
// Check out a few capabilities / limitations of the device.
//
if (fullIdentifyData.SpecialFunctionsEnabled & 1) {
//
// Determine if this drive supports the MSN functions.
//
DebugPrint((2,"IssueIdentify: Marking drive %d as removable. SFE = %d\n",
Channel * 2 + DeviceNumber,
fullIdentifyData.SpecialFunctionsEnabled));
deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_REMOVABLE_DRIVE;
}
if (fullIdentifyData.MaximumBlockTransfer) {
//
// Determine max. block transfer for this device.
//
deviceExtension->MaximumBlockXfer[(Channel * 2) + DeviceNumber] =
(UCHAR)(fullIdentifyData.MaximumBlockTransfer & 0xFF);
}
ScsiPortMoveMemory(&deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber],&fullIdentifyData,sizeof(IDENTIFY_DATA2));
if (deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber].GeneralConfiguration & 0x20 &&
Command != IDE_COMMAND_IDENTIFY) {
//
// This device interrupts with the assertion of DRQ after receiving
// Atapi Packet Command
//
deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_INT_DRQ;
DebugPrint((2,
"IssueIdentify: Device interrupts on assertion of DRQ.\n"));
} else {
DebugPrint((2,
"IssueIdentify: Device does not interrupt on assertion of DRQ.\n"));
}
if (((deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber].GeneralConfiguration & 0xF00) == 0x100) &&
Command != IDE_COMMAND_IDENTIFY) {
//
// This is a tape.
//
deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_TAPE_DEVICE;
DebugPrint((2,
"IssueIdentify: Device is a tape drive.\n"));
} else {
DebugPrint((2,
"IssueIdentify: Device is not a tape drive.\n"));
}
//
// Work around for some IDE and one model Atapi that will present more than
// 256 bytes for the Identify data.
//
WaitOnBusy(baseIoAddress1,statusByte);
for (i = 0; i < 0x10000; i++) {
GetStatus(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_DRQ) {
//
// Suck out any remaining bytes and throw away.
//
ScsiPortReadPortUshort(&baseIoAddress1->Data);
} else {
break;
}
}
DebugPrint((3,
"IssueIdentify: Status after read words (%x)\n",
statusByte));
DebugPrintTickCount();
return TRUE;
} // end IssueIdentify()
BOOLEAN
SetDriveParameters(
IN PVOID HwDeviceExtension,
IN ULONG DeviceNumber,
IN ULONG Channel
)
/*++
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[Channel];
PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel];
PIDENTIFY_DATA2 identifyData = &deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber];
ULONG i;
UCHAR statusByte;
DebugPrint((1,
"SetDriveParameters: Number of heads %x\n",
identifyData->NumberOfHeads));
DebugPrint((1,
"SetDriveParameters: Sectors per track %x\n",
identifyData->SectorsPerTrack));
//
// Set up registers for SET PARAMETER command.
//
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)(((DeviceNumber << 4) | 0xA0) | (identifyData->NumberOfHeads - 1)));
ScsiPortWritePortUchar(&baseIoAddress1->BlockCount,
(UCHAR)identifyData->SectorsPerTrack);
//
// Send SET PARAMETER command.
//
ScsiPortWritePortUchar(&baseIoAddress1->Command,
IDE_COMMAND_SET_DRIVE_PARAMETERS);
//
// Wait for up to 30 milliseconds for ERROR or command complete.
//
for (i=0; i<30 * 1000; i++) {
UCHAR errorByte;
GetStatus(baseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_ERROR) {
errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1);
DebugPrint((1,
"SetDriveParameters: Error bit set. Status %x, error %x\n",
errorByte,
statusByte));
return FALSE;
} else if ((statusByte & ~IDE_STATUS_INDEX ) == IDE_STATUS_IDLE) {
break;
} else {
ScsiPortStallExecution(100);
}
}
//
// Check for timeout.
//
if (i == 30 * 1000) {
return FALSE;
} else {
return TRUE;
}
} // end SetDriveParameters()
BOOLEAN
AtapiResetController(
IN PVOID HwDeviceExtension,
IN ULONG PathId
)
/*++
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;
ULONG numberChannels = deviceExtension->NumberChannels;
PIDE_REGISTERS_1 baseIoAddress1;
PIDE_REGISTERS_2 baseIoAddress2;
BOOLEAN result = FALSE;
ULONG i,j;
UCHAR statusByte;
DebugPrint((2,"AtapiResetController: Reset IDE\n"));
//
// Check and see if we are processing an internal srb
//
if (deviceExtension->OriginalSrb) {
deviceExtension->CurrentSrb = deviceExtension->OriginalSrb;
deviceExtension->OriginalSrb = NULL;
}
//
// Check if request is in progress.
//
if (deviceExtension->CurrentSrb) {
//
// Complete outstanding request with SRB_STATUS_BUS_RESET.
//
ScsiPortCompleteRequest(deviceExtension,
deviceExtension->CurrentSrb->PathId,
deviceExtension->CurrentSrb->TargetId,
deviceExtension->CurrentSrb->Lun,
(ULONG)SRB_STATUS_BUS_RESET);
//
// Clear request tracking fields.
//
deviceExtension->CurrentSrb = NULL;
deviceExtension->WordsLeft = 0;
deviceExtension->DataBuffer = NULL;
//
// Indicate ready for next request.
//
ScsiPortNotification(NextRequest,
deviceExtension,
NULL);
}
//
// Clear DMA
//
if (deviceExtension->DMAInProgress) {
for (j = 0; j < numberChannels; j++) {
UCHAR dmaStatus;
dmaStatus = ScsiPortReadPortUchar (&deviceExtension->BusMasterPortBase[j]->Status);
ScsiPortWritePortUchar (&deviceExtension->BusMasterPortBase[j]->Command, 0); // disable BusMastering
ScsiPortWritePortUchar (&deviceExtension->BusMasterPortBase[j]->Status,
(UCHAR) (dmaStatus & (BUSMASTER_DEVICE0_DMA_OK | BUSMASTER_DEVICE1_DMA_OK))); // clear interrupt/error
}
deviceExtension->DMAInProgress = FALSE;
}
//
// Clear expecting interrupt flag.
//
deviceExtension->ExpectingInterrupt = FALSE;
deviceExtension->RDP = FALSE;
for (j = 0; j < numberChannels; j++) {
baseIoAddress1 = deviceExtension->BaseIoAddress1[j];
baseIoAddress2 = deviceExtension->BaseIoAddress2[j];
//
// Do special processing for ATAPI and IDE disk devices.
//
for (i = 0; i < 2; i++) {
//
// Check if device present.
//
if (deviceExtension->DeviceFlags[i + (j * 2)] & DFLAGS_DEVICE_PRESENT) {
//
// Check for ATAPI disk.
//
if (deviceExtension->DeviceFlags[i + (j * 2)] & DFLAGS_ATAPI_DEVICE) {
//
// Issue soft reset and issue identify.
//
GetStatus(baseIoAddress1,statusByte);
DebugPrint((1,
"AtapiResetController: Status before Atapi reset (%x).\n",
statusByte));
AtapiSoftReset(baseIoAddress1,i, FALSE);
GetStatus(baseIoAddress1,statusByte);
if (statusByte == 0x0) {
IssueIdentify(HwDeviceExtension,
i,
j,
IDE_COMMAND_ATAPI_IDENTIFY,
FALSE);
} else {
DebugPrint((1,
"AtapiResetController: Status after soft reset %x\n",
statusByte));
}
} else {
//
// Write IDE reset controller bits.
//
IdeHardReset(baseIoAddress1, baseIoAddress2,result);
if (!result) {
return FALSE;
}
//
// Set disk geometry parameters.
//
if (!SetDriveParameters(HwDeviceExtension,
i,
j)) {
DebugPrint((1,
"AtapiResetController: SetDriveParameters failed\n"));
}
// re-enable MSN
IdeMediaStatus(TRUE, HwDeviceExtension, j * numberChannels + i);
}
}
}
}
//
// Call the HwInitialize routine to setup multi-block.
//
AtapiHwInitialize(HwDeviceExtension);
return TRUE;
} // 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[Srb->TargetId >> 1];
PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1];
ULONG i;
UCHAR errorByte;
UCHAR srbStatus;
UCHAR scsiStatus;
//
// Read the error register.
//
errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1);
DebugPrint((1,
"MapError: Error register is %x\n",
errorByte));
if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE) {
switch (errorByte >> 4) {
case SCSI_SENSE_NO_SENSE:
DebugPrint((1,
"ATAPI: No sense information\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_RECOVERED_ERROR:
DebugPrint((1,
"ATAPI: Recovered error\n"));
scsiStatus = 0;
srbStatus = SRB_STATUS_SUCCESS;
break;
case SCSI_SENSE_NOT_READY:
DebugPrint((1,
"ATAPI: Device not ready\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_MEDIUM_ERROR:
DebugPrint((1,
"ATAPI: Media error\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_HARDWARE_ERROR:
DebugPrint((1,
"ATAPI: Hardware error\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_ILLEGAL_REQUEST:
DebugPrint((1,
"ATAPI: Illegal request\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_UNIT_ATTENTION:
DebugPrint((1,
"ATAPI: Unit attention\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_DATA_PROTECT:
DebugPrint((1,
"ATAPI: Data protect\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_BLANK_CHECK:
DebugPrint((1,
"ATAPI: Blank check\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
case SCSI_SENSE_ABORTED_COMMAND:
DebugPrint((1,
"Atapi: Command Aborted\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
break;
default:
DebugPrint((1,
"ATAPI: Invalid sense information\n"));
scsiStatus = 0;
srbStatus = SRB_STATUS_ERROR;
break;
}
} else {
scsiStatus = 0;
//
// Save errorByte,to be used by SCSIOP_REQUEST_SENSE.
//
deviceExtension->ReturningMediaStatus = errorByte;
if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) {
DebugPrint((1,
"IDE: Media change\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
} else if (errorByte & IDE_ERROR_COMMAND_ABORTED) {
DebugPrint((1,
"IDE: Command abort\n"));
srbStatus = SRB_STATUS_ABORTED;
scsiStatus = SCSISTAT_CHECK_CONDITION;
if (Srb->SenseInfoBuffer) {
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND;
senseBuffer->AdditionalSenseCode = 0;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
deviceExtension->ErrorCount++;
} else if (errorByte & IDE_ERROR_END_OF_MEDIA) {
DebugPrint((1,
"IDE: End of media\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED)){
deviceExtension->ErrorCount++;
}
} else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) {
DebugPrint((1,
"IDE: Illegal length\n"));
srbStatus = SRB_STATUS_INVALID_REQUEST;
} else if (errorByte & IDE_ERROR_BAD_BLOCK) {
DebugPrint((1,
"IDE: Bad block\n"));
srbStatus = SRB_STATUS_ERROR;
scsiStatus = SCSISTAT_CHECK_CONDITION;
if (Srb->SenseInfoBuffer) {
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR;
senseBuffer->AdditionalSenseCode = 0;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
} else if (errorByte & IDE_ERROR_ID_NOT_FOUND) {
DebugPrint((1,
"IDE: Id not found\n"));
srbStatus = SRB_STATUS_ERROR;
scsiStatus = SCSISTAT_CHECK_CONDITION;
if (Srb->SenseInfoBuffer) {
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR;
senseBuffer->AdditionalSenseCode = 0;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
deviceExtension->ErrorCount++;
} else if (errorByte & IDE_ERROR_MEDIA_CHANGE) {
DebugPrint((1,
"IDE: Media change\n"));
scsiStatus = SCSISTAT_CHECK_CONDITION;
srbStatus = SRB_STATUS_ERROR;
if (Srb->SenseInfoBuffer) {
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION;
senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
} else if (errorByte & IDE_ERROR_DATA_ERROR) {
DebugPrint((1,
"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) {
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
senseBuffer->ErrorCode = 0x70;
senseBuffer->Valid = 1;
senseBuffer->AdditionalSenseLength = 0xb;
senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR;
senseBuffer->AdditionalSenseCode = 0;
senseBuffer->AdditionalSenseCodeQualifier = 0;
srbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
}
if ((deviceExtension->ErrorCount >= MAX_ERRORS) &&
(!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_USE_DMA))) {
deviceExtension->DWordIO = FALSE;
deviceExtension->MaximumBlockXfer[Srb->TargetId] = 0;
DebugPrint((1,
"MapError: Disabling 32-bit PIO and Multi-sector IOs\n"));
//
// Log the error.
//
ScsiPortLogError( HwDeviceExtension,
Srb,
Srb->PathId,
Srb->TargetId,
Srb->Lun,
SP_BAD_FW_WARNING,
4);
//
// Reprogram to not use Multi-sector.
//
for (i = 0; i < 4; i++) {
UCHAR statusByte;
if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT &&
!(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) {
//
// Select the device.
//
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)(((i & 0x1) << 4) | 0xA0));
//
// Setup sector count to reflect the # of blocks.
//
ScsiPortWritePortUchar(&baseIoAddress1->BlockCount,
0);
//
// Issue the command.
//
ScsiPortWritePortUchar(&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 = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1);
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;
}
deviceExtension->DeviceParameters[i].IdeReadCommand = IDE_COMMAND_READ;
deviceExtension->DeviceParameters[i].IdeWriteCommand = IDE_COMMAND_WRITE;
deviceExtension->DeviceParameters[i].MaxWordPerInterrupt = 256;
deviceExtension->MaximumBlockXfer[i] = 0;
}
}
}
}
//
// Set SCSI status to indicate a check condition.
//
Srb->ScsiStatus = scsiStatus;
return srbStatus;
} // end MapError()
BOOLEAN
AtapiHwInitialize(
IN PVOID HwDeviceExtension
)
/*++
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;
for (i = 0; i < 4; i++) {
if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) {
if (!(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) {
//
// Enable media status notification
//
baseIoAddress = deviceExtension->BaseIoAddress1[i >> 1];
IdeMediaStatus(TRUE,HwDeviceExtension,i);
//
// If supported, setup Multi-block transfers.
//
if (deviceExtension->MaximumBlockXfer[i]) {
//
// Select the device.
//
ScsiPortWritePortUchar(&baseIoAddress->DriveSelect,
(UCHAR)(((i & 0x1) << 4) | 0xA0));
//
// Setup sector count to reflect the # of blocks.
//
ScsiPortWritePortUchar(&baseIoAddress->BlockCount,
deviceExtension->MaximumBlockXfer[i]);
//
// Issue the command.
//
ScsiPortWritePortUchar(&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 = ScsiPortReadPortUchar((PUCHAR)baseIoAddress + 1);
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]));
}
}
} else if (!(deviceExtension->DeviceFlags[i] & DFLAGS_CHANGER_INITED)){
ULONG j;
BOOLEAN isSanyo = FALSE;
UCHAR vendorId[26];
//
// Attempt to identify any special-case devices - psuedo-atapi changers, atapi changers, etc.
//
for (j = 0; j < 13; j += 2) {
//
// Build a buffer based on the identify data.
//
vendorId[j] = ((PUCHAR)deviceExtension->IdentifyData[i].ModelNumber)[j + 1];
vendorId[j+1] = ((PUCHAR)deviceExtension->IdentifyData[i].ModelNumber)[j];
}
if (!AtapiStringCmp (vendorId, "CD-ROM CDR", 11)) {
//
// Inquiry string for older model had a '-', newer is '_'
//
if (vendorId[12] == 'C') {
//
// Torisan changer. Set the bit. This will be used in several places
// acting like 1) a multi-lun device and 2) building the 'special' TUR's.
//
deviceExtension->DeviceFlags[i] |= (DFLAGS_CHANGER_INITED | DFLAGS_SANYO_ATAPI_CHANGER);
deviceExtension->DiscsPresent[i] = 3;
isSanyo = TRUE;
}
}
}
//
// We need to get our device ready for action before
// returning from this function
//
// According to the atapi spec 2.5 or 2.6, an atapi device
// clears its status BSY bit when it is ready for atapi commands.
// However, some devices (Panasonic SQ-TC500N) are still
// not ready even when the status BSY is clear. They don't react
// to atapi commands.
//
// Since there is really no other indication that tells us
// the drive is really ready for action. We are going to check BSY
// is clear and then just wait for an arbitrary amount of time!
//
if (deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE) {
PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[i >> 1];
PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[i >> 1];
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.
//
ScsiPortStallExecution(100);
GetStatus(baseIoAddress1, statusByte);
waitCount--;
}
// 5000 * 100us = 500,000us = 500ms = 0.5s
waitCount = 5000;
do {
ScsiPortStallExecution(100);
} while (waitCount--);
}
}
}
return TRUE;
} // end AtapiHwInitialize()
VOID
AtapiHwInitializeChanger (
IN PVOID HwDeviceExtension,
IN ULONG TargetId,
IN PMECHANICAL_STATUS_INFORMATION_HEADER MechanismStatus)
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
if (MechanismStatus) {
deviceExtension->DiscsPresent[TargetId] = MechanismStatus->NumberAvailableSlots;
if (deviceExtension->DiscsPresent[TargetId] > 1) {
deviceExtension->DeviceFlags[TargetId] |= DFLAGS_ATAPI_CHANGER;
}
}
return;
}
BOOLEAN
FindDevices(
IN PVOID HwDeviceExtension,
IN BOOLEAN AtapiOnly,
IN ULONG Channel
)
/*++
Routine Description:
This routine is called from AtapiFindController to identify
devices attached to an IDE controller.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
AtapiOnly - Indicates that routine should return TRUE only if
an ATAPI device is attached to the controller.
Return Value:
TRUE - True if devices found.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Channel];
PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel];
BOOLEAN deviceResponded = FALSE,
skipSetParameters = FALSE;
ULONG waitCount = 10000;
ULONG deviceNumber;
ULONG i;
UCHAR signatureLow,
signatureHigh;
UCHAR statusByte;
DebugPrintTickCount();
//
// Clear expecting interrupt flag and current SRB field.
//
deviceExtension->ExpectingInterrupt = FALSE;
deviceExtension->CurrentSrb = NULL;
// We are about to talk to our devices before our interrupt handler is installed
// If our device uses sharable level sensitive interrupt, we may assert too
// many bogus interrupts
// Turn off device interrupt here
for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) {
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)((deviceNumber << 4) | 0xA0));
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_DISABLE_INTERRUPTS);
}
//
// Search for devices.
//
for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) {
//
// Select the device.
//
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)((deviceNumber << 4) | 0xA0));
//
// Check here for some SCSI adapters that incorporate IDE emulation.
//
GetStatus(baseIoAddress1, statusByte);
if (statusByte == 0xFF) {
continue;
}
DebugPrintTickCount();
AtapiSoftReset(baseIoAddress1,deviceNumber, TRUE);
DebugPrintTickCount();
WaitOnBusy(baseIoAddress1,statusByte);
DebugPrintTickCount();
signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow);
signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
//
// ATAPI signature found.
// Issue the ATAPI identify command if this
// is not for the crash dump utility.
//
atapiIssueId:
if (!deviceExtension->DriverMustPoll) {
//
// Issue ATAPI packet identify command.
//
if (IssueIdentify(HwDeviceExtension,
deviceNumber,
Channel,
IDE_COMMAND_ATAPI_IDENTIFY,
TRUE)) {
//
// Indicate ATAPI device.
//
DebugPrint((1,
"FindDevices: Device %x is ATAPI\n",
deviceNumber));
deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_ATAPI_DEVICE;
deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_DEVICE_PRESENT;
deviceResponded = TRUE;
GetStatus(baseIoAddress1, statusByte);
if (statusByte & IDE_STATUS_ERROR) {
AtapiSoftReset(baseIoAddress1, deviceNumber, TRUE);
}
} else {
//
// Indicate no working device.
//
DebugPrint((1,
"FindDevices: Device %x not responding\n",
deviceNumber));
deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] &= ~DFLAGS_DEVICE_PRESENT;
}
}
} else {
//
// Issue IDE Identify. If an Atapi device is actually present, the signature
// will be asserted, and the drive will be recognized as such.
//
if (IssueIdentify(HwDeviceExtension,
deviceNumber,
Channel,
IDE_COMMAND_IDENTIFY,
TRUE)) {
//
// IDE drive found.
//
DebugPrint((1,
"FindDevices: Device %x is IDE\n",
deviceNumber));
deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_DEVICE_PRESENT;
if (!AtapiOnly) {
deviceResponded = TRUE;
}
//
// Indicate IDE - not ATAPI device.
//
deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] &= ~DFLAGS_ATAPI_DEVICE;
} else {
//
// Look to see if an Atapi device is present.
//
AtapiSoftReset(baseIoAddress1, deviceNumber, TRUE);
WaitOnBusy(baseIoAddress1,statusByte);
signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow);
signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh);
if (signatureLow == 0x14 && signatureHigh == 0xEB) {
goto atapiIssueId;
}
}
}
#if DBG
if (deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] & DFLAGS_DEVICE_PRESENT) {
{
ULONG i;
UCHAR string[41];
for (i=0; i<8; i+=2) {
string[i] = deviceExtension->IdentifyData[Channel * MAX_CHANNEL + deviceNumber].FirmwareRevision[i + 1];
string[i + 1] = deviceExtension->IdentifyData[Channel * MAX_CHANNEL + deviceNumber].FirmwareRevision[i];
}
string[i] = 0;
DebugPrint((1, "FindDevices: firmware version: %s\n", string));
for (i=0; i<40; i+=2) {
string[i] = deviceExtension->IdentifyData[Channel * MAX_CHANNEL + deviceNumber].ModelNumber[i + 1];
string[i + 1] = deviceExtension->IdentifyData[Channel * MAX_CHANNEL + deviceNumber].ModelNumber[i];
}
string[i] = 0;
DebugPrint((1, "FindDevices: model number: %s\n", string));
}
}
#endif
}
for (i = 0; i < 2; i++) {
if ((deviceExtension->DeviceFlags[i + (Channel * 2)] & DFLAGS_DEVICE_PRESENT) &&
(!(deviceExtension->DeviceFlags[i + (Channel * 2)] & DFLAGS_ATAPI_DEVICE)) && deviceResponded) {
//
// This hideous hack is to deal with ESDI devices that return
// garbage geometry in the IDENTIFY data.
// This is ONLY for the crashdump environment as
// these are ESDI devices.
//
if (deviceExtension->IdentifyData[i].SectorsPerTrack ==
0x35 &&
deviceExtension->IdentifyData[i].NumberOfHeads ==
0x07) {
DebugPrint((1,
"FindDevices: Found nasty Compaq ESDI!\n"));
//
// Change these values to something reasonable.
//
deviceExtension->IdentifyData[i].SectorsPerTrack =
0x34;
deviceExtension->IdentifyData[i].NumberOfHeads =
0x0E;
}
if (deviceExtension->IdentifyData[i].SectorsPerTrack ==
0x35 &&
deviceExtension->IdentifyData[i].NumberOfHeads ==
0x0F) {
DebugPrint((1,
"FindDevices: Found nasty Compaq ESDI!\n"));
//
// Change these values to something reasonable.
//
deviceExtension->IdentifyData[i].SectorsPerTrack =
0x34;
deviceExtension->IdentifyData[i].NumberOfHeads =
0x0F;
}
if (deviceExtension->IdentifyData[i].SectorsPerTrack ==
0x36 &&
deviceExtension->IdentifyData[i].NumberOfHeads ==
0x07) {
DebugPrint((1,
"FindDevices: Found nasty UltraStor ESDI!\n"));
//
// Change these values to something reasonable.
//
deviceExtension->IdentifyData[i].SectorsPerTrack =
0x3F;
deviceExtension->IdentifyData[i].NumberOfHeads =
0x10;
skipSetParameters = TRUE;
}
if (!skipSetParameters) {
//
// Select the device.
//
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)((i << 4) | 0xA0));
WaitOnBusy(baseIoAddress1,statusByte);
if (statusByte & IDE_STATUS_ERROR) {
//
// Reset the device.
//
DebugPrint((2,
"FindDevices: Resetting controller before SetDriveParameters.\n"));
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl,IDE_DC_RESET_CONTROLLER | IDE_DC_DISABLE_INTERRUPTS);
ScsiPortStallExecution(500 * 1000);
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_DISABLE_INTERRUPTS);
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)((i << 4) | 0xA0));
do {
//
// Wait for Busy to drop.
//
ScsiPortStallExecution(100);
GetStatus(baseIoAddress1, statusByte);
} while ((statusByte & IDE_STATUS_BUSY) && waitCount--);
}
WaitOnBusy(baseIoAddress1,statusByte);
DebugPrint((2,
"FindDevices: Status before SetDriveParameters: (%x) (%x)\n",
statusByte,
ScsiPortReadPortUchar(&baseIoAddress1->DriveSelect)));
//
// Use the IDENTIFY data to set drive parameters.
//
if (!SetDriveParameters(HwDeviceExtension,i,Channel)) {
DebugPrint((0,
"AtapHwInitialize: Set drive parameters for device %d failed\n",
i));
//
// Don't use this device as writes could cause corruption.
//
deviceExtension->DeviceFlags[i + Channel] = 0;
continue;
}
if (deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] & DFLAGS_REMOVABLE_DRIVE) {
//
// Pick up ALL IDE removable drives that conform to Yosemite V0.2...
//
AtapiOnly = FALSE;
}
//
// Indicate that a device was found.
//
if (!AtapiOnly) {
deviceResponded = TRUE;
}
}
}
}
//
// Make sure master device is selected on exit.
//
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, 0xA0);
//
// Reset the controller. This is a feeble attempt to leave the ESDI
// controllers in a state that ATDISK driver will recognize them.
// The problem in ATDISK has to do with timings as it is not reproducible
// in debug. The reset should restore the controller to its poweron state
// and give the system enough time to settle.
//
if (!deviceResponded) {
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl,IDE_DC_RESET_CONTROLLER | IDE_DC_DISABLE_INTERRUPTS);
ScsiPortStallExecution(50 * 1000);
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl,IDE_DC_REENABLE_CONTROLLER);
}
// Turn device interrupt back on
for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) {
ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,
(UCHAR)((deviceNumber << 4) | 0xA0));
// Clear any pending interrupts
GetStatus(baseIoAddress1, statusByte);
ScsiPortWritePortUchar(&baseIoAddress2->DeviceControl, IDE_DC_REENABLE_CONTROLLER);
}