mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7983 lines
250 KiB
7983 lines
250 KiB
/*++
|
|
|
|
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);
|
|
}
|
|
|