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.
2037 lines
45 KiB
2037 lines
45 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
scsiflop.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the scsi floppy disk boot driver for the DUO Base prom.
|
|
|
|
Author:
|
|
|
|
Lluis Abello (lluis) Apr-15-93.
|
|
All code stolen from bldr\scsidisk.c by jhavens. Disk Partitions, CDROMS and
|
|
so on removed.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "fwp.h"
|
|
#include "ntdddisk.h"
|
|
#include "scsi.h"
|
|
#include "scsiboot.h"
|
|
#include "stdio.h"
|
|
#include "string.h"
|
|
#include "duobase.h"
|
|
|
|
//
|
|
// SCSI driver constants.
|
|
//
|
|
|
|
#define MAXIMUM_NUMBER_SECTORS 128 // maximum number of transfer sector
|
|
#define MAXIMUM_NUMBER_RETRIES 8 // maximum number of read/write retries
|
|
#define MAXIMUM_SECTOR_SIZE 2048 // define the maximum supported sector size
|
|
#define MODE_DATA_SIZE 192
|
|
#define HITACHI_MODE_DATA_SIZE 8
|
|
|
|
//
|
|
// Define device driver prototypes.
|
|
//
|
|
|
|
ARC_STATUS
|
|
ScsiDiskClose (
|
|
IN ULONG FileId
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskMount (
|
|
IN PCHAR MountPath,
|
|
IN MOUNT_OPERATION Operation
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskOpen (
|
|
IN PCHAR OpenPath,
|
|
IN OPEN_MODE OpenMode,
|
|
OUT PULONG FileId
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskRead (
|
|
IN ULONG FileId,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskGetReadStatus (
|
|
IN ULONG FileId
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskSeek (
|
|
IN ULONG FileId,
|
|
IN PLARGE_INTEGER Offset,
|
|
IN SEEK_MODE SeekMode
|
|
);
|
|
|
|
|
|
ARC_STATUS
|
|
ScsiDiskGetFileInformation (
|
|
IN ULONG FileId,
|
|
OUT PFILE_INFORMATION Finfo
|
|
);
|
|
|
|
NTSTATUS
|
|
ScsiDiskBootIO (
|
|
IN PMDL MdlAddress,
|
|
IN ULONG LogicalBlock,
|
|
IN PPARTITION_CONTEXT PartitionContext
|
|
);
|
|
|
|
VOID
|
|
ScsiDiskBootSetup (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
ScsiPortExecute(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
ScsiDiskStartUnit(
|
|
IN PPARTITION_CONTEXT PartitionContext
|
|
);
|
|
|
|
VOID
|
|
ScsiDiskStartUnit(
|
|
IN PPARTITION_CONTEXT PartitionContext
|
|
);
|
|
|
|
ULONG
|
|
ClassModeSense(
|
|
IN PPARTITION_CONTEXT Context,
|
|
IN PCHAR ModeSenseBuffer,
|
|
IN ULONG Length,
|
|
IN UCHAR PageMode
|
|
);
|
|
|
|
PVOID
|
|
ClassFindModePage(
|
|
IN PCHAR ModeSenseBuffer,
|
|
IN ULONG Length,
|
|
IN UCHAR PageMode
|
|
);
|
|
BOOLEAN
|
|
IsFloppyDevice(
|
|
PPARTITION_CONTEXT Context
|
|
);
|
|
|
|
PIRP
|
|
BuildReadRequest(
|
|
IN PPARTITION_CONTEXT PartitionContext,
|
|
IN PMDL Mdl,
|
|
IN ULONG LogicalBlockAddress
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Define static data.
|
|
//
|
|
|
|
BL_DEVICE_ENTRY_TABLE ScsiDiskEntryTable = {
|
|
ScsiDiskClose,
|
|
NULL,
|
|
ScsiDiskOpen,
|
|
ScsiDiskRead,
|
|
ScsiDiskGetReadStatus,
|
|
ScsiDiskSeek,
|
|
NULL,
|
|
ScsiDiskGetFileInformation,
|
|
(PARC_SET_FILE_INFO_ROUTINE)NULL
|
|
};
|
|
|
|
|
|
//
|
|
// Global poiter for buffers.
|
|
//
|
|
|
|
PREAD_CAPACITY_DATA ReadCapacityBuffer;
|
|
PUCHAR SenseInfoBuffer;
|
|
|
|
#define SECTORS_IN_LOGICAL_VOLUME 0x20
|
|
|
|
|
|
ARC_STATUS
|
|
ScsiDiskGetFileInformation (
|
|
IN ULONG FileId,
|
|
OUT PFILE_INFORMATION Finfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns information on the scsi partition.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file table index.
|
|
|
|
Finfo - Supplies a pointer to where the File Informatino is stored.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PPARTITION_CONTEXT Context;
|
|
|
|
RtlZeroMemory(Finfo, sizeof(FILE_INFORMATION));
|
|
|
|
Context = &BlFileTable[FileId].u.PartitionContext;
|
|
|
|
Finfo->StartingAddress = RtlConvertLongToLargeInteger (Context->StartingSector);
|
|
Finfo->StartingAddress = RtlLargeIntegerShiftLeft(Finfo->StartingAddress,
|
|
Context->SectorShift);
|
|
|
|
Finfo->EndingAddress = RtlLargeIntegerAdd(Finfo->StartingAddress,
|
|
Context->PartitionLength);
|
|
|
|
Finfo->Type = DiskPeripheral;
|
|
|
|
return ESUCCESS;
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
ScsiDiskClose (
|
|
IN ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function closes the file table entry specified by the file id.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file table index.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
BlFileTable[FileId].Flags.Open = 0;
|
|
return ESUCCESS;
|
|
}
|
|
|
|
ARC_STATUS
|
|
ScsiDiskOpen (
|
|
IN PCHAR OpenPath,
|
|
IN OPEN_MODE OpenMode,
|
|
OUT PULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in the file table entry. In particular the Scsi address
|
|
of the device is determined from the name. The block size of device is
|
|
queried from the target controller, and the partition information is read
|
|
from the device.
|
|
|
|
Arguments:
|
|
|
|
OpenPath - Supplies the name of the device being opened.
|
|
|
|
OpenMode - Unused.
|
|
|
|
FileId - Supplies the index to the file table entry to be initialized.
|
|
|
|
Return Value:
|
|
|
|
Retruns the arc status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Partition;
|
|
ULONG Id;
|
|
BOOLEAN IsCdRom;
|
|
BOOLEAN IsFloppy;
|
|
PPARTITION_CONTEXT Context;
|
|
|
|
Context = &BlFileTable[*FileId].u.PartitionContext;
|
|
|
|
//
|
|
// Determine the scsi port device object.
|
|
//
|
|
|
|
if (FwGetPathMnemonicKey(OpenPath, "scsi", &Id)) {
|
|
return ENODEV;
|
|
}
|
|
|
|
if (ScsiPortDeviceObject[Id] == NULL) {
|
|
return ENODEV;
|
|
}
|
|
|
|
Context->PortDeviceObject = ScsiPortDeviceObject[Id];
|
|
|
|
//
|
|
// Get the logical unit, path Id and target id from the name.
|
|
// If it's not a floppy return ENODEV.
|
|
// NOTE: FwGetPathMnemonicKey returns 0 for success.
|
|
//
|
|
|
|
if (FwGetPathMnemonicKey(OpenPath, "fdisk", &Id)) {
|
|
return ENODEV;
|
|
}
|
|
|
|
Context->DiskId = Id;
|
|
|
|
if (FwGetPathMnemonicKey(OpenPath, "disk", &Id)) {
|
|
return ENODEV;
|
|
}
|
|
|
|
Context->PathId = Id / SCSI_MAXIMUM_TARGETS_PER_BUS;
|
|
|
|
Context->TargetId = Id % SCSI_MAXIMUM_TARGETS_PER_BUS;
|
|
|
|
//
|
|
// Read the capacity of the disk to determine the block size.
|
|
//
|
|
|
|
if (ReadDriveCapacity(Context)) {
|
|
return ENODEV;
|
|
}
|
|
return ESUCCESS;
|
|
}
|
|
|
|
ARC_STATUS
|
|
ScsiDiskRead (
|
|
IN ULONG FileId,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reads data from the hard disk starting at the position
|
|
specified in the file table.
|
|
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file table index.
|
|
|
|
Buffer - Supplies a poiner to the buffer that receives the data
|
|
read.
|
|
|
|
Length - Supplies the number of bytes to be read.
|
|
|
|
Count - Supplies a pointer to a variable that receives the number of
|
|
bytes actually read.
|
|
|
|
Return Value:
|
|
|
|
The read operation is performed and the read completion status is
|
|
returned.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
|
|
ARC_STATUS ArcStatus;
|
|
ULONG FrameNumber;
|
|
ULONG Index;
|
|
ULONG Limit;
|
|
PMDL MdlAddress;
|
|
UCHAR MdlBuffer[sizeof(MDL) + ((64 / 4) + 1) * sizeof(ULONG)];
|
|
NTSTATUS NtStatus;
|
|
ULONG NumberOfPages;
|
|
PULONG PageFrame;
|
|
ULONG Offset;
|
|
LARGE_INTEGER Position;
|
|
LARGE_INTEGER LogicalBlock;
|
|
CHAR TempBuffer[MAXIMUM_SECTOR_SIZE + 128];
|
|
PCHAR TempPointer;
|
|
PIO_SCSI_CAPABILITIES PortCapabilities;
|
|
ULONG adapterLimit;
|
|
ULONG SectorSize;
|
|
ULONG TransferCount;
|
|
ULONG BytesToTransfer;
|
|
|
|
//
|
|
// If the requested size of the transfer is zero return ESUCCESS
|
|
//
|
|
|
|
if (Length==0) {
|
|
return ESUCCESS;
|
|
}
|
|
|
|
//
|
|
// Compute a Dcache aligned pointer into the temporary buffer.
|
|
//
|
|
|
|
TempPointer = (PVOID)((ULONG)(TempBuffer +
|
|
KeGetDcacheFillSize() - 1) & ~(KeGetDcacheFillSize() - 1));
|
|
|
|
|
|
//
|
|
// Calculate the actual sector size.
|
|
//
|
|
|
|
SectorSize = 1 << BlFileTable[FileId].u.PartitionContext.SectorShift;
|
|
|
|
//
|
|
// If the current position is not at a sector boundary, then read the
|
|
// first sector separately and copy the data.
|
|
//
|
|
|
|
Offset = BlFileTable[FileId].Position.LowPart & (SectorSize - 1);
|
|
*Count = 0;
|
|
if (Offset != 0) {
|
|
|
|
Position = BlFileTable[FileId].Position;
|
|
BlFileTable[FileId].Position = RtlLargeIntegerSubtract(Position,
|
|
RtlConvertLongToLargeInteger(Offset));
|
|
ArcStatus = ScsiDiskRead(FileId, TempPointer, SectorSize, &TransferCount);
|
|
if (ArcStatus != ESUCCESS) {
|
|
BlFileTable[FileId].Position = Position;
|
|
return ArcStatus;
|
|
}
|
|
|
|
//
|
|
// Copy the data to the specified buffer.
|
|
//
|
|
|
|
if ((SectorSize - Offset) > Length) {
|
|
Limit = Offset + Length;
|
|
|
|
} else {
|
|
Limit = SectorSize;
|
|
}
|
|
|
|
for (Index = Offset; Index < Limit; Index += 1) {
|
|
((PCHAR)Buffer)[Index - Offset] = TempPointer[Index];
|
|
}
|
|
|
|
//
|
|
// Update transfer parameters.
|
|
//
|
|
|
|
*Count += Limit - Offset;
|
|
Length -= Limit - Offset;
|
|
Buffer = (PVOID)((PCHAR)Buffer + Limit - Offset);
|
|
BlFileTable[FileId].Position = RtlLargeIntegerAdd( Position,
|
|
RtlConvertLongToLargeInteger(Limit - Offset));
|
|
}
|
|
|
|
ArcStatus = GetAdapterCapabilities(
|
|
BlFileTable[FileId].u.PartitionContext.PortDeviceObject,
|
|
&PortCapabilities
|
|
);
|
|
|
|
if (ArcStatus != ESUCCESS ||
|
|
PortCapabilities->MaximumTransferLength < 0x1000 ||
|
|
PortCapabilities->MaximumTransferLength > 0x10000) {
|
|
|
|
adapterLimit = 0x10000;
|
|
|
|
} else {
|
|
|
|
adapterLimit = PortCapabilities->MaximumTransferLength;
|
|
}
|
|
|
|
//
|
|
// The position is aligned on a sector boundary. Read as many sectors
|
|
// as possible in a contiguous run in 64Kb chunks.
|
|
//
|
|
|
|
BytesToTransfer = Length & (~(SectorSize - 1));
|
|
while (BytesToTransfer != 0) {
|
|
|
|
//
|
|
// The scsi controller doesn't support transfers bigger than 64Kb.
|
|
// Transfer the maximum number of bytes possible.
|
|
//
|
|
|
|
Limit = (BytesToTransfer > adapterLimit ? adapterLimit : BytesToTransfer);
|
|
|
|
//
|
|
// Build the memory descriptor list.
|
|
//
|
|
|
|
|
|
MdlAddress = (PMDL)&MdlBuffer[0];
|
|
MdlAddress->Next = NULL;
|
|
MdlAddress->Size = sizeof(MDL) +
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(Buffer, Limit) * sizeof(ULONG);
|
|
MdlAddress->StartVa = (PVOID)PAGE_ALIGN(Buffer);
|
|
MdlAddress->ByteCount = Limit;
|
|
MdlAddress->ByteOffset = BYTE_OFFSET(Buffer);
|
|
PageFrame = (PULONG)(MdlAddress + 1);
|
|
FrameNumber = (((ULONG)MdlAddress->StartVa) & 0x1fffffff) >> PAGE_SHIFT;
|
|
NumberOfPages = (MdlAddress->ByteCount +
|
|
MdlAddress->ByteOffset + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
for (Index = 0; Index < NumberOfPages; Index += 1) {
|
|
*PageFrame++ = FrameNumber++;
|
|
}
|
|
|
|
//
|
|
// Flush I/O buffers and read from the boot device.
|
|
//
|
|
|
|
KeFlushIoBuffers(MdlAddress, TRUE, TRUE);
|
|
LogicalBlock = RtlLargeIntegerShiftRight(BlFileTable[FileId].Position,
|
|
BlFileTable[FileId].u.PartitionContext.SectorShift);
|
|
LogicalBlock.LowPart += BlFileTable[FileId].u.PartitionContext.StartingSector;
|
|
NtStatus = ScsiDiskBootIO(MdlAddress,
|
|
LogicalBlock.LowPart,
|
|
&BlFileTable[FileId].u.PartitionContext);
|
|
|
|
if (NtStatus != ESUCCESS) {
|
|
return EIO;
|
|
}
|
|
|
|
*Count += Limit;
|
|
Length -= Limit;
|
|
Buffer = (PVOID)((PCHAR)Buffer + Limit);
|
|
BytesToTransfer -= Limit;
|
|
BlFileTable[FileId].Position = RtlLargeIntegerAdd(BlFileTable[FileId].Position,
|
|
RtlConvertLongToLargeInteger(Limit));
|
|
}
|
|
|
|
//
|
|
// If there is any residual data to read, then read the last sector
|
|
// separately and copy the data.
|
|
//
|
|
|
|
if (Length != 0) {
|
|
Position = BlFileTable[FileId].Position;
|
|
ArcStatus = ScsiDiskRead(FileId, TempPointer, SectorSize, &TransferCount);
|
|
if (ArcStatus != ESUCCESS) {
|
|
BlFileTable[FileId].Position = Position;
|
|
return ArcStatus;
|
|
}
|
|
|
|
//
|
|
// Copy the data to the specified buffer.
|
|
//
|
|
|
|
for (Index = 0; Index < Length; Index += 1) {
|
|
((PCHAR)Buffer)[Index] = TempPointer[Index];
|
|
}
|
|
|
|
//
|
|
// Update transfer parameters.
|
|
//
|
|
|
|
*Count += Length;
|
|
BlFileTable[FileId].Position = RtlLargeIntegerAdd(Position,
|
|
RtlConvertLongToLargeInteger(Length));
|
|
}
|
|
|
|
return ESUCCESS;
|
|
|
|
}
|
|
|
|
ARC_STATUS
|
|
ScsiDiskGetReadStatus (
|
|
IN ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
return ESUCCESS;
|
|
}
|
|
|
|
ARC_STATUS
|
|
ScsiDiskSeek (
|
|
IN ULONG FileId,
|
|
IN PLARGE_INTEGER Offset,
|
|
IN SEEK_MODE SeekMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sets the device position to the specified offset for
|
|
the specified file id.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file table index.
|
|
|
|
Offset - Supplies to new device position.
|
|
|
|
SeekMode - Supplies the mode for the position.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Set the current device position as specifed by the seek mode.
|
|
//
|
|
|
|
if (SeekMode == SeekAbsolute) {
|
|
BlFileTable[FileId].Position = *Offset;
|
|
|
|
} else if (SeekMode == SeekRelative) {
|
|
BlFileTable[FileId].Position = RtlLargeIntegerAdd(BlFileTable[FileId].Position,
|
|
*Offset);
|
|
}
|
|
|
|
return ESUCCESS;
|
|
}
|
|
|
|
VOID
|
|
HardDiskInitialize(
|
|
IN OUT PDRIVER_LOOKUP_ENTRY LookupTable,
|
|
IN ULONG Entries
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the scsi controller and the
|
|
device entry table for the scsi driver.
|
|
|
|
Arguments:
|
|
|
|
LookupTable.
|
|
Entries
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG scsiNumber;
|
|
PDEVICE_EXTENSION scsiPort;
|
|
PSCSI_CONFIGURATION_INFO configInfo;
|
|
PSCSI_BUS_SCAN_DATA busScanData;
|
|
ULONG busNumber;
|
|
PLUNINFO lunInfo;
|
|
PINQUIRYDATA inquiryData;
|
|
PCHAR Identifier;
|
|
PARTITION_CONTEXT Context;
|
|
|
|
RtlZeroMemory(&Context, sizeof(PARTITION_CONTEXT));
|
|
|
|
//
|
|
// Initialize the common buffers.
|
|
//
|
|
|
|
ReadCapacityBuffer = ExAllocatePool( NonPagedPool, sizeof(READ_CAPACITY_DATA));
|
|
|
|
SenseInfoBuffer = ExAllocatePool( NonPagedPool, SENSE_BUFFER_SIZE);
|
|
|
|
if (ReadCapacityBuffer == NULL || SenseInfoBuffer == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Scan the scsi ports looking for floppy disk devices.
|
|
//
|
|
|
|
for (scsiNumber = 0; ScsiPortDeviceObject[scsiNumber]; scsiNumber++) {
|
|
|
|
scsiPort = ScsiPortDeviceObject[scsiNumber]->DeviceExtension;
|
|
configInfo = scsiPort->ScsiInfo;
|
|
Context.PortDeviceObject = ScsiPortDeviceObject[scsiNumber];
|
|
|
|
for (busNumber=0; busNumber < (ULONG)configInfo->NumberOfBuses; busNumber++) {
|
|
|
|
busScanData = configInfo->BusScanData[busNumber];
|
|
|
|
//
|
|
// Set LunInfo to beginning of list.
|
|
//
|
|
|
|
lunInfo = busScanData->LunInfoList;
|
|
|
|
while (lunInfo != NULL) {
|
|
|
|
inquiryData = (PVOID)lunInfo->InquiryData;
|
|
|
|
ScsiDebugPrint(3,"FindScsiDevices: Inquiry data at %lx\n",
|
|
inquiryData);
|
|
|
|
if ((inquiryData->DeviceType == DIRECT_ACCESS_DEVICE) &&
|
|
(!lunInfo->DeviceClaimed)) {
|
|
//DbgPrint("ScsiVendor ID at %lx\n",inquiryData->VendorId);
|
|
//DbgBreakPoint();
|
|
|
|
ScsiDebugPrint(1,
|
|
"FindScsiDevices: Vendor string is %.24s\n",
|
|
inquiryData->VendorId);
|
|
|
|
//
|
|
// Create a dummy paritition context so that I/O can be
|
|
// done on the device. SendSrbSynchronous only uses the
|
|
// port device object pointer and the scsi address of the
|
|
// logical unit.
|
|
//
|
|
|
|
Context.PathId = lunInfo->PathId;
|
|
Context.TargetId = lunInfo->TargetId;
|
|
Context.DiskId = lunInfo->Lun;
|
|
|
|
//
|
|
// Check to see if the device is a floppy.
|
|
//
|
|
//
|
|
if (inquiryData->RemovableMedia &&
|
|
inquiryData->DeviceType == DIRECT_ACCESS_DEVICE &&
|
|
IsFloppyDevice(&Context) ) {
|
|
|
|
//
|
|
// Create name for disk object.
|
|
//
|
|
|
|
LookupTable->DevicePath =
|
|
ExAllocatePool(NonPagedPool,
|
|
sizeof("scsi(%d)disk(%d)fdisk(%d)"));
|
|
|
|
if (LookupTable->DevicePath == NULL) {
|
|
return;
|
|
}
|
|
|
|
sprintf(LookupTable->DevicePath,
|
|
"scsi(%d)disk(%d)fdisk(%d)",
|
|
scsiNumber,
|
|
lunInfo->TargetId + lunInfo->PathId * SCSI_MAXIMUM_TARGETS_PER_BUS,
|
|
lunInfo->Lun
|
|
);
|
|
LookupTable->DispatchTable = &ScsiDiskEntryTable;
|
|
|
|
ScsiDebugPrint(1,"Found ARC device %s\n",LookupTable->DevicePath);
|
|
//
|
|
// Increment to the next entry.
|
|
//
|
|
|
|
LookupTable++;
|
|
|
|
//
|
|
// Claim disk device by marking configuration
|
|
// record owned.
|
|
//
|
|
|
|
lunInfo->DeviceClaimed = TRUE;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get next LunInfo.
|
|
//
|
|
|
|
lunInfo = lunInfo->NextLunInfo;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ScsiDiskBootIO (
|
|
IN PMDL MdlAddress,
|
|
IN ULONG LogicalBlock,
|
|
IN PPARTITION_CONTEXT PartitionContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the read/write routine for the hard disk boot driver.
|
|
|
|
Arguments:
|
|
|
|
MdlAddress - Supplies a pointer to an MDL for the IO operation.
|
|
|
|
LogicalBlock - Supplies the starting block number.
|
|
|
|
DeviceUnit - Supplies the SCSI Id number.
|
|
|
|
Return Value:
|
|
|
|
The final status of the read operation (STATUS_UNSUCCESSFUL or
|
|
STATUS_SUCCESS).
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS Status;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION NextIrpStack;
|
|
PSCSI_REQUEST_BLOCK Srb;
|
|
ULONG RetryCount = MAXIMUM_RETRIES;
|
|
|
|
ScsiDebugPrint(1,"ScsiDiskBootIO enter routine\n");
|
|
|
|
//
|
|
// Check that the request is within the limits of the partition.
|
|
//
|
|
if (PartitionContext->StartingSector > LogicalBlock) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
if (PartitionContext->EndingSector <
|
|
LogicalBlock + (MdlAddress->ByteCount >> PartitionContext->SectorShift)) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Retry:
|
|
|
|
//
|
|
// Build the I/O Request.
|
|
//
|
|
|
|
Irp = BuildReadRequest(PartitionContext, MdlAddress, LogicalBlock);
|
|
|
|
NextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|
Srb = NextIrpStack->Parameters.Others.Argument1;
|
|
|
|
//
|
|
// Call the port driver.
|
|
//
|
|
|
|
IoCallDriver(PartitionContext->PortDeviceObject, Irp);
|
|
|
|
//
|
|
// Check the status.
|
|
//
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Determine the cause of the error.
|
|
//
|
|
|
|
if (InterpretSenseInfo(Srb, &Status, PartitionContext) && RetryCount--) {
|
|
|
|
goto Retry;
|
|
}
|
|
|
|
if (Status == EAGAIN) {
|
|
Status = EIO;
|
|
}
|
|
|
|
ScsiDebugPrint((1, "SCSI: Read request failed. Arc Status: %d, Srb Status: %x\n",
|
|
Status,
|
|
Srb->SrbStatus
|
|
));
|
|
|
|
} else {
|
|
|
|
Status = ESUCCESS;
|
|
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
ARC_STATUS
|
|
ReadDriveCapacity(
|
|
IN PPARTITION_CONTEXT PartitionContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a read capacity to a target id and returns
|
|
when it is complete.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
PCDB Cdb;
|
|
PSCSI_REQUEST_BLOCK Srb = &PrimarySrb.Srb;
|
|
ULONG LastSector;
|
|
ULONG retries = 1;
|
|
ARC_STATUS status;
|
|
ULONG BytesPerSector;
|
|
|
|
ScsiDebugPrint(2,"SCSI ReadCapacity: Enter routine\n");
|
|
|
|
|
|
//
|
|
// Build the read capacity CDB.
|
|
//
|
|
|
|
Srb->CdbLength = 10;
|
|
Cdb = (PCDB)Srb->Cdb;
|
|
|
|
//
|
|
// Zero CDB in SRB on stack.
|
|
//
|
|
|
|
RtlZeroMemory(Cdb, MAXIMUM_CDB_SIZE);
|
|
|
|
Cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
|
|
|
|
Retry:
|
|
|
|
status = SendSrbSynchronous(PartitionContext,
|
|
Srb,
|
|
ReadCapacityBuffer,
|
|
sizeof(READ_CAPACITY_DATA),
|
|
FALSE);
|
|
|
|
if (status == ESUCCESS) {
|
|
|
|
BytesPerSector = 0;
|
|
|
|
//
|
|
// Copy sector size from read capacity buffer to device extension
|
|
// in reverse byte order.
|
|
//
|
|
|
|
((PFOUR_BYTE)&BytesPerSector)->Byte0 =
|
|
((PFOUR_BYTE)&ReadCapacityBuffer->BytesPerBlock)->Byte3;
|
|
|
|
((PFOUR_BYTE)&BytesPerSector)->Byte1 =
|
|
((PFOUR_BYTE)&ReadCapacityBuffer->BytesPerBlock)->Byte2;
|
|
|
|
if (BytesPerSector == 0) {
|
|
|
|
//
|
|
// Assume this is a brain dead cd-rom and the sector size is 2048.
|
|
//
|
|
|
|
BytesPerSector = 2048;
|
|
|
|
}
|
|
|
|
//
|
|
// Calculate sector to byte shift.
|
|
//
|
|
|
|
WHICH_BIT(BytesPerSector, PartitionContext->SectorShift);
|
|
|
|
//
|
|
// Copy last sector in reverse byte order.
|
|
//
|
|
|
|
((PFOUR_BYTE)&LastSector)->Byte0 =
|
|
((PFOUR_BYTE)&ReadCapacityBuffer->LogicalBlockAddress)->Byte3;
|
|
|
|
((PFOUR_BYTE)&LastSector)->Byte1 =
|
|
((PFOUR_BYTE)&ReadCapacityBuffer->LogicalBlockAddress)->Byte2;
|
|
|
|
((PFOUR_BYTE)&LastSector)->Byte2 =
|
|
((PFOUR_BYTE)&ReadCapacityBuffer->LogicalBlockAddress)->Byte1;
|
|
|
|
((PFOUR_BYTE)&LastSector)->Byte3 =
|
|
((PFOUR_BYTE)&ReadCapacityBuffer->LogicalBlockAddress)->Byte0;
|
|
|
|
|
|
PartitionContext->PartitionLength = RtlConvertLongToLargeInteger(LastSector + 1);
|
|
PartitionContext->PartitionLength = RtlLargeIntegerShiftLeft(PartitionContext->PartitionLength,
|
|
PartitionContext->SectorShift);
|
|
|
|
PartitionContext->StartingSector=0;
|
|
PartitionContext->EndingSector = LastSector + 1;
|
|
|
|
ScsiDebugPrint(2,"SCSI ReadDriveCapacity: Sector size is %d\n",
|
|
BytesPerSector);
|
|
|
|
ScsiDebugPrint(2,"SCSI ReadDriveCapacity: Number of Sectors is %d\n",
|
|
LastSector + 1);
|
|
|
|
|
|
}
|
|
|
|
if (status == EAGAIN || status == EBUSY) {
|
|
|
|
if (retries--) {
|
|
|
|
//
|
|
// Retry request.
|
|
//
|
|
|
|
goto Retry;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end ReadDriveCapacity()
|
|
|
|
|
|
ARC_STATUS
|
|
SendSrbSynchronous(
|
|
PPARTITION_CONTEXT PartitionContext,
|
|
PSCSI_REQUEST_BLOCK Srb,
|
|
PVOID BufferAddress,
|
|
ULONG BufferLength,
|
|
BOOLEAN WriteToDevice
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by SCSI device controls to complete an
|
|
SRB and send it to the port driver synchronously (ie wait for
|
|
completion).
|
|
The CDB is already completed along with the SRB CDB size and
|
|
request timeout value.
|
|
|
|
Arguments:
|
|
|
|
PartitionContext
|
|
SRB
|
|
Buffer address and length (if transfer)
|
|
|
|
WriteToDevice - Indicates the direction of the transfer.
|
|
|
|
Return Value:
|
|
|
|
ARC_STATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
ULONG retryCount = 1;
|
|
ARC_STATUS status;
|
|
|
|
//
|
|
// Write length to SRB.
|
|
//
|
|
|
|
Srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
|
|
//
|
|
// Set SCSI bus address.
|
|
//
|
|
|
|
Srb->PathId = PartitionContext->PathId;
|
|
Srb->TargetId = PartitionContext->TargetId;
|
|
Srb->Lun = PartitionContext->DiskId;
|
|
|
|
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
|
|
//
|
|
// Enable auto request sense.
|
|
//
|
|
|
|
Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
|
|
if (SenseInfoBuffer == NULL) {
|
|
ScsiDebugPrint(1,"SendSrbSynchronous: Can't allocate request sense buffer\n");
|
|
return(ENOMEM);
|
|
}
|
|
|
|
Srb->SenseInfoBuffer = SenseInfoBuffer;
|
|
|
|
Srb->DataBuffer = BufferAddress;
|
|
|
|
//
|
|
// Start retries here.
|
|
//
|
|
|
|
retry:
|
|
|
|
Irp = InitializeIrp(
|
|
&PrimarySrb,
|
|
IRP_MJ_SCSI,
|
|
PartitionContext->PortDeviceObject,
|
|
BufferAddress,
|
|
BufferLength
|
|
);
|
|
|
|
if (BufferAddress != NULL) {
|
|
|
|
if (WriteToDevice) {
|
|
|
|
Srb->SrbFlags = SRB_FLAGS_DATA_OUT;
|
|
|
|
} else {
|
|
|
|
Srb->SrbFlags = SRB_FLAGS_DATA_IN;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Clear flags.
|
|
//
|
|
|
|
Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
|
|
}
|
|
|
|
//
|
|
// Disable synchronous transfers.
|
|
//
|
|
|
|
Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
|
|
//
|
|
// Set the transfer length.
|
|
//
|
|
|
|
Srb->DataTransferLength = BufferLength;
|
|
|
|
//
|
|
// Zero out status.
|
|
//
|
|
|
|
Srb->ScsiStatus = Srb->SrbStatus = 0;
|
|
|
|
//
|
|
// Get next stack location and
|
|
// set major function code.
|
|
//
|
|
|
|
IrpStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
|
|
//
|
|
// Set up SRB for execute scsi request.
|
|
// Save SRB address in next stack for port driver.
|
|
//
|
|
|
|
IrpStack->Parameters.Others.Argument1 = (PVOID)Srb;
|
|
|
|
//
|
|
// Set up IRP Address.
|
|
//
|
|
|
|
Srb->OriginalRequest = Irp;
|
|
|
|
Srb->NextSrb = 0;
|
|
|
|
//
|
|
// No need to check the following 2 returned statuses as
|
|
// SRB will have ending status.
|
|
//
|
|
|
|
(VOID)IoCallDriver(PartitionContext->PortDeviceObject, Irp);
|
|
|
|
//
|
|
// Check that request completed without error.
|
|
//
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Update status and determine if request should be retried.
|
|
//
|
|
|
|
if (InterpretSenseInfo(Srb, &status, PartitionContext)) {
|
|
|
|
//
|
|
// If retries are not exhausted then
|
|
// retry this operation.
|
|
//
|
|
|
|
if (retryCount--) {
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
status = ESUCCESS;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end SendSrbSynchronous()
|
|
|
|
|
|
BOOLEAN
|
|
InterpretSenseInfo(
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
OUT ARC_STATUS *Status,
|
|
PPARTITION_CONTEXT PartitionContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine interprets the data returned from the SCSI
|
|
request sense. It determines the status to return in the
|
|
IRP and whether this request can be retried.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
SRB
|
|
ARC_STATUS to update IRP
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN TRUE: Drivers should retry this request.
|
|
FALSE: Drivers should not retry this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSENSE_DATA SenseBuffer = Srb->SenseInfoBuffer;
|
|
BOOLEAN retry;
|
|
|
|
//
|
|
// Check that request sense buffer is valid.
|
|
//
|
|
|
|
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
|
|
|
|
ScsiDebugPrint(2,"InterpretSenseInfo: Error code is %x\n",
|
|
SenseBuffer->ErrorCode);
|
|
|
|
ScsiDebugPrint(2,"InterpretSenseInfo: Sense key is %x\n",
|
|
SenseBuffer->SenseKey);
|
|
|
|
ScsiDebugPrint(2,"InterpretSenseInfo: Additional sense code is %x\n",
|
|
SenseBuffer->AdditionalSenseCode);
|
|
|
|
ScsiDebugPrint(2,"InterpretSenseInfo: Additional sense code qualifier is %x\n",
|
|
SenseBuffer->AdditionalSenseCodeQualifier);
|
|
|
|
switch (SenseBuffer->SenseKey) {
|
|
|
|
case SCSI_SENSE_NOT_READY:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Device not ready\n");
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Waiting for device\n");
|
|
|
|
*Status = EBUSY;
|
|
|
|
retry = TRUE;
|
|
|
|
switch (SenseBuffer->AdditionalSenseCode) {
|
|
|
|
case SCSI_ADSENSE_LUN_NOT_READY:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Lun not ready\n");
|
|
|
|
switch (SenseBuffer->AdditionalSenseCodeQualifier) {
|
|
|
|
case SCSI_SENSEQ_BECOMING_READY:
|
|
|
|
ScsiDebugPrint(1,
|
|
"InterpretSenseInfo:"
|
|
" In process of becoming ready\n");
|
|
|
|
FwStallExecution( 1000 * 1000 * 3 );
|
|
|
|
break;
|
|
|
|
case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
|
|
|
|
ScsiDebugPrint(1,
|
|
"InterpretSenseInfo:"
|
|
" Manual intervention required\n");
|
|
*Status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
retry = FALSE;
|
|
break;
|
|
|
|
case SCSI_SENSEQ_FORMAT_IN_PROGRESS:
|
|
|
|
ScsiDebugPrint(1,
|
|
"InterpretSenseInfo:"
|
|
" Format in progress\n");
|
|
retry = FALSE;
|
|
break;
|
|
|
|
default:
|
|
|
|
FwStallExecution( 1000 * 1000 * 3 );
|
|
|
|
//
|
|
// Try a start unit too.
|
|
//
|
|
|
|
case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
|
|
|
|
ScsiDebugPrint(1,
|
|
"InterpretSenseInfo:"
|
|
" Initializing command required\n");
|
|
|
|
//
|
|
// This sense code/additional sense code
|
|
// combination may indicate that the device
|
|
// needs to be started.
|
|
//
|
|
|
|
ScsiDiskStartUnit(PartitionContext);
|
|
break;
|
|
|
|
}
|
|
|
|
} // end switch
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_DATA_PROTECT:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Media write protected\n");
|
|
|
|
*Status = EACCES;
|
|
|
|
retry = FALSE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_MEDIUM_ERROR:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Bad media\n");
|
|
*Status = EIO;
|
|
|
|
retry = TRUE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_HARDWARE_ERROR:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Hardware error\n");
|
|
*Status = EIO;
|
|
|
|
retry = TRUE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_ILLEGAL_REQUEST:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Illegal SCSI request\n");
|
|
|
|
switch (SenseBuffer->AdditionalSenseCode) {
|
|
|
|
case SCSI_ADSENSE_ILLEGAL_COMMAND:
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Illegal command\n");
|
|
break;
|
|
|
|
case SCSI_ADSENSE_ILLEGAL_BLOCK:
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Illegal block address\n");
|
|
break;
|
|
|
|
case SCSI_ADSENSE_INVALID_LUN:
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Invalid LUN\n");
|
|
break;
|
|
|
|
case SCSI_ADSENSE_MUSIC_AREA:
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Music area\n");
|
|
break;
|
|
|
|
case SCSI_ADSENSE_DATA_AREA:
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Data area\n");
|
|
break;
|
|
|
|
case SCSI_ADSENSE_VOLUME_OVERFLOW:
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Volume overflow\n");
|
|
break;
|
|
|
|
} // end switch ...
|
|
|
|
*Status = EINVAL;
|
|
|
|
retry = FALSE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|
|
|
ScsiDebugPrint(3,"InterpretSenseInfo: Unit attention\n");
|
|
|
|
switch (SenseBuffer->AdditionalSenseCode) {
|
|
|
|
case SCSI_ADSENSE_MEDIUM_CHANGED:
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Media changed\n");
|
|
break;
|
|
|
|
case SCSI_ADSENSE_BUS_RESET:
|
|
break;
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Bus reset\n");
|
|
|
|
}
|
|
|
|
*Status = EAGAIN;
|
|
|
|
retry = TRUE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_ABORTED_COMMAND:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Command aborted\n");
|
|
|
|
*Status = EIO;
|
|
|
|
retry = TRUE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_NO_SENSE:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: No specific sense key\n");
|
|
|
|
*Status = EIO;
|
|
|
|
retry = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Unrecognized sense code\n");
|
|
|
|
*Status = STATUS_UNSUCCESSFUL;
|
|
|
|
retry = TRUE;
|
|
|
|
} // end switch
|
|
|
|
} else {
|
|
|
|
//
|
|
// Request sense buffer not valid. No sense information
|
|
// to pinpoint the error. Return general request fail.
|
|
//
|
|
|
|
ScsiDebugPrint(1,"InterpretSenseInfo: Request sense info not valid\n");
|
|
|
|
*Status = EIO;
|
|
|
|
retry = TRUE;
|
|
}
|
|
|
|
return retry;
|
|
|
|
} // end InterpretSenseInfo()
|
|
|
|
|
|
VOID
|
|
RetryRequest(
|
|
PPARTITION_CONTEXT PartitionContext,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION NextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK Srb = &PrimarySrb.Srb;
|
|
PMDL Mdl = Irp->MdlAddress;
|
|
ULONG TransferByteCount = Mdl->ByteCount;
|
|
|
|
|
|
//
|
|
// Reset byte count of transfer in SRB Extension.
|
|
//
|
|
|
|
Srb->DataTransferLength = TransferByteCount;
|
|
|
|
//
|
|
// Zero SRB statuses.
|
|
//
|
|
|
|
Srb->SrbStatus = Srb->ScsiStatus = 0;
|
|
|
|
//
|
|
// Set up major SCSI function.
|
|
//
|
|
|
|
NextIrpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Save SRB address in next stack for port driver.
|
|
//
|
|
|
|
NextIrpStack->Parameters.Others.Argument1 = (PVOID)Srb;
|
|
|
|
//
|
|
// Return the results of the call to the port driver.
|
|
//
|
|
|
|
(PVOID)IoCallDriver(PartitionContext->PortDeviceObject, Irp);
|
|
|
|
return;
|
|
|
|
} // end RetryRequest()
|
|
|
|
PIRP
|
|
BuildReadRequest(
|
|
IN PPARTITION_CONTEXT PartitionContext,
|
|
IN PMDL Mdl,
|
|
IN ULONG LogicalBlockAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Note:
|
|
|
|
If the IRP is for a disk transfer, the byteoffset field
|
|
will already have been adjusted to make it relative to
|
|
the beginning of the disk. In this way, this routine can
|
|
be shared between the disk and cdrom class drivers.
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP Irp = &PrimarySrb.Irp;
|
|
PIO_STACK_LOCATION NextIrpStack;
|
|
PSCSI_REQUEST_BLOCK Srb = &PrimarySrb.Srb;
|
|
PCDB Cdb;
|
|
USHORT TransferBlocks;
|
|
|
|
//
|
|
// Initialize the rest of the IRP.
|
|
//
|
|
|
|
Irp->MdlAddress = Mdl;
|
|
|
|
Irp->Tail.Overlay.CurrentStackLocation = &PrimarySrb.IrpStack[IRP_STACK_SIZE];
|
|
|
|
NextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Write length to SRB.
|
|
//
|
|
|
|
Srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
|
|
//
|
|
// Set up IRP Address.
|
|
//
|
|
|
|
Srb->OriginalRequest = Irp;
|
|
|
|
Srb->NextSrb = 0;
|
|
|
|
//
|
|
// Set up target id and logical unit number.
|
|
//
|
|
|
|
Srb->PathId = PartitionContext->PathId;
|
|
Srb->TargetId = PartitionContext->TargetId;
|
|
Srb->Lun = PartitionContext->DiskId;
|
|
|
|
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
|
|
Srb->DataBuffer = MmGetMdlVirtualAddress(Mdl);
|
|
|
|
//
|
|
// Save byte count of transfer in SRB Extension.
|
|
//
|
|
|
|
Srb->DataTransferLength = Mdl->ByteCount;
|
|
|
|
//
|
|
// Indicate auto request sense by specifying buffer and size.
|
|
//
|
|
|
|
Srb->SenseInfoBuffer = SenseInfoBuffer;
|
|
|
|
Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
|
|
//
|
|
// Set timeout value in seconds.
|
|
//
|
|
|
|
Srb->TimeOutValue = SCSI_DISK_TIMEOUT;
|
|
|
|
//
|
|
// Zero statuses.
|
|
//
|
|
|
|
Srb->SrbStatus = Srb->ScsiStatus = 0;
|
|
|
|
//
|
|
// Indicate that 10-byte CDB's will be used.
|
|
//
|
|
|
|
Srb->CdbLength = 10;
|
|
|
|
//
|
|
// Fill in CDB fields.
|
|
//
|
|
|
|
Cdb = (PCDB)Srb->Cdb;
|
|
|
|
Cdb->CDB10.LogicalUnitNumber = PartitionContext->DiskId;
|
|
|
|
TransferBlocks = (USHORT)(Mdl->ByteCount >> PartitionContext->SectorShift);
|
|
|
|
//
|
|
// Move little endian values into CDB in big endian format.
|
|
//
|
|
|
|
Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte3;
|
|
Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte2;
|
|
Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte1;
|
|
Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte0;
|
|
|
|
Cdb->CDB10.Reserved2 = 0;
|
|
|
|
Cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&TransferBlocks)->Byte1;
|
|
Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&TransferBlocks)->Byte0;
|
|
|
|
Cdb->CDB10.Control = 0;
|
|
|
|
//
|
|
// Set transfer direction flag and Cdb command.
|
|
//
|
|
|
|
Srb->SrbFlags = SRB_FLAGS_DATA_IN;
|
|
|
|
Cdb->CDB10.OperationCode = SCSIOP_READ;
|
|
|
|
//
|
|
// Disable synchronous transfers.
|
|
//
|
|
|
|
Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
|
|
//
|
|
// Set up major SCSI function.
|
|
//
|
|
|
|
NextIrpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Save SRB address in next stack for port driver.
|
|
//
|
|
|
|
NextIrpStack->Parameters.Others.Argument1 = (PVOID)Srb;
|
|
|
|
return(Irp);
|
|
|
|
} // end BuildReadRequest()
|
|
|
|
VOID
|
|
ScsiDiskStartUnit(
|
|
IN PPARTITION_CONTEXT PartitionContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send command to SCSI unit to start or power up.
|
|
Because this command is issued asynchronounsly, that is without
|
|
waiting on it to complete, the IMMEDIATE flag is not set. This
|
|
means that the CDB will not return until the drive has powered up.
|
|
This should keep subsequent requests from being submitted to the
|
|
device before it has completely spun up.
|
|
This routine is called from the InterpretSense routine, when a
|
|
request sense returns data indicating that a drive must be
|
|
powered up.
|
|
|
|
Arguments:
|
|
|
|
PartitionContext - structure containing pointer to port device driver.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
PIRP irp;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PSCSI_REQUEST_BLOCK originalSrb = &PrimarySrb.Srb;
|
|
PCDB cdb;
|
|
|
|
ScsiDebugPrint(3,"StartUnit: Enter routine\n");
|
|
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
//
|
|
// Write length to SRB.
|
|
//
|
|
|
|
srb.Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
|
|
//
|
|
// Set up SCSI bus address.
|
|
//
|
|
|
|
srb.PathId = originalSrb->PathId;
|
|
srb.TargetId = originalSrb->TargetId;
|
|
srb.Lun = originalSrb->Lun;
|
|
|
|
srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
|
|
//
|
|
// Zero out status.
|
|
//
|
|
|
|
srb.ScsiStatus = srb.SrbStatus = 0;
|
|
|
|
//
|
|
// Set timeout value large enough for drive to spin up.
|
|
// NOTE: This value is arbitrary.
|
|
//
|
|
|
|
srb.TimeOutValue = 30;
|
|
|
|
//
|
|
// Set the transfer length.
|
|
//
|
|
|
|
srb.DataTransferLength = 0;
|
|
srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | SRB_FLAGS_DISABLE_AUTOSENSE;
|
|
srb.SenseInfoBufferLength = 0;
|
|
srb.SenseInfoBuffer = NULL;
|
|
|
|
//
|
|
// Build the start unit CDB.
|
|
//
|
|
|
|
srb.CdbLength = 6;
|
|
cdb = (PCDB)srb.Cdb;
|
|
|
|
cdb->CDB10.OperationCode = SCSIOP_START_STOP_UNIT;
|
|
cdb->START_STOP.Start = 1;
|
|
|
|
//
|
|
// Build the IRP
|
|
// to be sent to the port driver.
|
|
//
|
|
|
|
irp = InitializeIrp(
|
|
&PrimarySrb,
|
|
IRP_MJ_SCSI,
|
|
PartitionContext->PortDeviceObject,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
srb.OriginalRequest = irp;
|
|
|
|
//
|
|
// Save SRB address in next stack for port driver.
|
|
//
|
|
|
|
irpStack->Parameters.Others.Argument1 = &srb;
|
|
|
|
//
|
|
// No need to check the following 2 returned statuses as
|
|
// SRB will have ending status.
|
|
//
|
|
|
|
(VOID)IoCallDriver(PartitionContext->PortDeviceObject, irp);
|
|
|
|
} // end StartUnit()
|
|
|
|
ULONG
|
|
ClassModeSense(
|
|
IN PPARTITION_CONTEXT Context,
|
|
IN PCHAR ModeSenseBuffer,
|
|
IN ULONG Length,
|
|
IN UCHAR PageMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a mode sense command to a target id and returns
|
|
when it is complete.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Length of the transferred data is returned.
|
|
|
|
--*/
|
|
{
|
|
PCDB cdb;
|
|
PSCSI_REQUEST_BLOCK Srb = &PrimarySrb.Srb;
|
|
ULONG retries = 1;
|
|
NTSTATUS status;
|
|
|
|
ScsiDebugPrint((3,"SCSI ModeSense: Enter routine\n"));
|
|
|
|
//
|
|
// Build the read capacity CDB.
|
|
//
|
|
|
|
Srb->CdbLength = 6;
|
|
cdb = (PCDB)Srb->Cdb;
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
Srb->TimeOutValue = 2;
|
|
|
|
RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);
|
|
|
|
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
|
cdb->MODE_SENSE.PageCode = PageMode;
|
|
cdb->MODE_SENSE.AllocationLength = Length;
|
|
|
|
Retry:
|
|
|
|
status = SendSrbSynchronous(Context,
|
|
Srb,
|
|
ModeSenseBuffer,
|
|
Length,
|
|
FALSE);
|
|
|
|
|
|
if (status == EAGAIN || status == EBUSY) {
|
|
|
|
//
|
|
// Routine SendSrbSynchronous does not retry
|
|
// requests returned with this status.
|
|
// Read Capacities should be retried
|
|
// anyway.
|
|
//
|
|
|
|
if (retries--) {
|
|
|
|
//
|
|
// Retry request.
|
|
//
|
|
|
|
goto Retry;
|
|
}
|
|
} else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
return(Srb->DataTransferLength);
|
|
} else {
|
|
return(0);
|
|
}
|
|
|
|
} // end ClassModeSense()
|
|
|
|
PVOID
|
|
ClassFindModePage(
|
|
IN PCHAR ModeSenseBuffer,
|
|
IN ULONG Length,
|
|
IN UCHAR PageMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine scans through the mode sense data and finds the requested
|
|
mode sense page code.
|
|
|
|
Arguments:
|
|
ModeSenseBuffer - Supplies a pointer to the mode sense data.
|
|
|
|
Length - Indicates the length of valid data.
|
|
|
|
PageMode - Supplies the page mode to be searched for.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the the requested mode page. If the mode page was not found
|
|
then NULL is return.
|
|
|
|
--*/
|
|
{
|
|
PUCHAR limit;
|
|
|
|
limit = ModeSenseBuffer + Length;
|
|
|
|
//
|
|
// Skip the mode select header and block descriptors.
|
|
//
|
|
|
|
if (Length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
return(NULL);
|
|
}
|
|
|
|
ModeSenseBuffer += sizeof(MODE_PARAMETER_HEADER) +
|
|
((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
|
|
|
|
//
|
|
// ModeSenseBuffer now points at pages walk the pages looking for the
|
|
// requested page until the limit is reached.
|
|
//
|
|
|
|
while (ModeSenseBuffer < limit) {
|
|
|
|
if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
|
|
return(ModeSenseBuffer);
|
|
}
|
|
|
|
//
|
|
// Adavance to the next page.
|
|
//
|
|
|
|
ModeSenseBuffer += ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength + 2;
|
|
}
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
IsFloppyDevice(
|
|
PPARTITION_CONTEXT Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine performs the necessary functioons to determinee if a device is
|
|
really a floppy rather than a harddisk. This is done by a mode sense
|
|
command. First, a check is made to see if the medimum type is set. Second
|
|
a check is made for the flexible parameters mode page.
|
|
|
|
Arguments:
|
|
|
|
Context - Supplies the device object to be tested.
|
|
|
|
Return Value:
|
|
|
|
Return TRUE if the indicated device is a floppy.
|
|
|
|
--*/
|
|
{
|
|
|
|
PVOID modeData;
|
|
PUCHAR pageData;
|
|
ULONG length;
|
|
|
|
modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
|
|
|
|
if (modeData == NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
RtlZeroMemory(modeData, MODE_DATA_SIZE);
|
|
|
|
length = ClassModeSense(Context,
|
|
modeData,
|
|
MODE_DATA_SIZE,
|
|
MODE_SENSE_RETURN_ALL);
|
|
|
|
if (length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
|
|
//
|
|
// Retry the request in case of a check condition.
|
|
//
|
|
|
|
length = ClassModeSense(Context,
|
|
modeData,
|
|
MODE_DATA_SIZE,
|
|
MODE_SENSE_RETURN_ALL);
|
|
|
|
if (length < sizeof(MODE_PARAMETER_HEADER)) {
|
|
|
|
ExFreePool(modeData);
|
|
return(FALSE);
|
|
|
|
}
|
|
}
|
|
|
|
if (((PMODE_PARAMETER_HEADER) modeData)->MediumType >= MODE_FD_SINGLE_SIDE
|
|
&& ((PMODE_PARAMETER_HEADER) modeData)->MediumType <= MODE_FD_MAXIMUM_TYPE) {
|
|
|
|
ScsiDebugPrint((1, "Scsidisk: MediumType value %2x, This is a floppy.\n", ((PMODE_PARAMETER_HEADER) modeData)->MediumType));
|
|
ExFreePool(modeData);
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Look for the flexible disk mode page.
|
|
//
|
|
|
|
pageData = ClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE);
|
|
|
|
if (pageData != NULL) {
|
|
|
|
ScsiDebugPrint((1, "Scsidisk: Flexible disk page found, This is a floppy.\n"));
|
|
ExFreePool(modeData);
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
ExFreePool(modeData);
|
|
return(FALSE);
|
|
|
|
} // end IsFloppyDevice()
|
|
|