mirror of
https://github.com/reactos/reactos.git
synced 2026-06-20 04:17:19 +08:00
2064 lines
62 KiB
C
2064 lines
62 KiB
C
/*
|
|
* PROJECT: ReactOS ATA Port Driver
|
|
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
|
* PURPOSE: SCSI/ATA Translation layer
|
|
* COPYRIGHT: Copyright 2026 Dmitry Borisov <di.sean@protonmail.com>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "atapi.h"
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
static const UCHAR AtapReadWriteCommandMap[12][2] =
|
|
{
|
|
/* Write EXT Read EXT */
|
|
{ IDE_COMMAND_WRITE_EXT, IDE_COMMAND_READ_EXT }, // PIO single
|
|
{ IDE_COMMAND_WRITE_MULTIPLE_EXT, IDE_COMMAND_READ_MULTIPLE_EXT }, // PIO multiple
|
|
{ IDE_COMMAND_WRITE_DMA_EXT, IDE_COMMAND_READ_DMA_EXT }, // DMA
|
|
|
|
/* Write FUA EXT Read FUA EXT */
|
|
{ 0, 0 }, // PIO single
|
|
{ IDE_COMMAND_WRITE_MULTIPLE_FUA_EXT, 0 }, // PIO multiple
|
|
{ IDE_COMMAND_WRITE_DMA_FUA_EXT, 0 }, // DMA
|
|
|
|
/* Write Read */
|
|
{ IDE_COMMAND_WRITE, IDE_COMMAND_READ, }, // PIO single
|
|
{ IDE_COMMAND_WRITE_MULTIPLE, IDE_COMMAND_READ_MULTIPLE, }, // PIO multiple
|
|
{ IDE_COMMAND_WRITE_DMA, IDE_COMMAND_READ_DMA, }, // DMA
|
|
|
|
/* Write FUA Read FUA */
|
|
{ 0, 0 }, // PIO single
|
|
{ 0, 0 }, // PIO multiple
|
|
{ 0, 0 }, // DMA
|
|
};
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
static
|
|
inline
|
|
UCHAR
|
|
AtaReadWriteCommand(
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PATAPORT_IO_CONTEXT Device)
|
|
{
|
|
ULONG CmdEntry;
|
|
|
|
if (Request->Flags & REQUEST_FLAG_LBA48)
|
|
{
|
|
if (Device->DeviceFlags & DEVICE_PIO_FOR_LBA48_XFER)
|
|
Request->Flags &= ~REQUEST_FLAG_DMA;
|
|
|
|
CmdEntry = 0;
|
|
}
|
|
else
|
|
{
|
|
CmdEntry = 6;
|
|
}
|
|
|
|
if (Request->Flags & REQUEST_FLAG_FUA)
|
|
{
|
|
CmdEntry += 3;
|
|
}
|
|
|
|
if (Request->Flags & REQUEST_FLAG_DMA)
|
|
{
|
|
CmdEntry += 2;
|
|
}
|
|
else if (Request->Flags & REQUEST_FLAG_READ_WRITE_MULTIPLE)
|
|
{
|
|
CmdEntry += 1;
|
|
}
|
|
|
|
return AtapReadWriteCommandMap[CmdEntry][(Request->Flags & REQUEST_FLAG_DATA_IN) ? 1 : 0];
|
|
}
|
|
|
|
BOOLEAN
|
|
AtaReqDmaTransferToPioTransfer(
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
PATAPORT_IO_CONTEXT Device = (PATAPORT_IO_CONTEXT)Request->Device;
|
|
|
|
ASSERT(!(Device->DeviceFlags & DEVICE_PIO_VIA_DMA));
|
|
ASSERT(Request->Flags & REQUEST_DMA_FLAGS);
|
|
|
|
/* ATAPI commands */
|
|
if (Request->Flags & REQUEST_FLAG_PACKET_COMMAND)
|
|
{
|
|
Request->Flags &= ~REQUEST_DMA_FLAGS;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* For ATA commands there is no simple way to achieve this,
|
|
* we have to fix the command opcode.
|
|
* Determine if it is safe or allowed to change the command.
|
|
*/
|
|
if (Request->Flags & REQUEST_FLAG_READ_WRITE)
|
|
{
|
|
Request->Flags &= ~REQUEST_DMA_FLAGS;
|
|
|
|
Request->TaskFile.Command = AtaReadWriteCommand(Request, Device);
|
|
if (Request->TaskFile.Command == 0)
|
|
{
|
|
/* PIO is not available */
|
|
Request->Flags |= REQUEST_DMA_FLAGS;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* PIO is not available */
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
ULONG64
|
|
AtaReqLbaFromTaskFile(
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
PATA_TASKFILE TaskFile = &Request->TaskFile;
|
|
PATAPORT_IO_CONTEXT Device = (PATAPORT_IO_CONTEXT)Request->Device;
|
|
ULONG64 Lba;
|
|
|
|
if (Device->DeviceFlags & DEVICE_LBA_MODE)
|
|
{
|
|
Lba = ((ULONG64)TaskFile->LowLba << 0) |
|
|
((ULONG64)TaskFile->MidLba << 8) |
|
|
((ULONG64)TaskFile->HighLba << 16);
|
|
|
|
if (Request->Flags & REQUEST_FLAG_LBA48)
|
|
{
|
|
/* 48-bit command */
|
|
Lba |= ((ULONG64)TaskFile->LowLbaEx << 24) |
|
|
((ULONG64)TaskFile->MidLbaEx << 32) |
|
|
((ULONG64)TaskFile->HighLbaEx << 40);
|
|
}
|
|
else
|
|
{
|
|
/* 28-bit command */
|
|
Lba |= (ULONG64)(TaskFile->DriveSelect & 0x0F) << 24;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULONG Cylinder, Head, Sector;
|
|
|
|
/* Legacy CHS translation */
|
|
Cylinder = TaskFile->LowLba;
|
|
Head = TaskFile->DriveSelect & 0x0F;
|
|
Sector = ((ULONG64)TaskFile->HighLba << 8) | TaskFile->MidLba;
|
|
|
|
// TODO: Use 64bit math?
|
|
Lba = (((Cylinder * Device->Heads) + Head) * Device->SectorsPerTrack) + (Sector - 1);
|
|
}
|
|
|
|
return Lba;
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaReqTranslateFixedError(
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_Out_ PUCHAR SK,
|
|
_Out_ PUCHAR ASK,
|
|
_Out_ PUCHAR ASCQ)
|
|
{
|
|
if (Request->Output.Status & IDE_STATUS_DEVICE_FAULT)
|
|
{
|
|
*SK = SCSI_SENSE_HARDWARE_ERROR;
|
|
*ASK = SCSI_ADSENSE_INTERNAL_TARGET_FAILURE;
|
|
*ASCQ = SCSI_SENSEQ_INTERNAL_TARGET_FAILURE;
|
|
}
|
|
else if (Request->Output.Error & IDE_ERROR_DATA_ERROR)
|
|
{
|
|
if (Request->Flags & REQUEST_FLAG_DATA_OUT)
|
|
{
|
|
*SK = SCSI_SENSE_DATA_PROTECT;
|
|
*ASK = SCSI_ADSENSE_WRITE_PROTECT;
|
|
*ASCQ = 0;
|
|
}
|
|
else
|
|
{
|
|
*SK = SCSI_SENSE_MEDIUM_ERROR;
|
|
*ASK = SCSI_ADSENSE_UNRECOVERED_ERROR;
|
|
*ASCQ = SCSI_SENSEQ_UNRECOVERED_READ_ERROR;
|
|
}
|
|
}
|
|
else if (Request->Output.Error & IDE_ERROR_ID_NOT_FOUND)
|
|
{
|
|
*SK = SCSI_SENSE_ILLEGAL_REQUEST;
|
|
*ASK = SCSI_ADSENSE_ILLEGAL_BLOCK;
|
|
*ASCQ = SCSI_SENSEQ_LOGICAL_ADDRESS_OUT_OF_RANGE;
|
|
}
|
|
else if (Request->Output.Error & IDE_ERROR_CRC_ERROR)
|
|
{
|
|
*SK = SCSI_SENSE_HARDWARE_ERROR;
|
|
*ASK = SCSI_ADSENSE_LUN_COMMUNICATION;
|
|
*ASCQ = SCSI_SESNEQ_COMM_CRC_ERROR;
|
|
}
|
|
/* Return vendor specific codes for obsolete bits */
|
|
else if (Request->Output.Error & IDE_ERROR_MEDIA_CHANGE)
|
|
{
|
|
*SK = SCSI_SENSE_UNIT_ATTENTION;
|
|
*ASK = SCSI_ADSENSE_MEDIUM_CHANGED;
|
|
*ASCQ = 0;
|
|
}
|
|
else if (Request->Output.Error & IDE_ERROR_MEDIA_CHANGE_REQ)
|
|
{
|
|
*SK = SCSI_SENSE_UNIT_ATTENTION;
|
|
*ASK = SCSI_ADSENSE_OPERATOR_REQUEST;
|
|
*ASCQ = SCSI_SENSEQ_MEDIUM_REMOVAL;
|
|
}
|
|
else if (Request->Output.Error & IDE_ERROR_END_OF_MEDIA)
|
|
{
|
|
*SK = SCSI_SENSE_NOT_READY;
|
|
*ASK = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
|
|
*ASCQ = 0;
|
|
}
|
|
else if (Request->Output.Error & IDE_ERROR_ADDRESS_NOT_FOUND)
|
|
{
|
|
*SK = SCSI_SENSE_MEDIUM_ERROR;
|
|
*ASK = SCSI_ADSENSE_ADDRESS_MARK_NOT_FOUND_FOR_DATA_FIELD;
|
|
*ASCQ = 0;
|
|
}
|
|
/* The ABORT bit has low priority and indicates unknown error */
|
|
else if (Request->Output.Error & IDE_ERROR_COMMAND_ABORTED)
|
|
{
|
|
*SK = SCSI_SENSE_ABORTED_COMMAND;
|
|
*ASK = SCSI_ADSENSE_NO_SENSE;
|
|
*ASCQ = 0;
|
|
}
|
|
else
|
|
{
|
|
*SK = SCSI_SENSE_ABORTED_COMMAND;
|
|
*ASK = SCSI_ADSENSE_NO_SENSE;
|
|
*ASCQ = 0;
|
|
|
|
/* No sense data */
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UCHAR
|
|
AtaReqSetFixedAtaSenseData(
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
UCHAR SrbStatus, SK, ASK, ASCQ;
|
|
|
|
ASSERT(!(Request->SrbStatus & SRB_STATUS_AUTOSENSE_VALID));
|
|
|
|
if (!AtaReqTranslateFixedError(Request, &SK, &ASK, &ASCQ))
|
|
return Request->SrbStatus;
|
|
|
|
SenseCode.SrbStatus = Request->SrbStatus;
|
|
SenseCode.SenseKey = SK;
|
|
SenseCode.AdditionalSenseCode = ASK;
|
|
SenseCode.AdditionalSenseCodeQualifier = ASCQ;
|
|
|
|
SrbStatus = AtaReqSetFixedSenseData(Request->Srb, SenseCode);
|
|
AtaReqSetLbaInformation(Request->Srb, AtaReqLbaFromTaskFile(Request));
|
|
|
|
return SrbStatus;
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqCopySatlBuffer(
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PVOID Buffer,
|
|
_In_ ULONG Length)
|
|
{
|
|
PSCSI_REQUEST_BLOCK Srb = Request->Srb;
|
|
PMDL Mdl;
|
|
PVOID BaseAddress;
|
|
ULONG_PTR Offset;
|
|
ULONG BytesCount;
|
|
|
|
/* The driver can overwrite the Request->Mdl field during translation */
|
|
Mdl = Request->Irp->MdlAddress;
|
|
ASSERT(Mdl);
|
|
|
|
BaseAddress = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority);
|
|
if (!BaseAddress)
|
|
return SRB_STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (Length < Srb->DataTransferLength)
|
|
{
|
|
/* This indicates a residual underrun */
|
|
BytesCount = Length;
|
|
Request->SrbStatus = SRB_STATUS_DATA_OVERRUN;
|
|
}
|
|
else
|
|
{
|
|
BytesCount = Srb->DataTransferLength;
|
|
Request->SrbStatus = SRB_STATUS_SUCCESS;
|
|
}
|
|
Request->DataTransferLength = BytesCount;
|
|
|
|
/* Calculate the offset within DataBuffer */
|
|
Offset = (ULONG_PTR)BaseAddress +
|
|
(ULONG_PTR)Srb->DataBuffer -
|
|
(ULONG_PTR)MmGetMdlVirtualAddress(Mdl);
|
|
|
|
RtlCopyMemory((PVOID)Offset, Buffer, BytesCount);
|
|
return Request->SrbStatus;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqTerminateInvalidOpCode(
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
|
|
SenseCode.SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
|
SenseCode.SenseKey = SCSI_SENSE_ILLEGAL_REQUEST;
|
|
SenseCode.AdditionalSenseCode = SCSI_ADSENSE_ILLEGAL_COMMAND;
|
|
SenseCode.AdditionalSenseCodeQualifier = 0;
|
|
|
|
return AtaReqSetFixedSenseData(Srb, SenseCode);
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqTerminateInvalidField(
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
|
|
SenseCode.SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
|
SenseCode.SenseKey = SCSI_SENSE_ILLEGAL_REQUEST;
|
|
SenseCode.AdditionalSenseCode = SCSI_ADSENSE_INVALID_CDB;
|
|
SenseCode.AdditionalSenseCodeQualifier = 0;
|
|
|
|
return AtaReqSetFixedSenseData(Srb, SenseCode);
|
|
}
|
|
|
|
/* static */
|
|
UCHAR
|
|
AtaReqTerminateInvalidFieldParameter(
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
|
|
SenseCode.SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
|
SenseCode.SenseKey = SCSI_SENSE_ILLEGAL_REQUEST;
|
|
SenseCode.AdditionalSenseCode = SCSI_ADSENSE_INVALID_FIELD_PARAMETER_LIST;
|
|
SenseCode.AdditionalSenseCodeQualifier = 0;
|
|
|
|
return AtaReqSetFixedSenseData(Srb, SenseCode);
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqTerminateInvalidRange(
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
|
|
/*
|
|
* This normally should not happen and only used
|
|
* to aid in detecting bugs in the class drivers or software.
|
|
*/
|
|
ASSERT(FALSE);
|
|
|
|
SenseCode.SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
|
SenseCode.SenseKey = SCSI_SENSE_ILLEGAL_REQUEST;
|
|
SenseCode.AdditionalSenseCode = SCSI_ADSENSE_ILLEGAL_BLOCK;
|
|
SenseCode.AdditionalSenseCodeQualifier = 0;
|
|
|
|
return AtaReqSetFixedSenseData(Srb, SenseCode);
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaReqBuildIdentifyCommand(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
Request->Flags = REQUEST_FLAG_DATA_IN | REQUEST_FLAG_EXCLUSIVE | REQUEST_FLAG_POLL;
|
|
Request->DataBuffer = &DevExt->IdentifyDeviceData;
|
|
Request->DataTransferLength = sizeof(DevExt->IdentifyDeviceData);
|
|
|
|
if (!AtaReqAllocateMdl(Request))
|
|
return FALSE;
|
|
|
|
RtlZeroMemory(&Request->TaskFile, sizeof(Request->TaskFile));
|
|
Request->TaskFile.Command = IDE_COMMAND_IDENTIFY;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
AtaReqBuildReadLogTaskFile(
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ UCHAR LogAddress,
|
|
_In_ UCHAR PageNumber,
|
|
_In_ USHORT LogPageCount)
|
|
{
|
|
PATA_TASKFILE TaskFile = &Request->TaskFile;
|
|
|
|
/* PIO Data-In command */
|
|
Request->Flags = REQUEST_FLAG_DATA_IN | REQUEST_FLAG_LBA48;
|
|
Request->DataTransferLength = LogPageCount * IDE_GP_LOG_SECTOR_SIZE;
|
|
|
|
TaskFile->Feature = 0;
|
|
TaskFile->FeatureEx = 0;
|
|
|
|
/* LOG PAGE COUNT */
|
|
TaskFile->SectorCount = (UCHAR)LogPageCount;
|
|
TaskFile->SectorCountEx = (UCHAR)(LogPageCount >> 8);
|
|
|
|
TaskFile->LowLba = LogAddress; // LOG ADDRESS
|
|
TaskFile->MidLba = PageNumber; // PAGE NUMBER
|
|
TaskFile->HighLba = 0; // Reserved
|
|
TaskFile->LowLbaEx = 0; // Reserved
|
|
TaskFile->MidLbaEx = 0; // PAGE NUMBER EX
|
|
TaskFile->HighLbaEx = 0; // Reserved
|
|
TaskFile->Command = IDE_COMMAND_READ_LOG_EXT;
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaReqBuildLbaTaskFile(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ ULONG64 Lba,
|
|
_In_ ULONG SectorCount)
|
|
{
|
|
PATA_TASKFILE TaskFile = &Request->TaskFile;
|
|
UCHAR DriveSelect;
|
|
|
|
/* Sector count must not exceed the maximum transfer length */
|
|
ASSERT(SectorCount <= ATA_MAX_SECTORS_PER_IO);
|
|
|
|
if (SectorCount > ATA_MAX_SECTORS_PER_IO)
|
|
return FALSE;
|
|
|
|
Request->Flags |= REQUEST_FLAG_SET_DEVICE_REGISTER;
|
|
|
|
/* 28-bit or 48-bit command */
|
|
if (DevExt->Device.DeviceFlags & DEVICE_LBA_MODE)
|
|
{
|
|
DriveSelect = IDE_LBA_MODE;
|
|
|
|
TaskFile->Feature = 0;
|
|
TaskFile->SectorCount = (UCHAR)SectorCount;
|
|
TaskFile->LowLba = (UCHAR)Lba; // LBA bits 0-7
|
|
TaskFile->MidLba = (UCHAR)(Lba >> 8); // LBA bits 8-15
|
|
TaskFile->HighLba = (UCHAR)(Lba >> 16); // LBA bits 16-23
|
|
|
|
if ((DevExt->Device.DeviceFlags & DEVICE_LBA48) &&
|
|
((Request->Flags & REQUEST_FLAG_FUA) || AtaCommandUseLba48(Lba, SectorCount)))
|
|
{
|
|
if ((Lba + SectorCount) > ATA_MAX_LBA_48)
|
|
return FALSE;
|
|
|
|
/* 48-bit command */
|
|
TaskFile->FeatureEx = 0;
|
|
TaskFile->SectorCountEx = (UCHAR)(SectorCount >> 8);
|
|
TaskFile->LowLbaEx = (UCHAR)(Lba >> 24); // LBA bits 24-31
|
|
TaskFile->MidLbaEx = (UCHAR)(Lba >> 32); // LBA bits 32-39
|
|
TaskFile->HighLbaEx = (UCHAR)(Lba >> 40); // LBA bits 40-47
|
|
|
|
Request->Flags |= REQUEST_FLAG_LBA48;
|
|
}
|
|
else
|
|
{
|
|
if ((Lba + SectorCount) > ATA_MAX_LBA_28)
|
|
return FALSE;
|
|
|
|
/* 28-bit command */
|
|
DriveSelect |= ((Lba >> 24) & 0x0F); // LBA bits 24-27
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULONG ChsTemp, Cylinder, Head, Sector;
|
|
|
|
if ((Lba + SectorCount) > ATA_MAX_LBA_28)
|
|
return FALSE;
|
|
|
|
ChsTemp = (ULONG)Lba / DevExt->Device.SectorsPerTrack;
|
|
|
|
/* Legacy CHS translation */
|
|
Cylinder = ChsTemp / DevExt->Device.Heads;
|
|
Head = ChsTemp % DevExt->Device.Heads;
|
|
Sector = ((ULONG)Lba % DevExt->Device.SectorsPerTrack) + 1;
|
|
|
|
/* Check for the 137 GB limit */
|
|
if (Cylinder > 65535 || Head > 15 || Sector > 255)
|
|
return FALSE;
|
|
|
|
TaskFile->Feature = 0;
|
|
TaskFile->SectorCount = (UCHAR)SectorCount;
|
|
TaskFile->LowLba = (UCHAR)Sector;
|
|
TaskFile->MidLba = (UCHAR)Cylinder;
|
|
TaskFile->HighLba = (UCHAR)(Cylinder >> 8);
|
|
|
|
DriveSelect = Head;
|
|
}
|
|
TaskFile->DriveSelect = DevExt->Device.DeviceSelect | DriveSelect;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaReqBuildNcqReadWriteTaskFile(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ ULONG64 Lba,
|
|
_In_ ULONG SectorCount)
|
|
{
|
|
PATA_TASKFILE TaskFile = &Request->TaskFile;
|
|
|
|
ASSERT(DevExt->Device.PortData->PortFlags & PORT_FLAG_NCQ);
|
|
ASSERT(!(DevExt->Device.DeviceFlags & DEVICE_PIO_ONLY));
|
|
|
|
/* Sector count must not exceed the maximum transfer length */
|
|
ASSERT(SectorCount <= ATA_MAX_SECTORS_PER_IO);
|
|
|
|
if (SectorCount > ATA_MAX_SECTORS_PER_IO)
|
|
return FALSE;
|
|
|
|
if ((Lba + SectorCount) > ATA_MAX_LBA_48)
|
|
return FALSE;
|
|
|
|
Request->Flags |= REQUEST_FLAG_NCQ |
|
|
REQUEST_DMA_FLAGS |
|
|
REQUEST_FLAG_LBA48 |
|
|
REQUEST_FLAG_SET_DEVICE_REGISTER;
|
|
|
|
TaskFile->Feature = (UCHAR)SectorCount;
|
|
TaskFile->FeatureEx = (UCHAR)(SectorCount >> 8);
|
|
|
|
// TODO: RARC, PRIO, ICC, AUX
|
|
TaskFile->SectorCount = 0;
|
|
TaskFile->SectorCountEx = 0;
|
|
|
|
TaskFile->LowLba = (UCHAR)Lba; // LBA bits 0-7
|
|
TaskFile->MidLba = (UCHAR)(Lba >> 8); // LBA bits 8-15
|
|
TaskFile->HighLba = (UCHAR)(Lba >> 16); // LBA bits 16-23
|
|
TaskFile->LowLbaEx = (UCHAR)(Lba >> 24); // LBA bits 24-31
|
|
TaskFile->MidLbaEx = (UCHAR)(Lba >> 32); // LBA bits 32-39
|
|
TaskFile->HighLbaEx = (UCHAR)(Lba >> 40); // LBA bits 40-47
|
|
|
|
TaskFile->DriveSelect = IDE_LBA_MODE;
|
|
if (Request->Flags & REQUEST_FLAG_FUA)
|
|
TaskFile->DriveSelect |= IDE_DEVICE_FUA_NCQ;
|
|
|
|
if (Request->Flags & REQUEST_FLAG_DATA_OUT)
|
|
TaskFile->Command = IDE_COMMAND_WRITE_FPDMA_QUEUED;
|
|
else
|
|
TaskFile->Command = IDE_COMMAND_READ_FPDMA_QUEUED;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiReadWrite(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
PCDB Cdb;
|
|
ULONG64 Lba;
|
|
ULONG SectorCount;
|
|
|
|
Request->Flags = REQUEST_FLAG_READ_WRITE;
|
|
|
|
Cdb = (PCDB)Srb->Cdb;
|
|
switch (Cdb->AsByte[0])
|
|
{
|
|
case SCSIOP_READ6:
|
|
case SCSIOP_WRITE6:
|
|
{
|
|
Lba = CdbGetLogicalBlockAddress6(Cdb);
|
|
break;
|
|
}
|
|
|
|
case SCSIOP_READ:
|
|
case SCSIOP_WRITE:
|
|
{
|
|
Lba = CdbGetLogicalBlockAddress10(Cdb);
|
|
|
|
if (Cdb->CDB10.ForceUnitAccess)
|
|
Request->Flags |= REQUEST_FLAG_FUA;
|
|
break;
|
|
}
|
|
|
|
case SCSIOP_READ12:
|
|
case SCSIOP_WRITE12:
|
|
{
|
|
Lba = CdbGetLogicalBlockAddress12(Cdb);
|
|
|
|
if (Cdb->CDB12.ForceUnitAccess)
|
|
Request->Flags |= REQUEST_FLAG_FUA;
|
|
break;
|
|
}
|
|
|
|
case SCSIOP_READ16:
|
|
case SCSIOP_WRITE16:
|
|
{
|
|
Lba = CdbGetLogicalBlockAddress16(Cdb);
|
|
|
|
if (Cdb->CDB16.ForceUnitAccess)
|
|
Request->Flags |= REQUEST_FLAG_FUA;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
UNREACHABLE;
|
|
}
|
|
|
|
/* Check for write operations */
|
|
if (Cdb->AsByte[0] & 0x02)
|
|
Request->Flags |= REQUEST_FLAG_DATA_OUT;
|
|
else
|
|
Request->Flags |= REQUEST_FLAG_DATA_IN;
|
|
|
|
// FIXME: HACK Workaround for disk.sys which incorrectly enables FUA support
|
|
if (!(DevExt->Device.DeviceFlags & DEVICE_NCQ))
|
|
Request->Flags &= ~REQUEST_FLAG_FUA;
|
|
|
|
SectorCount = Request->DataTransferLength + (DevExt->Device.SectorSize - 1);
|
|
SectorCount /= DevExt->Device.SectorSize;
|
|
|
|
if (DevExt->Device.DeviceFlags & DEVICE_NCQ)
|
|
{
|
|
if (!AtaReqBuildNcqReadWriteTaskFile(DevExt, Request, Lba, SectorCount))
|
|
return AtaReqTerminateInvalidRange(Srb);
|
|
}
|
|
else
|
|
{
|
|
if (!AtaReqBuildLbaTaskFile(DevExt, Request, Lba, SectorCount))
|
|
return AtaReqTerminateInvalidRange(Srb);
|
|
|
|
if (!(DevExt->Device.DeviceFlags & DEVICE_PIO_ONLY) &&
|
|
!(SRB_GET_FLAGS(Srb) & SRB_FLAG_PIO_RETRY))
|
|
{
|
|
Request->Flags |= REQUEST_DMA_FLAGS;
|
|
}
|
|
else if (DevExt->Device.MultiSectorCount != 0)
|
|
{
|
|
Request->Flags |= REQUEST_FLAG_READ_WRITE_MULTIPLE;
|
|
}
|
|
|
|
/* Choose the command opcode */
|
|
Request->TaskFile.Command = AtaReadWriteCommand(Request, &DevExt->Device);
|
|
if (Request->TaskFile.Command == 0)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiSynchronizeCache(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
UCHAR Command;
|
|
|
|
if (!AtaDevIsVolatileWriteCacheEnabled(&DevExt->IdentifyDeviceData))
|
|
return SRB_STATUS_SUCCESS;
|
|
|
|
Command = AtaDeviceGetFlushCacheCommand(DevExt);
|
|
if (Command == 0)
|
|
return SRB_STATUS_SUCCESS;
|
|
|
|
/* Prepare a non-data command */
|
|
Request->TaskFile.Command = Command;
|
|
|
|
/* NOTE: This command may take longer than 30 seconds to complete */
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiVerify(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
PCDB Cdb;
|
|
ULONG64 Lba;
|
|
ULONG VerificationLength;
|
|
|
|
Cdb = (PCDB)Srb->Cdb;
|
|
|
|
/* Byte-by-byte comparison not supported */
|
|
if (Cdb->VERIFY16.ByteCheck || Cdb->VERIFY16.BlockVerify)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
/* Prepare a non-data command */
|
|
switch (Cdb->AsByte[0])
|
|
{
|
|
case SCSIOP_VERIFY:
|
|
{
|
|
Lba = CdbGetLogicalBlockAddress10(Cdb);
|
|
VerificationLength = CdbGetTransferLength10(Cdb);
|
|
break;
|
|
}
|
|
|
|
case SCSIOP_VERIFY12:
|
|
{
|
|
Lba = CdbGetLogicalBlockAddress12(Cdb);
|
|
VerificationLength = CdbGetTransferLength12(Cdb);
|
|
break;
|
|
}
|
|
|
|
case SCSIOP_VERIFY16:
|
|
{
|
|
Lba = CdbGetLogicalBlockAddress16(Cdb);
|
|
VerificationLength = CdbGetTransferLength16(Cdb);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
UNREACHABLE;
|
|
}
|
|
|
|
if (!AtaReqBuildLbaTaskFile(DevExt, Request, Lba, VerificationLength))
|
|
return AtaReqTerminateInvalidRange(Srb);
|
|
|
|
/* Choose the command opcode */
|
|
if (Request->Flags & REQUEST_FLAG_LBA48)
|
|
Request->TaskFile.Command = IDE_COMMAND_VERIFY_EXT;
|
|
else
|
|
Request->TaskFile.Command = IDE_COMMAND_VERIFY;
|
|
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
static
|
|
ATA_COMPLETION_ACTION
|
|
AtaReqCompleteReadCapacity(
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt;
|
|
PSCSI_REQUEST_BLOCK Srb = Request->Srb;
|
|
ULONG Length;
|
|
|
|
DevExt = CONTAINING_RECORD(Request->Device, ATAPORT_DEVICE_EXTENSION, Device);
|
|
|
|
AtaDeviceSetAddressingMode(DevExt);
|
|
|
|
if (DevExt->Device.TotalSectors == 1)
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
|
|
SenseCode.SrbStatus = SRB_STATUS_ERROR;
|
|
SenseCode.SenseKey = SCSI_SENSE_NOT_READY;
|
|
SenseCode.AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
|
|
SenseCode.AdditionalSenseCodeQualifier = 0;
|
|
|
|
Request->SrbStatus = AtaReqSetFixedSenseData(Srb, SenseCode);
|
|
return COMPLETE_IRP;
|
|
}
|
|
|
|
if (Srb->Cdb[0] == SCSIOP_READ_CAPACITY)
|
|
{
|
|
PREAD_CAPACITY_DATA CapacityData;
|
|
ULONG MaximumLba;
|
|
|
|
MaximumLba = min(DevExt->Device.TotalSectors - 1, MAXULONG);
|
|
|
|
CapacityData = DevExt->Device.LocalBuffer;
|
|
CapacityData->LogicalBlockAddress = RtlUlongByteSwap(MaximumLba);
|
|
CapacityData->BytesPerBlock = RtlUlongByteSwap(DevExt->Device.SectorSize);
|
|
|
|
Length = sizeof(*CapacityData);
|
|
}
|
|
else // SCSIOP_READ_CAPACITY16
|
|
{
|
|
PREAD_CAPACITY16_DATA CapacityData;
|
|
ULONG LogicalPerPhysicalExponent, LogicalSectorsPerPhysicalSector;
|
|
ULONG LogicalSectorAlignment, LowestAlignedBlock;
|
|
ULONG64 MaximumLba;
|
|
|
|
LogicalSectorAlignment = AtaDevLogicalSectorAlignment(&DevExt->IdentifyDeviceData);
|
|
if (LogicalSectorAlignment != 0)
|
|
{
|
|
LogicalSectorsPerPhysicalSector =
|
|
AtaDevLogicalSectorsPerPhysicalSector(&DevExt->IdentifyDeviceData,
|
|
&LogicalPerPhysicalExponent);
|
|
|
|
|
|
LowestAlignedBlock = (LogicalSectorsPerPhysicalSector - LogicalSectorAlignment);
|
|
LowestAlignedBlock %= LogicalSectorsPerPhysicalSector;
|
|
}
|
|
else
|
|
{
|
|
LowestAlignedBlock = 0;
|
|
}
|
|
|
|
MaximumLba = DevExt->Device.TotalSectors - 1;
|
|
|
|
CapacityData = DevExt->Device.LocalBuffer;
|
|
|
|
RtlZeroMemory(CapacityData, sizeof(*CapacityData));
|
|
CapacityData->LogicalBlockAddress.QuadPart = RtlUlonglongByteSwap(MaximumLba);
|
|
CapacityData->BytesPerBlock = RtlUlongByteSwap(DevExt->Device.SectorSize);
|
|
CapacityData->LogicalPerPhysicalExponent = LogicalPerPhysicalExponent;
|
|
CapacityData->LowestAlignedBlock_MSB = (UCHAR)(LowestAlignedBlock >> 8);
|
|
CapacityData->LowestAlignedBlock_LSB = (UCHAR)LowestAlignedBlock;
|
|
|
|
if (AtaDevHasTrimFunction(&DevExt->IdentifyDeviceData))
|
|
{
|
|
if (AtaDevHasDratFunction(&DevExt->IdentifyDeviceData))
|
|
{
|
|
CapacityData->LBPME = 1;
|
|
|
|
if (AtaDevHasRzatFunction(&DevExt->IdentifyDeviceData))
|
|
CapacityData->LBPRZ = 1;
|
|
}
|
|
}
|
|
|
|
Length = CdbGetAllocationLength16((PCDB)Srb->Cdb);
|
|
}
|
|
|
|
AtaReqCopySatlBuffer(Request, DevExt->Device.LocalBuffer, Length);
|
|
return COMPLETE_IRP;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiReadCapacity(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
if (Srb->Cdb[0] == SCSIOP_READ_CAPACITY)
|
|
{
|
|
if (Srb->DataTransferLength < sizeof(READ_CAPACITY_DATA))
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
else // SCSIOP_READ_CAPACITY16
|
|
{
|
|
if (Srb->DataTransferLength < CdbGetAllocationLength16((PCDB)Srb->Cdb))
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
|
|
/* Update the identify data */
|
|
if (!AtaReqBuildIdentifyCommand(DevExt, Request))
|
|
return SRB_STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Request->Complete = AtaReqCompleteReadCapacity;
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqControlModePage(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PVOID Buffer)
|
|
{
|
|
PMODE_CONTROL_PAGE PageData = Buffer;
|
|
|
|
PageData->PageCode = MODE_PAGE_CONTROL;
|
|
PageData->PageLength =
|
|
sizeof(PageData) - RTL_SIZEOF_THROUGH_FIELD(MODE_CONTROL_PAGE, PageLength);
|
|
|
|
PageData->QERR = 0;
|
|
PageData->QueueAlgorithmModifier = 1;
|
|
PageData->BusyTimeoutPeriod[0] = 0xFF;
|
|
PageData->BusyTimeoutPeriod[1] = 0xFF;
|
|
PageData->D_SENSE = !!(DevExt->Device.DeviceFlags & DEVICE_DESCRIPTOR_SENSE);
|
|
|
|
return sizeof(PageData);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqControlExtensionModePage(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PVOID Buffer)
|
|
{
|
|
PMODE_CONTROL_EXTENSION_PAGE PageData = Buffer;
|
|
|
|
PageData->SubPageFormat = 1;
|
|
PageData->SubPageCode = 0x00;
|
|
PageData->PageCode = MODE_PAGE_CONTROL;
|
|
PageData->PageLength[1] =
|
|
sizeof(PageData) - RTL_SIZEOF_THROUGH_FIELD(MODE_CONTROL_EXTENSION_PAGE, PageLength);
|
|
|
|
return sizeof(PageData);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqRwErrorRecoveryModePage(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PVOID Buffer)
|
|
{
|
|
PMODE_READ_WRITE_RECOVERY_PAGE PageData = Buffer;
|
|
|
|
PageData->PageCode = MODE_PAGE_ERROR_RECOVERY;
|
|
PageData->PageLength =
|
|
sizeof(PageData) - RTL_SIZEOF_THROUGH_FIELD(MODE_READ_WRITE_RECOVERY_PAGE, PageLength);
|
|
|
|
PageData->AWRE = 1;
|
|
|
|
return sizeof(PageData);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqCachingModePage(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PVOID Buffer)
|
|
{
|
|
PMODE_CACHING_PAGE_SPC5 PageData = Buffer;
|
|
|
|
PageData->PageCode = MODE_PAGE_CACHING;
|
|
PageData->PageLength =
|
|
sizeof(PageData) - RTL_SIZEOF_THROUGH_FIELD(MODE_CACHING_PAGE_SPC5, PageLength);
|
|
|
|
if (AtaDevIsVolatileWriteCacheEnabled(&DevExt->IdentifyDeviceData))
|
|
PageData->WriteCacheEnable = 1;
|
|
|
|
if (AtaDevIsReadLookAHeadEnabled(&DevExt->IdentifyDeviceData))
|
|
PageData->DisableReadAHead = 1;
|
|
|
|
return sizeof(PageData);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqInformationalExceptionsControlModePage(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PVOID Buffer)
|
|
{
|
|
PMODE_INFO_EXCEPTIONS PageData = Buffer;
|
|
|
|
PageData->PageCode = MODE_PAGE_FAULT_REPORTING;
|
|
PageData->PageLength =
|
|
sizeof(PageData) - RTL_SIZEOF_THROUGH_FIELD(MODE_INFO_EXCEPTIONS, PageLength);
|
|
|
|
/* Only report informational exception condition on request */
|
|
PageData->ReportMethod = 6;
|
|
|
|
return sizeof(PageData);
|
|
}
|
|
|
|
static
|
|
ATA_COMPLETION_ACTION
|
|
AtaReqCompleteModeSense(
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
PATAPORT_DEVICE_EXTENSION DevExt;
|
|
UCHAR PageCode;
|
|
USHORT ModeDataLength;
|
|
PUCHAR Buffer;
|
|
BOOLEAN Is6ByteCommand, IsWriteProtected;
|
|
PCDB Cdb;
|
|
union _HEADER
|
|
{
|
|
PMODE_PARAMETER_HEADER Header6;
|
|
PMODE_PARAMETER_HEADER10 Header10;
|
|
} ModeHeader;
|
|
|
|
DevExt = CONTAINING_RECORD(Request->Device, ATAPORT_DEVICE_EXTENSION, Device);
|
|
|
|
// TODO: Handle MSN
|
|
#if 0
|
|
/* Determine the media status */
|
|
if ((Request->TranslationState == 0) && (DevExt->Device.DeviceFlags & DEVICE_HAS_MEDIA_STATUS))
|
|
{
|
|
Request->Flags = 0;
|
|
Request->TaskFile.Command = IDE_COMMAND_GET_MEDIA_STATUS;
|
|
|
|
++Request->TranslationState;
|
|
return COMPLETE_START_AGAIN;
|
|
}
|
|
#endif
|
|
|
|
RtlZeroMemory(DevExt->Device.LocalBuffer, ATA_LOCAL_BUFFER_SIZE);
|
|
|
|
Cdb = (PCDB)Request->Srb->Cdb;
|
|
Is6ByteCommand = (Cdb->AsByte[0] == SCSIOP_MODE_SENSE);
|
|
|
|
Buffer = DevExt->Device.LocalBuffer;
|
|
|
|
/* Media status outputs */
|
|
if (Request->TranslationState != 0)
|
|
{
|
|
IsWriteProtected = (Request->Output.Status & IDE_STATUS_ERROR) &&
|
|
!(Request->Output.Error & IDE_ERROR_COMMAND_ABORTED) &&
|
|
(Request->Output.Error & IDE_ERROR_WRITE_PROTECT);
|
|
}
|
|
else
|
|
{
|
|
IsWriteProtected = FALSE;
|
|
}
|
|
|
|
ModeHeader.Header6 = (PMODE_PARAMETER_HEADER)Buffer;
|
|
|
|
/* Mode parameter header */
|
|
if (Is6ByteCommand)
|
|
{
|
|
if (IsWriteProtected)
|
|
ModeHeader.Header6->DeviceSpecificParameter |= MODE_DSP_WRITE_PROTECT;
|
|
if (DevExt->Device.DeviceFlags & DEVICE_HAS_FUA)
|
|
ModeHeader.Header6->DeviceSpecificParameter |= MODE_DSP_FUA_SUPPORTED;
|
|
|
|
Buffer += sizeof(*ModeHeader.Header6);
|
|
}
|
|
else
|
|
{
|
|
if (IsWriteProtected)
|
|
ModeHeader.Header10->DeviceSpecificParameter |= MODE_DSP_WRITE_PROTECT;
|
|
if (DevExt->Device.DeviceFlags & DEVICE_HAS_FUA)
|
|
ModeHeader.Header10->DeviceSpecificParameter |= MODE_DSP_FUA_SUPPORTED;
|
|
|
|
Buffer += sizeof(*ModeHeader.Header10);
|
|
}
|
|
|
|
/* Short LBA mode parameter block descriptor */
|
|
if (!Cdb->MODE_SENSE.Dbd)
|
|
{
|
|
PFORMAT_DESCRIPTOR FormatDescriptor;
|
|
|
|
if (Is6ByteCommand)
|
|
{
|
|
ModeHeader.Header6->BlockDescriptorLength = sizeof(*FormatDescriptor);
|
|
}
|
|
else
|
|
{
|
|
ModeHeader.Header10->BlockDescriptorLength[0] = (UCHAR)(sizeof(*FormatDescriptor) >> 8);
|
|
ModeHeader.Header10->BlockDescriptorLength[1] = (UCHAR)sizeof(*FormatDescriptor);
|
|
}
|
|
|
|
FormatDescriptor = (PFORMAT_DESCRIPTOR)Buffer;
|
|
FormatDescriptor->BlockLength[0] = (UCHAR)(DevExt->Device.SectorSize >> 16);
|
|
FormatDescriptor->BlockLength[1] = (UCHAR)(DevExt->Device.SectorSize >> 8);
|
|
FormatDescriptor->BlockLength[2] = (UCHAR)DevExt->Device.SectorSize;
|
|
|
|
Buffer += sizeof(*FormatDescriptor);
|
|
}
|
|
|
|
PageCode = Cdb->MODE_SENSE.PageCode;
|
|
|
|
/* Return SCSI mode pages */
|
|
if (PageCode == MODE_PAGE_CONTROL || PageCode == MODE_SENSE_RETURN_ALL)
|
|
{
|
|
UCHAR SubPageCode = Cdb->MODE_SENSE.Reserved3;
|
|
|
|
if (SubPageCode == 0x00 || SubPageCode == 0xFF)
|
|
Buffer += AtaReqControlModePage(DevExt, Buffer);
|
|
if (SubPageCode == 0x01 || SubPageCode == 0xFF)
|
|
Buffer += AtaReqControlExtensionModePage(DevExt, Buffer);
|
|
}
|
|
if (PageCode == MODE_PAGE_ERROR_RECOVERY || PageCode == MODE_SENSE_RETURN_ALL)
|
|
{
|
|
Buffer += AtaReqRwErrorRecoveryModePage(DevExt, Buffer);
|
|
}
|
|
if (PageCode == MODE_PAGE_CACHING || PageCode == MODE_SENSE_RETURN_ALL)
|
|
{
|
|
Buffer += AtaReqCachingModePage(DevExt, Buffer);
|
|
}
|
|
if (PageCode == MODE_PAGE_FAULT_REPORTING || PageCode == MODE_SENSE_RETURN_ALL)
|
|
{
|
|
Buffer += AtaReqInformationalExceptionsControlModePage(DevExt, Buffer);
|
|
}
|
|
|
|
ModeDataLength = Buffer - (PUCHAR)DevExt->Device.LocalBuffer;
|
|
|
|
if (Is6ByteCommand)
|
|
{
|
|
ModeDataLength -= RTL_SIZEOF_THROUGH_FIELD(MODE_PARAMETER_HEADER, MediumType);
|
|
|
|
ModeHeader.Header6->ModeDataLength = ModeDataLength;
|
|
}
|
|
else
|
|
{
|
|
ModeDataLength -= RTL_SIZEOF_THROUGH_FIELD(MODE_PARAMETER_HEADER10, MediumType);
|
|
|
|
ModeHeader.Header10->ModeDataLength[0] = (UCHAR)(ModeDataLength >> 8);
|
|
ModeHeader.Header10->ModeDataLength[1] = (UCHAR)ModeDataLength;
|
|
}
|
|
|
|
AtaReqCopySatlBuffer(Request,
|
|
DevExt->Device.LocalBuffer,
|
|
Buffer - (PUCHAR)DevExt->Device.LocalBuffer);
|
|
return COMPLETE_IRP;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiModeSense(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_In_ BOOLEAN Is6ByteCommand)
|
|
{
|
|
PCDB Cdb = (PCDB)Srb->Cdb;
|
|
|
|
if (Cdb->MODE_SENSE.Pc == MODE_SENSE_SAVED_VALUES)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
switch (Cdb->MODE_SENSE.PageCode)
|
|
{
|
|
case MODE_PAGE_FAULT_REPORTING:
|
|
{
|
|
if (!AtaDevHasSmartFeature(&DevExt->IdentifyDeviceData))
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
break;
|
|
}
|
|
|
|
case MODE_PAGE_CONTROL:
|
|
{
|
|
UCHAR SubPageCode = Cdb->MODE_SENSE.Reserved3;
|
|
|
|
if (SubPageCode != 0x00 && SubPageCode != 0x01 && SubPageCode != 0xFF)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
break;
|
|
}
|
|
|
|
case MODE_PAGE_ERROR_RECOVERY:
|
|
case MODE_PAGE_POWER_CONDITION:
|
|
case MODE_PAGE_CACHING:
|
|
case MODE_SENSE_RETURN_ALL:
|
|
break;
|
|
|
|
default:
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
|
|
/* Update the identify data */
|
|
if (!AtaReqBuildIdentifyCommand(DevExt, Request))
|
|
return SRB_STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Request->Complete = AtaReqCompleteModeSense;
|
|
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiModeSelect(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_In_ BOOLEAN Is6ByteCommand)
|
|
{
|
|
// TODO: Implement
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
AtaCreateStandardInquiryData(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PIDENTIFY_DEVICE_DATA IdentifyData = &DevExt->IdentifyDeviceData;
|
|
PINQUIRYDATA InquiryData = &DevExt->InquiryData;
|
|
ULONG Offset;
|
|
PUCHAR VersionDescriptor;
|
|
|
|
PAGED_CODE();
|
|
|
|
InquiryData->DeviceType = DIRECT_ACCESS_DEVICE;
|
|
InquiryData->RemovableMedia = AtaDevIsRemovable(IdentifyData);
|
|
InquiryData->Versions = 0x07; // SPC-5
|
|
InquiryData->ResponseDataFormat = 2; // This means "Complies to this standard"
|
|
|
|
if (DevExt->Device.DeviceFlags & DEVICE_NCQ)
|
|
InquiryData->CommandQueue = 1;
|
|
|
|
/* T10 vendor ID */
|
|
RtlCopyMemory(InquiryData->VendorId, "ATA ", RTL_FIELD_SIZE(INQUIRYDATA, VendorId));
|
|
|
|
/* Product ID */
|
|
AtaCopyIdStringUnsafe(InquiryData->ProductId,
|
|
IdentifyData->ModelNumber,
|
|
RTL_FIELD_SIZE(INQUIRYDATA, ProductId));
|
|
|
|
/* Product revision level */
|
|
if (IdentifyData->FirmwareRevision[4] == ' ' &&
|
|
IdentifyData->FirmwareRevision[5] == ' ' &&
|
|
IdentifyData->FirmwareRevision[6] == ' ' &&
|
|
IdentifyData->FirmwareRevision[7] == ' ')
|
|
{
|
|
Offset = 0;
|
|
}
|
|
else
|
|
{
|
|
Offset = 4;
|
|
}
|
|
AtaCopyIdStringUnsafe(InquiryData->ProductRevisionLevel,
|
|
&IdentifyData->FirmwareRevision[Offset],
|
|
RTL_FIELD_SIZE(INQUIRYDATA, ProductRevisionLevel));
|
|
|
|
VersionDescriptor = &InquiryData->Reserved3[3];
|
|
|
|
/* SAM-5 (no version claimed) */
|
|
*VersionDescriptor++ = 0x00;
|
|
*VersionDescriptor++ = 0xA0;
|
|
|
|
/* SPC-5 (no version claimed) */
|
|
*VersionDescriptor++ = 0x05;
|
|
*VersionDescriptor++ = 0xC0;
|
|
|
|
/* SBC-4 (no version claimed) */
|
|
*VersionDescriptor++ = 0x06;
|
|
*VersionDescriptor++ = 0x00;
|
|
|
|
if (AtaDevHasIeee1667(IdentifyData))
|
|
{
|
|
/* IEEE 1667 (no version claimed) */
|
|
*VersionDescriptor++ = 0xFF;
|
|
*VersionDescriptor++ = 0xC0;
|
|
}
|
|
|
|
if (IdentifyData->MajorRevision != 0xFFFF)
|
|
{
|
|
if (IdentifyData->MajorRevision & (1 << 8))
|
|
{
|
|
/* ATA/ATAPI-8 ATA8-ACS ATA/ATAPI Command Set (no version claimed) */
|
|
*VersionDescriptor++ = 0x16;
|
|
*VersionDescriptor++ = 0x23;
|
|
}
|
|
else if (IdentifyData->MajorRevision & (1 << 7))
|
|
{
|
|
if (IdentifyData->MinorRevision == 0x1D)
|
|
{
|
|
/* ATA/ATAPI-7 INCITS 397-2005 */
|
|
*VersionDescriptor++ = 0x16;
|
|
*VersionDescriptor++ = 0x1C;
|
|
}
|
|
else
|
|
{
|
|
/* ATA/ATAPI-7 (no version claimed) */
|
|
*VersionDescriptor++ = 0x16;
|
|
*VersionDescriptor++ = 0x00;
|
|
}
|
|
}
|
|
else if (IdentifyData->MajorRevision & (1 << 6))
|
|
{
|
|
if (IdentifyData->MinorRevision == 0x22)
|
|
{
|
|
/* ATA/ATAPI-6 INCITS 361-2002 */
|
|
*VersionDescriptor++ = 0x15;
|
|
*VersionDescriptor++ = 0xFD;
|
|
}
|
|
else
|
|
{
|
|
/* ATA/ATAPI-6 (no version claimed) */
|
|
*VersionDescriptor++ = 0x15;
|
|
*VersionDescriptor++ = 0xE0;
|
|
}
|
|
}
|
|
}
|
|
|
|
InquiryData->AdditionalLength =
|
|
((ULONG_PTR)VersionDescriptor - (ULONG_PTR)InquiryData) -
|
|
RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
|
|
|
|
TRACE("VendorId: '%.*s'\n",
|
|
RTL_FIELD_SIZE(INQUIRYDATA, VendorId), InquiryData->VendorId);
|
|
TRACE("ProductId: '%.*s'\n",
|
|
RTL_FIELD_SIZE(INQUIRYDATA, ProductId), InquiryData->ProductId);
|
|
TRACE("ProductRevisionLevel: '%.*s'\n",
|
|
RTL_FIELD_SIZE(INQUIRYDATA, ProductRevisionLevel), InquiryData->ProductRevisionLevel);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqScsiReportLuns(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
PCDB Cdb = (PCDB)Srb->Cdb;
|
|
PLUN_LIST LunList;
|
|
ULONG Length, LunListLength;
|
|
|
|
Length = Cdb->REPORT_LUNS.AllocationLength[0] << 24 |
|
|
Cdb->REPORT_LUNS.AllocationLength[1] << 16 |
|
|
Cdb->REPORT_LUNS.AllocationLength[2] << 8 |
|
|
Cdb->REPORT_LUNS.AllocationLength[3];
|
|
|
|
if (Srb->DataTransferLength < Length)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
LunList = DevExt->Device.LocalBuffer;
|
|
LunListLength = RTL_FIELD_SIZE(LUN_LIST, Lun[0]);
|
|
|
|
RtlZeroMemory(LunList, sizeof(*LunList));
|
|
LunList->LunListLength[0] = (UCHAR)(LunListLength >> 24);
|
|
LunList->LunListLength[1] = (UCHAR)(LunListLength >> 16);
|
|
LunList->LunListLength[2] = (UCHAR)(LunListLength >> 8);
|
|
LunList->LunListLength[3] = (UCHAR)LunListLength;
|
|
|
|
return AtaReqCopySatlBuffer(Request, DevExt->Device.LocalBuffer, Length);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqScsiInquirySupportedPages(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PVPD_SUPPORTED_PAGES_PAGE SupportedPages = DevExt->Device.LocalBuffer;
|
|
ULONG i = 0;
|
|
|
|
RtlZeroMemory(SupportedPages, sizeof(*SupportedPages));
|
|
SupportedPages->SupportedPageList[i++] = VPD_SUPPORTED_PAGES;
|
|
SupportedPages->SupportedPageList[i++] = VPD_SERIAL_NUMBER;
|
|
SupportedPages->SupportedPageList[i++] = VPD_DEVICE_IDENTIFIERS;
|
|
//SupportedPages->SupportedPageList[i++] = VPD_EXTENDED_INQUIRY_DATA; // TODO: Implement
|
|
// SupportedPages->SupportedPageList[i++] = VPD_ATA_INFORMATION; // TODO: Implement
|
|
SupportedPages->SupportedPageList[i++] = VPD_BLOCK_LIMITS;
|
|
SupportedPages->SupportedPageList[i++] = VPD_BLOCK_DEVICE_CHARACTERISTICS;
|
|
SupportedPages->SupportedPageList[i++] = VPD_LOGICAL_BLOCK_PROVISIONING;
|
|
SupportedPages->PageLength = i;
|
|
|
|
return sizeof(*SupportedPages) + i;
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqScsiInquirySerialNumber(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PVPD_SERIAL_NUMBER_PAGE SerialNumberPage = DevExt->Device.LocalBuffer;
|
|
|
|
RtlZeroMemory(SerialNumberPage, FIELD_OFFSET(VPD_SERIAL_NUMBER_PAGE, SerialNumber));
|
|
SerialNumberPage->PageLength = RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, SerialNumber);
|
|
|
|
AtaCopyIdStringUnsafe(SerialNumberPage->SerialNumber,
|
|
DevExt->IdentifyDeviceData.SerialNumber,
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, SerialNumber));
|
|
|
|
return FIELD_OFFSET(VPD_SERIAL_NUMBER_PAGE, SerialNumber) +
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, SerialNumber);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqScsiInquiryDeviceIdentifiers(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PVPD_IDENTIFICATION_PAGE DeviceIdentificationPage = DevExt->Device.LocalBuffer;
|
|
PVPD_IDENTIFICATION_DESCRIPTOR Descriptor;
|
|
UCHAR PageLength;
|
|
|
|
PageLength = FIELD_OFFSET(VPD_IDENTIFICATION_PAGE, Descriptors) +
|
|
FIELD_OFFSET(VPD_IDENTIFICATION_DESCRIPTOR, Identifier);
|
|
|
|
if (AtaDevHasWorldWideName(&DevExt->IdentifyDeviceData))
|
|
{
|
|
PageLength += RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, WorldWideName);
|
|
}
|
|
else
|
|
{
|
|
PageLength += RTL_FIELD_SIZE(INQUIRYDATA, VendorId) +
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, ModelNumber) +
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, SerialNumber);
|
|
}
|
|
|
|
RtlZeroMemory(DeviceIdentificationPage, PageLength);
|
|
DeviceIdentificationPage->PageLength =
|
|
PageLength - RTL_SIZEOF_THROUGH_FIELD(VPD_IDENTIFICATION_PAGE, PageLength);
|
|
|
|
Descriptor = (PVPD_IDENTIFICATION_DESCRIPTOR)&DeviceIdentificationPage->Descriptors[0];
|
|
|
|
if (AtaDevHasWorldWideName(&DevExt->IdentifyDeviceData))
|
|
{
|
|
/* NAA descriptor */
|
|
Descriptor->CodeSet = VpdCodeSetBinary;
|
|
Descriptor->IdentifierType = VpdIdentifierTypeFCPHName;
|
|
Descriptor->IdentifierLength = RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, WorldWideName);
|
|
|
|
AtaCopyIdStringUnsafe(Descriptor->Identifier,
|
|
(PUCHAR)DevExt->IdentifyDeviceData.WorldWideName,
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, WorldWideName));
|
|
}
|
|
else
|
|
{
|
|
PUCHAR Identifier;
|
|
|
|
/* T10 vendor ID based descriptor */
|
|
Descriptor->CodeSet = VpdCodeSetAscii;
|
|
Descriptor->IdentifierType = VpdIdentifierTypeVendorId;
|
|
Descriptor->IdentifierLength = RTL_FIELD_SIZE(INQUIRYDATA, VendorId) +
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, ModelNumber) +
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, SerialNumber);
|
|
|
|
Identifier = &Descriptor->Identifier[0];
|
|
|
|
/* T10 vendor ID */
|
|
RtlCopyMemory(Identifier, "ATA ", RTL_FIELD_SIZE(INQUIRYDATA, VendorId));
|
|
Identifier += RTL_FIELD_SIZE(INQUIRYDATA, VendorId);
|
|
|
|
/* Model number field */
|
|
AtaCopyIdStringUnsafe(Identifier,
|
|
DevExt->IdentifyDeviceData.ModelNumber,
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, ModelNumber));
|
|
Identifier += RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, ModelNumber);
|
|
|
|
/* Serial number field */
|
|
AtaCopyIdStringUnsafe(Identifier,
|
|
DevExt->IdentifyDeviceData.SerialNumber,
|
|
RTL_FIELD_SIZE(IDENTIFY_DEVICE_DATA, SerialNumber));
|
|
}
|
|
|
|
return PageLength + RTL_SIZEOF_THROUGH_FIELD(VPD_IDENTIFICATION_PAGE, PageLength);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqScsiInquiryBlockLimits(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PVPD_BLOCK_LIMITS_PAGE BlockLimitsPage = DevExt->Device.LocalBuffer;
|
|
USHORT PageLength;
|
|
|
|
PageLength = sizeof(*BlockLimitsPage) -
|
|
RTL_SIZEOF_THROUGH_FIELD(VPD_BLOCK_LIMITS_PAGE, PageLength);
|
|
|
|
RtlZeroMemory(BlockLimitsPage, sizeof(*BlockLimitsPage));
|
|
BlockLimitsPage->PageLength[0] = (UCHAR)(PageLength >> 8);
|
|
BlockLimitsPage->PageLength[1] = (UCHAR)PageLength;
|
|
|
|
// TODO: Implement
|
|
#if 0
|
|
if (AtaDevHasTrimFunction(&DevExt->IdentifyDeviceData))
|
|
{
|
|
BlockLimitsPage->MaximumUnmapLBACount[0] = 0;
|
|
BlockLimitsPage->MaximumUnmapLBACount[1] = 0;
|
|
BlockLimitsPage->MaximumUnmapLBACount[2] = 0;
|
|
BlockLimitsPage->MaximumUnmapLBACount[4] = 0;
|
|
|
|
BlockLimitsPage->MaximumUnmapBlockDescriptorCount[0] = 0;
|
|
BlockLimitsPage->MaximumUnmapBlockDescriptorCount[1] = 0;
|
|
BlockLimitsPage->MaximumUnmapBlockDescriptorCount[2] = 0;
|
|
BlockLimitsPage->MaximumUnmapBlockDescriptorCount[3] = 0;
|
|
}
|
|
#endif
|
|
|
|
return sizeof(*BlockLimitsPage);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqScsiInquiryBlockDeviceCharacteristics(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE Characteristics = DevExt->Device.LocalBuffer;
|
|
USHORT MediumRotationRate;
|
|
|
|
RtlZeroMemory(Characteristics, sizeof(*Characteristics));
|
|
Characteristics->PageLength =
|
|
sizeof(*Characteristics) -
|
|
RTL_SIZEOF_THROUGH_FIELD(VPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE, PageLength);
|
|
|
|
MediumRotationRate = AtaDevMediumRotationRate(&DevExt->IdentifyDeviceData);
|
|
Characteristics->MediumRotationRateMsb = (UCHAR)(MediumRotationRate >> 8);
|
|
Characteristics->MediumRotationRateLsb = (UCHAR)MediumRotationRate;
|
|
Characteristics->NominalFormFactor = AtaDevNominalFormFactor(&DevExt->IdentifyDeviceData);
|
|
Characteristics->ZONED = AtaDevZonedCapabilities(&DevExt->IdentifyDeviceData);
|
|
return sizeof(*Characteristics);
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
AtaReqScsiInquiryLogicalBlockProvisioning(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt)
|
|
{
|
|
PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE LogicalBlockProvisioningPage = DevExt->Device.LocalBuffer;
|
|
|
|
RtlZeroMemory(LogicalBlockProvisioningPage,
|
|
FIELD_OFFSET(VPD_LOGICAL_BLOCK_PROVISIONING_PAGE, ProvisioningGroupDescr));
|
|
LogicalBlockProvisioningPage->PageLength[1] =
|
|
FIELD_OFFSET(VPD_LOGICAL_BLOCK_PROVISIONING_PAGE, ProvisioningGroupDescr) -
|
|
RTL_SIZEOF_THROUGH_FIELD(VPD_LOGICAL_BLOCK_PROVISIONING_PAGE, PageLength);
|
|
|
|
if (AtaDevHasTrimFunction(&DevExt->IdentifyDeviceData))
|
|
{
|
|
// TODO: Implement
|
|
#if 0
|
|
LogicalBlockProvisioningPage->LBPU = 1; // UNMAP
|
|
LogicalBlockProvisioningPage->LBPWS = 1; // WRITE SAME (16) + UNMAP
|
|
LogicalBlockProvisioningPage->LBPWS10 = 1; // WRITE SAME (10) + UNMAP
|
|
#endif
|
|
|
|
if (AtaDevHasDratFunction(&DevExt->IdentifyDeviceData))
|
|
LogicalBlockProvisioningPage->ANC_SUP = 1;
|
|
|
|
if (AtaDevHasRzatFunction(&DevExt->IdentifyDeviceData))
|
|
LogicalBlockProvisioningPage->LBPRZ = 1;
|
|
}
|
|
|
|
return FIELD_OFFSET(VPD_LOGICAL_BLOCK_PROVISIONING_PAGE, ProvisioningGroupDescr);
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiInquiry(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
PVPD_SUPPORTED_PAGES_PAGE Buffer;
|
|
PCDB Cdb = (PCDB)Srb->Cdb;
|
|
ULONG Length;
|
|
|
|
/* Return the standard INQUIRY data */
|
|
if (!Cdb->CDB6INQUIRY3.EnableVitalProductData)
|
|
{
|
|
if (Cdb->CDB6INQUIRY3.PageCode != 0)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
return AtaReqCopySatlBuffer(Request,
|
|
&DevExt->InquiryData,
|
|
sizeof(DevExt->InquiryData));
|
|
}
|
|
|
|
switch (Cdb->CDB6INQUIRY3.PageCode)
|
|
{
|
|
case VPD_SUPPORTED_PAGES:
|
|
{
|
|
Length = AtaReqScsiInquirySupportedPages(DevExt);
|
|
break;
|
|
}
|
|
case VPD_SERIAL_NUMBER:
|
|
{
|
|
Length = AtaReqScsiInquirySerialNumber(DevExt);
|
|
break;
|
|
}
|
|
case VPD_DEVICE_IDENTIFIERS:
|
|
{
|
|
Length = AtaReqScsiInquiryDeviceIdentifiers(DevExt);
|
|
break;
|
|
}
|
|
case VPD_EXTENDED_INQUIRY_DATA:
|
|
{
|
|
// TODO: Implement
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
case VPD_ATA_INFORMATION:
|
|
{
|
|
// TODO: Implement
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
case VPD_BLOCK_LIMITS:
|
|
{
|
|
Length = AtaReqScsiInquiryBlockLimits(DevExt);
|
|
break;
|
|
}
|
|
case VPD_BLOCK_DEVICE_CHARACTERISTICS:
|
|
{
|
|
Length = AtaReqScsiInquiryBlockDeviceCharacteristics(DevExt);
|
|
break;
|
|
}
|
|
case VPD_LOGICAL_BLOCK_PROVISIONING:
|
|
{
|
|
Length = AtaReqScsiInquiryLogicalBlockProvisioning(DevExt);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
|
|
/* Data bytes common to all VPD pages */
|
|
Buffer = DevExt->Device.LocalBuffer;
|
|
Buffer->DeviceType = DevExt->InquiryData.DeviceType;
|
|
Buffer->DeviceTypeQualifier = DevExt->InquiryData.DeviceTypeQualifier;
|
|
Buffer->PageCode = Cdb->CDB6INQUIRY3.PageCode;
|
|
|
|
return AtaReqCopySatlBuffer(Request, DevExt->Device.LocalBuffer, Length);
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiTestUnitReady(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
return SRB_STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiMediumRemoval(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
PCDB Cdb = (PCDB)Srb->Cdb;
|
|
|
|
if (AtaDevHasRemovableMediaFeature(&DevExt->IdentifyDeviceData))
|
|
{
|
|
if (Cdb->MEDIA_REMOVAL.Prevent)
|
|
Request->TaskFile.Command = IDE_COMMAND_DOOR_LOCK;
|
|
else
|
|
Request->TaskFile.Command = IDE_COMMAND_DOOR_UNLOCK;
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
/* Pretend success */
|
|
return SRB_STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiStartStopUnit(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
PCDB Cdb = (PCDB)Srb->Cdb;
|
|
|
|
if (Cdb->START_STOP.LoadEject)
|
|
{
|
|
Request->TaskFile.Command = IDE_COMMAND_MEDIA_EJECT;
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
/* Adding more handling will conflict with the power manager, assume success */
|
|
return SRB_STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiRequestSense(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
SCSI_SENSE_CODE SenseCode;
|
|
|
|
SenseCode.SrbStatus = SRB_STATUS_SUCCESS;
|
|
SenseCode.SenseKey = SCSI_SENSE_NO_SENSE;
|
|
SenseCode.AdditionalSenseCode = SCSI_ADSENSE_NO_SENSE;
|
|
SenseCode.AdditionalSenseCodeQualifier = 0;
|
|
|
|
return AtaReqSetFixedSenseData(Srb, SenseCode);
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
AtaReqFilterPassThrough(
|
|
_In_ PATA_TASKFILE TaskFile)
|
|
{
|
|
switch (TaskFile->Command)
|
|
{
|
|
case IDE_COMMAND_SET_FEATURE:
|
|
{
|
|
if (TaskFile->Feature == IDE_FEATURE_SET_TRANSFER_MODE)
|
|
return FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
case IDE_COMMAND_SET_DRIVE_PARAMETERS:
|
|
case IDE_COMMAND_SET_MULTIPLE:
|
|
return FALSE;
|
|
|
|
// TODO: Anything else to check?
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
AtaReqSetAtaStatusSenseData(
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ UCHAR SK,
|
|
_In_ UCHAR ASK,
|
|
_In_ UCHAR ASCQ)
|
|
{
|
|
PATA_TASKFILE TaskFile = &Request->Output;
|
|
PSCSI_REQUEST_BLOCK Srb = Request->Srb;
|
|
PDESCRIPTOR_SENSE_DATA SenseData;
|
|
ULONG BufferLength;
|
|
PSCSI_SENSE_DESCRIPTOR_ATA_STATUS_RETURN Descriptor;
|
|
|
|
Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
|
|
|
|
BufferLength = FIELD_OFFSET(DESCRIPTOR_SENSE_DATA, DescriptorBuffer);
|
|
BufferLength += sizeof(*Descriptor);
|
|
if (Srb->SenseInfoBufferLength < BufferLength)
|
|
return;
|
|
|
|
SenseData = Srb->SenseInfoBuffer;
|
|
RtlZeroMemory(SenseData, BufferLength);
|
|
|
|
/* Descriptor format sense data */
|
|
SenseData->ErrorCode = SCSI_SENSE_ERRORCODE_DESCRIPTOR_CURRENT;
|
|
SenseData->SenseKey = SK;
|
|
SenseData->AdditionalSenseCode = ASK;
|
|
SenseData->AdditionalSenseCodeQualifier = ASCQ;
|
|
|
|
SenseData->AdditionalSenseLength =
|
|
BufferLength - RTL_SIZEOF_THROUGH_FIELD(DESCRIPTOR_SENSE_DATA, AdditionalSenseLength);
|
|
|
|
/* ATA Status Return sense data descriptor */
|
|
Descriptor = (PSCSI_SENSE_DESCRIPTOR_ATA_STATUS_RETURN)&SenseData->DescriptorBuffer[0];
|
|
Descriptor->Header.DescriptorType = SCSI_SENSE_DESCRIPTOR_TYPE_ATA_STATUS_RETURN;
|
|
Descriptor->Header.AdditionalLength = 0x0C;
|
|
|
|
/* Return the 28-bit extended status */
|
|
Descriptor->Error = TaskFile->Error;
|
|
Descriptor->Device = TaskFile->DriveSelect;
|
|
Descriptor->Status = TaskFile->Status;
|
|
Descriptor->SectorCount7_0 = TaskFile->SectorCount;
|
|
Descriptor->LbaLow7_0 = TaskFile->LowLba;
|
|
Descriptor->LbaMid7_0 = TaskFile->MidLba;
|
|
Descriptor->LbaHigh7_0 = TaskFile->HighLba;
|
|
Descriptor->LbaLow15_8 = TaskFile->DriveSelect & 0x0F;
|
|
|
|
/* Return the 48-bit extended status */
|
|
if (Request->Flags & REQUEST_FLAG_LBA48)
|
|
{
|
|
Descriptor->Extend = 1;
|
|
Descriptor->SectorCount15_8 = TaskFile->SectorCountEx;
|
|
Descriptor->LbaLow15_8 = TaskFile->LowLbaEx;
|
|
Descriptor->LbaMid15_8 = TaskFile->MidLba;
|
|
Descriptor->LbaHigh15_8 = TaskFile->HighLbaEx;
|
|
}
|
|
|
|
Request->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
|
|
}
|
|
|
|
static
|
|
ATA_COMPLETION_ACTION
|
|
AtaReqCompleteAtaPassThrough(
|
|
_In_ PATA_DEVICE_REQUEST Request)
|
|
{
|
|
PCDB Cdb = (PCDB)Request->Srb->Cdb;
|
|
|
|
if (!(Request->Flags & REQUEST_FLAG_HAS_TASK_FILE))
|
|
return COMPLETE_IRP;
|
|
|
|
if ((Cdb->ATA_PASSTHROUGH16.CkCond == 1) || (Request->SrbStatus != SRB_STATUS_SUCCESS))
|
|
{
|
|
UCHAR SK, ASK, ASCQ;
|
|
|
|
if (Request->SrbStatus != SRB_STATUS_SUCCESS)
|
|
{
|
|
AtaReqTranslateFixedError(Request, &SK, &ASK, &ASCQ);
|
|
}
|
|
else
|
|
{
|
|
SK = SCSI_SENSE_RECOVERED_ERROR;
|
|
ASK = SCSI_ADSENSE_NO_SENSE;
|
|
ASCQ = SCSI_SENSEQ_ATA_PASS_THROUGH_INFORMATION_AVAILABLE;
|
|
}
|
|
|
|
AtaReqSetAtaStatusSenseData(Request, SK, ASK, ASCQ);
|
|
}
|
|
|
|
return COMPLETE_IRP;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqScsiAtaPassThrough(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
PATA_TASKFILE TaskFile = &Request->TaskFile;
|
|
PCDB Cdb;
|
|
|
|
Request->Flags = REQUEST_FLAG_SET_DEVICE_REGISTER |
|
|
REQUEST_FLAG_SAVE_TASK_FILE |
|
|
REQUEST_FLAG_PASSTHROUGH |
|
|
(Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION);
|
|
|
|
Cdb = (PCDB)Srb->Cdb;
|
|
switch (Cdb->ATA_PASSTHROUGH16.Protocol)
|
|
{
|
|
case ATA_PASSTHROUGH_PROTOCOL_NON_DATA:
|
|
{
|
|
/* Check for data transfer */
|
|
if (Cdb->ATA_PASSTHROUGH16.TLength != 0)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
break;
|
|
}
|
|
|
|
case ATA_PASSTHROUGH_PROTOCOL_PIO_DATA_IN:
|
|
{
|
|
/* Check for write operation */
|
|
if (!Cdb->ATA_PASSTHROUGH16.TDir)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
break;
|
|
}
|
|
|
|
case ATA_PASSTHROUGH_PROTOCOL_PIO_DATA_OUT:
|
|
{
|
|
/* Check for read operation */
|
|
if (Cdb->ATA_PASSTHROUGH16.TDir)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
break;
|
|
}
|
|
|
|
case ATA_PASSTHROUGH_PROTOCOL_DMA:
|
|
case ATA_PASSTHROUGH_PROTOCOL_UDMA_DATA_IN:
|
|
case ATA_PASSTHROUGH_PROTOCOL_UDMA_DATA_OUT:
|
|
{
|
|
if (DevExt->Device.DeviceFlags & DEVICE_PIO_ONLY)
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
Request->Flags |= REQUEST_DMA_FLAGS;
|
|
break;
|
|
}
|
|
|
|
case ATA_PASSTHROUGH_PROTOCOL_NCQ:
|
|
{
|
|
if (!(DevExt->Device.DeviceFlags & DEVICE_NCQ))
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
/*
|
|
* Do not set the REQUEST_FLAG_LBA48 flag.
|
|
* We can get here even when the command is SCSIOP_ATA_PASSTHROUGH12.
|
|
*/
|
|
Request->Flags |= REQUEST_FLAG_NCQ | REQUEST_DMA_FLAGS;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
}
|
|
|
|
/*
|
|
* NOTE: We ignore the T_LENGTH field in the CDB
|
|
* and use the DataTransferLength field instead.
|
|
*/
|
|
if ((Cdb->ATA_PASSTHROUGH16.TLength == 0) && (Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION))
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
if (Srb->Cdb[0] == SCSIOP_ATA_PASSTHROUGH12)
|
|
{
|
|
TaskFile->Feature = Cdb->ATA_PASSTHROUGH12.Features;
|
|
TaskFile->SectorCount = Cdb->ATA_PASSTHROUGH12.SectorCount;
|
|
TaskFile->LowLba = Cdb->ATA_PASSTHROUGH12.LbaLow;
|
|
TaskFile->MidLba = Cdb->ATA_PASSTHROUGH12.LbaMid;
|
|
TaskFile->HighLba = Cdb->ATA_PASSTHROUGH12.LbaHigh;
|
|
TaskFile->DriveSelect = Cdb->ATA_PASSTHROUGH12.Device;
|
|
TaskFile->Command = Cdb->ATA_PASSTHROUGH12.Command;
|
|
}
|
|
else
|
|
{
|
|
TaskFile->Feature = Cdb->ATA_PASSTHROUGH16.Features7_0;
|
|
TaskFile->SectorCount = Cdb->ATA_PASSTHROUGH16.SectorCount7_0;
|
|
TaskFile->LowLba = Cdb->ATA_PASSTHROUGH16.LbaLow7_0;
|
|
TaskFile->MidLba = Cdb->ATA_PASSTHROUGH16.LbaMid7_0;
|
|
TaskFile->HighLba = Cdb->ATA_PASSTHROUGH16.LbaHigh7_0;
|
|
TaskFile->DriveSelect = Cdb->ATA_PASSTHROUGH16.Device;
|
|
TaskFile->Command = Cdb->ATA_PASSTHROUGH16.Command;
|
|
|
|
if (Cdb->ATA_PASSTHROUGH16.Extend)
|
|
{
|
|
TaskFile->FeatureEx = Cdb->ATA_PASSTHROUGH16.Features15_8;
|
|
TaskFile->SectorCountEx = Cdb->ATA_PASSTHROUGH16.SectorCount15_8;
|
|
TaskFile->LowLbaEx = Cdb->ATA_PASSTHROUGH16.LbaLow15_8;
|
|
TaskFile->MidLbaEx = Cdb->ATA_PASSTHROUGH16.LbaMid15_8;
|
|
TaskFile->HighLbaEx = Cdb->ATA_PASSTHROUGH16.LbaHigh15_8;
|
|
|
|
Request->Flags |= REQUEST_FLAG_LBA48;
|
|
}
|
|
}
|
|
|
|
if (!AtaReqFilterPassThrough(TaskFile))
|
|
return AtaReqTerminateInvalidField(Srb);
|
|
|
|
/* We can set the unique queue tag only after having the slot allocation done */
|
|
if (Request->Flags & REQUEST_FLAG_NCQ)
|
|
TaskFile->SectorCount &= ~0xF8;
|
|
|
|
/* Set the master/slave bit to the correct value */
|
|
TaskFile->DriveSelect &= ~IDE_DRIVE_SELECT_SLAVE;
|
|
TaskFile->DriveSelect |= DevExt->Device.DeviceSelect & IDE_DRIVE_SELECT_SLAVE;
|
|
|
|
TRACE("TF: Send %02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
TaskFile->Command,
|
|
TaskFile->Feature,
|
|
TaskFile->LowLba,
|
|
TaskFile->MidLba,
|
|
TaskFile->HighLba,
|
|
TaskFile->DriveSelect,
|
|
TaskFile->SectorCount);
|
|
|
|
Request->Complete = AtaReqCompleteAtaPassThrough;
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqExecuteScsiAta(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
switch (Srb->Cdb[0])
|
|
{
|
|
case SCSIOP_REPORT_LUNS:
|
|
return AtaReqScsiReportLuns(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_INQUIRY:
|
|
return AtaReqScsiInquiry(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_TEST_UNIT_READY:
|
|
return AtaReqScsiTestUnitReady(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_MEDIUM_REMOVAL:
|
|
return AtaReqScsiMediumRemoval(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_START_STOP_UNIT:
|
|
return AtaReqScsiStartStopUnit(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_REQUEST_SENSE:
|
|
return AtaReqScsiRequestSense(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_MODE_SENSE:
|
|
case SCSIOP_MODE_SENSE10:
|
|
return AtaReqScsiModeSense(DevExt,
|
|
Request,
|
|
Srb,
|
|
(Srb->Cdb[0] == SCSIOP_MODE_SENSE));
|
|
|
|
case SCSIOP_MODE_SELECT:
|
|
case SCSIOP_MODE_SELECT10:
|
|
return AtaReqScsiModeSelect(DevExt,
|
|
Request,
|
|
Srb,
|
|
(Srb->Cdb[0] == SCSIOP_MODE_SELECT));
|
|
|
|
case SCSIOP_SERVICE_ACTION_IN16:
|
|
{
|
|
UCHAR ServiceAction = ((PCDB)Srb->Cdb)->READ_CAPACITY16.ServiceAction;
|
|
|
|
if (ServiceAction != SERVICE_ACTION_READ_CAPACITY16)
|
|
break;
|
|
|
|
__fallthrough;
|
|
}
|
|
case SCSIOP_READ_CAPACITY:
|
|
return AtaReqScsiReadCapacity(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_READ6:
|
|
case SCSIOP_WRITE6:
|
|
case SCSIOP_READ:
|
|
case SCSIOP_WRITE:
|
|
case SCSIOP_READ12:
|
|
case SCSIOP_WRITE12:
|
|
case SCSIOP_READ16:
|
|
case SCSIOP_WRITE16:
|
|
return AtaReqScsiReadWrite(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_SYNCHRONIZE_CACHE:
|
|
case SCSIOP_SYNCHRONIZE_CACHE16:
|
|
return AtaReqScsiSynchronizeCache(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_VERIFY:
|
|
case SCSIOP_VERIFY12:
|
|
case SCSIOP_VERIFY16:
|
|
return AtaReqScsiVerify(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_ATA_PASSTHROUGH12:
|
|
case SCSIOP_ATA_PASSTHROUGH16:
|
|
return AtaReqScsiAtaPassThrough(DevExt, Request, Srb);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
INFO("Unknown command %02x\n", Srb->Cdb[0]);
|
|
|
|
return AtaReqTerminateInvalidOpCode(Srb);
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqPreparePacketCommand(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
/* Prepare a packet command */
|
|
Request->Flags = REQUEST_FLAG_PACKET_COMMAND |
|
|
(Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION);
|
|
|
|
if ((Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) &&
|
|
AtaPacketCommandUseDma(Srb->Cdb[0]) &&
|
|
!(DevExt->Device.DeviceFlags & DEVICE_PIO_ONLY) &&
|
|
!(SRB_GET_FLAGS(Srb) & SRB_FLAG_PIO_RETRY))
|
|
{
|
|
Request->Flags |= REQUEST_DMA_FLAGS;
|
|
}
|
|
|
|
RtlCopyMemory(Request->Cdb, Srb->Cdb, RTL_FIELD_SIZE(SCSI_REQUEST_BLOCK, Cdb));
|
|
|
|
return SRB_STATUS_PENDING;
|
|
}
|
|
|
|
static
|
|
UCHAR
|
|
AtaReqExecuteScsiAtapi(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
switch (Srb->Cdb[0])
|
|
{
|
|
case SCSIOP_ATA_PASSTHROUGH12:
|
|
case SCSIOP_ATA_PASSTHROUGH16:
|
|
return AtaReqScsiAtaPassThrough(DevExt, Request, Srb);
|
|
|
|
case SCSIOP_GET_CONFIGURATION:
|
|
// FIXME: HACK NP21/W emulation bug
|
|
if (IsNEC_98)
|
|
return AtaReqTerminateInvalidOpCode(Srb);
|
|
break;
|
|
|
|
case SCSIOP_INQUIRY:
|
|
if (DevExt->Device.DeviceFlags & DEVICE_CACHE_INQUIRY)
|
|
{
|
|
return AtaReqCopySatlBuffer(Request,
|
|
&DevExt->InquiryData,
|
|
sizeof(DevExt->InquiryData));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return AtaReqPreparePacketCommand(DevExt, Request, Srb);
|
|
}
|
|
|
|
UCHAR
|
|
AtaReqExecuteScsi(
|
|
_In_ PATAPORT_DEVICE_EXTENSION DevExt,
|
|
_In_ PATA_DEVICE_REQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb)
|
|
{
|
|
if (IS_ATAPI(&DevExt->Device))
|
|
return AtaReqExecuteScsiAtapi(DevExt, Request, Srb);
|
|
else
|
|
return AtaReqExecuteScsiAta(DevExt, Request, Srb);
|
|
}
|