diff --git a/reactos/drivers/storage/atapi/atapi.c b/reactos/drivers/storage/atapi/atapi.c index 144aef7f798..4e7287025ea 100644 --- a/reactos/drivers/storage/atapi/atapi.c +++ b/reactos/drivers/storage/atapi/atapi.c @@ -1,3550 +1,6541 @@ /* - * ReactOS kernel - * Copyright (C) 2001, 2002, 2003, 2004, 2005 ReactOS Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * PROJECT: ReactOS Storage Stack + * LICENSE: DDK - see license.txt in the root dir + * FILE: drivers/storage/atapi/atapi.c + * PURPOSE: ATAPI IDE miniport driver + * PROGRAMMERS: Based on a source code sample from Microsoft NT4 DDK */ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS ATAPI miniport driver - * FILE: drivers/storage/atapi/atapi.c - * PURPOSE: ATAPI miniport driver - * PROGRAMMERS: Eric Kohl (ekohl@rz-online.de) - * REVISIONS: - * 09-09-2001 Created - */ - -/* - * Note: - * This driver is derived from Rex Jolliff's ide driver. Lots of his - * routines are still in here although they belong into the higher level - * drivers. They will be moved away as soon as possible. - */ - -/* - * TODO: - * - implement sending of atapi commands - * - handle removable atapi non-cdrom drives - */ - -#define ENABLE_PCI -#define ENABLE_NATIVE_PCI -#define ENABLE_ISA -#define ENABLE_DMA - -// ------------------------------------------------------------------------- #include -#include -#include +#include "atapi.h" // includes scsi.h #include #include #include -#include "atapi.h" - -#define NDEBUG +//#define NDEBUG #include -#define VERSION "0.0.1" - - -NTSTATUS NTAPI -DriverEntry(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath); - -// ------------------------------------------------------- File Static Data - -#ifdef ENABLE_DMA -typedef struct _PRD -{ - ULONG PhysAddress; - ULONG Length; -} PRD, *PPRD; -#endif - -// ATAPI_MINIPORT_EXTENSION // -// DESCRIPTION: -// Extension to be placed in each port device object -// -// ACCESS: -// Allocated from NON-PAGED POOL -// Available at any IRQL +// Device extension // -typedef struct _ATAPI_MINIPORT_EXTENSION +typedef struct _HW_DEVICE_EXTENSION { + + // + // Current request on controller. + // + + PSCSI_REQUEST_BLOCK CurrentSrb; + + // + // Base register locations + // + + PIDE_REGISTERS_1 BaseIoAddress1[2]; + PIDE_REGISTERS_2 BaseIoAddress2[2]; + + // + // Interrupt level + // + + ULONG InterruptLevel; + + // + // Interrupt Mode (Level or Edge) + // + + ULONG InterruptMode; + + // + // Data buffer pointer. + // + + PUSHORT DataBuffer; + + // + // Data words left. + // + + ULONG WordsLeft; + + // + // Number of channels being supported by one instantiation + // of the device extension. Normally (and correctly) one, but + // with so many broken PCI IDE controllers being sold, we have + // to support them. + // + + ULONG NumberChannels; + + // + // Count of errors. Used to turn off features. + // + + ULONG ErrorCount; + + // + // Indicates number of platters on changer-ish devices. + // + + ULONG DiscsPresent[4]; + + // + // Flags word for each possible device. + // + + USHORT DeviceFlags[4]; + + // + // Indicates the number of blocks transferred per int. according to the + // identify data. + // + + UCHAR MaximumBlockXfer[4]; + + // + // Indicates expecting an interrupt + // + + BOOLEAN ExpectingInterrupt; + + // + // Indicate last tape command was DSC Restrictive. + // + + BOOLEAN RDP; + + // + // Driver is being used by the crash dump utility or ntldr. + // + + BOOLEAN DriverMustPoll; + + // + // Indicates use of 32-bit PIO + // + + BOOLEAN DWordIO; + + // + // Indicates whether '0x1f0' is the base address. Used + // in SMART Ioctl calls. + // + + BOOLEAN PrimaryAddress; + + // + // Placeholder for the sub-command value of the last + // SMART command. + // + + UCHAR SmartCommand; + + // + // Placeholder for status register after a GET_MEDIA_STATUS command + // + + UCHAR ReturningMediaStatus; + + UCHAR Reserved[1]; + + // + // Identify data for device + // + + IDENTIFY_DATA FullIdentifyData; + IDENTIFY_DATA2 IdentifyData[4]; + + // + // Mechanism Status Srb Data + // + PSCSI_REQUEST_BLOCK OriginalSrb; + SCSI_REQUEST_BLOCK InternalSrb; + MECHANICAL_STATUS_INFORMATION_HEADER MechStatusData; + SENSE_DATA MechStatusSense; + ULONG MechStatusRetryCount; + +} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION; + +// +// Logical unit extension +// + +typedef struct _HW_LU_EXTENSION { + ULONG Reserved; +} HW_LU_EXTENSION, *PHW_LU_EXTENSION; + +PSCSI_REQUEST_BLOCK +STDCALL +BuildMechanismStatusSrb ( + IN PVOID HwDeviceExtension, + IN ULONG PathId, + IN ULONG TargetId + ); + +PSCSI_REQUEST_BLOCK +STDCALL +BuildRequestSenseSrb ( + IN PVOID HwDeviceExtension, + IN ULONG PathId, + IN ULONG TargetId + ); + +VOID +STDCALL +AtapiHwInitializeChanger ( + IN PVOID HwDeviceExtension, + IN ULONG TargetId, + IN PMECHANICAL_STATUS_INFORMATION_HEADER MechanismStatus + ); + +ULONG +STDCALL +AtapiSendCommand( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +STDCALL +AtapiZeroMemory( + IN PCHAR Buffer, + IN ULONG Count + ); + +VOID +STDCALL +AtapiHexToString ( + ULONG Value, + PCHAR *Buffer + ); + +LONG +STDCALL +AtapiStringCmp ( + PCHAR FirstStr, + PCHAR SecondStr, + ULONG Count + ); + +BOOLEAN +STDCALL +AtapiInterrupt( + IN PVOID HwDeviceExtension + ); + +BOOLEAN +STDCALL +AtapiHwInitialize( + IN PVOID HwDeviceExtension + ); + +ULONG +STDCALL +IdeBuildSenseBuffer( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +STDCALL +IdeMediaStatus( + IN BOOLEAN EnableMSN, + IN PVOID HwDeviceExtension, + IN ULONG Channel + ); + + + +BOOLEAN +STDCALL +IssueIdentify( + IN PVOID HwDeviceExtension, + IN ULONG DeviceNumber, + IN ULONG Channel, + IN UCHAR Command + ) + +/*++ + +Routine Description: + + Issue IDENTIFY command to a device. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + DeviceNumber - Indicates which device. + Command - Either the standard (EC) or the ATAPI packet (A1) IDENTIFY. + +Return Value: + + TRUE if all goes well. + +--*/ + { - IDE_DRIVE_IDENTIFY DeviceParams[2]; - ULONG DeviceFlags[2]; - ULONG TransferSize[2]; + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Channel] ; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel]; + ULONG waitCount = 20000; + ULONG i,j; + UCHAR statusByte; + UCHAR signatureLow, + signatureHigh; - ULONG CommandPortBase; - ULONG ControlPortBase; - ULONG BusMasterRegisterBase; + // + // Select device 0 or 1. + // - PSCSI_REQUEST_BLOCK CurrentSrb; + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((DeviceNumber << 4) | 0xA0)); - PUCHAR DataBuffer; - ULONG DataTransferLength; + // + // Check that the status register makes sense. + // - BOOLEAN (FASTCALL *Handler)(IN struct _ATAPI_MINIPORT_EXTENSION* DevExt); -#ifdef ENABLE_DMA - BOOLEAN UseDma; - ULONG PRDCount; - ULONG PRDMaxCount; - PPRD PRDTable; - SCSI_PHYSICAL_ADDRESS PRDTablePhysicalAddress; -#endif -} ATAPI_MINIPORT_EXTENSION, *PATAPI_MINIPORT_EXTENSION; + GetBaseStatus(baseIoAddress1, statusByte); -/* DeviceFlags */ -#define DEVICE_PRESENT 0x00000001 -#define DEVICE_ATAPI 0x00000002 -#define DEVICE_MULTI_SECTOR_CMD 0x00000004 -#define DEVICE_DWORD_IO 0x00000008 -#define DEVICE_48BIT_ADDRESS 0x00000010 -#define DEVICE_MEDIA_STATUS 0x00000020 -#define DEVICE_DMA_CMD 0x00000040 -#define DEVICE_NO_FLUSH 0x00000080 + if (Command == IDE_COMMAND_IDENTIFY) { + + // + // Mask status byte ERROR bits. + // + + statusByte &= ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX); + + DebugPrint((1, + "IssueIdentify: Checking for IDE. Status (%x)\n", + statusByte)); + + // + // Check if register value is reasonable. + // + + if (statusByte != IDE_STATUS_IDLE) { + + // + // Reset the controller. + // + + AtapiSoftReset(baseIoAddress1,DeviceNumber); + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((DeviceNumber << 4) | 0xA0)); + + WaitOnBusy(baseIoAddress2,statusByte); + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + // + // Device is Atapi. + // + + return FALSE; + } + + DebugPrint((1, + "IssueIdentify: Resetting controller.\n")); + + ScsiPortWritePortUchar(&baseIoAddress2->AlternateStatus,IDE_DC_RESET_CONTROLLER ); + ScsiPortStallExecution(500 * 1000); + ScsiPortWritePortUchar(&baseIoAddress2->AlternateStatus,IDE_DC_REENABLE_CONTROLLER); -typedef struct _UNIT_EXTENSION -{ - ULONG Dummy; -} UNIT_EXTENSION, *PUNIT_EXTENSION; + // We really should wait up to 31 seconds + // The ATA spec. allows device 0 to come back from BUSY in 31 seconds! + // (30 seconds for device 1) + do { -PCI_SLOT_NUMBER LastSlotNumber; + // + // Wait for Busy to drop. + // -#ifdef ENABLE_NATIVE_PCI -typedef struct _PCI_NATIVE_CONTROLLER -{ - USHORT VendorID; - USHORT DeviceID; -} -PCI_NATIVE_CONTROLLER, *PPCI_NATIVE_CONTROLLER; + ScsiPortStallExecution(100); + GetStatus(baseIoAddress2, statusByte); + + } while ((statusByte & IDE_STATUS_BUSY) && waitCount--); + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((DeviceNumber << 4) | 0xA0)); + + // + // Another check for signature, to deal with one model Atapi that doesn't assert signature after + // a soft reset. + // + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + // + // Device is Atapi. + // + + return FALSE; + } + + statusByte &= ~IDE_STATUS_INDEX; + + if (statusByte != IDE_STATUS_IDLE) { + + // + // Give up on this. + // + + return FALSE; + } + + } + + } else { + + DebugPrint((1, + "IssueIdentify: Checking for ATAPI. Status (%x)\n", + statusByte)); -PCI_NATIVE_CONTROLLER const PciNativeController[] = -{ - { - 0x105A, // Promise - 0x4D68, // PDC20268, Ultra100TX2 - }, - { - 0x105A, // Promise - 0x4D30, // PDC20267, Ultra100 } -}; -#endif + + // + // Load CylinderHigh and CylinderLow with number bytes to transfer. + // + + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh, (0x200 >> 8)); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow, (0x200 & 0xFF)); + + for (j = 0; j < 2; j++) { + + // + // Send IDENTIFY command. + // + + WaitOnBusy(baseIoAddress2,statusByte); + + ScsiPortWritePortUchar(&baseIoAddress1->Command, Command); + + // + // Wait for DRQ. + // + + for (i = 0; i < 4; i++) { + + WaitForDrq(baseIoAddress2, statusByte); + + if (statusByte & IDE_STATUS_DRQ) { + + // + // Read status to acknowledge any interrupts generated. + // + + GetBaseStatus(baseIoAddress1, statusByte); + + // + // One last check for Atapi. + // -// ----------------------------------------------- Discardable Declarations + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); -#ifdef ALLOC_PRAGMA + if (signatureLow == 0x14 && signatureHigh == 0xEB) { -// make the initialization routines discardable, so that they -// don't waste space + // + // Device is Atapi. + // -#pragma alloc_text(init, DriverEntry) + return FALSE; + } -// make the PASSIVE_LEVEL routines pageable, so that they don't -// waste nonpaged memory + break; + } -#endif /* ALLOC_PRAGMA */ + if (Command == IDE_COMMAND_IDENTIFY) { -// ---------------------------------------------------- Forward Declarations + // + // Check the signature. If DRQ didn't come up it's likely Atapi. + // -#ifdef ENABLE_DMA -static BOOLEAN -AtapiInitDma(PATAPI_MINIPORT_EXTENSION DevExt, - PSCSI_REQUEST_BLOCK Srb, - UCHAR cmd); -#endif + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); -static ULONG STDCALL -AtapiFindCompatiblePciController(PVOID DeviceExtension, - PVOID HwContext, - PVOID BusInformation, - PCHAR ArgumentString, - PPORT_CONFIGURATION_INFORMATION ConfigInfo, - PBOOLEAN Again); + if (signatureLow == 0x14 && signatureHigh == 0xEB) { -static ULONG STDCALL -AtapiFindIsaBusController(PVOID DeviceExtension, - PVOID HwContext, - PVOID BusInformation, - PCHAR ArgumentString, - PPORT_CONFIGURATION_INFORMATION ConfigInfo, - PBOOLEAN Again); + // + // Device is Atapi. + // -static ULONG STDCALL -AtapiFindNativePciController(PVOID DeviceExtension, - PVOID HwContext, - PVOID BusInformation, - PCHAR ArgumentString, - PPORT_CONFIGURATION_INFORMATION ConfigInfo, - PBOOLEAN Again); + return FALSE; + } + } -static BOOLEAN STDCALL -AtapiInitialize(IN PVOID DeviceExtension); + WaitOnBusy(baseIoAddress2,statusByte); + } -static BOOLEAN STDCALL -AtapiResetBus(IN PVOID DeviceExtension, - IN ULONG PathId); + if (i == 4 && j == 0) { -static BOOLEAN STDCALL -AtapiStartIo(IN PVOID DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb); + // + // Device didn't respond correctly. It will be given one more chances. + // -static VOID -AtapiExecuteCommand(PATAPI_MINIPORT_EXTENSION DevExt, - UCHAR command, - BOOLEAN (FASTCALL *Handler)(PATAPI_MINIPORT_EXTENSION)); + DebugPrint((1, + "IssueIdentify: DRQ never asserted (%x). Error reg (%x)\n", + statusByte, + ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1))); -static BOOLEAN STDCALL -AtapiInterrupt(IN PVOID DeviceExtension); + AtapiSoftReset(baseIoAddress1,DeviceNumber); -static BOOLEAN FASTCALL -AtapiNoDataInterrupt(PATAPI_MINIPORT_EXTENSION DevExt); + GetStatus(baseIoAddress2,statusByte); -static BOOLEAN FASTCALL -AtapiPacketInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt); + DebugPrint((1, + "IssueIdentify: Status after soft reset (%x)\n", + statusByte)); -static BOOLEAN FASTCALL -AtapiSmartInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt); + } else { -static BOOLEAN FASTCALL -AtapiReadInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt); + break; -#ifdef ENABLE_DMA -static BOOLEAN FASTCALL -AtapiDmaPacketInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt); - -static BOOLEAN FASTCALL -AtapiDmaInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt); -#endif - -static BOOLEAN FASTCALL -AtapiWriteInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt); - -static BOOLEAN -AtapiFindDevices(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PPORT_CONFIGURATION_INFORMATION ConfigInfo); - -static BOOLEAN -AtapiIdentifyDevice(IN ULONG CommandPort, - IN ULONG ControlPort, - IN ULONG DriveNum, - IN BOOLEAN Atapi, - OUT PIDE_DRIVE_IDENTIFY DrvParms); - -static BOOLEAN -AtapiPolledRead(IN ULONG CommandPort, - IN ULONG ControlPort, - IN UCHAR PreComp, - IN UCHAR SectorCnt, - IN UCHAR SectorNum, - IN UCHAR CylinderLow, - IN UCHAR CylinderHigh, - IN UCHAR DrvHead, - IN UCHAR Command, - OUT PUCHAR Buffer); - -static ULONG -AtapiSendAtapiCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb); - -static ULONG -AtapiSendIdeCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb); - -static ULONG -AtapiSendSmartCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb); - -static ULONG -AtapiInquiry(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb); - -static ULONG -AtapiReadCapacity(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb); - -static ULONG -AtapiReadWrite(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb); - -static ULONG -AtapiFlushCache(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb); - -static ULONG -AtapiTestUnitReady(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb); - -static UCHAR -AtapiErrorToScsi(PVOID DeviceExtension, - PSCSI_REQUEST_BLOCK Srb); - -static VOID -AtapiScsiSrbToAtapi (PSCSI_REQUEST_BLOCK Srb); - -// ---------------------------------------------------------------- Inlines - -void -IDESwapBytePairs(UCHAR *Buf, - int Cnt) -{ - UCHAR t; - int i; - - for (i = 0; i < Cnt; i += 2) - { - t = Buf[i]; - Buf[i] = Buf[i+1]; - Buf[i+1] = t; + } } -} + + // + // Check for error on really stupid master devices that assert random + // patterns of bits in the status register at the slave address. + // + + if ((Command == IDE_COMMAND_IDENTIFY) && (statusByte & IDE_STATUS_ERROR)) { + return FALSE; + } + + DebugPrint((1, + "IssueIdentify: Status before read words %x\n", + statusByte)); + + // + // Suck out 256 words. After waiting for one model that asserts busy + // after receiving the Packet Identify command. + // + + WaitOnBusy(baseIoAddress2,statusByte); + + if (!(statusByte & IDE_STATUS_DRQ)) { + return FALSE; + } + + ReadBuffer(baseIoAddress1, + (PUSHORT)&deviceExtension->FullIdentifyData, + 256); + + // + // Check out a few capabilities / limitations of the device. + // + + if (deviceExtension->FullIdentifyData.SpecialFunctionsEnabled & 1) { + + // + // Determine if this drive supports the MSN functions. + // + + DebugPrint((2,"IssueIdentify: Marking drive %d as removable. SFE = %d\n", + Channel * 2 + DeviceNumber, + deviceExtension->FullIdentifyData.SpecialFunctionsEnabled)); -// ------------------------------------------------------- Public Interface + deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_REMOVABLE_DRIVE; + } -// DriverEntry -// -// DESCRIPTION: -// This function initializes the driver, locates and claims -// hardware resources, and creates various NT objects needed -// to process I/O requests. -// -// RUN LEVEL: -// PASSIVE_LEVEL -// -// ARGUMENTS: -// IN PDRIVER_OBJECT DriverObject System allocated Driver Object -// for this driver -// IN PUNICODE_STRING RegistryPath Name of registry driver service -// key -// -// RETURNS: -// NTSTATUS + if (deviceExtension->FullIdentifyData.MaximumBlockTransfer) { + + // + // Determine max. block transfer for this device. + // + + deviceExtension->MaximumBlockXfer[(Channel * 2) + DeviceNumber] = + (UCHAR)(deviceExtension->FullIdentifyData.MaximumBlockTransfer & 0xFF); + } + + ScsiPortMoveMemory(&deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber],&deviceExtension->FullIdentifyData,sizeof(IDENTIFY_DATA2)); + + if (deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber].GeneralConfiguration & 0x20 && + Command != IDE_COMMAND_IDENTIFY) { + + // + // This device interrupts with the assertion of DRQ after receiving + // Atapi Packet Command + // + + deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_INT_DRQ; + + DebugPrint((2, + "IssueIdentify: Device interrupts on assertion of DRQ.\n")); + + } else { + + DebugPrint((2, + "IssueIdentify: Device does not interrupt on assertion of DRQ.\n")); + } + + if (((deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber].GeneralConfiguration & 0xF00) == 0x100) && + Command != IDE_COMMAND_IDENTIFY) { + + // + // This is a tape. + // + + deviceExtension->DeviceFlags[(Channel * 2) + DeviceNumber] |= DFLAGS_TAPE_DEVICE; + + DebugPrint((2, + "IssueIdentify: Device is a tape drive.\n")); + + } else { + + DebugPrint((2, + "IssueIdentify: Device is not a tape drive.\n")); + } + + // + // Work around for some IDE and one model Atapi that will present more than + // 256 bytes for the Identify data. + // + + WaitOnBusy(baseIoAddress2,statusByte); + + for (i = 0; i < 0x10000; i++) { + + GetStatus(baseIoAddress2,statusByte); + + if (statusByte & IDE_STATUS_DRQ) { + + // + // Suck out any remaining bytes and throw away. + // + + ScsiPortReadPortUshort(&baseIoAddress1->Data); + + } else { + + break; + + } + } + + DebugPrint((3, + "IssueIdentify: Status after read words (%x)\n", + statusByte)); + + return TRUE; + +} // end IssueIdentify() + + +BOOLEAN +STDCALL +SetDriveParameters( + IN PVOID HwDeviceExtension, + IN ULONG DeviceNumber, + IN ULONG Channel + ) + +/*++ + +Routine Description: + + Set drive parameters using the IDENTIFY data. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + DeviceNumber - Indicates which device. + +Return Value: + + TRUE if all goes well. + + +--*/ -NTSTATUS NTAPI -DriverEntry(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath) { - HW_INITIALIZATION_DATA InitData; - NTSTATUS Status; + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Channel]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel]; + PIDENTIFY_DATA2 identifyData = &deviceExtension->IdentifyData[(Channel * 2) + DeviceNumber]; + ULONG i; + UCHAR statusByte; - DPRINT("ATAPI Driver %s\n", VERSION); - DPRINT("RegistryPath: '%wZ'\n", RegistryPath); + DebugPrint((1, + "SetDriveParameters: Number of heads %x\n", + identifyData->NumberOfHeads)); - /* Initialize data structure */ - RtlZeroMemory(&InitData, - sizeof(HW_INITIALIZATION_DATA)); - InitData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA); - InitData.HwInitialize = AtapiInitialize; - InitData.HwResetBus = AtapiResetBus; - InitData.HwStartIo = AtapiStartIo; - InitData.HwInterrupt = AtapiInterrupt; + DebugPrint((1, + "SetDriveParameters: Sectors per track %x\n", + identifyData->SectorsPerTrack)); - InitData.DeviceExtensionSize = sizeof(ATAPI_MINIPORT_EXTENSION); - InitData.SpecificLuExtensionSize = sizeof(UNIT_EXTENSION); + // + // Set up registers for SET PARAMETER command. + // - InitData.MapBuffers = TRUE; + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((DeviceNumber << 4) | 0xA0) | (identifyData->NumberOfHeads - 1))); - /* Search the PCI bus for compatibility mode ide controllers */ -#ifdef ENABLE_PCI - InitData.NeedPhysicalAddresses = TRUE; + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, + (UCHAR)identifyData->SectorsPerTrack); - InitData.HwFindAdapter = AtapiFindCompatiblePciController; - InitData.NumberOfAccessRanges = 3; - InitData.AdapterInterfaceType = PCIBus; + // + // Send SET PARAMETER command. + // - InitData.VendorId = NULL; - InitData.VendorIdLength = 0; - InitData.DeviceId = NULL; - InitData.DeviceIdLength = 0; + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_SET_DRIVE_PARAMETERS); - Status = ScsiPortInitialize(DriverObject, - RegistryPath, - &InitData, - NULL); -// if (newStatus < statusToReturn) -// statusToReturn = newStatus; -#endif + // + // Wait for up to 30 milliseconds for ERROR or command complete. + // - /* Search the PCI bus for all ide controllers */ -#ifdef ENABLE_NATIVE_PCI - InitData.NeedPhysicalAddresses = TRUE; + for (i=0; i<30 * 1000; i++) { - InitData.HwFindAdapter = AtapiFindNativePciController; - InitData.NumberOfAccessRanges = 3; - InitData.AdapterInterfaceType = PCIBus; + UCHAR errorByte; - InitData.VendorId = 0; - InitData.VendorIdLength = 0; - InitData.DeviceId = 0; - InitData.DeviceIdLength = 0; + GetStatus(baseIoAddress2, statusByte); - LastSlotNumber.u.AsULONG = 0xFFFFFFFF; + if (statusByte & IDE_STATUS_ERROR) { + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + DebugPrint((1, + "SetDriveParameters: Error bit set. Status %x, error %x\n", + errorByte, + statusByte)); - Status = ScsiPortInitialize(DriverObject, - RegistryPath, - &InitData, - NULL); -// if (newStatus < statusToReturn) -// statusToReturn = newStatus; -#endif + return FALSE; + } else if ((statusByte & ~IDE_STATUS_INDEX ) == IDE_STATUS_IDLE) { + break; + } else { + ScsiPortStallExecution(100); + } + } - /* Search the ISA bus for ide controllers */ -#ifdef ENABLE_ISA - InitData.HwFindAdapter = AtapiFindIsaBusController; - InitData.NumberOfAccessRanges = 2; - InitData.AdapterInterfaceType = Isa; + // + // Check for timeout. + // - InitData.VendorId = NULL; - InitData.VendorIdLength = 0; - InitData.DeviceId = NULL; - InitData.DeviceIdLength = 0; + if (i == 30 * 1000) { + return FALSE; + } else { + return TRUE; + } - Status = ScsiPortInitialize(DriverObject, - RegistryPath, - &InitData, - NULL); -// if (newStatus < statusToReturn) -// statusToReturn = newStatus; -#endif +} // end SetDriveParameters() - DPRINT("Returning from DriverEntry\n"); + +BOOLEAN +STDCALL +AtapiResetController( + IN PVOID HwDeviceExtension, + IN ULONG PathId + ) - return(Status); -} +/*++ + +Routine Description: + + Reset IDE controller and/or Atapi device. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + Nothing. + + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG numberChannels = deviceExtension->NumberChannels; + PIDE_REGISTERS_1 baseIoAddress1; + PIDE_REGISTERS_2 baseIoAddress2; + BOOLEAN result = FALSE; + ULONG i,j; + UCHAR statusByte; + + DebugPrint((2,"AtapiResetController: Reset IDE\n")); + + // + // Check and see if we are processing an internal srb + // + if (deviceExtension->OriginalSrb) { + deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + } + + // + // Check if request is in progress. + // + + if (deviceExtension->CurrentSrb) { + + // + // Complete outstanding request with SRB_STATUS_BUS_RESET. + // + + ScsiPortCompleteRequest(deviceExtension, + deviceExtension->CurrentSrb->PathId, + deviceExtension->CurrentSrb->TargetId, + deviceExtension->CurrentSrb->Lun, + (ULONG)SRB_STATUS_BUS_RESET); + + // + // Clear request tracking fields. + // + + deviceExtension->CurrentSrb = NULL; + deviceExtension->WordsLeft = 0; + deviceExtension->DataBuffer = NULL; + + // + // Indicate ready for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + } + + // + // Clear expecting interrupt flag. + // + + deviceExtension->ExpectingInterrupt = FALSE; + deviceExtension->RDP = FALSE; + + for (j = 0; j < numberChannels; j++) { + + baseIoAddress1 = deviceExtension->BaseIoAddress1[j]; + baseIoAddress2 = deviceExtension->BaseIoAddress2[j]; + + // + // Do special processing for ATAPI and IDE disk devices. + // + + for (i = 0; i < 2; i++) { + + // + // Check if device present. + // + + if (deviceExtension->DeviceFlags[i + (j * 2)] & DFLAGS_DEVICE_PRESENT) { + + // + // Check for ATAPI disk. + // + + if (deviceExtension->DeviceFlags[i + (j * 2)] & DFLAGS_ATAPI_DEVICE) { + + // + // Issue soft reset and issue identify. + // + + GetStatus(baseIoAddress2,statusByte); + DebugPrint((1, + "AtapiResetController: Status before Atapi reset (%x).\n", + statusByte)); + + AtapiSoftReset(baseIoAddress1,i); + + GetStatus(baseIoAddress2,statusByte); + + if (statusByte == 0x0) { + + IssueIdentify(HwDeviceExtension, + i, + j, + IDE_COMMAND_ATAPI_IDENTIFY); + } else { + + DebugPrint((1, + "AtapiResetController: Status after soft reset %x\n", + statusByte)); + } + + } else { + + // + // Write IDE reset controller bits. + // + + IdeHardReset(baseIoAddress2,result); + + if (!result) { + return FALSE; + } + + // + // Set disk geometry parameters. + // + + if (!SetDriveParameters(HwDeviceExtension, + i, + j)) { + + DebugPrint((1, + "AtapiResetController: SetDriveParameters failed\n")); + } + } + } + } + } + + // + // Call the HwInitialize routine to setup multi-block. + // + + AtapiHwInitialize(HwDeviceExtension); + + return TRUE; + +} // end AtapiResetController() + + + +ULONG +STDCALL +MapError( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine maps ATAPI and IDE errors to specific SRB statuses. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + SRB status + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + ULONG i; + UCHAR errorByte; + UCHAR srbStatus; + UCHAR scsiStatus; + + // + // Read the error register. + // + + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + DebugPrint((1, + "MapError: Error register is %x\n", + errorByte)); + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + switch (errorByte >> 4) { + case SCSI_SENSE_NO_SENSE: + + DebugPrint((1, + "ATAPI: No sense information\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_RECOVERED_ERROR: + + DebugPrint((1, + "ATAPI: Recovered error\n")); + scsiStatus = 0; + srbStatus = SRB_STATUS_SUCCESS; + break; + + case SCSI_SENSE_NOT_READY: + + DebugPrint((1, + "ATAPI: Device not ready\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_MEDIUM_ERROR: + + DebugPrint((1, + "ATAPI: Media error\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_HARDWARE_ERROR: + + DebugPrint((1, + "ATAPI: Hardware error\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_ILLEGAL_REQUEST: + + DebugPrint((1, + "ATAPI: Illegal request\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_UNIT_ATTENTION: + + DebugPrint((1, + "ATAPI: Unit attention\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_DATA_PROTECT: + + DebugPrint((1, + "ATAPI: Data protect\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_BLANK_CHECK: + + DebugPrint((1, + "ATAPI: Blank check\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_ABORTED_COMMAND: + DebugPrint((1, + "Atapi: Command Aborted\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + default: + + DebugPrint((1, + "ATAPI: Invalid sense information\n")); + scsiStatus = 0; + srbStatus = SRB_STATUS_ERROR; + break; + } + + } else { + + scsiStatus = 0; + + // + // Save errorByte,to be used by SCSIOP_REQUEST_SENSE. + // + + deviceExtension->ReturningMediaStatus = errorByte; + + if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) { + DebugPrint((1, + "IDE: Media change\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + } else if (errorByte & IDE_ERROR_COMMAND_ABORTED) { + DebugPrint((1, + "IDE: Command abort\n")); + srbStatus = SRB_STATUS_ABORTED; + scsiStatus = SCSISTAT_CHECK_CONDITION; + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + deviceExtension->ErrorCount++; + + } else if (errorByte & IDE_ERROR_END_OF_MEDIA) { + + DebugPrint((1, + "IDE: End of media\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED)){ + deviceExtension->ErrorCount++; + } + + } else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) { + + DebugPrint((1, + "IDE: Illegal length\n")); + srbStatus = SRB_STATUS_INVALID_REQUEST; + + } else if (errorByte & IDE_ERROR_BAD_BLOCK) { + + DebugPrint((1, + "IDE: Bad block\n")); + srbStatus = SRB_STATUS_ERROR; + scsiStatus = SCSISTAT_CHECK_CONDITION; + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + } else if (errorByte & IDE_ERROR_ID_NOT_FOUND) { + + DebugPrint((1, + "IDE: Id not found\n")); + srbStatus = SRB_STATUS_ERROR; + scsiStatus = SCSISTAT_CHECK_CONDITION; + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + deviceExtension->ErrorCount++; + + } else if (errorByte & IDE_ERROR_MEDIA_CHANGE) { + + DebugPrint((1, + "IDE: Media change\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + } else if (errorByte & IDE_ERROR_DATA_ERROR) { + + DebugPrint((1, + "IDE: Data error\n")); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED)){ + deviceExtension->ErrorCount++; + } + + // + // Build sense buffer + // + + if (Srb->SenseInfoBuffer) { + + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + } + + if (deviceExtension->ErrorCount >= MAX_ERRORS) { + deviceExtension->DWordIO = FALSE; + deviceExtension->MaximumBlockXfer[Srb->TargetId] = 0; + + DebugPrint((1, + "MapError: Disabling 32-bit PIO and Multi-sector IOs\n")); + + // + // Log the error. + // + + ScsiPortLogError( HwDeviceExtension, + Srb, + Srb->PathId, + Srb->TargetId, + Srb->Lun, + SP_BAD_FW_WARNING, + 4); + // + // Reprogram to not use Multi-sector. + // + + for (i = 0; i < 4; i++) { + UCHAR statusByte; + + if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT && + !(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) { + + // + // Select the device. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((i & 0x1) << 4) | 0xA0)); + + // + // Setup sector count to reflect the # of blocks. + // + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, + 0); + + // + // Issue the command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_SET_MULTIPLE); + + // + // Wait for busy to drop. + // + + WaitOnBaseBusy(baseIoAddress1,statusByte); + + // + // Check for errors. Reset the value to 0 (disable MultiBlock) if the + // command was aborted. + // + + if (statusByte & IDE_STATUS_ERROR) { + + // + // Read the error register. + // + + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + + DebugPrint((1, + "AtapiHwInitialize: Error setting multiple mode. Status %x, error byte %x\n", + statusByte, + errorByte)); + // + // Adjust the devExt. value, if necessary. + // + + deviceExtension->MaximumBlockXfer[i] = 0; + + } + } + } + } + } + + + // + // Set SCSI status to indicate a check condition. + // + + Srb->ScsiStatus = scsiStatus; + + return srbStatus; + +} // end MapError() BOOLEAN -AtapiClaimHwResources(PATAPI_MINIPORT_EXTENSION DevExt, - PPORT_CONFIGURATION_INFORMATION ConfigInfo, - INTERFACE_TYPE InterfaceType, - ULONG CommandPortBase, - ULONG ControlPortBase, - ULONG BusMasterPortBase, - ULONG InterruptVector) +STDCALL +AtapiHwInitialize( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + TRUE - if initialization successful. + FALSE - if initialization unsuccessful. + +--*/ + { - SCSI_PHYSICAL_ADDRESS IoAddress; - PVOID IoBase; -#ifdef ENABLE_DMA - ULONG Length; -#endif - IoAddress = ScsiPortConvertUlongToPhysicalAddress(CommandPortBase); - IoBase = ScsiPortGetDeviceBase((PVOID)DevExt, - InterfaceType, - ConfigInfo->SystemIoBusNumber, - IoAddress, - 8, - TRUE); - if (IoBase == NULL) - { - return FALSE; - } - DevExt->Handler = NULL; - DevExt->CommandPortBase = (ULONG)IoBase; - (*ConfigInfo->AccessRanges)[0].RangeStart = IoAddress; - (*ConfigInfo->AccessRanges)[0].RangeLength = 8; - (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE; + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress; + ULONG i; + UCHAR statusByte, errorByte; - if (ControlPortBase) - { - IoAddress = ScsiPortConvertUlongToPhysicalAddress(ControlPortBase + 2); - IoBase = ScsiPortGetDeviceBase((PVOID)DevExt, - InterfaceType, - ConfigInfo->SystemIoBusNumber, - IoAddress, - 1, - TRUE); - if (IoBase == NULL) - { - ScsiPortFreeDeviceBase((PVOID)DevExt, - (PVOID)DevExt->CommandPortBase); - return FALSE; - } - DevExt->ControlPortBase = (ULONG)IoBase; - (*ConfigInfo->AccessRanges)[1].RangeStart = IoAddress; - (*ConfigInfo->AccessRanges)[1].RangeLength = 1; - (*ConfigInfo->AccessRanges)[1].RangeInMemory = FALSE; - } - if (BusMasterPortBase) - { - IoAddress = ScsiPortConvertUlongToPhysicalAddress(BusMasterPortBase); - IoBase = ScsiPortGetDeviceBase((PVOID)DevExt, - InterfaceType, - ConfigInfo->SystemIoBusNumber, - IoAddress, - 8, - TRUE); - if (IoBase == NULL) - { - ScsiPortFreeDeviceBase((PVOID)DevExt, (PVOID)DevExt->CommandPortBase); - ScsiPortFreeDeviceBase((PVOID)DevExt, (PVOID)DevExt->ControlPortBase); - return FALSE; - } - DevExt->BusMasterRegisterBase = (ULONG)IoBase; - (*ConfigInfo->AccessRanges)[2].RangeStart = IoAddress; - (*ConfigInfo->AccessRanges)[2].RangeLength = 8; - (*ConfigInfo->AccessRanges)[2].RangeInMemory = FALSE; -#ifdef ENABLE_DMA -// ConfigInfo->DmaChannel = SP_UNINITIALIZED_VALUE; -// ConfigInfo->DmaPort = SP_UNINITIALIZED_VALUE; - ConfigInfo->DmaWidth = Width32Bits; -// ConfigInfo->DmaSpeed = Compatible; - ConfigInfo->ScatterGather = TRUE; - ConfigInfo->Master = TRUE; - ConfigInfo->NumberOfPhysicalBreaks = 0x10000 / PAGE_SIZE + 1; - ConfigInfo->Dma32BitAddresses = TRUE; - ConfigInfo->NeedPhysicalAddresses = TRUE; - ConfigInfo->MapBuffers = TRUE; - DevExt->PRDMaxCount = PAGE_SIZE / sizeof(PRD); - DevExt->PRDTable = ScsiPortGetUncachedExtension(DevExt, ConfigInfo, sizeof(PRD) * DevExt->PRDMaxCount); - if (DevExt->PRDTable != NULL) - { - DevExt->PRDTablePhysicalAddress = ScsiPortGetPhysicalAddress(DevExt, NULL, DevExt->PRDTable, &Length); - } - if (DevExt->PRDTable == NULL || - DevExt->PRDTablePhysicalAddress.QuadPart == 0LL || - Length < sizeof(PRD) * DevExt->PRDMaxCount) - { - ScsiPortFreeDeviceBase((PVOID)DevExt, (PVOID)DevExt->CommandPortBase); - ScsiPortFreeDeviceBase((PVOID)DevExt, (PVOID)DevExt->ControlPortBase); - ScsiPortFreeDeviceBase((PVOID)DevExt, (PVOID)DevExt->BusMasterRegisterBase); - return FALSE; - } -#endif - } - ConfigInfo->BusInterruptLevel = InterruptVector; - ConfigInfo->BusInterruptVector = InterruptVector; - ConfigInfo->InterruptMode = (InterfaceType == Isa) ? Latched : LevelSensitive; + for (i = 0; i < 4; i++) { + if (deviceExtension->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) { - if ((CommandPortBase == 0x1F0 || ControlPortBase == 0x3F4) && !ConfigInfo->AtdiskPrimaryClaimed) - { - ConfigInfo->AtdiskPrimaryClaimed = TRUE; - } - if ((CommandPortBase == 0x170 || ControlPortBase == 0x374) && !ConfigInfo->AtdiskSecondaryClaimed) - { - ConfigInfo->AtdiskSecondaryClaimed = TRUE; - } - return TRUE; + if (!(deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE)) { + + // + // Enable media status notification + // + + baseIoAddress = deviceExtension->BaseIoAddress1[i >> 1]; + + IdeMediaStatus(TRUE,HwDeviceExtension,i); + + // + // If supported, setup Multi-block transfers. + // + if (deviceExtension->MaximumBlockXfer[i]) { + + // + // Select the device. + // + + ScsiPortWritePortUchar(&baseIoAddress->DriveSelect, + (UCHAR)(((i & 0x1) << 4) | 0xA0)); + + // + // Setup sector count to reflect the # of blocks. + // + + ScsiPortWritePortUchar(&baseIoAddress->BlockCount, + deviceExtension->MaximumBlockXfer[i]); + + // + // Issue the command. + // + + ScsiPortWritePortUchar(&baseIoAddress->Command, + IDE_COMMAND_SET_MULTIPLE); + + // + // Wait for busy to drop. + // + + WaitOnBaseBusy(baseIoAddress,statusByte); + + // + // Check for errors. Reset the value to 0 (disable MultiBlock) if the + // command was aborted. + // + + if (statusByte & IDE_STATUS_ERROR) { + + // + // Read the error register. + // + + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress + 1); + + DebugPrint((1, + "AtapiHwInitialize: Error setting multiple mode. Status %x, error byte %x\n", + statusByte, + errorByte)); + // + // Adjust the devExt. value, if necessary. + // + + deviceExtension->MaximumBlockXfer[i] = 0; + + } else { + DebugPrint((2, + "AtapiHwInitialize: Using Multiblock on Device %d. Blocks / int - %d\n", + i, + deviceExtension->MaximumBlockXfer[i])); + } + } + } else if (!(deviceExtension->DeviceFlags[i] & DFLAGS_CHANGER_INITED)){ + + ULONG j; + BOOLEAN isSanyo = FALSE; + UCHAR vendorId[26]; + + // + // Attempt to identify any special-case devices - psuedo-atapi changers, atapi changers, etc. + // + + for (j = 0; j < 13; j += 2) { + + // + // Build a buffer based on the identify data. + // + + vendorId[j] = ((PUCHAR)deviceExtension->IdentifyData[i].ModelNumber)[j + 1]; + vendorId[j+1] = ((PUCHAR)deviceExtension->IdentifyData[i].ModelNumber)[j]; + } + + if (!AtapiStringCmp (vendorId, "CD-ROM CDR", 11)) { + + // + // Inquiry string for older model had a '-', newer is '_' + // + + if (vendorId[12] == 'C') { + + // + // Torisan changer. Set the bit. This will be used in several places + // acting like 1) a multi-lun device and 2) building the 'special' TUR's. + // + + deviceExtension->DeviceFlags[i] |= (DFLAGS_CHANGER_INITED | DFLAGS_SANYO_ATAPI_CHANGER); + deviceExtension->DiscsPresent[i] = 3; + isSanyo = TRUE; + } + } + } + + // + // We need to get our device ready for action before + // returning from this function + // + // According to the atapi spec 2.5 or 2.6, an atapi device + // clears its status BSY bit when it is ready for atapi commands. + // However, some devices (Panasonic SQ-TC500N) are still + // not ready even when the status BSY is clear. They don't react + // to atapi commands. + // + // Since there is really no other indication that tells us + // the drive is really ready for action. We are going to check BSY + // is clear and then just wait for an arbitrary amount of time! + // + if (deviceExtension->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE) { + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[i >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[i >> 1]; + ULONG waitCount; + + // have to get out of the loop sometime! + // 10000 * 100us = 1000,000us = 1000ms = 1s + waitCount = 10000; + GetStatus(baseIoAddress2, statusByte); + while ((statusByte & IDE_STATUS_BUSY) && waitCount) { + // + // Wait for Busy to drop. + // + ScsiPortStallExecution(100); + GetStatus(baseIoAddress2, statusByte); + waitCount--; + } + + // 5000 * 100us = 500,000us = 500ms = 0.5s + waitCount = 5000; + do { + ScsiPortStallExecution(100); + } while (waitCount--); + } + } + } + + return TRUE; + +} // end AtapiHwInitialize() + + +VOID +STDCALL +AtapiHwInitializeChanger ( + IN PVOID HwDeviceExtension, + IN ULONG TargetId, + IN PMECHANICAL_STATUS_INFORMATION_HEADER MechanismStatus) +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + + if (MechanismStatus) { + deviceExtension->DiscsPresent[TargetId] = MechanismStatus->NumberAvailableSlots; + if (deviceExtension->DiscsPresent[TargetId] > 1) { + deviceExtension->DeviceFlags[TargetId] |= DFLAGS_ATAPI_CHANGER; + } + } + return; } -#ifdef ENABLE_PCI -static ULONG STDCALL -AtapiFindCompatiblePciController(PVOID DeviceExtension, - PVOID HwContext, - PVOID BusInformation, - PCHAR ArgumentString, - PPORT_CONFIGURATION_INFORMATION ConfigInfo, - PBOOLEAN Again) + +BOOLEAN +STDCALL +FindDevices( + IN PVOID HwDeviceExtension, + IN BOOLEAN AtapiOnly, + IN ULONG Channel + ) + +/*++ + +Routine Description: + + This routine is called from AtapiFindController to identify + devices attached to an IDE controller. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + AtapiOnly - Indicates that routine should return TRUE only if + an ATAPI device is attached to the controller. + +Return Value: + + TRUE - True if devices found. + +--*/ + { - PATAPI_MINIPORT_EXTENSION DevExt = (PATAPI_MINIPORT_EXTENSION)DeviceExtension; - PCI_SLOT_NUMBER SlotNumber; - PCI_COMMON_CONFIG PciConfig; - ULONG DataSize; - ULONG StartDeviceNumber; - ULONG DeviceNumber; - ULONG StartFunctionNumber; - ULONG FunctionNumber; - BOOLEAN ChannelFound; - BOOLEAN DeviceFound; - ULONG BusMasterBasePort = 0; + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Channel]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Channel]; + BOOLEAN deviceResponded = FALSE, + skipSetParameters = FALSE; + ULONG waitCount = 10000; + ULONG deviceNumber; + ULONG i; + UCHAR signatureLow, + signatureHigh; + UCHAR statusByte; - DPRINT("AtapiFindCompatiblePciController() Bus: %lu Slot: %lu\n", - ConfigInfo->SystemIoBusNumber, - ConfigInfo->SlotNumber); + // + // Clear expecting interrupt flag and current SRB field. + // - *Again = FALSE; + deviceExtension->ExpectingInterrupt = FALSE; + deviceExtension->CurrentSrb = NULL; + + // + // Search for devices. + // + + for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) { + + // + // Select the device. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((deviceNumber << 4) | 0xA0)); + + // + // Check here for some SCSI adapters that incorporate IDE emulation. + // + + GetStatus(baseIoAddress2, statusByte); + if (statusByte == 0xFF) { + continue; + } + + AtapiSoftReset(baseIoAddress1,deviceNumber); + WaitOnBusy(baseIoAddress2,statusByte); + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + // + // ATAPI signature found. + // Issue the ATAPI identify command if this + // is not for the crash dump utility. + // + +atapiIssueId: + + if (!deviceExtension->DriverMustPoll) { + + // + // Issue ATAPI packet identify command. + // + + if (IssueIdentify(HwDeviceExtension, + deviceNumber, + Channel, + IDE_COMMAND_ATAPI_IDENTIFY)) { + + // + // Indicate ATAPI device. + // + + DebugPrint((1, + "FindDevices: Device %x is ATAPI\n", + deviceNumber)); + + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_ATAPI_DEVICE; + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_DEVICE_PRESENT; + + deviceResponded = TRUE; + + GetStatus(baseIoAddress2, statusByte); + if (statusByte & IDE_STATUS_ERROR) { + AtapiSoftReset(baseIoAddress1, deviceNumber); + } + + + } else { + + // + // Indicate no working device. + // + + DebugPrint((1, + "FindDevices: Device %x not responding\n", + deviceNumber)); + + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] &= ~DFLAGS_DEVICE_PRESENT; + } + + } + + } else { + + // + // Issue IDE Identify. If an Atapi device is actually present, the signature + // will be asserted, and the drive will be recognized as such. + // + + if (IssueIdentify(HwDeviceExtension, + deviceNumber, + Channel, + IDE_COMMAND_IDENTIFY)) { + + // + // IDE drive found. + // + + + DebugPrint((1, + "FindDevices: Device %x is IDE\n", + deviceNumber)); + + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] |= DFLAGS_DEVICE_PRESENT; + + if (!AtapiOnly) { + deviceResponded = TRUE; + } + + // + // Indicate IDE - not ATAPI device. + // + + deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] &= ~DFLAGS_ATAPI_DEVICE; + + + } else { + + // + // Look to see if an Atapi device is present. + // + + AtapiSoftReset(baseIoAddress1,deviceNumber); + + WaitOnBusy(baseIoAddress2,statusByte); + + signatureLow = ScsiPortReadPortUchar(&baseIoAddress1->CylinderLow); + signatureHigh = ScsiPortReadPortUchar(&baseIoAddress1->CylinderHigh); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + goto atapiIssueId; + } + } + } + } + + for (i = 0; i < 2; i++) { + if ((deviceExtension->DeviceFlags[i + (Channel * 2)] & DFLAGS_DEVICE_PRESENT) && + (!(deviceExtension->DeviceFlags[i + (Channel * 2)] & DFLAGS_ATAPI_DEVICE)) && deviceResponded) { + + // + // This hideous hack is to deal with ESDI devices that return + // garbage geometry in the IDENTIFY data. + // This is ONLY for the crashdump environment as + // these are ESDI devices. + // + + if (deviceExtension->IdentifyData[i].SectorsPerTrack == + 0x35 && + deviceExtension->IdentifyData[i].NumberOfHeads == + 0x07) { + + DebugPrint((1, + "FindDevices: Found nasty Compaq ESDI!\n")); + + // + // Change these values to something reasonable. + // + + deviceExtension->IdentifyData[i].SectorsPerTrack = + 0x34; + deviceExtension->IdentifyData[i].NumberOfHeads = + 0x0E; + } + + if (deviceExtension->IdentifyData[i].SectorsPerTrack == + 0x35 && + deviceExtension->IdentifyData[i].NumberOfHeads == + 0x0F) { + + DebugPrint((1, + "FindDevices: Found nasty Compaq ESDI!\n")); + + // + // Change these values to something reasonable. + // + + deviceExtension->IdentifyData[i].SectorsPerTrack = + 0x34; + deviceExtension->IdentifyData[i].NumberOfHeads = + 0x0F; + } + + + if (deviceExtension->IdentifyData[i].SectorsPerTrack == + 0x36 && + deviceExtension->IdentifyData[i].NumberOfHeads == + 0x07) { + + DebugPrint((1, + "FindDevices: Found nasty UltraStor ESDI!\n")); + + // + // Change these values to something reasonable. + // + + deviceExtension->IdentifyData[i].SectorsPerTrack = + 0x3F; + deviceExtension->IdentifyData[i].NumberOfHeads = + 0x10; + skipSetParameters = TRUE; + } + + + if (!skipSetParameters) { + + WaitOnBusy(baseIoAddress2,statusByte); + + // + // Select the device. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((i << 4) | 0xA0)); + + GetStatus(baseIoAddress2, statusByte); + + if (statusByte & IDE_STATUS_ERROR) { + + // + // Reset the device. + // + + DebugPrint((2, + "FindDevices: Resetting controller before SetDriveParameters.\n")); + + ScsiPortWritePortUchar(&baseIoAddress2->AlternateStatus,IDE_DC_RESET_CONTROLLER ); + ScsiPortStallExecution(500 * 1000); + ScsiPortWritePortUchar(&baseIoAddress2->AlternateStatus,IDE_DC_REENABLE_CONTROLLER); + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)((i << 4) | 0xA0)); + + do { + + // + // Wait for Busy to drop. + // + + ScsiPortStallExecution(100); + GetStatus(baseIoAddress2, statusByte); + + } while ((statusByte & IDE_STATUS_BUSY) && waitCount--); + } + + WaitOnBusy(baseIoAddress2,statusByte); + DebugPrint((2, + "FindDevices: Status before SetDriveParameters: (%x) (%x)\n", + statusByte, + ScsiPortReadPortUchar(&baseIoAddress1->DriveSelect))); + + // + // Use the IDENTIFY data to set drive parameters. + // + + if (!SetDriveParameters(HwDeviceExtension,i,Channel)) { + + DebugPrint((0, + "AtapHwInitialize: Set drive parameters for device %d failed\n", + i)); + + // + // Don't use this device as writes could cause corruption. + // + + deviceExtension->DeviceFlags[i + Channel] = 0; + continue; + + } + if (deviceExtension->DeviceFlags[deviceNumber + (Channel * 2)] & DFLAGS_REMOVABLE_DRIVE) { + + // + // Pick up ALL IDE removable drives that conform to Yosemite V0.2... + // + + AtapiOnly = FALSE; + } + + + // + // Indicate that a device was found. + // + + if (!AtapiOnly) { + deviceResponded = TRUE; + } + } + } + } + + // + // Make sure master device is selected on exit. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, 0xA0); + + // + // Reset the controller. This is a feeble attempt to leave the ESDI + // controllers in a state that ATDISK driver will recognize them. + // The problem in ATDISK has to do with timings as it is not reproducible + // in debug. The reset should restore the controller to its poweron state + // and give the system enough time to settle. + // + + if (!deviceResponded) { + + ScsiPortWritePortUchar(&baseIoAddress2->AlternateStatus,IDE_DC_RESET_CONTROLLER ); + ScsiPortStallExecution(50 * 1000); + ScsiPortWritePortUchar(&baseIoAddress2->AlternateStatus,IDE_DC_REENABLE_CONTROLLER); + } + + return deviceResponded; + +} // end FindDevices() + + +ULONG +STDCALL +AtapiParseArgumentString( + IN PCHAR String, + IN PCHAR KeyWord + ) + +/*++ + +Routine Description: + + This routine will parse the string for a match on the keyword, then + calculate the value for the keyword and return it to the caller. + +Arguments: + + String - The ASCII string to parse. + KeyWord - The keyword for the value desired. + +Return Values: + + Zero if value not found + Value converted from ASCII to binary. + +--*/ + +{ + PCHAR cptr; + PCHAR kptr; + ULONG value; + ULONG stringLength = 0; + ULONG keyWordLength = 0; + ULONG index; + + if (!String) { + return 0; + } + if (!KeyWord) { + return 0; + } + + // + // Calculate the string length and lower case all characters. + // + + cptr = String; + while (*cptr) { + if (*cptr >= 'A' && *cptr <= 'Z') { + *cptr = *cptr + ('a' - 'A'); + } + cptr++; + stringLength++; + } + + // + // Calculate the keyword length and lower case all characters. + // + + cptr = KeyWord; + while (*cptr) { + + if (*cptr >= 'A' && *cptr <= 'Z') { + *cptr = *cptr + ('a' - 'A'); + } + cptr++; + keyWordLength++; + } + + if (keyWordLength > stringLength) { + + // + // Can't possibly have a match. + // + + return 0; + } + + // + // Now setup and start the compare. + // + + cptr = String; + +ContinueSearch: + + // + // The input string may start with white space. Skip it. + // + + while (*cptr == ' ' || *cptr == '\t') { + cptr++; + } + + if (*cptr == '\0') { + + // + // end of string. + // + + return 0; + } + + kptr = KeyWord; + while (*cptr++ == *kptr++) { + + if (*(cptr - 1) == '\0') { + + // + // end of string + // + + return 0; + } + } + + if (*(kptr - 1) == '\0') { + + // + // May have a match backup and check for blank or equals. + // + + cptr--; + while (*cptr == ' ' || *cptr == '\t') { + cptr++; + } + + // + // Found a match. Make sure there is an equals. + // + + if (*cptr != '=') { + + // + // Not a match so move to the next semicolon. + // + + while (*cptr) { + if (*cptr++ == ';') { + goto ContinueSearch; + } + } + return 0; + } + + // + // Skip the equals sign. + // + + cptr++; + + // + // Skip white space. + // + + while ((*cptr == ' ') || (*cptr == '\t')) { + cptr++; + } + + if (*cptr == '\0') { + + // + // Early end of string, return not found + // + + return 0; + } + + if (*cptr == ';') { + + // + // This isn't it either. + // + + cptr++; + goto ContinueSearch; + } + + value = 0; + if ((*cptr == '0') && (*(cptr + 1) == 'x')) { + + // + // Value is in Hex. Skip the "0x" + // + + cptr += 2; + for (index = 0; *(cptr + index); index++) { + + if (*(cptr + index) == ' ' || + *(cptr + index) == '\t' || + *(cptr + index) == ';') { + break; + } + + if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) { + value = (16 * value) + (*(cptr + index) - '0'); + } else { + if ((*(cptr + index) >= 'a') && (*(cptr + index) <= 'f')) { + value = (16 * value) + (*(cptr + index) - 'a' + 10); + } else { + + // + // Syntax error, return not found. + // + return 0; + } + } + } + } else { + + // + // Value is in Decimal. + // + + for (index = 0; *(cptr + index); index++) { + + if (*(cptr + index) == ' ' || + *(cptr + index) == '\t' || + *(cptr + index) == ';') { + break; + } + + if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) { + value = (10 * value) + (*(cptr + index) - '0'); + } else { + + // + // Syntax error return not found. + // + return 0; + } + } + } + + return value; + } else { + + // + // Not a match check for ';' to continue search. + // + + while (*cptr) { + if (*cptr++ == ';') { + goto ContinueSearch; + } + } + + return 0; + } +} + + + + + +ULONG +STDCALL +AtapiFindController( + IN PVOID HwDeviceExtension, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ) +/*++ + +Routine Description: + + This function is called by the OS-specific port driver after + the necessary storage has been allocated, to gather information + about the adapter's configuration. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Context - Address of adapter count + ArgumentString - Used to determine whether driver is client of ntldr or crash dump utility. + ConfigInfo - Configuration information structure describing HBA + Again - Indicates search for adapters to continue + +Return Value: + + ULONG + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PULONG adapterCount = (PULONG)Context; + PUCHAR ioSpace; + ULONG i,j; + ULONG irq; + ULONG portBase; + ULONG retryCount; + PCI_SLOT_NUMBER slotData; + PPCI_COMMON_CONFIG pciData; + ULONG pciBuffer; + BOOLEAN atapiOnly; + UCHAR statusByte; + BOOLEAN preConfig = FALSE; + // + // The following table specifies the ports to be checked when searching for + // an IDE controller. A zero entry terminates the search. + // + + CONST ULONG AdapterAddresses[5] = {0x1F0, 0x170, 0x1e8, 0x168, 0}; + + // + // The following table specifies interrupt levels corresponding to the + // port addresses in the previous table. + // + + CONST ULONG InterruptLevels[5] = {14, 15, 11, 10, 0}; + + if (!deviceExtension) { + return SP_RETURN_ERROR; + } + + // + // Check to see if this is a special configuration environment. + // + + portBase = irq = 0; + if (ArgumentString) { + + irq = AtapiParseArgumentString(ArgumentString, "Interrupt"); + if (irq ) { + + // + // Both parameters must be present to proceed + // + + portBase = AtapiParseArgumentString(ArgumentString, "BaseAddress"); + if (!portBase) { + + // + // Try a default search for the part. + // + + irq = 0; + } + } + } + + + + // + // Scan though the adapter address looking for adapters. + // + if (ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[0].RangeStart) != 0) { + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + (*ConfigInfo->AccessRanges)[0].RangeStart, + (*ConfigInfo->AccessRanges)[0].RangeLength, + (BOOLEAN) !((*ConfigInfo->AccessRanges)[0].RangeInMemory)); + *Again = FALSE; + // + // Since we have pre-configured information we only need to go through this loop once + // + preConfig = TRUE; + portBase = ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[0].RangeStart); + + } + + + + while (AdapterAddresses[*adapterCount] != 0) { + + retryCount = 4; + + for (i = 0; i < 4; i++) { + + // + // Zero device fields to ensure that if earlier devices were found, + // but not claimed, the fields are cleared. + // + + deviceExtension->DeviceFlags[i] &= ~(DFLAGS_ATAPI_DEVICE | DFLAGS_DEVICE_PRESENT | DFLAGS_TAPE_DEVICE); + } + + // + // Get the system physical address for this IO range. + // + + + // + // Check if configInfo has the default information + // if not, we go and find ourselves + // + + if (preConfig == FALSE) { + + if (portBase) { + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(portBase), + 8, + TRUE); + } else { + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*adapterCount]), + 8, + TRUE); + } + + }// ConfigInfo check + // + // Update the adapter count. + // + + (*adapterCount)++; + + // + // Check if ioSpace accessible. + // + + if (!ioSpace) { + continue; + } + +retryIdentifier: + + // + // Select master. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->DriveSelect, 0xA0); + + // + // Check if card at this address. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow, 0xAA); + + // + // Check if indentifier can be read back. + // + + if ((statusByte = ScsiPortReadPortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow)) != 0xAA) { + + DebugPrint((2, + "AtapiFindController: Identifier read back from Master (%x)\n", + statusByte)); + + statusByte = ScsiPortReadPortUchar(&((PATAPI_REGISTERS_2)ioSpace)->AlternateStatus); + + if (statusByte & IDE_STATUS_BUSY) { + + i = 0; + + // + // Could be the TEAC in a thinkpad. Their dos driver puts it in a sleep-mode that + // warm boots don't clear. + // + + do { + ScsiPortStallExecution(1000); + statusByte = ScsiPortReadPortUchar(&((PATAPI_REGISTERS_1)ioSpace)->Command); + DebugPrint((3, + "AtapiFindController: First access to status %x\n", + statusByte)); + } while ((statusByte & IDE_STATUS_BUSY) && ++i < 10); + + if (retryCount-- && (!(statusByte & IDE_STATUS_BUSY))) { + goto retryIdentifier; + } + } + + // + // Select slave. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->DriveSelect, 0xB0); + + // + // See if slave is present. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow, 0xAA); + + if ((statusByte = ScsiPortReadPortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow)) != 0xAA) { + + DebugPrint((2, + "AtapiFindController: Identifier read back from Slave (%x)\n", + statusByte)); + + // + // + // No controller at this base address. + // + + ScsiPortFreeDeviceBase(HwDeviceExtension, + ioSpace); + + continue; + } + } + + // + // Record base IO address. + // + + deviceExtension->BaseIoAddress1[0] = (PIDE_REGISTERS_1)(ioSpace); + + // + // Fill in the access array information only if default params are not in there. + // + if (preConfig == FALSE) { + + // + // An adapter has been found request another call, only if we didn't get preconfigured info. + // + *Again = TRUE; + + if (portBase) { + (*ConfigInfo->AccessRanges)[0].RangeStart = ScsiPortConvertUlongToPhysicalAddress(portBase); + } else { + (*ConfigInfo->AccessRanges)[0].RangeStart = + ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*adapterCount - 1]); + } + + (*ConfigInfo->AccessRanges)[0].RangeLength = 8; + (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE; + + // + // Indicate the interrupt level corresponding to this IO range. + // + + if (irq) { + ConfigInfo->BusInterruptLevel = irq; + } else { + ConfigInfo->BusInterruptLevel = InterruptLevels[*adapterCount - 1]; + } + + if (ConfigInfo->AdapterInterfaceType == MicroChannel) { + ConfigInfo->InterruptMode = LevelSensitive; + } else { + ConfigInfo->InterruptMode = Latched; + } + } + // + // Get the system physical address for the second IO range. + // + + + if (portBase) { + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(portBase + 0x206), + 1, + TRUE); + } else { + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*adapterCount - 1] + 0x206), + 1, + TRUE); + } + + deviceExtension->BaseIoAddress2[0] = (PIDE_REGISTERS_2)(ioSpace); + + deviceExtension->NumberChannels = 1; + + ConfigInfo->NumberOfBuses = 1; + ConfigInfo->MaximumNumberOfTargets = 2; + + // + // Indicate maximum transfer length is 64k. + // + + ConfigInfo->MaximumTransferLength = 0x10000; + + DebugPrint((1, + "AtapiFindController: Found IDE at %x\n", + deviceExtension->BaseIoAddress1[0])); + + + // + // For Daytona, the atdisk driver gets the first shot at the + // primary and secondary controllers. + // + + if (preConfig == FALSE) { + + + if (*adapterCount - 1 < 2) { + + // + // Determine whether this driver is being initialized by the + // system or as a crash dump driver. + // + + if (ArgumentString) { + + if (AtapiParseArgumentString(ArgumentString, "dump") == 1) { + DebugPrint((3, + "AtapiFindController: Crash dump\n")); + atapiOnly = FALSE; + deviceExtension->DriverMustPoll = TRUE; + } else { + DebugPrint((3, + "AtapiFindController: Atapi Only\n")); + atapiOnly = TRUE; + deviceExtension->DriverMustPoll = FALSE; + } + } else { + + DebugPrint((3, + "AtapiFindController: Atapi Only\n")); + atapiOnly = TRUE; + deviceExtension->DriverMustPoll = FALSE; + } + + } else { + atapiOnly = FALSE; + } + + // + // If this is a PCI machine, pick up all devices. + // + + + pciData = (PPCI_COMMON_CONFIG)&pciBuffer; + + slotData.u.bits.DeviceNumber = 0; + slotData.u.bits.FunctionNumber = 0; + + if (ScsiPortGetBusData(deviceExtension, + PCIConfiguration, + 0, // BusNumber + slotData.u.AsULONG, + pciData, + sizeof(ULONG))) { + + atapiOnly = FALSE; + + // + // Wait on doing this, until a reliable method + // of determining support is found. + // + + #if 0 + deviceExtension->DWordIO = TRUE; + #endif + + } else { + deviceExtension->DWordIO = FALSE; + } + + } else { + + atapiOnly = FALSE; + deviceExtension->DriverMustPoll = FALSE; + + }// preConfig check + + // + // Save the Interrupe Mode for later use + // + deviceExtension->InterruptMode = ConfigInfo->InterruptMode; + + // + // Search for devices on this controller. + // + + if (FindDevices(HwDeviceExtension, + atapiOnly, + 0)) { + + // + // Claim primary or secondary ATA IO range. + // + + if (portBase) { + switch (portBase) { + case 0x170: + ConfigInfo->AtdiskSecondaryClaimed = TRUE; + deviceExtension->PrimaryAddress = FALSE; + break; + case 0x1f0: + ConfigInfo->AtdiskPrimaryClaimed = TRUE; + deviceExtension->PrimaryAddress = TRUE; + break; + default: + break; + } + } else { + if (*adapterCount == 1) { + ConfigInfo->AtdiskPrimaryClaimed = TRUE; + deviceExtension->PrimaryAddress = TRUE; + } else if (*adapterCount == 2) { + ConfigInfo->AtdiskSecondaryClaimed = TRUE; + deviceExtension->PrimaryAddress = FALSE; + } + } + + return(SP_RETURN_FOUND); + } + } + + // + // The entire table has been searched and no adapters have been found. + // There is no need to call again and the device base can now be freed. + // Clear the adapter count for the next bus. + // + + *Again = FALSE; + *(adapterCount) = 0; - /* both channels were claimed: exit */ - if (ConfigInfo->AtdiskPrimaryClaimed == TRUE && - ConfigInfo->AtdiskSecondaryClaimed == TRUE) return(SP_RETURN_NOT_FOUND); - SlotNumber.u.AsULONG = ConfigInfo->SlotNumber; - StartDeviceNumber = SlotNumber.u.bits.DeviceNumber; - StartFunctionNumber = SlotNumber.u.bits.FunctionNumber; - for (DeviceNumber = StartDeviceNumber; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber++) - { - SlotNumber.u.bits.DeviceNumber = DeviceNumber; - for (FunctionNumber = StartFunctionNumber; FunctionNumber < PCI_MAX_FUNCTION; FunctionNumber++) - { - SlotNumber.u.bits.FunctionNumber = FunctionNumber; - ChannelFound = FALSE; - DeviceFound = FALSE; - - DataSize = ScsiPortGetBusData(DeviceExtension, - PCIConfiguration, - ConfigInfo->SystemIoBusNumber, - SlotNumber.u.AsULONG, - &PciConfig, - PCI_COMMON_HDR_LENGTH); - if (DataSize != PCI_COMMON_HDR_LENGTH) - { - if (FunctionNumber == 0) - { - break; - } - else - { - continue; - } - } - - DPRINT("%x %x\n", PciConfig.BaseClass, PciConfig.SubClass); - if (PciConfig.BaseClass == 0x01 && - PciConfig.SubClass == 0x01) // && -// (PciConfig.ProgIf & 0x05) == 0) - { - /* both channels are in compatibility mode */ - DPRINT("Bus %1lu Device %2lu Func %1lu VenID 0x%04hx DevID 0x%04hx\n", - ConfigInfo->SystemIoBusNumber, - SlotNumber.u.bits.DeviceNumber, - SlotNumber.u.bits.FunctionNumber, - PciConfig.VendorID, - PciConfig.DeviceID); - DPRINT("ProgIF 0x%02hx\n", PciConfig.ProgIf); - - DPRINT("Found IDE controller in compatibility mode!\n"); - - ConfigInfo->NumberOfBuses = 1; - ConfigInfo->MaximumNumberOfTargets = 2; - ConfigInfo->MaximumTransferLength = 0x10000; /* max 64Kbyte */ - - if (PciConfig.ProgIf & 0x80) - { - DPRINT("Found IDE Bus Master controller!\n"); - if (PciConfig.u.type0.BaseAddresses[4] & PCI_ADDRESS_IO_SPACE) - { - BusMasterBasePort = PciConfig.u.type0.BaseAddresses[4] & PCI_ADDRESS_IO_ADDRESS_MASK; - DPRINT(" IDE Bus Master Registers at IO %lx\n", BusMasterBasePort); - } - } - if (ConfigInfo->AtdiskPrimaryClaimed == FALSE) - { - /* Both channels unclaimed: Claim primary channel */ - DPRINT("Primary channel!\n"); - ChannelFound = AtapiClaimHwResources(DevExt, - ConfigInfo, - PCIBus, - 0x1F0, - 0x3F4, - BusMasterBasePort, - 14); - *Again = TRUE; - } - else if (ConfigInfo->AtdiskSecondaryClaimed == FALSE) - { - /* Primary channel already claimed: claim secondary channel */ - DPRINT("Secondary channel!\n"); - - ChannelFound = AtapiClaimHwResources(DevExt, - ConfigInfo, - PCIBus, - 0x170, - 0x374, - BusMasterBasePort ? BusMasterBasePort + 8 : 0, - 15); - *Again = FALSE; - } - /* Find attached devices */ - if (ChannelFound) - { - DeviceFound = AtapiFindDevices(DevExt, ConfigInfo); - ConfigInfo->SlotNumber = SlotNumber.u.AsULONG; - DPRINT("AtapiFindCompatiblePciController() returns: SP_RETURN_FOUND\n"); - return(SP_RETURN_FOUND); - } - } - if (FunctionNumber == 0 && !(PciConfig.HeaderType & PCI_MULTIFUNCTION)) - { - break; - } - } - StartFunctionNumber = 0; - } - DPRINT("AtapiFindCompatiblePciController() returns: SP_RETURN_NOT_FOUND\n"); - - return(SP_RETURN_NOT_FOUND); -} -#endif +} // end AtapiFindController() -#ifdef ENABLE_ISA -static ULONG STDCALL -AtapiFindIsaBusController(PVOID DeviceExtension, - PVOID HwContext, - PVOID BusInformation, - PCHAR ArgumentString, - PPORT_CONFIGURATION_INFORMATION ConfigInfo, - PBOOLEAN Again) + + + +BOOLEAN +STDCALL +FindBrokenController( + IN PVOID DeviceExtension, + IN PUCHAR VendorID, + IN ULONG VendorIDLength, + IN PUCHAR DeviceID, + IN ULONG DeviceIDLength, + IN OUT PULONG FunctionNumber, + IN OUT PULONG SlotNumber, + IN ULONG BusNumber, + OUT PBOOLEAN LastSlot + ) + +/*++ + +Routine Description: + + Walk PCI slot information looking for Vendor and Product ID matches. + +Arguments: + +Return Value: + + TRUE if card found. + +--*/ { - PATAPI_MINIPORT_EXTENSION DevExt = (PATAPI_MINIPORT_EXTENSION)DeviceExtension; - BOOLEAN ChannelFound = FALSE; - BOOLEAN DeviceFound = FALSE; + PHW_DEVICE_EXTENSION deviceExtension = DeviceExtension; + ULONG rangeNumber = 0; + ULONG pciBuffer; + ULONG slotNumber; + ULONG functionNumber; + ULONG status; + PCI_SLOT_NUMBER slotData; + PPCI_COMMON_CONFIG pciData; + UCHAR vendorString[5]; + UCHAR deviceString[5]; + PUCHAR vendorStrPtr; + PUCHAR deviceStrPtr; - DPRINT("AtapiFindIsaBusController() called!\n"); + pciData = (PPCI_COMMON_CONFIG)&pciBuffer; - *Again = FALSE; + slotData.u.AsULONG = 0; - ConfigInfo->NumberOfBuses = 1; - ConfigInfo->MaximumNumberOfTargets = 2; - ConfigInfo->MaximumTransferLength = 0x10000; /* max 64Kbyte */ + // + // Look at each device. + // - if (ConfigInfo->AtdiskPrimaryClaimed == FALSE) - { - /* Both channels unclaimed: Claim primary channel */ - DPRINT("Primary channel!\n"); + for (slotNumber = *SlotNumber; + slotNumber < 32; + slotNumber++) { - ChannelFound = AtapiClaimHwResources(DevExt, - ConfigInfo, - Isa, - 0x1F0, - 0x3F4, - 0, - 14); - *Again = TRUE; - } - else if (ConfigInfo->AtdiskSecondaryClaimed == FALSE) - { - /* Primary channel already claimed: claim secondary channel */ - DPRINT("Secondary channel!\n"); + slotData.u.bits.DeviceNumber = slotNumber; - ChannelFound = AtapiClaimHwResources(DevExt, - ConfigInfo, - Isa, - 0x170, - 0x374, - 0, - 15); - *Again = FALSE; - } - else - { - DPRINT("AtapiFindIsaBusController() both channels claimed. Returns: SP_RETURN_NOT_FOUND\n"); - *Again = FALSE; - return(SP_RETURN_NOT_FOUND); - } + // + // Look at each function. + // - /* Find attached devices */ - if (ChannelFound) - { - DeviceFound = AtapiFindDevices(DevExt, - ConfigInfo); - DPRINT("AtapiFindIsaBusController() returns: SP_RETURN_FOUND\n"); - return(SP_RETURN_FOUND); - } - *Again = FALSE; - return SP_RETURN_NOT_FOUND; -} -#endif + for (functionNumber= *FunctionNumber; + functionNumber < 8; + functionNumber++) { + slotData.u.bits.FunctionNumber = functionNumber; -#ifdef ENABLE_NATIVE_PCI -static ULONG STDCALL -AtapiFindNativePciController(PVOID DeviceExtension, - PVOID HwContext, - PVOID BusInformation, - PCHAR ArgumentString, - PPORT_CONFIGURATION_INFORMATION ConfigInfo, - PBOOLEAN Again) -{ - PATAPI_MINIPORT_EXTENSION DevExt = (PATAPI_MINIPORT_EXTENSION)DeviceExtension; - PCI_COMMON_CONFIG PciConfig; - PCI_SLOT_NUMBER SlotNumber; - ULONG DataSize; - ULONG DeviceNumber; - ULONG StartDeviceNumber; - ULONG FunctionNumber; - ULONG StartFunctionNumber; - ULONG BusMasterBasePort; - ULONG Count; - BOOLEAN ChannelFound; + if (!ScsiPortGetBusData(DeviceExtension, + PCIConfiguration, + BusNumber, + slotData.u.AsULONG, + pciData, + sizeof(ULONG))) { - DPRINT("AtapiFindNativePciController() called!\n"); + // + // Out of PCI data. + // - SlotNumber.u.AsULONG = ConfigInfo->SlotNumber; - StartDeviceNumber = SlotNumber.u.bits.DeviceNumber; - StartFunctionNumber = SlotNumber.u.bits.FunctionNumber; - for (DeviceNumber = StartDeviceNumber; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber++) - { - SlotNumber.u.bits.DeviceNumber = DeviceNumber; - for (FunctionNumber = StartFunctionNumber; FunctionNumber < PCI_MAX_FUNCTION; FunctionNumber++) - { - SlotNumber.u.bits.FunctionNumber = FunctionNumber; - DataSize = ScsiPortGetBusData(DeviceExtension, - PCIConfiguration, - ConfigInfo->SystemIoBusNumber, - SlotNumber.u.AsULONG, - &PciConfig, - PCI_COMMON_HDR_LENGTH); - if (DataSize != PCI_COMMON_HDR_LENGTH) - { - break; - } - for (Count = 0; Count < sizeof(PciNativeController)/sizeof(PCI_NATIVE_CONTROLLER); Count++) - { - if (PciConfig.VendorID == PciNativeController[Count].VendorID && - PciConfig.DeviceID == PciNativeController[Count].DeviceID) - { - break; - } - } - if (Count < sizeof(PciNativeController)/sizeof(PCI_NATIVE_CONTROLLER)) - { - /* We have found a known native pci ide controller */ - if ((PciConfig.ProgIf & 0x80) && (PciConfig.u.type0.BaseAddresses[4] & PCI_ADDRESS_IO_SPACE)) - { - DPRINT("Found IDE Bus Master controller!\n"); - BusMasterBasePort = PciConfig.u.type0.BaseAddresses[4] & PCI_ADDRESS_IO_ADDRESS_MASK; - DPRINT(" IDE Bus Master Registers at IO %lx\n", BusMasterBasePort); - } - else - { - BusMasterBasePort = 0; - } - - DPRINT("VendorID: %04x, DeviceID: %04x\n", PciConfig.VendorID, PciConfig.DeviceID); - ConfigInfo->NumberOfBuses = 1; - ConfigInfo->MaximumNumberOfTargets = 2; - ConfigInfo->MaximumTransferLength = 0x10000; /* max 64Kbyte */ - - /* FIXME: - We must not store and use the last tested slot number. If there is a recall - to the some device and we will claim the primary channel again than the call - to ScsiPortGetDeviceBase in AtapiClaimHwResource will fail and we can try to - claim the secondary channel. - */ - ChannelFound = FALSE; - if (LastSlotNumber.u.AsULONG != SlotNumber.u.AsULONG) - { - /* try to claim primary channel */ - if ((PciConfig.u.type0.BaseAddresses[0] & PCI_ADDRESS_IO_SPACE) && - (PciConfig.u.type0.BaseAddresses[1] & PCI_ADDRESS_IO_SPACE)) - { - /* primary channel is enabled */ - ChannelFound = AtapiClaimHwResources(DevExt, - ConfigInfo, - PCIBus, - PciConfig.u.type0.BaseAddresses[0] & PCI_ADDRESS_IO_ADDRESS_MASK, - PciConfig.u.type0.BaseAddresses[1] & PCI_ADDRESS_IO_ADDRESS_MASK, - BusMasterBasePort, - PciConfig.u.type0.InterruptLine); - if (ChannelFound) - { - AtapiFindDevices(DevExt, ConfigInfo); - *Again = TRUE; - ConfigInfo->SlotNumber = LastSlotNumber.u.AsULONG = SlotNumber.u.AsULONG; - return SP_RETURN_FOUND; - } - } - } - if (!ChannelFound) - { - /* try to claim secondary channel */ - if ((PciConfig.u.type0.BaseAddresses[2] & PCI_ADDRESS_IO_SPACE) && - (PciConfig.u.type0.BaseAddresses[3] & PCI_ADDRESS_IO_SPACE)) - { - /* secondary channel is enabled */ - ChannelFound = AtapiClaimHwResources(DevExt, - ConfigInfo, - PCIBus, - PciConfig.u.type0.BaseAddresses[2] & PCI_ADDRESS_IO_ADDRESS_MASK, - PciConfig.u.type0.BaseAddresses[3] & PCI_ADDRESS_IO_ADDRESS_MASK, - BusMasterBasePort ? BusMasterBasePort + 8 : 0, - PciConfig.u.type0.InterruptLine); - if (ChannelFound) - { - AtapiFindDevices(DevExt, ConfigInfo); - *Again = FALSE; - LastSlotNumber.u.AsULONG = 0xFFFFFFFF; - return SP_RETURN_FOUND; - } - } - } - } - } - StartFunctionNumber = 0; - } - *Again = FALSE; - LastSlotNumber.u.AsULONG = 0xFFFFFFFF; - DPRINT("AtapiFindNativePciController() done!\n"); - - return(SP_RETURN_NOT_FOUND); -} -#endif - - -static BOOLEAN STDCALL -AtapiInitialize(IN PVOID DeviceExtension) -{ - return(TRUE); -} - - -static BOOLEAN STDCALL -AtapiResetBus(IN PVOID DeviceExtension, - IN ULONG PathId) -{ - return(TRUE); -} - - -static BOOLEAN STDCALL -AtapiStartIo(IN PVOID DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb) -{ - PATAPI_MINIPORT_EXTENSION DevExt; - ULONG Result; - - DPRINT("AtapiStartIo() called\n"); - - DevExt = (PATAPI_MINIPORT_EXTENSION)DeviceExtension; - - switch (Srb->Function) - { - case SRB_FUNCTION_EXECUTE_SCSI: - DevExt->CurrentSrb = Srb; - if (DevExt->DeviceFlags[Srb->TargetId] & DEVICE_ATAPI) - { - Result = AtapiSendAtapiCommand(DevExt, - Srb); - } - else - { - Result = AtapiSendIdeCommand(DevExt, - Srb); - } - break; - - case SRB_FUNCTION_ABORT_COMMAND: - if (DevExt->CurrentSrb != NULL) - { - Result = SRB_STATUS_ABORT_FAILED; - } - else - { - Result = SRB_STATUS_SUCCESS; - } - break; - - case SRB_FUNCTION_IO_CONTROL: - { - PSRB_IO_CONTROL SrbIoControl = (PSRB_IO_CONTROL)Srb->DataBuffer; - if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) || - Srb->DataTransferLength < SrbIoControl->Length + sizeof(SRB_IO_CONTROL)) - { - Result = SRB_STATUS_INVALID_REQUEST; + *LastSlot = TRUE; + return FALSE; } - else - { - if (!_strnicmp((char*)SrbIoControl->Signature, "ScsiDisk", 8)) - { - switch (SrbIoControl->ControlCode) - { - default: - Result = SRB_STATUS_INVALID_REQUEST; - break; - case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE: - case IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES: - case IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS: - case IOCTL_SCSI_MINIPORT_ENABLE_SMART: - case IOCTL_SCSI_MINIPORT_DISABLE_SMART: - case IOCTL_SCSI_MINIPORT_RETURN_STATUS: - case IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS: - case IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS: - case IOCTL_SCSI_MINIPORT_READ_SMART_LOG: -#if 0 - case IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG: -#endif - Result = AtapiSendSmartCommand(DevExt, Srb); - break; + if (pciData->VendorID == PCI_INVALID_VENDORID) { - case IOCTL_SCSI_MINIPORT_SMART_VERSION: - { - GETVERSIONINPARAMS Version; - ULONG i; + // + // No PCI device, or no more functions on device + // move to next PCI device. + // - DPRINT("IOCTL_SCSI_MINIPORT_SMART_VERSION\n"); + break; + } - RtlZeroMemory(&Version, sizeof(GETVERSIONINPARAMS)); - Version.bVersion = 1; - Version.bRevision = 1; - for (i = 0; i < 2; i++) - { - switch (DevExt->DeviceFlags[i] & (DEVICE_PRESENT|DEVICE_ATAPI)) - { - case DEVICE_PRESENT: - Version.bIDEDeviceMap |= 0x01 << i; - break; -/* - case DEVICE_PRESENT|DEVICE_ATAPI: - Version.bIDEDeviceMap |= 0x11 << i; - break; -*/ - } + // + // Translate hex ids to strings. + // + + vendorStrPtr = vendorString; + deviceStrPtr = deviceString; + AtapiHexToString(pciData->VendorID, &vendorStrPtr); + AtapiHexToString(pciData->DeviceID, &deviceStrPtr); + + DebugPrint((2, + "FindBrokenController: Bus %x Slot %x Function %x Vendor %s Product %s\n", + BusNumber, + slotNumber, + functionNumber, + vendorString, + deviceString)); + + // + // Compare strings. + // + + if (AtapiStringCmp(vendorString, + VendorID, + VendorIDLength) || + AtapiStringCmp(deviceString, + DeviceID, + DeviceIDLength)) { + + // + // Not our PCI device. Try next device/function + // + + continue; + } + + *FunctionNumber = functionNumber; + *SlotNumber = slotNumber; + return TRUE; + + } // next PCI function + + *FunctionNumber = 0; + + } // next PCI slot + + *LastSlot = TRUE; + return FALSE; +} // end FindBrokenController + + +ULONG +STDCALL +AtapiFindNativeModeController( + IN PVOID HwDeviceExtension, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ) +/*++ + +Routine Description: + + This function is called by the OS-specific port driver after + the necessary storage has been allocated, to gather information + about the adapter's configuration. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Context - Address of adapter count + BusInformation - + ArgumentString - Used to determine whether driver is client of ntldr or crash dump utility. + ConfigInfo - Configuration information structure describing HBA + Again - Indicates search for adapters to continue + +Return Value: + + ULONG + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG nativeModeAdapterTableIndex = (ULONG)Context; + ULONG channel; + PUCHAR ioSpace; + BOOLEAN atapiOnly, + deviceFound = FALSE; + UCHAR statusByte; + PCI_SLOT_NUMBER slotData; + PCI_COMMON_CONFIG pciData; + ULONG funcNumber; + ULONG busDataRead; + UCHAR vendorString[5]; + UCHAR deviceString[5]; + PUCHAR vendorStrPtr; + PUCHAR deviceStrPtr; + SCSI_PHYSICAL_ADDRESS IoBasePort1; + SCSI_PHYSICAL_ADDRESS IoBasePort2; + + // + // The following table specifies the ports to be checked when searching for + // an IDE controller. A zero entry terminates the search. + // + + CONST ULONG AdapterAddresses[3] = {0x1F0, 0x170, 0}; + + if (!deviceExtension) { + return SP_RETURN_ERROR; + } + + *Again = FALSE; + + slotData.u.AsULONG = 0; + slotData.u.bits.DeviceNumber = ConfigInfo->SlotNumber; + + for (funcNumber= 0; funcNumber < 8; funcNumber++) { + + slotData.u.bits.FunctionNumber = funcNumber; + + busDataRead = ScsiPortGetBusData(HwDeviceExtension, + PCIConfiguration, + ConfigInfo->SystemIoBusNumber, + slotData.u.AsULONG, + &pciData, + sizeof (pciData)); + if (busDataRead != sizeof (pciData)) { + return SP_RETURN_ERROR; + } + if (pciData.VendorID == PCI_INVALID_VENDORID) { + return SP_RETURN_ERROR; + } + + // + // Translate hex ids to strings. + // + + vendorStrPtr = vendorString; + deviceStrPtr = deviceString; + AtapiHexToString(pciData.VendorID, &vendorStrPtr); + AtapiHexToString(pciData.DeviceID, &deviceStrPtr); + + // + // Compare strings. + // + + if (AtapiStringCmp(vendorString, + NativeModeAdapters[nativeModeAdapterTableIndex].VendorId, + NativeModeAdapters[nativeModeAdapterTableIndex].VendorIdLength) || + AtapiStringCmp(deviceString, + NativeModeAdapters[nativeModeAdapterTableIndex].DeviceId, + NativeModeAdapters[nativeModeAdapterTableIndex].DeviceIdLength)) { + continue; + } + + if (pciData.ProgIf & ((1 << 2) | (1 << 0))) { + // both primary and secondary channel are in native mode + + // Found our device + *Again = TRUE; + + break; + } + } + + if (*Again == TRUE) { + + for (channel = 0; channel < 2; channel++) { + + IoBasePort1 = (*ConfigInfo->AccessRanges)[channel * 2 + 0].RangeStart; + IoBasePort2 = (*ConfigInfo->AccessRanges)[channel * 2 + 1].RangeStart; + IoBasePort2 = ScsiPortConvertUlongToPhysicalAddress(ScsiPortConvertPhysicalAddressToUlong(IoBasePort2) + 2); + + // + // Get the system physical address for this IO range. + // + + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + IoBasePort1, + 8, + TRUE); + + // + // Check if ioSpace accessible. + // + + if (!ioSpace) { + continue; + } + + // + // Select master. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->DriveSelect, 0xA0); + + // + // Check if card at this address. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow, 0xAA); + + // + // Check if indentifier can be read back. + // + + if ((statusByte = ScsiPortReadPortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow)) != 0xAA) { + + DebugPrint((2, + "AtapiFindPciController: Identifier read back from Master (%x)\n", + statusByte)); + + + // + // Select slave. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->DriveSelect, 0xB0); + + // + // See if slave is present. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow, 0xAA); + + if ((statusByte = ScsiPortReadPortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow)) != 0xAA) { + + DebugPrint((2, + "AtapiFindPciController: Identifier read back from Slave (%x)\n", + statusByte)); + + // + // + // No controller at this base address. + // + + ScsiPortFreeDeviceBase(HwDeviceExtension, + ioSpace); + + // + // If the chip is there, but we couldn't find the primary channel, try the secondary. + // If we couldn't find a secondary, who cares. + // + + if (channel == 1) { + + goto setStatusAndExit; + + } else { + continue; + } + } + } + + // + // Record base IO address. + // + + deviceExtension->BaseIoAddress1[channel] = (PIDE_REGISTERS_1)(ioSpace); + + // + // Get the system physical address for the second IO range. + // + + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + IoBasePort2, + 1, + TRUE); + + deviceExtension->BaseIoAddress2[channel] = (PIDE_REGISTERS_2)(ioSpace); + + deviceExtension->NumberChannels = 2; + + // + // Indicate only one bus. + // + + ConfigInfo->NumberOfBuses = 1; + + // + // Indicate four devices can be attached to the adapter, since we + // have to serialize access to the two channels. + // + + ConfigInfo->MaximumNumberOfTargets = 4; + + // + // Indicate maximum transfer length is 64k. + // + + ConfigInfo->MaximumTransferLength = 0x10000; + + DebugPrint((1, + "AtapiFindPciController: Found native mode IDE at %x\n", + deviceExtension->BaseIoAddress1[channel])); + + // + // Since we will always pick up this part, and not atdisk, so indicate. + // + + atapiOnly = FALSE; + + // + // Save the Interrupe Mode for later use + // + deviceExtension->InterruptMode = ConfigInfo->InterruptMode; + + // + // Search for devices on this controller. + // + + if (FindDevices(HwDeviceExtension, + atapiOnly, + channel)){ + deviceFound = TRUE; + } + + // + // Claim primary or secondary ATA IO range. + // + + if (ScsiPortConvertPhysicalAddressToUlong(IoBasePort1) == AdapterAddresses[0]) { + ConfigInfo->AtdiskPrimaryClaimed = TRUE; + deviceExtension->PrimaryAddress = TRUE; + + } else if (ScsiPortConvertPhysicalAddressToUlong(IoBasePort2) == AdapterAddresses[1]) { + ConfigInfo->AtdiskSecondaryClaimed = TRUE; + deviceExtension->PrimaryAddress = FALSE; + } + } + } + +setStatusAndExit: + + if (deviceFound) { + + *Again = TRUE; + return SP_RETURN_FOUND; + } + + *Again = FALSE; + return SP_RETURN_NOT_FOUND; + +} // end AtapiFindNativeModeController() + + +ULONG +STDCALL +AtapiFindPCIController( + IN PVOID HwDeviceExtension, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ) +/*++ + +Routine Description: + + This function is called by the OS-specific port driver after + the necessary storage has been allocated, to gather information + about the adapter's configuration. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Context - Address of adapter count + BusInformation - + ArgumentString - Used to determine whether driver is client of ntldr or crash dump utility. + ConfigInfo - Configuration information structure describing HBA + Again - Indicates search for adapters to continue + +Return Value: + + ULONG + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PULONG adapterCount = (PULONG)Context; + ULONG channel = 0; + static ULONG functionNumber, + slotNumber, + controllers; + ULONG i,j; + PUCHAR ioSpace; + BOOLEAN atapiOnly, + lastSlot, + controllerFound = FALSE, + deviceFound = FALSE; + UCHAR statusByte; + + // + // The following table specifies the ports to be checked when searching for + // an IDE controller. A zero entry terminates the search. + // + + CONST ULONG AdapterAddresses[5] = {0x1F0, 0x170, 0x1e8, 0x168, 0}; + + // + // The following table specifies interrupt levels corresponding to the + // port addresses in the previous table. + // + + CONST ULONG InterruptLevels[5] = {14, 15, 11, 10, 0}; + + if (!deviceExtension) { + return SP_RETURN_ERROR; + } + + // + // Since scsiport will call this function first before it calls AtapiFindController + // we need to bypass it if we have data installed in ConfigInfo, by the pcmcia driver. + // In that case atapifindcontroller should be called first. + // Instead of modifying atapi driverEntry to search of PCIBus first (now its ISA) + // the check is put here. + // + + if (ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[0].RangeStart) != 0) { + + return AtapiFindController(HwDeviceExtension, + Context, + BusInformation, + ArgumentString, + ConfigInfo, + Again); + } + + + // + // Gronk PCI config space looking for the broken PCI IDE controllers that have only + // one FIFO for both channels. + // Don't do this. It's incorrect and nasty. It has to be done to work around these + // broken parts, no other reason can justify this. + // + + for (i = controllers; i < BROKEN_ADAPTERS; i++) { + + // + // Determine if both channels are enabled and have devices. + // + + lastSlot = FALSE; + + if (FindBrokenController(deviceExtension, + BrokenAdapters[i].VendorId, + BrokenAdapters[i].VendorIdLength, + BrokenAdapters[i].DeviceId, + BrokenAdapters[i].DeviceIdLength, + &functionNumber, + &slotNumber, + ConfigInfo->SystemIoBusNumber, + &lastSlot)) { + + slotNumber++; + functionNumber = 0; + controllerFound = TRUE; + + DebugPrint((1, + "Found broken PCI IDE controller: VendorId %s, DeviceId %s\n", + BrokenAdapters[i].VendorId, + BrokenAdapters[i].DeviceId)); + + if (AdapterAddresses[*adapterCount] != 0) { + + for (j = 0; j < 2; j++) { + + // + // Get the system physical address for this IO range. + // + + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*adapterCount]), + 8, + TRUE); + + // + // Update the adapter count. + // + + (*adapterCount)++; + + // + // Check if ioSpace accessible. + // + + if (!ioSpace) { + continue; + } + + // + // Select master. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->DriveSelect, 0xA0); + + // + // Check if card at this address. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow, 0xAA); + + // + // Check if indentifier can be read back. + // + + if ((statusByte = ScsiPortReadPortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow)) != 0xAA) { + + DebugPrint((2, + "AtapiFindPciController: Identifier read back from Master (%x)\n", + statusByte)); + + + // + // Select slave. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->DriveSelect, 0xB0); + + // + // See if slave is present. + // + + ScsiPortWritePortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow, 0xAA); + + if ((statusByte = ScsiPortReadPortUchar(&((PIDE_REGISTERS_1)ioSpace)->CylinderLow)) != 0xAA) { + + DebugPrint((2, + "AtapiFindPciController: Identifier read back from Slave (%x)\n", + statusByte)); + + // + // + // No controller at this base address. + // + + ScsiPortFreeDeviceBase(HwDeviceExtension, + ioSpace); + + // + // If the chip is there, but we couldn't find the primary channel, try the secondary. + // If we couldn't find a secondary, who cares. + // + + if (j == 1) { + + goto setStatusAndExit; + + } else { + continue; } - Version.fCapabilities = CAP_ATA_ID_CMD/*|CAP_ATAPI_ID_CMD|CAP_SMART_CMD*/; - SrbIoControl->Length = min(sizeof(GETVERSIONINPARAMS), Srb->DataTransferLength - sizeof(SRB_IO_CONTROL)); - memcpy(SrbIoControl + 1, &Version, SrbIoControl->Length); - Result = SRB_STATUS_SUCCESS; - break; + } + } + + if (controllerFound) { + + // + // Record base IO address. + // + + deviceExtension->BaseIoAddress1[channel] = (PIDE_REGISTERS_1)(ioSpace); + + // + // Fill in the access array information. + // + + (*ConfigInfo->AccessRanges)[channel].RangeStart = + ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*adapterCount - 1]); + + (*ConfigInfo->AccessRanges)[channel].RangeLength = 8; + (*ConfigInfo->AccessRanges)[channel].RangeInMemory = FALSE; + + // + // Indicate the interrupt level corresponding to this IO range. + // + + if (channel == 0) { + ConfigInfo->BusInterruptLevel = InterruptLevels[*adapterCount - 1]; + ConfigInfo->InterruptMode = Latched; + } else { + ConfigInfo->BusInterruptLevel2 = InterruptLevels[*adapterCount - 1]; + ConfigInfo->InterruptMode2 = Latched; } - case IOCTL_SCSI_MINIPORT_IDENTIFY: - { - SENDCMDOUTPARAMS OutParams; - SENDCMDINPARAMS InParams = *(PSENDCMDINPARAMS)(SrbIoControl + 1); + // + // Get the system physical address for the second IO range. + // - DPRINT("IOCTL_SCSI_MINIPORT_IDENTIFY\n"); + ioSpace = ScsiPortGetDeviceBase(HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*adapterCount - 1] + 0x206), + 1, + TRUE); - if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1) - { - Result = SRB_STATUS_INVALID_REQUEST; - break; - } - - RtlZeroMemory(&OutParams, sizeof(SENDCMDOUTPARAMS)); + deviceExtension->BaseIoAddress2[channel] = (PIDE_REGISTERS_2)(ioSpace); - if (InParams.irDriveRegs.bCommandReg != IDE_CMD_IDENT_ATA_DRV) - { - DPRINT("bCommandReg: %x\n", InParams.irDriveRegs.bCommandReg); - OutParams.DriverStatus.bIDEError = 1; - Result = SRB_STATUS_INVALID_REQUEST; - } - else if (InParams.bDriveNumber > 1 || - (DevExt->DeviceFlags[InParams.bDriveNumber] & (DEVICE_PRESENT|DEVICE_ATAPI)) != DEVICE_PRESENT) - { - OutParams.DriverStatus.bIDEError = 1; - Result = SRB_STATUS_NO_DEVICE; - } - else - { - Result = SRB_STATUS_SUCCESS; - } - if (Result == SRB_STATUS_SUCCESS) - { - SrbIoControl->Length = min(sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE, Srb->DataTransferLength - sizeof(SRB_IO_CONTROL)); - } - else - { - SrbIoControl->Length = min(sizeof(SENDCMDOUTPARAMS) - 1, Srb->DataTransferLength - sizeof(SRB_IO_CONTROL)); - } + deviceExtension->NumberChannels = 2; - if (SrbIoControl->Length >= sizeof(SENDCMDOUTPARAMS) - 1) - { - OutParams.cBufferSize = min(SrbIoControl->Length, IDENTIFY_BUFFER_SIZE); - } - - memcpy(SrbIoControl + 1, &OutParams, min (SrbIoControl->Length, sizeof(SENDCMDOUTPARAMS) - 1)); - - if (SrbIoControl->Length > sizeof(SENDCMDOUTPARAMS) - 1) - { - RtlCopyMemory((PVOID)((ULONG_PTR)(SrbIoControl + 1) + sizeof(SENDCMDOUTPARAMS) - 1), &DevExt->DeviceParams[InParams.bDriveNumber], OutParams.cBufferSize); - } - break; + // + // Indicate only one bus. + // + + ConfigInfo->NumberOfBuses = 1; + + // + // Indicate four devices can be attached to the adapter, since we + // have to serialize access to the two channels. + // + + ConfigInfo->MaximumNumberOfTargets = 4; + + // + // Indicate maximum transfer length is 64k. + // + + ConfigInfo->MaximumTransferLength = 0x10000; + + DebugPrint((1, + "AtapiFindPciController: Found broken IDE at %x\n", + deviceExtension->BaseIoAddress1[channel])); + + // + // Since we will always pick up this part, and not atdisk, so indicate. + // + + atapiOnly = FALSE; + + // + // Save the Interrupe Mode for later use + // + deviceExtension->InterruptMode = ConfigInfo->InterruptMode; + + // + // Search for devices on this controller. + // + + if (FindDevices(HwDeviceExtension, + atapiOnly, + channel++)){ + deviceFound = TRUE; + } + + // + // Claim primary or secondary ATA IO range. + // + + if (*adapterCount == 1) { + ConfigInfo->AtdiskPrimaryClaimed = TRUE; + deviceExtension->PrimaryAddress = TRUE; + + } else if (*adapterCount == 2) { + ConfigInfo->AtdiskSecondaryClaimed = TRUE; + deviceExtension->PrimaryAddress = FALSE; } } } - else - { - Result = SRB_STATUS_INVALID_REQUEST; - SrbIoControl->Length = 0; - } } - break; } - default: - Result = SRB_STATUS_INVALID_REQUEST; - break; - } +setStatusAndExit: - Srb->SrbStatus = Result; - - - if (Result != SRB_STATUS_PENDING) - { - DevExt->CurrentSrb = NULL; - - ScsiPortNotification(RequestComplete, - DeviceExtension, - Srb); - ScsiPortNotification(NextRequest, - DeviceExtension, - NULL); - } - else - { - DPRINT("SrbStatus = SRB_STATUS_PENDING\n"); - } - - DPRINT("AtapiStartIo() done\n"); - - return(TRUE); -} - -static BOOLEAN STDCALL -AtapiInterrupt(IN PVOID DeviceExtension) -{ - PATAPI_MINIPORT_EXTENSION DevExt; - UCHAR Status; - DevExt = (PATAPI_MINIPORT_EXTENSION)DeviceExtension; - - if (DevExt->Handler == NULL) - { - Status = IDEReadAltStatus(DevExt->ControlPortBase); - if ((Status & (IDE_SR_DRDY|IDE_SR_BUSY|IDE_SR_ERR|IDE_SR_DRQ)) != IDE_SR_DRDY) - { - static ULONG Count = 0; - Count++; - DPRINT1("Unexpected Interrupt, CommandPort=%04x, Status=%02x, Count=%ld\n", - DevExt->CommandPortBase, Status, Count); - } - return FALSE; - } -#ifdef ENABLE_DMA - if (DevExt->UseDma) - { - Status = IDEReadDMAStatus(DevExt->BusMasterRegisterBase); - if (!(Status & 0x04)) - { - return FALSE; - } - } - else -#endif - { - Status = IDEReadAltStatus(DevExt->ControlPortBase); - if (Status & IDE_SR_BUSY) - { - return FALSE; - } - } - return DevExt->Handler(DevExt); -} - -// ---------------------------------------------------- Discardable statics - -#ifdef ENABLE_DMA -static BOOLEAN -AtapiConfigDma(PATAPI_MINIPORT_EXTENSION DeviceExtension, ULONG UnitNumber) -{ - BOOLEAN Result = FALSE; - UCHAR Status; - - if (UnitNumber < 2) - { - if (DeviceExtension->PRDTable) - { - if (DeviceExtension->DeviceParams[UnitNumber].Capabilities & IDE_DRID_DMA_SUPPORTED) - { - if ((DeviceExtension->DeviceParams[UnitNumber].TMFieldsValid & 0x0004) && - (DeviceExtension->DeviceParams[UnitNumber].UltraDmaModes & 0x7F00)) - { - Result = TRUE; - } - else if (DeviceExtension->DeviceParams[UnitNumber].TMFieldsValid & 0x0002) - { - if ((DeviceExtension->DeviceParams[UnitNumber].MultiDmaModes & 0x0404) == 0x0404) - { - Result = TRUE; - } -#if 0 - /* FIXME: - * should we support single mode dma ? - */ - else if ((DeviceExtension->DeviceParams[UnitNumber].DmaModes & 0x0404) == 0x0404) - { - Result = TRUE; - } -#endif - } - Status = IDEReadDMAStatus(DeviceExtension->BusMasterRegisterBase); - if (Result) - { - IDEWriteDMAStatus(DeviceExtension->BusMasterRegisterBase, Status | (UnitNumber ? 0x40 : 0x20)); - } - else - { - IDEWriteDMAStatus(DeviceExtension->BusMasterRegisterBase, Status & (UnitNumber ? ~0x40 : ~0x20)); - } - } - } - } - return Result; -} -#endif - -/********************************************************************** - * NAME INTERNAL - * AtapiFindDevices - * - * DESCRIPTION - * Searches for devices on the given port. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceExtension - * Port device specific information. - * - * ConfigInfo - * Port configuration information. - * - * RETURN VALUE - * TRUE: At least one device is attached to the port. - * FALSE: No device is attached to the port. - */ - -static BOOLEAN -AtapiFindDevices(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PPORT_CONFIGURATION_INFORMATION ConfigInfo) -{ - BOOLEAN DeviceFound = FALSE; - ULONG CommandPortBase; - ULONG ControlPortBase; - ULONG UnitNumber; - ULONG Retries; - UCHAR High; - UCHAR Low; - - DPRINT("AtapiFindDevices() called\n"); - - CommandPortBase = ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[0].RangeStart); - DPRINT(" CommandPortBase: %x\n", CommandPortBase); - - ControlPortBase = ScsiPortConvertPhysicalAddressToUlong((*ConfigInfo->AccessRanges)[1].RangeStart); - DPRINT(" ControlPortBase: %x\n", ControlPortBase); - - for (UnitNumber = 0; UnitNumber < 2; UnitNumber++) - { - /* Select drive */ - IDEWriteDriveHead(CommandPortBase, - IDE_DH_FIXED | (UnitNumber ? IDE_DH_DRV1 : 0)); - ScsiPortStallExecution(500); - - /* Disable interrupts */ - IDEWriteDriveControl(ControlPortBase, - IDE_DC_nIEN); - ScsiPortStallExecution(500); - - /* Check if a device is attached to the interface */ - IDEWriteCylinderHigh(CommandPortBase, 0xaa); - IDEWriteCylinderLow(CommandPortBase, 0x55); - - High = IDEReadCylinderHigh(CommandPortBase); - Low = IDEReadCylinderLow(CommandPortBase); - - IDEWriteCylinderHigh(CommandPortBase, 0); - IDEWriteCylinderLow(CommandPortBase, 0); - - if (Low != 0x55 || High != 0xaa) - { - DPRINT("No Drive found. UnitNumber %d CommandPortBase %x\n", UnitNumber, CommandPortBase); - continue; - } - - AtapiExecuteCommand(DeviceExtension, IDE_CMD_RESET, NULL); - - for (Retries = 0; Retries < 20000; Retries++) - { - if (!(IDEReadStatus(CommandPortBase) & IDE_SR_BUSY)) - { - break; - } - ScsiPortStallExecution(150); - } - if (Retries >= 20000) - { - DPRINT("Timeout on drive %lu\n", UnitNumber); - DeviceExtension->DeviceFlags[UnitNumber] &= ~DEVICE_PRESENT; - continue; - } - - High = IDEReadCylinderHigh(CommandPortBase); - Low = IDEReadCylinderLow(CommandPortBase); - - DPRINT(" Check drive %lu: High 0x%x Low 0x%x\n", - UnitNumber, - High, - Low); - - if (High == 0xEB && Low == 0x14) - { - if (AtapiIdentifyDevice(CommandPortBase, - ControlPortBase, - UnitNumber, - TRUE, - &DeviceExtension->DeviceParams[UnitNumber])) - { - DPRINT(" ATAPI drive found!\n"); - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_PRESENT; - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_ATAPI; - DeviceExtension->TransferSize[UnitNumber] = - DeviceExtension->DeviceParams[UnitNumber].BytesPerSector; -#ifdef ENABLE_DMA - if (AtapiConfigDma(DeviceExtension, UnitNumber)) - { - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_DMA_CMD; - } -#endif - if (!(DeviceExtension->DeviceParams[UnitNumber].SupportedFeatures83 & 0x1000) || - !(DeviceExtension->DeviceParams[UnitNumber].EnabledFeatures86 & 0x1000)) - { - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_NO_FLUSH; - } - - /* Don't flush CD/DVD drives */ - if (((DeviceExtension->DeviceParams[UnitNumber].ConfigBits >> 8) & 0x1F) == READ_ONLY_DIRECT_ACCESS_DEVICE) - { - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_NO_FLUSH; - } - DeviceFound = TRUE; - } - else - { - DPRINT(" No ATAPI drive found!\n"); - } - } - else - { - if (AtapiIdentifyDevice(CommandPortBase, - ControlPortBase, - UnitNumber, - FALSE, - &DeviceExtension->DeviceParams[UnitNumber])) - { - DPRINT(" IDE drive found!\n"); - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_PRESENT; - DeviceExtension->TransferSize[UnitNumber] = DeviceExtension->DeviceParams[UnitNumber].BytesPerSector; - if ((DeviceExtension->DeviceParams[UnitNumber].RWMultImplemented & 0x8000) && - (DeviceExtension->DeviceParams[UnitNumber].RWMultImplemented & 0xff) && - (DeviceExtension->DeviceParams[UnitNumber].RWMultCurrent & 0x100) && - (DeviceExtension->DeviceParams[UnitNumber].RWMultCurrent & 0xff)) - { - DeviceExtension->TransferSize[UnitNumber] *= (DeviceExtension->DeviceParams[UnitNumber].RWMultCurrent & 0xff); - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_MULTI_SECTOR_CMD; - } - if (DeviceExtension->DeviceParams[UnitNumber].SupportedFeatures83 & 0x0400 && - DeviceExtension->DeviceParams[UnitNumber].EnabledFeatures86 & 0x0400) - { - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_48BIT_ADDRESS; - } - if (DeviceExtension->DeviceParams[UnitNumber].DWordIo) - { - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_DWORD_IO; - } -#ifdef ENABLE_DMA - if (AtapiConfigDma(DeviceExtension, UnitNumber)) - { - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_DMA_CMD; - } -#endif - if (DeviceExtension->DeviceFlags[UnitNumber] & DEVICE_48BIT_ADDRESS) - { - if (!(DeviceExtension->DeviceParams[UnitNumber].SupportedFeatures83 & 0x2000) || - !(DeviceExtension->DeviceParams[UnitNumber].EnabledFeatures86 & 0x2000)) - { - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_NO_FLUSH; - } - } - else - { - if (!(DeviceExtension->DeviceParams[UnitNumber].SupportedFeatures83 & 0x1000) || - !(DeviceExtension->DeviceParams[UnitNumber].EnabledFeatures86 & 0x1000)) - { - DeviceExtension->DeviceFlags[UnitNumber] |= DEVICE_NO_FLUSH; - } - } - DeviceFound = TRUE; - } - else - { - DPRINT(" No IDE drive found!\n"); - } - } - } - - /* Reset pending interrupts */ - IDEReadStatus(CommandPortBase); - /* Reenable interrupts */ - IDEWriteDriveControl(ControlPortBase, 0); - ScsiPortStallExecution(500); - /* Return with drive 0 selected */ - IDEWriteDriveHead(CommandPortBase, IDE_DH_FIXED); - ScsiPortStallExecution(500); - - DPRINT("AtapiFindDrives() done (DeviceFound %s)\n", (DeviceFound) ? "TRUE" : "FALSE"); - - return(DeviceFound); -} - - -/* - * AtapiIdentifyDevice - * - * DESCRIPTION: - * Get the identification block from the drive - * - * RUN LEVEL: - * PASSIVE_LEVEL - * - * ARGUMENTS: - * CommandPort - * Address of the command port - * ControlPort - * Address of the control port - * DriveNum - * The drive index (0,1) - * Atapi - * Send an ATA(FALSE) or an ATAPI(TRUE) identify comand - * DrvParms - * Address to write drive ident block - * - * RETURNS: - * TRUE: The drive identification block was retrieved successfully - * FALSE: an error ocurred - */ - -static BOOLEAN -AtapiIdentifyDevice(IN ULONG CommandPort, - IN ULONG ControlPort, - IN ULONG DriveNum, - IN BOOLEAN Atapi, - OUT PIDE_DRIVE_IDENTIFY DrvParms) -{ - LONG i; - ULONG mode; - char SerialNumber[20]; - char FirmwareRev[8]; - char ModelNumber[40]; - - /* Get the Drive Identify block from drive or die */ - if (AtapiPolledRead(CommandPort, - ControlPort, - 0, - 1, - 0, - 0, - 0, - (DriveNum ? IDE_DH_DRV1 : 0), - (Atapi ? IDE_CMD_IDENT_ATAPI_DRV : IDE_CMD_IDENT_ATA_DRV), - (PUCHAR)DrvParms) == FALSE) - { - DPRINT("AtapiPolledRead() failed\n"); - return FALSE; - } - - /* Report on drive parameters if debug mode */ - memcpy(SerialNumber, DrvParms->SerialNumber, 20); - memcpy(FirmwareRev, DrvParms->FirmwareRev, 8); - memcpy(ModelNumber, DrvParms->ModelNumber, 40); - IDESwapBytePairs((PUCHAR)SerialNumber, 20); - IDESwapBytePairs((PUCHAR)FirmwareRev, 8); - IDESwapBytePairs((PUCHAR)ModelNumber, 40); - DPRINT("Config:%04x Cyls:%5d Heads:%2d Sectors/Track:%3d Gaps:%02d %02d\n", - DrvParms->ConfigBits, - DrvParms->LogicalCyls, - DrvParms->LogicalHeads, - DrvParms->SectorsPerTrack, - DrvParms->InterSectorGap, - DrvParms->InterSectorGapSize); - DPRINT("Bytes/PLO:%3d Vendor Cnt:%2d Serial number:[%.20s]\n", - DrvParms->BytesInPLO, - DrvParms->VendorUniqueCnt, - SerialNumber); - DPRINT("Cntlr type:%2d BufSiz:%5d ECC bytes:%3d Firmware Rev:[%.8s]\n", - DrvParms->ControllerType, - DrvParms->BufferSize * IDE_SECTOR_BUF_SZ, - DrvParms->ECCByteCnt, - FirmwareRev); - DPRINT("Model:[%.40s]\n", ModelNumber); - DPRINT("RWMultMax?:%04x RWMult?:%02x LBA:%d DMA:%d MinPIO:%d ns MinDMA:%d ns\n", - (DrvParms->RWMultImplemented), - (DrvParms->RWMultCurrent) & 0xff, - (DrvParms->Capabilities & IDE_DRID_LBA_SUPPORTED) ? 1 : 0, - (DrvParms->Capabilities & IDE_DRID_DMA_SUPPORTED) ? 1 : 0, - DrvParms->MinPIOTransTime, - DrvParms->MinDMATransTime); - DPRINT("TM:Cyls:%d Heads:%d Sectors/Trk:%d Capacity:%ld\n", - DrvParms->TMCylinders, - DrvParms->TMHeads, - DrvParms->TMSectorsPerTrk, - (ULONG)(DrvParms->TMCapacityLo + (DrvParms->TMCapacityHi << 16))); - DPRINT("TM:SectorCount: 0x%04x%04x = %lu\n", - DrvParms->TMSectorCountHi, - DrvParms->TMSectorCountLo, - (ULONG)((DrvParms->TMSectorCountHi << 16) + DrvParms->TMSectorCountLo)); - DPRINT("SupportedFeatures83: %x, EnabledFeatures86 %x\n", DrvParms->SupportedFeatures83, DrvParms->EnabledFeatures86); - DPRINT("Max48BitAddress: %I64d\n", *(PULONGLONG)DrvParms->Max48BitAddress); - if (DrvParms->TMFieldsValid & 0x0004) - { - if ((DrvParms->UltraDmaModes >> 8) && (DrvParms->UltraDmaModes & 0xff)) - { - mode = 7; - while (!(DrvParms->UltraDmaModes & (0x0100 << mode))) - { - mode--; - } - DPRINT("Ultra DMA mode %d is selected\n", mode); - } - else if ((DrvParms->MultiDmaModes >> 8) & (DrvParms->MultiDmaModes & 0x07)) - { - mode = 2; - while(!(DrvParms->MultiDmaModes & (0x01 << mode))) - { - mode--; - } - DPRINT("Multi DMA mode %d is selected\n", mode); - } - } - - if (! Atapi && 0 != (DrvParms->Capabilities & IDE_DRID_LBA_SUPPORTED)) - { - /* LBA ATA drives always have a sector size of 512 */ - DrvParms->BytesPerSector = 512; - } - else - { - DPRINT("BytesPerSector %d\n", DrvParms->BytesPerSector); - if (DrvParms->BytesPerSector == 0) - { - DrvParms->BytesPerSector = 512; + if (lastSlot) { + slotNumber = 0; + functionNumber = 0; } - else - { - for (i = 15; i >= 0; i--) - { - if (DrvParms->BytesPerSector & (1 << i)) - { - DrvParms->BytesPerSector = 1 << i; - break; - } + + controllers = i; + + if (controllerFound && deviceFound) { + + *Again = TRUE; + return SP_RETURN_FOUND; + } + } + + + // + // The entire table has been searched and no adapters have been found. + // + + *Again = FALSE; + + return SP_RETURN_NOT_FOUND; + +} // end AtapiFindPCIController() + + +ULONG +STDCALL +Atapi2Scsi( + IN PSCSI_REQUEST_BLOCK Srb, + IN char *DataBuffer, + IN ULONG ByteCount + ) +{ + ULONG bytesAdjust = 0; + if (Srb->Cdb[0] == ATAPI_MODE_SENSE) { + + PMODE_PARAMETER_HEADER_10 header_10 = (PMODE_PARAMETER_HEADER_10)DataBuffer; + PMODE_PARAMETER_HEADER header = (PMODE_PARAMETER_HEADER)DataBuffer; + + header->ModeDataLength = header_10->ModeDataLengthLsb; + header->MediumType = header_10->MediumType; + + // + // ATAPI Mode Parameter Header doesn't have these fields. + // + + header->DeviceSpecificParameter = header_10->Reserved[0]; + header->BlockDescriptorLength = header_10->Reserved[1]; + + ByteCount -= sizeof(MODE_PARAMETER_HEADER_10); + if (ByteCount > 0) + ScsiPortMoveMemory(DataBuffer+sizeof(MODE_PARAMETER_HEADER), + DataBuffer+sizeof(MODE_PARAMETER_HEADER_10), + ByteCount); + + // + // Change ATAPI_MODE_SENSE opcode back to SCSIOP_MODE_SENSE + // so that we don't convert again. + // + + Srb->Cdb[0] = SCSIOP_MODE_SENSE; + + bytesAdjust = sizeof(MODE_PARAMETER_HEADER_10) - + sizeof(MODE_PARAMETER_HEADER); + + + } + + // + // Convert to words. + // + + return bytesAdjust >> 1; +} + + +VOID +STDCALL +AtapiCallBack( + IN PVOID HwDeviceExtension + ) +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCSI_REQUEST_BLOCK srb = deviceExtension->CurrentSrb; + PATAPI_REGISTERS_2 baseIoAddress2; + UCHAR statusByte; + + // + // If the last command was DSC restrictive, see if it's set. If so, the device is + // ready for a new request. Otherwise, reset the timer and come back to here later. + // + + if (srb && (!(deviceExtension->ExpectingInterrupt))) { +#if DBG + if (!IS_RDP((srb->Cdb[0]))) { + DebugPrint((1, + "AtapiCallBack: Invalid CDB marked as RDP - %x\n", + srb->Cdb[0])); + } +#endif + + baseIoAddress2 = (PATAPI_REGISTERS_2)deviceExtension->BaseIoAddress2[srb->TargetId >> 1]; + if (deviceExtension->RDP) { + GetStatus(baseIoAddress2, statusByte); + if (statusByte & IDE_STATUS_DSC) { + + ScsiPortNotification(RequestComplete, + deviceExtension, + srb); + + // + // Clear current SRB. + // + + deviceExtension->CurrentSrb = NULL; + deviceExtension->RDP = FALSE; + + // + // Ask for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + + + return; + + } else { + + DebugPrint((3, + "AtapiCallBack: Requesting another timer for Op %x\n", + deviceExtension->CurrentSrb->Cdb[0])); + + ScsiPortNotification(RequestTimerCall, + HwDeviceExtension, + AtapiCallBack, + 1000); + return; } } } - DPRINT("BytesPerSector %d\n", DrvParms->BytesPerSector); - return TRUE; + DebugPrint((2, + "AtapiCallBack: Calling ISR directly due to BUSY\n")); + AtapiInterrupt(HwDeviceExtension); } + +BOOLEAN +STDCALL +AtapiInterrupt( + IN PVOID HwDeviceExtension + ) -// AtapiPolledRead -// -// DESCRIPTION: -// Read a sector of data from the drive in a polled fashion. -// -// RUN LEVEL: -// PASSIVE_LEVEL -// -// ARGUMENTS: -// IN ULONG CommandPort Address of command port for drive -// IN ULONG ControlPort Address of control port for drive -// IN UCHAR PreComp Value to write to precomp register -// IN UCHAR SectorCnt Value to write to sectorCnt register -// IN UCHAR SectorNum Value to write to sectorNum register -// IN UCHAR CylinderLow Value to write to CylinderLow register -// IN UCHAR CylinderHigh Value to write to CylinderHigh register -// IN UCHAR DrvHead Value to write to Drive/Head register -// IN UCHAR Command Value to write to Command register -// OUT PUCHAR Buffer Buffer for output data -// -// RETURNS: -// BOOLEAN: TRUE success, FALSE error -// +/*++ + +Routine Description: + + This is the interrupt service routine for ATAPI IDE miniport driver. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + TRUE if expecting an interrupt. + +--*/ -static BOOLEAN -AtapiPolledRead(IN ULONG CommandPort, - IN ULONG ControlPort, - IN UCHAR PreComp, - IN UCHAR SectorCnt, - IN UCHAR SectorNum, - IN UCHAR CylinderLow, - IN UCHAR CylinderHigh, - IN UCHAR DrvHead, - IN UCHAR Command, - OUT PUCHAR Buffer) { - ULONG SectorCount = 0; - ULONG RetryCount; - BOOLEAN Junk = FALSE; - UCHAR Status; + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCSI_REQUEST_BLOCK srb = deviceExtension->CurrentSrb; + PATAPI_REGISTERS_1 baseIoAddress1; + PATAPI_REGISTERS_2 baseIoAddress2; + ULONG wordCount = 0, wordsThisInterrupt = 256; + ULONG status; + ULONG i; + UCHAR statusByte,interruptReason; + BOOLEAN commandComplete = FALSE; + BOOLEAN atapiDev = FALSE; - /* Wait for BUSY to clear */ - for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++) - { - Status = IDEReadStatus(CommandPort); - if (!(Status & IDE_SR_BUSY)) - { - break; + if (srb) { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[srb->TargetId >> 1]; + baseIoAddress2 = (PATAPI_REGISTERS_2)deviceExtension->BaseIoAddress2[srb->TargetId >> 1]; + } else { + DebugPrint((2, + "AtapiInterrupt: CurrentSrb is NULL\n")); + // + // We can only support one ATAPI IDE master on Carolina, so find + // the base address that is non NULL and clear its interrupt before + // returning. + // + +#ifdef _PPC_ + + if ((PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[0] != NULL) { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[0]; + } else { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[1]; } - ScsiPortStallExecution(10); - } - DPRINT("status=%02x\n", Status); - DPRINT("waited %ld usecs for busy to clear\n", RetryCount * 10); - if (RetryCount >= IDE_MAX_BUSY_RETRIES) - { - DPRINT("Drive is BUSY for too long\n"); - return FALSE; - } - /* Write Drive/Head to select drive */ - IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead); - ScsiPortStallExecution(500); - - /* Disable interrupts */ - IDEWriteDriveControl(ControlPort, IDE_DC_nIEN); - ScsiPortStallExecution(500); - -#if 0 - /* Wait for STATUS.BUSY and STATUS.DRQ to clear */ - for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++) - { - Status = IDEReadStatus(CommandPort); - if (!(Status & IDE_SR_BUSY) && !(Status & IDE_SR_DRQ)) - { - break; - } - ScsiPortStallExecution(10); - } - if (RetryCount >= IDE_MAX_BUSY_RETRIES) - { - return FALSE; - } -#endif - - /* Issue command to drive */ - if (DrvHead & IDE_DH_LBA) - { - DPRINT("READ:DRV=%d:LBA=1:BLK=%08d:SC=%02x:CM=%02x\n", - DrvHead & IDE_DH_DRV1 ? 1 : 0, - ((DrvHead & 0x0f) << 24) + (CylinderHigh << 16) + (CylinderLow << 8) + SectorNum, - SectorCnt, - Command); - } - else - { - DPRINT("READ:DRV=%d:LBA=0:CH=%02x:CL=%02x:HD=%01x:SN=%02x:SC=%02x:CM=%02x\n", - DrvHead & IDE_DH_DRV1 ? 1 : 0, - CylinderHigh, - CylinderLow, - DrvHead & 0x0f, - SectorNum, - SectorCnt, - Command); - } - - /* Setup command parameters */ - IDEWritePrecomp(CommandPort, PreComp); - IDEWriteSectorCount(CommandPort, SectorCnt); - IDEWriteSectorNum(CommandPort, SectorNum); - IDEWriteCylinderHigh(CommandPort, CylinderHigh); - IDEWriteCylinderLow(CommandPort, CylinderLow); - IDEWriteDriveHead(CommandPort, IDE_DH_FIXED | DrvHead); - - /* Issue the command */ - IDEWriteCommand(CommandPort, Command); - ScsiPortStallExecution(50); - - /* wait for DRQ or error */ - for (RetryCount = 0; RetryCount < IDE_MAX_POLL_RETRIES; RetryCount++) - { - Status = IDEReadStatus(CommandPort); - if (!(Status & IDE_SR_BUSY)) - { - if (Status & IDE_SR_ERR) - { - IDEWriteDriveControl(ControlPort, 0); - ScsiPortStallExecution(50); - IDEReadStatus(CommandPort); - - return FALSE; - } - - if (Status & IDE_SR_DRQ) - { - break; - } - else - { - IDEWriteDriveControl(ControlPort, 0); - ScsiPortStallExecution(50); - IDEReadStatus(CommandPort); - - return FALSE; - } - } - ScsiPortStallExecution(10); - } - - /* timed out */ - if (RetryCount >= IDE_MAX_POLL_RETRIES) - { - IDEWriteDriveControl(ControlPort, 0); - ScsiPortStallExecution(50); - IDEReadStatus(CommandPort); - - return FALSE; - } - - while (1) - { - /* Read data into buffer */ - if (Junk == FALSE) - { - IDEReadBlock(CommandPort, Buffer, IDE_SECTOR_BUF_SZ); - Buffer += IDE_SECTOR_BUF_SZ; - } - else - { - UCHAR JunkBuffer[IDE_SECTOR_BUF_SZ]; - IDEReadBlock(CommandPort, JunkBuffer, IDE_SECTOR_BUF_SZ); - } - SectorCount++; - - /* Check for error or more sectors to read */ - for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++) - { - Status = IDEReadStatus(CommandPort); - if (!(Status & IDE_SR_BUSY)) - { - if (Status & IDE_SR_ERR) - { - IDEWriteDriveControl(ControlPort, 0); - ScsiPortStallExecution(50); - IDEReadStatus(CommandPort); - - return FALSE; - } - if (Status & IDE_SR_DRQ) - { - if (SectorCount >= SectorCnt) - { - DPRINT("Buffer size exceeded!\n"); - Junk = TRUE; - } - break; - } - else - { - if (SectorCount > SectorCnt) - { - DPRINT("Read %lu sectors of junk!\n", - SectorCount - SectorCnt); - } - IDEWriteDriveControl(ControlPort, 0); - ScsiPortStallExecution(50); - IDEReadStatus(CommandPort); - - return TRUE; - } - } - } - } -} - - -// ------------------------------------------- Nondiscardable statics - -static ULONG -AtapiSendSmartCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb) -{ - PSRB_IO_CONTROL SrbIoControl = (PSRB_IO_CONTROL)Srb->DataBuffer; - SENDCMDINPARAMS InParams; - PSENDCMDOUTPARAMS OutParams = (PSENDCMDOUTPARAMS)(SrbIoControl + 1); - ULONG Retries; - UCHAR Status; - - if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 || - SrbIoControl->Length < sizeof(SENDCMDOUTPARAMS) - 1) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams = *(PSENDCMDINPARAMS)(SrbIoControl + 1); - - DPRINT("%02x %02x %02x %02x %02x %02x %02x %02x\n", - InParams.irDriveRegs.bFeaturesReg, - InParams.irDriveRegs.bSectorCountReg, - InParams.irDriveRegs.bSectorNumberReg, - InParams.irDriveRegs.bCylLowReg, - InParams.irDriveRegs.bCylHighReg, - InParams.irDriveRegs.bDriveHeadReg, - InParams.irDriveRegs.bCommandReg, - InParams.irDriveRegs.bReserved); - - if (InParams.bDriveNumber > 1 || - (DeviceExtension->DeviceFlags[InParams.bDriveNumber] & (DEVICE_PRESENT|DEVICE_ATAPI)) != DEVICE_PRESENT) - { - RtlZeroMemory(&OutParams, sizeof(SENDCMDOUTPARAMS)); - OutParams->DriverStatus.bIDEError = 1; - return SRB_STATUS_NO_DEVICE; - } - - DeviceExtension->DataTransferLength = 0; - - switch (SrbIoControl->ControlCode) - { - case IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS: - DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS\n"); - - if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE || - SrbIoControl->Length < sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE || - InParams.irDriveRegs.bFeaturesReg != READ_ATTRIBUTES || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams.irDriveRegs.bSectorCountReg = 0; - InParams.irDriveRegs.bSectorNumberReg = 0; - DeviceExtension->DataTransferLength = READ_ATTRIBUTE_BUFFER_SIZE; - break; - - case IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS: - DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS\n"); - - if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE || - SrbIoControl->Length < sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE || - InParams.irDriveRegs.bFeaturesReg != READ_THRESHOLDS || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams.irDriveRegs.bSectorCountReg = 0; - InParams.irDriveRegs.bSectorNumberReg = 0; - DeviceExtension->DataTransferLength = READ_THRESHOLD_BUFFER_SIZE; - break; - - case IOCTL_SCSI_MINIPORT_READ_SMART_LOG: - DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_LOG\n"); - - if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 + max(1, InParams.irDriveRegs.bSectorCountReg) * SMART_LOG_SECTOR_SIZE || - SrbIoControl->Length < sizeof(SENDCMDOUTPARAMS) - 1 + max(1, InParams.irDriveRegs.bSectorCountReg) * SMART_LOG_SECTOR_SIZE || - InParams.irDriveRegs.bFeaturesReg != SMART_READ_LOG || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - DeviceExtension->DataTransferLength = max(1, InParams.irDriveRegs.bSectorCountReg) * SMART_LOG_SECTOR_SIZE; - break; - - case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE: - DPRINT("IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE\n"); - - if (InParams.irDriveRegs.bFeaturesReg != ENABLE_DISABLE_AUTOSAVE || - (InParams.irDriveRegs.bSectorCountReg != 0 && InParams.irDriveRegs.bSectorCountReg != 1) || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams.irDriveRegs.bSectorNumberReg = 0; - break; - - case IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES: - DPRINT("IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES\n"); - - if (InParams.irDriveRegs.bFeaturesReg != SAVE_ATTRIBUTE_VALUES || - (InParams.irDriveRegs.bSectorCountReg != 0 && InParams.irDriveRegs.bSectorCountReg != 0xf1) || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams.irDriveRegs.bSectorNumberReg = 0; - break; - - case IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS: - DPRINT("IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS\n"); - - if (InParams.irDriveRegs.bFeaturesReg != EXECUTE_OFFLINE_DIAGS || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams.irDriveRegs.bSectorCountReg = 0; - break; - - case IOCTL_SCSI_MINIPORT_ENABLE_SMART: - DPRINT("IOCTL_SCSI_MINIPORT_ENABLE_SMART\n"); - - if (InParams.irDriveRegs.bFeaturesReg != ENABLE_SMART || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams.irDriveRegs.bSectorCountReg = 0; - InParams.irDriveRegs.bSectorNumberReg = 0; - break; - - case IOCTL_SCSI_MINIPORT_DISABLE_SMART: - DPRINT("IOCTL_SCSI_MINIPORT_DISABLE_SMART\n"); - - if (InParams.irDriveRegs.bFeaturesReg != DISABLE_SMART || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams.irDriveRegs.bSectorCountReg = 0; - InParams.irDriveRegs.bSectorNumberReg = 0; - break; - - case IOCTL_SCSI_MINIPORT_RETURN_STATUS: - DPRINT("IOCTL_SCSI_MINIPORT_RETURN_STATUS\n"); - - if (InParams.irDriveRegs.bFeaturesReg != RETURN_SMART_STATUS || - InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW || - InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI || - InParams.irDriveRegs.bCommandReg != SMART_CMD) - { - return SRB_STATUS_INVALID_REQUEST; - } - InParams.irDriveRegs.bSectorCountReg = 0; - InParams.irDriveRegs.bSectorNumberReg = 0; - break; - } - - Srb->TargetId = InParams.bDriveNumber; - - /* Set pointer to data buffer. */ - DeviceExtension->DataBuffer = (PUCHAR)OutParams->bBuffer; - - DeviceExtension->CurrentSrb = Srb; - - /* wait for BUSY to clear */ - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY)) - { - break; - } - ScsiPortStallExecution(10); - } - if (Retries >= IDE_MAX_BUSY_RETRIES) - { - DPRINT ("Drive is BUSY for too long\n"); - return(SRB_STATUS_BUSY); - } - - /* Select the desired drive */ - InParams.irDriveRegs.bDriveHeadReg = (InParams.bDriveNumber ? IDE_DH_DRV1 : IDE_DH_DRV0) | IDE_DH_FIXED; - IDEWriteDriveHead(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bDriveHeadReg); - ScsiPortStallExecution(2); - - IDEWritePrecomp(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bFeaturesReg); - IDEWriteSectorCount(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bSectorCountReg); - IDEWriteSectorNum(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bSectorNumberReg); - IDEWriteCylinderLow(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bCylLowReg); - IDEWriteCylinderHigh(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bCylHighReg); - - AtapiExecuteCommand(DeviceExtension, InParams.irDriveRegs.bCommandReg, AtapiSmartInterrupt); - - /* Wait for interrupt. */ - return SRB_STATUS_PENDING; - -} - -static ULONG -AtapiSendAtapiCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb) -{ - UCHAR ByteCountHigh; - UCHAR ByteCountLow; - ULONG Retries; - ULONG CdbSize; - UCHAR Status; - - DPRINT("AtapiSendAtapiCommand() called!\n"); - - if (Srb->PathId != 0) - { - Srb->SrbStatus = SRB_STATUS_INVALID_PATH_ID; - return(SRB_STATUS_INVALID_PATH_ID); - } - - if (Srb->TargetId > 1) - { - Srb->SrbStatus = SRB_STATUS_INVALID_TARGET_ID; - return(SRB_STATUS_INVALID_TARGET_ID); - } - - if (Srb->Lun != 0) - { - Srb->SrbStatus = SRB_STATUS_INVALID_LUN; - return(SRB_STATUS_INVALID_LUN); - } - - if (!(DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_PRESENT)) - { - Srb->SrbStatus = SRB_STATUS_NO_DEVICE; - return(SRB_STATUS_NO_DEVICE); - } - - if (Srb->Cdb[0] == SCSIOP_MODE_SENSE) - { - Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; - return (SRB_STATUS_INVALID_REQUEST); - } - - DPRINT("AtapiSendAtapiCommand(): TargetId: %lu\n", - Srb->TargetId); - - if (Srb->Cdb[0] == SCSIOP_INQUIRY) - return(AtapiInquiry(DeviceExtension, - Srb)); - - /* Set pointer to data buffer. */ - DeviceExtension->DataBuffer = (PUCHAR)Srb->DataBuffer; - DeviceExtension->DataTransferLength = Srb->DataTransferLength; - DeviceExtension->CurrentSrb = Srb; - - DPRINT("BufferAddress %x, BufferLength %d\n", Srb->DataBuffer, Srb->DataTransferLength); - - /* Wait for BUSY to clear */ - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY)) - { - break; - } - ScsiPortStallExecution(10); - } - DPRINT("status=%02x\n", Status); - DPRINT("waited %ld usecs for busy to clear\n", Retries * 10); - if (Retries >= IDE_MAX_BUSY_RETRIES) - { - DPRINT("Drive is BUSY for too long\n"); - return(SRB_STATUS_BUSY); - } - - /* Select the desired drive */ - IDEWriteDriveHead(DeviceExtension->CommandPortBase, - IDE_DH_FIXED | (Srb->TargetId ? IDE_DH_DRV1 : 0)); - - /* Wait a little while */ - ScsiPortStallExecution(50); - -#if 0 - /* Wait for BUSY to clear and DRDY to assert */ - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY) && (Status & IDE_SR_DRDY)) - { - break; - } - ScsiPortStallExecution(10); - } - DPRINT("waited %ld usecs for busy to clear after drive select\n", Retries * 10); - if (Retries >= IDE_MAX_BUSY_RETRIES) - { - DPRINT("Drive is BUSY for too long after drive select\n"); - return(SRB_STATUS_BUSY); - } -#endif - -#ifdef ENABLE_DMA - if ((DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_DMA_CMD) && - (Srb->Cdb[0] == SCSIOP_READ || Srb->Cdb[0] == SCSIOP_WRITE)) - { - DeviceExtension->UseDma = AtapiInitDma(DeviceExtension, Srb, Srb->SrbFlags & SRB_FLAGS_DATA_IN ? 1 << 3 : 0); - } - else - { - DeviceExtension->UseDma = FALSE; - } -#endif - - if (DeviceExtension->DataTransferLength < 0x10000) - { - ByteCountLow = (UCHAR)(DeviceExtension->DataTransferLength & 0xFF); - ByteCountHigh = (UCHAR)(DeviceExtension->DataTransferLength >> 8); - } - else - { - ByteCountLow = 0xFF; - ByteCountHigh = 0xFF; - } - - /* Set feature register */ -#ifdef ENABLE_DMA - IDEWritePrecomp(DeviceExtension->CommandPortBase, DeviceExtension->UseDma ? 1 : 0); + GetBaseStatus(baseIoAddress1, statusByte); #else - IDEWritePrecomp(DeviceExtension->CommandPortBase, 0); -#endif - /* Set command packet length */ - IDEWriteCylinderHigh(DeviceExtension->CommandPortBase, ByteCountHigh); - IDEWriteCylinderLow(DeviceExtension->CommandPortBase, ByteCountLow); - - /* Issue command to drive */ -#ifdef ENABLE_DMA - if (DeviceExtension->UseDma) - { - AtapiExecuteCommand(DeviceExtension, IDE_CMD_PACKET, AtapiDmaPacketInterrupt); - } - else -#endif - { - AtapiExecuteCommand(DeviceExtension, IDE_CMD_PACKET, AtapiPacketInterrupt); - } - - /* Wait for DRQ to assert */ - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if ((Status & IDE_SR_DRQ)) - { - break; - } - ScsiPortStallExecution(10); - } - - /* Convert special SCSI SRBs to ATAPI format */ - switch (Srb->Cdb[0]) - { - case SCSIOP_FORMAT_UNIT: - case SCSIOP_MODE_SELECT: - AtapiScsiSrbToAtapi (Srb); - break; - } - - CdbSize = ((DeviceExtension->DeviceParams[Srb->TargetId].ConfigBits & 0x3) == 1) ? 16 : 12; - DPRINT("CdbSize: %lu\n", CdbSize); - - /* Write command packet */ - IDEWriteBlock(DeviceExtension->CommandPortBase, - (PUSHORT)Srb->Cdb, - CdbSize); - -#ifdef ENABLE_DMA - if (DeviceExtension->UseDma) - { - UCHAR DmaCommand; - /* start DMA */ - DmaCommand = IDEReadDMACommand(DeviceExtension->BusMasterRegisterBase); - IDEWriteDMACommand(DeviceExtension->BusMasterRegisterBase, DmaCommand|0x01); - } -#endif - DPRINT("AtapiSendAtapiCommand() done\n"); - - return(SRB_STATUS_PENDING); -} - - -static ULONG -AtapiSendIdeCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb) -{ - ULONG SrbStatus = SRB_STATUS_SUCCESS; - - DPRINT("AtapiSendIdeCommand() called!\n"); - - DPRINT("PathId: %lu TargetId: %lu Lun: %lu\n", - Srb->PathId, - Srb->TargetId, - Srb->Lun); - - if (Srb->PathId != 0) - { - Srb->SrbStatus = SRB_STATUS_INVALID_PATH_ID; - return(SRB_STATUS_INVALID_PATH_ID); - } - - if (Srb->TargetId > 1) - { - Srb->SrbStatus = SRB_STATUS_INVALID_TARGET_ID; - return(SRB_STATUS_INVALID_TARGET_ID); - } - - if (Srb->Lun != 0) - { - Srb->SrbStatus = SRB_STATUS_INVALID_LUN; - return(SRB_STATUS_INVALID_LUN); - } - - if (!(DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_PRESENT)) - { - Srb->SrbStatus = SRB_STATUS_NO_DEVICE; - return(SRB_STATUS_NO_DEVICE); - } - - switch (Srb->Cdb[0]) - { - case SCSIOP_INQUIRY: - SrbStatus = AtapiInquiry(DeviceExtension, - Srb); - break; - - case SCSIOP_READ_CAPACITY: - SrbStatus = AtapiReadCapacity(DeviceExtension, - Srb); - break; - - case SCSIOP_READ: - case SCSIOP_WRITE: - SrbStatus = AtapiReadWrite(DeviceExtension, - Srb); - break; - - case SCSIOP_SYNCHRONIZE_CACHE: - SrbStatus = AtapiFlushCache(DeviceExtension, - Srb); - break; - - case SCSIOP_TEST_UNIT_READY: - SrbStatus = AtapiTestUnitReady(DeviceExtension, - Srb); - break; - - case SCSIOP_MODE_SENSE: - - case SCSIOP_VERIFY: - case SCSIOP_START_STOP_UNIT: - case SCSIOP_REQUEST_SENSE: - break; - - default: - DbgPrint("AtapiSendIdeCommand():unknown command %x\n", - Srb->Cdb[0]); - SrbStatus = SRB_STATUS_INVALID_REQUEST; - break; - } - - DPRINT("AtapiSendIdeCommand() done!\n"); - - return(SrbStatus); -} - - -static ULONG -AtapiInquiry(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb) -{ - PIDE_DRIVE_IDENTIFY DeviceParams; - PINQUIRYDATA InquiryData; - - DPRINT("SCSIOP_INQUIRY: DeviceExtension %p TargetId: %lu\n", - DeviceExtension, Srb->TargetId); - - InquiryData = Srb->DataBuffer; - DeviceParams = &DeviceExtension->DeviceParams[Srb->TargetId]; - - /* clear buffer */ - memset(Srb->DataBuffer, 0, Srb->DataTransferLength); - - /* set device class */ - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_ATAPI) - { - /* get it from the ATAPI configuration word */ - InquiryData->DeviceType = (DeviceParams->ConfigBits >> 8) & 0x1F; - DPRINT("Device class: %u\n", InquiryData->DeviceType); - } - else - { - /* hard-disk */ - InquiryData->DeviceType = DIRECT_ACCESS_DEVICE; - } - - DPRINT("ConfigBits: 0x%x\n", DeviceParams->ConfigBits); - if (DeviceParams->ConfigBits & 0x80) - { - DPRINT("Removable media!\n"); - InquiryData->RemovableMedia = 1; - } - - memcpy(InquiryData->VendorId, DeviceParams->ModelNumber, 20); - IDESwapBytePairs(InquiryData->VendorId, 20); - - memcpy(&InquiryData->ProductId[12], " ", 4); - - memcpy(InquiryData->ProductRevisionLevel, DeviceParams->FirmwareRev, 4); - IDESwapBytePairs(InquiryData->ProductRevisionLevel, 4); - - InquiryData->AdditionalLength = 31; - - DPRINT("VendorId: '%.20s'\n", InquiryData->VendorId); - - Srb->SrbStatus = SRB_STATUS_SUCCESS; - return(SRB_STATUS_SUCCESS); -} - - -static ULONG -AtapiReadCapacity(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb) -{ - PREAD_CAPACITY_DATA CapacityData; - PIDE_DRIVE_IDENTIFY DeviceParams; - LARGE_INTEGER LastSector; - - DPRINT("SCSIOP_READ_CAPACITY: TargetId: %lu\n", Srb->TargetId); - CapacityData = (PREAD_CAPACITY_DATA)Srb->DataBuffer; - DeviceParams = &DeviceExtension->DeviceParams[Srb->TargetId]; - - /* Set sector (block) size to 512 bytes (big-endian). */ - CapacityData->BytesPerBlock = 0x20000; - - /* Calculate last sector (big-endian). */ - if (DeviceParams->Capabilities & IDE_DRID_LBA_SUPPORTED) - { - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_48BIT_ADDRESS) - { - ((PUSHORT)&LastSector)[0] = DeviceParams->Max48BitAddress[0]; - ((PUSHORT)&LastSector)[1] = DeviceParams->Max48BitAddress[1]; - ((PUSHORT)&LastSector)[2] = DeviceParams->Max48BitAddress[2]; - ((PUSHORT)&LastSector)[3] = DeviceParams->Max48BitAddress[3]; - LastSector.QuadPart -= 1; - - } - else - { - LastSector.u.HighPart = 0; - LastSector.u.LowPart = (ULONG)((DeviceParams->TMSectorCountHi << 16) + - DeviceParams->TMSectorCountLo)-1; - } - } - else - { - LastSector.u.HighPart = 0; - LastSector.u.LowPart = (ULONG)(DeviceParams->LogicalCyls * - DeviceParams->LogicalHeads * - DeviceParams->SectorsPerTrack)-1; - } - if (LastSector.u.HighPart) - { - DPRINT1("Disk is too large for our implementation (%I64d sectors\n", LastSector.QuadPart); - KEBUGCHECK(0); - } - - CapacityData->LogicalBlockAddress = (((PUCHAR)&LastSector)[0] << 24) | - (((PUCHAR)&LastSector)[1] << 16) | - (((PUCHAR)&LastSector)[2] << 8) | - ((PUCHAR)&LastSector)[3]; - - DPRINT("LastCount: %lu (%08lx / %08lx)\n", - LastSector, - LastSector, - CapacityData->LogicalBlockAddress); - - Srb->SrbStatus = SRB_STATUS_SUCCESS; - return(SRB_STATUS_SUCCESS); -} - - -static ULONG -AtapiReadWrite(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb) -{ - PIDE_DRIVE_IDENTIFY DeviceParams; - ULONG StartingSector; - ULONG SectorCount; - UCHAR CylinderHigh[2]; - UCHAR CylinderLow[2]; - UCHAR DrvHead; - UCHAR SectorNumber[2]; - UCHAR Command; - ULONG Retries; - UCHAR Status; - BOOLEAN (FASTCALL *Handler)(PATAPI_MINIPORT_EXTENSION DevExt); - - DPRINT("AtapiReadWrite() called!\n"); - DPRINT("SCSIOP_WRITE: TargetId: %lu\n", - Srb->TargetId); - - DeviceParams = &DeviceExtension->DeviceParams[Srb->TargetId]; - - /* Get starting sector number from CDB. */ - StartingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | - ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | - ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | - ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; - - SectorCount = (Srb->DataTransferLength + DeviceParams->BytesPerSector - 1) / - DeviceParams->BytesPerSector; - - DPRINT("Starting sector %lu Number of bytes %lu Sectors %lu\n", - StartingSector, - Srb->DataTransferLength, - SectorCount); - - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_48BIT_ADDRESS) - { - SectorNumber[0] = StartingSector & 0xff; - CylinderLow[0] = (StartingSector >> 8) & 0xff; - CylinderHigh[0] = (StartingSector >> 16) & 0xff; - SectorNumber[1] = (StartingSector >> 24) & 0xff; - CylinderLow[1] = 0; - CylinderHigh[1] = 0; - DrvHead = (Srb->TargetId ? IDE_DH_DRV1 : 0) | IDE_DH_LBA; -#if 0 - DPRINT("%s:BUS=%04x:DRV=%d:LBA48=1:BLK=%08d:SC=%02x:CM=%02x\n", - (Srb->SrbFlags & SRB_FLAGS_DATA_IN) ? "READ" : "WRITE", - DeviceExtension->CommandPortBase, - DrvHead & IDE_DH_DRV1 ? 1 : 0, - (SectorNumber[1] << 24) + - (CylinderHigh[0] << 16) + (CylinderLow[0] << 8) + DectorNumberLow[0], - SectorCount, - Command); -#endif - } - else if (DeviceParams->Capabilities & IDE_DRID_LBA_SUPPORTED) - { - SectorNumber[0] = StartingSector & 0xff; - CylinderLow[0] = (StartingSector >> 8) & 0xff; - CylinderHigh[0] = (StartingSector >> 16) & 0xff; - SectorNumber[1] = 0; - CylinderLow[1] = 0; - CylinderHigh[1] = 0; - DrvHead = ((StartingSector >> 24) & 0x0f) | - (Srb->TargetId ? IDE_DH_DRV1 : 0) | - IDE_DH_LBA; - - DPRINT("%s:BUS=%04x:DRV=%d:LBA=1:BLK=%08d:SC=%02x:CM=%02x\n", - (Srb->SrbFlags & SRB_FLAGS_DATA_IN) ? "READ" : "WRITE", - DeviceExtension->CommandPortBase, - DrvHead & IDE_DH_DRV1 ? 1 : 0, - ((DrvHead & 0x0f) << 24) + - (CylinderHigh[0] << 16) + (CylinderLow[0] << 8) + SectorNumber[0], - SectorCount, - Command); - } - else - { - SectorNumber[0] = (StartingSector % DeviceParams->SectorsPerTrack) + 1; - StartingSector /= DeviceParams->SectorsPerTrack; - DrvHead = (StartingSector % DeviceParams->LogicalHeads) | - (Srb->TargetId ? IDE_DH_DRV1 : 0); - StartingSector /= DeviceParams->LogicalHeads; - CylinderLow[0] = StartingSector & 0xff; - CylinderHigh[0] = StartingSector >> 8; - SectorNumber[1] = 0; - CylinderLow[1] = 0; - CylinderHigh[1] = 0; - - DPRINT("%s:BUS=%04x:DRV=%d:LBA=0:CH=%02x:CL=%02x:HD=%01x:SN=%02x:SC=%02x:CM=%02x\n", - (Srb->SrbFlags & SRB_FLAGS_DATA_IN) ? "READ" : "WRITE", - DeviceExtension->CommandPortBase, - DrvHead & IDE_DH_DRV1 ? 1 : 0, - CylinderHigh[0], - CylinderLow[0], - DrvHead & 0x0f, - SectorNumber[0], - SectorCount, - Command); - } - - /* Set pointer to data buffer. */ - DeviceExtension->DataBuffer = (PUCHAR)Srb->DataBuffer; - DeviceExtension->DataTransferLength = Srb->DataTransferLength; - - DeviceExtension->CurrentSrb = Srb; - - /* wait for BUSY to clear */ - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY)) - { - break; + if (deviceExtension->InterruptMode == LevelSensitive) { + if (deviceExtension->BaseIoAddress1[0] != NULL) { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[0]; + GetBaseStatus(baseIoAddress1, statusByte); + } + if (deviceExtension->BaseIoAddress1[1] != NULL) { + baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[1]; + GetBaseStatus(baseIoAddress1, statusByte); + } } - ScsiPortStallExecution(10); - } - DPRINT("status=%02x\n", Status); - DPRINT("waited %ld usecs for busy to clear\n", Retries * 10); - if (Retries >= IDE_MAX_BUSY_RETRIES) - { - DPRINT ("Drive is BUSY for too long\n"); - return(SRB_STATUS_BUSY); - } - - /* Select the desired drive */ - IDEWriteDriveHead(DeviceExtension->CommandPortBase, - IDE_DH_FIXED | DrvHead); - - ScsiPortStallExecution(10); -#if 0 - /* wait for BUSY to clear and DRDY to assert */ - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY) && (Status & IDE_SR_DRDY)) - { - break; - } - ScsiPortStallExecution(10); - } - DPRINT("waited %ld usecs for busy to clear after drive select\n", Retries * 10); - if (Retries >= IDE_MAX_BUSY_RETRIES) - { - DPRINT("Drive is BUSY for too long after drive select\n"); - return(SRB_STATUS_BUSY); - } #endif - - /* Setup command parameters */ - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_48BIT_ADDRESS) - { - IDEWritePrecomp(DeviceExtension->CommandPortBase, 0); - IDEWriteSectorCount(DeviceExtension->CommandPortBase, SectorCount >> 8); - IDEWriteSectorNum(DeviceExtension->CommandPortBase, SectorNumber[1]); - IDEWriteCylinderLow(DeviceExtension->CommandPortBase, CylinderLow[1]); - IDEWriteCylinderHigh(DeviceExtension->CommandPortBase, CylinderHigh[1]); + return FALSE; } - IDEWritePrecomp(DeviceExtension->CommandPortBase, 0); - IDEWriteSectorCount(DeviceExtension->CommandPortBase, SectorCount & 0xff); - IDEWriteSectorNum(DeviceExtension->CommandPortBase, SectorNumber[0]); - IDEWriteCylinderLow(DeviceExtension->CommandPortBase, CylinderLow[0]); - IDEWriteCylinderHigh(DeviceExtension->CommandPortBase, CylinderHigh[0]); + if (!(deviceExtension->ExpectingInterrupt)) { -#ifdef ENABLE_DMA - if (DeviceExtension->PRDTable && - DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_DMA_CMD) - { - DeviceExtension->UseDma = AtapiInitDma(DeviceExtension, Srb, Srb->SrbFlags & SRB_FLAGS_DATA_IN ? 1 << 3 : 0); - } - if (DeviceExtension->UseDma) - { - Handler = AtapiDmaInterrupt; - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_48BIT_ADDRESS) - { - Command = Srb->SrbFlags & SRB_FLAGS_DATA_IN ? IDE_CMD_READ_DMA_EXT : IDE_CMD_WRITE_DMA_EXT; - } - else - { - Command = Srb->SrbFlags & SRB_FLAGS_DATA_IN ? IDE_CMD_READ_DMA : IDE_CMD_WRITE_DMA; - } - } - else -#endif - { - Handler = Srb->SrbFlags & SRB_FLAGS_DATA_IN ? AtapiReadInterrupt : AtapiWriteInterrupt; - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_MULTI_SECTOR_CMD) - { - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_48BIT_ADDRESS) - { - Command = Srb->SrbFlags & SRB_FLAGS_DATA_IN ? IDE_CMD_READ_MULTIPLE_EXT : IDE_CMD_WRITE_MULTIPLE_EXT; - } - else - { - Command = Srb->SrbFlags & SRB_FLAGS_DATA_IN ? IDE_CMD_READ_MULTIPLE : IDE_CMD_WRITE_MULTIPLE; - } - } - else - { - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_48BIT_ADDRESS) - { - Command = Srb->SrbFlags & SRB_FLAGS_DATA_IN ? IDE_CMD_READ_EXT : IDE_CMD_WRITE_EXT; - } - else - { - Command = Srb->SrbFlags & SRB_FLAGS_DATA_IN ? IDE_CMD_READ : IDE_CMD_WRITE; - } - } + DebugPrint((3, + "AtapiInterrupt: Unexpected interrupt.\n")); + return FALSE; } - AtapiExecuteCommand(DeviceExtension, Command, Handler); + // + // Clear interrupt by reading status. + // -#ifdef ENABLE_DMA - if (DeviceExtension->UseDma) - { - UCHAR DmaCommand; - /* start DMA */ - DmaCommand = IDEReadDMACommand(DeviceExtension->BusMasterRegisterBase); - IDEWriteDMACommand(DeviceExtension->BusMasterRegisterBase, DmaCommand|0x01); - } - else -#endif - { - if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) - { - /* Write data block */ - PUCHAR TargetAddress; - ULONG TransferSize; + GetBaseStatus(baseIoAddress1, statusByte); - /* Wait for controller ready */ - for (Retries = 0; Retries < IDE_MAX_WRITE_RETRIES; Retries++) - { - UCHAR Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY) || (Status & IDE_SR_ERR)) - { - break; - } - ScsiPortStallExecution(10); - } - if (Retries >= IDE_MAX_WRITE_RETRIES) - { - DPRINT1("Drive is BUSY for too long after sending write command\n"); - return(SRB_STATUS_BUSY); - } + DebugPrint((3, + "AtapiInterrupt: Entered with status (%x)\n", + statusByte)); - /* Update DeviceExtension data */ - TransferSize = DeviceExtension->TransferSize[Srb->TargetId]; - if (DeviceExtension->DataTransferLength < TransferSize) - { - TransferSize = DeviceExtension->DataTransferLength; - } - TargetAddress = DeviceExtension->DataBuffer; - DeviceExtension->DataBuffer += TransferSize; - DeviceExtension->DataTransferLength -= TransferSize; + if (statusByte & IDE_STATUS_BUSY) { + if (deviceExtension->DriverMustPoll) { - /* Write data block */ - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_DWORD_IO) - { - IDEWriteBlock32(DeviceExtension->CommandPortBase, - TargetAddress, - TransferSize); - } - else - { - IDEWriteBlock(DeviceExtension->CommandPortBase, - TargetAddress, - TransferSize); - } - } - } + // + // Crashdump is polling and we got caught with busy asserted. + // Just go away, and we will be polled again shortly. + // - DPRINT("AtapiReadWrite() done!\n"); + DebugPrint((3, + "AtapiInterrupt: Hit BUSY while polling during crashdump.\n")); - /* Wait for interrupt. */ - return(SRB_STATUS_PENDING); -} - - -static ULONG -AtapiFlushCache(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb) -{ - ULONG Retries; - UCHAR Status; - - DPRINT("AtapiFlushCache() called!\n"); - DPRINT("SCSIOP_SYNCRONIZE_CACHE: TargetId: %lu\n", - Srb->TargetId); - - if (DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_NO_FLUSH) - { - /* - * NOTE: Don't flush the cache for CD/DVD drives. Although - * the ATAPI-6 standard allows that, it has been experimentally - * proved that it can damage some broken LG drives. Afterall - * it doesn't make sense to flush cache on devices we don't - * write to. - */ - - /* The device states it doesn't support the command or it is disabled */ - DPRINT("The drive doesn't support FLUSH_CACHE\n"); - return SRB_STATUS_INVALID_REQUEST; - } - - /* Wait for BUSY to clear */ - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY)) - { - break; - } - ScsiPortStallExecution(10); - } - DPRINT("Status=%02x\n", Status); - DPRINT("Waited %ld usecs for busy to clear\n", Retries * 10); - if (Retries >= IDE_MAX_BUSY_RETRIES) - { - DPRINT1("Drive is BUSY for too long\n"); - return(SRB_STATUS_BUSY); - } - - /* Select the desired drive */ - IDEWriteDriveHead(DeviceExtension->CommandPortBase, - IDE_DH_FIXED | (Srb->TargetId ? IDE_DH_DRV1 : 0)); - ScsiPortStallExecution(10); - - /* Issue command to drive */ - AtapiExecuteCommand(DeviceExtension, - DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_48BIT_ADDRESS ? IDE_CMD_FLUSH_CACHE_EXT : IDE_CMD_FLUSH_CACHE, - AtapiNoDataInterrupt); - - - DPRINT("AtapiFlushCache() done!\n"); - - /* Wait for interrupt. */ - return(SRB_STATUS_PENDING); -} - - -static ULONG -AtapiTestUnitReady(PATAPI_MINIPORT_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb) -{ - ULONG Retries; - UCHAR Status; - UCHAR Error; - - DPRINT1("AtapiTestUnitReady() called!\n"); - - DPRINT1("SCSIOP_TEST_UNIT_READY: TargetId: %lu\n", - Srb->TargetId); - - /* Return success if media status is not supported */ - if (!(DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_MEDIA_STATUS)) - { - Srb->SrbStatus = SRB_STATUS_SUCCESS; - return(SRB_STATUS_SUCCESS); - } - - /* Wait for BUSY to clear */ - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY)) - { - break; - } - ScsiPortStallExecution(10); - } - DPRINT1("Status=%02x\n", Status); - DPRINT1("Waited %ld usecs for busy to clear\n", Retries * 10); - if (Retries >= IDE_MAX_BUSY_RETRIES) - { - DPRINT1("Drive is BUSY for too long\n"); - return(SRB_STATUS_BUSY); - } - - /* Select the desired drive */ - IDEWriteDriveHead(DeviceExtension->CommandPortBase, - IDE_DH_FIXED | (Srb->TargetId ? IDE_DH_DRV1 : 0)); - ScsiPortStallExecution(10); - - /* Issue command to drive */ - AtapiExecuteCommand(DeviceExtension, IDE_CMD_GET_MEDIA_STATUS, AtapiNoDataInterrupt); - - /* Wait for controller ready */ - for (Retries = 0; Retries < IDE_MAX_WRITE_RETRIES; Retries++) - { - Status = IDEReadStatus(DeviceExtension->CommandPortBase); - if (!(Status & IDE_SR_BUSY) || (Status & IDE_SR_ERR)) - { - break; - } - ScsiPortStallExecution(10); - } - if (Retries >= IDE_MAX_WRITE_RETRIES) - { - DPRINT1("Drive is BUSY for too long after sending write command\n"); - DeviceExtension->Handler = NULL; - return(SRB_STATUS_BUSY); - } - - if (Status & IDE_SR_ERR) - { - Error = IDEReadError(DeviceExtension->CommandPortBase); - if (Error == IDE_ER_UNC) - { -CHECKPOINT1; - /* Handle write protection 'error' */ - Srb->SrbStatus = SRB_STATUS_SUCCESS; - DeviceExtension->Handler = NULL; - return(SRB_STATUS_SUCCESS); - } - else - { -CHECKPOINT1; - /* Indicate expecting an interrupt. */ - return(SRB_STATUS_PENDING); - } - } - - DeviceExtension->Handler = NULL; - - DPRINT1("AtapiTestUnitReady() done!\n"); - - Srb->SrbStatus = SRB_STATUS_SUCCESS; - return(SRB_STATUS_SUCCESS); -} - - -static UCHAR -AtapiErrorToScsi(PVOID DeviceExtension, - PSCSI_REQUEST_BLOCK Srb) -{ - PATAPI_MINIPORT_EXTENSION DevExt; - ULONG CommandPortBase; - ULONG ControlPortBase; - UCHAR ErrorReg; - UCHAR ScsiStatus; - UCHAR SrbStatus; - - DPRINT("AtapiErrorToScsi() called\n"); - - DevExt = (PATAPI_MINIPORT_EXTENSION)DeviceExtension; - - CommandPortBase = DevExt->CommandPortBase; - ControlPortBase = DevExt->ControlPortBase; - - ErrorReg = IDEReadError(CommandPortBase); - - if (DevExt->DeviceFlags[Srb->TargetId] & DEVICE_ATAPI) - { - switch (ErrorReg >> 4) - { - case SCSI_SENSE_NO_SENSE: - DPRINT("ATAPI error: SCSI_SENSE_NO_SENSE\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - case SCSI_SENSE_RECOVERED_ERROR: - DPRINT("ATAPI error: SCSI_SENSE_RECOVERED_SENSE\n"); - ScsiStatus = 0; - SrbStatus = SRB_STATUS_SUCCESS; - break; - - case SCSI_SENSE_NOT_READY: - DPRINT("ATAPI error: SCSI_SENSE_NOT_READY\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - case SCSI_SENSE_MEDIUM_ERROR: - DPRINT("ATAPI error: SCSI_SENSE_MEDIUM_ERROR\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - case SCSI_SENSE_HARDWARE_ERROR: - DPRINT("ATAPI error: SCSI_SENSE_HARDWARE_ERROR\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - case SCSI_SENSE_ILLEGAL_REQUEST: - DPRINT("ATAPI error: SCSI_SENSE_ILLEGAL_REQUEST\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - case SCSI_SENSE_UNIT_ATTENTION: - DPRINT("ATAPI error: SCSI_SENSE_UNIT_ATTENTION\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - case SCSI_SENSE_DATA_PROTECT: - DPRINT("ATAPI error: SCSI_SENSE_DATA_PROTECT\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - case SCSI_SENSE_BLANK_CHECK: - DPRINT("ATAPI error: SCSI_SENSE_BLANK_CHECK\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - case SCSI_SENSE_ABORTED_COMMAND: - DPRINT("ATAPI error: SCSI_SENSE_ABORTED_COMMAND\n"); - ScsiStatus = SCSISTAT_CHECK_CONDITION; - SrbStatus = SRB_STATUS_ERROR; - break; - - default: - DPRINT("ATAPI error: Invalid sense key\n"); - ScsiStatus = 0; - SrbStatus = SRB_STATUS_ERROR; - break; - } - } - else - { - DPRINT1("IDE error: %02x\n", ErrorReg); - - ScsiStatus = 0; - SrbStatus = SRB_STATUS_ERROR; - -#if 0 - UCHAR SectorCount, SectorNum, CylinderLow, CylinderHigh; - UCHAR DriveHead; - - CylinderLow = IDEReadCylinderLow(CommandPortBase); - CylinderHigh = IDEReadCylinderHigh(CommandPortBase); - DriveHead = IDEReadDriveHead(CommandPortBase); - SectorCount = IDEReadSectorCount(CommandPortBase); - SectorNum = IDEReadSectorNum(CommandPortBase); - - DPRINT1("IDE Error: ERR:%02x CYLLO:%02x CYLHI:%02x SCNT:%02x SNUM:%02x\n", - ErrorReg, - CylinderLow, - CylinderHigh, - SectorCount, - SectorNum); -#endif - } - - - - Srb->ScsiStatus = ScsiStatus; - - DPRINT("AtapiErrorToScsi() done\n"); - - return(SrbStatus); -} - - -static VOID -AtapiScsiSrbToAtapi (PSCSI_REQUEST_BLOCK Srb) -{ - DPRINT("AtapiConvertScsiToAtapi() called\n"); - - Srb->CdbLength = 12; - - switch (Srb->Cdb[0]) - { - case SCSIOP_FORMAT_UNIT: - Srb->Cdb[0] = ATAPI_FORMAT_UNIT; - break; - - case SCSIOP_MODE_SELECT: - { - PATAPI_MODE_SELECT12 AtapiModeSelect; - UCHAR Length; - - AtapiModeSelect = (PATAPI_MODE_SELECT12)Srb->Cdb; - Length = ((PCDB)Srb->Cdb)->MODE_SELECT.ParameterListLength; - - RtlZeroMemory (Srb->Cdb, - MAXIMUM_CDB_SIZE); - AtapiModeSelect->OperationCode = ATAPI_MODE_SELECT; - AtapiModeSelect->PFBit = 1; - AtapiModeSelect->ParameterListLengthMsb = 0; - AtapiModeSelect->ParameterListLengthLsb = Length; - } - break; - } -} - -static VOID FASTCALL -AtapiCompleteRequest(PATAPI_MINIPORT_EXTENSION DevExt, - UCHAR SrbStatus) -{ - PSCSI_REQUEST_BLOCK Srb; - Srb = DevExt->CurrentSrb; - - DPRINT("AtapiCompleteRequest(DevExt %x, SrbStatus %x)\n", DevExt, SrbStatus); - - Srb->SrbStatus = SrbStatus; - if (SrbStatus == SRB_STATUS_ERROR) - { - Srb->SrbStatus = AtapiErrorToScsi((PVOID)DevExt, Srb); - } - else if (SrbStatus == SRB_STATUS_DATA_OVERRUN) - { - Srb->DataTransferLength -= DevExt->DataTransferLength; - } - - DevExt->Handler = NULL; - ScsiPortNotification(RequestComplete, (PVOID)DevExt, Srb); - ScsiPortNotification(NextRequest, (PVOID)DevExt, NULL); -} - -#ifdef ENABLE_DMA -static BOOLEAN FASTCALL -AtapiDmaPacketInterrupt(PATAPI_MINIPORT_EXTENSION DevExt) -{ - UCHAR SrbStatus; - UCHAR DmaCommand; - UCHAR DmaStatus; - UCHAR Status; - UCHAR Error; - UCHAR SensKey; - - DPRINT("AtapiPacketDmaInterrupt\n"); - - DevExt->UseDma = FALSE; - - /* stop DMA */ - DmaCommand = IDEReadDMACommand(DevExt->BusMasterRegisterBase); - IDEWriteDMACommand(DevExt->BusMasterRegisterBase, DmaCommand & 0xfe); - /* get DMA status */ - DmaStatus = IDEReadDMAStatus(DevExt->BusMasterRegisterBase); - /* clear the INTR & ERROR bits */ - IDEWriteDMAStatus(DevExt->BusMasterRegisterBase, DmaStatus | 0x06); - - Status = IDEReadStatus(DevExt->CommandPortBase); - DPRINT("DriveStatus: %x\n", Status); - - if (Status & (IDE_SR_BUSY|IDE_SR_ERR)) - { - if (Status & IDE_SR_ERR) - { - Error = IDEReadError(DevExt->CommandPortBase); - SensKey = Error >> 4; - DPRINT("DriveError: %x, SenseKey: %x\n", Error, SensKey); - } - SrbStatus = SRB_STATUS_ERROR; - } - else - { - if ((DmaStatus & 0x07) != 0x04) - { - DPRINT("DmaStatus: %02x\n", DmaStatus); - SrbStatus = SRB_STATUS_ERROR; - } - else - { - SrbStatus = STATUS_SUCCESS; - } - } - AtapiCompleteRequest(DevExt, SrbStatus); - DPRINT("AtapiDmaPacketInterrupt() done\n"); - return TRUE; -} -#endif - -static BOOLEAN FASTCALL -AtapiPacketInterrupt(PATAPI_MINIPORT_EXTENSION DevExt) -{ - PSCSI_REQUEST_BLOCK Srb; - UCHAR Status; - UCHAR IntReason; - ULONG TransferSize; - ULONG JunkSize = 0; - BOOLEAN IsLastBlock; - PUCHAR TargetAddress; - ULONG Retries; - UCHAR SrbStatus; - UCHAR Error; - UCHAR SensKey; - - DPRINT("AtapiPacketInterrupt()\n"); - - Srb = DevExt->CurrentSrb; - - Status = IDEReadStatus(DevExt->CommandPortBase); - DPRINT("DriveStatus: %x\n", Status); - - if (Status & (IDE_SR_BUSY|IDE_SR_ERR)) - { - if (Status & IDE_SR_ERR) - { - Error = IDEReadError(DevExt->CommandPortBase); - SensKey = Error >> 4; - DPRINT("DriveError: %x, SenseKey: %x\n", Error, SensKey); - } - - AtapiCompleteRequest(DevExt, SRB_STATUS_ERROR); - DPRINT("AtapiPacketInterrupt() done\n"); - return TRUE; - } - - IntReason = IDEReadSectorCount(DevExt->CommandPortBase); - TransferSize = IDEReadCylinderLow(DevExt->CommandPortBase); - TransferSize += IDEReadCylinderHigh(DevExt->CommandPortBase) << 8; - - if (!(Status & IDE_SR_DRQ)) - { - if (DevExt->DataTransferLength > 0) - { - DPRINT1("AtapiPacketInterrupt: data underrun (%d bytes), command was %02x\n", - DevExt->DataTransferLength, Srb->Cdb[0]); - SrbStatus = SRB_STATUS_DATA_OVERRUN; - } - else - { - SrbStatus = SRB_STATUS_SUCCESS; - } - AtapiCompleteRequest(DevExt, SrbStatus); - DPRINT("AtapiPacketInterrupt() done\n"); - return TRUE; - } - - TargetAddress = DevExt->DataBuffer; - - if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) - { - DPRINT("read data\n"); - if (DevExt->DataTransferLength <= TransferSize) - { - JunkSize = TransferSize - DevExt->DataTransferLength; - TransferSize = DevExt->DataTransferLength; - - DevExt->DataTransferLength = 0; - IsLastBlock = TRUE; - } - else - { - DevExt->DataTransferLength -= TransferSize; - IsLastBlock = FALSE; - } - - DPRINT("TargetAddress %x, TransferSize %d\n", TargetAddress, TransferSize); - - DevExt->DataBuffer += TransferSize; - - IDEReadBlock(DevExt->CommandPortBase, TargetAddress, TransferSize); - - /* check DRQ */ - if (IsLastBlock) - { - /* Read remaining junk from device */ - while (JunkSize > 0) - { - IDEReadWord(DevExt->CommandPortBase); - JunkSize -= 2; - } - - for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES && (IDEReadStatus(DevExt->CommandPortBase) & IDE_SR_BUSY); Retries++) - { - ScsiPortStallExecution(10); - } - - /* Check for data overrun */ - while (IDEReadStatus(DevExt->CommandPortBase) & IDE_SR_DRQ) - { - DPRINT1("AtapiInterrupt(): reading overrun data!\n"); - IDEReadWord(DevExt->CommandPortBase); - } - } - - SrbStatus = SRB_STATUS_SUCCESS; - } - else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) - { - DPRINT("write data\n"); - if (DevExt->DataTransferLength < TransferSize) - { - TransferSize = DevExt->DataTransferLength; - } - - TargetAddress = DevExt->DataBuffer; - - DPRINT("TargetAddress %x, TransferSize %x\n", TargetAddress, TransferSize); - - DevExt->DataBuffer += TransferSize; - DevExt->DataTransferLength -= TransferSize; - - /* Write the sector */ - IDEWriteBlock(DevExt->CommandPortBase, TargetAddress, TransferSize); - SrbStatus = SRB_STATUS_SUCCESS; - IsLastBlock = FALSE; - } - else - { - DPRINT("Unspecified transfer direction!\n"); - SrbStatus = SRB_STATUS_SUCCESS; - IsLastBlock = TRUE; - } - if (IsLastBlock) - { - AtapiCompleteRequest(DevExt, SrbStatus); - } - DPRINT("AtapiPacketInterrupt() done\n"); - return TRUE; -} - -static BOOLEAN FASTCALL -AtapiNoDataInterrupt(PATAPI_MINIPORT_EXTENSION DevExt) -{ - UCHAR Status; - - DPRINT("AtapiNoDataInterrupt()\n"); - - Status = IDEReadStatus(DevExt->CommandPortBase); - AtapiCompleteRequest(DevExt, - (Status & (IDE_SR_DRDY|IDE_SR_BUSY|IDE_SR_ERR|IDE_SR_DRQ)) == IDE_SR_DRDY ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR); - - DPRINT("AtapiNoDatanterrupt() done!\n"); - return TRUE; -} - -#ifdef ENABLE_DMA -static BOOLEAN FASTCALL -AtapiDmaInterrupt(PATAPI_MINIPORT_EXTENSION DevExt) -{ - UCHAR DmaCommand; - UCHAR DmaStatus; - UCHAR Status; - - DPRINT("AtapiDmaInterrupt()\n"); - - DevExt->UseDma = FALSE; - /* stop DMA */ - DmaCommand = IDEReadDMACommand(DevExt->BusMasterRegisterBase); - IDEWriteDMACommand(DevExt->BusMasterRegisterBase, DmaCommand & 0xfe); - /* get DMA status */ - DmaStatus = IDEReadDMAStatus(DevExt->BusMasterRegisterBase); - /* clear the INTR & ERROR bits */ - IDEWriteDMAStatus(DevExt->BusMasterRegisterBase, DmaStatus | 0x06); - - /* read the drive status */ - Status = IDEReadStatus(DevExt->CommandPortBase); - if ((Status & (IDE_SR_DRDY|IDE_SR_BUSY|IDE_SR_ERR|IDE_SR_WERR|IDE_SR_DRQ)) == IDE_SR_DRDY && - (DmaStatus & 0x07) == 4) - { - AtapiCompleteRequest(DevExt, SRB_STATUS_SUCCESS); - DPRINT("AtapiDmaInterrupt() done\n"); - return TRUE; - } - DPRINT1("Status %x\n", Status); - DPRINT1("%x\n", DmaStatus); - AtapiCompleteRequest(DevExt, SRB_STATUS_ERROR); - DPRINT1("AtapiDmaReadInterrupt() done\n"); - return TRUE; -} -#endif - -static BOOLEAN FASTCALL -AtapiSmartInterrupt(PATAPI_MINIPORT_EXTENSION DevExt) -{ - PSCSI_REQUEST_BLOCK Srb; - UCHAR DeviceStatus; - PSRB_IO_CONTROL SrbIoControl; - PSENDCMDOUTPARAMS OutParams; - PIDEREGS IdeRegs; - - DPRINT("AtapiSmartInterrupt() called!\n"); - - Srb = DevExt->CurrentSrb; - - - DeviceStatus = IDEReadStatus(DevExt->CommandPortBase); - if ((DeviceStatus & (IDE_SR_DRQ|IDE_SR_BUSY|IDE_SR_ERR)) != (DevExt->DataTransferLength ? IDE_SR_DRQ : 0)) - { - if (DeviceStatus & (IDE_SR_ERR|IDE_SR_DRQ)) - { - AtapiCompleteRequest(DevExt, SRB_STATUS_ERROR); - DPRINT("AtapiSmartInterrupt() done!\n"); - return TRUE; - } - DPRINT("AtapiSmartInterrupt() done!\n"); - return FALSE; - } - - DPRINT("CommandPortBase: %lx ControlPortBase: %lx\n", DevExt->CommandPortBase, DevExt->ControlPortBase); - - if (DevExt->DataTransferLength) - { - IDEReadBlock(DevExt->CommandPortBase, DevExt->DataBuffer, 512); - DevExt->DataTransferLength -= 512; - DevExt->DataBuffer += 512; - } - - if (DevExt->DataTransferLength == 0) - { - SrbIoControl = (PSRB_IO_CONTROL)Srb->DataBuffer; - OutParams = (PSENDCMDOUTPARAMS)(SrbIoControl + 1); - - OutParams->DriverStatus.bDriverError = 0; - OutParams->DriverStatus.bIDEError = 0; - - if (SrbIoControl->ControlCode == IOCTL_SCSI_MINIPORT_RETURN_STATUS) - { - IdeRegs = (PIDEREGS)OutParams->bBuffer; - - IdeRegs->bFeaturesReg = RETURN_SMART_STATUS; - IdeRegs->bSectorCountReg = IDEReadSectorCount(DevExt->CommandPortBase); - IdeRegs->bSectorNumberReg = IDEReadSectorNum(DevExt->CommandPortBase); - IdeRegs->bCylLowReg = IDEReadCylinderLow(DevExt->CommandPortBase); - IdeRegs->bCylHighReg = IDEReadCylinderHigh(DevExt->CommandPortBase); - IdeRegs->bDriveHeadReg = IDEReadDriveHead(DevExt->CommandPortBase); - IdeRegs->bCommandReg = SMART_CMD; - IdeRegs->bReserved = 0; - - OutParams->cBufferSize = 8; - } - else - { - OutParams->cBufferSize = DevExt->DataBuffer - OutParams->bBuffer; + return TRUE; } - AtapiCompleteRequest(DevExt, SRB_STATUS_SUCCESS); - } + // + // Ensure BUSY is non-asserted. + // - DPRINT("AtapiSmartInterrupt() done!\n"); + for (i = 0; i < 10; i++) { - return(TRUE); -} - - -static BOOLEAN FASTCALL -AtapiReadInterrupt(PATAPI_MINIPORT_EXTENSION DevExt) -{ - PSCSI_REQUEST_BLOCK Srb; - UCHAR DeviceStatus; - BOOLEAN IsLastBlock; - PUCHAR TargetAddress; - ULONG TransferSize; - - DPRINT("AtapiReadInterrupt() called!\n"); - - Srb = DevExt->CurrentSrb; - - DeviceStatus = IDEReadStatus(DevExt->CommandPortBase); - if ((DeviceStatus & (IDE_SR_DRQ|IDE_SR_BUSY|IDE_SR_ERR)) != IDE_SR_DRQ) - { - if (DeviceStatus & (IDE_SR_ERR|IDE_SR_DRQ)) - { - AtapiCompleteRequest(DevExt, SRB_STATUS_ERROR); - DPRINT("AtapiReadInterrupt() done!\n"); - return TRUE; + GetBaseStatus(baseIoAddress1, statusByte); + if (!(statusByte & IDE_STATUS_BUSY)) { + break; + } + ScsiPortStallExecution(5000); } - DPRINT("AtapiReadInterrupt() done!\n"); - return FALSE; - } - DPRINT("CommandPortBase: %lx ControlPortBase: %lx\n", DevExt->CommandPortBase, DevExt->ControlPortBase); + if (i == 10) { - /* Update controller/device state variables */ - TargetAddress = DevExt->DataBuffer; - TransferSize = DevExt->TransferSize[Srb->TargetId]; + DebugPrint((2, + "AtapiInterrupt: BUSY on entry. Status %x, Base IO %x\n", + statusByte, + baseIoAddress1)); - DPRINT("TransferLength: %lu\n", Srb->DataTransferLength); - DPRINT("TransferSize: %lu\n", TransferSize); - - if (DevExt->DataTransferLength <= TransferSize) - { - TransferSize = DevExt->DataTransferLength; - DevExt->DataTransferLength = 0; - IsLastBlock = TRUE; - } - else - { - DevExt->DataTransferLength -= TransferSize; - IsLastBlock = FALSE; - } - DevExt->DataBuffer += TransferSize; - DPRINT("IsLastBlock == %s\n", (IsLastBlock) ? "TRUE" : "FALSE"); - - /* Copy the block of data */ - if (DevExt->DeviceFlags[Srb->TargetId] & DEVICE_DWORD_IO) - { - IDEReadBlock32(DevExt->CommandPortBase, TargetAddress, TransferSize); - } - else - { - IDEReadBlock(DevExt->CommandPortBase, TargetAddress, TransferSize); - } - - if (IsLastBlock) - { - AtapiCompleteRequest(DevExt, SRB_STATUS_SUCCESS); - } - - DPRINT("AtapiReadInterrupt() done!\n"); - - return(TRUE); -} - -static BOOLEAN FASTCALL -AtapiWriteInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt) -{ - PSCSI_REQUEST_BLOCK Srb; - UCHAR DeviceStatus; - BOOLEAN IsLastBlock; - PUCHAR TargetAddress; - ULONG TransferSize; - - DPRINT("AtapiWriteInterrupt() called!\n"); - - DeviceStatus = IDEReadStatus(DevExt->CommandPortBase); - if ((DeviceStatus & (IDE_SR_DRDY|IDE_SR_BUSY|IDE_SR_ERR|IDE_SR_WERR)) != IDE_SR_DRDY) - { - if (DeviceStatus & (IDE_SR_DRDY|IDE_SR_ERR|IDE_SR_WERR)) - { - AtapiCompleteRequest(DevExt, SRB_STATUS_ERROR); - DPRINT("AtapiWriteInterrupt() done!\n"); - return TRUE; + ScsiPortNotification(RequestTimerCall, + HwDeviceExtension, + AtapiCallBack, + 500); + return TRUE; } - DPRINT("AtapiWriteInterrupt() done!\n"); - return FALSE; } - else - { - Srb = DevExt->CurrentSrb; - TransferSize = DevExt->TransferSize[Srb->TargetId]; - if (DevExt->DataTransferLength < TransferSize) - { - TransferSize = DevExt->DataTransferLength; - } - if (TransferSize > 0 && (DeviceStatus & IDE_SR_DRQ)) - { - IsLastBlock = FALSE; - TargetAddress = DevExt->DataBuffer; - DevExt->DataBuffer += TransferSize; - DevExt->DataTransferLength -= TransferSize; - DPRINT("TransferLength: %lu\n", Srb->DataTransferLength); - DPRINT("TransferSize: %lu\n", TransferSize); - /* Write the sector */ - if (DevExt->DeviceFlags[Srb->TargetId] & DEVICE_DWORD_IO) - { - IDEWriteBlock32(DevExt->CommandPortBase, TargetAddress, TransferSize); - } - else - { - IDEWriteBlock(DevExt->CommandPortBase, TargetAddress, TransferSize); - } - } - else if (DeviceStatus & IDE_SR_DRQ) - { - DPRINT("AtapiWriteInterrupt(): data overrun error!\n"); - IsLastBlock = TRUE; - } - else if (TransferSize > 0 && !(DeviceStatus & IDE_SR_DRQ)) - { - DPRINT1("%d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", TransferSize); - IsLastBlock = TRUE; + + // + // Check for error conditions. + // + + if (statusByte & IDE_STATUS_ERROR) { + + if (srb->Cdb[0] != SCSIOP_REQUEST_SENSE) { + + // + // Fail this request. + // + + status = SRB_STATUS_ERROR; + goto CompleteRequest; } - else - { - IsLastBlock = TRUE; - } - if (IsLastBlock) - { - AtapiCompleteRequest(DevExt, SRB_STATUS_SUCCESS); + } + + // + // check reason for this interrupt. + // + + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + interruptReason = (ScsiPortReadPortUchar(&baseIoAddress1->InterruptReason) & 0x3); + atapiDev = TRUE; + wordsThisInterrupt = 256; + + } else { + + if (statusByte & IDE_STATUS_DRQ) { + + if (deviceExtension->MaximumBlockXfer[srb->TargetId]) { + wordsThisInterrupt = 256 * deviceExtension->MaximumBlockXfer[srb->TargetId]; + + } + + if (srb->SrbFlags & SRB_FLAGS_DATA_IN) { + + interruptReason = 0x2; + + } else if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + interruptReason = 0x0; + + } else { + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + } else if (statusByte & IDE_STATUS_BUSY) { + + return FALSE; + + } else { + + if (deviceExtension->WordsLeft) { + + ULONG k; + + // + // Funky behaviour seen with PCI IDE (not all, just one). + // The ISR hits with DRQ low, but comes up later. + // + + for (k = 0; k < 5000; k++) { + GetStatus(baseIoAddress2,statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) { + ScsiPortStallExecution(100); + } else { + break; + } + } + + if (k == 5000) { + + // + // reset the controller. + // + + DebugPrint((1, + "AtapiInterrupt: Resetting due to DRQ not up. Status %x, Base IO %x\n", + statusByte, + baseIoAddress1)); + + AtapiResetController(HwDeviceExtension,srb->PathId); + return TRUE; + } else { + + interruptReason = (srb->SrbFlags & SRB_FLAGS_DATA_IN) ? 0x2 : 0x0; + } + + } else { + + // + // Command complete - verify, write, or the SMART enable/disable. + // + // Also get_media_status + + interruptReason = 0x3; + } } - DPRINT("AtapiWriteInterrupt() done!\n"); - return TRUE; } -} -static VOID -AtapiExecuteCommand(PATAPI_MINIPORT_EXTENSION DevExt, - UCHAR command, - BOOLEAN (FASTCALL *Handler)(PATAPI_MINIPORT_EXTENSION)) + if (interruptReason == 0x1 && (statusByte & IDE_STATUS_DRQ)) { + + // + // Write the packet. + // + + DebugPrint((2, + "AtapiInterrupt: Writing Atapi packet.\n")); + + // + // Send CDB to device. + // + + WriteBuffer(baseIoAddress1, + (PUSHORT)srb->Cdb, + 6); + + return TRUE; + + } else if (interruptReason == 0x0 && (statusByte & IDE_STATUS_DRQ)) { + + // + // Write the data. + // + + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + // + // Pick up bytes to transfer and convert to words. + // + + wordCount = + ScsiPortReadPortUchar(&baseIoAddress1->ByteCountLow); + + wordCount |= + ScsiPortReadPortUchar(&baseIoAddress1->ByteCountHigh) << 8; + + // + // Covert bytes to words. + // + + wordCount >>= 1; + + if (wordCount != deviceExtension->WordsLeft) { + DebugPrint((3, + "AtapiInterrupt: %d words requested; %d words xferred\n", + deviceExtension->WordsLeft, + wordCount)); + } + + // + // Verify this makes sense. + // + + if (wordCount > deviceExtension->WordsLeft) { + wordCount = deviceExtension->WordsLeft; + } + + } else { + + // + // IDE path. Check if words left is at least 256. + // + + if (deviceExtension->WordsLeft < wordsThisInterrupt) { + + // + // Transfer only words requested. + // + + wordCount = deviceExtension->WordsLeft; + + } else { + + // + // Transfer next block. + // + + wordCount = wordsThisInterrupt; + } + } + + // + // Ensure that this is a write command. + // + + if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + + DebugPrint((3, + "AtapiInterrupt: Write interrupt\n")); + + WaitOnBusy(baseIoAddress2,statusByte); + + if (atapiDev || !deviceExtension->DWordIO) { + + WriteBuffer(baseIoAddress1, + deviceExtension->DataBuffer, + wordCount); + } else { + + PIDE_REGISTERS_3 address3 = (PIDE_REGISTERS_3)baseIoAddress1; + + WriteBuffer2(address3, + (PULONG)(deviceExtension->DataBuffer), + wordCount / 2); + } + } else { + + DebugPrint((1, + "AtapiInterrupt: Int reason %x, but srb is for a write %x.\n", + interruptReason, + srb)); + + // + // Fail this request. + // + + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + + // + // Advance data buffer pointer and bytes left. + // + + deviceExtension->DataBuffer += wordCount; + deviceExtension->WordsLeft -= wordCount; + + return TRUE; + + } else if (interruptReason == 0x2 && (statusByte & IDE_STATUS_DRQ)) { + + + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + // + // Pick up bytes to transfer and convert to words. + // + + wordCount = + ScsiPortReadPortUchar(&baseIoAddress1->ByteCountLow); + + wordCount |= + ScsiPortReadPortUchar(&baseIoAddress1->ByteCountHigh) << 8; + + // + // Covert bytes to words. + // + + wordCount >>= 1; + + if (wordCount != deviceExtension->WordsLeft) { + DebugPrint((3, + "AtapiInterrupt: %d words requested; %d words xferred\n", + deviceExtension->WordsLeft, + wordCount)); + } + + // + // Verify this makes sense. + // + + if (wordCount > deviceExtension->WordsLeft) { + wordCount = deviceExtension->WordsLeft; + } + + } else { + + // + // Check if words left is at least 256. + // + + if (deviceExtension->WordsLeft < wordsThisInterrupt) { + + // + // Transfer only words requested. + // + + wordCount = deviceExtension->WordsLeft; + + } else { + + // + // Transfer next block. + // + + wordCount = wordsThisInterrupt; + } + } + + // + // Ensure that this is a read command. + // + + if (srb->SrbFlags & SRB_FLAGS_DATA_IN) { + + DebugPrint((3, + "AtapiInterrupt: Read interrupt\n")); + + WaitOnBusy(baseIoAddress2,statusByte); + + if (atapiDev || !deviceExtension->DWordIO) { + ReadBuffer(baseIoAddress1, + deviceExtension->DataBuffer, + wordCount); + + } else { + PIDE_REGISTERS_3 address3 = (PIDE_REGISTERS_3)baseIoAddress1; + + ReadBuffer2(address3, + (PULONG)(deviceExtension->DataBuffer), + wordCount / 2); + } + } else { + + DebugPrint((1, + "AtapiInterrupt: Int reason %x, but srb is for a read %x.\n", + interruptReason, + srb)); + + // + // Fail this request. + // + + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + // + // Translate ATAPI data back to SCSI data if needed + // + + if (srb->Cdb[0] == ATAPI_MODE_SENSE && + deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + // + //convert and adjust the wordCount + // + + wordCount -= Atapi2Scsi(srb, (char *)deviceExtension->DataBuffer, + wordCount << 1); + } + // + // Advance data buffer pointer and bytes left. + // + + deviceExtension->DataBuffer += wordCount; + deviceExtension->WordsLeft -= wordCount; + + // + // Check for read command complete. + // + + if (deviceExtension->WordsLeft == 0) { + + if (deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + // + // Work around to make many atapi devices return correct sector size + // of 2048. Also certain devices will have sector count == 0x00, check + // for that also. + // + + if ((srb->Cdb[0] == 0x25) && + ((deviceExtension->IdentifyData[srb->TargetId].GeneralConfiguration >> 8) & 0x1f) == 0x05) { + + deviceExtension->DataBuffer -= wordCount; + if (deviceExtension->DataBuffer[0] == 0x00) { + + *((ULONG *) &(deviceExtension->DataBuffer[0])) = 0xFFFFFF7F; + + } + + *((ULONG *) &(deviceExtension->DataBuffer[2])) = 0x00080000; + deviceExtension->DataBuffer += wordCount; + } + } else { + + // + // Completion for IDE drives. + // + + + if (deviceExtension->WordsLeft) { + + status = SRB_STATUS_DATA_OVERRUN; + + } else { + + status = SRB_STATUS_SUCCESS; + + } + + goto CompleteRequest; + + } + } + + return TRUE; + + } else if (interruptReason == 0x3 && !(statusByte & IDE_STATUS_DRQ)) { + + // + // Command complete. + // + + if (deviceExtension->WordsLeft) { + + status = SRB_STATUS_DATA_OVERRUN; + + } else { + + status = SRB_STATUS_SUCCESS; + + } + +CompleteRequest: + + // + // Check and see if we are processing our secret (mechanism status/request sense) srb + // + if (deviceExtension->OriginalSrb) { + + ULONG srbStatus; + + if (srb->Cdb[0] == SCSIOP_MECHANISM_STATUS) { + + if (status == SRB_STATUS_SUCCESS) { + // Bingo!! + AtapiHwInitializeChanger (HwDeviceExtension, + srb->TargetId, + (PMECHANICAL_STATUS_INFORMATION_HEADER) srb->DataBuffer); + + // Get ready to issue the original srb + srb = deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + + } else { + // failed! Get the sense key and maybe try again + srb = deviceExtension->CurrentSrb = BuildRequestSenseSrb ( + HwDeviceExtension, + deviceExtension->OriginalSrb->PathId, + deviceExtension->OriginalSrb->TargetId); + } + + srbStatus = AtapiSendCommand(HwDeviceExtension, deviceExtension->CurrentSrb); + if (srbStatus == SRB_STATUS_PENDING) { + return TRUE; + } + + } else { // srb->Cdb[0] == SCSIOP_REQUEST_SENSE) + + PSENSE_DATA senseData = (PSENSE_DATA) srb->DataBuffer; + + if (status == SRB_STATUS_DATA_OVERRUN) { + // Check to see if we at least get mininum number of bytes + if ((srb->DataTransferLength - deviceExtension->WordsLeft) > + (FIELD_OFFSET (SENSE_DATA, AdditionalSenseLength) + sizeof(senseData->AdditionalSenseLength))) { + status = SRB_STATUS_SUCCESS; + } + } + + if (status == SRB_STATUS_SUCCESS) { + if ((senseData->SenseKey != SCSI_SENSE_ILLEGAL_REQUEST) && + deviceExtension->MechStatusRetryCount) { + + // The sense key doesn't say the last request is illegal, so try again + deviceExtension->MechStatusRetryCount--; + srb = deviceExtension->CurrentSrb = BuildMechanismStatusSrb ( + HwDeviceExtension, + deviceExtension->OriginalSrb->PathId, + deviceExtension->OriginalSrb->TargetId); + } else { + + // last request was illegal. No point trying again + + AtapiHwInitializeChanger (HwDeviceExtension, + srb->TargetId, + (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL); + + // Get ready to issue the original srb + srb = deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + } + + srbStatus = AtapiSendCommand(HwDeviceExtension, deviceExtension->CurrentSrb); + if (srbStatus == SRB_STATUS_PENDING) { + return TRUE; + } + } + } + + // If we get here, it means AtapiSendCommand() has failed + // Can't recover. Pretend the original srb has failed and complete it. + + if (deviceExtension->OriginalSrb) { + AtapiHwInitializeChanger (HwDeviceExtension, + srb->TargetId, + (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL); + srb = deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + } + + // fake an error and read no data + status = SRB_STATUS_ERROR; + srb->ScsiStatus = 0; + deviceExtension->DataBuffer = srb->DataBuffer; + deviceExtension->WordsLeft = srb->DataTransferLength; + deviceExtension->RDP = FALSE; + + } else if (status == SRB_STATUS_ERROR) { + + // + // Map error to specific SRB status and handle request sense. + // + + status = MapError(deviceExtension, + srb); + + deviceExtension->RDP = FALSE; + + } else { + + // + // Wait for busy to drop. + // + + for (i = 0; i < 30; i++) { + GetStatus(baseIoAddress2,statusByte); + if (!(statusByte & IDE_STATUS_BUSY)) { + break; + } + ScsiPortStallExecution(500); + } + + if (i == 30) { + + // + // reset the controller. + // + + DebugPrint((1, + "AtapiInterrupt: Resetting due to BSY still up - %x. Base Io %x\n", + statusByte, + baseIoAddress1)); + AtapiResetController(HwDeviceExtension,srb->PathId); + return TRUE; + } + + // + // Check to see if DRQ is still up. + // + + if (statusByte & IDE_STATUS_DRQ) { + + for (i = 0; i < 500; i++) { + GetStatus(baseIoAddress2,statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) { + break; + } + ScsiPortStallExecution(100); + + } + + if (i == 500) { + + // + // reset the controller. + // + + DebugPrint((1, + "AtapiInterrupt: Resetting due to DRQ still up - %x\n", + statusByte)); + AtapiResetController(HwDeviceExtension,srb->PathId); + return TRUE; + } + + } + } + + + // + // Clear interrupt expecting flag. + // + + deviceExtension->ExpectingInterrupt = FALSE; + + // + // Sanity check that there is a current request. + // + + if (srb != NULL) { + + // + // Set status in SRB. + // + + srb->SrbStatus = (UCHAR)status; + + // + // Check for underflow. + // + + if (deviceExtension->WordsLeft) { + + // + // Subtract out residual words and update if filemark hit, + // setmark hit , end of data, end of media... + // + + if (!(deviceExtension->DeviceFlags[srb->TargetId] & DFLAGS_TAPE_DEVICE)) { + if (status == SRB_STATUS_DATA_OVERRUN) { + srb->DataTransferLength -= deviceExtension->WordsLeft; + } else { + srb->DataTransferLength = 0; + } + } else { + srb->DataTransferLength -= deviceExtension->WordsLeft; + } + } + + if (srb->Function != SRB_FUNCTION_IO_CONTROL) { + + // + // Indicate command complete. + // + + if (!(deviceExtension->RDP)) { + ScsiPortNotification(RequestComplete, + deviceExtension, + srb); + + } + } else { + + PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + UCHAR error = 0; + + if (status != SRB_STATUS_SUCCESS) { + error = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + } + + // + // Build the SMART status block depending upon the completion status. + // + + cmdOutParameters->cBufferSize = wordCount; + cmdOutParameters->DriverStatus.bDriverError = (error) ? SMART_IDE_ERROR : 0; + cmdOutParameters->DriverStatus.bIDEError = error; + + // + // If the sub-command is return smart status, jam the value from cylinder low and high, into the + // data buffer. + // + + if (deviceExtension->SmartCommand == RETURN_SMART_STATUS) { + cmdOutParameters->bBuffer[0] = RETURN_SMART_STATUS; + cmdOutParameters->bBuffer[1] = ScsiPortReadPortUchar(&baseIoAddress1->InterruptReason); + cmdOutParameters->bBuffer[2] = ScsiPortReadPortUchar(&baseIoAddress1->Unused1); + cmdOutParameters->bBuffer[3] = ScsiPortReadPortUchar(&baseIoAddress1->ByteCountLow); + cmdOutParameters->bBuffer[4] = ScsiPortReadPortUchar(&baseIoAddress1->ByteCountHigh); + cmdOutParameters->bBuffer[5] = ScsiPortReadPortUchar(&baseIoAddress1->DriveSelect); + cmdOutParameters->bBuffer[6] = SMART_CMD; + cmdOutParameters->cBufferSize = 8; + } + + // + // Indicate command complete. + // + + ScsiPortNotification(RequestComplete, + deviceExtension, + srb); + + } + + } else { + + DebugPrint((1, + "AtapiInterrupt: No SRB!\n")); + } + + // + // Indicate ready for next request. + // + + if (!(deviceExtension->RDP)) { + + // + // Clear current SRB. + // + + deviceExtension->CurrentSrb = NULL; + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + } else { + + ScsiPortNotification(RequestTimerCall, + HwDeviceExtension, + AtapiCallBack, + 2000); + } + + return TRUE; + + } else { + + // + // Unexpected int. + // + + DebugPrint((3, + "AtapiInterrupt: Unexpected interrupt. InterruptReason %x. Status %x.\n", + interruptReason, + statusByte)); + return FALSE; + } + + return TRUE; + +} // end AtapiInterrupt() + + +ULONG +STDCALL +IdeSendSmartCommand( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine handles SMART enable, disable, read attributes and threshold commands. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + SRB status + +--*/ + { - if (DevExt->Handler != NULL) - { - DPRINT1("DevExt->Handler is already set!!\n"); - } - DevExt->Handler = Handler; - IDEWriteCommand(DevExt->CommandPortBase, command); - ScsiPortStallExecution(1); -} + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + SENDCMDINPARAMS cmdInParameters = *(PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + PIDEREGS regs = &cmdInParameters.irDriveRegs; + ULONG i; + UCHAR statusByte,targetId; + + + if (cmdInParameters.irDriveRegs.bCommandReg == SMART_CMD) { + + targetId = cmdInParameters.bDriveNumber; + + //TODO optimize this check + + if ((!(deviceExtension->DeviceFlags[targetId] & DFLAGS_DEVICE_PRESENT)) || + (deviceExtension->DeviceFlags[targetId] & DFLAGS_ATAPI_DEVICE)) { + + return SRB_STATUS_SELECTION_TIMEOUT; + } + + deviceExtension->SmartCommand = cmdInParameters.irDriveRegs.bFeaturesReg; + + // + // Determine which of the commands to carry out. + // + + if ((cmdInParameters.irDriveRegs.bFeaturesReg == READ_ATTRIBUTES) || + (cmdInParameters.irDriveRegs.bFeaturesReg == READ_THRESHOLDS)) { + + WaitOnBusy(baseIoAddress2,statusByte); + + if (statusByte & IDE_STATUS_BUSY) { + DebugPrint((1, + "IdeSendSmartCommand: Returning BUSY status\n")); + return SRB_STATUS_BUSY; + } + + // + // Zero the ouput buffer as the input buffer info. has been saved off locally (the buffers are the same). + // + + for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) + READ_ATTRIBUTE_BUFFER_SIZE - 1); i++) { + ((PUCHAR)cmdOutParameters)[i] = 0; + } + + // + // Set data buffer pointer and words left. + // + + deviceExtension->DataBuffer = (PUSHORT)cmdOutParameters->bBuffer; + deviceExtension->WordsLeft = READ_ATTRIBUTE_BUFFER_SIZE / 2; + + // + // Indicate expecting an interrupt. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,(UCHAR)(((targetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar((PUCHAR)baseIoAddress1 + 1,regs->bFeaturesReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount,regs->bSectorCountReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber,regs->bSectorNumberReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow,regs->bCylLowReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh,regs->bCylHighReg); + ScsiPortWritePortUchar(&baseIoAddress1->Command,regs->bCommandReg); + + // + // Wait for interrupt. + // + + return SRB_STATUS_PENDING; + + } else if ((cmdInParameters.irDriveRegs.bFeaturesReg == ENABLE_SMART) || + (cmdInParameters.irDriveRegs.bFeaturesReg == DISABLE_SMART) || + (cmdInParameters.irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) || + (cmdInParameters.irDriveRegs.bFeaturesReg == ENABLE_DISABLE_AUTOSAVE) || + (cmdInParameters.irDriveRegs.bFeaturesReg == EXECUTE_OFFLINE_DIAGS) || + (cmdInParameters.irDriveRegs.bFeaturesReg == SAVE_ATTRIBUTE_VALUES)) { + + WaitOnBusy(baseIoAddress2,statusByte); + + if (statusByte & IDE_STATUS_BUSY) { + DebugPrint((1, + "IdeSendSmartCommand: Returning BUSY status\n")); + return SRB_STATUS_BUSY; + } + + // + // Zero the ouput buffer as the input buffer info. has been saved off locally (the buffers are the same). + // + + for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) - 1); i++) { + ((PUCHAR)cmdOutParameters)[i] = 0; + } + + // + // Set data buffer pointer and indicate no data transfer. + // + + deviceExtension->DataBuffer = (PUSHORT)cmdOutParameters->bBuffer; + deviceExtension->WordsLeft = 0; + + // + // Indicate expecting an interrupt. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,(UCHAR)(((targetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar((PUCHAR)baseIoAddress1 + 1,regs->bFeaturesReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount,regs->bSectorCountReg); + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber,regs->bSectorNumberReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow,regs->bCylLowReg); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh,regs->bCylHighReg); + ScsiPortWritePortUchar(&baseIoAddress1->Command,regs->bCommandReg); + + // + // Wait for interrupt. + // + + return SRB_STATUS_PENDING; + } + } + + return SRB_STATUS_INVALID_REQUEST; + +} // end IdeSendSmartCommand() + + +ULONG +STDCALL +IdeReadWrite( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine handles IDE read and writes. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + SRB status + +--*/ -#ifdef ENABLE_DMA -static BOOLEAN -AtapiInitDma(PATAPI_MINIPORT_EXTENSION DevExt, - PSCSI_REQUEST_BLOCK Srb, - UCHAR cmd) { - PVOID StartAddress; - PVOID EndAddress; - PPRD PRDEntry = DevExt->PRDTable; - SCSI_PHYSICAL_ADDRESS PhysicalAddress; - ULONG Length; - ULONG tmpLength; - UCHAR Status; + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + ULONG startingSector,i; + ULONG wordCount; + UCHAR statusByte,statusByte2; + UCHAR cylinderHigh,cylinderLow,drvSelect,sectorNumber; - DPRINT("AtapiInitDma()\n"); + // + // Select device 0 or 1. + // - StartAddress = Srb->DataBuffer; - EndAddress = (PVOID)((ULONG_PTR)StartAddress + Srb->DataTransferLength); - DevExt->PRDCount = 0; + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); - while (StartAddress < EndAddress) - { - PhysicalAddress = ScsiPortGetPhysicalAddress(DevExt, Srb, StartAddress, &Length); - if (PhysicalAddress.QuadPart == 0LL || Length == 0) - { - return FALSE; - } + WaitOnBusy(baseIoAddress2,statusByte2); - while (Length) - { - /* calculate the length up to the next 64k boundary */ - tmpLength = 0x10000 - (PhysicalAddress.u.LowPart & 0xffff); - if (tmpLength > Length) - { - tmpLength = Length; - } - DevExt->PRDCount++; - if (DevExt->PRDCount > DevExt->PRDMaxCount) - { - return FALSE; - } - if (tmpLength == 0x10000) - { - /* Some dirty controllers cannot handle 64k transfers. We split a 64k transfer in two 32k. */ - tmpLength = 0x8000; - DPRINT("PRD Nr. %d VirtualAddress %08x PhysicalAddress %08x, Length %04x\n", - DevExt->PRDCount - 1, StartAddress, PhysicalAddress.u.LowPart, tmpLength); - PRDEntry->PhysAddress = PhysicalAddress.u.LowPart; - PRDEntry->Length = tmpLength; - PRDEntry++; - DevExt->PRDCount++; - if (DevExt->PRDCount > DevExt->PRDMaxCount) - { - return FALSE; - } - PhysicalAddress.u.LowPart += tmpLength; - StartAddress = (PVOID)((ULONG_PTR)StartAddress + tmpLength); - Length -= tmpLength; - PRDEntry->PhysAddress = PhysicalAddress.u.LowPart; - } - DPRINT("PRD Nr. %d VirtualAddress %08x PhysicalAddress %08x, Length %04x\n", - DevExt->PRDCount - 1, StartAddress, PhysicalAddress.u.LowPart, tmpLength); - PRDEntry->PhysAddress = PhysicalAddress.u.LowPart; - PRDEntry->Length = tmpLength; - PRDEntry++; - StartAddress = (PVOID)((ULONG_PTR)StartAddress + tmpLength); - PhysicalAddress.u.LowPart += tmpLength; - Length -= tmpLength; - } + if (statusByte2 & IDE_STATUS_BUSY) { + DebugPrint((1, + "IdeReadWrite: Returning BUSY status\n")); + return SRB_STATUS_BUSY; } - /* set the end marker in the last PRD */ - PRDEntry--; - PRDEntry->Length |= 0x80000000; - /* set the PDR table */ - IDEWritePRDTable(DevExt->BusMasterRegisterBase, DevExt->PRDTablePhysicalAddress.u.LowPart); - /* write the DMA command */ - IDEWriteDMACommand(DevExt->BusMasterRegisterBase, cmd); - /* reset the status and interrupt bit */ - Status = IDEReadDMAStatus(DevExt->BusMasterRegisterBase); - IDEWriteDMAStatus(DevExt->BusMasterRegisterBase, Status | 0x06); - return TRUE; -} -#endif -/* EOF */ + // + // Set data buffer pointer and words left. + // + + deviceExtension->DataBuffer = (PUSHORT)Srb->DataBuffer; + deviceExtension->WordsLeft = Srb->DataTransferLength / 2; + + // + // Indicate expecting an interrupt. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + // + // Set up sector count register. Round up to next block. + // + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, + (UCHAR)((Srb->DataTransferLength + 0x1FF) / 0x200)); + + // + // Get starting sector number from CDB. + // + + startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + + DebugPrint((2, + "IdeReadWrite: Starting sector is %x, Number of bytes %x\n", + startingSector, + Srb->DataTransferLength)); + + // + // Set up sector number register. + // + + sectorNumber = (UCHAR)((startingSector % deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) + 1); + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber,sectorNumber); + + // + // Set up cylinder low register. + // + + cylinderLow = (UCHAR)(startingSector / (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads)); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow,cylinderLow); + + // + // Set up cylinder high register. + // + + cylinderHigh = (UCHAR)((startingSector / (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads)) >> 8); + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh,cylinderHigh); + + // + // Set up head and drive select register. + // + + drvSelect = (UCHAR)(((startingSector / deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) % + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads) |((Srb->TargetId & 0x1) << 4) | 0xA0); + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect,drvSelect); + + DebugPrint((2, + "IdeReadWrite: Cylinder %x Head %x Sector %x\n", + startingSector / + (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads), + (startingSector / + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) % + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads, + startingSector % + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack + 1)); + + // + // Check if write request. + // + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + + // + // Send read command. + // + + if (deviceExtension->MaximumBlockXfer[Srb->TargetId]) { + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_READ_MULTIPLE); + + } else { + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_READ); + } + } else { + + + // + // Send write command. + // + + if (deviceExtension->MaximumBlockXfer[Srb->TargetId]) { + wordCount = 256 * deviceExtension->MaximumBlockXfer[Srb->TargetId]; + + if (deviceExtension->WordsLeft < wordCount) { + + // + // Transfer only words requested. + // + + wordCount = deviceExtension->WordsLeft; + + } + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_WRITE_MULTIPLE); + + } else { + wordCount = 256; + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_WRITE); + } + + // + // Wait for BSY and DRQ. + // + + WaitOnBaseBusy(baseIoAddress1,statusByte); + + if (statusByte & IDE_STATUS_BUSY) { + + DebugPrint((1, + "IdeReadWrite 2: Returning BUSY status %x\n", + statusByte)); + return SRB_STATUS_BUSY; + } + + for (i = 0; i < 1000; i++) { + GetBaseStatus(baseIoAddress1, statusByte); + if (statusByte & IDE_STATUS_DRQ) { + break; + } + ScsiPortStallExecution(200); + + } + + if (!(statusByte & IDE_STATUS_DRQ)) { + + DebugPrint((1, + "IdeReadWrite: DRQ never asserted (%x) original status (%x)\n", + statusByte, + statusByte2)); + + deviceExtension->WordsLeft = 0; + + // + // Clear interrupt expecting flag. + // + + deviceExtension->ExpectingInterrupt = FALSE; + + // + // Clear current SRB. + // + + deviceExtension->CurrentSrb = NULL; + + return SRB_STATUS_TIMEOUT; + } + + // + // Write next 256 words. + // + + WriteBuffer(baseIoAddress1, + deviceExtension->DataBuffer, + wordCount); + + // + // Adjust buffer address and words left count. + // + + deviceExtension->WordsLeft -= wordCount; + deviceExtension->DataBuffer += wordCount; + + } + + // + // Wait for interrupt. + // + + return SRB_STATUS_PENDING; + +} // end IdeReadWrite() + + + +ULONG +STDCALL +IdeVerify( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine handles IDE Verify. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + SRB status + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + ULONG startingSector; + ULONG sectors; + ULONG endSector; + USHORT sectorCount; + + // + // Drive has these number sectors. + // + + sectors = deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfCylinders; + + DebugPrint((3, + "IdeVerify: Total sectors %x\n", + sectors)); + + // + // Get starting sector number from CDB. + // + + startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + + DebugPrint((3, + "IdeVerify: Starting sector %x. Number of blocks %x\n", + startingSector, + ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb)); + + sectorCount = (USHORT)(((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8 | + ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb ); + endSector = startingSector + sectorCount; + + DebugPrint((3, + "IdeVerify: Ending sector %x\n", + endSector)); + + if (endSector > sectors) { + + // + // Too big, round down. + // + + DebugPrint((1, + "IdeVerify: Truncating request to %x blocks\n", + sectors - startingSector - 1)); + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount, + (UCHAR)(sectors - startingSector - 1)); + + } else { + + // + // Set up sector count register. Round up to next block. + // + + if (sectorCount > 0xFF) { + sectorCount = (USHORT)0xFF; + } + + ScsiPortWritePortUchar(&baseIoAddress1->BlockCount,(UCHAR)sectorCount); + } + + // + // Set data buffer pointer and words left. + // + + deviceExtension->DataBuffer = (PUSHORT)Srb->DataBuffer; + deviceExtension->WordsLeft = Srb->DataTransferLength / 2; + + // + // Indicate expecting an interrupt. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + // + // Set up sector number register. + // + + ScsiPortWritePortUchar(&baseIoAddress1->BlockNumber, + (UCHAR)((startingSector % + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) + 1)); + + // + // Set up cylinder low register. + // + + ScsiPortWritePortUchar(&baseIoAddress1->CylinderLow, + (UCHAR)(startingSector / + (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads))); + + // + // Set up cylinder high register. + // + + ScsiPortWritePortUchar(&baseIoAddress1->CylinderHigh, + (UCHAR)((startingSector / + (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads)) >> 8)); + + // + // Set up head and drive select register. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((startingSector / + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) % + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads) | + ((Srb->TargetId & 0x1) << 4) | 0xA0)); + + DebugPrint((2, + "IdeVerify: Cylinder %x Head %x Sector %x\n", + startingSector / + (deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads), + (startingSector / + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) % + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads, + startingSector % + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack + 1)); + + + // + // Send verify command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_VERIFY); + + // + // Wait for interrupt. + // + + return SRB_STATUS_PENDING; + +} // end IdeVerify() + + +VOID +STDCALL +Scsi2Atapi( + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Convert SCSI packet command to Atapi packet command. + +Arguments: + + Srb - IO request packet + +Return Value: + + None + +--*/ +{ + // + // Change the cdb length + // + + Srb->CdbLength = 12; + + switch (Srb->Cdb[0]) { + case SCSIOP_MODE_SENSE: { + PMODE_SENSE_10 modeSense10 = (PMODE_SENSE_10)Srb->Cdb; + UCHAR PageCode = ((PCDB)Srb->Cdb)->MODE_SENSE.PageCode; + UCHAR Length = ((PCDB)Srb->Cdb)->MODE_SENSE.AllocationLength; + + AtapiZeroMemory(Srb->Cdb,MAXIMUM_CDB_SIZE); + + modeSense10->OperationCode = ATAPI_MODE_SENSE; + modeSense10->PageCode = PageCode; + modeSense10->ParameterListLengthMsb = 0; + modeSense10->ParameterListLengthLsb = Length; + break; + } + + case SCSIOP_MODE_SELECT: { + PMODE_SELECT_10 modeSelect10 = (PMODE_SELECT_10)Srb->Cdb; + UCHAR Length = ((PCDB)Srb->Cdb)->MODE_SELECT.ParameterListLength; + + // + // Zero the original cdb + // + + AtapiZeroMemory(Srb->Cdb,MAXIMUM_CDB_SIZE); + + modeSelect10->OperationCode = ATAPI_MODE_SELECT; + modeSelect10->PFBit = 1; + modeSelect10->ParameterListLengthMsb = 0; + modeSelect10->ParameterListLengthLsb = Length; + break; + } + + case SCSIOP_FORMAT_UNIT: + Srb->Cdb[0] = ATAPI_FORMAT_UNIT; + break; + } +} + + + +ULONG +STDCALL +AtapiSendCommand( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Send ATAPI packet command to device. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PATAPI_REGISTERS_1 baseIoAddress1 = (PATAPI_REGISTERS_1)deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PATAPI_REGISTERS_2 baseIoAddress2 = (PATAPI_REGISTERS_2)deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + ULONG i; + ULONG flags; + UCHAR statusByte,byteCountLow,byteCountHigh; + + // + // We need to know how many platters our atapi cd-rom device might have. + // Before anyone tries to send a srb to our target for the first time, + // we must "secretly" send down a separate mechanism status srb in order to + // initialize our device extension changer data. That's how we know how + // many platters our target has. + // + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_CHANGER_INITED) && + !deviceExtension->OriginalSrb) { + + ULONG srbStatus; + + // + // Set this flag now. If the device hangs on the mech. status + // command, we will not have the change to set it. + // + deviceExtension->DeviceFlags[Srb->TargetId] |= DFLAGS_CHANGER_INITED; + + deviceExtension->MechStatusRetryCount = 3; + deviceExtension->CurrentSrb = BuildMechanismStatusSrb ( + HwDeviceExtension, + Srb->PathId, + Srb->TargetId); + deviceExtension->OriginalSrb = Srb; + + srbStatus = AtapiSendCommand(HwDeviceExtension, deviceExtension->CurrentSrb); + if (srbStatus == SRB_STATUS_PENDING) { + return srbStatus; + } else { + deviceExtension->CurrentSrb = deviceExtension->OriginalSrb; + deviceExtension->OriginalSrb = NULL; + AtapiHwInitializeChanger (HwDeviceExtension, + Srb->TargetId, + (PMECHANICAL_STATUS_INFORMATION_HEADER) NULL); + // fall out + } + } + + DebugPrint((2, + "AtapiSendCommand: Command %x to TargetId %d lun %d\n", + Srb->Cdb[0], + Srb->TargetId, + Srb->Lun)); + + // + // Make sure command is to ATAPI device. + // + + flags = deviceExtension->DeviceFlags[Srb->TargetId]; + if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) { + if ((Srb->Lun) > (deviceExtension->DiscsPresent[Srb->TargetId] - 1)) { + + // + // Indicate no device found at this address. + // + + return SRB_STATUS_SELECTION_TIMEOUT; + } + } else if (Srb->Lun > 0) { + return SRB_STATUS_SELECTION_TIMEOUT; + } + + if (!(flags & DFLAGS_ATAPI_DEVICE)) { + return SRB_STATUS_SELECTION_TIMEOUT; + } + + // + // Select device 0 or 1. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + + // + // Verify that controller is ready for next command. + // + + GetStatus(baseIoAddress2,statusByte); + + DebugPrint((2, + "AtapiSendCommand: Entered with status %x\n", + statusByte)); + + if (statusByte & IDE_STATUS_BUSY) { + DebugPrint((1, + "AtapiSendCommand: Device busy (%x)\n", + statusByte)); + return SRB_STATUS_BUSY; + + } + + if (statusByte & IDE_STATUS_ERROR) { + if (Srb->Cdb[0] != SCSIOP_REQUEST_SENSE) { + + DebugPrint((1, + "AtapiSendCommand: Error on entry: (%x)\n", + statusByte)); + // + // Read the error reg. to clear it and fail this request. + // + + return MapError(deviceExtension, + Srb); + } + } + + // + // If a tape drive has doesn't have DSC set and the last command is restrictive, don't send + // the next command. See discussion of Restrictive Delayed Process commands in QIC-157. + // + + if ((!(statusByte & IDE_STATUS_DSC)) && + (flags & DFLAGS_TAPE_DEVICE) && deviceExtension->RDP) { + ScsiPortStallExecution(1000); + DebugPrint((2,"AtapiSendCommand: DSC not set. %x\n",statusByte)); + return SRB_STATUS_BUSY; + } + + if (IS_RDP(Srb->Cdb[0])) { + + deviceExtension->RDP = TRUE; + + DebugPrint((3, + "AtapiSendCommand: %x mapped as DSC restrictive\n", + Srb->Cdb[0])); + + } else { + + deviceExtension->RDP = FALSE; + } + + if (statusByte & IDE_STATUS_DRQ) { + + DebugPrint((1, + "AtapiSendCommand: Entered with status (%x). Attempting to recover.\n", + statusByte)); + // + // Try to drain the data that one preliminary device thinks that it has + // to transfer. Hopefully this random assertion of DRQ will not be present + // in production devices. + // + + for (i = 0; i < 0x10000; i++) { + + GetStatus(baseIoAddress2, statusByte); + + if (statusByte & IDE_STATUS_DRQ) { + + ScsiPortReadPortUshort(&baseIoAddress1->Data); + + } else { + + break; + } + } + + if (i == 0x10000) { + + DebugPrint((1, + "AtapiSendCommand: DRQ still asserted.Status (%x)\n", + statusByte)); + + AtapiSoftReset(baseIoAddress1,Srb->TargetId); + + DebugPrint((1, + "AtapiSendCommand: Issued soft reset to Atapi device. \n")); + + // + // Re-initialize Atapi device. + // + + IssueIdentify(HwDeviceExtension, + (Srb->TargetId & 0x1), + (Srb->TargetId >> 1), + IDE_COMMAND_ATAPI_IDENTIFY); + + // + // Inform the port driver that the bus has been reset. + // + + ScsiPortNotification(ResetDetected, HwDeviceExtension, 0); + + // + // Clean up device extension fields that AtapiStartIo won't. + // + + deviceExtension->ExpectingInterrupt = FALSE; + deviceExtension->RDP = FALSE; + + return SRB_STATUS_BUS_RESET; + + } + } + + if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) { + + // + // As the cdrom driver sets the LUN field in the cdb, it must be removed. + // + + Srb->Cdb[1] &= ~0xE0; + + if ((Srb->Cdb[0] == SCSIOP_TEST_UNIT_READY) && (flags & DFLAGS_SANYO_ATAPI_CHANGER)) { + + // + // Torisan changer. TUR's are overloaded to be platter switches. + // + + Srb->Cdb[7] = Srb->Lun; + + } + } + + // + // Convert SCSI to ATAPI commands if needed + // + + switch (Srb->Cdb[0]) { + case SCSIOP_MODE_SENSE: + case SCSIOP_MODE_SELECT: + case SCSIOP_FORMAT_UNIT: + if (!(flags & DFLAGS_TAPE_DEVICE)) { + Scsi2Atapi(Srb); + } + + break; + } + + // + // Set data buffer pointer and words left. + // + + deviceExtension->DataBuffer = (PUSHORT)Srb->DataBuffer; + deviceExtension->WordsLeft = Srb->DataTransferLength / 2; + + WaitOnBusy(baseIoAddress2,statusByte); + + // + // Write transfer byte count to registers. + // + + byteCountLow = (UCHAR)(Srb->DataTransferLength & 0xFF); + byteCountHigh = (UCHAR)(Srb->DataTransferLength >> 8); + + if (Srb->DataTransferLength >= 0x10000) { + byteCountLow = byteCountHigh = 0xFF; + } + + ScsiPortWritePortUchar(&baseIoAddress1->ByteCountLow,byteCountLow); + ScsiPortWritePortUchar(&baseIoAddress1->ByteCountHigh, byteCountHigh); + + ScsiPortWritePortUchar((PUCHAR)baseIoAddress1 + 1,0); + + + if (flags & DFLAGS_INT_DRQ) { + + // + // This device interrupts when ready to receive the packet. + // + // Write ATAPI packet command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_ATAPI_PACKET); + + DebugPrint((3, + "AtapiSendCommand: Wait for int. to send packet. Status (%x)\n", + statusByte)); + + deviceExtension->ExpectingInterrupt = TRUE; + + return SRB_STATUS_PENDING; + + } else { + + // + // Write ATAPI packet command. + // + + ScsiPortWritePortUchar(&baseIoAddress1->Command, + IDE_COMMAND_ATAPI_PACKET); + + // + // Wait for DRQ. + // + + WaitOnBusy(baseIoAddress2, statusByte); + WaitForDrq(baseIoAddress2, statusByte); + + if (!(statusByte & IDE_STATUS_DRQ)) { + + DebugPrint((1, + "AtapiSendCommand: DRQ never asserted (%x)\n", + statusByte)); + return SRB_STATUS_ERROR; + } + } + + // + // Need to read status register. + // + + GetBaseStatus(baseIoAddress1, statusByte); + + // + // Send CDB to device. + // + + WaitOnBusy(baseIoAddress2,statusByte); + + WriteBuffer(baseIoAddress1, + (PUSHORT)Srb->Cdb, + 6); + + // + // Indicate expecting an interrupt and wait for it. + // + + deviceExtension->ExpectingInterrupt = TRUE; + + return SRB_STATUS_PENDING; + +} // end AtapiSendCommand() + +ULONG +STDCALL +IdeSendCommand( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Program ATA registers for IDE disk transfer. + +Arguments: + + HwDeviceExtension - ATAPI driver storage. + Srb - System request block. + +Return Value: + + SRB status (pending if all goes well). + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress1 = deviceExtension->BaseIoAddress1[Srb->TargetId >> 1]; + PIDE_REGISTERS_2 baseIoAddress2 = deviceExtension->BaseIoAddress2[Srb->TargetId >> 1]; + PCDB cdb; + + UCHAR statusByte,errorByte; + ULONG status; + ULONG i; + PMODE_PARAMETER_HEADER modeData; + + DebugPrint((2, + "IdeSendCommand: Command %x to device %d\n", + Srb->Cdb[0], + Srb->TargetId)); + + + + switch (Srb->Cdb[0]) { + case SCSIOP_INQUIRY: + + // + // Filter out all TIDs but 0 and 1 since this is an IDE interface + // which support up to two devices. + // + + if ((Srb->Lun != 0) || + (!deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT)) { + + // + // Indicate no device found at this address. + // + + status = SRB_STATUS_SELECTION_TIMEOUT; + break; + + } else { + + PINQUIRYDATA inquiryData = Srb->DataBuffer; + PIDENTIFY_DATA2 identifyData = &deviceExtension->IdentifyData[Srb->TargetId]; + + // + // Zero INQUIRY data structure. + // + + for (i = 0; i < Srb->DataTransferLength; i++) { + ((PUCHAR)Srb->DataBuffer)[i] = 0; + } + + // + // Standard IDE interface only supports disks. + // + + inquiryData->DeviceType = DIRECT_ACCESS_DEVICE; + + // + // Set the removable bit, if applicable. + // + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_REMOVABLE_DRIVE) { + inquiryData->RemovableMedia = 1; + } + + // + // Fill in vendor identification fields. + // + + for (i = 0; i < 20; i += 2) { + inquiryData->VendorId[i] = + ((PUCHAR)identifyData->ModelNumber)[i + 1]; + inquiryData->VendorId[i+1] = + ((PUCHAR)identifyData->ModelNumber)[i]; + } + + // + // Initialize unused portion of product id. + // + + for (i = 0; i < 4; i++) { + inquiryData->ProductId[12+i] = ' '; + } + + // + // Move firmware revision from IDENTIFY data to + // product revision in INQUIRY data. + // + + for (i = 0; i < 4; i += 2) { + inquiryData->ProductRevisionLevel[i] = + ((PUCHAR)identifyData->FirmwareRevision)[i+1]; + inquiryData->ProductRevisionLevel[i+1] = + ((PUCHAR)identifyData->FirmwareRevision)[i]; + } + + status = SRB_STATUS_SUCCESS; + } + + break; + + case SCSIOP_MODE_SENSE: + + // + // This is used to determine of the media is write-protected. + // Since IDE does not support mode sense then we will modify just the portion we need + // so the higher level driver can determine if media is protected. + // + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED) { + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar(&baseIoAddress1->Command,IDE_COMMAND_GET_MEDIA_STATUS); + WaitOnBusy(baseIoAddress2,statusByte); + + if (!(statusByte & IDE_STATUS_ERROR)){ + + // + // no error occured return success, media is not protected + // + + deviceExtension->ExpectingInterrupt = FALSE; + status = SRB_STATUS_SUCCESS; + + } else { + + // + // error occured, handle it locally, clear interrupt + // + + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + + GetBaseStatus(baseIoAddress1, statusByte); + deviceExtension->ExpectingInterrupt = FALSE; + status = SRB_STATUS_SUCCESS; + + if (errorByte & IDE_ERROR_DATA_ERROR) { + + // + //media is write-protected, set bit in mode sense buffer + // + + modeData = (PMODE_PARAMETER_HEADER)Srb->DataBuffer; + + Srb->DataTransferLength = sizeof(MODE_PARAMETER_HEADER); + modeData->DeviceSpecificParameter |= MODE_DSP_WRITE_PROTECT; + } + } + status = SRB_STATUS_SUCCESS; + } else { + status = SRB_STATUS_INVALID_REQUEST; + } + break; + + case SCSIOP_TEST_UNIT_READY: + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED) { + + // + // Select device 0 or 1. + // + + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar(&baseIoAddress1->Command,IDE_COMMAND_GET_MEDIA_STATUS); + + // + // Wait for busy. If media has not changed, return success + // + + WaitOnBusy(baseIoAddress2,statusByte); + + if (!(statusByte & IDE_STATUS_ERROR)){ + deviceExtension->ExpectingInterrupt = FALSE; + status = SRB_STATUS_SUCCESS; + } else { + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress1 + 1); + if (errorByte == IDE_ERROR_DATA_ERROR){ + + // + // Special case: If current media is write-protected, + // the 0xDA command will always fail since the write-protect bit + // is sticky,so we can ignore this error + // + + GetBaseStatus(baseIoAddress1, statusByte); + deviceExtension->ExpectingInterrupt = FALSE; + status = SRB_STATUS_SUCCESS; + + } else { + + // + // Request sense buffer to be build + // + deviceExtension->ExpectingInterrupt = TRUE; + status = SRB_STATUS_PENDING; + } + } + } else { + status = SRB_STATUS_SUCCESS; + } + + break; + + case SCSIOP_READ_CAPACITY: + + // + // Claim 512 byte blocks (big-endian). + // + + ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock = 0x20000; + + // + // Calculate last sector. + // + + + i = (deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads * + deviceExtension->IdentifyData[Srb->TargetId].NumberOfCylinders * + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack) - 1; + + ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress = + (((PUCHAR)&i)[0] << 24) | (((PUCHAR)&i)[1] << 16) | + (((PUCHAR)&i)[2] << 8) | ((PUCHAR)&i)[3]; + + DebugPrint((1, + "IDE disk %x - #sectors %x, #heads %x, #cylinders %x\n", + Srb->TargetId, + deviceExtension->IdentifyData[Srb->TargetId].SectorsPerTrack, + deviceExtension->IdentifyData[Srb->TargetId].NumberOfHeads, + deviceExtension->IdentifyData[Srb->TargetId].NumberOfCylinders)); + + + status = SRB_STATUS_SUCCESS; + break; + + case SCSIOP_VERIFY: + status = IdeVerify(HwDeviceExtension,Srb); + + break; + + case SCSIOP_READ: + case SCSIOP_WRITE: + + status = IdeReadWrite(HwDeviceExtension, + Srb); + break; + + case SCSIOP_START_STOP_UNIT: + + // + //Determine what type of operation we should perform + // + cdb = (PCDB)Srb->Cdb; + + if (cdb->START_STOP.LoadEject == 1){ + + // + // Eject media, + // first select device 0 or 1. + // + ScsiPortWritePortUchar(&baseIoAddress1->DriveSelect, + (UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0)); + ScsiPortWritePortUchar(&baseIoAddress1->Command,IDE_COMMAND_MEDIA_EJECT); + } + status = SRB_STATUS_SUCCESS; + break; + + case SCSIOP_REQUEST_SENSE: + // this function makes sense buffers to report the results + // of the original GET_MEDIA_STATUS command + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_MEDIA_STATUS_ENABLED) { + status = IdeBuildSenseBuffer(HwDeviceExtension,Srb); + break; + } + + default: + + DebugPrint((1, + "IdeSendCommand: Unsupported command %x\n", + Srb->Cdb[0])); + + status = SRB_STATUS_INVALID_REQUEST; + + } // end switch + + return status; + +} // end IdeSendCommand() + +VOID +STDCALL +IdeMediaStatus( + BOOLEAN EnableMSN, + IN PVOID HwDeviceExtension, + ULONG Channel + ) +/*++ + +Routine Description: + + Enables disables media status notification + +Arguments: + +HwDeviceExtension - ATAPI driver storage. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PIDE_REGISTERS_1 baseIoAddress = deviceExtension->BaseIoAddress1[Channel >> 1]; + UCHAR statusByte,errorByte; + + + if (EnableMSN == TRUE){ + + // + // If supported enable Media Status Notification support + // + + if ((deviceExtension->DeviceFlags[Channel] & DFLAGS_REMOVABLE_DRIVE)) { + + // + // enable + // + ScsiPortWritePortUchar((PUCHAR)baseIoAddress + 1,(UCHAR) (0x95)); + ScsiPortWritePortUchar(&baseIoAddress->Command, + IDE_COMMAND_ENABLE_MEDIA_STATUS); + + WaitOnBaseBusy(baseIoAddress,statusByte); + + if (statusByte & IDE_STATUS_ERROR) { + // + // Read the error register. + // + errorByte = ScsiPortReadPortUchar((PUCHAR)baseIoAddress + 1); + + DebugPrint((1, + "IdeMediaStatus: Error enabling media status. Status %x, error byte %x\n", + statusByte, + errorByte)); + } else { + deviceExtension->DeviceFlags[Channel] |= DFLAGS_MEDIA_STATUS_ENABLED; + DebugPrint((1,"IdeMediaStatus: Media Status Notification Supported\n")); + deviceExtension->ReturningMediaStatus = 0; + + } + + } + } else { // end if EnableMSN == TRUE + + // + // disable if previously enabled + // + if ((deviceExtension->DeviceFlags[Channel] & DFLAGS_MEDIA_STATUS_ENABLED)) { + + ScsiPortWritePortUchar((PUCHAR)baseIoAddress + 1,(UCHAR) (0x31)); + ScsiPortWritePortUchar(&baseIoAddress->Command, + IDE_COMMAND_ENABLE_MEDIA_STATUS); + + WaitOnBaseBusy(baseIoAddress,statusByte); + deviceExtension->DeviceFlags[Channel] &= ~DFLAGS_MEDIA_STATUS_ENABLED; + } + + + } + + + +} + +ULONG +STDCALL +IdeBuildSenseBuffer( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Builts an artificial sense buffer to report the results of a GET_MEDIA_STATUS + command. This function is invoked to satisfy the SCSIOP_REQUEST_SENSE. +Arguments: + + HwDeviceExtension - ATAPI driver storage. + Srb - System request block. + +Return Value: + + SRB status (ALWAYS SUCCESS). + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG status; + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->DataBuffer; + + + if (senseBuffer){ + + + if(deviceExtension->ReturningMediaStatus & IDE_ERROR_MEDIA_CHANGE) { + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; + senseBuffer->AdditionalSenseCodeQualifier = 0; + } else if(deviceExtension->ReturningMediaStatus & IDE_ERROR_MEDIA_CHANGE_REQ) { + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; + senseBuffer->AdditionalSenseCodeQualifier = 0; + } else if(deviceExtension->ReturningMediaStatus & IDE_ERROR_END_OF_MEDIA) { + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_NOT_READY; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE; + senseBuffer->AdditionalSenseCodeQualifier = 0; + } else if(deviceExtension->ReturningMediaStatus & IDE_ERROR_DATA_ERROR) { + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xb; + senseBuffer->SenseKey = SCSI_SENSE_DATA_PROTECT; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + } + return SRB_STATUS_SUCCESS; + } + return SRB_STATUS_ERROR; + +}// End of IdeBuildSenseBuffer + + + + +BOOLEAN +STDCALL +AtapiStartIo( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine is called from the SCSI port driver synchronized + with the kernel to start an IO request. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + TRUE + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG status; + + // + // Determine which function. + // + + switch (Srb->Function) { + + case SRB_FUNCTION_EXECUTE_SCSI: + + // + // Sanity check. Only one request can be outstanding on a + // controller. + // + + if (deviceExtension->CurrentSrb) { + + DebugPrint((1, + "AtapiStartIo: Already have a request!\n")); + Srb->SrbStatus = SRB_STATUS_BUSY; + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + return FALSE; + } + + // + // Indicate that a request is active on the controller. + // + + deviceExtension->CurrentSrb = Srb; + + // + // Send command to device. + // + + if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE) { + + status = AtapiSendCommand(HwDeviceExtension, + Srb); + + } else if (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT) { + + status = IdeSendCommand(HwDeviceExtension, + Srb); + } else { + + status = SRB_STATUS_SELECTION_TIMEOUT; + } + + break; + + case SRB_FUNCTION_ABORT_COMMAND: + + // + // Verify that SRB to abort is still outstanding. + // + + if (!deviceExtension->CurrentSrb) { + + DebugPrint((1, "AtapiStartIo: SRB to abort already completed\n")); + + // + // Complete abort SRB. + // + + status = SRB_STATUS_ABORT_FAILED; + + break; + } + + // + // Abort function indicates that a request timed out. + // Call reset routine. Card will only be reset if + // status indicates something is wrong. + // Fall through to reset code. + // + + case SRB_FUNCTION_RESET_BUS: + + // + // Reset Atapi and SCSI bus. + // + + DebugPrint((1, "AtapiStartIo: Reset bus request received\n")); + + if (!AtapiResetController(deviceExtension, + Srb->PathId)) { + + DebugPrint((1,"AtapiStartIo: Reset bus failed\n")); + + // + // Log reset failure. + // + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + 0, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 5 << 8 + ); + + status = SRB_STATUS_ERROR; + + } else { + + status = SRB_STATUS_SUCCESS; + } + + break; + + case SRB_FUNCTION_IO_CONTROL: + + if (deviceExtension->CurrentSrb) { + + DebugPrint((1, + "AtapiStartIo: Already have a request!\n")); + Srb->SrbStatus = SRB_STATUS_BUSY; + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + return FALSE; + } + + // + // Indicate that a request is active on the controller. + // + + deviceExtension->CurrentSrb = Srb; + + if (AtapiStringCmp( ((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature,"SCSIDISK",strlen("SCSIDISK"))) { + + DebugPrint((1, + "AtapiStartIo: IoControl signature incorrect. Send %s, expected %s\n", + ((PSRB_IO_CONTROL)(Srb->DataBuffer))->Signature, + "SCSIDISK")); + + status = SRB_STATUS_INVALID_REQUEST; + break; + } + + switch (((PSRB_IO_CONTROL)(Srb->DataBuffer))->ControlCode) { + + case IOCTL_SCSI_MINIPORT_SMART_VERSION: { + + PGETVERSIONINPARAMS versionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + UCHAR deviceNumber; + + // + // Version and revision per SMART 1.03 + // + + versionParameters->bVersion = 1; + versionParameters->bRevision = 1; + versionParameters->bReserved = 0; + + // + // Indicate that support for IDE IDENTIFY, ATAPI IDENTIFY and SMART commands. + // + + versionParameters->fCapabilities = (CAP_ATA_ID_CMD | CAP_ATAPI_ID_CMD | CAP_SMART_CMD); + + // + // This is done because of how the IOCTL_SCSI_MINIPORT + // determines 'targetid's'. Disk.sys places the real target id value + // in the DeviceMap field. Once we do some parameter checking, the value passed + // back to the application will be determined. + // + + deviceNumber = versionParameters->bIDEDeviceMap; + + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT) || + (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE)) { + + status = SRB_STATUS_SELECTION_TIMEOUT; + break; + } + + // + // NOTE: This will only set the bit + // corresponding to this drive's target id. + // The bit mask is as follows: + // + // Sec Pri + // S M S M + // 3 2 1 0 + // + + if (deviceExtension->NumberChannels == 1) { + if (deviceExtension->PrimaryAddress) { + deviceNumber = 1 << Srb->TargetId; + } else { + deviceNumber = 4 << Srb->TargetId; + } + } else { + deviceNumber = 1 << Srb->TargetId; + } + + versionParameters->bIDEDeviceMap = deviceNumber; + + status = SRB_STATUS_SUCCESS; + break; + } + + case IOCTL_SCSI_MINIPORT_IDENTIFY: { + + PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + SENDCMDINPARAMS cmdInParameters = *(PSENDCMDINPARAMS)(((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL)); + ULONG i; + UCHAR targetId; + + + if (cmdInParameters.irDriveRegs.bCommandReg == ID_CMD) { + + // + // Extract the target. + // + + targetId = cmdInParameters.bDriveNumber; + + if (!(deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_DEVICE_PRESENT) || + (deviceExtension->DeviceFlags[Srb->TargetId] & DFLAGS_ATAPI_DEVICE)) { + + status = SRB_STATUS_SELECTION_TIMEOUT; + break; + } + + // + // Zero the output buffer + // + + for (i = 0; i < (sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1); i++) { + ((PUCHAR)cmdOutParameters)[i] = 0; + } + + // + // Build status block. + // + + cmdOutParameters->cBufferSize = IDENTIFY_BUFFER_SIZE; + cmdOutParameters->DriverStatus.bDriverError = 0; + cmdOutParameters->DriverStatus.bIDEError = 0; + + // + // Extract the identify data from the device extension. + // + + ScsiPortMoveMemory (cmdOutParameters->bBuffer, &deviceExtension->IdentifyData[targetId], IDENTIFY_DATA_SIZE); + + status = SRB_STATUS_SUCCESS; + + + } else { + status = SRB_STATUS_INVALID_REQUEST; + } + break; + } + + case IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS: + case IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS: + case IOCTL_SCSI_MINIPORT_ENABLE_SMART: + case IOCTL_SCSI_MINIPORT_DISABLE_SMART: + case IOCTL_SCSI_MINIPORT_RETURN_STATUS: + case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE: + case IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES: + case IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS: + + status = IdeSendSmartCommand(HwDeviceExtension,Srb); + break; + + default : + + status = SRB_STATUS_INVALID_REQUEST; + break; + + } + + break; + + default: + + // + // Indicate unsupported command. + // + + status = SRB_STATUS_INVALID_REQUEST; + + break; + + } // end switch + + // + // Check if command complete. + // + + if (status != SRB_STATUS_PENDING) { + + DebugPrint((2, + "AtapiStartIo: Srb %x complete with status %x\n", + Srb, + status)); + + // + // Clear current SRB. + // + + deviceExtension->CurrentSrb = NULL; + + // + // Set status in SRB. + // + + Srb->SrbStatus = (UCHAR)status; + + // + // Indicate command complete. + // + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + + // + // Indicate ready for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + } + + return TRUE; + +} // end AtapiStartIo() + + +ULONG +STDCALL +DriverEntry( + IN PVOID DriverObject, + IN PVOID Argument2 + ) + +/*++ + +Routine Description: + + Installable driver initialization entry point for system. + +Arguments: + + Driver Object + +Return Value: + + Status from ScsiPortInitialize() + +--*/ + +{ + HW_INITIALIZATION_DATA hwInitializationData; + ULONG adapterCount; + ULONG i; + ULONG statusToReturn, newStatus; + + DebugPrint((1,"\n\nATAPI IDE MiniPort Driver\n")); + + statusToReturn = 0xffffffff; + + // + // Zero out structure. + // + + AtapiZeroMemory(((PUCHAR)&hwInitializationData), sizeof(HW_INITIALIZATION_DATA)); + + // + // Set size of hwInitializationData. + // + + hwInitializationData.HwInitializationDataSize = + sizeof(HW_INITIALIZATION_DATA); + + // + // Set entry points. + // + + hwInitializationData.HwInitialize = AtapiHwInitialize; + hwInitializationData.HwResetBus = AtapiResetController; + hwInitializationData.HwStartIo = AtapiStartIo; + hwInitializationData.HwInterrupt = AtapiInterrupt; + + // + // Specify size of extensions. + // + + hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION); + hwInitializationData.SpecificLuExtensionSize = sizeof(HW_LU_EXTENSION); + + // + // Indicate PIO device. + // + + hwInitializationData.MapBuffers = TRUE; + + // + // Native Mode Devices + // + for (i=0; i ='A' && first<='Z') { + first = first - 'A' + 'a'; + } + if (last>='A' && last<='Z') { + last = last - 'A' + 'a'; + } + if (first != last) { + + // + // No match + // + + return first - last; + } + } + }while (--Count && first); + } + + return 0; +} + + +VOID +STDCALL +AtapiZeroMemory( + IN PCHAR Buffer, + IN ULONG Count + ) +{ + ULONG i; + + for (i = 0; i < Count; i++) { + Buffer[i] = 0; + } +} + + +VOID +STDCALL +AtapiHexToString ( + IN ULONG Value, + IN OUT PCHAR *Buffer + ) +{ + PCHAR string; + PCHAR firstdig; + CHAR temp; + ULONG i; + USHORT digval; + + string = *Buffer; + + firstdig = string; + + for (i = 0; i < 4; i++) { + digval = (USHORT)(Value % 16); + Value /= 16; + + // + // convert to ascii and store. Note this will create + // the buffer with the digits reversed. + // + + if (digval > 9) { + *string++ = (char) (digval - 10 + 'a'); + } else { + *string++ = (char) (digval + '0'); + } + + } + + // + // Reverse the digits. + // + + *string-- = '\0'; + + do { + temp = *string; + *string = *firstdig; + *firstdig = temp; + --string; + ++firstdig; + } while (firstdig < string); +} + + + +PSCSI_REQUEST_BLOCK +STDCALL +BuildMechanismStatusSrb ( + IN PVOID HwDeviceExtension, + IN ULONG PathId, + IN ULONG TargetId + ) +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + + srb = &deviceExtension->InternalSrb; + + AtapiZeroMemory((PUCHAR) srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb->PathId = (UCHAR) PathId; + srb->TargetId = (UCHAR) TargetId; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + // + // Set flags to disable synchronous negociation. + // + srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Set timeout to 2 seconds. + // + srb->TimeOutValue = 4; + + srb->CdbLength = 6; + srb->DataBuffer = &deviceExtension->MechStatusData; + srb->DataTransferLength = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER); + + // + // Set CDB operation code. + // + cdb = (PCDB)srb->Cdb; + cdb->MECH_STATUS.OperationCode = SCSIOP_MECHANISM_STATUS; + cdb->MECH_STATUS.AllocationLength[1] = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER); + + return srb; +} + + +PSCSI_REQUEST_BLOCK +STDCALL +BuildRequestSenseSrb ( + IN PVOID HwDeviceExtension, + IN ULONG PathId, + IN ULONG TargetId + ) +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + + srb = &deviceExtension->InternalSrb; + + AtapiZeroMemory((PUCHAR) srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb->PathId = (UCHAR) PathId; + srb->TargetId = (UCHAR) TargetId; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + // + // Set flags to disable synchronous negociation. + // + srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Set timeout to 2 seconds. + // + srb->TimeOutValue = 4; + + srb->CdbLength = 6; + srb->DataBuffer = &deviceExtension->MechStatusSense; + srb->DataTransferLength = sizeof(SENSE_DATA); + + // + // Set CDB operation code. + // + cdb = (PCDB)srb->Cdb; + cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE; + cdb->CDB6INQUIRY.AllocationLength = sizeof(SENSE_DATA); + + return srb; +} + + + + + diff --git a/reactos/drivers/storage/atapi/atapi.h b/reactos/drivers/storage/atapi/atapi.h index 4b203b505a6..148c94fdd02 100644 --- a/reactos/drivers/storage/atapi/atapi.h +++ b/reactos/drivers/storage/atapi/atapi.h @@ -1,267 +1,459 @@ -// -// ATAPI.H - defines and typedefs for the IDE Driver module. -// +/* + * PROJECT: ReactOS Storage Stack + * LICENSE: DDK - see license.txt in the root dir + * FILE: drivers/storage/atapi/atapi.h + * PURPOSE: ATAPI IDE miniport driver + * PROGRAMMERS: Based on a source code sample from Microsoft NT4 DDK + */ -#ifndef __ATAPI_H -#define __ATAPI_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define IDE_MAXIMUM_DEVICES 8 - -#define IDE_MAX_NAME_LENGTH 50 - -#define IDE_SECTOR_BUF_SZ 512 -#define IDE_MAX_SECTORS_PER_XFER 256 -#define IDE_MAX_RESET_RETRIES 10000 -#define IDE_MAX_POLL_RETRIES 100000 -#define IDE_MAX_WRITE_RETRIES 1000 -#define IDE_MAX_BUSY_RETRIES 50000 -#define IDE_MAX_DRQ_RETRIES 10000 -//#define IDE_MAX_CMD_RETRIES 1 -#define IDE_MAX_CMD_RETRIES 0 -#define IDE_CMD_TIMEOUT 5 -#define IDE_RESET_PULSE_LENGTH 500 /* maybe a little too long */ -#define IDE_RESET_BUSY_TIMEOUT 120 -#define IDE_RESET_DRDY_TIMEOUT 120 - -// Control Block offsets and masks -#define IDE_REG_ALT_STATUS 0x0000 -#define IDE_REG_DEV_CNTRL 0x0000 /* device control register */ -#define IDE_DC_SRST 0x04 /* drive reset (both drives) */ -#define IDE_DC_nIEN 0x02 /* IRQ enable (active low) */ -#define IDE_REG_DRV_ADDR 0x0001 - -// Command Block offsets and masks -#define IDE_REG_DATA_PORT 0x0000 -#define IDE_REG_ERROR 0x0001 /* error register */ -#define IDE_ER_AMNF 0x01 /* addr mark not found */ -#define IDE_ER_TK0NF 0x02 /* track 0 not found */ -#define IDE_ER_ABRT 0x04 /* command aborted */ -#define IDE_ER_MCR 0x08 /* media change requested */ -#define IDE_ER_IDNF 0x10 /* ID not found */ -#define IDE_ER_MC 0x20 /* Media changed */ -#define IDE_ER_UNC 0x40 /* Uncorrectable data error */ -#define IDE_REG_PRECOMP 0x0001 -#define IDE_REG_SECTOR_CNT 0x0002 -#define IDE_REG_SECTOR_NUM 0x0003 -#define IDE_REG_CYL_LOW 0x0004 -#define IDE_REG_CYL_HIGH 0x0005 -#define IDE_REG_DRV_HEAD 0x0006 -#define IDE_DH_FIXED 0xA0 -#define IDE_DH_LBA 0x40 -#define IDE_DH_HDMASK 0x0F -#define IDE_DH_DRV0 0x00 -#define IDE_DH_DRV1 0x10 -#define IDE_REG_STATUS 0x0007 -#define IDE_SR_BUSY 0x80 -#define IDE_SR_DRDY 0x40 -#define IDE_SR_WERR 0x20 -#define IDE_SR_DRQ 0x08 -#define IDE_SR_ERR 0x01 -#define IDE_REG_COMMAND 0x0007 - -/* IDE/ATA commands */ -#define IDE_CMD_RESET 0x08 -#define IDE_CMD_READ 0x20 -#define IDE_CMD_READ_ONCE 0x21 -#define IDE_CMD_READ_EXT 0x24 /* 48 bit */ -#define IDE_CMD_READ_DMA_EXT 0x25 /* 48 bit */ -#define IDE_CMD_READ_MULTIPLE_EXT 0x29 /* 48 bit */ -#define IDE_CMD_WRITE 0x30 -#define IDE_CMD_WRITE_ONCE 0x31 -#define IDE_CMD_WRITE_EXT 0x34 /* 48 bit */ -#define IDE_CMD_WRITE_DMA_EXT 0x35 /* 48 bit */ -#define IDE_CMD_WRITE_MULTIPLE_EXT 0x39 /* 48 bit */ -#define IDE_CMD_PACKET 0xA0 -#define IDE_CMD_READ_MULTIPLE 0xC4 -#define IDE_CMD_WRITE_MULTIPLE 0xC5 -#define IDE_CMD_READ_DMA 0xC8 -#define IDE_CMD_WRITE_DMA 0xCA -#define IDE_CMD_FLUSH_CACHE 0xE7 -#define IDE_CMD_FLUSH_CACHE_EXT 0xEA /* 48 bit */ -#define IDE_CMD_IDENT_ATA_DRV 0xEC -#define IDE_CMD_IDENT_ATAPI_DRV 0xA1 -#define IDE_CMD_GET_MEDIA_STATUS 0xDA +#include +#include // -// Access macros for command registers -// Each macro takes an address of the command port block, and data +// IDE register definition // -#define IDEReadError(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + IDE_REG_ERROR))) -#define IDEWritePrecomp(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + IDE_REG_PRECOMP), (Data))) -#define IDEReadSectorCount(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + IDE_REG_SECTOR_CNT))) -#define IDEWriteSectorCount(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + IDE_REG_SECTOR_CNT), (Data))) -#define IDEReadSectorNum(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + IDE_REG_SECTOR_NUM))) -#define IDEWriteSectorNum(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + IDE_REG_SECTOR_NUM), (Data))) -#define IDEReadCylinderLow(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + IDE_REG_CYL_LOW))) -#define IDEWriteCylinderLow(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + IDE_REG_CYL_LOW), (Data))) -#define IDEReadCylinderHigh(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + IDE_REG_CYL_HIGH))) -#define IDEWriteCylinderHigh(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + IDE_REG_CYL_HIGH), (Data))) -#define IDEReadDriveHead(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + IDE_REG_DRV_HEAD))) -#define IDEWriteDriveHead(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + IDE_REG_DRV_HEAD), (Data))) -#define IDEReadStatus(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + IDE_REG_STATUS))) -#define IDEWriteCommand(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + IDE_REG_COMMAND), (Data))) -#define IDEReadDMACommand(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address)))) -#define IDEWriteDMACommand(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address)), (Data))) -#define IDEReadDMAStatus(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + 2))) -#define IDEWriteDMAStatus(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + 2), (Data))) -#define IDEWritePRDTable(Address, Data) \ - (ScsiPortWritePortUlong((PULONG)((Address) + 4), (Data))) +typedef struct _IDE_REGISTERS_1 { + USHORT Data; + UCHAR BlockCount; + UCHAR BlockNumber; + UCHAR CylinderLow; + UCHAR CylinderHigh; + UCHAR DriveSelect; + UCHAR Command; +} IDE_REGISTERS_1, *PIDE_REGISTERS_1; + +typedef struct _IDE_REGISTERS_2 { + UCHAR AlternateStatus; + UCHAR DriveAddress; +} IDE_REGISTERS_2, *PIDE_REGISTERS_2; + +typedef struct _IDE_REGISTERS_3 { + ULONG Data; + UCHAR Others[4]; +} IDE_REGISTERS_3, *PIDE_REGISTERS_3; // -// Data block read and write commands +// Device Extension Device Flags // -#define IDEReadBlock(Address, Buffer, Count) \ - (ScsiPortReadPortBufferUshort((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2)) -#define IDEWriteBlock(Address, Buffer, Count) \ - (ScsiPortWritePortBufferUshort((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2)) -#define IDEReadBlock32(Address, Buffer, Count) \ - (ScsiPortReadPortBufferUlong((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4)) -#define IDEWriteBlock32(Address, Buffer, Count) \ - (ScsiPortWritePortBufferUlong((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4)) +#define DFLAGS_DEVICE_PRESENT 0x0001 // Indicates that some device is present. +#define DFLAGS_ATAPI_DEVICE 0x0002 // Indicates whether Atapi commands can be used. +#define DFLAGS_TAPE_DEVICE 0x0004 // Indicates whether this is a tape device. +#define DFLAGS_INT_DRQ 0x0008 // Indicates whether device interrupts as DRQ is set after + // receiving Atapi Packet Command +#define DFLAGS_REMOVABLE_DRIVE 0x0010 // Indicates that the drive has the 'removable' bit set in + // identify data (offset 128) +#define DFLAGS_MEDIA_STATUS_ENABLED 0x0020 // Media status notification enabled +#define DFLAGS_ATAPI_CHANGER 0x0040 // Indicates atapi 2.5 changer present. +#define DFLAGS_SANYO_ATAPI_CHANGER 0x0080 // Indicates multi-platter device, not conforming to the 2.5 spec. +#define DFLAGS_CHANGER_INITED 0x0100 // Indicates that the init path for changers has already been done. +// +// Used to disable 'advanced' features. +// -#define IDEReadWord(Address) \ - (ScsiPortReadPortUshort((PUSHORT)((Address) + IDE_REG_DATA_PORT))) +#define MAX_ERRORS 4 // -// Access macros for control registers -// Each macro takes an address of the control port blank and data +// ATAPI command definitions // -#define IDEReadAltStatus(Address) \ - (ScsiPortReadPortUchar((PUCHAR)((Address) + IDE_REG_ALT_STATUS))) -#define IDEWriteDriveControl(Address, Data) \ - (ScsiPortWritePortUchar((PUCHAR)((Address) + IDE_REG_DEV_CNTRL), (Data))) + +#define ATAPI_MODE_SENSE 0x5A +#define ATAPI_MODE_SELECT 0x55 +#define ATAPI_FORMAT_UNIT 0x24 + +// +// ATAPI Command Descriptor Block +// + +typedef struct _MODE_SENSE_10 { + UCHAR OperationCode; + UCHAR Reserved1; + UCHAR PageCode : 6; + UCHAR Pc : 2; + UCHAR Reserved2[4]; + UCHAR ParameterListLengthMsb; + UCHAR ParameterListLengthLsb; + UCHAR Reserved3[3]; +} MODE_SENSE_10, *PMODE_SENSE_10; + +typedef struct _MODE_SELECT_10 { + UCHAR OperationCode; + UCHAR Reserved1 : 4; + UCHAR PFBit : 1; + UCHAR Reserved2 : 3; + UCHAR Reserved3[5]; + UCHAR ParameterListLengthMsb; + UCHAR ParameterListLengthLsb; + UCHAR Reserved4[3]; +} MODE_SELECT_10, *PMODE_SELECT_10; + +typedef struct _MODE_PARAMETER_HEADER_10 { + UCHAR ModeDataLengthMsb; + UCHAR ModeDataLengthLsb; + UCHAR MediumType; + UCHAR Reserved[5]; +}MODE_PARAMETER_HEADER_10, *PMODE_PARAMETER_HEADER_10; + +// +// IDE command definitions +// + +#define IDE_COMMAND_ATAPI_RESET 0x08 +#define IDE_COMMAND_RECALIBRATE 0x10 +#define IDE_COMMAND_READ 0x20 +#define IDE_COMMAND_WRITE 0x30 +#define IDE_COMMAND_VERIFY 0x40 +#define IDE_COMMAND_SEEK 0x70 +#define IDE_COMMAND_SET_DRIVE_PARAMETERS 0x91 +#define IDE_COMMAND_ATAPI_PACKET 0xA0 +#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1 +#define IDE_COMMAND_READ_MULTIPLE 0xC4 +#define IDE_COMMAND_WRITE_MULTIPLE 0xC5 +#define IDE_COMMAND_SET_MULTIPLE 0xC6 +#define IDE_COMMAND_READ_DMA 0xC8 +#define IDE_COMMAND_WRITE_DMA 0xCA +#define IDE_COMMAND_GET_MEDIA_STATUS 0xDA +#define IDE_COMMAND_ENABLE_MEDIA_STATUS 0xEF +#define IDE_COMMAND_IDENTIFY 0xEC +#define IDE_COMMAND_MEDIA_EJECT 0xED + +// +// IDE status definitions +// + +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_IDLE 0x50 +#define IDE_STATUS_BUSY 0x80 + +// +// IDE drive select/head definitions +// + +#define IDE_DRIVE_SELECT_1 0xA0 +#define IDE_DRIVE_SELECT_2 0x10 + +// +// IDE drive control definitions +// + +#define IDE_DC_DISABLE_INTERRUPTS 0x02 +#define IDE_DC_RESET_CONTROLLER 0x04 +#define IDE_DC_REENABLE_CONTROLLER 0x00 + +// +// IDE error definitions +// + +#define IDE_ERROR_BAD_BLOCK 0x80 +#define IDE_ERROR_DATA_ERROR 0x40 +#define IDE_ERROR_MEDIA_CHANGE 0x20 +#define IDE_ERROR_ID_NOT_FOUND 0x10 +#define IDE_ERROR_MEDIA_CHANGE_REQ 0x08 +#define IDE_ERROR_COMMAND_ABORTED 0x04 +#define IDE_ERROR_END_OF_MEDIA 0x02 +#define IDE_ERROR_ILLEGAL_LENGTH 0x01 + +// +// ATAPI register definition +// + +typedef struct _ATAPI_REGISTERS_1 { + USHORT Data; + UCHAR InterruptReason; + UCHAR Unused1; + UCHAR ByteCountLow; + UCHAR ByteCountHigh; + UCHAR DriveSelect; + UCHAR Command; +} ATAPI_REGISTERS_1, *PATAPI_REGISTERS_1; + +typedef struct _ATAPI_REGISTERS_2 { + UCHAR AlternateStatus; + UCHAR DriveAddress; +} ATAPI_REGISTERS_2, *PATAPI_REGISTERS_2; + +// +// ATAPI interrupt reasons +// + +#define ATAPI_IR_COD 0x01 +#define ATAPI_IR_IO 0x02 + +// +// IDENTIFY data +// + +typedef struct _IDENTIFY_DATA { + USHORT GeneralConfiguration; // 00 00 + USHORT NumberOfCylinders; // 02 1 + USHORT Reserved1; // 04 2 + USHORT NumberOfHeads; // 06 3 + USHORT UnformattedBytesPerTrack; // 08 4 + USHORT UnformattedBytesPerSector; // 0A 5 + USHORT SectorsPerTrack; // 0C 6 + USHORT VendorUnique1[3]; // 0E 7-9 + USHORT SerialNumber[10]; // 14 10-19 + USHORT BufferType; // 28 20 + USHORT BufferSectorSize; // 2A 21 + USHORT NumberOfEccBytes; // 2C 22 + USHORT FirmwareRevision[4]; // 2E 23-26 + USHORT ModelNumber[20]; // 36 27-46 + UCHAR MaximumBlockTransfer; // 5E 47 + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 48 + USHORT Capabilities; // 62 49 + USHORT Reserved2; // 64 50 + UCHAR VendorUnique3; // 66 51 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 52 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A 53 + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C 54 + USHORT NumberOfCurrentHeads; // 6E 55 + USHORT CurrentSectorsPerTrack; // 70 56 + ULONG CurrentSectorCapacity; // 72 57-58 + USHORT CurrentMultiSectorSetting; // 59 + ULONG UserAddressableSectors; // 60-61 + USHORT SingleWordDMASupport : 8; // 62 + USHORT SingleWordDMAActive : 8; + USHORT MultiWordDMASupport : 8; // 63 + USHORT MultiWordDMAActive : 8; + USHORT AdvancedPIOModes : 8; // 64 + USHORT Reserved4 : 8; + USHORT MinimumMWXferCycleTime; // 65 + USHORT RecommendedMWXferCycleTime; // 66 + USHORT MinimumPIOCycleTime; // 67 + USHORT MinimumPIOCycleTimeIORDY; // 68 + USHORT Reserved5[2]; // 69-70 + USHORT ReleaseTimeOverlapped; // 71 + USHORT ReleaseTimeServiceCommand; // 72 + USHORT MajorRevision; // 73 + USHORT MinorRevision; // 74 + USHORT Reserved6[50]; // 75-126 + USHORT SpecialFunctionsEnabled; // 127 + USHORT Reserved7[128]; // 128-255 +} IDENTIFY_DATA, *PIDENTIFY_DATA; + +// +// Identify data without the Reserved4. +// + +typedef struct _IDENTIFY_DATA2 { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 +} IDENTIFY_DATA2, *PIDENTIFY_DATA2; + +#define IDENTIFY_DATA_SIZE sizeof(IDENTIFY_DATA) + +// +// IDENTIFY capability bit definitions. +// + +#define IDENTIFY_CAPABILITIES_DMA_SUPPORTED 0x0100 +#define IDENTIFY_CAPABILITIES_LBA_SUPPORTED 0x0200 + +// +// IDENTIFY DMA timing cycle modes. +// + +#define IDENTIFY_DMA_CYCLES_MODE_0 0x00 +#define IDENTIFY_DMA_CYCLES_MODE_1 0x01 +#define IDENTIFY_DMA_CYCLES_MODE_2 0x02 + + +typedef struct _BROKEN_CONTROLLER_INFORMATION { + PCHAR VendorId; + ULONG VendorIdLength; + PCHAR DeviceId; + ULONG DeviceIdLength; +}BROKEN_CONTROLLER_INFORMATION, *PBROKEN_CONTROLLER_INFORMATION; + +BROKEN_CONTROLLER_INFORMATION const BrokenAdapters[] = { + { "1095", 4, "0640", 4}, + { "1039", 4, "0601", 4} +}; + +#define BROKEN_ADAPTERS (sizeof(BrokenAdapters) / sizeof(BROKEN_CONTROLLER_INFORMATION)) + +typedef struct _NATIVE_MODE_CONTROLLER_INFORMATION { + PCHAR VendorId; + ULONG VendorIdLength; + PCHAR DeviceId; + ULONG DeviceIdLength; +}NATIVE_MODE_CONTROLLER_INFORMATION, *PNATIVE_MODE_CONTROLLER_INFORMATION; + +NATIVE_MODE_CONTROLLER_INFORMATION const NativeModeAdapters[] = { + { "10ad", 4, "0105", 4} +}; +#define NUM_NATIVE_MODE_ADAPTERS (sizeof(NativeModeAdapters) / sizeof(NATIVE_MODE_CONTROLLER_INFORMATION)) + +// +// Beautification macros +// + +#define GetStatus(BaseIoAddress, Status) \ + Status = ScsiPortReadPortUchar(&BaseIoAddress->AlternateStatus); + +#define GetBaseStatus(BaseIoAddress, Status) \ + Status = ScsiPortReadPortUchar(&BaseIoAddress->Command); + +#define WriteCommand(BaseIoAddress, Command) \ + ScsiPortWritePortUchar(&BaseIoAddress->Command, Command); -// IDE_DRIVE_IDENTIFY +#define ReadBuffer(BaseIoAddress, Buffer, Count) \ + ScsiPortReadPortBufferUshort(&BaseIoAddress->Data, \ + Buffer, \ + Count); -typedef struct _IDE_DRIVE_IDENTIFY -{ - USHORT ConfigBits; /*00*/ - USHORT LogicalCyls; /*01*/ - USHORT Reserved02; /*02*/ - USHORT LogicalHeads; /*03*/ - USHORT BytesPerTrack; /*04*/ - USHORT BytesPerSector; /*05*/ - USHORT SectorsPerTrack; /*06*/ - UCHAR InterSectorGap; /*07*/ - UCHAR InterSectorGapSize; - UCHAR Reserved08H; /*08*/ - UCHAR BytesInPLO; - USHORT VendorUniqueCnt; /*09*/ - UCHAR SerialNumber[20]; /*10*/ - USHORT ControllerType; /*20*/ - USHORT BufferSize; /*21*/ - USHORT ECCByteCnt; /*22*/ - UCHAR FirmwareRev[8]; /*23*/ - UCHAR ModelNumber[40]; /*27*/ - USHORT RWMultImplemented; /*47*/ - USHORT DWordIo; /*48*/ - USHORT Capabilities; /*49*/ -#define IDE_DRID_STBY_SUPPORTED 0x2000 -#define IDE_DRID_IORDY_SUPPORTED 0x0800 -#define IDE_DRID_IORDY_DISABLE 0x0400 -#define IDE_DRID_LBA_SUPPORTED 0x0200 -#define IDE_DRID_DMA_SUPPORTED 0x0100 - USHORT Reserved50; /*50*/ - USHORT MinPIOTransTime; /*51*/ - USHORT MinDMATransTime; /*52*/ - USHORT TMFieldsValid; /*53*/ - USHORT TMCylinders; /*54*/ - USHORT TMHeads; /*55*/ - USHORT TMSectorsPerTrk; /*56*/ - USHORT TMCapacityLo; /*57*/ - USHORT TMCapacityHi; /*58*/ - USHORT RWMultCurrent; /*59*/ - USHORT TMSectorCountLo; /*60*/ - USHORT TMSectorCountHi; /*61*/ - USHORT DmaModes; /*62*/ - USHORT MultiDmaModes; /*63*/ - USHORT Reserved64[5]; /*64*/ - USHORT Reserved69[2]; /*69*/ - USHORT Reserved71[4]; /*71*/ - USHORT MaxQueueDepth; /*75*/ - USHORT Reserved76[4]; /*76*/ - USHORT MajorRevision; /*80*/ - USHORT MinorRevision; /*81*/ - USHORT SupportedFeatures82; /*82*/ - USHORT SupportedFeatures83; /*83*/ - USHORT SupportedFeatures84; /*84*/ - USHORT EnabledFeatures85; /*85*/ - USHORT EnabledFeatures86; /*86*/ - USHORT EnabledFeatures87; /*87*/ - USHORT UltraDmaModes; /*88*/ - USHORT Reserved89[11]; /*89*/ - USHORT Max48BitAddress[4]; /*100*/ - USHORT Reserved104[151]; /*104*/ - USHORT Checksum; /*255*/ -} IDE_DRIVE_IDENTIFY, *PIDE_DRIVE_IDENTIFY; +#define WriteBuffer(BaseIoAddress, Buffer, Count) \ + ScsiPortWritePortBufferUshort(&BaseIoAddress->Data, \ + Buffer, \ + Count); +#define ReadBuffer2(BaseIoAddress, Buffer, Count) \ + ScsiPortReadPortBufferUlong(&BaseIoAddress->Data, \ + Buffer, \ + Count); -/* Special ATAPI commands */ +#define WriteBuffer2(BaseIoAddress, Buffer, Count) \ + ScsiPortWritePortBufferUlong(&BaseIoAddress->Data, \ + Buffer, \ + Count); -#define ATAPI_FORMAT_UNIT 0x24 -#define ATAPI_MODE_SELECT 0x55 -#define ATAPI_MODE_SENSE 0x5A - - -/* Special ATAPI_MODE_SELECT (12 bytes) command block */ - -typedef struct _ATAPI_MODE_SELECT12 -{ - UCHAR OperationCode; - UCHAR Reserved1:4; - UCHAR PFBit:1; - UCHAR Reserved2:3; - UCHAR Reserved3[5]; - UCHAR ParameterListLengthMsb; - UCHAR ParameterListLengthLsb; - UCHAR Reserved4[3]; -} ATAPI_MODE_SELECT12, *PATAPI_MODE_SELECT12; - - -/* Special ATAPI_MODE_SENSE (12 bytes) command block */ - -typedef struct _ATAPI_MODE_SENSE12 -{ - UCHAR OperationCode; - UCHAR Reserved1; - UCHAR PageCode:6; - UCHAR Pc:2; - UCHAR Reserved2[4]; - UCHAR ParameterListLengthMsb; - UCHAR ParameterListLengthLsb; - UCHAR Reserved3[3]; -} ATAPI_MODE_SENSE12, *PATAPI_MODE_SENSE12; - -#ifdef __cplusplus +#define WaitOnBusy(BaseIoAddress, Status) \ +{ \ + ULONG i; \ + for (i=0; i<20000; i++) { \ + GetStatus(BaseIoAddress, Status); \ + if (Status & IDE_STATUS_BUSY) { \ + ScsiPortStallExecution(150); \ + continue; \ + } else { \ + break; \ + } \ + } \ } -#endif -#endif /* __ATAPT_H */ +#define WaitOnBaseBusy(BaseIoAddress, Status) \ +{ \ + ULONG i; \ + for (i=0; i<20000; i++) { \ + GetBaseStatus(BaseIoAddress, Status); \ + if (Status & IDE_STATUS_BUSY) { \ + ScsiPortStallExecution(150); \ + continue; \ + } else { \ + break; \ + } \ + } \ +} + +#define WaitForDrq(BaseIoAddress, Status) \ +{ \ + ULONG i; \ + for (i=0; i<1000; i++) { \ + GetStatus(BaseIoAddress, Status); \ + if (Status & IDE_STATUS_BUSY) { \ + ScsiPortStallExecution(100); \ + } else if (Status & IDE_STATUS_DRQ) { \ + break; \ + } else { \ + ScsiPortStallExecution(200); \ + } \ + } \ +} +#define WaitShortForDrq(BaseIoAddress, Status) \ +{ \ + ULONG i; \ + for (i=0; i<2; i++) { \ + GetStatus(BaseIoAddress, Status); \ + if (Status & IDE_STATUS_BUSY) { \ + ScsiPortStallExecution(100); \ + } else if (Status & IDE_STATUS_DRQ) { \ + break; \ + } else { \ + ScsiPortStallExecution(100); \ + } \ + } \ +} + +#define AtapiSoftReset(BaseIoAddress,DeviceNumber) \ +{\ + UCHAR statusByte; \ + ScsiPortWritePortUchar(&BaseIoAddress->DriveSelect,(UCHAR)(((DeviceNumber & 0x1) << 4) | 0xA0)); \ + ScsiPortStallExecution(500);\ + ScsiPortWritePortUchar(&BaseIoAddress->Command, IDE_COMMAND_ATAPI_RESET); \ + ScsiPortStallExecution(1000*1000);\ + ScsiPortWritePortUchar(&BaseIoAddress->DriveSelect,(UCHAR)((DeviceNumber << 4) | 0xA0)); \ + WaitOnBusy( ((PIDE_REGISTERS_2)((PUCHAR)BaseIoAddress + 0x206)), statusByte); \ + ScsiPortStallExecution(500);\ +} + +#define IdeHardReset(BaseIoAddress,result) \ +{\ + UCHAR statusByte;\ + ULONG i;\ + ScsiPortWritePortUchar(&BaseIoAddress->AlternateStatus,IDE_DC_RESET_CONTROLLER );\ + ScsiPortStallExecution(50 * 1000);\ + ScsiPortWritePortUchar(&BaseIoAddress->AlternateStatus,IDE_DC_REENABLE_CONTROLLER);\ + for (i = 0; i < 1000 * 1000; i++) {\ + statusByte = ScsiPortReadPortUchar(&BaseIoAddress->AlternateStatus);\ + if (statusByte != IDE_STATUS_IDLE && statusByte != 0x0) {\ + ScsiPortStallExecution(5);\ + } else {\ + break;\ + }\ + }\ + if (i == 1000*1000) {\ + result = FALSE;\ + }\ + result = TRUE;\ +} + +#define IS_RDP(OperationCode)\ + ((OperationCode == SCSIOP_ERASE)||\ + (OperationCode == SCSIOP_LOAD_UNLOAD)||\ + (OperationCode == SCSIOP_LOCATE)||\ + (OperationCode == SCSIOP_REWIND) ||\ + (OperationCode == SCSIOP_SPACE)||\ + (OperationCode == SCSIOP_SEEK)||\ + (OperationCode == SCSIOP_WRITE_FILEMARKS)) + diff --git a/reactos/drivers/storage/atapi/atapi.rbuild b/reactos/drivers/storage/atapi/atapi.rbuild index bde1e5dc86f..9355a5f8438 100644 --- a/reactos/drivers/storage/atapi/atapi.rbuild +++ b/reactos/drivers/storage/atapi/atapi.rbuild @@ -1,4 +1,4 @@ - + . diff --git a/reactos/drivers/storage/atapi/atapi.rc b/reactos/drivers/storage/atapi/atapi.rc index dae9319aefe..94e9860bbcd 100644 --- a/reactos/drivers/storage/atapi/atapi.rc +++ b/reactos/drivers/storage/atapi/atapi.rc @@ -1,7 +1,7 @@ /* $Id$ */ #define REACTOS_VERSION_DLL -#define REACTOS_STR_FILE_DESCRIPTION "ATAPI Device Driver\0" +#define REACTOS_STR_FILE_DESCRIPTION "ATAPI IDE Miniport Driver\0" #define REACTOS_STR_INTERNAL_NAME "atapi\0" #define REACTOS_STR_ORIGINAL_FILENAME "atapi.sys\0" #include diff --git a/reactos/drivers/storage/atapi/license.txt b/reactos/drivers/storage/atapi/license.txt new file mode 100644 index 00000000000..29389c47231 --- /dev/null +++ b/reactos/drivers/storage/atapi/license.txt @@ -0,0 +1,53 @@ +Window NT Device Driver Kit +END-USER LICENSE AGREEMENT FOR MICROSOFT SOFTWARE + +IMPORTANT-READ CAREFULLY: This Microsoft End-User License Agreement ("EULA") is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation for the Microsoft software product identified above, which includes computer software and associated media and printed materials, and may include "online" or electronic documentation ("SOFTWARE PRODUCT" or "SOFTWARE"). By installing, copying, or otherwise using the SOFTWARE PRODUCT, you agree to be bound by the terms of this EULA. If you do not agree to the terms of this EULA, promptly return the unused SOFTWARE PRODUCT to the place from which you obtained it for a full refund, or if you received the SOFTWARE PRODUCT as part of a subscription or other service from Microsoft, you may cancel the subscription and receive a refund of a prorata portion of the subscription price. + +SOFTWARE PRODUCT LICENSE +The SOFTWARE PRODUCT is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE PRODUCT is licensed, not sold. To develop Windows NT(tm) device drivers, you will need to utilize two computers, one for development and one for debugging. + +1. GRANT OF LICENSE. This EULA grants you the following limited, non-exclusive rights: +* SOFTWARE PRODUCT. (a) You may make two (2) copies of the SOFTWARE PRODUCT for installation and use on two (2) computers, one for use in development and one for use in debugging to design, develop, and test your software product(s), including but not limited to device driver(s) and other software products, for use with Microsoft(r) Windows(r) or Windows NT ("Software Product"). You may make an additional copy of the Windows NT Workstation operating system (licensed and provided separately) for use on a single computer used for debugging purposes. +* Microsoft Developer Network Subscriber. If you acquired the SOFTWARE PRODUCT through a subscription to the Microsoft Developer Network, and you are either an individual developer or an individual designated within a single entity, you are granted the following additional rights with respect to the SOFTWARE PRODUCT: (a) you may make and use copies of the SOFTWARE PRODUCT on up to ten (10) separate computers, provided that you are the only individual using the SOFTWARE PRODUCT on each such computer, and (b) if you are a single entity, you may designate one individual within your organization to have the right to use the SOFTWARE PRODUCT in the manner described herein. +* SAMPLE CODE. You may modify the sample source code ("Sample Code") to design, develop and test your Software Product, and reproduce and distribute the Sample Code with such modifications in source and object code forms, provided that you comply with the Distribution Requirements described below. +* DISTRIBUTION REQUIREMENTS. You may copy and redistribute the Sample Code as described above, provided that (a) you distribute the Sample Code only in conjunction with and as a part of your Software Product; (b) you do not make any statements to the effect or which imply that your Software Product is "certified" by Microsoft or that its performance is guaranteed by Microsoft; (c) you do not use Microsoft's name, logo, or trademarks to market your Software Product; (d) you include a valid copyright notice on your Software Product; and (e) you indemnify, hold harmless, and defend Microsoft from and against any claims or lawsuits, including attorneys' fees, that arise or result from the use or distribution of your Software Product. Contact Microsoft for the applicable royalties due and other licensing terms for all other uses and/or distribution of the Sample Code +* Microsoft reserves all rights not expressly granted to you. + +2. COPYRIGHT. All right, title, and copyrights in and to the SOFTWARE PRODUCT (including but not limited to any images, photographs, animations, video, audio, music, text, and "applets" incorporated into the SOFTWARE PRODUCT), and any copies of the SOFTWARE PRODUCT, are owned by Microsoft or its suppliers. The SOFTWARE PRODUCT is protected by copyright laws and international treaty provisions. Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted material except that you may either (a) make one copy of the SOFTWARE PRODUCT solely for backup or archival purposes, or (b) install the SOFTWARE PRODUCT on a single computer, provided you keep the original solely for backup or archival purposes. You may not copy the printed materials accompanying the SOFTWARE PRODUCT. + +3. PRERELEASE CODE. The SOFTWARE PRODUCT may contain PRERELEASE CODE that is not at the level of performance and compatibility of the final, generally available, product offering. These portions of the SOFTWARE PRODUCT may not operate correctly and may be substantially modified prior to first commercial shipment. Microsoft is not obligated to make this or any later version of the SOFTWARE PRODUCT commercially available. Microsoft grants you the right to distribute test versions of your Application created using the PRERELEASE CODE provided you comply with the Distribution Requirements described in Section 1 and the following additional provisions: (a) you must mark the test version of your Application "BETA" and (b) you are solely responsible for updating your customers with versions of your Application that operate satisfactorily with the final commercial release of the PRERELEASE CODE. + +4. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS. +( Limitations on Reverse Engineering, Decompilation, and Disassembly. You may not reverse-engineer, decompile, or disassemble the SOFTWARE PRODUCT, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation. +( Rental. You may not rent or lease the SOFTWARE PRODUCT. +( Software Transfer. You may permanently transfer all of your rights under this EULA, provided you retain no copies, you transfer all of the SOFTWARE PRODUCT (including all component parts, the media and printed materials, any upgrades, this EULA, and, if applicable, the Certificate of Authenticity), and the recipient agrees to the terms of this EULA. If the SOFTWARE PRODUCT is an upgrade, any transfer must include all prior versions of the SOFTWARE PRODUCT. +( Termination. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the SOFTWARE PRODUCT and all of its component parts. + +5. EXPORT RESTRICTIONS. You agree that neither you nor your customers intend to or will, directly or indirectly, export or transmit (a) the SOFTWARE PRODUCT or related documentation and technical data or (b) your Application as described in Section 1 of this Agreement (or any part thereof), or process, or service that is the direct product of the SOFTWARE PRODUCT to any country to which such export or transmission is restricted by any applicable U.S. regulation or statute, without the prior written consent, if required, of the Bureau of Export Administration of the U.S. Department of Commerce, or such other governmental entity as may have jurisdiction over such export or transmission. + +NO WARRANTIES. To the maximum extent permitted by applicable law, Microsoft expressly disclaims any warranty for the SOFTWARE PRODUCT. The SOFTWARE PRODUCT and any related documentation are provided "as is" without warranty of any kind, either express or implied, including, without limitation, the implied warranties of merchantability or fitness for a particular purpose. The entire risk arising out of use or performance of the SOFTWARE PRODUCT remains with you. + +LIMITATION OF LIABILITY. Microsoft's entire liability and your exclusive remedy under this EULA shall not exceed one hundred dollars (US$100.00). + +NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no event shall Microsoft or its suppliers be liable for any damages whatsoever (including, without limitation, damages for loss of business profit, business interruption, loss of business information, or any other pecuniary loss) arising out of the use or inability to use this Microsoft product, even if Microsoft has been advised of the possibility of such damages. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you. + +U.S. GOVERNMENT RESTRICTED RIGHTS.The SOFTWARE and documentation are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer Software -- Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer is Microsoft Corporation/One Microsoft Way/Redmond, WA 98052-6399. + +If you acquired this product in the United States, this Agreement is governed by the laws of the State of Washington. If you acquired this product outside the United States, local law may apply. + +Should you have any questions concerning this Agreement, if you desire rights to use the product beyond what is listed here, or if you desire to contact Microsoft for any reason, please contact your local Microsoft subsidiary or sales office or write to: Microsoft Sales and Service, One Microsoft Way, Redmond, WA 98052-6399. + +Si vous avez acquis votre produit Microsoft au CANADA, la garantie limitée suivante vous concerne : + +GARANTIE LIMITÉE + +EXCLUSION DE GARANTIES. Microsoft renonce entièrement à toute garantie pour le LOGICIEL. Le LOGICIEL et toute autre documentation s'y rapportant sont fournis «comme tels» sans aucune garantie quelle qu'elle soit, expresse ou implicite, y compris, mais ne se limitant pas, aux garanties implicites de la qualité marchande ou un usage particulier. Le risque total découlant de l'utilisation ou de la performance du LOGICIEL est entre vos mains. + +RESPONSABILITÉ LIMITÉE. La seule obligation de Microsoft et votre recours exclusif concernant ce contrat n'excèderont pas cent dollars (US$100.00). + +ABSENCE DE RESPONSABILITÉ POUR LES DOMMAGES INDIRECTS. Microsoft ou ses fournisseurs ne pourront être tenus responsables en aucune circonstance de tout dommage quel qu'il soit (y compris mais non de façon limitative aux dommages directs ou indirects causés par la perte de bénéfices commerciaux, l'interruption des affaires, la perte d'information commerciale ou toute autre perte pécuniaire) résultant de l'utilisation ou de l'impossibilité d'utilisation de ce produit, et ce, même si la société Microsoft a été avisée de l'éventualité de tels dommages. Certains états/juridictions ne permettent pas l'exclusion ou la limitation de responsabilité relative aux dommages indirects ou consécutifs, et la limitation ci-dessus peut ne pas s'appliquer à votre égard. + +La présente Convention est régie par les lois de la province d'Ontario, Canada. Chacune des parties à la présente reconnaît irrévocablement la compétence des tribunaux de la province d'Ontario et consent à instituer tout litige qui pourrait découler de la présente auprès des tribunaux situés dans le district judiciaire de York, province d'Ontario. + +Au cas où vous auriez des questions concernant cette licence ou que vous désiriez vous mettre en rapport avec Microsoft pour quelque raison que ce soit, veuillez contacter la succursale Microsoft desservant votre pays, dont l'adresse est fournie dans ce produit, ou écrire à : Microsoft Customer Sales and Service, One Microsoft Way, Redmond, Washington 98052-6399. + diff --git a/reactos/drivers/storage/cdrom/cdrom.c b/reactos/drivers/storage/cdrom/cdrom.c index f1ad1dae316..c522365d6da 100644 --- a/reactos/drivers/storage/cdrom/cdrom.c +++ b/reactos/drivers/storage/cdrom/cdrom.c @@ -1,36 +1,10 @@ /* - * ReactOS kernel - * Copyright (C) 2001, 2002 ReactOS Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * PROJECT: ReactOS Storage Stack + * LICENSE: DDK - see license.txt in the root dir + * FILE: drivers/storage/cdrom/cdrom.c + * PURPOSE: CDROM driver + * PROGRAMMERS: Based on a source code sample from Microsoft NT4 DDK */ -/* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: services/storage/cdrom/cdrom.c - * PURPOSE: cdrom class driver - * PROGRAMMER: Eric Kohl (ekohl@rz-online.de) - */ - -/* - * TODO: - * - Add io timer routine for autorun support. - * - Add cdaudio support (cd player). - */ - -/* INCLUDES *****************************************************************/ #include #include @@ -40,1666 +14,6952 @@ #include #include -#define NDEBUG +//#define NDEBUG #include -#define VERSION "0.0.1" +#define CDB12GENERIC_LENGTH 12 +typedef struct _XA_CONTEXT { -#define SCSI_CDROM_TIMEOUT 10 /* Default timeout: 10 seconds */ + // + // Pointer to the device object. + // + PDEVICE_OBJECT DeviceObject; -typedef struct _ERROR_RECOVERY_DATA6 -{ - MODE_PARAMETER_HEADER Header; - MODE_READ_RECOVERY_PAGE ReadRecoveryPage; -} ERROR_RECOVERY_DATA6, *PERROR_RECOVERY_DATA6; + // + // Pointer to the original request when + // a mode select must be sent. + // + PIRP OriginalRequest; -typedef struct _ERROR_RECOVERY_DATA10 -{ - MODE_PARAMETER_HEADER10 Header; - MODE_READ_RECOVERY_PAGE ReadRecoveryPage; + // + // Pointer to the mode select srb. + // + + PSCSI_REQUEST_BLOCK Srb; +} XA_CONTEXT, *PXA_CONTEXT; + +typedef struct _ERROR_RECOVERY_DATA { + MODE_PARAMETER_HEADER Header; + MODE_PARAMETER_BLOCK BlockDescriptor; + MODE_READ_RECOVERY_PAGE ReadRecoveryPage; +} ERROR_RECOVERY_DATA, *PERROR_RECOVERY_DATA; + +typedef struct _ERROR_RECOVERY_DATA10 { + MODE_PARAMETER_HEADER10 Header10; + MODE_PARAMETER_BLOCK BlockDescriptor10; + MODE_READ_RECOVERY_PAGE ReadRecoveryPage10; } ERROR_RECOVERY_DATA10, *PERROR_RECOVERY_DATA10; -typedef struct _MODE_CAPABILITIES_PAGE2 -{ - UCHAR PageCode:6; - UCHAR Reserved1:1; - UCHAR PSBit:1; - UCHAR PageLength; - UCHAR Reserved2[2]; - UCHAR Capabilities[4]; - UCHAR MaximumSpeedSupported[2]; - UCHAR Reserved3; - UCHAR NumberVolumeLevels; - UCHAR BufferSize[2]; - UCHAR CurrentSpeed[2]; - UCHAR Reserved4; - UCHAR Reserved5:1; - UCHAR DigitalOutput:4; - UCHAR Reserved6:3; - UCHAR Reserved7[2]; -} MODE_CAPABILITIES_PAGE2, *PMODE_CAPABILITIES_PAGE2; +// +// CdRom specific addition to device extension. +// -typedef struct _MODE_CAPABILITIES_DATA6 -{ - MODE_PARAMETER_HEADER Header; - MODE_CAPABILITIES_PAGE2 CababilitiesPage; -} MODE_CAPABILITIES_DATA6, *PMODE_CAPABILITIES_DATA6; +typedef struct _CDROM_DATA { -typedef struct _MODE_CAPABILITIES_DATA10 -{ - MODE_PARAMETER_HEADER10 Header; - MODE_CAPABILITIES_PAGE2 CababilitiesPage; -} MODE_CAPABILITIES_DATA10, *PMODE_CAPABILITIES_DATA10; + // + // Indicates whether an audio play operation + // is currently being performed. + // -typedef struct _CDROM_DATA -{ - BOOLEAN PlayActive; - BOOLEAN RawAccess; - USHORT XaFlags; + BOOLEAN PlayActive; + + // + // Indicates whether the blocksize used for user data + // is 2048 or 2352. + // + + BOOLEAN RawAccess; + + // + // Indicates whether 6 or 10 byte mode sense/select + // should be used. + // + + USHORT XAFlags; + + // + // Storage for the error recovery page. This is used + // as an easy method to switch block sizes. + // + + union { + ERROR_RECOVERY_DATA u1; + ERROR_RECOVERY_DATA10 u2; + }; + + + // + // Pointer to the original irp for the raw read. + // + + PIRP SavedReadIrp; + + // + // Used to protect accesses to the RawAccess flag. + // + + KSPIN_LOCK FormSpinLock; + + // + // Even if media change support is requested, there are some devices + // that are not supported. This flag will indicate that such a device + // is present when it is FALSE. + // + + BOOLEAN MediaChangeSupported; + + // + // The media change event is being supported. The media change timer + // should be running whenever this is true. + // + + BOOLEAN MediaChange; + + // + // The timer value to support media change events. This is a countdown + // value used to determine when to poll the device for a media change. + // The max value for the timer is 255 seconds. + // + + UCHAR MediaChangeCountDown; + +#ifdef DBG + // + // Second timer to keep track of how long the media change IRP has been + // in use. If this value exceeds the timeout (#defined) then we should + // print out a message to the user and set the MediaChangeIrpLost flag + // + + SHORT MediaChangeIrpTimeInUse; + + // + // Set by CdRomTickHandler when we determine that the media change irp has + // been lost + // + + BOOLEAN MediaChangeIrpLost; +#endif + + UCHAR PadReserve; // use this for new flags. + + // + // An IRP is allocated and kept for the duration that media change + // detection is in effect. If this is NULL and MediaChange is TRUE, + // the detection is in progress. This should always be NULL when + // MediaChange is FALSE. + // + + PIRP MediaChangeIrp; + + // + // The timer work list is a collection of IRPS that are prepared for + // submission, but need to allow some time to pass before they are + // run. + // + + LIST_ENTRY TimerIrpList; + KSPIN_LOCK TimerIrpSpinLock; } CDROM_DATA, *PCDROM_DATA; -/* CDROM_DATA.XaFlags */ -#define XA_USE_6_BYTE 0x0001 -#define XA_USE_10_BYTE 0x0002 -#define XA_USE_READ_CD 0x0004 -#define XA_NOT_SUPPORTED 0x0008 +#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + sizeof(CDROM_DATA) +#define SCSI_CDROM_TIMEOUT 10 +#define SCSI_CHANGER_BONUS_TIMEOUT 10 +#define HITACHI_MODE_DATA_SIZE 12 +#define MODE_DATA_SIZE 64 +#define RAW_SECTOR_SIZE 2352 +#define COOKED_SECTOR_SIZE 2048 +#define MEDIA_CHANGE_DEFAULT_TIME 4 +#define CDROM_SRB_LIST_SIZE 4 -BOOLEAN STDCALL -CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath, - IN PCLASS_INIT_DATA InitializationData, - IN PDEVICE_OBJECT PortDeviceObject, - IN ULONG PortNumber); +#ifdef DBG -BOOLEAN STDCALL -CdromClassCheckDevice(IN PINQUIRYDATA InquiryData); +// +// Used to detect the loss of the autorun irp. The driver prints out a message +// (debug level 0) if this timeout ever occurs +// +#define MEDIA_CHANGE_TIMEOUT_TIME 300 -NTSTATUS STDCALL -CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -static VOID -CdromClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension, - IN ULONG DeviceNumber); - -static NTSTATUS -CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath, - IN PDEVICE_OBJECT PortDeviceObject, - IN ULONG PortNumber, - IN ULONG DeviceNumber, - IN PIO_SCSI_CAPABILITIES Capabilities, - IN PSCSI_INQUIRY_DATA InquiryData, - IN PCLASS_INIT_DATA InitializationData); - - -NTSTATUS STDCALL -CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -VOID STDCALL -CdromClassStartIo (IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -NTSTATUS STDCALL -CdromDeviceControlCompletion (IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context); - -VOID STDCALL -CdromTimerRoutine(IN PDEVICE_OBJECT DeviceObject, - IN PVOID Context); - -VOID STDCALL -CdromWorkItem(IN PDEVICE_OBJECT DeviceObject, - IN PVOID Context); - - -/* FUNCTIONS ****************************************************************/ - -/********************************************************************** - * NAME EXPORTED - * DriverEntry - * - * DESCRIPTION: - * This function initializes the driver, locates and claims - * hardware resources, and creates various NT objects needed - * to process I/O requests. - * - * RUN LEVEL: - * PASSIVE_LEVEL - * - * ARGUMENTS: - * DriverObject - * System allocated Driver Object for this driver - * RegistryPath - * Name of registry driver service key - * - * RETURNS: - * Status. - */ - -NTSTATUS STDCALL -DriverEntry(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath) -{ - CLASS_INIT_DATA InitData; - - DPRINT("CD-ROM Class Driver %s\n", - VERSION); - DPRINT("RegistryPath '%wZ'\n", - RegistryPath); - - InitData.InitializationDataSize = sizeof(CLASS_INIT_DATA); - InitData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION) + sizeof(CDROM_DATA); - InitData.DeviceType = FILE_DEVICE_CD_ROM; - InitData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE; - - InitData.ClassError = NULL; - InitData.ClassReadWriteVerification = CdromClassCheckReadWrite; - InitData.ClassFindDeviceCallBack = CdromClassCheckDevice; - InitData.ClassFindDevices = CdromClassFindDevices; - InitData.ClassDeviceControl = CdromClassDeviceControl; - InitData.ClassShutdownFlush = NULL; - InitData.ClassCreateClose = NULL; - InitData.ClassStartIo = CdromClassStartIo; - - return(ScsiClassInitialize(DriverObject, - RegistryPath, - &InitData)); -} - - -/********************************************************************** - * NAME EXPORTED - * CdromClassFindDevices - * - * DESCRIPTION: - * This function searches for device that are attached to the - * given scsi port. - * - * RUN LEVEL: - * PASSIVE_LEVEL - * - * ARGUMENTS: - * DriverObject - * System allocated Driver Object for this driver - * RegistryPath - * Name of registry driver service key. - * InitializationData - * Pointer to the main initialization data - * PortDeviceObject - * Scsi port device object - * PortNumber - * Port number - * - * RETURNS: - * TRUE: At least one disk drive was found - * FALSE: No disk drive found - */ - -BOOLEAN STDCALL -CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath, - IN PCLASS_INIT_DATA InitializationData, - IN PDEVICE_OBJECT PortDeviceObject, - IN ULONG PortNumber) -{ - PCONFIGURATION_INFORMATION ConfigInfo; - PIO_SCSI_CAPABILITIES PortCapabilities; - PSCSI_ADAPTER_BUS_INFO AdapterBusInfo; - PSCSI_INQUIRY_DATA UnitInfo; - PINQUIRYDATA InquiryData; - PCHAR Buffer; - ULONG Bus; - ULONG DeviceCount; - BOOLEAN FoundDevice = FALSE; - NTSTATUS Status; - - DPRINT("CdromClassFindDevices() called.\n"); - - /* Get port capabilities */ - Status = ScsiClassGetCapabilities(PortDeviceObject, - &PortCapabilities); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ScsiClassGetCapabilities() failed! (Status 0x%lX)\n", Status); - return(FALSE); - } - - DPRINT("PortCapabilities: %p\n", PortCapabilities); - DPRINT("MaximumTransferLength: %lu\n", PortCapabilities->MaximumTransferLength); - DPRINT("MaximumPhysicalPages: %lu\n", PortCapabilities->MaximumPhysicalPages); - - /* Get inquiry data */ - Status = ScsiClassGetInquiryData(PortDeviceObject, - (PSCSI_ADAPTER_BUS_INFO *)&Buffer); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ScsiClassGetInquiryData() failed! (Status 0x%lX)\n", Status); - return(FALSE); - } - - /* Check whether there are unclaimed devices */ - AdapterBusInfo = (PSCSI_ADAPTER_BUS_INFO)Buffer; - DeviceCount = ScsiClassFindUnclaimedDevices(InitializationData, - AdapterBusInfo); - if (DeviceCount == 0) - { - DPRINT("No unclaimed devices!\n"); - return(FALSE); - } - - DPRINT("Found %lu unclaimed devices!\n", DeviceCount); - - ConfigInfo = IoGetConfigurationInformation(); - DPRINT("Number of SCSI ports: %lu\n", ConfigInfo->ScsiPortCount); - - /* Search each bus of this adapter */ - for (Bus = 0; Bus < (ULONG)AdapterBusInfo->NumberOfBuses; Bus++) - { - DPRINT("Searching bus %lu\n", Bus); - - UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + AdapterBusInfo->BusData[Bus].InquiryDataOffset); - - while (AdapterBusInfo->BusData[Bus].InquiryDataOffset) - { - InquiryData = (PINQUIRYDATA)UnitInfo->InquiryData; - - if ((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) && - (InquiryData->DeviceTypeQualifier == 0) && - (UnitInfo->DeviceClaimed == FALSE)) - { - DPRINT("Vendor: '%.24s'\n", - InquiryData->VendorId); - - /* Create device objects for disk */ - Status = CdromClassCreateDeviceObject(DriverObject, - RegistryPath, - PortDeviceObject, - PortNumber, - ConfigInfo->CdRomCount, - PortCapabilities, - UnitInfo, - InitializationData); - if (NT_SUCCESS(Status)) - { - ConfigInfo->CdRomCount++; - FoundDevice = TRUE; - } - } - - if (UnitInfo->NextInquiryDataOffset == 0) - break; - - UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + UnitInfo->NextInquiryDataOffset); - } - } - - ExFreePool(Buffer); - - DPRINT("CdromClassFindDevices() done\n"); - - return(FoundDevice); -} - - -/********************************************************************** - * NAME EXPORTED - * CdromClassCheckDevice - * - * DESCRIPTION - * This function checks the InquiryData for the correct device - * type and qualifier. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * InquiryData - * Pointer to the inquiry data for the device in question. - * - * RETURN VALUE - * TRUE: A disk device was found. - * FALSE: Otherwise. - */ - -BOOLEAN STDCALL -CdromClassCheckDevice(IN PINQUIRYDATA InquiryData) -{ - return((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) && - (InquiryData->DeviceTypeQualifier == 0)); -} - - -/********************************************************************** - * NAME EXPORTED - * CdromClassCheckReadWrite - * - * DESCRIPTION - * This function checks the given IRP for correct data. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceObject - * Pointer to the device. - * Irp - * Irp to check. - * - * RETURN VALUE - * STATUS_SUCCESS: The IRP matches the requirements of the given device. - * Others: Failure. - */ - -NTSTATUS STDCALL -CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - DPRINT("CdromClassCheckReadWrite() called\n"); - - return(STATUS_SUCCESS); -} - - -static VOID -CdromClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension, - IN ULONG DeviceNumber) -{ - - - DeviceExtension->MediaChangeEvent = - IoCreateSynchronizationEvent (NULL, - &DeviceExtension->MediaChangeEventHandle); - - KeClearEvent (DeviceExtension->MediaChangeEvent); -} - - -/********************************************************************** - * NAME EXPORTED - * CdromClassCreateDeviceObject - * - * DESCRIPTION: - * Create the raw device and any partition devices on this drive - * - * RUN LEVEL: - * PASSIVE_LEVEL - * - * ARGUMENTS: - * DriverObject - * System allocated Driver Object for this driver. - * RegistryPath - * Name of registry driver service key. - * PortDeviceObject - * PortNumber - * DeviceNumber - * Capabilities - * InquiryData - * InitializationData - * - * RETURNS: - * Status. - */ - -static NTSTATUS -CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath, - IN PDEVICE_OBJECT PortDeviceObject, - IN ULONG PortNumber, - IN ULONG DeviceNumber, - IN PIO_SCSI_CAPABILITIES Capabilities, - IN PSCSI_INQUIRY_DATA InquiryData, - IN PCLASS_INIT_DATA InitializationData) -{ - PDEVICE_EXTENSION DiskDeviceExtension; /* defined in class2.h */ - PDEVICE_OBJECT DiskDeviceObject; - PCDROM_DATA CdromData; - CHAR NameBuffer[80]; - SCSI_REQUEST_BLOCK Srb; - PUCHAR Buffer; - ULONG Length; - PCDB Cdb; - NTSTATUS Status; - - DPRINT("CdromClassCreateDeviceObject() called\n"); - - /* Claim the cdrom device */ - Status = ScsiClassClaimDevice(PortDeviceObject, - InquiryData, - FALSE, - &PortDeviceObject); - if (!NT_SUCCESS(Status)) - { - DbgPrint("Could not claim cdrom device\n"); - return(Status); - } - - /* Create cdrom device */ - sprintf(NameBuffer, - "\\Device\\CdRom%lu", - DeviceNumber); - - Status = ScsiClassCreateDeviceObject(DriverObject, - NameBuffer, - NULL, - &DiskDeviceObject, - InitializationData); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ScsiClassCreateDeviceObject() failed (Status %x)\n", Status); - - /* Release (unclaim) the disk */ - ScsiClassClaimDevice(PortDeviceObject, - InquiryData, - TRUE, - NULL); - - return(Status); - } - - DiskDeviceObject->Flags |= DO_DIRECT_IO; - DiskDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; - DiskDeviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1; - - if (PortDeviceObject->AlignmentRequirement > DiskDeviceObject->AlignmentRequirement) - { - DiskDeviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement; - } - - DiskDeviceExtension = DiskDeviceObject->DeviceExtension; - DiskDeviceExtension->LockCount = 0; - DiskDeviceExtension->DeviceNumber = DeviceNumber; - DiskDeviceExtension->DeviceObject = DiskDeviceObject; - DiskDeviceExtension->PortDeviceObject = PortDeviceObject; - DiskDeviceExtension->PhysicalDevice = DiskDeviceObject; - DiskDeviceExtension->PortCapabilities = Capabilities; - DiskDeviceExtension->StartingOffset.QuadPart = 0; - DiskDeviceExtension->PortNumber = (UCHAR)PortNumber; - DiskDeviceExtension->PathId = InquiryData->PathId; - DiskDeviceExtension->TargetId = InquiryData->TargetId; - DiskDeviceExtension->Lun = InquiryData->Lun; - - /* zero-out disk data */ - CdromData = (PCDROM_DATA)(DiskDeviceExtension + 1); - RtlZeroMemory(CdromData, - sizeof(CDROM_DATA)); - - DiskDeviceExtension->SenseData = ExAllocatePool(NonPagedPool, - sizeof(SENSE_DATA)); - if (DiskDeviceExtension->SenseData == NULL) - { - DPRINT1("Failed to allocate sense data buffer!\n"); - - IoDeleteDevice(DiskDeviceObject); - - /* Release (unclaim) the disk */ - ScsiClassClaimDevice(PortDeviceObject, - InquiryData, - TRUE, - NULL); - - return(STATUS_INSUFFICIENT_RESOURCES); - } - - /* Get timeout value */ - DiskDeviceExtension->TimeOutValue = - ScsiClassQueryTimeOutRegistryValue(RegistryPath); - if (DiskDeviceExtension->TimeOutValue == 0) - DiskDeviceExtension->TimeOutValue = SCSI_CDROM_TIMEOUT; - - /* Initialize lookaside list for SRBs */ - ScsiClassInitializeSrbLookasideList(DiskDeviceExtension, - 4); - - /* Get disk geometry */ - DiskDeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool, - sizeof(DISK_GEOMETRY)); - if (DiskDeviceExtension->DiskGeometry == NULL) - { - DPRINT1("Failed to allocate geometry buffer!\n"); - - ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead); - - IoDeleteDevice(DiskDeviceObject); - - /* Release (unclaim) the disk */ - ScsiClassClaimDevice(PortDeviceObject, - InquiryData, - TRUE, - NULL); - - return(STATUS_INSUFFICIENT_RESOURCES); - } - - /* Read the drive's capacity */ - Status = ScsiClassReadDriveCapacity(DiskDeviceObject); - if (!NT_SUCCESS(Status) || - DiskDeviceExtension->DiskGeometry->BytesPerSector == 0) - { - /* Set ISO9660 defaults */ - DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048; - DiskDeviceExtension->DiskGeometry->MediaType = RemovableMedia; - DiskDeviceExtension->SectorShift = 11; - DiskDeviceExtension->PartitionLength.QuadPart = (ULONGLONG)0x7fffffff; - } - else - { - /* Make sure the BytesPerSector value is a power of 2 */ -// DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048; - } - - DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector); - - /* Initialize media change support */ - CdromClassCreateMediaChangeEvent (DiskDeviceExtension, - DeviceNumber); - if (DiskDeviceExtension->MediaChangeEvent != NULL) - { - DPRINT("Allocated media change event!\n"); - - /* FIXME: Allocate media change IRP and SRB */ - } - - /* Use 6 byte xa commands by default */ - CdromData->XaFlags |= XA_USE_6_BYTE; - - /* Read 'error recovery page' to get additional drive capabilities */ - Length = sizeof(MODE_READ_RECOVERY_PAGE) + MODE_HEADER_LENGTH; - - RtlZeroMemory (&Srb, - sizeof(SCSI_REQUEST_BLOCK)); - Srb.CdbLength = 6; - Srb.TimeOutValue = DiskDeviceExtension->TimeOutValue; - - Cdb = (PCDB)Srb.Cdb; - Cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; - Cdb->MODE_SENSE.PageCode = 0x01; - Cdb->MODE_SENSE.AllocationLength = (UCHAR)Length; - - Buffer = ExAllocatePool (NonPagedPool, - max(sizeof(ERROR_RECOVERY_DATA6), - max(sizeof(ERROR_RECOVERY_DATA10), - max(sizeof(MODE_CAPABILITIES_DATA6), - sizeof(MODE_CAPABILITIES_DATA10))))); - if (Buffer == NULL) - { - DPRINT1("Allocating recovery page buffer failed!\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - Status = ScsiClassSendSrbSynchronous (DiskDeviceObject, - &Srb, - Buffer, - Length, - FALSE); - - if (!NT_SUCCESS (Status)) - { - DPRINT("MODE_SENSE(6) failed\n"); - - /* Try the 10 byte version */ - Length = sizeof(MODE_READ_RECOVERY_PAGE) + MODE_HEADER_LENGTH10; - - RtlZeroMemory (&Srb, - sizeof(SCSI_REQUEST_BLOCK)); - Srb.CdbLength = 10; - Srb.TimeOutValue = DiskDeviceExtension->TimeOutValue; - - Cdb = (PCDB)Srb.Cdb; - Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; - Cdb->MODE_SENSE10.PageCode = 0x01; - Cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(Length >> 8); - Cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(Length & 0xFF); - - Status = ScsiClassSendSrbSynchronous (DiskDeviceObject, - &Srb, - Buffer, - Length, - FALSE); - - if (Status == STATUS_DATA_OVERRUN) - { - DPRINT1("Data overrun\n"); - - /* FIXME */ - } - else if (NT_SUCCESS (Status)) - { - DPRINT("Use 10 byte commands\n"); - CdromData->XaFlags &= XA_USE_6_BYTE; - CdromData->XaFlags |= XA_USE_10_BYTE; - } - else - { - DPRINT("XA not supported\n"); - CdromData->XaFlags |= XA_NOT_SUPPORTED; - } - } - else - { - DPRINT("Use 6 byte commands\n"); - } - - /* Read 'capabilities & mechanical status page' to get additional drive capabilities */ - Length = sizeof(MODE_READ_RECOVERY_PAGE) + MODE_HEADER_LENGTH; - - if (!(CdromData->XaFlags & XA_NOT_SUPPORTED)) - { - RtlZeroMemory (&Srb, sizeof(SCSI_REQUEST_BLOCK)); - Srb.TimeOutValue = DiskDeviceExtension->TimeOutValue; - Cdb = (PCDB)Srb.Cdb; - - if (CdromData->XaFlags & XA_USE_10_BYTE) - { - /* Try the 10 byte version */ - Length = sizeof(MODE_CAPABILITIES_PAGE2) + MODE_HEADER_LENGTH10; - - Srb.CdbLength = 10; - Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; - Cdb->MODE_SENSE10.PageCode = 0x2a; - Cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(Length >> 8); - Cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(Length & 0xFF); - } - else - { - Length = sizeof(MODE_CAPABILITIES_PAGE2) + MODE_HEADER_LENGTH; - - Srb.CdbLength = 6; - Cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; - Cdb->MODE_SENSE.PageCode = 0x2a; - Cdb->MODE_SENSE.AllocationLength = (UCHAR)Length; - } - Status = ScsiClassSendSrbSynchronous (DiskDeviceObject, - &Srb, - Buffer, - Length, - FALSE); - if (NT_SUCCESS (Status)) - { -#if 0 - PMODE_CAPABILITIES_PAGE2 CapabilitiesData; - if (CdromData->XaFlags & XA_USE_10_BYTE) - { - CapabilitiesData = (PMODE_CAPABILITIES_PAGE2)(Buffer + sizeof(MODE_PARAMETER_HEADER10)); - } - else - { - CapabilitiesData = (PMODE_CAPABILITIES_PAGE2)(Buffer + sizeof(MODE_PARAMETER_HEADER)); - } - - DbgPrint("Capabilities for '%s':\n", NameBuffer); - if (CapabilitiesData->Reserved2[0] & 0x20) - { - DbgPrint(" Drive supports reading of DVD-RAM discs\n"); - } - if (CapabilitiesData->Reserved2[0] & 0x10) - { - DbgPrint(" Drive supports reading of DVD-R discs\n"); - } - if (CapabilitiesData->Reserved2[0] & 0x08) - { - DbgPrint(" Drive supports reading of DVD-ROM discs\n"); - } - if (CapabilitiesData->Reserved2[0] & 0x04) - { - DbgPrint(" Drive supports reading CD-R discs with addressing method 2\n"); - } - if (CapabilitiesData->Reserved2[0] & 0x02) - { - DbgPrint(" Drive can read from CD-R/W (CD-E) discs (orange book, part III)\n"); - } - if (CapabilitiesData->Reserved2[0] & 0x01) - { - DbgPrint(" Drive supports read from CD-R discs (orange book, part II)\n"); - } - DPRINT("CapabilitiesData.Reserved2[1] %x\n", CapabilitiesData->Reserved2[1]); - if (CapabilitiesData->Reserved2[1] & 0x01) - { - DbgPrint(" Drive can write to CD-R discs (orange book, part II)\n"); - } - if (CapabilitiesData->Reserved2[1] & 0x02) - { - DbgPrint(" Drive can write to CD-R/W (CD-E) discs (orange book, part III)\n"); - } - if (CapabilitiesData->Reserved2[1] & 0x04) - { - DbgPrint(" Drive can fake writes\n"); - } - if (CapabilitiesData->Reserved2[1] & 0x10) - { - DbgPrint(" Drive can write DVD-R discs\n"); - } - if (CapabilitiesData->Reserved2[1] & 0x20) - { - DbgPrint(" Drive can write DVD-RAM discs\n"); - } - DPRINT("CapabilitiesData.Capabilities[0] %x\n", CapabilitiesData->Capabilities[0]); - if (CapabilitiesData->Capabilities[0] & 0x01) - { - DbgPrint(" Drive supports audio play operations\n"); - } - if (CapabilitiesData->Capabilities[0] & 0x02) - { - DbgPrint(" Drive can deliver a composite audio/video data stream\n"); - } - if (CapabilitiesData->Capabilities[0] & 0x04) - { - DbgPrint(" Drive supports digital output on port 1\n"); - } - if (CapabilitiesData->Capabilities[0] & 0x08) - { - DbgPrint(" Drive supports digital output on port 2\n"); - } - if (CapabilitiesData->Capabilities[0] & 0x10) - { - DbgPrint(" Drive can read mode 2, form 1 (XA) data\n"); - } - if (CapabilitiesData->Capabilities[0] & 0x20) - { - DbgPrint(" Drive can read mode 2, form 2 data\n"); - } - if (CapabilitiesData->Capabilities[0] & 0x40) - { - DbgPrint(" Drive can read multisession discs\n"); - } - DPRINT("CapabilitiesData.Capabilities[1] %x\n", CapabilitiesData->Capabilities[1]); - if (CapabilitiesData->Capabilities[1] & 0x01) - { - DbgPrint(" Drive can read Red Book audio data\n"); - } - if (CapabilitiesData->Capabilities[1] & 0x02) - { - DbgPrint(" Drive can continue a read cdda operation from a loss of streaming\n"); - } - if (CapabilitiesData->Capabilities[1] & 0x04) - { - DbgPrint(" Subchannel reads can return combined R-W information\n"); - } - if (CapabilitiesData->Capabilities[1] & 0x08) - { - DbgPrint(" R-W data will be returned deinterleaved and error corrected\n"); - } - if (CapabilitiesData->Capabilities[1] & 0x10) - { - DbgPrint(" Drive supports C2 error pointers\n"); - } - if (CapabilitiesData->Capabilities[1] & 0x20) - { - DbgPrint(" Drive can return International Standard Recording Code info\n"); - } - if (CapabilitiesData->Capabilities[1] & 0x40) - { - DbgPrint(" Drive can return Media Catalog Number (UPC) info\n"); - } - DPRINT("CapabilitiesData.Capabilities[2] %x\n", CapabilitiesData->Capabilities[2]); - if (CapabilitiesData->Capabilities[2] & 0x01) - { - DbgPrint(" Drive can lock the door\n"); - } - if (CapabilitiesData->Capabilities[2] & 0x02) - { - DbgPrint(" The door is locked\n"); - } - if (CapabilitiesData->Capabilities[2] & 0x04) - { - } - if (CapabilitiesData->Capabilities[2] & 0x08) - { - DbgPrint(" Drive can eject a disc or changer cartridge\n"); - } - if (CapabilitiesData->Capabilities[2] & 0x10) - { - DbgPrint(" Drive supports C2 error pointers\n"); - } - switch (CapabilitiesData->Capabilities[2] >> 5) - { - case 0: - DbgPrint(" Drive use a caddy type loading mechanism\n"); - break; - case 1: - DbgPrint(" Drive use a tray type loading mechanism\n"); - break; - case 2: - DbgPrint(" Drive use a pop-up type loading mechanism\n"); - break; - case 4: - DbgPrint(" Drive is a changer with individually changeable discs\n"); - break; - case 5: - DbgPrint(" Drive is a changer with cartridge mechanism\n"); - break; - } - DPRINT("CapabilitiesData.Capabilities[3] %x\n", CapabilitiesData->Capabilities[3]); - if (CapabilitiesData->Capabilities[3] & 0x01) - { - DbgPrint(" Audio level for each channel can be controlled independently\n"); - } - if (CapabilitiesData->Capabilities[3] & 0x02) - { - DbgPrint(" Audio for each channel can be muted independently\n"); - } - if (CapabilitiesData->Capabilities[3] & 0x04) - { - DbgPrint(" Changer can report exact contents of slots\n"); - } - if (CapabilitiesData->Capabilities[3] & 0x08) - { - DbgPrint(" Drive supports software slot selection\n"); - } - DbgPrint(" Maximum speed is %d kB/s\n", - (CapabilitiesData->MaximumSpeedSupported[0] << 8) - | CapabilitiesData->MaximumSpeedSupported[1]); - DbgPrint(" Current speed is %d kB/s\n", - (CapabilitiesData->CurrentSpeed[0] << 8) - | CapabilitiesData->CurrentSpeed[1]); - DbgPrint(" Number of discrete volume levels is %d\n", - (CapabilitiesData->Reserved3 << 8) - | CapabilitiesData->NumberVolumeLevels); - DbgPrint(" Buffer size is %d kB\n", - (CapabilitiesData->BufferSize[0] << 8) - | CapabilitiesData->BufferSize[1]); #endif - } - else - { - DPRINT("XA not supported\n"); - CdromData->XaFlags |= XA_NOT_SUPPORTED; - } - } +#define PLAY_ACTIVE(DeviceExtension) (((PCDROM_DATA)(DeviceExtension + 1))->PlayActive) +#define MSF_TO_LBA(Minutes,Seconds,Frames) \ + (ULONG)((60 * 75 * (Minutes)) + (75 * (Seconds)) + ((Frames) - 150)) - ExFreePool (Buffer); - - /* Initialize device timer */ - IoInitializeTimer(DiskDeviceObject, - CdromTimerRoutine, - NULL); - IoStartTimer(DiskDeviceObject); - - DiskDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - - DPRINT("CdromClassCreateDeviceObjects() done\n"); - - return(STATUS_SUCCESS); +#define LBA_TO_MSF(Lba,Minutes,Seconds,Frames) \ +{ \ + (Minutes) = (UCHAR)(Lba / (60 * 75)); \ + (Seconds) = (UCHAR)((Lba % (60 * 75)) / 75); \ + (Frames) = (UCHAR)((Lba % (60 * 75)) % 75); \ } +#define DEC_TO_BCD(x) (((x / 10) << 4) + (x % 10)) + +// +// Define flags for XA, CDDA, and Mode Select/Sense +// + +#define XA_USE_6_BYTE 0x01 +#define XA_USE_10_BYTE 0x02 +#define XA_USE_READ_CD 0x04 +#define XA_NOT_SUPPORTED 0x08 + +#define PLEXTOR_CDDA 0x10 +#define NEC_CDDA 0x20 + +// +// Sector types for READ_CD +// + +#define ANY_SECTOR 0 +#define CD_DA_SECTOR 1 +#define YELLOW_MODE1_SECTOR 2 +#define YELLOW_MODE2_SECTOR 3 +#define FORM2_MODE1_SECTOR 4 +#define FORM2_MODE2_SECTOR 5 + + +#ifdef POOL_TAGGING +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif +#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'CscS') +#endif + + +NTSTATUS +STDCALL +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +BOOLEAN +STDCALL +ScsiCdRomFindDevices( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + IN PCLASS_INIT_DATA InitializationData, + IN PDEVICE_OBJECT PortDeviceObject, + IN ULONG PortNumber + ); + +NTSTATUS +STDCALL +ScsiCdRomOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +STDCALL +ScsiCdRomReadVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +STDCALL +ScsiCdRomSwitchMode( + IN PDEVICE_OBJECT DeviceObject, + IN ULONG SectorSize, + IN PIRP OriginalRequest + ); + +NTSTATUS +STDCALL +CdRomDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +STDCALL +CdRomDeviceControlCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +STDCALL +CdRomSetVolumeIntermediateCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +STDCALL +CdRomSwitchModeCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +STDCALL +CdRomXACompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +STDCALL +CdRomClassIoctlCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +STDCALL +ScsiCdRomStartIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +STDCALL +CdRomTickHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context + ); + +BOOLEAN +STDCALL +CdRomCheckRegistryForMediaChangeValue( + IN PUNICODE_STRING RegistryPath, + IN ULONG DeviceNumber + ); + +NTSTATUS +STDCALL +CdRomUpdateCapacity( + IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP IrpToComplete, + IN OPTIONAL PKEVENT IoctlEvent + ); + +NTSTATUS +STDCALL +CreateCdRomDeviceObject( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PortDeviceObject, + IN ULONG PortNumber, + IN PULONG DeviceCount, + PIO_SCSI_CAPABILITIES PortCapabilities, + IN PSCSI_INQUIRY_DATA LunInfo, + IN PCLASS_INIT_DATA InitializationData, + IN PUNICODE_STRING RegistryPath + ); + +VOID +STDCALL +ScanForSpecial( + PDEVICE_OBJECT DeviceObject, + PINQUIRYDATA InquiryData, + PIO_SCSI_CAPABILITIES PortCapabilities + ); + +BOOLEAN +STDCALL +CdRomIsPlayActive( + IN PDEVICE_OBJECT DeviceObject + ); + +VOID +STDCALL +HitachProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +VOID +STDCALL +ToshibaProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +BOOLEAN +STDCALL +IsThisAnAtapiChanger( + IN PDEVICE_OBJECT DeviceObject, + OUT PULONG DiscsPresent + ); + +BOOLEAN +STDCALL +IsThisASanyo( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR PathId, + IN UCHAR TargetId + ); + +BOOLEAN +STDCALL +IsThisAMultiLunDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PDEVICE_OBJECT PortDeviceObject + ); + +VOID +STDCALL +CdRomCreateNamedEvent( + IN PDEVICE_EXTENSION DeviceExtension, + IN ULONG DeviceNumber + ); + +#ifdef _PPC_ +NTSTATUS +FindScsiAdapter ( + IN HANDLE KeyHandle, + IN UNICODE_STRING ScsiUnicodeString[], + OUT PUCHAR IntermediateController + ); +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, DriverEntry) +#pragma alloc_text(PAGE, ScsiCdRomFindDevices) +#pragma alloc_text(PAGE, CreateCdRomDeviceObject) +#pragma alloc_text(PAGE, ScanForSpecial) +//#pragma alloc_text(PAGE, CdRomDeviceControl) +#pragma alloc_text(PAGE, HitachProcessError) +#pragma alloc_text(PAGE, CdRomIsPlayActive) +#pragma alloc_text(PAGE, ScsiCdRomReadVerification) +#pragma alloc_text(INIT, CdRomCheckRegistryForMediaChangeValue) +#pragma alloc_text(INIT, IsThisAnAtapiChanger) +#pragma alloc_text(INIT, IsThisASanyo) +#pragma alloc_text(INIT, IsThisAMultiLunDevice) +#pragma alloc_text(INIT, CdRomCreateNamedEvent) +#ifdef _PPC_ +#pragma alloc_text(PAGE, FindScsiAdapter) +#endif +#endif + +ULONG NoLoad = 0; + + +NTSTATUS +STDCALL +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine initializes the cdrom class driver. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + RegistryPath - Pointer to the name of the services node for this driver. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ -/********************************************************************** - * NAME - * CdromClassReadTocEntry - * - * ARGUMENTS: - * DeviceObject - * TrackNo - * Buffer - * Length - * - * RETURNS: - * Status. - */ -static NTSTATUS -CdromClassReadTocEntry (PDEVICE_OBJECT DeviceObject, - SIZE_T TrackNo, - PVOID Buffer, - SIZE_T Length) { - PDEVICE_EXTENSION DeviceExtension; - SCSI_REQUEST_BLOCK Srb; - PCDB Cdb; + CLASS_INIT_DATA InitializationData; - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + if(NoLoad) { + return STATUS_NO_SUCH_DEVICE; + } - RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK)); - Srb.CdbLength = 10; - Srb.TimeOutValue = DeviceExtension->TimeOutValue; + // + // Zero InitData + // - Cdb = (PCDB)Srb.Cdb; - Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC; - Cdb->READ_TOC.StartingTrack = TrackNo; - Cdb->READ_TOC.Format = 0; - Cdb->READ_TOC.AllocationLength[0] = Length >> 8; - Cdb->READ_TOC.AllocationLength[1] = Length & 0xff; - Cdb->READ_TOC.Msf = 1; + RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA)); - return(ScsiClassSendSrbSynchronous(DeviceObject, - &Srb, - Buffer, - Length, - FALSE)); -} + // + // Set sizes + // + InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA); + InitializationData.DeviceExtensionSize = DEVICE_EXTENSION_SIZE; + + InitializationData.DeviceType = FILE_DEVICE_CD_ROM; + InitializationData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE; + + // + // Set entry points + // + + InitializationData.ClassReadWriteVerification = ScsiCdRomReadVerification; + InitializationData.ClassDeviceControl = CdRomDeviceControl; + InitializationData.ClassFindDevices = ScsiCdRomFindDevices; + InitializationData.ClassShutdownFlush = NULL; + InitializationData.ClassCreateClose = NULL; + InitializationData.ClassStartIo = ScsiCdRomStartIo; + + // + // Call the class init routine + // + + return ScsiClassInitialize( DriverObject, RegistryPath, &InitializationData); + +} // end DriverEntry() + + +BOOLEAN +STDCALL +ScsiCdRomFindDevices( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + IN PCLASS_INIT_DATA InitializationData, + IN PDEVICE_OBJECT PortDeviceObject, + IN ULONG PortNumber + ) + +/*++ + +Routine Description: + + Connect to SCSI port driver. Get adapter capabilities and + SCSI bus configuration information. Search inquiry data + for CDROM devices to process. + +Arguments: + + DriverObject - CDROM class driver object. + PortDeviceObject - SCSI port driver device object. + PortNumber - The system ordinal for this scsi adapter. + +Return Value: + + TRUE if CDROM device present on this SCSI adapter. + +--*/ -static NTSTATUS -CdromClassReadLastSession (PDEVICE_OBJECT DeviceObject, - SIZE_T TrackNo, - PVOID Buffer, - SIZE_T Length) { - PDEVICE_EXTENSION DeviceExtension; - SCSI_REQUEST_BLOCK Srb; - PCDB Cdb; + PIO_SCSI_CAPABILITIES portCapabilities; + PULONG cdRomCount; + PCHAR buffer; + PSCSI_INQUIRY_DATA lunInfo; + PSCSI_ADAPTER_BUS_INFO adapterInfo; + PINQUIRYDATA inquiryData; + ULONG scsiBus; + NTSTATUS status; + BOOLEAN foundDevice = FALSE; - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + // + // Call port driver to get adapter capabilities. + // - RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK)); - Srb.CdbLength = 10; - Srb.TimeOutValue = DeviceExtension->TimeOutValue; + status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities); - Cdb = (PCDB)Srb.Cdb; - Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC; - Cdb->READ_TOC.StartingTrack = TrackNo; - Cdb->READ_TOC.Format = 1; - Cdb->READ_TOC.AllocationLength[0] = Length >> 8; - Cdb->READ_TOC.AllocationLength[1] = Length & 0xff; - Cdb->READ_TOC.Msf = 0; + if (!NT_SUCCESS(status)) { + DebugPrint((1,"FindScsiDevices: ScsiClassGetCapabilities failed\n")); + return foundDevice; + } - return(ScsiClassSendSrbSynchronous(DeviceObject, - &Srb, - Buffer, - Length, - FALSE)); -} + // + // Call port driver to get inquiry information to find cdroms. + // + + status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer); + + if (!NT_SUCCESS(status)) { + DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n")); + return foundDevice; + } + + // + // Get the address of the count of the number of cdroms already initialized. + // + + cdRomCount = &IoGetConfigurationInformation()->CdRomCount; + adapterInfo = (PVOID) buffer; + + // + // For each SCSI bus this adapter supports ... + // + + for (scsiBus=0; scsiBus < adapterInfo->NumberOfBuses; scsiBus++) { + + // + // Get the SCSI bus scan data for this bus. + // + + lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset); + + // + // Search list for unclaimed disk devices. + // + + while (adapterInfo->BusData[scsiBus].InquiryDataOffset) { + + inquiryData = (PVOID)lunInfo->InquiryData; + + if ((inquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) && + (inquiryData->DeviceTypeQualifier == 0) && + (!lunInfo->DeviceClaimed)) { + + DebugPrint((1,"FindScsiDevices: Vendor string is %.24s\n", + inquiryData->VendorId)); + + // + // Create device objects for cdrom + // + + status = CreateCdRomDeviceObject(DriverObject, + PortDeviceObject, + PortNumber, + cdRomCount, + portCapabilities, + lunInfo, + InitializationData, + RegistryPath); + + if (NT_SUCCESS(status)) { + + // + // Increment system cdrom device count. + // + + (*cdRomCount)++; + + // + // Indicate that a cdrom device was found. + // + + foundDevice = TRUE; + } + } + + // + // Get next LunInfo. + // + + if (lunInfo->NextInquiryDataOffset == 0) { + break; + } + + lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset); + } + } + + ExFreePool(buffer); -/********************************************************************** - * NAME EXPORTED - * CdromClassDeviceControl - * - * DESCRIPTION: - * Answer requests for device control calls - * - * RUN LEVEL: - * PASSIVE_LEVEL - * - * ARGUMENTS: - * DeviceObject - * Irp - * Standard dispatch arguments - * - * RETURNS: - * Status. - */ + return foundDevice; + +} // end FindScsiCdRoms() + + +VOID +STDCALL +CdRomCreateNamedEvent( + IN PDEVICE_EXTENSION DeviceExtension, + IN ULONG DeviceNumber + ) + +/*++ + +Routine Description: + + Create the named synchronization event for notification of media change + events to the system. The event is reset before this function returns. + +Arguments: + + DeviceExtension - the device extension pointer for storage of the event pointer. + +Return Value: + + None. + +--*/ -NTSTATUS STDCALL -CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) { - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION IrpStack; - ULONG ControlCode, InputLength, OutputLength; - PCDROM_DATA CdromData; - ULONG Information; - NTSTATUS Status; + UNICODE_STRING unicodeString; + OBJECT_ATTRIBUTES objectAttributes; + CCHAR eventNameBuffer[MAXIMUM_FILENAME_LENGTH]; + STRING eventNameString; + HANDLE handle; + NTSTATUS status; - DPRINT("CdromClassDeviceControl() called!\n"); - Status = STATUS_INVALID_DEVICE_REQUEST; - Information = 0; - IrpStack = IoGetCurrentIrpStackLocation(Irp); - ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode; - InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; - OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - CdromData = (PCDROM_DATA)(DeviceExtension + 1); + sprintf(eventNameBuffer,"\\Device\\MediaChangeEvent%d", + DeviceNumber); - switch (ControlCode) - { - case IOCTL_CDROM_GET_DRIVE_GEOMETRY: - DPRINT ("CdromClassDeviceControl: IOCTL_CDROM_GET_DRIVE_GEOMETRY\n"); - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - break; - } - IoMarkIrpPending (Irp); - IoStartPacket (DeviceObject, - Irp, - NULL, - NULL); - return STATUS_PENDING; + RtlInitString(&eventNameString, + eventNameBuffer); - case IOCTL_CDROM_CHECK_VERIFY: - DPRINT ("CdromClassDeviceControl: IOCTL_CDROM_CHECK_VERIFY\n"); - if (OutputLength != 0 && OutputLength < sizeof (ULONG)) - { - DPRINT1("Buffer too small\n"); - Status = STATUS_BUFFER_TOO_SMALL; - break; - } - IoMarkIrpPending (Irp); - IoStartPacket (DeviceObject, - Irp, - NULL, - NULL); - return STATUS_PENDING; + status = RtlAnsiStringToUnicodeString(&unicodeString, + &eventNameString, + TRUE); - case IOCTL_CDROM_READ_TOC: - DPRINT("IOCTL_CDROM_READ_TOC\n"); - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC)) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - } - else - { - PCDROM_TOC TocBuffer; - USHORT Length; - - TocBuffer = Irp->AssociatedIrp.SystemBuffer; - - /* First read the lead out */ - Length = 4 + sizeof(TRACK_DATA); - Status = CdromClassReadTocEntry(DeviceObject, - 0xAA, - TocBuffer, - Length); - if (NT_SUCCESS(Status)) - { - if (TocBuffer->FirstTrack == 0xaa) - { - /* there is an empty cd */ - Information = Length; - } - else - { - /* read the toc */ - Length = 4 + sizeof(TRACK_DATA) * (TocBuffer->LastTrack - TocBuffer->FirstTrack + 2); - Status = CdromClassReadTocEntry(DeviceObject, - TocBuffer->FirstTrack, - TocBuffer, Length); - if (NT_SUCCESS(Status)) - { - Information = Length; - } - } - } - } - break; - - case IOCTL_CDROM_GET_LAST_SESSION: - DPRINT("IOCTL_CDROM_GET_LAST_SESSION\n"); - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < 4 + sizeof(TRACK_DATA)) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - } - else - { - PCDROM_TOC TocBuffer; - USHORT Length; - - TocBuffer = Irp->AssociatedIrp.SystemBuffer; - Length = 4 + sizeof(TRACK_DATA); - Status = CdromClassReadLastSession(DeviceObject, - 0, - TocBuffer, - Length); - if (NT_SUCCESS(Status)) - { - Information = Length; - } - } - break; - - default: - /* Call the common device control function */ - return(ScsiClassDeviceControl(DeviceObject, Irp)); + if (!NT_SUCCESS(status)) { + return; } - /* Verify the device if the user caused the error */ - if (!NT_SUCCESS(Status) && IoIsErrorUserInduced(Status)) - { - IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); - } + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_PERMANENT | OBJ_CASE_INSENSITIVE | OBJ_OPENIF, + NULL, + NULL); - Irp->IoStatus.Status = Status; - Irp->IoStatus.Information = Information; - IoCompleteRequest(Irp, - IO_DISK_INCREMENT); + DeviceExtension->MediaChangeEvent = IoCreateSynchronizationEvent(&unicodeString, + &handle); + DeviceExtension->MediaChangeEventHandle = handle; - return Status; + KeClearEvent(DeviceExtension->MediaChangeEvent); + + RtlFreeUnicodeString(&unicodeString); } + +NTSTATUS +STDCALL +CreateCdRomDeviceObject( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PortDeviceObject, + IN ULONG PortNumber, + IN PULONG DeviceCount, + IN PIO_SCSI_CAPABILITIES PortCapabilities, + IN PSCSI_INQUIRY_DATA LunInfo, + IN PCLASS_INIT_DATA InitializationData, + IN PUNICODE_STRING RegistryPath + ) -/********************************************************************** - * NAME - * CdromClassStartIo - * - * DESCRIPTION: - * Starts IRP processing. - * - * RUN LEVEL: - * PASSIVE_LEVEL - * - * ARGUMENTS: - * DeviceObject - * Irp - * Standard dispatch arguments - * - * RETURNS: - * None. - */ -VOID STDCALL -CdromClassStartIo (IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) +/*++ + +Routine Description: + + This routine creates an object for the device and then calls the + SCSI port driver for media capacity and sector size. + +Arguments: + + DriverObject - Pointer to driver object created by system. + PortDeviceObject - to connect to SCSI port driver. + DeviceCount - Number of previously installed CDROMs. + PortCapabilities - Pointer to structure returned by SCSI port + driver describing adapter capabilites (and limitations). + LunInfo - Pointer to configuration information for this device. + +Return Value: + + NTSTATUS + +--*/ { - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION IrpStack; - PIO_STACK_LOCATION SubIrpStack; - ULONG MaximumTransferLength; - ULONG TransferPages; - PSCSI_REQUEST_BLOCK Srb; - PIRP SubIrp; - PUCHAR SenseBuffer; - PVOID DataBuffer; - PCDB Cdb; + UCHAR ntNameBuffer[64]; + STRING ntNameString; + UNICODE_STRING ntUnicodeString; + NTSTATUS status; + BOOLEAN changerDevice; + SCSI_REQUEST_BLOCK srb; + ULONG length; + PCDROM_DATA cddata; + PCDB cdb; + PVOID senseData = NULL; + PDEVICE_OBJECT deviceObject = NULL; + PDEVICE_EXTENSION deviceExtension = NULL; + PUCHAR buffer; + ULONG bps; + ULONG lastBit; + ULONG timeOut; + BOOLEAN srbListInitialized = FALSE; - DPRINT("CdromClassStartIo() called!\n"); + // + // Claim the device. Note that any errors after this + // will goto the generic handler, where the device will + // be released. + // - IoMarkIrpPending (Irp); + status = ScsiClassClaimDevice(PortDeviceObject, + LunInfo, + FALSE, + &PortDeviceObject); - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - IrpStack = IoGetCurrentIrpStackLocation (Irp); - - MaximumTransferLength = DeviceExtension->PortCapabilities->MaximumTransferLength; - - if (DeviceObject->Flags & DO_VERIFY_VOLUME) - { - if (!(IrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) - { - DPRINT1 ("Verify required\n"); - - if (Irp->Tail.Overlay.Thread) - { - IoSetHardErrorOrVerifyDevice (Irp, - DeviceObject); - } - Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED; - - /* FIXME: Update drive capacity */ - IoCompleteRequest (Irp, - IO_DISK_INCREMENT); - IoStartNextPacket (DeviceObject, - FALSE); - return; - } + if (!NT_SUCCESS(status)) { + return(status); } - if (IrpStack->MajorFunction == IRP_MJ_READ) - { - DPRINT ("CdromClassStartIo: IRP_MJ_READ\n"); + // + // Create device object for this device. + // - TransferPages = - ADDRESS_AND_SIZE_TO_SPAN_PAGES (MmGetMdlVirtualAddress(Irp->MdlAddress), - IrpStack->Parameters.Read.Length); + sprintf(ntNameBuffer, + "\\Device\\CdRom%d", + *DeviceCount); - /* Check transfer size */ - if ((IrpStack->Parameters.Read.Length > MaximumTransferLength) || - (TransferPages > DeviceExtension->PortCapabilities->MaximumPhysicalPages)) - { - /* Transfer size is too large - split it */ - TransferPages = - DeviceExtension->PortCapabilities->MaximumPhysicalPages - 1; + status = ScsiClassCreateDeviceObject(DriverObject, + ntNameBuffer, + NULL, + &deviceObject, + InitializationData); - /* Adjust transfer size */ - if (MaximumTransferLength > TransferPages * PAGE_SIZE) - MaximumTransferLength = TransferPages * PAGE_SIZE; + if (!NT_SUCCESS(status)) { + DebugPrint((1,"CreateCdRomDeviceObjects: Can not create device %s\n", + ntNameBuffer)); - if (MaximumTransferLength == 0) - MaximumTransferLength = PAGE_SIZE; - - /* Split the transfer */ - ScsiClassSplitRequest (DeviceObject, - Irp, - MaximumTransferLength); - return; - } - else - { - /* Build SRB */ - ScsiClassBuildRequest (DeviceObject, - Irp); - } - } - else if (IrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) - { - DPRINT ("CdromClassStartIo: IRP_MJ_IRP_MJ_DEVICE_CONTROL\n"); - - /* Allocate an IRP for sending requests to the port driver */ - SubIrp = IoAllocateIrp ((CCHAR)(DeviceObject->StackSize + 1), - FALSE); - if (SubIrp == NULL) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - IoCompleteRequest (Irp, - IO_DISK_INCREMENT); - IoStartNextPacket (DeviceObject, - FALSE); - return; - } - - /* Allocate an SRB */ - Srb = ExAllocatePool (NonPagedPool, - sizeof (SCSI_REQUEST_BLOCK)); - if (Srb == NULL) - { - IoFreeIrp (SubIrp); - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - IoCompleteRequest (Irp, - IO_DISK_INCREMENT); - IoStartNextPacket (DeviceObject, - FALSE); - return; - } - - /* Allocte a sense buffer */ - SenseBuffer = ExAllocatePool (NonPagedPoolCacheAligned, - SENSE_BUFFER_SIZE); - if (SenseBuffer == NULL) - { - ExFreePool (Srb); - IoFreeIrp (SubIrp); - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - IoCompleteRequest (Irp, - IO_DISK_INCREMENT); - IoStartNextPacket (DeviceObject, - FALSE); - return; - } - - /* Initialize the IRP */ - IoSetNextIrpStackLocation (SubIrp); - SubIrp->IoStatus.Information = 0; - SubIrp->IoStatus.Status = STATUS_SUCCESS; - SubIrp->Flags = 0; - SubIrp->UserBuffer = NULL; - - SubIrpStack = IoGetCurrentIrpStackLocation (SubIrp); - SubIrpStack->DeviceObject = DeviceExtension->DeviceObject; - SubIrpStack->Parameters.Others.Argument2 = (PVOID)Irp; - - /* Initialize next stack location */ - SubIrpStack = IoGetNextIrpStackLocation (SubIrp); - SubIrpStack->MajorFunction = IRP_MJ_SCSI; - SubIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; - SubIrpStack->Parameters.Scsi.Srb = Srb; - - /* Initialize the SRB */ - RtlZeroMemory(Srb, - sizeof (SCSI_REQUEST_BLOCK)); - Srb->Length = sizeof (SCSI_REQUEST_BLOCK); - Srb->PathId = DeviceExtension->PathId; - Srb->TargetId = DeviceExtension->TargetId; - Srb->Lun = DeviceExtension->Lun; - Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - Srb->SrbStatus = SRB_STATUS_SUCCESS; - Srb->ScsiStatus = 0; - Srb->NextSrb = 0; - Srb->OriginalRequest = SubIrp; - Srb->SenseInfoBuffer = SenseBuffer; - Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; - - /* Initialize the CDB */ - Cdb = (PCDB)Srb->Cdb; - - /* Set the completion routine */ - IoSetCompletionRoutine (SubIrp, - CdromDeviceControlCompletion, - Srb, - TRUE, - TRUE, - TRUE); - - switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) - { - case IOCTL_CDROM_GET_DRIVE_GEOMETRY: - DPRINT ("CdromClassStartIo: IOCTL_CDROM_GET_DRIVE_GEOMETRY\n"); - Srb->DataTransferLength = sizeof(READ_CAPACITY_DATA); - Srb->CdbLength = 10; - Srb->TimeOutValue = DeviceExtension->TimeOutValue; - Srb->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN; - Cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; - - /* Allocate data buffer */ - DataBuffer = ExAllocatePool (NonPagedPoolCacheAligned, - sizeof(READ_CAPACITY_DATA)); - if (DataBuffer == NULL) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - IoCompleteRequest (Irp, - IO_DISK_INCREMENT); - ExFreePool (SenseBuffer); - ExFreePool (Srb); - IoFreeIrp (SubIrp); - IoStartNextPacket (DeviceObject, - FALSE); - return; - } - - /* Allocate an MDL for the data buffer */ - SubIrp->MdlAddress = IoAllocateMdl (DataBuffer, - sizeof(READ_CAPACITY_DATA), - FALSE, - FALSE, - NULL); - if (SubIrp->MdlAddress == NULL) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - IoCompleteRequest (Irp, - IO_DISK_INCREMENT); - ExFreePool (DataBuffer); - ExFreePool (SenseBuffer); - ExFreePool (Srb); - IoFreeIrp (SubIrp); - IoStartNextPacket (DeviceObject, - FALSE); - return; - } - - MmBuildMdlForNonPagedPool (SubIrp->MdlAddress); - Srb->DataBuffer = DataBuffer; - - RtlZeroMemory(DataBuffer, sizeof(READ_CAPACITY_DATA)); - IoCallDriver (DeviceExtension->PortDeviceObject, - SubIrp); - return; - - case IOCTL_CDROM_CHECK_VERIFY: - DPRINT ("CdromClassStartIo: IOCTL_CDROM_CHECK_VERIFY\n"); - Srb->CdbLength = 6; - Srb->TimeOutValue = DeviceExtension->TimeOutValue * 2; - Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER; - Cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; - - IoCallDriver (DeviceExtension->PortDeviceObject, - SubIrp); - return; - - default: - IoCompleteRequest (Irp, - IO_NO_INCREMENT); - return; - } + RtlFreeUnicodeString(&ntUnicodeString); + goto CreateCdRomDeviceObjectExit; } - /* Call the SCSI port driver */ - IoCallDriver (DeviceExtension->PortDeviceObject, - Irp); -} + // + // Indicate that IRPs should include MDLs. + // + + deviceObject->Flags |= DO_DIRECT_IO; + + // + // Set up required stack size in device object. + // + + deviceObject->StackSize = PortDeviceObject->StackSize + 2; + + deviceExtension = deviceObject->DeviceExtension; + + // + // Allocate spinlock for split request completion. + // + + KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock); + + // + // This is the physical device. + // + + deviceExtension->PhysicalDevice = deviceObject; + + // + // Initialize lock count to zero. The lock count is used to + // disable the ejection mechanism when media is mounted. + // + + deviceExtension->LockCount = 0; + + // + // Save system cdrom number + // + + deviceExtension->DeviceNumber = *DeviceCount; + + // + // Copy port device object to device extension. + // + + deviceExtension->PortDeviceObject = PortDeviceObject; + + // + // Set the alignment requirements for the device based on the + // host adapter requirements + // + + if (PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) { + deviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement; + } + + // + // Save address of port driver capabilities. + // + + deviceExtension->PortCapabilities = PortCapabilities; + + // + // Clear SRB flags. + // + + deviceExtension->SrbFlags = 0; + deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Allocate request sense buffer. + // + + senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE); + + if (senseData == NULL) { + + // + // The buffer cannot be allocated. + // + + status = STATUS_INSUFFICIENT_RESOURCES; + goto CreateCdRomDeviceObjectExit; + } + + // + // Set the sense data pointer in the device extension. + // + + deviceExtension->SenseData = senseData; + + // + // CDROMs are not partitionable so starting offset is 0. + // + + deviceExtension->StartingOffset.LowPart = 0; + deviceExtension->StartingOffset.HighPart = 0; + + // + // Path/TargetId/LUN describes a device location on the SCSI bus. + // This information comes from the LunInfo buffer. + // + + deviceExtension->PortNumber = (UCHAR)PortNumber; + deviceExtension->PathId = LunInfo->PathId; + deviceExtension->TargetId = LunInfo->TargetId; + deviceExtension->Lun = LunInfo->Lun; + + // + // Set timeout value in seconds. + // + + timeOut = ScsiClassQueryTimeOutRegistryValue(RegistryPath); + if (timeOut) { + deviceExtension->TimeOutValue = timeOut; + } else { + deviceExtension->TimeOutValue = SCSI_CDROM_TIMEOUT; + } + + // + // Build the lookaside list for srb's for the physical disk. Should only + // need a couple. + // + + ScsiClassInitializeSrbLookasideList(deviceExtension, + CDROM_SRB_LIST_SIZE); + + srbListInitialized = TRUE; + + // + // Back pointer to device object. + // + + deviceExtension->DeviceObject = deviceObject; + + // + // Allocate buffer for drive geometry. + // + + deviceExtension->DiskGeometry = + ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY)); + + if (deviceExtension->DiskGeometry == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto CreateCdRomDeviceObjectExit; + } + + // + // Set up media change support defaults. + // + + cddata = (PCDROM_DATA)(deviceExtension + 1); + + KeInitializeSpinLock(&cddata->FormSpinLock); + KeInitializeSpinLock(&cddata->TimerIrpSpinLock); + InitializeListHead(&cddata->TimerIrpList); + + cddata->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME; + cddata->MediaChangeSupported = FALSE; + cddata->MediaChange = FALSE; + + // + // Assume that there is initially no media in the device + // only notify upper layers if there is something there + // + + deviceExtension->MediaChangeNoMedia = TRUE; + cddata->MediaChangeIrp = NULL; +#ifdef DBG + cddata->MediaChangeIrpTimeInUse = 0; + cddata->MediaChangeIrpLost = FALSE; +#endif + + // + // Scan for Scsi controllers that require special processing. + // + + ScanForSpecial(deviceObject, + (PINQUIRYDATA) LunInfo->InquiryData, + PortCapabilities); + + // + // Do READ CAPACITY. This SCSI command + // returns the last sector address on the device + // and the bytes per sector. + // These are used to calculate the drive capacity + // in bytes. + // + + status = ScsiClassReadDriveCapacity(deviceObject); + bps = deviceExtension->DiskGeometry->BytesPerSector; + + if (!NT_SUCCESS(status) || !bps) { + + DebugPrint((1, + "CreateCdRomDeviceObjects: Can't read capacity for device %s\n", + ntNameBuffer)); + + // + // Set disk geometry to default values (per ISO 9660). + // + + bps = 2048; + deviceExtension->SectorShift = 11; + deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff); + } else { + + // + // Insure that bytes per sector is a power of 2 + // This corrects a problem with the HP 4020i CDR where it + // returns an incorrect number for bytes per sector. + // + + lastBit = (ULONG) -1; + while (bps) { + lastBit++; + bps = bps >> 1; + } + + bps = 1 << lastBit; + } + deviceExtension->DiskGeometry->BytesPerSector = bps; + DebugPrint((2, "CreateCdRomDeviceObject: Calc'd bps = %x\n", bps)); + + // + // Check to see if this is some sort of changer device + // + + changerDevice = FALSE; + + // + // Search for devices that have special requirements for media + // change support. + // + + if (deviceExtension->Lun > 0) { + changerDevice = TRUE; + } + + if (!changerDevice) { + changerDevice = IsThisASanyo(deviceObject, deviceExtension->PathId, + deviceExtension->TargetId); + } + + if (!changerDevice) { + ULONG tmp; + changerDevice = IsThisAnAtapiChanger(deviceObject, &tmp); + } + + if (!changerDevice) { + changerDevice = IsThisAMultiLunDevice(deviceObject, PortDeviceObject); + } + + // + // If it is a changer device, increment the timeout to take platter-swapping + // time into account + // + + if(changerDevice) { + deviceExtension->TimeOutValue += SCSI_CHANGER_BONUS_TIMEOUT; + } + + // + // Create the media change named event. If this succeeds then continue + // initializing the media change support data items. + // + + CdRomCreateNamedEvent(deviceExtension,*DeviceCount); + if (deviceExtension->MediaChangeEvent) { + + // + // If this is not a changer, get an IRP for the timer request + // and initialize the timer. + // + + if (!changerDevice) { + + // + // Not a changer device - continue with media change initialization. + // Determine if the user actually wants media change events. + // + + if (CdRomCheckRegistryForMediaChangeValue(RegistryPath, *DeviceCount)) { + PIO_STACK_LOCATION irpStack; + PSCSI_REQUEST_BLOCK srb; + PIRP irp; + + // + // User wants it - preallocate IRP and SRB. + // + + irp = IoAllocateIrp((CCHAR)(deviceObject->StackSize+1), + FALSE); + if (irp) { + PVOID buffer; + + srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK)); + buffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE); + + if (srb && buffer) { + PCDB cdb; + + // + // All resources have been allocated set up the IRP. + // + + IoSetNextIrpStackLocation(irp); + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->DeviceObject = deviceObject; + irpStack = IoGetNextIrpStackLocation(irp); + cddata->MediaChangeIrp = irp; + irpStack->Parameters.Scsi.Srb = srb; + + // + // Initialize the SRB + // + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb->CdbLength = 6; + srb->TimeOutValue = deviceExtension->TimeOutValue * 2; + srb->QueueTag = SP_UNTAGGED; + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Initialize and set up the sense information buffer + // + + RtlZeroMemory(buffer, SENSE_BUFFER_SIZE); + srb->SenseInfoBuffer = buffer; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Initialize the CDB + // + + cdb = (PCDB)&srb->Cdb[0]; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + cdb->CDB6GENERIC.LogicalUnitNumber = deviceExtension->Lun; + + // + // It is ok to support media change events on this device. + // + + cddata->MediaChangeSupported = TRUE; + cddata->MediaChange = TRUE; + + } else { + + if (srb) { + ExFreePool(srb); + } + if (buffer) { + ExFreePool(buffer); + } + IoFreeIrp(irp); + } + } + } else { + deviceExtension->MediaChangeEvent = NULL; + } + } else { + deviceExtension->MediaChangeEvent = NULL; + } + } + + // + // Assume use of 6-byte mode sense/select for now. + // + + cddata->XAFlags |= XA_USE_6_BYTE; + + // + // Build and issue mode sense with Read error recovery page. This will be used to change + // block size in case of any raw reads (Mode 2, Form 2). + // + + length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH); + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = deviceExtension->TimeOutValue; + + // + // Build the MODE SENSE CDB. The data returned will be kept in the device extension + // and used to set block size. + // + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = 0x1; + cdb->MODE_SENSE.AllocationLength = (UCHAR)length; + + buffer = ExAllocatePool(NonPagedPoolCacheAligned, (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH10)); + if (!buffer) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto CreateCdRomDeviceObjectExit; + } + + status = ScsiClassSendSrbSynchronous(deviceObject, + &srb, + buffer, + length, + FALSE); + if (!NT_SUCCESS(status)) { + + // + // May be Atapi, try 10-byte. + // + + length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH10); + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Build the MODE SENSE CDB. + // + + srb.CdbLength = 10; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = deviceExtension->TimeOutValue; + + cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; + cdb->MODE_SENSE10.PageCode = 0x1; + + cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(length >> 8); + cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(length & 0xFF); + + status = ScsiClassSendSrbSynchronous(deviceObject, + &srb, + buffer, + length, + FALSE); + if (status == STATUS_DATA_OVERRUN) { + + // + // Build and issue the ReadCd command to ensure that this device supports it. + // + + RtlZeroMemory(cdb, 12); + + cdb->READ_CD.OperationCode = SCSIOP_READ_CD; + + status = ScsiClassSendSrbSynchronous(deviceObject, + &srb, + NULL, + 0, + FALSE); + + // + // If the command wasn't rejected then support the READ_CD. + // + + if (NT_SUCCESS(status) || (status == STATUS_NO_MEDIA_IN_DEVICE)) { + + // + // Using Read CD precludes issueing a mode select to + // set the user data size. So, no buffer copy is + // necessary. + // + + cddata->XAFlags &= ~XA_USE_6_BYTE; + cddata->XAFlags = XA_USE_READ_CD | XA_USE_10_BYTE; + } else { + + RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA10)); + cddata->u1.Header.ModeDataLength = 0; + + cddata->XAFlags &= ~XA_USE_6_BYTE; + cddata->XAFlags |= XA_USE_10_BYTE; + } + + } else if (NT_SUCCESS(status)) { + + RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA10)); + cddata->u1.Header.ModeDataLength = 0; + + cddata->XAFlags &= ~XA_USE_6_BYTE; + cddata->XAFlags |= XA_USE_10_BYTE; + + } else { + cddata->XAFlags |= XA_NOT_SUPPORTED; + } + } else { + RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA)); + cddata->u1.Header.ModeDataLength = 0; + } + + ExFreePool(buffer); + + // + // Start the timer now regardless of if Autorun is enabled. + // The timer must run forever since IoStopTimer faults. + // + + IoInitializeTimer(deviceObject, CdRomTickHandler, NULL); + IoStartTimer(deviceObject); + + return(STATUS_SUCCESS); + +CreateCdRomDeviceObjectExit: + + // + // Release the device since an error occured. + // + + ScsiClassClaimDevice(PortDeviceObject, + LunInfo, + TRUE, + NULL); + + if (senseData != NULL) { + ExFreePool(senseData); + } + + if (deviceExtension->DiskGeometry != NULL) { + ExFreePool(deviceExtension->DiskGeometry); + } + + if (deviceObject != NULL) { + if (srbListInitialized) { + ExDeleteNPagedLookasideList(&deviceExtension->SrbLookasideListHead); + } + IoDeleteDevice(deviceObject); + } -NTSTATUS STDCALL -CdromDeviceControlCompletion (IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context) + return status; + +} // end CreateCdRomDeviceObject() + + +VOID +STDCALL +ScsiCdRomStartIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) { - PDEVICE_EXTENSION DeviceExtension; - PDEVICE_EXTENSION PhysicalExtension; - PIO_STACK_LOCATION IrpStack; - PIO_STACK_LOCATION OrigCurrentIrpStack; - PIO_STACK_LOCATION OrigNextIrpStack; - PSCSI_REQUEST_BLOCK Srb; - PIRP OrigIrp; - BOOLEAN Retry; - NTSTATUS Status; - DPRINT ("CdromDeviceControlCompletion() called\n"); + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PIO_STACK_LOCATION irpStack; + PIRP irp2 = NULL; + ULONG transferPages; + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + ULONG maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength; + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + PCDROM_DATA cdData; + PSCSI_REQUEST_BLOCK srb = NULL; + PCDB cdb; + PUCHAR senseBuffer = NULL; + PVOID dataBuffer; + NTSTATUS status; + BOOLEAN use6Byte; - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - PhysicalExtension = (PDEVICE_EXTENSION)DeviceExtension->PhysicalDevice->DeviceExtension; - Srb = (PSCSI_REQUEST_BLOCK) Context; + // + // Mark IRP with status pending. + // - IrpStack = IoGetCurrentIrpStackLocation (Irp); + IoMarkIrpPending(Irp); - /* Get the original IRP */ - OrigIrp = (PIRP)IrpStack->Parameters.Others.Argument2; - OrigCurrentIrpStack = IoGetCurrentIrpStackLocation (OrigIrp); - OrigNextIrpStack = IoGetNextIrpStackLocation (OrigIrp); + // + // If the flag is set in the device object, force a verify. + // - if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) - { - Status = STATUS_SUCCESS; - } - else - { - DPRINT ("SrbStatus %lx\n", Srb->SrbStatus); + if (DeviceObject->Flags & DO_VERIFY_VOLUME) { + DebugPrint((2, "ScsiCdRomStartIo: [%lx] Volume needs verified\n", Irp)); + if (!(currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) { - /* Interpret sense info */ - Retry = ScsiClassInterpretSenseInfo (DeviceObject, - Srb, - IrpStack->MajorFunction, - IrpStack->Parameters.DeviceIoControl.IoControlCode, - MAXIMUM_RETRIES - (ULONG)OrigNextIrpStack->Parameters.Others.Argument1, - &Status); - DPRINT ("Retry %u\n", Retry); + if (Irp->Tail.Overlay.Thread) { + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + } - if (Retry == TRUE && - (ULONG)OrigNextIrpStack->Parameters.Others.Argument1 > 0) - { - DPRINT1 ("Try again (Retry count 0x%p)\n", - (ULONG)OrigNextIrpStack->Parameters.Others.Argument1); + Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED; - OrigNextIrpStack->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)OrigNextIrpStack->Parameters.Others.Argument1 - 1); + DebugPrint((2, "ScsiCdRomStartIo: [%lx] Calling UpdateCapcity - " + "ioctl event = %lx\n", + Irp, + nextIrpStack->Parameters.Others.Argument1 + )); - /* Release 'old' buffers */ - ExFreePool (Srb->SenseInfoBuffer); - if (Srb->DataBuffer) - ExFreePool(Srb->DataBuffer); - ExFreePool(Srb); + // + // our device control dispatch routine stores an event in the next + // stack location to signal when startio has completed. We need to + // pass this in so that the update capacity completion routine can + // set it rather than completing the Irp. + // - if (Irp->MdlAddress != NULL) - IoFreeMdl(Irp->MdlAddress); + status = CdRomUpdateCapacity(deviceExtension, + Irp, + nextIrpStack->Parameters.Others.Argument1 + ); - IoFreeIrp(Irp); - - /* Call the StartIo routine again */ - CdromClassStartIo (DeviceObject, - OrigIrp); - - return STATUS_MORE_PROCESSING_REQUIRED; - } - - DPRINT ("Status %lx\n", Status); + DebugPrint((2, "ScsiCdRomStartIo: [%lx] UpdateCapacity returned %lx\n", Irp, status)); + ASSERT(status == STATUS_PENDING); + return; + } } - if (NT_SUCCESS (Status)) - { - switch (OrigCurrentIrpStack->Parameters.DeviceIoControl.IoControlCode) - { - case IOCTL_CDROM_GET_DRIVE_GEOMETRY: - { - PREAD_CAPACITY_DATA CapacityBuffer; - ULONG LastSector; - ULONG SectorSize; + cdData = (PCDROM_DATA)(deviceExtension + 1); + use6Byte = cdData->XAFlags & XA_USE_6_BYTE; - DPRINT ("CdromClassControlCompletion: IOCTL_CDROM_GET_DRIVE_GEOMETRY\n"); + if (currentIrpStack->MajorFunction == IRP_MJ_READ) { - CapacityBuffer = (PREAD_CAPACITY_DATA)Srb->DataBuffer; - SectorSize = (((PUCHAR)&CapacityBuffer->BytesPerBlock)[0] << 24) | - (((PUCHAR)&CapacityBuffer->BytesPerBlock)[1] << 16) | - (((PUCHAR)&CapacityBuffer->BytesPerBlock)[2] << 8) | - ((PUCHAR)&CapacityBuffer->BytesPerBlock)[3]; + // + // Add partition byte offset to make starting byte relative to + // beginning of disk. In addition, add in skew for DM Driver, if any. + // - LastSector = (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[0] << 24) | - (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[1] << 16) | - (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[2] << 8) | - ((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[3]; + currentIrpStack->Parameters.Read.ByteOffset.QuadPart += (deviceExtension->StartingOffset.QuadPart); - if (SectorSize == 0) - SectorSize = 2048; - DeviceExtension->DiskGeometry->BytesPerSector = SectorSize; + // + // Calculate number of pages in this transfer. + // - DeviceExtension->PartitionLength.QuadPart = (LONGLONG)(LastSector + 1); - WHICH_BIT(DeviceExtension->DiskGeometry->BytesPerSector, - DeviceExtension->SectorShift); - DeviceExtension->PartitionLength.QuadPart = - (DeviceExtension->PartitionLength.QuadPart << DeviceExtension->SectorShift); + transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress), + currentIrpStack->Parameters.Read.Length); - if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - { - DeviceExtension->DiskGeometry->MediaType = RemovableMedia; - } - else - { - DeviceExtension->DiskGeometry->MediaType = FixedMedia; - } - DeviceExtension->DiskGeometry->Cylinders.QuadPart = - (LONGLONG)((LastSector + 1)/(32 * 64)); - DeviceExtension->DiskGeometry->SectorsPerTrack = 32; - DeviceExtension->DiskGeometry->TracksPerCylinder = 64; + // + // Check if request length is greater than the maximum number of + // bytes that the hardware can transfer. + // - RtlCopyMemory (OrigIrp->AssociatedIrp.SystemBuffer, - DeviceExtension->DiskGeometry, - sizeof(DISK_GEOMETRY)); - OrigIrp->IoStatus.Information = sizeof(DISK_GEOMETRY); - } - break; + if (cdData->RawAccess) { - case IOCTL_CDROM_CHECK_VERIFY: - DPRINT ("CdromDeviceControlCompletion: IOCTL_CDROM_CHECK_VERIFY\n"); - if (OrigCurrentIrpStack->Parameters.DeviceIoControl.OutputBufferLength != 0) - { - /* Return the media change counter */ - *((PULONG)(OrigIrp->AssociatedIrp.SystemBuffer)) = - PhysicalExtension->MediaChangeCount; - OrigIrp->IoStatus.Information = sizeof(ULONG); - } - else - { - OrigIrp->IoStatus.Information = 0; - } - break; + ASSERT(!(cdData->XAFlags & XA_USE_READ_CD)); - default: - OrigIrp->IoStatus.Information = 0; - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - } + // + // Fire off a mode select to switch back to cooked sectors. + // + + irp2 = IoAllocateIrp((CCHAR)(deviceExtension->DeviceObject->StackSize+1), + FALSE); + + if (!irp2) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK)); + if (!srb) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + cdb = (PCDB)srb->Cdb; + + // + // Allocate sense buffer. + // + + senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE); + + if (!senseBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + // + // Set up the irp. + // + + IoSetNextIrpStackLocation(irp2); + irp2->IoStatus.Status = STATUS_SUCCESS; + irp2->IoStatus.Information = 0; + irp2->Flags = 0; + irp2->UserBuffer = NULL; + + // + // Save the device object and irp in a private stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(irp2); + irpStack->DeviceObject = deviceExtension->DeviceObject; + irpStack->Parameters.Others.Argument2 = (PVOID) Irp; + + // + // The retry count will be in the real Irp, as the retry logic will + // recreate our private irp. + // + + if (!(nextIrpStack->Parameters.Others.Argument1)) { + + // + // Only jam this in if it doesn't exist. The completion routines can + // call StartIo directly in the case of retries and resetting it will + // cause infinite loops. + // + + nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + } + + // + // Construct the IRP stack for the lower level driver. + // + + irpStack = IoGetNextIrpStackLocation(irp2); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Cdb[1] |= deviceExtension->Lun << 5; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->OriginalRequest = irp2; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + srb->SenseInfoBuffer = senseBuffer; + + transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10); + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount ); + if (!dataBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + + // + // Set the new block size in the descriptor. + // + + cdData->u1.BlockDescriptor.BlockLength[0] = (UCHAR)(COOKED_SECTOR_SIZE >> 16) & 0xFF; + cdData->u1.BlockDescriptor.BlockLength[1] = (UCHAR)(COOKED_SECTOR_SIZE >> 8) & 0xFF; + cdData->u1.BlockDescriptor.BlockLength[2] = (UCHAR)(COOKED_SECTOR_SIZE & 0xFF); + + // + // Move error page into dataBuffer. + // + + RtlCopyMemory(srb->DataBuffer, &cdData->u1.Header, transferByteCount); + + // + // Build and send a mode select to switch into raw mode. + // + + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_OUT); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = deviceExtension->TimeOutValue * 2; + + if (use6Byte) { + srb->CdbLength = 6; + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)transferByteCount; + } else { + + srb->CdbLength = 10; + cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; + cdb->MODE_SELECT10.PFBit = 1; + cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(transferByteCount >> 8); + cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(transferByteCount & 0xFF); + } + + // + // Update completion routine. + // + + IoSetCompletionRoutine(irp2, + CdRomSwitchModeCompletion, + srb, + TRUE, + TRUE, + TRUE); + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + } + + if ((currentIrpStack->Parameters.Read.Length > maximumTransferLength) || + (transferPages > + deviceExtension->PortCapabilities->MaximumPhysicalPages)) { + + // + // Request needs to be split. Completion of each portion of the + // request will fire off the next portion. The final request will + // signal Io to send a new request. + // + + transferPages = + deviceExtension->PortCapabilities->MaximumPhysicalPages - 1; + + if(maximumTransferLength > transferPages << PAGE_SHIFT) { + maximumTransferLength = transferPages << PAGE_SHIFT; + } + + // + // Check that the maximum transfer size is not zero + // + + if(maximumTransferLength == 0) { + maximumTransferLength = PAGE_SIZE; + } + + ScsiClassSplitRequest(DeviceObject, Irp, maximumTransferLength); + return; + + } else { + + // + // Build SRB and CDB for this IRP. + // + + ScsiClassBuildRequest(DeviceObject, Irp); + + } + + + } else if (currentIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + + // + // Allocate an irp, srb and associated structures. + // + + irp2 = IoAllocateIrp((CCHAR)(deviceExtension->DeviceObject->StackSize+1), + FALSE); + + if (!irp2) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + IoStartNextPacket(DeviceObject, FALSE); + DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__)); + return; + } + + srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK)); + if (!srb) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__)); + return; + } + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + cdb = (PCDB)srb->Cdb; + + // + // Allocate sense buffer. + // + + senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE); + + if (!senseBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__)); + return; + } + + // + // Set up the irp. + // + + IoSetNextIrpStackLocation(irp2); + irp2->IoStatus.Status = STATUS_SUCCESS; + irp2->IoStatus.Information = 0; + irp2->Flags = 0; + irp2->UserBuffer = NULL; + + // + // Save the device object and irp in a private stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(irp2); + irpStack->DeviceObject = deviceExtension->DeviceObject; + irpStack->Parameters.Others.Argument2 = (PVOID) Irp; + + // + // The retry count will be in the real Irp, as the retry logic will + // recreate our private irp. + // + + if (!(nextIrpStack->Parameters.Others.Argument1)) { + + // + // Only jam this in if it doesn't exist. The completion routines can + // call StartIo directly in the case of retries and resetting it will + // cause infinite loops. + // + + nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + } + + // + // Construct the IRP stack for the lower level driver. + // + + irpStack = IoGetNextIrpStackLocation(irp2); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + + IoSetCompletionRoutine(irp2, + CdRomDeviceControlCompletion, + srb, + TRUE, + TRUE, + TRUE); + // + // Setup those fields that are generic to all requests. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Cdb[1] |= deviceExtension->Lun << 5; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->OriginalRequest = irp2; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + srb->SenseInfoBuffer = senseBuffer; + + switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_CDROM_RAW_READ: { + + // + // Determine whether the drive is currently in raw or cooked mode, + // and which command to use to read the data. + // + + if (!(cdData->XAFlags & XA_USE_READ_CD)) { + + PRAW_READ_INFO rawReadInfo = + (PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; + ULONG maximumTransferLength; + ULONG transferPages; + + if (cdData->RawAccess) { + + ULONG startingSector; + UCHAR min, sec, frame; + + // + // Free the recently allocated irp, as we don't need it. + // + + IoFreeIrp(irp2); + + cdb = (PCDB)srb->Cdb; + RtlZeroMemory(cdb, 12); + + // + // Calculate starting offset. + // + + startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift); + transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE; + maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength; + transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress), + transferByteCount); + + // + // Determine if request is within limits imposed by miniport. + // + + if (transferByteCount > maximumTransferLength || + transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages) { + + // + // The claim is that this won't happen, and is backed up by + // ActiveMovie usage, which does unbuffered XA reads of 0x18000, yet + // we get only 4 sector requests. + // + + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoStartNextPacket(DeviceObject, FALSE); + return; + + } + + srb->OriginalRequest = Irp; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->CdbLength = 10; + srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); + + if (rawReadInfo->TrackMode == CDDA) { + if (cdData->XAFlags & PLEXTOR_CDDA) { + + srb->CdbLength = 12; + + cdb->PLXTR_READ_CDDA.LogicalUnitNumber = deviceExtension->Lun; + cdb->PLXTR_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->PLXTR_READ_CDDA.TransferBlockByte3 = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->PLXTR_READ_CDDA.TransferBlockByte2 = (UCHAR) (rawReadInfo->SectorCount >> 8); + cdb->PLXTR_READ_CDDA.TransferBlockByte1 = 0; + cdb->PLXTR_READ_CDDA.TransferBlockByte0 = 0; + + cdb->PLXTR_READ_CDDA.SubCode = 0; + cdb->PLXTR_READ_CDDA.OperationCode = 0xD8; + + } else if (cdData->XAFlags & NEC_CDDA) { + + cdb->NEC_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->NEC_READ_CDDA.TransferBlockByte1 = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->NEC_READ_CDDA.TransferBlockByte0 = (UCHAR) (rawReadInfo->SectorCount >> 8); + + cdb->NEC_READ_CDDA.OperationCode = 0xD4; + } + } else { + + cdb->CDB10.LogicalUnitNumber = deviceExtension->Lun; + + cdb->CDB10.TransferBlocksMsb = (UCHAR) (rawReadInfo->SectorCount >> 8); + cdb->CDB10.TransferBlocksLsb = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + + cdb->CDB10.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->CDB10.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->CDB10.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->CDB10.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->CDB10.OperationCode = SCSIOP_READ; + } + + srb->SrbStatus = srb->ScsiStatus = 0; + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + nextIrpStack->Parameters.Scsi.Srb = srb; + + if (!(nextIrpStack->Parameters.Others.Argument1)) { + + // + // Only jam this in if it doesn't exist. The completion routines can + // call StartIo directly in the case of retries and resetting it will + // cause infinite loops. + // + + nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + } + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, + CdRomXACompletion, + srb, + TRUE, + TRUE, + TRUE); + + IoCallDriver(deviceExtension->PortDeviceObject, Irp); + return; + + } else { + + transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10); + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount ); + if (!dataBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + + // + // Set the new block size in the descriptor. + // + + cdData->u1.BlockDescriptor.BlockLength[0] = (UCHAR)(RAW_SECTOR_SIZE >> 16) & 0xFF; + cdData->u1.BlockDescriptor.BlockLength[1] = (UCHAR)(RAW_SECTOR_SIZE >> 8) & 0xFF; + cdData->u1.BlockDescriptor.BlockLength[2] = (UCHAR)(RAW_SECTOR_SIZE & 0xFF); + + + // + // TODO: Set density code, based on operation + // + + cdData->u1.BlockDescriptor.DensityCode = 0; + + + // + // Move error page into dataBuffer. + // + + RtlCopyMemory(srb->DataBuffer, &cdData->u1.Header, transferByteCount); + + + // + // Build and send a mode select to switch into raw mode. + // + + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_OUT); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = deviceExtension->TimeOutValue * 2; + + if (use6Byte) { + srb->CdbLength = 6; + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)transferByteCount; + } else { + + srb->CdbLength = 10; + cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; + cdb->MODE_SELECT10.PFBit = 1; + cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(transferByteCount >> 8); + cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(transferByteCount & 0xFF); + } + + // + // Update completion routine. + // + + IoSetCompletionRoutine(irp2, + CdRomSwitchModeCompletion, + srb, + TRUE, + TRUE, + TRUE); + + } + + } else { + + PRAW_READ_INFO rawReadInfo = + (PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; + ULONG startingSector; + + // + // Free the recently allocated irp, as we don't need it. + // + + IoFreeIrp(irp2); + + cdb = (PCDB)srb->Cdb; + RtlZeroMemory(cdb, 12); + + + // + // Calculate starting offset. + // + + startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift); + transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE; + + + srb->OriginalRequest = Irp; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); + srb->CdbLength = 12; + srb->SrbStatus = srb->ScsiStatus = 0; + + // + // Fill in CDB fields. + // + + cdb = (PCDB)srb->Cdb; + + + cdb->READ_CD.TransferBlocks[2] = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->READ_CD.TransferBlocks[1] = (UCHAR) (rawReadInfo->SectorCount >> 8 ); + cdb->READ_CD.TransferBlocks[0] = (UCHAR) (rawReadInfo->SectorCount >> 16); + + + cdb->READ_CD.StartingLBA[3] = (UCHAR) (startingSector & 0xFF); + cdb->READ_CD.StartingLBA[2] = (UCHAR) ((startingSector >> 8)); + cdb->READ_CD.StartingLBA[1] = (UCHAR) ((startingSector >> 16)); + cdb->READ_CD.StartingLBA[0] = (UCHAR) ((startingSector >> 24)); + + // + // Setup cdb depending upon the sector type we want. + // + + switch (rawReadInfo->TrackMode) { + case CDDA: + + cdb->READ_CD.ExpectedSectorType = CD_DA_SECTOR; + cdb->READ_CD.IncludeUserData = 1; + cdb->READ_CD.HeaderCode = 3; + cdb->READ_CD.IncludeSyncData = 1; + break; + + case YellowMode2: + + cdb->READ_CD.ExpectedSectorType = YELLOW_MODE2_SECTOR; + cdb->READ_CD.IncludeUserData = 1; + cdb->READ_CD.HeaderCode = 1; + cdb->READ_CD.IncludeSyncData = 1; + break; + + case XAForm2: + + cdb->READ_CD.ExpectedSectorType = FORM2_MODE2_SECTOR; + cdb->READ_CD.IncludeUserData = 1; + cdb->READ_CD.HeaderCode = 3; + cdb->READ_CD.IncludeSyncData = 1; + break; + + default: + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__)); + return; + } + + cdb->READ_CD.OperationCode = SCSIOP_READ_CD; + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + nextIrpStack->Parameters.Scsi.Srb = srb; + + if (!(nextIrpStack->Parameters.Others.Argument1)) { + + // + // Only jam this in if it doesn't exist. The completion routines can + // call StartIo directly in the case of retries and resetting it will + // cause infinite loops. + // + + nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + } + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, + CdRomXACompletion, + srb, + TRUE, + TRUE, + TRUE); + + IoCallDriver(deviceExtension->PortDeviceObject, Irp); + return; + + } + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_GET_DRIVE_GEOMETRY: { + + // + // Issue ReadCapacity to update device extension + // with information for current media. + // + + DebugPrint((3, + "CdRomStartIo: Get drive capacity\n")); + + // + // setup remaining srb and cdb parameters. + // + + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN); + srb->DataTransferLength = sizeof(READ_CAPACITY_DATA); + srb->CdbLength = 10; + srb->TimeOutValue = deviceExtension->TimeOutValue; + + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(READ_CAPACITY_DATA)); + if (!dataBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__)); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + sizeof(READ_CAPACITY_DATA), + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__)); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_CHECK_VERIFY: { + + // + // Since a test unit ready is about to be performed, reset the timer + // value to decrease the opportunities for it to race with this code. + // + + cdData->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME; + + // + // Set up the SRB/CDB + // + + srb->CdbLength = 6; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + srb->TimeOutValue = deviceExtension->TimeOutValue * 2; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER); + + DebugPrint((2, "ScsiCdRomStartIo: [%lx] Sending CHECK_VERIFY irp %lx\n", Irp, irp2)); + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_GET_LAST_SESSION: + + // + // Set format to return first and last session numbers. + // + + cdb->READ_TOC.Format = GET_LAST_SESSION; + + // + // Fall through to READ TOC code. + // + + case IOCTL_CDROM_READ_TOC: { + + + if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) { + + // + // Use MSF addressing if not request for session information. + // + + cdb->READ_TOC.Msf = CDB_USE_MSF; + } + + // + // Set size of TOC structure. + // + + transferByteCount = + currentIrpStack->Parameters.Read.Length > + sizeof(CDROM_TOC) ? sizeof(CDROM_TOC): + currentIrpStack->Parameters.Read.Length; + + cdb->READ_TOC.AllocationLength[0] = (UCHAR) (transferByteCount >> 8); + cdb->READ_TOC.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF); + + cdb->READ_TOC.Control = 0; + + // + // Start at beginning of disc. + // + + cdb->READ_TOC.StartingTrack = 0; + + // + // setup remaining srb and cdb parameters. + // + + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->CdbLength = 10; + srb->TimeOutValue = deviceExtension->TimeOutValue; + + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount); + if (!dataBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC; + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_PLAY_AUDIO_MSF: { + + PCDROM_PLAY_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Set up the SRB/CDB + // + + srb->CdbLength = 10; + cdb->PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF; + + cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM; + cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS; + cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF; + + cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM; + cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS; + cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF; + + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_READ_Q_CHANNEL: { + + PSUB_Q_CHANNEL_DATA userChannelData = + Irp->AssociatedIrp.SystemBuffer; + PCDROM_SUB_Q_DATA_FORMAT inputBuffer = + Irp->AssociatedIrp.SystemBuffer; + + // + // Allocate buffer for subq channel information. + // + + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, + sizeof(SUB_Q_CHANNEL_DATA)); + + if (!dataBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + transferByteCount, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + + srb->DataBuffer = dataBuffer; + + // + // Always logical unit 0, but only use MSF addressing + // for IOCTL_CDROM_CURRENT_POSITION + // + + if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION) + cdb->SUBCHANNEL.Msf = CDB_USE_MSF; + + // + // Return subchannel data + // + + cdb->SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK; + + // + // Specify format of informatin to return + // + + cdb->SUBCHANNEL.Format = inputBuffer->Format; + + // + // Specify which track to access (only used by Track ISRC reads) + // + + if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC) { + cdb->SUBCHANNEL.TrackNumber = inputBuffer->Track; + } + + // + // Set size of channel data -- however, this is dependent on + // what information we are requesting (which Format) + // + + switch( inputBuffer->Format ) { + + case IOCTL_CDROM_CURRENT_POSITION: + transferByteCount = sizeof(SUB_Q_CURRENT_POSITION); + break; + + case IOCTL_CDROM_MEDIA_CATALOG: + transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER); + break; + + case IOCTL_CDROM_TRACK_ISRC: + transferByteCount = sizeof(SUB_Q_TRACK_ISRC); + break; + } + + cdb->SUBCHANNEL.AllocationLength[0] = (UCHAR) (transferByteCount >> 8); + cdb->SUBCHANNEL.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF); + cdb->SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->CdbLength = 10; + srb->TimeOutValue = deviceExtension->TimeOutValue; + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_PAUSE_AUDIO: { + + cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME; + cdb->PAUSE_RESUME.Action = CDB_AUDIO_PAUSE; + + srb->CdbLength = 10; + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_RESUME_AUDIO: { + + cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME; + cdb->PAUSE_RESUME.Action = CDB_AUDIO_RESUME; + + srb->CdbLength = 10; + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_SEEK_AUDIO_MSF: { + + PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG logicalBlockAddress; + + logicalBlockAddress = MSF_TO_LBA(inputBuffer->M, inputBuffer->S, inputBuffer->F); + + cdb->SEEK.OperationCode = SCSIOP_SEEK; + cdb->SEEK.LogicalBlockAddress[0] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3; + cdb->SEEK.LogicalBlockAddress[1] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2; + cdb->SEEK.LogicalBlockAddress[2] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1; + cdb->SEEK.LogicalBlockAddress[3] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0; + + srb->CdbLength = 10; + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_STOP_AUDIO: { + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.Immediate = 1; + cdb->START_STOP.Start = 0; + cdb->START_STOP.LoadEject = 0; + + srb->CdbLength = 6; + srb->TimeOutValue = deviceExtension->TimeOutValue; + + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER); + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + } + + case IOCTL_CDROM_GET_CONTROL: { + + PAUDIO_OUTPUT audioOutput; + PCDROM_AUDIO_CONTROL audioControl = Irp->AssociatedIrp.SystemBuffer; + + // + // Allocate buffer for volume control information. + // + + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, + MODE_DATA_SIZE); + + if (!dataBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + MODE_DATA_SIZE, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + srb->DataBuffer = dataBuffer; + + RtlZeroMemory(dataBuffer, MODE_DATA_SIZE); + + // + // Setup for either 6 or 10 byte CDBs. + // + + if (use6Byte) { + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE; + cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE; + + // + // Disable block descriptors. + // + + cdb->MODE_SENSE.Dbd = TRUE; + + srb->CdbLength = 6; + } else { + + cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; + cdb->MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE; + cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8); + cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF); + + // + // Disable block descriptors. + // + + cdb->MODE_SENSE10.Dbd = TRUE; + + srb->CdbLength = 10; + } + + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->DataTransferLength = MODE_DATA_SIZE; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN); + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + + } + + case IOCTL_CDROM_GET_VOLUME: + case IOCTL_CDROM_SET_VOLUME: { + + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, + MODE_DATA_SIZE); + + if (!dataBuffer) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + irp2->MdlAddress = IoAllocateMdl(dataBuffer, + MODE_DATA_SIZE, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp2->MdlAddress) { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + ExFreePool(senseBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp2); + IoStartNextPacket(DeviceObject, FALSE); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp2->MdlAddress); + srb->DataBuffer = dataBuffer; + + RtlZeroMemory(dataBuffer, MODE_DATA_SIZE); + + + if (use6Byte) { + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE; + cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE; + + srb->CdbLength = 6; + + } else { + + cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; + cdb->MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE; + cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8); + cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF); + + srb->CdbLength = 10; + } + + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->DataTransferLength = MODE_DATA_SIZE; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN); + + if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_SET_VOLUME) { + + // + // Setup a different completion routine as the mode sense data is needed in order + // to send the mode select. + // + + IoSetCompletionRoutine(irp2, + CdRomSetVolumeIntermediateCompletion, + srb, + TRUE, + TRUE, + TRUE); + + } + + IoCallDriver(deviceExtension->PortDeviceObject, irp2); + return; + + } + + default: + + // + // Just complete the request - CdRomClassIoctlCompletion will take + // care of it for us + // + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return; + + } // end switch() } - /* Release the SRB and associated buffers */ - if (Srb != NULL) - { - DPRINT("Srb %p\n", Srb); + // + // If a read or an unhandled IRP_MJ_XX, end up here. The unhandled IRP_MJ's + // are expected and composed of AutoRun Irps, at present. + // - if (Srb->DataBuffer != NULL) - ExFreePool (Srb->DataBuffer); - - if (Srb->SenseInfoBuffer != NULL) - ExFreePool (Srb->SenseInfoBuffer); - - ExFreePool (Srb); - } - - if (OrigIrp->PendingReturned) - { - IoMarkIrpPending (OrigIrp); - } - - /* Release the MDL */ - if (Irp->MdlAddress != NULL) - { - IoFreeMdl (Irp->MdlAddress); - } - - /* Release the sub irp */ - IoFreeIrp (Irp); - - /* Set io status information */ - OrigIrp->IoStatus.Status = Status; - if (!NT_SUCCESS(Status) && IoIsErrorUserInduced (Status)) - { - IoSetHardErrorOrVerifyDevice (OrigIrp, - DeviceObject); - OrigIrp->IoStatus.Information = 0; - } - - /* Complete the original IRP */ - IoCompleteRequest (OrigIrp, - IO_DISK_INCREMENT); - IoStartNextPacket (DeviceObject, - FALSE); - - DPRINT ("CdromDeviceControlCompletion() done\n"); - - return STATUS_MORE_PROCESSING_REQUIRED; + IoCallDriver(deviceExtension->PortDeviceObject, Irp); + return; } -VOID STDCALL -CdromTimerRoutine(IN PDEVICE_OBJECT DeviceObject, - IN PVOID Context) + +NTSTATUS +STDCALL +ScsiCdRomReadVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the entry called by the I/O system for read requests. + It builds the SRB and sends it to the port driver. + +Arguments: + + DeviceObject - the system object for the device. + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ + { - PIO_WORKITEM WorkItem; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension+1); + KIRQL irql; + SCSI_REQUEST_BLOCK srb; + PCDB cdb = (PCDB)srb.Cdb; + NTSTATUS status; - DPRINT ("CdromTimerRoutine() called\n"); - WorkItem = IoAllocateWorkItem(DeviceObject); - if (!WorkItem) - { - return; + // + // If the cd is playing music then reject this request. + // + + if (PLAY_ACTIVE(deviceExtension)) { + Irp->IoStatus.Status = STATUS_DEVICE_BUSY; + return STATUS_DEVICE_BUSY; } - IoQueueWorkItem(WorkItem, - CdromWorkItem, - DelayedWorkQueue, - WorkItem); -} + // + // Verify parameters of this request. + // Check that ending sector is on disc and + // that number of bytes to transfer is a multiple of + // the sector size. + // + + startingOffset.QuadPart = currentIrpStack->Parameters.Read.ByteOffset.QuadPart + + transferByteCount; + + if (!deviceExtension->DiskGeometry->BytesPerSector) { + deviceExtension->DiskGeometry->BytesPerSector = 2048; + } + + if ((startingOffset.QuadPart > deviceExtension->PartitionLength.QuadPart) || + (transferByteCount & deviceExtension->DiskGeometry->BytesPerSector - 1)) { + + DebugPrint((1,"ScsiCdRomRead: Invalid I/O parameters\n")); + DebugPrint((1, "\toffset %x:%x, Length %x:%x\n", + startingOffset.u.HighPart, + startingOffset.u.LowPart, + deviceExtension->PartitionLength.u.HighPart, + deviceExtension->PartitionLength.u.LowPart)); + DebugPrint((1, "\tbps %x\n", deviceExtension->DiskGeometry->BytesPerSector)); + + // + // Fail request with status of invalid parameters. + // + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + return STATUS_INVALID_PARAMETER; + } -VOID STDCALL -CdromWorkItem(IN PDEVICE_OBJECT DeviceObject, - IN PVOID Context) + return STATUS_SUCCESS; + +} // end ScsiCdRomReadVerification() + + + +NTSTATUS +STDCALL +CdRomDeviceControlCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) { - PIRP Irp; - KEVENT Event; - IO_STATUS_BLOCK IoStatus; - NTSTATUS Status; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION physicalExtension = deviceExtension->PhysicalDevice->DeviceExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension + 1); + BOOLEAN use6Byte = cdData->XAFlags & XA_USE_6_BYTE; + PIO_STACK_LOCATION realIrpStack; + PIO_STACK_LOCATION realIrpNextStack; + PSCSI_REQUEST_BLOCK srb = Context; + PIRP realIrp = NULL; + NTSTATUS status; + BOOLEAN retry; - DPRINT("CdromWorkItem() called\n"); + // + // Extract the 'real' irp from the irpstack. + // - IoFreeWorkItem((PIO_WORKITEM) Context); + realIrp = (PIRP) irpStack->Parameters.Others.Argument2; + realIrpStack = IoGetCurrentIrpStackLocation(realIrp); + realIrpNextStack = IoGetNextIrpStackLocation(realIrp); - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); + // + // Check SRB status for success of completing request. + // - Irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_CHECK_VERIFY, - DeviceObject, - NULL, - 0, - NULL, - 0, - FALSE, - &Event, - &IoStatus); - if (Irp == NULL) - { - DPRINT("IoBuildDeviceIoControlRequest failed\n"); - return; + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + DebugPrint((2, + "CdRomDeviceControlCompletion: Irp %lx, Srb %lx Real Irp %lx Status %lx\n", + Irp, + srb, + realIrp, + srb->SrbStatus)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + DebugPrint((2, "CdRomDeviceControlCompletion: Releasing Queue\n")); + ScsiClassReleaseQueue(DeviceObject); + } + + + retry = ScsiClassInterpretSenseInfo(DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->Parameters.DeviceIoControl.IoControlCode, + MAXIMUM_RETRIES - ((ULONG)realIrpNextStack->Parameters.Others.Argument1), + &status); + + DebugPrint((2, "CdRomDeviceControlCompletion: IRP will %sbe retried\n", + (retry ? "" : "not "))); + + // + // Some of the Device Controls need special cases on non-Success status's. + // + + if (realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + if ((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_CONTROL) || + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME)) { + + if (status == STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + retry = FALSE; + } + } + + if (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_Q_CHANNEL) { + PLAY_ACTIVE(deviceExtension) = FALSE; + } + } + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + if (((realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) || + (realIrpStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL)) && + (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY)) { + + // + // Update the geometry information, as the media could have changed. + // The completion routine for this will complete the real irp and start + // the next packet. + // + + status = CdRomUpdateCapacity(deviceExtension,realIrp, NULL); + DebugPrint((2, "CdRomDeviceControlCompletion: [%lx] CdRomUpdateCapacity completed with status %lx\n", realIrp, status)); + ASSERT(status == STATUS_PENDING); + + return STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + } + + if (retry && (realIrpNextStack->Parameters.Others.Argument1 = (ULONG)realIrpNextStack->Parameters.Others.Argument1-1)) { + + + if (((ULONG)realIrpNextStack->Parameters.Others.Argument1)) { + + // + // Retry request. + // + + DebugPrint((1, "Retry request %lx - Calling StartIo\n", Irp)); + + + ExFreePool(srb->SenseInfoBuffer); + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + ExFreePool(srb); + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Call StartIo directly since IoStartNextPacket hasn't been called, + // the serialisation is still intact. + // + + ScsiCdRomStartIo(DeviceObject, realIrp); + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + // + // Exhausted retries. Fall through and complete the request with the appropriate status. + // + + } + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; } - Status = IoCallDriver(DeviceObject, Irp); - DPRINT("Status: %x\n", Status); + if (NT_SUCCESS(status)) { + + switch (realIrpStack->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_CDROM_GET_DRIVE_GEOMETRY: { + + PREAD_CAPACITY_DATA readCapacityBuffer = srb->DataBuffer; + ULONG lastSector; + ULONG bps; + ULONG lastBit; + ULONG tmp; + + // + // Swizzle bytes from Read Capacity and translate into + // the necessary geometry information in the device extension. + // + + tmp = readCapacityBuffer->BytesPerBlock; + ((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3; + ((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2; + ((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1; + ((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0; + + // + // Insure that bps is a power of 2. + // This corrects a problem with the HP 4020i CDR where it + // returns an incorrect number for bytes per sector. + // + + if (!bps) { + bps = 2048; + } else { + lastBit = (ULONG) -1; + while (bps) { + lastBit++; + bps = bps >> 1; + } + + bps = 1 << lastBit; + } + deviceExtension->DiskGeometry->BytesPerSector = bps; + + DebugPrint((2, + "CdRomDeviceControlCompletion: Calculated bps %#x\n", + deviceExtension->DiskGeometry->BytesPerSector)); + + // + // Copy last sector in reverse byte order. + // + + tmp = readCapacityBuffer->LogicalBlockAddress; + ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3; + ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2; + ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1; + ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0; + + // + // Calculate sector to byte shift. + // + + WHICH_BIT(bps, deviceExtension->SectorShift); + + DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Sector size is %d\n", + deviceExtension->DiskGeometry->BytesPerSector)); + + DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Number of Sectors is %d\n", + lastSector + 1)); + + // + // Calculate media capacity in bytes. + // + + deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1); + + // + // Calculate number of cylinders. + // + + deviceExtension->DiskGeometry->Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64)); + + deviceExtension->PartitionLength.QuadPart = + (deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift); + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { + + // + // This device supports removable media. + // + + deviceExtension->DiskGeometry->MediaType = RemovableMedia; + + } else { + + // + // Assume media type is fixed disk. + // + + deviceExtension->DiskGeometry->MediaType = FixedMedia; + } + + // + // Assume sectors per track are 32; + // + + deviceExtension->DiskGeometry->SectorsPerTrack = 32; + + // + // Assume tracks per cylinder (number of heads) is 64. + // + + deviceExtension->DiskGeometry->TracksPerCylinder = 64; + + // + // Copy the device extension's geometry info into the user buffer. + // + + RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer, + deviceExtension->DiskGeometry, + sizeof(DISK_GEOMETRY)); + + // + // update information field. + // + + realIrp->IoStatus.Information = sizeof(DISK_GEOMETRY); + break; + } + + case IOCTL_CDROM_CHECK_VERIFY: + + if((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY) && + (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength)) { + + *((PULONG)realIrp->AssociatedIrp.SystemBuffer) = + physicalExtension->MediaChangeCount; + realIrp->IoStatus.Information = sizeof(ULONG); + } else { + realIrp->IoStatus.Information = 0; + } + + DebugPrint((2, "CdRomDeviceControlCompletion: [%lx] completing CHECK_VERIFY buddy irp %lx\n", realIrp, Irp)); + break; + + case IOCTL_CDROM_GET_LAST_SESSION: + case IOCTL_CDROM_READ_TOC: { + + PCDROM_TOC toc = srb->DataBuffer; + + // + // Copy the device extension's geometry info into the user buffer. + // + + RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer, + toc, + srb->DataTransferLength); + + // + // update information field. + // + + realIrp->IoStatus.Information = srb->DataTransferLength; + break; + } + + case IOCTL_CDROM_PLAY_AUDIO_MSF: + + PLAY_ACTIVE(deviceExtension) = TRUE; + + break; + + case IOCTL_CDROM_READ_Q_CHANNEL: { + + PSUB_Q_CHANNEL_DATA userChannelData = realIrp->AssociatedIrp.SystemBuffer; + PCDROM_SUB_Q_DATA_FORMAT inputBuffer = realIrp->AssociatedIrp.SystemBuffer; + PSUB_Q_CHANNEL_DATA subQPtr = srb->DataBuffer; + +#if DBG + switch( inputBuffer->Format ) { + + case IOCTL_CDROM_CURRENT_POSITION: + DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus )); + DebugPrint((2,"CdRomDeviceControlCompletion: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR )); + DebugPrint((2,"CdRomDeviceControlCompletion: Control = 0x%x\n", subQPtr->CurrentPosition.Control )); + DebugPrint((2,"CdRomDeviceControlCompletion: Track = %u\n", subQPtr->CurrentPosition.TrackNumber )); + DebugPrint((2,"CdRomDeviceControlCompletion: Index = %u\n", subQPtr->CurrentPosition.IndexNumber )); + DebugPrint((2,"CdRomDeviceControlCompletion: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) )); + DebugPrint((2,"CdRomDeviceControlCompletion: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) )); + break; + + case IOCTL_CDROM_MEDIA_CATALOG: + DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus )); + DebugPrint((2,"CdRomDeviceControlCompletion: Mcval is %u\n", subQPtr->MediaCatalog.Mcval )); + break; + + case IOCTL_CDROM_TRACK_ISRC: + DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus )); + DebugPrint((2,"CdRomDeviceControlCompletion: Tcval is %u\n", subQPtr->TrackIsrc.Tcval )); + break; + + } +#endif + + // + // Update the play active status. + // + + if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) { + + PLAY_ACTIVE(deviceExtension) = TRUE; + + } else { + + PLAY_ACTIVE(deviceExtension) = FALSE; + + } + + // + // Check if output buffer is large enough to contain + // the data. + // + + if (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength < + srb->DataTransferLength) { + + srb->DataTransferLength = + realIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + } + + // + // Copy our buffer into users. + // + + RtlMoveMemory(userChannelData, + subQPtr, + srb->DataTransferLength); + + realIrp->IoStatus.Information = srb->DataTransferLength; + break; + } + + case IOCTL_CDROM_PAUSE_AUDIO: + + PLAY_ACTIVE(deviceExtension) = FALSE; + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_RESUME_AUDIO: + + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_SEEK_AUDIO_MSF: + + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_STOP_AUDIO: + + PLAY_ACTIVE(deviceExtension) = FALSE; + + realIrp->IoStatus.Information = 0; + break; + + case IOCTL_CDROM_GET_CONTROL: { + + PCDROM_AUDIO_CONTROL audioControl = srb->DataBuffer; + PAUDIO_OUTPUT audioOutput; + ULONG bytesTransferred; + + audioOutput = ScsiClassFindModePage((PCHAR)audioControl, + srb->DataTransferLength, + CDROM_AUDIO_CONTROL_PAGE, + use6Byte); + // + // Verify the page is as big as expected. + // + + bytesTransferred = (PCHAR) audioOutput - (PCHAR) audioControl + + sizeof(AUDIO_OUTPUT); + + if (audioOutput != NULL && + srb->DataTransferLength >= bytesTransferred) { + + audioControl->LbaFormat = audioOutput->LbaFormat; + + audioControl->LogicalBlocksPerSecond = + (audioOutput->LogicalBlocksPerSecond[0] << (UCHAR)8) | + audioOutput->LogicalBlocksPerSecond[1]; + + realIrp->IoStatus.Information = sizeof(CDROM_AUDIO_CONTROL); + + } else { + realIrp->IoStatus.Information = 0; + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + case IOCTL_CDROM_GET_VOLUME: { + + PAUDIO_OUTPUT audioOutput; + PVOLUME_CONTROL volumeControl = srb->DataBuffer; + ULONG i,bytesTransferred; + + audioOutput = ScsiClassFindModePage((PCHAR)volumeControl, + srb->DataTransferLength, + CDROM_AUDIO_CONTROL_PAGE, + use6Byte); + + // + // Verify the page is as big as expected. + // + + bytesTransferred = (PCHAR) audioOutput - (PCHAR) volumeControl + + sizeof(AUDIO_OUTPUT); + + if (audioOutput != NULL && + srb->DataTransferLength >= bytesTransferred) { + + for (i=0; i<4; i++) { + volumeControl->PortVolume[i] = + audioOutput->PortOutput[i].Volume; + } + + // + // Set bytes transferred in IRP. + // + + realIrp->IoStatus.Information = sizeof(VOLUME_CONTROL); + + } else { + realIrp->IoStatus.Information = 0; + status = STATUS_INVALID_DEVICE_REQUEST; + } + + break; + } + + case IOCTL_CDROM_SET_VOLUME: + + realIrp->IoStatus.Information = sizeof(VOLUME_CONTROL); + break; + + default: + + ASSERT(FALSE); + realIrp->IoStatus.Information = 0; + status = STATUS_INVALID_DEVICE_REQUEST; + + } // end switch() + } + + // + // Deallocate srb and sense buffer. + // + + if (srb) { + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + if (srb->SenseInfoBuffer) { + ExFreePool(srb->SenseInfoBuffer); + } + ExFreePool(srb); + } + + if (realIrp->PendingReturned) { + IoMarkIrpPending(realIrp); + } + + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Set status in completing IRP. + // + + realIrp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + DebugPrint((1, "CdRomDeviceCompletion - Setting Hard Error on realIrp %lx\n", + realIrp)); + IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject); + realIrp->IoStatus.Information = 0; + } + + IoCompleteRequest(realIrp, IO_DISK_INCREMENT); + + IoStartNextPacket(DeviceObject, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +STDCALL +CdRomSetVolumeIntermediateCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension + 1); + BOOLEAN use6Byte = cdData->XAFlags & XA_USE_6_BYTE; + PIO_STACK_LOCATION realIrpStack; + PIO_STACK_LOCATION realIrpNextStack; + PSCSI_REQUEST_BLOCK srb = Context; + PIRP realIrp = NULL; + NTSTATUS status; + BOOLEAN retry; + + // + // Extract the 'real' irp from the irpstack. + // + + realIrp = (PIRP) irpStack->Parameters.Others.Argument2; + realIrpStack = IoGetCurrentIrpStackLocation(realIrp); + realIrpNextStack = IoGetNextIrpStackLocation(realIrp); + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + DebugPrint((2, + "CdRomSetVolumeIntermediateCompletion: Irp %lx, Srb %lx Real Irp\n", + Irp, + srb, + realIrp)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ScsiClassReleaseQueue(DeviceObject); + } + + + retry = ScsiClassInterpretSenseInfo(DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->Parameters.DeviceIoControl.IoControlCode, + MAXIMUM_RETRIES - ((ULONG)realIrpNextStack->Parameters.Others.Argument1), + &status); + + if (status == STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + retry = FALSE; + } + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && (realIrpNextStack->Parameters.Others.Argument1 = (ULONG)realIrpNextStack->Parameters.Others.Argument1-1)) { + + if (((ULONG)realIrpNextStack->Parameters.Others.Argument1)) { + + // + // Retry request. + // + + DebugPrint((1, "Retry request %lx - Calling StartIo\n", Irp)); + + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Call StartIo directly since IoStartNextPacket hasn't been called, + // the serialisation is still intact. + // + ScsiCdRomStartIo(DeviceObject, realIrp); + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + // + // Exhausted retries. Fall through and complete the request with the appropriate status. + // + + } + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status)) { + + PAUDIO_OUTPUT audioInput = NULL; + PAUDIO_OUTPUT audioOutput; + PVOLUME_CONTROL volumeControl = realIrp->AssociatedIrp.SystemBuffer; + ULONG i,bytesTransferred,headerLength; + PVOID dataBuffer; + PCDB cdb; + + audioInput = ScsiClassFindModePage((PCHAR)srb->DataBuffer, + srb->DataTransferLength, + CDROM_AUDIO_CONTROL_PAGE, + use6Byte); + + // + // Check to make sure the mode sense data is valid before we go on + // + + if(audioInput == NULL) { + + DebugPrint((1, "Mode Sense Page %d not found\n", + CDROM_AUDIO_CONTROL_PAGE)); + + realIrp->IoStatus.Information = 0; + realIrp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; + IoCompleteRequest(realIrp, IO_DISK_INCREMENT); + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + if (use6Byte) { + headerLength = sizeof(MODE_PARAMETER_HEADER); + } else { + headerLength = sizeof(MODE_PARAMETER_HEADER10); + } + + bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength; + + // + // Allocate a new buffer for the mode select. + // + + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, bytesTransferred); + + if (!dataBuffer) { + realIrp->IoStatus.Information = 0; + realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(realIrp, IO_DISK_INCREMENT); + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + RtlZeroMemory(dataBuffer, bytesTransferred); + + // + // Rebuild the data buffer to include the user requested values. + // + + audioOutput = (PAUDIO_OUTPUT) ((PCHAR) dataBuffer + headerLength); + + for (i=0; i<4; i++) { + audioOutput->PortOutput[i].Volume = + volumeControl->PortVolume[i]; + audioOutput->PortOutput[i].ChannelSelection = + audioInput->PortOutput[i].ChannelSelection; + } + + audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE; + audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2; + audioOutput->Immediate = MODE_SELECT_IMMEDIATE; + + // + // Free the old data buffer, mdl. + // + + ExFreePool(srb->DataBuffer); + IoFreeMdl(Irp->MdlAddress); + + // + // rebuild the srb. + // + + cdb = (PCDB)srb->Cdb; + RtlZeroMemory(cdb, CDB12GENERIC_LENGTH); + + srb->SrbStatus = srb->ScsiStatus = 0; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_OUT); + srb->DataTransferLength = bytesTransferred; + + if (use6Byte) { + + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.ParameterListLength = (UCHAR) bytesTransferred; + cdb->MODE_SELECT.PFBit = 1; + srb->CdbLength = 6; + } else { + + cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; + cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8); + cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF); + cdb->MODE_SELECT10.PFBit = 1; + srb->CdbLength = 10; + } + + // + // Prepare the MDL + // + + Irp->MdlAddress = IoAllocateMdl(dataBuffer, + bytesTransferred, + FALSE, + FALSE, + (PIRP) NULL); + + if (!Irp->MdlAddress) { + realIrp->IoStatus.Information = 0; + realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(realIrp, IO_DISK_INCREMENT); + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(Irp); + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + MmBuildMdlForNonPagedPool(Irp->MdlAddress); + srb->DataBuffer = dataBuffer; + + irpStack = IoGetNextIrpStackLocation(Irp); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + + // + // reset the irp completion. + // + + IoSetCompletionRoutine(Irp, + CdRomDeviceControlCompletion, + srb, + TRUE, + TRUE, + TRUE); + // + // Call the port driver. + // + + IoCallDriver(deviceExtension->PortDeviceObject, Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Deallocate srb and sense buffer. + // + + if (srb) { + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + if (srb->SenseInfoBuffer) { + ExFreePool(srb->SenseInfoBuffer); + } + ExFreePool(srb); + } + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + if (realIrp->PendingReturned) { + IoMarkIrpPending(realIrp); + } + + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Set status in completing IRP. + // + + realIrp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject); + realIrp->IoStatus.Information = 0; + } + + IoCompleteRequest(realIrp, IO_DISK_INCREMENT); + + IoStartNextPacket(DeviceObject, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + + +NTSTATUS +STDCALL +CdRomSwitchModeCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension + 1); + BOOLEAN use6Byte = cdData->XAFlags & XA_USE_6_BYTE; + PIO_STACK_LOCATION realIrpStack; + PIO_STACK_LOCATION realIrpNextStack; + PSCSI_REQUEST_BLOCK srb = Context; + PIRP realIrp = NULL; + NTSTATUS status; + BOOLEAN retry; + + // + // Extract the 'real' irp from the irpstack. + // + + realIrp = (PIRP) irpStack->Parameters.Others.Argument2; + realIrpStack = IoGetCurrentIrpStackLocation(realIrp); + realIrpNextStack = IoGetNextIrpStackLocation(realIrp); + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + DebugPrint((2, + "CdRomSetVolumeIntermediateCompletion: Irp %lx, Srb %lx Real Irp\n", + Irp, + srb, + realIrp)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ScsiClassReleaseQueue(DeviceObject); + } + + + retry = ScsiClassInterpretSenseInfo(DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->Parameters.DeviceIoControl.IoControlCode, + MAXIMUM_RETRIES - ((ULONG)realIrpNextStack->Parameters.Others.Argument1), + &status); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && (realIrpNextStack->Parameters.Others.Argument1 = (ULONG)realIrpNextStack->Parameters.Others.Argument1-1)) { + + if (((ULONG)realIrpNextStack->Parameters.Others.Argument1)) { + + // + // Retry request. + // + + DebugPrint((1, "Retry request %lx - Calling StartIo\n", Irp)); + + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Call StartIo directly since IoStartNextPacket hasn't been called, + // the serialisation is still intact. + // + + ScsiCdRomStartIo(DeviceObject, realIrp); + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + // + // Exhausted retries. Fall through and complete the request with the appropriate status. + // + } + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status)) { + + ULONG sectorSize, startingSector, transferByteCount; + PCDB cdb; + + // + // Update device ext. to show which mode we are currently using. + // + + sectorSize = cdData->u1.BlockDescriptor.BlockLength[0] << 16; + sectorSize |= (cdData->u1.BlockDescriptor.BlockLength[1] << 8); + sectorSize |= (cdData->u1.BlockDescriptor.BlockLength[2]); + + cdData->RawAccess = (sectorSize == RAW_SECTOR_SIZE) ? TRUE : FALSE; + + // + // Free the old data buffer, mdl. + // + + ExFreePool(srb->DataBuffer); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + + // + // rebuild the srb. + // + + cdb = (PCDB)srb->Cdb; + RtlZeroMemory(cdb, CDB12GENERIC_LENGTH); + + + if (cdData->RawAccess) { + + PRAW_READ_INFO rawReadInfo = + (PRAW_READ_INFO)realIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; + + ULONG maximumTransferLength; + ULONG transferPages; + UCHAR min, sec, frame; + + // + // Calculate starting offset. + // + + startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift); + transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE; + maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength; + transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(realIrp->MdlAddress), + transferByteCount); + + // + // Determine if request is within limits imposed by miniport. + // If the request is larger than the miniport's capabilities, split it. + // + + if (transferByteCount > maximumTransferLength || + transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages) { + + realIrp->IoStatus.Information = 0; + realIrp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(realIrp, IO_DISK_INCREMENT); + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + + IoStartNextPacket(DeviceObject, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + srb->OriginalRequest = realIrp; + srb->SrbFlags = deviceExtension->SrbFlags; + srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN); + srb->DataTransferLength = transferByteCount; + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->CdbLength = 10; + srb->DataBuffer = MmGetMdlVirtualAddress(realIrp->MdlAddress); + + if (rawReadInfo->TrackMode == CDDA) { + if (cdData->XAFlags & PLEXTOR_CDDA) { + + srb->CdbLength = 12; + + cdb->PLXTR_READ_CDDA.LogicalUnitNumber = deviceExtension->Lun; + cdb->PLXTR_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->PLXTR_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->PLXTR_READ_CDDA.TransferBlockByte3 = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->PLXTR_READ_CDDA.TransferBlockByte2 = (UCHAR) (rawReadInfo->SectorCount >> 8); + cdb->PLXTR_READ_CDDA.TransferBlockByte1 = 0; + cdb->PLXTR_READ_CDDA.TransferBlockByte0 = 0; + + cdb->PLXTR_READ_CDDA.SubCode = 0; + cdb->PLXTR_READ_CDDA.OperationCode = 0xD8; + + } else if (cdData->XAFlags & NEC_CDDA) { + + cdb->NEC_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->NEC_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->NEC_READ_CDDA.TransferBlockByte1 = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + cdb->NEC_READ_CDDA.TransferBlockByte0 = (UCHAR) (rawReadInfo->SectorCount >> 8); + + cdb->NEC_READ_CDDA.OperationCode = 0xD4; + } + } else { + cdb->CDB10.LogicalUnitNumber = deviceExtension->Lun; + + cdb->CDB10.TransferBlocksMsb = (UCHAR) (rawReadInfo->SectorCount >> 8); + cdb->CDB10.TransferBlocksLsb = (UCHAR) (rawReadInfo->SectorCount & 0xFF); + + cdb->CDB10.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF); + cdb->CDB10.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF); + cdb->CDB10.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF); + cdb->CDB10.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF); + + cdb->CDB10.OperationCode = SCSIOP_READ; + } + + srb->SrbStatus = srb->ScsiStatus = 0; + + + irpStack = IoGetNextIrpStackLocation(realIrp); + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->Parameters.Scsi.Srb = srb; + + if (!(irpStack->Parameters.Others.Argument1)) { + + // + // Only jam this in if it doesn't exist. The completion routines can + // call StartIo directly in the case of retries and resetting it will + // cause infinite loops. + // + + irpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + } + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(realIrp, + CdRomXACompletion, + srb, + TRUE, + TRUE, + TRUE); + } else { + + ULONG maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength; + ULONG transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(realIrp->MdlAddress), + realIrpStack->Parameters.Read.Length); + // + // Back to cooked sectors. Build and send a normal read. + // The real work for setting offsets and checking for splitrequests was + // done in startio + // + + if ((realIrpStack->Parameters.Read.Length > maximumTransferLength) || + (transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages)) { + + // + // Request needs to be split. Completion of each portion of the request will + // fire off the next portion. The final request will signal Io to send a new request. + // + + ScsiClassSplitRequest(DeviceObject, realIrp, maximumTransferLength); + return STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + // + // Build SRB and CDB for this IRP. + // + + ScsiClassBuildRequest(DeviceObject, realIrp); + + } + } + + // + // Call the port driver. + // + + IoCallDriver(deviceExtension->PortDeviceObject, realIrp); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Update device Extension flags to indicate that XA isn't supported. + // + + cdData->XAFlags |= XA_NOT_SUPPORTED; + + // + // Deallocate srb and sense buffer. + // + + if (srb) { + if (srb->DataBuffer) { + ExFreePool(srb->DataBuffer); + } + if (srb->SenseInfoBuffer) { + ExFreePool(srb->SenseInfoBuffer); + } + ExFreePool(srb); + } + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + if (realIrp->PendingReturned) { + IoMarkIrpPending(realIrp); + } + + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + + IoFreeIrp(Irp); + + // + // Set status in completing IRP. + // + + realIrp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject); + realIrp->IoStatus.Information = 0; + } + + IoCompleteRequest(realIrp, IO_DISK_INCREMENT); + + IoStartNextPacket(DeviceObject, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +STDCALL +CdRomXACompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine executes when the port driver has completed a request. + It looks at the SRB status in the completing SRB and if not success + it checks for valid request sense buffer information. If valid, the + info is used to update status with more precise message of type of + error. This routine deallocates the SRB. + +Arguments: + + DeviceObject - Supplies the device object which represents the logical + unit. + + Irp - Supplies the Irp which has completed. + + Context - Supplies a pointer to the SRB. + +Return Value: + + NT status + +--*/ + +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION irpNextStack = IoGetNextIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = Context; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + NTSTATUS status; + BOOLEAN retry; + + // + // Check SRB status for success of completing request. + // + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + DebugPrint((2,"ScsiClassIoComplete: IRP %lx, SRB %lx\n", Irp, srb)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ScsiClassReleaseQueue(DeviceObject); + } + + retry = ScsiClassInterpretSenseInfo( + DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0, + MAXIMUM_RETRIES - ((ULONG)irpNextStack->Parameters.Others.Argument1), + &status); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && (irpNextStack->Parameters.Others.Argument1 = (ULONG)irpNextStack->Parameters.Others.Argument1-1)) { + + if (((ULONG)irpNextStack->Parameters.Others.Argument1)) { + + // + // Retry request. + // + + DebugPrint((1, "CdRomXACompletion: Retry request %lx - Calling StartIo\n", Irp)); + + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + + // + // Call StartIo directly since IoStartNextPacket hasn't been called, + // the serialisation is still intact. + // + + ScsiCdRomStartIo(DeviceObject, Irp); + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + // + // Exhausted retries. Fall through and complete the request with the appropriate status. + // + } + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + + } // end if (SRB_STATUS(srb->SrbStatus) ... + + // + // Return SRB to nonpaged pool. + // + + ExFreePool(srb); + + // + // Set status in completing IRP. + // + + Irp->IoStatus.Status = status; + + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + Irp->IoStatus.Information = 0; + } + + // + // If pending has be returned for this irp then mark the current stack as + // pending. + // + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + //IoCompleteRequest(Irp, IO_DISK_INCREMENT); + IoStartNextPacket(DeviceObject, FALSE); + + return status; +} + + +NTSTATUS +STDCALL +CdRomDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the NT device control handler for CDROMs. + +Arguments: + + DeviceObject - for this CDROM + + Irp - IO Request packet + +Return Value: + + NTSTATUS + +--*/ + +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextStack; + PKEVENT deviceControlEvent; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension+1); + BOOLEAN use6Byte = cdData->XAFlags & XA_USE_6_BYTE; + SCSI_REQUEST_BLOCK srb; + PCDB cdb = (PCDB)srb.Cdb; + PVOID outputBuffer; + ULONG bytesTransferred = 0; + NTSTATUS status; + NTSTATUS status2; + KIRQL irql; + + ULONG ioctlCode; + ULONG baseCode; + ULONG functionCode; + +RetryControl: + + // + // Zero the SRB on stack. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + Irp->IoStatus.Information = 0; + + // + // if this is a class driver ioctl then we need to change the base code + // to IOCTL_CDROM_BASE so that the switch statement can handle it. + // + // WARNING - currently the scsi class ioctl function codes are between + // 0x200 & 0x300. this routine depends on that fact + // + + ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + baseCode = ioctlCode >> 16; + functionCode = (ioctlCode & (~0xffffc003)) >> 2; + + DebugPrint((1, "CdRomDeviceControl: Ioctl Code = %#08lx, Base Code = %#lx," + " Function Code = %#lx\n", + ioctlCode, + baseCode, + functionCode + )); + + if((functionCode >= 0x200) && (functionCode <= 0x300)) { + + ioctlCode = (ioctlCode & 0x0000ffff) | CTL_CODE(IOCTL_CDROM_BASE, 0, 0, 0); + + DebugPrint((1, "CdRomDeviceControl: Class Code - new ioctl code is %#08lx\n", + ioctlCode)); + + irpStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode; + + } + + switch (ioctlCode) { + + case IOCTL_CDROM_RAW_READ: { + + LARGE_INTEGER startingOffset; + ULONG transferBytes; + ULONG startingSector; + PRAW_READ_INFO rawReadInfo = (PRAW_READ_INFO)irpStack->Parameters.DeviceIoControl.Type3InputBuffer; + PUCHAR userData = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; + + // + // Ensure that XA reads are supported. + // + + if (cdData->XAFlags & XA_NOT_SUPPORTED) { + + DebugPrint((1, + "CdRomDeviceControl: XA Reads not supported. Flags (%x)\n", + cdData->XAFlags)); + + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + // + // Check that ending sector is on disc and buffers are there and of + // correct size. + // + + if (rawReadInfo == NULL) { + + // + // Called from user space. Validate the buffers. + // + + rawReadInfo = (PRAW_READ_INFO)userData; + irpStack->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)userData; + + if (rawReadInfo == NULL) { + + DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (No extent info\n")); + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(RAW_READ_INFO)) { + + DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Invalid info buffer\n")); + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + } + + startingOffset.QuadPart = rawReadInfo->DiskOffset.QuadPart; + startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift); + transferBytes = rawReadInfo->SectorCount * RAW_SECTOR_SIZE; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < transferBytes) { + + DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Bad buffer size)\n")); + + // + // Fail request with status of invalid parameters. + // + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + if ((startingOffset.QuadPart + transferBytes) > deviceExtension->PartitionLength.QuadPart) { + + DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Request Out of Bounds)\n")); + + // + // Fail request with status of invalid parameters. + // + + status = STATUS_INVALID_PARAMETER; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_GET_DRIVE_GEOMETRY: { + + DebugPrint((2,"CdRomDeviceControl: Get drive geometry\n")); + + if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof( DISK_GEOMETRY ) ) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject,Irp, NULL,NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_GET_LAST_SESSION: + case IOCTL_CDROM_READ_TOC: { + + // + // If the cd is playing music then reject this request. + // + + if (CdRomIsPlayActive(DeviceObject)) { + Irp->IoStatus.Status = STATUS_DEVICE_BUSY; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_DEVICE_BUSY; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_PLAY_AUDIO_MSF: { + + // + // Play Audio MSF + // + + DebugPrint((2,"CdRomDeviceControl: Play audio MSF\n")); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CDROM_PLAY_AUDIO_MSF)) { + + // + // Indicate unsuccessful status. + // + + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_SEEK_AUDIO_MSF: { + + + // + // Seek Audio MSF + // + + DebugPrint((2,"CdRomDeviceControl: Seek audio MSF\n")); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CDROM_SEEK_AUDIO_MSF)) { + + // + // Indicate unsuccessful status. + // + + status = STATUS_BUFFER_TOO_SMALL; + break; + } else { + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + + } + } + + case IOCTL_CDROM_PAUSE_AUDIO: { + + // + // Pause audio + // + + DebugPrint((2, "CdRomDeviceControl: Pause audio\n")); + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + + break; + } + + case IOCTL_CDROM_RESUME_AUDIO: { + + // + // Resume audio + // + + DebugPrint((2, "CdRomDeviceControl: Resume audio\n")); + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_READ_Q_CHANNEL: { + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(CDROM_SUB_Q_DATA_FORMAT)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + break; + } + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_GET_CONTROL: { + + DebugPrint((2, "CdRomDeviceControl: Get audio control\n")); + + // + // Verify user buffer is large enough for the data. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(CDROM_AUDIO_CONTROL)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + break; + + } else { + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + } + + case IOCTL_CDROM_GET_VOLUME: { + + DebugPrint((2, "CdRomDeviceControl: Get volume control\n")); + + // + // Verify user buffer is large enough for data. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(VOLUME_CONTROL)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + break; + + } else { + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + } + + case IOCTL_CDROM_SET_VOLUME: { + + DebugPrint((2, "CdRomDeviceControl: Set volume control\n")); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(VOLUME_CONTROL)) { + + // + // Indicate unsuccessful status. + // + + status = STATUS_BUFFER_TOO_SMALL; + break; + } else { + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + return STATUS_PENDING; + } + } + + case IOCTL_CDROM_STOP_AUDIO: { + + // + // Stop play. + // + + DebugPrint((2, "CdRomDeviceControl: Stop audio\n")); + + IoMarkIrpPending(Irp); + IoStartPacket(DeviceObject,Irp, NULL,NULL); + + return STATUS_PENDING; + } + + case IOCTL_CDROM_CHECK_VERIFY: { + DebugPrint((1, "CdRomDeviceControl: [%lx] Check Verify\n", Irp)); + IoMarkIrpPending(Irp); + + if((irpStack->Parameters.DeviceIoControl.OutputBufferLength) && + (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))) { + + DebugPrint((1, "CdRomDeviceControl: Check Verify: media count " + "buffer too small\n")); + + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + IoStartPacket(DeviceObject,Irp, NULL,NULL); + + return STATUS_PENDING; + } + + default: { + + // + // allocate an event and stuff it into our stack location. + // + + deviceControlEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT)); + + if(!deviceControlEvent) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + PIO_STACK_LOCATION currentStack; + + KeInitializeEvent(deviceControlEvent, NotificationEvent, FALSE); + + currentStack = IoGetCurrentIrpStackLocation(Irp); + nextStack = IoGetNextIrpStackLocation(Irp); + + // + // Copy the stack down a notch + // + + *nextStack = *currentStack; + + IoSetCompletionRoutine( + Irp, + CdRomClassIoctlCompletion, + deviceControlEvent, + TRUE, + TRUE, + TRUE + ); + + IoSetNextIrpStackLocation(Irp); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Override volume verifies on this stack location so that we + // will be forced through the synchronization. Once this location + // goes away we get the old value back + // + + nextStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME; + + IoMarkIrpPending(Irp); + + IoStartPacket(DeviceObject, Irp, NULL, NULL); + + // + // Wait for CdRomClassIoctlCompletion to set the event. This + // ensures serialization remains intact for these unhandled device + // controls. + // + + KeWaitForSingleObject( + deviceControlEvent, + Suspended, + KernelMode, + FALSE, + NULL); + + ExFreePool(deviceControlEvent); + + DebugPrint((2, "CdRomDeviceControl: irp %#08lx synchronized\n", Irp)); + + // + // If an error occured then propagate that back up - we are no longer + // guaranteed synchronization and the upper layers will have to + // retry. + // + // If no error occured, call down to the class driver directly + // then start up the next request. + // + + if(Irp->IoStatus.Status == STATUS_SUCCESS) { + + status = ScsiClassDeviceControl(DeviceObject, Irp); + + KeRaiseIrql(DISPATCH_LEVEL, &irql); + + IoStartNextPacket(DeviceObject, FALSE); + + KeLowerIrql(irql); + } + } + + return status; + } + + } // end switch() + + if (status == STATUS_VERIFY_REQUIRED) { + + // + // If the status is verified required and this request + // should bypass verify required then retry the request. + // + + if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) { + + status = STATUS_IO_DEVICE_ERROR; + goto RetryControl; + + } + } + + if (IoIsErrorUserInduced(status)) { + + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + + } + + // + // Update IRP with completion status. + // + + Irp->IoStatus.Status = status; + + // + // Complete the request. + // + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + DebugPrint((2, "CdRomDeviceControl: Status is %lx\n", status)); + return status; + +} // end ScsiCdRomDeviceControl() + + +VOID +STDCALL +ScanForSpecial( + PDEVICE_OBJECT DeviceObject, + PINQUIRYDATA InquiryData, + PIO_SCSI_CAPABILITIES PortCapabilities + ) + +/*++ + +Routine Description: + + This function checks to see if an SCSI logical unit requires an special + initialization or error processing. + +Arguments: + + DeviceObject - Supplies the device object to be tested. + + InquiryData - Supplies the inquiry data returned by the device of interest. + + PortCapabilities - Supplies the capabilities of the device object. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension+1); + + // + // Look for a Hitachi CDR-1750. Read-ahead must be disabled in order + // to get this cdrom drive to work on scsi adapters that use PIO. + // + + if ((strncmp(InquiryData->VendorId, "HITACHI CDR-1750S", strlen("HITACHI CDR-1750S")) == 0 || + strncmp(InquiryData->VendorId, "HITACHI CDR-3650/1650S", strlen("HITACHI CDR-3650/1650S")) == 0) + && PortCapabilities->AdapterUsesPio) { + + DebugPrint((1, "CdRom ScanForSpecial: Found Hitachi CDR-1750S.\n")); + + // + // Setup an error handler to reinitialize the cd rom after it is reset. + // + + deviceExtension->ClassError = HitachProcessError; + + } else if (( RtlCompareMemory( InquiryData->VendorId,"FUJITSU", 7 ) == 7 ) && + (( RtlCompareMemory( InquiryData->ProductId,"FMCD-101", 8 ) == 8 ) || + ( RtlCompareMemory( InquiryData->ProductId,"FMCD-102", 8 ) == 8 ))) { + + // + // When Read command is issued to FMCD-101 or FMCD-102 and there is a music + // cd in it. It takes longer time than SCSI_CDROM_TIMEOUT before returning + // error status. + // + + deviceExtension->TimeOutValue = 20; + + } else if (( RtlCompareMemory( InquiryData->VendorId,"TOSHIBA", 7) == 7) && + (( RtlCompareMemory( InquiryData->ProductId,"CD-ROM XM-34", 12) == 12))) { + + SCSI_REQUEST_BLOCK srb; + PCDB cdb; + ULONG length; + PUCHAR buffer; + NTSTATUS status; + + // + // Set the density code and the error handler. + // + + length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH); + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Build the MODE SENSE CDB. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = deviceExtension->TimeOutValue; + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = 0x1; + cdb->MODE_SENSE.AllocationLength = (UCHAR)length; + + buffer = ExAllocatePool(NonPagedPoolCacheAligned, (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH)); + if (!buffer) { + return; + } + + status = ScsiClassSendSrbSynchronous(DeviceObject, + &srb, + buffer, + length, + FALSE); + + ((PERROR_RECOVERY_DATA)buffer)->BlockDescriptor.DensityCode = 0x83; + ((PERROR_RECOVERY_DATA)buffer)->Header.ModeDataLength = 0x0; + + RtlCopyMemory(&cdData->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA)); + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Build the MODE SENSE CDB. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = deviceExtension->TimeOutValue; + + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)length; + + status = ScsiClassSendSrbSynchronous(DeviceObject, + &srb, + buffer, + length, + TRUE); + + if (!NT_SUCCESS(status)) { + DebugPrint((1, + "Cdrom.ScanForSpecial: Setting density code on Toshiba failed [%x]\n", + status)); + } + + deviceExtension->ClassError = ToshibaProcessError; + + ExFreePool(buffer); + + } + + // + // Determine special CD-DA requirements. + // + + if (RtlCompareMemory( InquiryData->VendorId,"PLEXTOR",7) == 7) { + cdData->XAFlags |= PLEXTOR_CDDA; + } else if (RtlCompareMemory ( InquiryData->VendorId,"NEC",3) == 3) { + cdData->XAFlags |= NEC_CDDA; + } + + return; +} + + +VOID +STDCALL +HitachProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ) +/*++ + +Routine Description: + + This routine checks the type of error. If the error indicates CD-ROM the + CD-ROM needs to be reinitialized then a Mode sense command is sent to the + device. This command disables read-ahead for the device. + +Arguments: + + DeviceObject - Supplies a pointer to the device object. + + Srb - Supplies a pointer to the failing Srb. + + Status - Not used. + + Retry - Not used. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; + LARGE_INTEGER largeInt; + PUCHAR modePage; + PIO_STACK_LOCATION irpStack; + PIRP irp; + PSCSI_REQUEST_BLOCK srb; + PCOMPLETION_CONTEXT context; + PCDB cdb; + ULONG alignment; + + UNREFERENCED_PARAMETER(Status); + UNREFERENCED_PARAMETER(Retry); + + largeInt.QuadPart = (LONGLONG) 1; + + // + // Check the status. The initialization command only needs to be sent + // if UNIT ATTENTION is returned. + // + + if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) { + + // + // The drive does not require reinitialization. + // + + return; + } + + // + // Found a bad HITACHI cd-rom. These devices do not work with PIO + // adapters when read-ahead is enabled. Read-ahead is disabled by + // a mode select command. The mode select page code is zero and the + // length is 6 bytes. All of the other bytes should be zero. + // + + + if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) { + + DebugPrint((1, "HitachiProcessError: Reinitializing the CD-ROM.\n")); + + // + // Send the special mode select command to disable read-ahead + // on the CD-ROM reader. + // + + alignment = DeviceObject->AlignmentRequirement ? + DeviceObject->AlignmentRequirement : 1; + + context = ExAllocatePool( + NonPagedPool, + sizeof(COMPLETION_CONTEXT) + HITACHI_MODE_DATA_SIZE + alignment + ); + + if (context == NULL) { + + // + // If there is not enough memory to fulfill this request, + // simply return. A subsequent retry will fail and another + // chance to start the unit. + // + + return; + } + + context->DeviceObject = DeviceObject; + srb = &context->Srb; + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + // + // Write length to SRB. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set up SCSI bus address. + // + + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->TimeOutValue = deviceExtension->TimeOutValue; + + // + // Set the transfer length. + // + + srb->DataTransferLength = HITACHI_MODE_DATA_SIZE; + srb->SrbFlags = SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_AUTOSENSE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // The data buffer must be aligned. + // + + srb->DataBuffer = (PVOID) (((ULONG) (context + 1) + (alignment - 1)) & + ~(alignment - 1)); + + + // + // Build the HITACHI read-ahead mode select CDB. + // + + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + cdb->MODE_SENSE.LogicalUnitNumber = srb->Lun; + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SENSE.AllocationLength = HITACHI_MODE_DATA_SIZE; + + // + // Initialize the mode sense data. + // + + modePage = srb->DataBuffer; + + RtlZeroMemory(modePage, HITACHI_MODE_DATA_SIZE); + + // + // Set the page length field to 6. + // + + modePage[5] = 6; + + // + // Build the asynchronous request to be sent to the port driver. + // + + irp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE, + DeviceObject, + srb->DataBuffer, + srb->DataTransferLength, + &largeInt, + NULL); + + IoSetCompletionRoutine(irp, + (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion, + context, + TRUE, + TRUE, + TRUE); + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + srb->OriginalRequest = irp; + + // + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = (PVOID)srb; + + // + // Set up IRP Address. + // + + (VOID)IoCallDriver(deviceExtension->PortDeviceObject, irp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, - Suspended, - KernelMode, - FALSE, - NULL); } } -/* EOF */ + +NTSTATUS +STDCALL +ToshibaProcessErrorCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + Completion routine for the ClassError routine to handle older Toshiba units + that require setting the density code. + +Arguments: + + DeviceObject - Supplies a pointer to the device object. + + Irp - Pointer to irp created to set the density code. + + Context - Supplies a pointer to the Mode Select Srb. + + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED + +--*/ + +{ + + PSCSI_REQUEST_BLOCK srb = Context; + + // + // Check for a frozen queue. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + + // + // Unfreeze the queue getting the device object from the context. + // + + ScsiClassReleaseQueue(DeviceObject); + } + + // + // Free all of the allocations. + // + + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + + // + // Indicate the I/O system should stop processing the Irp completion. + // + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +VOID +STDCALL +ToshibaProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ) + +/*++ + +Routine Description: + + This routine checks the type of error. If the error indicates a unit attention, + the density code needs to be set via a Mode select command. + +Arguments: + + DeviceObject - Supplies a pointer to the device object. + + Srb - Supplies a pointer to the failing Srb. + + Status - Not used. + + Retry - Not used. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension+1); + PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; + PIO_STACK_LOCATION irpStack; + PIRP irp; + PSCSI_REQUEST_BLOCK srb; + ULONG length; + PCDB cdb; + PUCHAR dataBuffer; + + + if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) { + return; + } + + // + // The Toshiba's require the density code to be set on power up and media changes. + // + + if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) { + + + irp = IoAllocateIrp((CCHAR)(deviceExtension->DeviceObject->StackSize+1), + FALSE); + + if (!irp) { + return; + } + + srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK)); + if (!srb) { + IoFreeIrp(irp); + return; + } + + + length = sizeof(ERROR_RECOVERY_DATA); + dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, length); + if (!dataBuffer) { + ExFreePool(srb); + IoFreeIrp(irp); + return; + } + + irp->MdlAddress = IoAllocateMdl(dataBuffer, + length, + FALSE, + FALSE, + (PIRP) NULL); + + if (!irp->MdlAddress) { + ExFreePool(srb); + ExFreePool(dataBuffer); + IoFreeIrp(irp); + return; + } + + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb->DataBuffer = dataBuffer; + cdb = (PCDB)srb->Cdb; + + // + // Set up the irp. + // + + IoSetNextIrpStackLocation(irp); + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + irp->Flags = 0; + irp->UserBuffer = NULL; + + // + // Save the device object and irp in a private stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->DeviceObject = deviceExtension->DeviceObject; + + // + // Construct the IRP stack for the lower level driver. + // + + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT; + irpStack->Parameters.Scsi.Srb = srb; + + IoSetCompletionRoutine(irp, + ToshibaProcessErrorCompletion, + srb, + TRUE, + TRUE, + TRUE); + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Cdb[1] |= deviceExtension->Lun << 5; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->OriginalRequest = irp; + srb->SenseInfoBufferLength = 0; + + // + // Set the transfer length. + // + + srb->DataTransferLength = length; + srb->SrbFlags = SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_AUTOSENSE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + + srb->CdbLength = 6; + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)length; + + // + // Copy the Mode page into the databuffer. + // + + RtlCopyMemory(srb->DataBuffer, &cdData->u1.Header, length); + + // + // Set the density code. + // + + ((PERROR_RECOVERY_DATA)srb->DataBuffer)->BlockDescriptor.DensityCode = 0x83; + + IoCallDriver(deviceExtension->PortDeviceObject, irp); + } +} + + +BOOLEAN +STDCALL +CdRomIsPlayActive( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine determines if the cd is currently playing music. + +Arguments: + + DeviceObject - Device object to test. + +Return Value: + + TRUE if the device is playing music. + +--*/ +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIRP irp; + IO_STATUS_BLOCK ioStatus; + KEVENT event; + NTSTATUS status; + PSUB_Q_CURRENT_POSITION currentBuffer; + + if (!PLAY_ACTIVE(deviceExtension)) { + return(FALSE); + } + + currentBuffer = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(SUB_Q_CURRENT_POSITION)); + + if (currentBuffer == NULL) { + return(FALSE); + } + + ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION; + ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0; + + // + // Create notification event object to be used to signal the + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // Build the synchronous request to be sent to the port driver + // to perform the request. + // + + irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_READ_Q_CHANNEL, + deviceExtension->DeviceObject, + currentBuffer, + sizeof(CDROM_SUB_Q_DATA_FORMAT), + currentBuffer, + sizeof(SUB_Q_CURRENT_POSITION), + FALSE, + &event, + &ioStatus); + + if (irp == NULL) { + ExFreePool(currentBuffer); + return FALSE; + } + + // + // Pass request to port driver and wait for request to complete. + // + + status = IoCallDriver(deviceExtension->DeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + if (!NT_SUCCESS(status)) { + ExFreePool(currentBuffer); + return FALSE; + } + + ExFreePool(currentBuffer); + + return(PLAY_ACTIVE(deviceExtension)); + +} + + + +NTSTATUS +STDCALL +CdRomMediaChangeCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine handles the completion of the test unit ready irps + used to determine if the media has changed. If the media has + changed, this code signals the named event to wake up other + system services that react to media change (aka AutoPlay). + +Arguments: + + DeviceObject - the object for the completion + Irp - the IRP being completed + Context - the SRB from the IRP + +Return Value: + + NTSTATUS + +--*/ + +{ + PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK) Context; + PIO_STACK_LOCATION cdStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION irpNextStack = IoGetNextIrpStackLocation(Irp); + PDEVICE_EXTENSION deviceExtension; + PDEVICE_EXTENSION physicalExtension; + PSENSE_DATA senseBuffer; + PCDROM_DATA cddata; + + ASSERT(Irp); + ASSERT(cdStack); + DeviceObject = cdStack->DeviceObject; + ASSERT(DeviceObject); + + deviceExtension = DeviceObject->DeviceExtension; + physicalExtension = deviceExtension->PhysicalDevice->DeviceExtension; + cddata = (PCDROM_DATA)(deviceExtension + 1); + + ASSERT(cddata->MediaChangeIrp == NULL); + + // + // If the sense data field is valid, look for a media change. + // otherwise this iteration of the polling will just assume nothing + // changed. + // + + DebugPrint((3, "CdRomMediaChangeHandler: Completing Autorun Irp 0x%lx " + "for device %d\n", + Irp, deviceExtension->DeviceNumber)); + + if (srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { + if (srb->SenseInfoBufferLength >= FIELD_OFFSET(SENSE_DATA, CommandSpecificInformation)) { + + // + // See if this is a media change. + // + + senseBuffer = srb->SenseInfoBuffer; + if ((senseBuffer->SenseKey & 0x0f) == SCSI_SENSE_UNIT_ATTENTION) { + if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_MEDIUM_CHANGED) { + + DebugPrint((1, "CdRomMediaChangeCompletion: New media inserted " + "into CdRom%d [irp = 0x%lx]\n", + deviceExtension->DeviceNumber, Irp)); + + // + // Media change event occurred - signal the named event. + // + + KeSetEvent(deviceExtension->MediaChangeEvent, + (KPRIORITY) 0, + FALSE); + + deviceExtension->MediaChangeNoMedia = FALSE; + + } + + if (DeviceObject->Vpb->Flags & VPB_MOUNTED) { + + // + // Must remember the media changed and force the + // file system to verify on next access + // + + DeviceObject->Flags |= DO_VERIFY_VOLUME; + } + + physicalExtension->MediaChangeCount++; + + } else if(((senseBuffer->SenseKey & 0x0f) == SCSI_SENSE_NOT_READY)&& + (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)&& + (!deviceExtension->MediaChangeNoMedia)){ + + // + // If there was no media in the device then signal the waiters if + // we haven't already done so before. + // + + DebugPrint((1, "CdRomMediaChangeCompletion: No media in device" + "CdRom%d [irp = 0x%lx]\n", + deviceExtension->DeviceNumber, Irp)); + + KeSetEvent(deviceExtension->MediaChangeEvent, + (KPRIORITY) 0, + FALSE); + + deviceExtension->MediaChangeNoMedia = TRUE; + + } + } + } else if((srb->SrbStatus == SRB_STATUS_SUCCESS)&& + (deviceExtension->MediaChangeNoMedia)) { + // + // We didn't have any media before and now the requests are succeeding + // we probably missed the Media change somehow. Signal the change + // anyway + // + + DebugPrint((1, "CdRomMediaChangeCompletion: Request completed normally" + "for CdRom%d which was marked w/NoMedia [irp = 0x%lx]\n", + deviceExtension->DeviceNumber, Irp)); + + KeSetEvent(deviceExtension->MediaChangeEvent, + (KPRIORITY) 0, + FALSE); + + deviceExtension->MediaChangeNoMedia = FALSE; + + } + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ScsiClassReleaseQueue(deviceExtension->DeviceObject); + } + + // + // Remember the IRP and SRB for use the next time. + // + + irpNextStack->Parameters.Scsi.Srb = srb; + cddata->MediaChangeIrp = Irp; + + if (deviceExtension->ClassError) { + + NTSTATUS status; + BOOLEAN retry; + + // + // Throw away the status and retry values. Just give the error routine a chance + // to do what it needs to. + // + + deviceExtension->ClassError(DeviceObject, + srb, + &status, + &retry); + } + + IoStartNextPacket(DeviceObject, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +VOID +STDCALL +CdRomTickHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine handles the once per second timer provided by the + Io subsystem. It is only used when the cdrom device itself is + a candidate for autoplay support. It should never be called if + the cdrom device is a changer device. + +Arguments: + + DeviceObject - what to check. + Context - not used. + +Return Value: + + None. + +--*/ + +{ + PIRP irp; + PIRP heldIrpList; + PIRP nextIrp; + PLIST_ENTRY listEntry; + PCDROM_DATA cddata; + PIO_STACK_LOCATION irpStack; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + + cddata = (PCDROM_DATA)(deviceExtension + 1); + + if (cddata->MediaChange) { + if (cddata->MediaChangeIrp != NULL) { + + // + // Media change support is active and the IRP is waiting. + // Decrement the timer. + // There is no MP protection on the timer counter. This + // code is the only code that will manipulate the timer + // and only one instance of it should be running at any + // given time. + // + + cddata->MediaChangeCountDown--; + +#ifdef DBG + cddata->MediaChangeIrpTimeInUse = 0; + cddata->MediaChangeIrpLost = FALSE; +#endif + + if (!cddata->MediaChangeCountDown) { + PSCSI_REQUEST_BLOCK srb; + PIO_STACK_LOCATION irpNextStack; + PCDB cdb; + + // + // Reset the timer. + // + + cddata->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME; + + // + // Prepare the IRP for the test unit ready + // + + irp = cddata->MediaChangeIrp; + cddata->MediaChangeIrp = NULL; + + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + irp->Flags = 0; + irp->UserBuffer = NULL; + + // + // If the irp is sent down when the volume needs to be + // verified, CdRomUpdateGeometryCompletion won't complete + // it since it's not associated with a thread. Marking + // it to override the verify causes it always be sent + // to the port driver + // + + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME; + + irpNextStack = IoGetNextIrpStackLocation(irp); + irpNextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpNextStack->Parameters.DeviceIoControl.IoControlCode = + IOCTL_SCSI_EXECUTE_NONE; + + // + // Prepare the SRB for execution. + // + + srb = irpNextStack->Parameters.Scsi.Srb; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | + SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + srb->DataTransferLength = 0; + srb->OriginalRequest = irp; + + RtlZeroMemory(srb->SenseInfoBuffer, SENSE_BUFFER_SIZE); + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + cdb = (PCDB) &srb->Cdb[0]; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + cdb->CDB6GENERIC.LogicalUnitNumber = srb->Lun; + + // + // Setup the IRP to perform a test unit ready. + // + + IoSetCompletionRoutine(irp, + CdRomMediaChangeCompletion, + srb, + TRUE, + TRUE, + TRUE); + + // + // Issue the request. + // + + IoStartPacket(DeviceObject, irp, NULL, NULL); + } + } else { + +#ifdef DBG + if(cddata->MediaChangeIrpLost == FALSE) { + if(cddata->MediaChangeIrpTimeInUse++ > + MEDIA_CHANGE_TIMEOUT_TIME) { + + DebugPrint((0, "CdRom%d: AutoPlay has lost it's irp and " + "doesn't know where to find it. Leave it " + "alone and it'll come home dragging it's " + "stack behind it.\n", + deviceExtension->DeviceNumber)); + cddata->MediaChangeIrpLost = TRUE; + } + } + +#endif + } + } + + // + // Process all generic timer IRPS in the timer list. As IRPs are pulled + // off of the TimerIrpList they must be remembered in the first loop + // if they are not sent to the lower driver. After all items have + // been pulled off the list, it is possible to put the held IRPs back + // into the TimerIrpList. + // + + heldIrpList = NULL; + if (IsListEmpty(&cddata->TimerIrpList)) { + listEntry = NULL; + } else { + listEntry = ExInterlockedRemoveHeadList(&cddata->TimerIrpList, + &cddata->TimerIrpSpinLock); + } + while (listEntry) { + + // + // There is something in the timer list. Pick up the IRP and + // see if it is ready to be submitted. + // + + irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry); + irpStack = IoGetCurrentIrpStackLocation(irp); + + if (irpStack->Parameters.Others.Argument3) { + ULONG count; + + // + // Decrement the countdown timer and put the IRP back in the list. + // + + count = (ULONG) irpStack->Parameters.Others.Argument3; + count--; + irpStack->Parameters.Others.Argument3 = (PVOID) count; + + ASSERT(irp->AssociatedIrp.MasterIrp == NULL); + if (heldIrpList) { + irp->AssociatedIrp.MasterIrp = (PVOID) heldIrpList; + } + heldIrpList = irp; + + } else { + + // + // Submit this IRP to the lower driver. This IRP does not + // need to be remembered here. It will be handled again when + // it completes. + // + + DebugPrint((1, "CdRomTickHandler: Reissuing request %lx (thread = %lx)\n", irp, irp->Tail.Overlay.Thread)); + + // + // feed this to the appropriate port driver + // + + IoCallDriver (deviceExtension->PortDeviceObject, irp); + + } + + // + // Pick up the next IRP from the timer list. + // + + listEntry = ExInterlockedRemoveHeadList(&cddata->TimerIrpList, + &cddata->TimerIrpSpinLock); + } + + // + // Move all held IRPs back onto the timer list. + // + + while (heldIrpList) { + + // + // Save the single list pointer before queueing this IRP. + // + + nextIrp = (PIRP) heldIrpList->AssociatedIrp.MasterIrp; + heldIrpList->AssociatedIrp.MasterIrp = NULL; + + // + // Return the held IRP to the timer list. + // + + ExInterlockedInsertTailList(&cddata->TimerIrpList, + &heldIrpList->Tail.Overlay.ListEntry, + &cddata->TimerIrpSpinLock); + + // + // Continue processing the held IRPs + // + + heldIrpList = nextIrp; + } +} + +BOOLEAN +STDCALL +CdRomCheckRegistryForMediaChangeValue( + IN PUNICODE_STRING RegistryPath, + IN ULONG DeviceNumber + ) + +/*++ + +Routine Description: + + The user must specify that AutoPlay is to run on the platform + by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\ + Services\Cdrom\Autorun:REG_DWORD:1. + + The user can override the global setting to enable or disable Autorun on a + specific cdrom device by setting the key HKEY_LOCAL_MACHINE\System\ + CurrentControlSet\Services\Cdrom\Device\Autorun:REG_DWORD to one or zero. + (CURRENTLY UNIMPLEMENTED) + + If this registry value does not exist or contains the value zero then + the timer to check for media change does not run. + +Arguments: + + RegistryPath - pointer to the unicode string inside + ...\CurrentControlSet\Services\Cdrom + DeviceNumber - The number of the device object + +Return Value: + + TRUE - Autorun is enabled. + FALSE - no autorun. + +--*/ + +{ +#define ITEMS_TO_QUERY 2 /* always 1 greater than what is searched */ + PRTL_QUERY_REGISTRY_TABLE parameters = NULL; + NTSTATUS status; + LONG zero = 0; + + LONG tmp = 0; + LONG doRun = 0; + + UCHAR buf[32]; + ANSI_STRING paramNum; + + UNICODE_STRING paramStr; + + UNICODE_STRING paramSuffix; + UNICODE_STRING paramPath; + UNICODE_STRING paramDevPath; + + // + // First append \Parameters to the passed in registry path + // + + RtlInitUnicodeString(¶mStr, L"\\Parameters"); + + RtlInitUnicodeString(¶mPath, NULL); + + paramPath.MaximumLength = RegistryPath->Length + + paramStr.Length + + sizeof(WCHAR); + + paramPath.Buffer = ExAllocatePool(PagedPool, paramPath.MaximumLength); + + if(!paramPath.Buffer) { + + DebugPrint((1,"CdRomCheckRegAP: couldn't allocate paramPath\n")); + + return FALSE; + } + + RtlZeroMemory(paramPath.Buffer, paramPath.MaximumLength); + RtlAppendUnicodeToString(¶mPath, RegistryPath->Buffer); + RtlAppendUnicodeToString(¶mPath, paramStr.Buffer); + + DebugPrint((2, "CdRomCheckRegAP: paramPath [%d] = %ws\n", + paramPath.Length, + paramPath.Buffer)); + + // + // build a counted ANSI string that contains + // the suffix for the path + // + + sprintf(buf, "\\Device%d", DeviceNumber); + RtlInitAnsiString(¶mNum, buf); + + // + // Next convert this into a unicode string + // + + status = RtlAnsiStringToUnicodeString(¶mSuffix, ¶mNum, TRUE); + + if(!NT_SUCCESS(status)) { + DebugPrint((1,"CdRomCheckRegAP: couldn't convert paramNum to paramSuffix\n")); + ExFreePool(paramPath.Buffer); + return FALSE; + } + + RtlInitUnicodeString(¶mDevPath, NULL); + + // + // now build the device specific path + // + + paramDevPath.MaximumLength = paramPath.Length + + paramSuffix.Length + + sizeof(WCHAR); + paramDevPath.Buffer = ExAllocatePool(PagedPool, paramDevPath.MaximumLength); + + if(!paramDevPath.Buffer) { + RtlFreeUnicodeString(¶mSuffix); + ExFreePool(paramPath.Buffer); + return FALSE; + } + + RtlZeroMemory(paramDevPath.Buffer, paramDevPath.MaximumLength); + RtlAppendUnicodeToString(¶mDevPath, paramPath.Buffer); + RtlAppendUnicodeToString(¶mDevPath, paramSuffix.Buffer); + + DebugPrint((2, "CdRomCheckRegAP: paramDevPath [%d] = %ws\n", + paramPath.Length, + paramPath.Buffer)); + + parameters = ExAllocatePool(NonPagedPool, + sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY); + + if (parameters) { + + // + // Check for the Autorun value. + // + + RtlZeroMemory(parameters, + (sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY)); + + parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + parameters[0].Name = L"Autorun"; + parameters[0].EntryContext = &doRun; + parameters[0].DefaultType = REG_DWORD; + parameters[0].DefaultData = &zero; + parameters[0].DefaultLength = sizeof(ULONG); + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + RegistryPath->Buffer, + parameters, + NULL, + NULL); + + DebugPrint((2, "CdRomCheckRegAP: cdrom/Autorun flag = %d\n", doRun)); + + RtlZeroMemory(parameters, + (sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY)); + + parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + parameters[0].Name = L"Autorun"; + parameters[0].EntryContext = &tmp; + parameters[0].DefaultType = REG_DWORD; + parameters[0].DefaultData = &doRun; + parameters[0].DefaultLength = sizeof(ULONG); + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + paramPath.Buffer, + parameters, + NULL, + NULL); + + DebugPrint((2, "CdRomCheckRegAP: cdrom/parameters/autorun flag = %d\n", tmp)); + + RtlZeroMemory(parameters, + (sizeof(RTL_QUERY_REGISTRY_TABLE) * ITEMS_TO_QUERY)); + + parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + parameters[0].Name = L"Autorun"; + parameters[0].EntryContext = &doRun; + parameters[0].DefaultType = REG_DWORD; + parameters[0].DefaultData = &tmp; + parameters[0].DefaultLength = sizeof(ULONG); + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + paramDevPath.Buffer, + parameters, + NULL, + NULL); + + DebugPrint((1, "CdRomCheckRegAP: cdrom/parameters/device%d/autorun flag = %d\n", DeviceNumber, doRun)); + + ExFreePool(parameters); + + } + + ExFreePool(paramPath.Buffer); + ExFreePool(paramDevPath.Buffer); + RtlFreeUnicodeString(¶mSuffix); + + DebugPrint((1, "CdRomCheckRegAP: Autoplay for device %d is %s\n", + DeviceNumber, + (doRun ? "on" : "off"))); + + if(doRun) { + return TRUE; + } + + return FALSE; +} + + +BOOLEAN +STDCALL +IsThisASanyo( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR PathId, + IN UCHAR TargetId + ) + +/*++ + +Routine Description: + + This routine is called by DriverEntry to determine whether a Sanyo 3-CD + changer device is present. + +Arguments: + + DeviceObject - Supplies the device object for the 'real' device. + + PathId - + +Return Value: + + TRUE - if an Atapi changer device is found. + +--*/ + +{ + KEVENT event; + PIRP irp; + PCHAR inquiryBuffer; + IO_STATUS_BLOCK ioStatus; + NTSTATUS status; + PSCSI_ADAPTER_BUS_INFO adapterInfo; + ULONG scsiBus; + PINQUIRYDATA inquiryData; + PSCSI_INQUIRY_DATA lunInfo; + + inquiryBuffer = ExAllocatePool(NonPagedPool, 2048); + KeInitializeEvent(&event, NotificationEvent, FALSE); + irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_INQUIRY_DATA, + DeviceObject, + NULL, + 0, + inquiryBuffer, + 2048, + FALSE, + &event, + &ioStatus); + if (!irp) { + return FALSE; + } + + status = IoCallDriver(DeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + if (!NT_SUCCESS(status)) { + return FALSE; + } + + adapterInfo = (PVOID) inquiryBuffer; + + for (scsiBus=0; scsiBus < (ULONG)adapterInfo->NumberOfBuses; scsiBus++) { + + // + // Get the SCSI bus scan data for this bus. + // + + lunInfo = (PVOID) (inquiryBuffer + adapterInfo->BusData[scsiBus].InquiryDataOffset); + + for (;;) { + + if (lunInfo->PathId == PathId && lunInfo->TargetId == TargetId) { + + inquiryData = (PVOID) lunInfo->InquiryData; + + if (RtlCompareMemory(inquiryData->VendorId, "TORiSAN CD-ROM CDR-C", 20) == 20) { + ExFreePool(inquiryBuffer); + return TRUE; + } + + ExFreePool(inquiryBuffer); + return FALSE; + } + + if (!lunInfo->NextInquiryDataOffset) { + break; + } + + lunInfo = (PVOID) (inquiryBuffer + lunInfo->NextInquiryDataOffset); + } + } + + ExFreePool(inquiryBuffer); + return FALSE; +} + + +BOOLEAN +STDCALL +IsThisAnAtapiChanger( + IN PDEVICE_OBJECT DeviceObject, + OUT PULONG DiscsPresent + ) + +/*++ + +Routine Description: + + This routine is called by DriverEntry to determine whether an Atapi + changer device is present. + +Arguments: + + DeviceObject - Supplies the device object for the 'real' device. + + DiscsPresent - Supplies a pointer to the number of Discs supported by the changer. + +Return Value: + + TRUE - if an Atapi changer device is found. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PMECHANICAL_STATUS_INFORMATION_HEADER mechanicalStatusBuffer; + NTSTATUS status; + SCSI_REQUEST_BLOCK srb; + PCDB cdb = (PCDB) &srb.Cdb[0]; + BOOLEAN retVal = FALSE; + + *DiscsPresent = 0; + + // + // Some devices can't handle 12 byte CDB's gracefully + // + + if(deviceExtension->DeviceFlags & DEV_NO_12BYTE_CDB) { + + return FALSE; + + } + + // + // Build and issue the mechanical status command. + // + + mechanicalStatusBuffer = ExAllocatePool(NonPagedPoolCacheAligned, + sizeof(MECHANICAL_STATUS_INFORMATION_HEADER)); + + if (!mechanicalStatusBuffer) { + retVal = FALSE; + } else { + + // + // Build and send the Mechanism status CDB. + // + + RtlZeroMemory(&srb, sizeof(srb)); + + srb.CdbLength = 12; + srb.TimeOutValue = 20; + + cdb->MECH_STATUS.OperationCode = SCSIOP_MECHANISM_STATUS; + cdb->MECH_STATUS.AllocationLength[1] = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER); + + status = ScsiClassSendSrbSynchronous(DeviceObject, + &srb, + mechanicalStatusBuffer, + sizeof(MECHANICAL_STATUS_INFORMATION_HEADER), + FALSE); + + + if (status == STATUS_SUCCESS) { + + // + // Indicate number of slots available + // + + *DiscsPresent = mechanicalStatusBuffer->NumberAvailableSlots; + if (*DiscsPresent > 1) { + retVal = TRUE; + } else { + + // + // If only one disc, no need for this driver. + // + + retVal = FALSE; + } + } else { + + // + // Device doesn't support this command. + // + + retVal = FALSE; + } + + ExFreePool(mechanicalStatusBuffer); + } + + return retVal; +} + + +BOOLEAN +STDCALL +IsThisAMultiLunDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PDEVICE_OBJECT PortDeviceObject + ) +/*++ + +Routine Description: + + This routine is called to determine whether a multi-lun + device is present. + +Arguments: + + DeviceObject - Supplies the device object for the 'real' device. + +Return Value: + + TRUE - if a Multi-lun device is found. + +--*/ +{ + PCHAR buffer; + PSCSI_INQUIRY_DATA lunInfo; + PSCSI_ADAPTER_BUS_INFO adapterInfo; + PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; + PINQUIRYDATA inquiryData; + ULONG scsiBus; + NTSTATUS status; + UCHAR lunCount = 0; + + status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer); + + if (!NT_SUCCESS(status)) { + DebugPrint((1,"IsThisAMultiLunDevice: ScsiClassGetInquiryData failed\n")); + return FALSE; + } + + adapterInfo = (PVOID) buffer; + + // + // For each SCSI bus this adapter supports ... + // + + for (scsiBus=0; scsiBus < adapterInfo->NumberOfBuses; scsiBus++) { + + // + // Get the SCSI bus scan data for this bus. + // + + lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset); + + while (adapterInfo->BusData[scsiBus].InquiryDataOffset) { + + inquiryData = (PVOID)lunInfo->InquiryData; + + if ((lunInfo->PathId == deviceExtension->PathId) && + (lunInfo->TargetId == deviceExtension->TargetId) && + (inquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE)) { + + DebugPrint((1,"IsThisAMultiLunDevice: Vendor string is %.24s\n", + inquiryData->VendorId)); + + // + // If this device has more than one cdrom-type lun then we + // won't support autoplay on it + // + + if (lunCount++) { + ExFreePool(buffer); + return TRUE; + } + } + + // + // Get next LunInfo. + // + + if (lunInfo->NextInquiryDataOffset == 0) { + break; + } + + lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset); + } + } + + ExFreePool(buffer); + return FALSE; + +} + + +NTSTATUS +STDCALL +CdRomUpdateGeometryCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine andles the completion of the test unit ready irps + used to determine if the media has changed. If the media has + changed, this code signals the named event to wake up other + system services that react to media change (aka AutoPlay). + +Arguments: + + DeviceObject - the object for the completion + Irp - the IRP being completed + Context - the SRB from the IRP + +Return Value: + + NTSTATUS + +--*/ + +{ + PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK) Context; + PREAD_CAPACITY_DATA readCapacityBuffer; + PDEVICE_EXTENSION deviceExtension; + PIO_STACK_LOCATION irpStack; + NTSTATUS status; + BOOLEAN retry; + ULONG retryCount; + ULONG lastSector; + PIRP originalIrp; + PCDROM_DATA cddata; + + // + // Get items saved in the private IRP stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(Irp); + retryCount = (ULONG) irpStack->Parameters.Others.Argument1; + originalIrp = (PIRP) irpStack->Parameters.Others.Argument2; + + if (!DeviceObject) { + DeviceObject = irpStack->DeviceObject; + } + ASSERT(DeviceObject); + + deviceExtension = DeviceObject->DeviceExtension; + cddata = (PCDROM_DATA) (deviceExtension + 1); + readCapacityBuffer = srb->DataBuffer; + + if ((NT_SUCCESS(Irp->IoStatus.Status)) && (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)) { + PFOUR_BYTE from; + PFOUR_BYTE to; + + DebugPrint((2, "CdRomUpdateCapacityCompletion: [%lx] successful completion of buddy-irp %lx\n", originalIrp, Irp)); + // + // Copy sector size from read capacity buffer to device extension + // in reverse byte order. + // + + from = (PFOUR_BYTE) &readCapacityBuffer->BytesPerBlock; + to = (PFOUR_BYTE) &deviceExtension->DiskGeometry->BytesPerSector; + to->Byte0 = from->Byte3; + to->Byte1 = from->Byte2; + to->Byte2 = from->Byte1; + to->Byte3 = from->Byte0; + + // + // Using the new BytesPerBlock, calculate and store the SectorShift. + // + + WHICH_BIT(deviceExtension->DiskGeometry->BytesPerSector, deviceExtension->SectorShift); + + // + // Copy last sector in reverse byte order. + // + + from = (PFOUR_BYTE) &readCapacityBuffer->LogicalBlockAddress; + to = (PFOUR_BYTE) &lastSector; + to->Byte0 = from->Byte3; + to->Byte1 = from->Byte2; + to->Byte2 = from->Byte1; + to->Byte3 = from->Byte0; + deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1); + + // + // Calculate number of cylinders. + // + + deviceExtension->DiskGeometry->Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64)); + deviceExtension->PartitionLength.QuadPart = + (deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift); + deviceExtension->DiskGeometry->MediaType = RemovableMedia; + + // + // Assume sectors per track are 32; + // + + deviceExtension->DiskGeometry->SectorsPerTrack = 32; + + // + // Assume tracks per cylinder (number of heads) is 64. + // + + deviceExtension->DiskGeometry->TracksPerCylinder = 64; + + } else { + + DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] unsuccessful completion of buddy-irp %lx (status - %lx)\n", originalIrp, Irp, Irp->IoStatus.Status)); + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ScsiClassReleaseQueue(DeviceObject); + } + + retry = ScsiClassInterpretSenseInfo(DeviceObject, + srb, + IRP_MJ_SCSI, + 0, + retryCount, + &status); + if (retry) { + retryCount--; + if (retryCount) { + PCDB cdb; + + DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] Retrying request %lx .. thread is %lx\n", originalIrp, Irp, Irp->Tail.Overlay.Thread)); + // + // set up a one shot timer to get this process started over + // + + irpStack->Parameters.Others.Argument1 = (PVOID) retryCount; + irpStack->Parameters.Others.Argument2 = (PVOID) originalIrp; + irpStack->Parameters.Others.Argument3 = (PVOID) 2; + + // + // Setup the IRP to be submitted again in the timer routine. + // + + irpStack = IoGetNextIrpStackLocation(Irp); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + IoSetCompletionRoutine(Irp, + CdRomUpdateGeometryCompletion, + srb, + TRUE, + TRUE, + TRUE); + + // + // Set up the SRB for read capacity. + // + + srb->CdbLength = 10; + srb->TimeOutValue = deviceExtension->TimeOutValue; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + srb->DataTransferLength = sizeof(READ_CAPACITY_DATA); + + // + // Set up the CDB + // + + cdb = (PCDB) &srb->Cdb[0]; + cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + cdb->CDB10.LogicalUnitNumber = deviceExtension->Lun; + + // + // Requests queued onto this list will be sent to the + // lower level driver during CdRomTickHandler + // + + ExInterlockedInsertHeadList(&cddata->TimerIrpList, + &Irp->Tail.Overlay.ListEntry, + &cddata->TimerIrpSpinLock); + + return STATUS_MORE_PROCESSING_REQUIRED; + } else { + + // + // This has been bounced for a number of times. Error the + // original request. + // + + originalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + RtlZeroMemory(deviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY)); + deviceExtension->DiskGeometry->BytesPerSector = 2048; + deviceExtension->SectorShift = 11; + deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff); + deviceExtension->DiskGeometry->MediaType = RemovableMedia; + } + } else { + + // + // Set up reasonable defaults + // + + RtlZeroMemory(deviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY)); + deviceExtension->DiskGeometry->BytesPerSector = 2048; + deviceExtension->SectorShift = 11; + deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff); + deviceExtension->DiskGeometry->MediaType = RemovableMedia; + } + } + + // + // Free resources held. + // + + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + if (Irp->MdlAddress) { + IoFreeMdl(Irp->MdlAddress); + } + IoFreeIrp(Irp); + if (originalIrp->Tail.Overlay.Thread) { + + DebugPrint((2, "CdRomUpdateCapacityCompletion: [%lx] completing original IRP\n", originalIrp)); + IoCompleteRequest(originalIrp, IO_DISK_INCREMENT); + + } else { + DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] original irp has " + "no thread\n", + originalIrp + )); + } + + // + // It's now safe to either start the next request or let the waiting ioctl + // request continue along it's merry way + // + + IoStartNextPacket(DeviceObject, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +STDCALL +CdRomUpdateCapacity( + IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP IrpToComplete, + IN OPTIONAL PKEVENT IoctlEvent + ) + +/*++ + +Routine Description: + + This routine updates the capacity of the disk as recorded in the device extension. + It also completes the IRP given with STATUS_VERIFY_REQUIRED. This routine is called + when a media change has occurred and it is necessary to determine the capacity of the + new media prior to the next access. + +Arguments: + + DeviceExtension - the device to update + IrpToComplete - the request that needs to be completed when done. + +Return Value: + + NTSTATUS + +--*/ + +{ + PCDB cdb; + PIRP irp; + PSCSI_REQUEST_BLOCK srb; + PREAD_CAPACITY_DATA capacityBuffer; + PIO_STACK_LOCATION irpStack; + PUCHAR senseBuffer; + NTSTATUS status; + + irp = IoAllocateIrp((CCHAR)(DeviceExtension->DeviceObject->StackSize+1), + FALSE); + + if (irp) { + + srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK)); + if (srb) { + capacityBuffer = ExAllocatePool(NonPagedPoolCacheAligned, + sizeof(READ_CAPACITY_DATA)); + + if (capacityBuffer) { + + + senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE); + + if (senseBuffer) { + + irp->MdlAddress = IoAllocateMdl(capacityBuffer, + sizeof(READ_CAPACITY_DATA), + FALSE, + FALSE, + (PIRP) NULL); + + if (irp->MdlAddress) { + + // + // Have all resources. Set up the IRP to send for the capacity. + // + + IoSetNextIrpStackLocation(irp); + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + irp->Flags = 0; + irp->UserBuffer = NULL; + + // + // Save the device object and retry count in a private stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->DeviceObject = DeviceExtension->DeviceObject; + irpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES; + irpStack->Parameters.Others.Argument2 = (PVOID) IrpToComplete; + + // + // Construct the IRP stack for the lower level driver. + // + + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; + irpStack->Parameters.Scsi.Srb = srb; + IoSetCompletionRoutine(irp, + CdRomUpdateGeometryCompletion, + srb, + TRUE, + TRUE, + TRUE); + // + // Prepare the MDL + // + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + + // + // Set up the SRB for read capacity. + // + + RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); + RtlZeroMemory(senseBuffer, SENSE_BUFFER_SIZE); + srb->CdbLength = 10; + srb->TimeOutValue = DeviceExtension->TimeOutValue; + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->PathId = DeviceExtension->PathId; + srb->TargetId = DeviceExtension->TargetId; + srb->Lun = DeviceExtension->Lun; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + srb->DataBuffer = capacityBuffer; + srb->DataTransferLength = sizeof(READ_CAPACITY_DATA); + srb->OriginalRequest = irp; + srb->SenseInfoBuffer = senseBuffer; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Set up the CDB + // + + cdb = (PCDB) &srb->Cdb[0]; + cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + cdb->CDB10.LogicalUnitNumber = DeviceExtension->Lun; + + // + // Set the return value in the IRP that will be completed + // upon completion of the read capacity. + // + + IrpToComplete->IoStatus.Status = STATUS_VERIFY_REQUIRED; + IoMarkIrpPending(IrpToComplete); + + status = IoCallDriver(DeviceExtension->PortDeviceObject, irp); + + // + // status is not checked because the completion routine for this + // IRP will always get called and it will free the resources. + // + + return STATUS_PENDING; + + } else { + ExFreePool(senseBuffer); + ExFreePool(capacityBuffer); + ExFreePool(srb); + IoFreeIrp(irp); + } + } else { + ExFreePool(capacityBuffer); + ExFreePool(srb); + IoFreeIrp(irp); + } + } else { + ExFreePool(srb); + IoFreeIrp(irp); + } + } else { + IoFreeIrp(irp); + } + } + + return STATUS_INSUFFICIENT_RESOURCES; +} + + +NTSTATUS +STDCALL +CdRomClassIoctlCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine signals the event used by CdRomDeviceControl to synchronize + class driver (and lower level driver) ioctls with cdrom's startio routine. + The irp completion is short-circuited so that CdRomDeviceControl can + reissue it once it wakes up. + +Arguments: + + DeviceObject - the device object + Irp - the request we are synchronizing + Context - a PKEVENT that we need to signal + +Return Value: + + NTSTATUS + +--*/ + +{ + PKEVENT syncEvent = (PKEVENT) Context; + + DebugPrint((2, "CdRomClassIoctlCompletion: setting event for irp %#08lx\n", + Irp + )); + + KeSetEvent(syncEvent, IO_DISK_INCREMENT, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} diff --git a/reactos/drivers/storage/cdrom/cdrom.rbuild b/reactos/drivers/storage/cdrom/cdrom.rbuild index ca6074b4ffc..a219abf6ab6 100644 --- a/reactos/drivers/storage/cdrom/cdrom.rbuild +++ b/reactos/drivers/storage/cdrom/cdrom.rbuild @@ -1,10 +1,12 @@ - + ntoskrnl hal class2 + scsiport .. cdrom.c + findscsi.c cdrom.rc diff --git a/reactos/drivers/storage/cdrom/findscsi.c b/reactos/drivers/storage/cdrom/findscsi.c new file mode 100644 index 00000000000..46e2ff34526 --- /dev/null +++ b/reactos/drivers/storage/cdrom/findscsi.c @@ -0,0 +1,315 @@ +/* + * PROJECT: ReactOS Storage Stack + * LICENSE: DDK - see license.txt in the root dir + * FILE: drivers/storage/cdrom/cdrom.c + * PURPOSE: CDROM driver + * PROGRAMMERS: Based on a source code sample from Microsoft NT4 DDK + */ + +#include +#include +#include +#include +#include +#include +#include + +//#define NDEBUG +#include + +NTSTATUS +STDCALL +FindScsiAdapter ( + IN HANDLE KeyHandle, + IN UNICODE_STRING ScsiUnicodeString[], + OUT PUCHAR IntermediateController + ); + +#define INIT_OPEN_KEY(name, rootHandle, pNewHandle) \ + InitializeObjectAttributes( \ + &objectAttributes, \ + (name), \ + OBJ_CASE_INSENSITIVE, \ + (rootHandle), \ + NULL \ + ); \ + \ + status = ZwOpenKey( \ + (pNewHandle), \ + KEY_READ | KEY_ENUMERATE_SUB_KEYS, \ + &objectAttributes \ + ); + + +NTSTATUS +STDCALL +FindScsiAdapter ( + IN HANDLE KeyHandle, + IN UNICODE_STRING ScsiUnicodeString[], + OUT UCHAR *IntermediateController + ) + +/*++ + + Routine Description: + + Recursive routine to walk registry tree under KeyHandle looking for + location of ScsiAdapter and other valid controllers that the ScsiAdapter + might hang off of. When ScsiAdapter is found, FindScsiAdapter + returns an ARC name for the intervening controller(s) between the + original key and ScsiAdapter. + + Arguments: + + KeyHandle -- Handle of open registry key (somewhere under + \Registry\Machine\Hardware\Description\System) + + ScsiUnicodeString -- NT name of SCSI device being sought in the registry + + IntermediateController -- Null terminated buffer which this routine fills with + ARC name for intervening controller(s). Null is returned + if ScsiAdapter sits at the root or if it is not found. + + Return Value: + + STATUS_SUCCESS -- IntermediateController set to something like multi(1) + + STATUS_OBJECT_PATH_NOT_FOUND -- all ok, but no ScsiAdapter + (with correct scsi id & lun info) found; In this case + IntermediateController is explicitly set to null. + + Other status codes as returned by open\enumerate registry routines + may also be returned. + +--*/ + +{ +#if 0 + NTSTATUS status; + ULONG index; + ULONG resultLength; + UCHAR lowerController[64]; + BOOLEAN validControllerNumber; + UNICODE_STRING unicodeString[64]; + OBJECT_ATTRIBUTES objectAttributes; + + HANDLE controllerHandle; + HANDLE controllerNumberHandle; + ULONG controllerIndex; + ULONG controllerNumberIndex; + UCHAR keyBuffer[256]; // Allow for variable length name at end + UCHAR numberKeyBuffer[64]; + PKEY_BASIC_INFORMATION pControllerKeyInformation; + PKEY_BASIC_INFORMATION pControllerNumberKeyInformation; + + // TODO: Any PAGED_CODE stuff... + + // + // Walk enumerated subkeys, looking for valid controllers + // + + for (controllerIndex = 0; TRUE; controllerIndex++) { + + // + // Ensure pControllerKeyInformation->Name is null terminated + // + + RtlZeroMemory(keyBuffer, sizeof(keyBuffer)); + + pControllerKeyInformation = (PKEY_BASIC_INFORMATION) keyBuffer; + + status = ZwEnumerateKey( + KeyHandle, + controllerIndex, + KeyBasicInformation, + pControllerKeyInformation, + sizeof(keyBuffer), + &resultLength + ); + + if (!NT_SUCCESS(status)) { + + if (status != STATUS_NO_MORE_ENTRIES) { + DebugPrint ((2, "FindScsiAdapter: Error 0x%x enumerating key\n", status)); + return(status); + } + + break; // return NOT_FOUND + } + + DebugPrint ((3, "FindScsiAdapter: Found Adapter=%S\n", pControllerKeyInformation->Name)); + + if (!_wcsicmp(pControllerKeyInformation->Name, L"ScsiAdapter")) { + + // + // Found scsi, now verify that it's the same one we're trying to initialize. + // + + INIT_OPEN_KEY (ScsiUnicodeString, KeyHandle, &controllerHandle); + + ZwClose(controllerHandle); + + if (NT_SUCCESS(status)) { + + // + // Found correct scsi, now build ARC name of IntermediateController + // (i.e. the intervening controllers, or everything above ScsiAdapter) + // start with null, and build string one controller at a time as we + // return up the recursively called routine. + // + + IntermediateController = "\0"; + + return (STATUS_SUCCESS); + } + + // + // Found ScsiAdapter, but wrong scsi id or Lun info doesn't match, + // (ignore other ZwOpenKey errors &) keep looking... + // + + } + + else if (!_wcsicmp(pControllerKeyInformation->Name, L"MultifunctionAdapter") || + !_wcsicmp(pControllerKeyInformation->Name, L"EisaAdapter")) { + + // + // This is a valid controller that may have ScsiAdapter beneath it. + // Open controller & walk controller's subkeys: 0, 1, 2, etc.... + // + + RtlInitUnicodeString (unicodeString, pControllerKeyInformation->Name); + + INIT_OPEN_KEY (unicodeString, KeyHandle, &controllerHandle); + + if (!NT_SUCCESS(status)) { + DebugPrint ((2, "FindScsiAdapter: ZwOpenKey got status = %x \n", status)); + ZwClose (controllerHandle); + return (status); + } + + + + for (controllerNumberIndex = 0; TRUE; controllerNumberIndex++) { + + RtlZeroMemory(numberKeyBuffer, sizeof(numberKeyBuffer)); + + pControllerNumberKeyInformation = (PKEY_BASIC_INFORMATION) numberKeyBuffer; + + status = ZwEnumerateKey( + controllerHandle, + controllerNumberIndex, + KeyBasicInformation, + pControllerNumberKeyInformation, + sizeof(numberKeyBuffer), + &resultLength + ); + + if (!NT_SUCCESS(status)) { + + if (status != STATUS_NO_MORE_ENTRIES) { + DebugPrint ((2, "FindScsiAdapter: Status %x enumerating key\n", status)); + ZwClose(controllerHandle); + return (status); + } + + ZwClose(controllerHandle); + + break; // next controller + } + + DebugPrint ((3, "FindScsiAdapter: Found Adapter #=%S\n", pControllerNumberKeyInformation->Name)); + + validControllerNumber = TRUE; + + for (index = 0; index < pControllerNumberKeyInformation->NameLength / 2; index++) { + + if (!isxdigit(pControllerNumberKeyInformation->Name[index])) { + validControllerNumber = FALSE; + break; + } + + } + + if (validControllerNumber) { + + // + // Found valid controller and controller number: check children for scsi. + // + + RtlInitUnicodeString (unicodeString, pControllerNumberKeyInformation->Name); + + INIT_OPEN_KEY (unicodeString, controllerHandle, &controllerNumberHandle); + + if (!NT_SUCCESS(status)) { + DebugPrint ((2, "FindScsiAdapter: Status %x opening controller number key\n", status)); + ZwClose(controllerNumberHandle); + ZwClose(controllerHandle); + return (status); + } + + RtlZeroMemory(lowerController, sizeof(lowerController)); + + status = FindScsiAdapter( + controllerNumberHandle, + ScsiUnicodeString, + &lowerController[0] + ); + + ZwClose(controllerNumberHandle); + + if (NT_SUCCESS(status)) { + + // + // SUCCESS! + // + // Scsi adapter DOES exist under this node, + // prepend Arc Name for the current adapter to whatever was returned + // by other calls to this routine. + // + + if (!_wcsicmp(pControllerKeyInformation->Name, L"MultifunctionAdapter")) { + sprintf(IntermediateController, "multi(0)%s", lowerController); + } else { + sprintf(IntermediateController, "eisa(0)%s", lowerController); + } + + ZwClose(controllerHandle); + + return(STATUS_SUCCESS); + } + + else if (status != STATUS_OBJECT_PATH_NOT_FOUND) { + ZwClose(controllerHandle); + return(status); + } + + // + // Scsi not found under this controller number, check next one + // + + } // if validControllerNumber + + } // for controllerNumberIndex + + + + // + // ScsiAdapter not found under this controller + // + + ZwClose(controllerHandle); + + } // else if valid subkey (i.e., scsi, multi, eisa) + + } // for controllerIndex + + // + // ScsiAdapter not found under key we were called with + // + + IntermediateController = "\0"; +#endif + return (STATUS_OBJECT_PATH_NOT_FOUND); +} + diff --git a/reactos/drivers/storage/cdrom/license.txt b/reactos/drivers/storage/cdrom/license.txt new file mode 100644 index 00000000000..29389c47231 --- /dev/null +++ b/reactos/drivers/storage/cdrom/license.txt @@ -0,0 +1,53 @@ +Window NT Device Driver Kit +END-USER LICENSE AGREEMENT FOR MICROSOFT SOFTWARE + +IMPORTANT-READ CAREFULLY: This Microsoft End-User License Agreement ("EULA") is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation for the Microsoft software product identified above, which includes computer software and associated media and printed materials, and may include "online" or electronic documentation ("SOFTWARE PRODUCT" or "SOFTWARE"). By installing, copying, or otherwise using the SOFTWARE PRODUCT, you agree to be bound by the terms of this EULA. If you do not agree to the terms of this EULA, promptly return the unused SOFTWARE PRODUCT to the place from which you obtained it for a full refund, or if you received the SOFTWARE PRODUCT as part of a subscription or other service from Microsoft, you may cancel the subscription and receive a refund of a prorata portion of the subscription price. + +SOFTWARE PRODUCT LICENSE +The SOFTWARE PRODUCT is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE PRODUCT is licensed, not sold. To develop Windows NT(tm) device drivers, you will need to utilize two computers, one for development and one for debugging. + +1. GRANT OF LICENSE. This EULA grants you the following limited, non-exclusive rights: +* SOFTWARE PRODUCT. (a) You may make two (2) copies of the SOFTWARE PRODUCT for installation and use on two (2) computers, one for use in development and one for use in debugging to design, develop, and test your software product(s), including but not limited to device driver(s) and other software products, for use with Microsoft(r) Windows(r) or Windows NT ("Software Product"). You may make an additional copy of the Windows NT Workstation operating system (licensed and provided separately) for use on a single computer used for debugging purposes. +* Microsoft Developer Network Subscriber. If you acquired the SOFTWARE PRODUCT through a subscription to the Microsoft Developer Network, and you are either an individual developer or an individual designated within a single entity, you are granted the following additional rights with respect to the SOFTWARE PRODUCT: (a) you may make and use copies of the SOFTWARE PRODUCT on up to ten (10) separate computers, provided that you are the only individual using the SOFTWARE PRODUCT on each such computer, and (b) if you are a single entity, you may designate one individual within your organization to have the right to use the SOFTWARE PRODUCT in the manner described herein. +* SAMPLE CODE. You may modify the sample source code ("Sample Code") to design, develop and test your Software Product, and reproduce and distribute the Sample Code with such modifications in source and object code forms, provided that you comply with the Distribution Requirements described below. +* DISTRIBUTION REQUIREMENTS. You may copy and redistribute the Sample Code as described above, provided that (a) you distribute the Sample Code only in conjunction with and as a part of your Software Product; (b) you do not make any statements to the effect or which imply that your Software Product is "certified" by Microsoft or that its performance is guaranteed by Microsoft; (c) you do not use Microsoft's name, logo, or trademarks to market your Software Product; (d) you include a valid copyright notice on your Software Product; and (e) you indemnify, hold harmless, and defend Microsoft from and against any claims or lawsuits, including attorneys' fees, that arise or result from the use or distribution of your Software Product. Contact Microsoft for the applicable royalties due and other licensing terms for all other uses and/or distribution of the Sample Code +* Microsoft reserves all rights not expressly granted to you. + +2. COPYRIGHT. All right, title, and copyrights in and to the SOFTWARE PRODUCT (including but not limited to any images, photographs, animations, video, audio, music, text, and "applets" incorporated into the SOFTWARE PRODUCT), and any copies of the SOFTWARE PRODUCT, are owned by Microsoft or its suppliers. The SOFTWARE PRODUCT is protected by copyright laws and international treaty provisions. Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted material except that you may either (a) make one copy of the SOFTWARE PRODUCT solely for backup or archival purposes, or (b) install the SOFTWARE PRODUCT on a single computer, provided you keep the original solely for backup or archival purposes. You may not copy the printed materials accompanying the SOFTWARE PRODUCT. + +3. PRERELEASE CODE. The SOFTWARE PRODUCT may contain PRERELEASE CODE that is not at the level of performance and compatibility of the final, generally available, product offering. These portions of the SOFTWARE PRODUCT may not operate correctly and may be substantially modified prior to first commercial shipment. Microsoft is not obligated to make this or any later version of the SOFTWARE PRODUCT commercially available. Microsoft grants you the right to distribute test versions of your Application created using the PRERELEASE CODE provided you comply with the Distribution Requirements described in Section 1 and the following additional provisions: (a) you must mark the test version of your Application "BETA" and (b) you are solely responsible for updating your customers with versions of your Application that operate satisfactorily with the final commercial release of the PRERELEASE CODE. + +4. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS. +( Limitations on Reverse Engineering, Decompilation, and Disassembly. You may not reverse-engineer, decompile, or disassemble the SOFTWARE PRODUCT, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation. +( Rental. You may not rent or lease the SOFTWARE PRODUCT. +( Software Transfer. You may permanently transfer all of your rights under this EULA, provided you retain no copies, you transfer all of the SOFTWARE PRODUCT (including all component parts, the media and printed materials, any upgrades, this EULA, and, if applicable, the Certificate of Authenticity), and the recipient agrees to the terms of this EULA. If the SOFTWARE PRODUCT is an upgrade, any transfer must include all prior versions of the SOFTWARE PRODUCT. +( Termination. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the SOFTWARE PRODUCT and all of its component parts. + +5. EXPORT RESTRICTIONS. You agree that neither you nor your customers intend to or will, directly or indirectly, export or transmit (a) the SOFTWARE PRODUCT or related documentation and technical data or (b) your Application as described in Section 1 of this Agreement (or any part thereof), or process, or service that is the direct product of the SOFTWARE PRODUCT to any country to which such export or transmission is restricted by any applicable U.S. regulation or statute, without the prior written consent, if required, of the Bureau of Export Administration of the U.S. Department of Commerce, or such other governmental entity as may have jurisdiction over such export or transmission. + +NO WARRANTIES. To the maximum extent permitted by applicable law, Microsoft expressly disclaims any warranty for the SOFTWARE PRODUCT. The SOFTWARE PRODUCT and any related documentation are provided "as is" without warranty of any kind, either express or implied, including, without limitation, the implied warranties of merchantability or fitness for a particular purpose. The entire risk arising out of use or performance of the SOFTWARE PRODUCT remains with you. + +LIMITATION OF LIABILITY. Microsoft's entire liability and your exclusive remedy under this EULA shall not exceed one hundred dollars (US$100.00). + +NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no event shall Microsoft or its suppliers be liable for any damages whatsoever (including, without limitation, damages for loss of business profit, business interruption, loss of business information, or any other pecuniary loss) arising out of the use or inability to use this Microsoft product, even if Microsoft has been advised of the possibility of such damages. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you. + +U.S. GOVERNMENT RESTRICTED RIGHTS.The SOFTWARE and documentation are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer Software -- Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer is Microsoft Corporation/One Microsoft Way/Redmond, WA 98052-6399. + +If you acquired this product in the United States, this Agreement is governed by the laws of the State of Washington. If you acquired this product outside the United States, local law may apply. + +Should you have any questions concerning this Agreement, if you desire rights to use the product beyond what is listed here, or if you desire to contact Microsoft for any reason, please contact your local Microsoft subsidiary or sales office or write to: Microsoft Sales and Service, One Microsoft Way, Redmond, WA 98052-6399. + +Si vous avez acquis votre produit Microsoft au CANADA, la garantie limitée suivante vous concerne : + +GARANTIE LIMITÉE + +EXCLUSION DE GARANTIES. Microsoft renonce entièrement à toute garantie pour le LOGICIEL. Le LOGICIEL et toute autre documentation s'y rapportant sont fournis «comme tels» sans aucune garantie quelle qu'elle soit, expresse ou implicite, y compris, mais ne se limitant pas, aux garanties implicites de la qualité marchande ou un usage particulier. Le risque total découlant de l'utilisation ou de la performance du LOGICIEL est entre vos mains. + +RESPONSABILITÉ LIMITÉE. La seule obligation de Microsoft et votre recours exclusif concernant ce contrat n'excèderont pas cent dollars (US$100.00). + +ABSENCE DE RESPONSABILITÉ POUR LES DOMMAGES INDIRECTS. Microsoft ou ses fournisseurs ne pourront être tenus responsables en aucune circonstance de tout dommage quel qu'il soit (y compris mais non de façon limitative aux dommages directs ou indirects causés par la perte de bénéfices commerciaux, l'interruption des affaires, la perte d'information commerciale ou toute autre perte pécuniaire) résultant de l'utilisation ou de l'impossibilité d'utilisation de ce produit, et ce, même si la société Microsoft a été avisée de l'éventualité de tels dommages. Certains états/juridictions ne permettent pas l'exclusion ou la limitation de responsabilité relative aux dommages indirects ou consécutifs, et la limitation ci-dessus peut ne pas s'appliquer à votre égard. + +La présente Convention est régie par les lois de la province d'Ontario, Canada. Chacune des parties à la présente reconnaît irrévocablement la compétence des tribunaux de la province d'Ontario et consent à instituer tout litige qui pourrait découler de la présente auprès des tribunaux situés dans le district judiciaire de York, province d'Ontario. + +Au cas où vous auriez des questions concernant cette licence ou que vous désiriez vous mettre en rapport avec Microsoft pour quelque raison que ce soit, veuillez contacter la succursale Microsoft desservant votre pays, dont l'adresse est fournie dans ce produit, ou écrire à : Microsoft Customer Sales and Service, One Microsoft Way, Redmond, Washington 98052-6399. + diff --git a/reactos/drivers/storage/class2/class2.c b/reactos/drivers/storage/class2/class2.c index abd33b4c33e..8914d99b428 100644 --- a/reactos/drivers/storage/class2/class2.c +++ b/reactos/drivers/storage/class2/class2.c @@ -1,36 +1,10 @@ /* - * ReactOS kernel - * Copyright (C) 2001, 2002, 2003 ReactOS Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * PROJECT: ReactOS Storage Stack + * LICENSE: DDK - see license.txt in the root dir + * FILE: drivers/storage/class2/class2.c + * PURPOSE: SCSI Class driver routines + * PROGRAMMERS: Based on a source code sample from Microsoft NT4 DDK */ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: services/storage/class2/class2.c - * PURPOSE: SCSI class driver - * PROGRAMMER: Eric Kohl (ekohl@rz-online.de) - */ - -/* - * TODO: - * - finish ScsiClassDeviceControl(). - */ - -/* INCLUDES *****************************************************************/ #include #include @@ -38,2539 +12,4754 @@ #include #include -#define NDEBUG +//#define NDEBUG #include -#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) - -#define VERSION "0.0.2" - #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24)) -#define TAG_SRBT TAG('S', 'r', 'b', 'T') -#define INQUIRY_DATA_SIZE 2048 -#define START_UNIT_TIMEOUT 30 -/* - * FIXME: - * Create a macro, which rounds up a size value to the next multiple of two. - */ -#define SENSEINFO_ALIGNMENT 32 +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, ScsiClassGetInquiryData) +#pragma alloc_text(PAGE, ScsiClassInitialize) +#pragma alloc_text(PAGE, ScsiClassGetCapabilities) +#pragma alloc_text(PAGE, ScsiClassSendSrbSynchronous) +#pragma alloc_text(PAGE, ScsiClassClaimDevice) +#pragma alloc_text(PAGE, ScsiClassSendSrbAsynchronous) +#endif -static NTSTATUS STDCALL -ScsiClassCreateClose(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); -static NTSTATUS STDCALL -ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +#define INQUIRY_DATA_SIZE 2048 +#define START_UNIT_TIMEOUT 30 -static NTSTATUS STDCALL -ScsiClassDeviceDispatch(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +NTSTATUS +STDCALL +ScsiClassCreateClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); -static NTSTATUS STDCALL -ScsiClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +NTSTATUS +STDCALL +ScsiClassReadWrite( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); -static VOID -ScsiClassRetryRequest (PDEVICE_OBJECT DeviceObject, - PIRP Irp, - PSCSI_REQUEST_BLOCK Srb, - BOOLEAN Associated); +NTSTATUS +STDCALL +ScsiClassDeviceControlDispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ); -static NTSTATUS STDCALL -ScsiClassCheckVerifyCompletion (IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context); +NTSTATUS +STDCALL +ScsiClassDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ); -/* FUNCTIONS ****************************************************************/ +NTSTATUS +STDCALL +ScsiClassInternalIoControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); -/********************************************************************** - * NAME EXPORTED - * DriverEntry - * - * DESCRIPTION - * This function initializes the driver. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DriverObject - * System allocated Driver Object for this driver. - * RegistryPath - * Name of registry driver service key. - * - * RETURNS - * Status - */ +NTSTATUS +STDCALL +ScsiClassShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); -NTSTATUS STDCALL -DriverEntry(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath) -{ - DPRINT("Class Driver %s\n", VERSION); - return(STATUS_SUCCESS); -} +NTSTATUS +STDCALL +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +// +// Class internal routines +// VOID -ScsiClassDebugPrint(IN ULONG DebugPrintLevel, - IN PCHAR DebugMessage, - ...) +STDCALL +RetryRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PSCSI_REQUEST_BLOCK Srb, + BOOLEAN Associated + ); + +VOID +STDCALL +StartUnit( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +STDCALL +ClassIoCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +STDCALL +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) { - char Buffer[256]; - va_list ap; - -#if 0 - if (DebugPrintLevel > InternalDebugLevel) - return; -#endif - - va_start(ap, DebugMessage); - vsprintf(Buffer, DebugMessage, ap); - va_end(ap); - - DbgPrint(Buffer); + return STATUS_SUCCESS; } + +ULONG +STDCALL +ScsiClassInitialize( + IN PVOID Argument1, + IN PVOID Argument2, + IN PCLASS_INIT_DATA InitializationData + ) + +/*++ + +Routine Description: + + This routine is called by a class driver during its + DriverEntry routine to initialize the driver. + +Arguments: + + Argument1 - Driver Object. + Argument2 - Registry Path. + InitializationData - Device-specific driver's initialization data. + +Return Value: + + A valid return code for a DriverEntry routine. + +--*/ -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassAsynchronousCompletion(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context) { - PCOMPLETION_CONTEXT CompletionContext; - PSCSI_REQUEST_BLOCK Srb; - CompletionContext = (PCOMPLETION_CONTEXT) Context; - Srb = &CompletionContext->Srb; - /* Release the queue if it is frozen */ - if (Srb->Function == SRB_FUNCTION_EXECUTE_SCSI && - Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) - { - ScsiClassReleaseQueue (CompletionContext->DeviceObject); + PDRIVER_OBJECT DriverObject = Argument1; + ULONG portNumber = 0; + PDEVICE_OBJECT portDeviceObject; + NTSTATUS status; + STRING deviceNameString; + UNICODE_STRING unicodeDeviceName; + PFILE_OBJECT fileObject; + CCHAR deviceNameBuffer[256]; + BOOLEAN deviceFound = FALSE; + + DebugPrint((3,"\n\nSCSI Class Driver\n")); + + // + // Validate the length of this structure. This is effectively a + // version check. + // + + if (InitializationData->InitializationDataSize > sizeof(CLASS_INIT_DATA)) { + + DebugPrint((0,"ScsiClassInitialize: Class driver wrong version\n")); + return (ULONG) STATUS_REVISION_MISMATCH; } - /* Release the completion context and the IRP */ - if (Irp->MdlAddress != NULL) - { - MmUnlockPages (Irp->MdlAddress); - IoFreeMdl (Irp->MdlAddress); - Irp->MdlAddress = NULL; - } - ExFreePool (CompletionContext); - IoFreeIrp (Irp); + // + // Check that each required entry is not NULL. Note that Shutdown, Flush and Error + // are not required entry points. + // - return STATUS_MORE_PROCESSING_REQUIRED; -} + if ((!InitializationData->ClassFindDevices) || + (!InitializationData->ClassDeviceControl) || + (!((InitializationData->ClassReadWriteVerification) || + (InitializationData->ClassStartIo)))) { + DebugPrint((0, + "ScsiClassInitialize: Class device-specific driver missing required entry\n")); -/* - * @implemented - */ -VOID STDCALL -ScsiClassBuildRequest(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION CurrentIrpStack; - PIO_STACK_LOCATION NextIrpStack; - LARGE_INTEGER StartingOffset; - LARGE_INTEGER StartingBlock; - PSCSI_REQUEST_BLOCK Srb; - PCDB Cdb; - ULONG LogicalBlockAddress; - USHORT TransferBlocks; - - DeviceExtension = DeviceObject->DeviceExtension; - CurrentIrpStack = IoGetCurrentIrpStackLocation(Irp); - NextIrpStack = IoGetNextIrpStackLocation(Irp); - StartingOffset = CurrentIrpStack->Parameters.Read.ByteOffset; - - /* Calculate logical block address */ - StartingBlock.QuadPart = StartingOffset.QuadPart >> DeviceExtension->SectorShift; - LogicalBlockAddress = (ULONG)StartingBlock.u.LowPart; - - DPRINT("Logical block address: %lu\n", LogicalBlockAddress); - - /* Allocate and initialize an SRB */ - Srb = ExAllocateFromNPagedLookasideList(&DeviceExtension->SrbLookasideListHead); - - Srb->SrbFlags = 0; - Srb->Length = sizeof(SCSI_REQUEST_BLOCK); //SCSI_REQUEST_BLOCK_SIZE; - Srb->OriginalRequest = Irp; - Srb->PathId = DeviceExtension->PathId; - Srb->TargetId = DeviceExtension->TargetId; - Srb->Lun = DeviceExtension->Lun; - Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - //FIXME: NT4 DDK sample uses MmGetMdlVirtualAddress! Why shouldn't we? - Srb->DataBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress); - Srb->DataTransferLength = CurrentIrpStack->Parameters.Read.Length; - Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; - Srb->QueueSortKey = LogicalBlockAddress; - - Srb->SenseInfoBuffer = (SENSE_DATA*)ROUND_UP((ULONG_PTR)(Srb + 1), SENSEINFO_ALIGNMENT); - Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; - - Srb->TimeOutValue = - ((Srb->DataTransferLength + 0xFFFF) >> 16) * DeviceExtension->TimeOutValue; - - Srb->SrbStatus = SRB_STATUS_SUCCESS; - Srb->ScsiStatus = 0; - Srb->NextSrb = 0; - - Srb->CdbLength = 10; - Cdb = (PCDB)Srb->Cdb; - - /* Initialize ATAPI packet (12 bytes) */ - RtlZeroMemory(Cdb, - MAXIMUM_CDB_SIZE); - - Cdb->CDB10.LogicalUnitNumber = DeviceExtension->Lun; - TransferBlocks = (USHORT)(CurrentIrpStack->Parameters.Read.Length >> DeviceExtension->SectorShift); - - /* Copy 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.TransferBlocksMsb = ((PFOUR_BYTE)&TransferBlocks)->Byte1; - Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&TransferBlocks)->Byte0; - - - if (CurrentIrpStack->MajorFunction == IRP_MJ_READ) - { - DPRINT("ScsiClassBuildRequest: Read Command\n"); - - Srb->SrbFlags |= SRB_FLAGS_DATA_IN; - Cdb->CDB10.OperationCode = SCSIOP_READ; - } - else - { - DPRINT("ScsiClassBuildRequest: Write Command\n"); - - Srb->SrbFlags |= SRB_FLAGS_DATA_OUT; - Cdb->CDB10.OperationCode = SCSIOP_WRITE; + return (ULONG) STATUS_REVISION_MISMATCH; } -#if 0 - /* if this is not a write-through request, then allow caching */ - if (!(CurrentIrpStack->Flags & SL_WRITE_THROUGH)) - { - Srb->SrbFlags |= SRB_FLAGS_ADAPTER_CACHE_ENABLE; - } - else if (DeviceExtension->DeviceFlags & DEV_WRITE_CACHE) - { - /* if write caching is enable then force media access in the cdb */ - Cdb->CDB10.ForceUnitAccess = TRUE; - } -#endif + // + // Update driver object with entry points. + // - /* Update srb flags */ - Srb->SrbFlags |= DeviceExtension->SrbFlags; + DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiClassCreateClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiClassCreateClose; + DriverObject->MajorFunction[IRP_MJ_READ] = ScsiClassReadWrite; + DriverObject->MajorFunction[IRP_MJ_WRITE] = ScsiClassReadWrite; + DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiClassInternalIoControl; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiClassDeviceControlDispatch; + DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ScsiClassShutdownFlush; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ScsiClassShutdownFlush; - /* Initialize next stack location */ - NextIrpStack->MajorFunction = IRP_MJ_SCSI; - NextIrpStack->Parameters.Scsi.Srb = Srb; - - /* Set retry count */ - CurrentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; - - DPRINT("IoSetCompletionRoutine (Irp %p Srb %p)\n", Irp, Srb); - IoSetCompletionRoutine(Irp, - ScsiClassIoComplete, - Srb, - TRUE, - TRUE, - TRUE); -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassClaimDevice(PDEVICE_OBJECT PortDeviceObject, - PSCSI_INQUIRY_DATA LunInfo, - BOOLEAN Release, - PDEVICE_OBJECT *NewPortDeviceObject OPTIONAL) -{ - PIO_STACK_LOCATION IoStack; - IO_STATUS_BLOCK IoStatusBlock; - SCSI_REQUEST_BLOCK Srb; - KEVENT Event; - PIRP Irp; - NTSTATUS Status; - - DPRINT("ScsiClassClaimDevice() called\n"); - - if (NewPortDeviceObject != NULL) - *NewPortDeviceObject = NULL; - - /* initialize an SRB */ - RtlZeroMemory(&Srb, - sizeof(SCSI_REQUEST_BLOCK)); - Srb.Length = SCSI_REQUEST_BLOCK_SIZE; - Srb.PathId = LunInfo->PathId; - Srb.TargetId = LunInfo->TargetId; - Srb.Lun = LunInfo->Lun; - Srb.Function = - (Release == TRUE) ? SRB_FUNCTION_RELEASE_DEVICE : SRB_FUNCTION_CLAIM_DEVICE; - - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); - - Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE, - PortDeviceObject, - NULL, - 0, - NULL, - 0, - TRUE, - &Event, - &IoStatusBlock); - if (Irp == NULL) - { - DPRINT("Failed to allocate Irp!\n"); - return(STATUS_INSUFFICIENT_RESOURCES); + if (InitializationData->ClassStartIo) { + DriverObject->DriverStartIo = InitializationData->ClassStartIo; } - /* Link Srb and Irp */ - IoStack = IoGetNextIrpStackLocation(Irp); - IoStack->Parameters.Scsi.Srb = &Srb; - Srb.OriginalRequest = Irp; + // + // Open port driver controller device objects by name. + // - /* Call SCSI port driver */ - Status = IoCallDriver(PortDeviceObject, - Irp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, - Suspended, - KernelMode, - FALSE, - NULL); - Status = IoStatusBlock.Status; - } + do { - if (Release == TRUE) - { - ObDereferenceObject(PortDeviceObject); - return(STATUS_SUCCESS); - } + sprintf(deviceNameBuffer, "\\Device\\ScsiPort%d", portNumber); -// Status = ObReferenceObjectByPointer(Srb.DataBuffer, - Status = ObReferenceObjectByPointer(PortDeviceObject, - 0, - NULL, - KernelMode); + DebugPrint((2, "ScsiClassInitialize: Open Port %s\n", deviceNameBuffer)); - if (NewPortDeviceObject != NULL) - { -// *NewPortDeviceObject = Srb.DataBuffer; - *NewPortDeviceObject = PortDeviceObject; - } + RtlInitString(&deviceNameString, deviceNameBuffer); - return(STATUS_SUCCESS); -} + status = RtlAnsiStringToUnicodeString(&unicodeDeviceName, + &deviceNameString, + TRUE); - -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject, - IN PCCHAR ObjectNameBuffer, - IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL, - IN OUT PDEVICE_OBJECT *DeviceObject, - IN PCLASS_INIT_DATA InitializationData) -{ - PDEVICE_OBJECT InternalDeviceObject; - PDEVICE_EXTENSION DeviceExtension; - ANSI_STRING AnsiName; - UNICODE_STRING DeviceName; - NTSTATUS Status; - - DPRINT("ScsiClassCreateDeviceObject() called\n"); - - *DeviceObject = NULL; - - RtlInitAnsiString(&AnsiName, - ObjectNameBuffer); - - Status = RtlAnsiStringToUnicodeString(&DeviceName, - &AnsiName, - TRUE); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - - DPRINT("Device name: '%wZ'\n", &DeviceName); - - Status = IoCreateDevice(DriverObject, - InitializationData->DeviceExtensionSize, - &DeviceName, - InitializationData->DeviceType, - InitializationData->DeviceCharacteristics, - FALSE, - &InternalDeviceObject); - if (NT_SUCCESS(Status)) - { - DeviceExtension = InternalDeviceObject->DeviceExtension; - - DeviceExtension->ClassError = InitializationData->ClassError; - DeviceExtension->ClassReadWriteVerification = InitializationData->ClassReadWriteVerification; - DeviceExtension->ClassFindDevices = InitializationData->ClassFindDevices; - DeviceExtension->ClassDeviceControl = InitializationData->ClassDeviceControl; - DeviceExtension->ClassShutdownFlush = InitializationData->ClassShutdownFlush; - DeviceExtension->ClassCreateClose = InitializationData->ClassCreateClose; - DeviceExtension->ClassStartIo = InitializationData->ClassStartIo; - - DeviceExtension->MediaChangeCount = 0; - - if (PhysicalDeviceObject != NULL) - { - DeviceExtension->PhysicalDevice = PhysicalDeviceObject; - } - else - { - DeviceExtension->PhysicalDevice = InternalDeviceObject; + if (!NT_SUCCESS(status)){ + break; } - *DeviceObject = InternalDeviceObject; + status = IoGetDeviceObjectPointer(&unicodeDeviceName, + FILE_READ_ATTRIBUTES, + &fileObject, + &portDeviceObject); + + if (NT_SUCCESS(status)) { + + // + // Call the device-specific driver's FindDevice routine. + // + + if (InitializationData->ClassFindDevices(DriverObject, Argument2, InitializationData, + portDeviceObject, portNumber)) { + + deviceFound = TRUE; + } + } + + // + // Check next SCSI adapter. + // + + portNumber++; + + } while(NT_SUCCESS(status)); + + return deviceFound ? STATUS_SUCCESS : STATUS_NO_SUCH_DEVICE; +} + + +NTSTATUS +STDCALL +ScsiClassCreateClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + SCSI class driver create and close routine. This is called by the I/O system + when the device is opened or closed. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + Irp - IRP involved. + +Return Value: + + Device-specific drivers return value or STATUS_SUCCESS. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + + // + // Invoke the device-specific routine, if one exists. Otherwise complete + // with SUCCESS + // + + if (deviceExtension->ClassCreateClose) { + + return deviceExtension->ClassCreateClose(DeviceObject, Irp); + + } else { + Irp->IoStatus.Status = STATUS_SUCCESS; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return(STATUS_SUCCESS); } - - RtlFreeUnicodeString(&DeviceName); - - return(Status); } -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassDeviceControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) + +NTSTATUS +STDCALL +ScsiClassReadWrite( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the system entry point for read and write requests. The device-specific handler is invoked + to perform any validation necessary. The number of bytes in the request are + checked against the maximum byte counts that the adapter supports and requests are broken up into + smaller sizes if necessary. + +Arguments: + + DeviceObject + Irp - IO request + +Return Value: + + NT Status + +--*/ + { - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION NextStack; - PIO_STACK_LOCATION Stack; - ULONG IoControlCode; - ULONG InputBufferLength; - ULONG OutputBufferLength; - ULONG ModifiedControlCode; - PSCSI_REQUEST_BLOCK Srb; - PCDB Cdb; - PIRP SubIrp; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG transferPages; + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + ULONG maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength; + NTSTATUS status; - DPRINT("ScsiClassDeviceControl() called\n"); + if (DeviceObject->Flags & DO_VERIFY_VOLUME && + !(currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) { - DeviceExtension = DeviceObject->DeviceExtension; - Stack = IoGetCurrentIrpStackLocation(Irp); + // + // if DO_VERIFY_VOLUME bit is set + // in device object flags, fail request. + // - IoControlCode = Stack->Parameters.DeviceIoControl.IoControlCode; - InputBufferLength = Stack->Parameters.DeviceIoControl.InputBufferLength; - OutputBufferLength = Stack->Parameters.DeviceIoControl.OutputBufferLength; + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); - if (IoControlCode == IOCTL_SCSI_GET_DUMP_POINTERS) - { - PDUMP_POINTERS DumpPointers; + Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED; + Irp->IoStatus.Information = 0; - if (OutputBufferLength < sizeof(DUMP_POINTERS)) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return(STATUS_BUFFER_TOO_SMALL); - } - DumpPointers = (PDUMP_POINTERS)Irp->AssociatedIrp.SystemBuffer; - - /* Initialize next stack location for call to the port driver */ - NextStack = IoGetNextIrpStackLocation(Irp); - - NextStack->Parameters = Stack->Parameters; - NextStack->MajorFunction = Stack->MajorFunction; - NextStack->MinorFunction = Stack->MinorFunction; - - /* Call port driver */ - return(IoCallDriver(DeviceExtension->PortDeviceObject, - Irp)); - } - if (IoControlCode == IOCTL_SCSI_GET_ADDRESS) - { - PSCSI_ADDRESS ScsiAddress; - - if (OutputBufferLength < sizeof(SCSI_ADDRESS)) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return(STATUS_BUFFER_TOO_SMALL); - } - - ScsiAddress = Irp->AssociatedIrp.SystemBuffer; - ScsiAddress->Length = sizeof(SCSI_ADDRESS); - ScsiAddress->PortNumber = DeviceExtension->PortNumber; - ScsiAddress->PathId = DeviceExtension->PathId; - ScsiAddress->TargetId = DeviceExtension->TargetId; - ScsiAddress->Lun = DeviceExtension->Lun; - - Irp->IoStatus.Information = sizeof(SCSI_ADDRESS); - Irp->IoStatus.Status = STATUS_SUCCESS; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return(STATUS_SUCCESS); + IoCompleteRequest(Irp, 0); + return STATUS_VERIFY_REQUIRED; } - if (IoControlCode == IOCTL_SCSI_PASS_THROUGH || - IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) - { - PSCSI_PASS_THROUGH ScsiPassThrough; + // + // Invoke the device specific routine to do whatever it needs to verify + // this request. + // - DPRINT("IOCTL_SCSI_PASS_THROUGH/IOCTL_SCSI_PASS_THROUGH_DIRECT\n"); + ASSERT(deviceExtension->ClassReadWriteVerification); - /* Check input size */ - if (InputBufferLength < sizeof(SCSI_PASS_THROUGH)) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return(STATUS_INVALID_PARAMETER); - } + status = deviceExtension->ClassReadWriteVerification(DeviceObject,Irp); - /* Initialize next stack location for call to the port driver */ - NextStack = IoGetNextIrpStackLocation(Irp); + if (!NT_SUCCESS(status)) { - ScsiPassThrough = Irp->AssociatedIrp.SystemBuffer; - ScsiPassThrough->PathId = DeviceExtension->PathId; - ScsiPassThrough->TargetId = DeviceExtension->TargetId; - ScsiPassThrough->Lun = DeviceExtension->Lun; - ScsiPassThrough->Cdb[1] |= DeviceExtension->Lun << 5; + // + // It is up to the device specific driver to set the Irp status. + // - NextStack->Parameters = Stack->Parameters; - NextStack->MajorFunction = Stack->MajorFunction; - NextStack->MinorFunction = Stack->MinorFunction; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return status; + } else if (status == STATUS_PENDING) { - /* Call port driver */ - return(IoCallDriver(DeviceExtension->PortDeviceObject, - Irp)); + IoMarkIrpPending(Irp); + return STATUS_PENDING; } - /* Allocate and initialize an SRB */ - Srb = ExAllocateFromNPagedLookasideList(&DeviceExtension->SrbLookasideListHead); + // + // Check for a zero length IO, as several macros will turn this into + // seemingly a 0xffffffff length request. + // + + if (transferByteCount == 0) { + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; - if (Srb == NULL) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - IoCompleteRequest(Irp, - IO_NO_INCREMENT); - return(STATUS_INSUFFICIENT_RESOURCES); } - /* Initialize the SRB */ - RtlZeroMemory(Srb, - sizeof(SCSI_REQUEST_BLOCK)); - Cdb = (PCDB)Srb->Cdb; + if (deviceExtension->ClassStartIo) { - ModifiedControlCode = (IoControlCode & 0x0000FFFF) | (IOCTL_DISK_BASE << 16); - switch (ModifiedControlCode) - { - case IOCTL_DISK_CHECK_VERIFY: - DPRINT ("ScsiClassDeviceControl: IOCTL_DISK_CHECK_VERIFY\n"); + IoMarkIrpPending(Irp); - if (OutputBufferLength != 0) - { - if (OutputBufferLength < sizeof(ULONG)) - { - DPRINT ("ScsiClassDeviceControl: IOCTL_DISK_CHECK_VERIFY SMALL\n"); - Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; - Irp->IoStatus.Information = 0; - /* Free the SRB */ - ExFreeToNPagedLookasideList(&DeviceExtension->SrbLookasideListHead, - Srb); - IoCompleteRequest (Irp, - IO_NO_INCREMENT); - return STATUS_BUFFER_TOO_SMALL; - } + IoStartPacket(DeviceObject, + Irp, + NULL, + NULL); - /* Allocate new IRP for TEST UNIT READY scsi command */ - SubIrp = IoAllocateIrp ((CCHAR)DeviceObject->StackSize + 3, - FALSE); - if (SubIrp == NULL) - { - DPRINT ("ScsiClassDeviceControl: IOCTL_DISK_CHECK_VERIFY NotEnuf\n"); - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - /* Free the SRB */ - ExFreeToNPagedLookasideList(&DeviceExtension->SrbLookasideListHead, - Srb); - IoCompleteRequest (Irp, - IO_NO_INCREMENT); - return STATUS_INSUFFICIENT_RESOURCES; - } - - SubIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread; - IoSetNextIrpStackLocation (SubIrp); - - NextStack = IoGetCurrentIrpStackLocation (SubIrp); - NextStack->Parameters.Others.Argument1 = Irp; - NextStack->DeviceObject = DeviceObject; - - IoSetCompletionRoutine (SubIrp, - ScsiClassCheckVerifyCompletion, - NULL, - TRUE, - TRUE, - TRUE); - - DPRINT ("ScsiClassDeviceControl: IOCTL_DISK_CHECK_VERIFY IoSet\n"); - - IoSetNextIrpStackLocation (SubIrp); - NextStack = IoGetCurrentIrpStackLocation (SubIrp); - NextStack->DeviceObject = DeviceObject; - - IoMarkIrpPending (Irp); - - Irp = SubIrp; - } - - /* Initialize SRB operation */ - Srb->CdbLength = 6; - Srb->TimeOutValue = DeviceExtension->TimeOutValue; - Cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; - -DPRINT ("ScsiClassDeviceControl: IOCTL_DISK_CHECK_VERIFY SrbAsync\n"); - - return(ScsiClassSendSrbAsynchronous(DeviceObject, - Srb, - Irp, - NULL, - 0, - FALSE)); - - default: - DPRINT("Unknown device io control code %lx\n", - ModifiedControlCode); - /* Free the SRB */ - ExFreeToNPagedLookasideList(&DeviceExtension->SrbLookasideListHead, - Srb); - /* Pass the IOCTL down to the port driver */ - NextStack = IoGetNextIrpStackLocation(Irp); - NextStack->Parameters = Stack->Parameters; - NextStack->MajorFunction = Stack->MajorFunction; - NextStack->MinorFunction = Stack->MinorFunction; - - /* Call port driver */ - return(IoCallDriver(DeviceExtension->PortDeviceObject, - Irp)); + return STATUS_PENDING; } - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; - IoCompleteRequest(Irp, IO_NO_INCREMENT); + // + // Mark IRP with status pending. + // - return(STATUS_UNSUCCESSFUL); -} + IoMarkIrpPending(Irp); + + // + // Add partition byte offset to make starting byte relative to + // beginning of disk. In addition, add in skew for DM Driver, if any. + // + + currentIrpStack->Parameters.Read.ByteOffset.QuadPart += (deviceExtension->StartingOffset.QuadPart + + deviceExtension->DMByteSkew); + + // + // Calculate number of pages in this transfer. + // + + transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress), + currentIrpStack->Parameters.Read.Length); + + // + // Check if request length is greater than the maximum number of + // bytes that the hardware can transfer. + // + + if (currentIrpStack->Parameters.Read.Length > maximumTransferLength || + transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages) { + + DebugPrint((2,"ScsiClassReadWrite: Request greater than maximum\n")); + DebugPrint((2,"ScsiClassReadWrite: Maximum is %lx\n", + maximumTransferLength)); + DebugPrint((2,"ScsiClassReadWrite: Byte count is %lx\n", + currentIrpStack->Parameters.Read.Length)); + + transferPages = + deviceExtension->PortCapabilities->MaximumPhysicalPages - 1; + + if (maximumTransferLength > transferPages << PAGE_SHIFT ) { + maximumTransferLength = transferPages << PAGE_SHIFT; + } + + // + // Check that maximum transfer size is not zero. + // + + if (maximumTransferLength == 0) { + maximumTransferLength = PAGE_SIZE; + } + + // + // Mark IRP with status pending. + // + + IoMarkIrpPending(Irp); + + // + // Request greater than port driver maximum. + // Break up into smaller routines. + // + + ScsiClassSplitRequest(DeviceObject, Irp, maximumTransferLength); -/* - * @implemented - */ -PVOID STDCALL -ScsiClassFindModePage(IN PCHAR ModeSenseBuffer, - IN ULONG Length, - IN UCHAR PageMode, - IN BOOLEAN Use6Byte) + return STATUS_PENDING; + } + + // + // Build SRB and CDB for this IRP. + // + + ScsiClassBuildRequest(DeviceObject, Irp); + + // + // Return the results of the call to the port driver. + // + + return IoCallDriver(deviceExtension->PortDeviceObject, Irp); + +} // end ScsiClassReadWrite() + + +NTSTATUS +STDCALL +ScsiClassGetCapabilities( + IN PDEVICE_OBJECT PortDeviceObject, + OUT PIO_SCSI_CAPABILITIES *PortCapabilities + ) + +/*++ + +Routine Description: + + This routine builds and sends a request to the port driver to + get a pointer to a structure that describes the adapter's + capabilities/limitations. This routine is sychronous. + +Arguments: + + PortDeviceObject - Port driver device object representing the HBA. + + PortCapabilities - Location to store pointer to capabilities structure. + +Return Value: + + Nt status indicating the results of the operation. + +Notes: + + This routine should only be called at initialization time. + +--*/ + { - ULONG DescriptorLength; - ULONG HeaderLength; - PCHAR End; - PCHAR Ptr; + PIRP irp; + IO_STATUS_BLOCK ioStatus; + KEVENT event; + NTSTATUS status; - DPRINT("ScsiClassFindModePage() called\n"); + PAGED_CODE(); - /* Get header length */ - HeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10); + // + // Create notification event object to be used to signal the + // request completion. + // - /* Check header length */ - if (Length < HeaderLength) - return NULL; + KeInitializeEvent(&event, NotificationEvent, FALSE); - /* Get descriptor length */ - if (Use6Byte == TRUE) - { - DescriptorLength = ((PMODE_PARAMETER_HEADER)ModeSenseBuffer)->BlockDescriptorLength; - } - else - { - DescriptorLength = ((PMODE_PARAMETER_HEADER10)ModeSenseBuffer)->BlockDescriptorLength[1]; + // + // Build the synchronous request to be sent to the port driver + // to perform the request. + // + + irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_CAPABILITIES, + PortDeviceObject, + NULL, + 0, + PortCapabilities, + sizeof(PVOID), + FALSE, + &event, + &ioStatus); + + if (irp == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; } - /* Set page pointers */ - Ptr = ModeSenseBuffer + HeaderLength + DescriptorLength; - End = ModeSenseBuffer + Length; + // + // Pass request to port driver and wait for request to complete. + // - /* Search for page */ - while (Ptr < End) - { - /* Check page code */ - if (((PMODE_DISCONNECT_PAGE)Ptr)->PageCode == PageMode) - return Ptr; + status = IoCallDriver(PortDeviceObject, irp); - /* Skip to next page */ - Ptr += ((PMODE_DISCONNECT_PAGE)Ptr)->PageLength; + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL); + return(ioStatus.Status); } - return NULL; -} + return status; +} // end ScsiClassGetCapabilities() + + +NTSTATUS +STDCALL +ScsiClassGetInquiryData( + IN PDEVICE_OBJECT PortDeviceObject, + OUT PSCSI_ADAPTER_BUS_INFO *ConfigInfo + ) + +/*++ + +Routine Description: + + This routine sends a request to a port driver to return + configuration information. Space for the information is + allocated by this routine. The caller is responsible for + freeing the configuration information. This routine is + synchronous. + +Arguments: + + PortDeviceObject - Port driver device object representing the HBA. + + ConfigInfo - Returns a pointer to the configuration information. + +Return Value: + + Nt status indicating the results of the operation. + +Notes: + + This routine should be called only at initialization time. + +--*/ -/* - * @implemented - */ -ULONG STDCALL -ScsiClassFindUnclaimedDevices(IN PCLASS_INIT_DATA InitializationData, - IN PSCSI_ADAPTER_BUS_INFO AdapterInformation) { - PSCSI_INQUIRY_DATA UnitInfo; - PINQUIRYDATA InquiryData; - PUCHAR Buffer; - ULONG Bus; - ULONG UnclaimedDevices = 0; + PIRP irp; + IO_STATUS_BLOCK ioStatus; + KEVENT event; + NTSTATUS status; + PSCSI_ADAPTER_BUS_INFO buffer; - DPRINT("ScsiClassFindUnclaimedDevices() called\n"); + PAGED_CODE(); - DPRINT("NumberOfBuses: %lu\n",AdapterInformation->NumberOfBuses); - Buffer = (PUCHAR)AdapterInformation; - for (Bus = 0; Bus < (ULONG)AdapterInformation->NumberOfBuses; Bus++) - { - DPRINT("Searching bus %lu\n", Bus); + buffer = ExAllocatePool(PagedPool, INQUIRY_DATA_SIZE); + *ConfigInfo = buffer; - UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + AdapterInformation->BusData[Bus].InquiryDataOffset); - - while (AdapterInformation->BusData[Bus].InquiryDataOffset) - { - InquiryData = (PINQUIRYDATA)UnitInfo->InquiryData; - - DPRINT("Device: '%.8s'\n", InquiryData->VendorId); - - if ((InitializationData->ClassFindDeviceCallBack(InquiryData) == TRUE) && - (UnitInfo->DeviceClaimed == FALSE)) - { - UnclaimedDevices++; - } - - if (UnitInfo->NextInquiryDataOffset == 0) - break; - - UnitInfo = (PSCSI_INQUIRY_DATA) (Buffer + UnitInfo->NextInquiryDataOffset); - } + if (buffer == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); } - return(UnclaimedDevices); -} + // + // Create notification event object to be used to signal the inquiry + // request completion. + // + KeInitializeEvent(&event, NotificationEvent, FALSE); -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassGetCapabilities(IN PDEVICE_OBJECT PortDeviceObject, - OUT PIO_SCSI_CAPABILITIES *PortCapabilities) + // + // Build the synchronous request to be sent to the port driver + // to perform the inquiries. + // + + irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_INQUIRY_DATA, + PortDeviceObject, + NULL, + 0, + buffer, + INQUIRY_DATA_SIZE, + FALSE, + &event, + &ioStatus); + + if (irp == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Pass request to port driver and wait for request to complete. + // + + status = IoCallDriver(PortDeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + if (!NT_SUCCESS(status)) { + + // + // Free the buffer on an error. + // + + ExFreePool(buffer); + *ConfigInfo = NULL; + + } + + return status; + +} // end ScsiClassGetInquiryData() + + +NTSTATUS +STDCALL +ScsiClassReadDriveCapacity( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine sends a READ CAPACITY to the requested device, updates + the geometry information in the device object and returns + when it is complete. This routine is synchronous. + +Arguments: + + DeviceObject - Supplies a pointer to the device object that represents + the device whose capacity is to be read. + +Return Value: + + Status is returned. + +--*/ { - IO_STATUS_BLOCK IoStatusBlock; - NTSTATUS Status; - KEVENT Event; - PIRP Irp; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + ULONG retries = 1; + ULONG lastSector; + PCDB cdb; + PREAD_CAPACITY_DATA readCapacityBuffer; + SCSI_REQUEST_BLOCK srb; + NTSTATUS status; - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); + // + // Allocate read capacity buffer from nonpaged pool. + // - Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_CAPABILITIES, - PortDeviceObject, - NULL, - 0, - PortCapabilities, - sizeof(PVOID), - FALSE, - &Event, - &IoStatusBlock); - if (Irp == NULL) - { - return(STATUS_INSUFFICIENT_RESOURCES); + readCapacityBuffer = ExAllocatePool(NonPagedPoolCacheAligned, + sizeof(READ_CAPACITY_DATA)); + + if (!readCapacityBuffer) { + return(STATUS_INSUFFICIENT_RESOURCES); } - Status = IoCallDriver(PortDeviceObject, - Irp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, - Suspended, - KernelMode, - FALSE, - NULL); - Status = IoStatusBlock.Status; + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Build the read capacity CDB. + // + + srb.CdbLength = 10; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = deviceExtension->TimeOutValue; + + cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + +Retry: + + status = ScsiClassSendSrbSynchronous(DeviceObject, + &srb, + readCapacityBuffer, + sizeof(READ_CAPACITY_DATA), + FALSE); + + if (NT_SUCCESS(status)) { + + // + // Copy sector size from read capacity buffer to device extension + // in reverse byte order. + // + + ((PFOUR_BYTE)&deviceExtension->DiskGeometry->BytesPerSector)->Byte0 = + ((PFOUR_BYTE)&readCapacityBuffer->BytesPerBlock)->Byte3; + + ((PFOUR_BYTE)&deviceExtension->DiskGeometry->BytesPerSector)->Byte1 = + ((PFOUR_BYTE)&readCapacityBuffer->BytesPerBlock)->Byte2; + + ((PFOUR_BYTE)&deviceExtension->DiskGeometry->BytesPerSector)->Byte2 = + ((PFOUR_BYTE)&readCapacityBuffer->BytesPerBlock)->Byte1; + + ((PFOUR_BYTE)&deviceExtension->DiskGeometry->BytesPerSector)->Byte3 = + ((PFOUR_BYTE)&readCapacityBuffer->BytesPerBlock)->Byte0; + + // + // 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; + + // + // Calculate sector to byte shift. + // + + WHICH_BIT(deviceExtension->DiskGeometry->BytesPerSector, deviceExtension->SectorShift); + + DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Sector size is %d\n", + deviceExtension->DiskGeometry->BytesPerSector)); + + DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Number of Sectors is %d\n", + lastSector + 1)); + + // + // Calculate media capacity in bytes. + // + + deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1); + + // + // Calculate number of cylinders. + // + + deviceExtension->DiskGeometry->Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64)); + + deviceExtension->PartitionLength.QuadPart = + (deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift); + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { + + // + // This device supports removable media. + // + + deviceExtension->DiskGeometry->MediaType = RemovableMedia; + + } else { + + // + // Assume media type is fixed disk. + // + + deviceExtension->DiskGeometry->MediaType = FixedMedia; + } + + // + // Assume sectors per track are 32; + // + + deviceExtension->DiskGeometry->SectorsPerTrack = 32; + + // + // Assume tracks per cylinder (number of heads) is 64. + // + + deviceExtension->DiskGeometry->TracksPerCylinder = 64; } - DPRINT("PortCapabilities at %p\n", *PortCapabilities); + if (status == STATUS_VERIFY_REQUIRED) { - return(Status); -} + // + // Routine ScsiClassSendSrbSynchronous does not retry + // requests returned with this status. + // Read Capacities should be retried + // anyway. + // + if (retries--) { -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassGetInquiryData(IN PDEVICE_OBJECT PortDeviceObject, - IN PSCSI_ADAPTER_BUS_INFO *ConfigInfo) + // + // Retry request. + // + + goto Retry; + } + } + + if (!NT_SUCCESS(status)) { + + // + // If the read capacity fails, set the geometry to reasonable parameter + // so things don't fail at unexpected places. Zero the geometry + // except for the bytes per sector and sector shift. + // + + RtlZeroMemory(deviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY)); + deviceExtension->DiskGeometry->BytesPerSector = 512; + deviceExtension->SectorShift = 9; + deviceExtension->PartitionLength.QuadPart = (LONGLONG) 0; + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { + + // + // This device supports removable media. + // + + deviceExtension->DiskGeometry->MediaType = RemovableMedia; + + } else { + + // + // Assume media type is fixed disk. + // + + deviceExtension->DiskGeometry->MediaType = FixedMedia; + } + } + + // + // Deallocate read capacity buffer. + // + + ExFreePool(readCapacityBuffer); + + return status; + +} // end ScsiClassReadDriveCapacity() + + +VOID +STDCALL +ScsiClassReleaseQueue( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine issues an internal device control command + to the port driver to release a frozen queue. The call + is issued asynchronously as ScsiClassReleaseQueue will be invoked + from the IO completion DPC (and will have no context to + wait for a synchronous call to complete). + +Arguments: + + DeviceObject - The device object for the logical unit with + the frozen queue. + +Return Value: + + None. + +--*/ { - PSCSI_ADAPTER_BUS_INFO Buffer; - IO_STATUS_BLOCK IoStatusBlock; - NTSTATUS Status; - KEVENT Event; - PIRP Irp; + PIO_STACK_LOCATION irpStack; + PIRP irp; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PCOMPLETION_CONTEXT context; + PSCSI_REQUEST_BLOCK srb; + KIRQL currentIrql; - DPRINT("ScsiClassGetInquiryData() called\n"); + // + // Allocate context from nonpaged pool. + // + + context = ExAllocatePool(NonPagedPoolMustSucceed, + sizeof(COMPLETION_CONTEXT)); + + // + // Save the device object in the context for use by the completion + // routine. + // + + context->DeviceObject = DeviceObject; + srb = &context->Srb; + + // + // Zero out srb. + // + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + // + // Write length to SRB. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set up SCSI bus address. + // + + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + + // + // If this device is removable then flush the queue. This will also + // release it. + // + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { + + srb->Function = SRB_FUNCTION_FLUSH_QUEUE; + + } else { + + srb->Function = SRB_FUNCTION_RELEASE_QUEUE; - *ConfigInfo = NULL; - Buffer = ExAllocatePool(NonPagedPool, - INQUIRY_DATA_SIZE); - if (Buffer == NULL) - { - return(STATUS_INSUFFICIENT_RESOURCES); } - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); + // + // Build the asynchronous request to be sent to the port driver. + // + + irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + + if(irp == NULL) { + + // + // We have no better way of dealing with this at the moment + // + + KeBugCheck((ULONG)0x0000002DL); - Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_INQUIRY_DATA, - PortDeviceObject, - NULL, - 0, - Buffer, - INQUIRY_DATA_SIZE, - FALSE, - &Event, - &IoStatusBlock); - if (Irp == NULL) - { - ExFreePool(Buffer); - return(STATUS_INSUFFICIENT_RESOURCES); } - Status = IoCallDriver(PortDeviceObject, - Irp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, - Suspended, - KernelMode, - FALSE, - NULL); - Status = IoStatusBlock.Status; + IoSetCompletionRoutine(irp, + (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion, + context, + TRUE, + TRUE, + TRUE); + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + srb->OriginalRequest = irp; + + // + // Store the SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = srb; + + // + // Since this routine can cause outstanding requests to be completed, and + // calling a completion routine at < DISPATCH_LEVEL is dangerous (if they + // call IoStartNextPacket we will bugcheck) raise up to dispatch level before + // issuing the request + // + + currentIrql = KeGetCurrentIrql(); + + if(currentIrql < DISPATCH_LEVEL) { + KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql); + IoCallDriver(deviceExtension->PortDeviceObject, irp); + KeLowerIrql(currentIrql); + } else { + IoCallDriver(deviceExtension->PortDeviceObject, irp); } - if (!NT_SUCCESS(Status)) - { - ExFreePool(Buffer); - } - else - { - *ConfigInfo = Buffer; - } + return; - DPRINT("ScsiClassGetInquiryData() done\n"); +} // end ScsiClassReleaseQueue() - return(Status); -} + +VOID +STDCALL +StartUnit( + IN PDEVICE_OBJECT DeviceObject + ) +/*++ -/* - * @implemented - */ -ULONG STDCALL -ScsiClassInitialize(IN PVOID Argument1, - IN PVOID Argument2, - IN PCLASS_INIT_DATA InitializationData) +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: + + DeviceObject - The device object for the logical unit with + the frozen queue. + +Return Value: + + None. + +--*/ { - PCONFIGURATION_INFORMATION ConfigInfo; - PDRIVER_OBJECT DriverObject = Argument1; - WCHAR NameBuffer[80]; - UNICODE_STRING PortName; - ULONG PortNumber; - PDEVICE_OBJECT PortDeviceObject; - PFILE_OBJECT FileObject; - BOOLEAN DiskFound = FALSE; - NTSTATUS Status; + PIO_STACK_LOCATION irpStack; + PIRP irp; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCOMPLETION_CONTEXT context; + PCDB cdb; - DPRINT("ScsiClassInitialize() called!\n"); + // + // Allocate Srb from nonpaged pool. + // - DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiClassCreateClose; - DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiClassCreateClose; - DriverObject->MajorFunction[IRP_MJ_READ] = ScsiClassReadWrite; - DriverObject->MajorFunction[IRP_MJ_WRITE] = ScsiClassReadWrite; - DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiClassInternalIoControl; - DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiClassDeviceDispatch; - DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ScsiClassShutdownFlush; - DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ScsiClassShutdownFlush; - if (InitializationData->ClassStartIo) - { - DriverObject->DriverStartIo = InitializationData->ClassStartIo; - } + context = ExAllocatePool(NonPagedPoolMustSucceed, + sizeof(COMPLETION_CONTEXT)); - ConfigInfo = IoGetConfigurationInformation(); + // + // Save the device object in the context for use by the completion + // routine. + // - DPRINT("ScsiPorts: %lu\n", ConfigInfo->ScsiPortCount); + context->DeviceObject = DeviceObject; + srb = &context->Srb; - /* look for ScsiPortX scsi port devices */ - for (PortNumber = 0; PortNumber < ConfigInfo->ScsiPortCount; PortNumber++) - { - swprintf(NameBuffer, - L"\\Device\\ScsiPort%lu", - PortNumber); - RtlInitUnicodeString(&PortName, - NameBuffer); - DPRINT("Checking scsi port %ld\n", PortNumber); - Status = IoGetDeviceObjectPointer(&PortName, - FILE_READ_ATTRIBUTES, - &FileObject, - &PortDeviceObject); - DPRINT("Status 0x%08lX\n", Status); - if (NT_SUCCESS(Status)) - { - DPRINT("ScsiPort%lu found.\n", PortNumber); + // + // Zero out srb. + // - /* check scsi port for attached disk drives */ - if (InitializationData->ClassFindDevices(DriverObject, - Argument2, - InitializationData, - PortDeviceObject, - PortNumber)) - { - DiskFound = TRUE; - } - } - else - { - DbgPrint("Couldn't find ScsiPort%lu (Status %lx)\n", PortNumber, Status); - } - } + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); - DPRINT("ScsiClassInitialize() done!\n"); + // + // Write length to SRB. + // - return((DiskFound == TRUE) ? STATUS_SUCCESS : STATUS_NO_SUCH_DEVICE); -} + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + // + // Set up SCSI bus address. + // + + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Set timeout value large enough for drive to spin up. + // + + srb->TimeOutValue = START_UNIT_TIMEOUT; + + // + // Set the transfer length. + // + + srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | SRB_FLAGS_DISABLE_AUTOSENSE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Build the start unit CDB. + // + + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.Start = 1; + cdb->START_STOP.LogicalUnitNumber = srb->Lun; + + // + // Build the asynchronous request to be sent to the port driver. + // Since this routine is called from a DPC the IRP should always be + // available. + // + + irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + IoSetCompletionRoutine(irp, + (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion, + context, + TRUE, + TRUE, + TRUE); + + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->MajorFunction = IRP_MJ_SCSI; + srb->OriginalRequest = irp; + + // + // Store the SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = srb; + + // + // Call the port driver with the IRP. + // + + IoCallDriver(deviceExtension->PortDeviceObject, irp); + + return; + +} // end StartUnit() + + +NTSTATUS +STDCALL +ScsiClassAsynchronousCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) +/*++ + +Routine Description: + + This routine is called when an asynchronous I/O request + which was issused by the class driver completes. Examples of such requests + are release queue or START UNIT. This routine releases the queue if + necessary. It then frees the context and the IRP. + +Arguments: + + DeviceObject - The device object for the logical unit; however since this + is the top stack location the value is NULL. + + Irp - Supplies a pointer to the Irp to be processed. + + Context - Supplies the context to be used to process this request. + +Return Value: + + None. + +--*/ -/********************************************************************** - * NAME EXPORTED - * ScsiClassInitializeSrbLookasideList - * - * DESCRIPTION - * Initializes a lookaside list for SRBs. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceExtension - * Class specific device extension. - * - * NumberElements - * Maximum number of elements of the lookaside list. - * - * RETURN VALUE - * None. - * - * @implemented - */ -VOID STDCALL -ScsiClassInitializeSrbLookasideList(IN PDEVICE_EXTENSION DeviceExtension, - IN ULONG NumberElements) { - ExInitializeNPagedLookasideList(&DeviceExtension->SrbLookasideListHead, - NULL, - NULL, - NonPagedPool, - sizeof(SCSI_REQUEST_BLOCK) + sizeof(SENSE_DATA) + SENSEINFO_ALIGNMENT - 1, - TAG_SRBT, - (USHORT)NumberElements); -} + PCOMPLETION_CONTEXT context = Context; + PSCSI_REQUEST_BLOCK srb; + srb = &context->Srb; + + // + // If this is an execute srb, then check the return status and make sure. + // the queue is not frozen. + // + + if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) { + + // + // Check for a frozen queue. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + + // + // Unfreeze the queue getting the device object from the context. + // + + ScsiClassReleaseQueue(context->DeviceObject); + } + } + + // + // Free the context and the Irp. + // + + if (Irp->MdlAddress != NULL) { + MmUnlockPages(Irp->MdlAddress); + IoFreeMdl(Irp->MdlAddress); + + Irp->MdlAddress = NULL; + } + + ExFreePool(context); + IoFreeIrp(Irp); + + // + // Indicate the I/O system should stop processing the Irp completion. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // ScsiClassAsynchronousCompletion() + + +VOID +STDCALL +ScsiClassSplitRequest( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG MaximumBytes + ) + +/*++ + +Routine Description: + + Break request into smaller requests. Each new request will be the + maximum transfer size that the port driver can handle or if it + is the final request, it may be the residual size. + + The number of IRPs required to process this request is written in the + current stack of the original IRP. Then as each new IRP completes + the count in the original IRP is decremented. When the count goes to + zero, the original IRP is completed. + +Arguments: + + DeviceObject - Pointer to the class device object to be addressed. + + Irp - Pointer to Irp the orginal request. + +Return Value: + + None. + +--*/ -/* - * @unimplemented - */ -NTSTATUS STDCALL -ScsiClassInternalIoControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) { - DPRINT("ScsiClassInternalIoContol() called\n"); + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + PVOID dataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); + ULONG dataLength = MaximumBytes; + ULONG irpCount = (transferByteCount + MaximumBytes - 1) / MaximumBytes; + ULONG i; + PSCSI_REQUEST_BLOCK srb; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); + DebugPrint((2, "ScsiClassSplitRequest: Requires %d IRPs\n", irpCount)); + DebugPrint((2, "ScsiClassSplitRequest: Original IRP %lx\n", Irp)); - return(STATUS_SUCCESS); -} + // + // If all partial transfers complete successfully then the status and + // bytes transferred are already set up. Failing a partial-transfer IRP + // will set status to error and bytes transferred to 0 during + // IoCompletion. Setting bytes transferred to 0 if an IRP fails allows + // asynchronous partial transfers. This is an optimization for the + // successful case. + // + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = transferByteCount; + + // + // Save number of IRPs to complete count on current stack + // of original IRP. + // + + nextIrpStack->Parameters.Others.Argument1 = (PVOID) irpCount; + + for (i = 0; i < irpCount; i++) { + + PIRP newIrp; + PIO_STACK_LOCATION newIrpStack; + + // + // Allocate new IRP. + // + + newIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + + if (newIrp == NULL) { + + DebugPrint((1,"ScsiClassSplitRequest: Can't allocate Irp\n")); + + // + // If an Irp can't be allocated then the orginal request cannot + // be executed. If this is the first request then just fail the + // orginal request; otherwise just return. When the pending + // requests complete, they will complete the original request. + // In either case set the IRP status to failure. + // + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + + if (i == 0) { + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + return; + } + + DebugPrint((2, "ScsiClassSplitRequest: New IRP %lx\n", newIrp)); + + // + // Write MDL address to new IRP. In the port driver the SRB data + // buffer field is used as an offset into the MDL, so the same MDL + // can be used for each partial transfer. This saves having to build + // a new MDL for each partial transfer. + // + + newIrp->MdlAddress = Irp->MdlAddress; + + // + // At this point there is no current stack. IoSetNextIrpStackLocation + // will make the first stack location the current stack so that the + // SRB address can be written there. + // + + IoSetNextIrpStackLocation(newIrp); + newIrpStack = IoGetCurrentIrpStackLocation(newIrp); + + newIrpStack->MajorFunction = currentIrpStack->MajorFunction; + newIrpStack->Parameters.Read.Length = dataLength; + newIrpStack->Parameters.Read.ByteOffset = startingOffset; + newIrpStack->DeviceObject = DeviceObject; + + // + // Build SRB and CDB. + // + + ScsiClassBuildRequest(DeviceObject, newIrp); + + // + // Adjust SRB for this partial transfer. + // + + newIrpStack = IoGetNextIrpStackLocation(newIrp); + + srb = newIrpStack->Parameters.Others.Argument1; + srb->DataBuffer = dataBuffer; + + // + // Write original IRP address to new IRP. + // + + newIrp->AssociatedIrp.MasterIrp = Irp; + + // + // Set the completion routine to ScsiClassIoCompleteAssociated. + // + + IoSetCompletionRoutine(newIrp, + ScsiClassIoCompleteAssociated, + srb, + TRUE, + TRUE, + TRUE); + + // + // Call port driver with new request. + // + + IoCallDriver(deviceExtension->PortDeviceObject, newIrp); + + // + // Set up for next request. + // + + dataBuffer = (PCHAR)dataBuffer + MaximumBytes; + + transferByteCount -= MaximumBytes; + + if (transferByteCount > MaximumBytes) { + + dataLength = MaximumBytes; + + } else { + + dataLength = transferByteCount; + } + + // + // Adjust disk byte offset. + // + + startingOffset.QuadPart = startingOffset.QuadPart + MaximumBytes; + } + + return; + +} // end ScsiClassSplitRequest() + + +NTSTATUS +STDCALL +ScsiClassIoComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine executes when the port driver has completed a request. + It looks at the SRB status in the completing SRB and if not success + it checks for valid request sense buffer information. If valid, the + info is used to update status with more precise message of type of + error. This routine deallocates the SRB. + +Arguments: + + DeviceObject - Supplies the device object which represents the logical + unit. + + Irp - Supplies the Irp which has completed. + + Context - Supplies a pointer to the SRB. + +Return Value: + + NT status + +--*/ -/* - * Implements part of the directives on: - * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/kmarch/hh/kmarch/other_c694d732-fa95-4841-8d61-2a55ee787905.xml.asp - */ -static VOID -ScsiClassInvalidateMedia(IN PDEVICE_OBJECT DeviceObject, - OUT NTSTATUS *Status) { - PDEVICE_EXTENSION DeviceExtension; - PDEVICE_EXTENSION PhysicalExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = Context; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + NTSTATUS status; + BOOLEAN retry; - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - PhysicalExtension = (PDEVICE_EXTENSION)DeviceExtension->PhysicalDevice->DeviceExtension; + // + // Check SRB status for success of completing request. + // - if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - { - DPRINT("Invalidate: test char yields TRUE\n"); - } - else - { - DPRINT("Invalidate: test char yields FALSE\n"); + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + DebugPrint((2,"ScsiClassIoComplete: IRP %lx, SRB %lx\n", Irp, srb)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ScsiClassReleaseQueue(DeviceObject); + } + + retry = ScsiClassInterpretSenseInfo( + DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0, + MAXIMUM_RETRIES - ((ULONG)irpStack->Parameters.Others.Argument4), + &status); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && (irpStack->Parameters.Others.Argument4 = (ULONG)irpStack->Parameters.Others.Argument4-1)) { + + // + // Retry request. + // + + DebugPrint((1, "Retry request %lx\n", Irp)); + RetryRequest(DeviceObject, Irp, srb, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; + } + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + + } // end if (SRB_STATUS(srb->SrbStatus) ... + + // + // Return SRB to list. + // + + ExFreeToNPagedLookasideList(&deviceExtension->SrbLookasideListHead, + srb); + + // + // Set status in completing IRP. + // + + Irp->IoStatus.Status = status; + if ((NT_SUCCESS(status)) && (Irp->Flags & IRP_PAGING_IO)) { + ASSERT(Irp->IoStatus.Information); } - if ((DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) && - (DeviceObject->Vpb->Flags & VPB_MOUNTED)) - { - DPRINT("Set DO_VERIFY_VOLUME\n"); - DeviceObject->Flags |= DO_VERIFY_VOLUME; - *Status = STATUS_VERIFY_REQUIRED; - } - else - { - *Status = STATUS_IO_DEVICE_ERROR; + // + // Set the hard error if necessary. + // + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem, and clear + // in IoStatus.Information field. + // + + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + Irp->IoStatus.Information = 0; } - /* Increment the media change count */ - PhysicalExtension->MediaChangeCount++; -} + // + // If pending has be returned for this irp then mark the current stack as + // pending. + // + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + if (deviceExtension->ClassStartIo) { + if (irpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) { + IoStartNextPacket(DeviceObject, FALSE); + } + } + + return status; + +} // end ScsiClassIoComplete() + + +NTSTATUS +STDCALL +ScsiClassIoCompleteAssociated( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine executes when the port driver has completed a request. + It looks at the SRB status in the completing SRB and if not success + it checks for valid request sense buffer information. If valid, the + info is used to update status with more precise message of type of + error. This routine deallocates the SRB. This routine is used for + requests which were build by split request. After it has processed + the request it decrements the Irp count in the master Irp. If the + count goes to zero then the master Irp is completed. + +Arguments: + + DeviceObject - Supplies the device object which represents the logical + unit. + + Irp - Supplies the Irp which has completed. + + Context - Supplies a pointer to the SRB. + +Return Value: + + NT status + +--*/ -/* - * @implemented - */ -BOOLEAN STDCALL -ScsiClassInterpretSenseInfo(IN PDEVICE_OBJECT DeviceObject, - IN PSCSI_REQUEST_BLOCK Srb, - IN UCHAR MajorFunctionCode, - IN ULONG IoDeviceCode, - IN ULONG RetryCount, - OUT NTSTATUS *Status) { - PDEVICE_EXTENSION DeviceExtension; - PDEVICE_EXTENSION PhysicalExtension; -#if 0 - PIO_ERROR_LOG_PACKET LogPacket; -#endif - PSENSE_DATA SenseData; - BOOLEAN LogError; - BOOLEAN Retry; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = Context; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIRP originalIrp = Irp->AssociatedIrp.MasterIrp; + LONG irpCount; + NTSTATUS status; + BOOLEAN retry; - DPRINT("ScsiClassInterpretSenseInfo() called\n"); + // + // Check SRB status for success of completing request. + // - DPRINT("Srb->SrbStatus %lx\n", Srb->SrbStatus); + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { - if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_PENDING) - { - *Status = STATUS_SUCCESS; - return(FALSE); + DebugPrint((2,"ScsiClassIoCompleteAssociated: IRP %lx, SRB %lx", Irp, srb)); + + // + // Release the queue if it is frozen. + // + + if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ScsiClassReleaseQueue(DeviceObject); + } + + retry = ScsiClassInterpretSenseInfo( + DeviceObject, + srb, + irpStack->MajorFunction, + irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0, + MAXIMUM_RETRIES - ((ULONG)irpStack->Parameters.Others.Argument4), + &status); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && + status == STATUS_VERIFY_REQUIRED) { + + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && (irpStack->Parameters.Others.Argument4 = (ULONG)irpStack->Parameters.Others.Argument4-1)) { + + // + // Retry request. If the class driver has supplied a StartIo, + // call it directly for retries. + // + + DebugPrint((1, "Retry request %lx\n", Irp)); + + /* + if (!deviceExtension->ClassStartIo) { + RetryRequest(DeviceObject, Irp, srb, TRUE); + } else { + deviceExtension->ClassStartIo(DeviceObject, Irp); + } + */ + + RetryRequest(DeviceObject, Irp, srb, TRUE); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + + + } else { + + // + // Set status for successful request. + // + + status = STATUS_SUCCESS; + + } // end if (SRB_STATUS(srb->SrbStatus) ... + + // + // Return SRB to list. + // + + ExFreeToNPagedLookasideList(&deviceExtension->SrbLookasideListHead, + srb); + + // + // Set status in completing IRP. + // + + Irp->IoStatus.Status = status; + + DebugPrint((2, "ScsiClassIoCompleteAssociated: Partial xfer IRP %lx\n", Irp)); + + // + // Get next stack location. This original request is unused + // except to keep track of the completing partial IRPs so the + // stack location is valid. + // + + irpStack = IoGetNextIrpStackLocation(originalIrp); + + // + // Update status only if error so that if any partial transfer + // completes with error, then the original IRP will return with + // error. If any of the asynchronous partial transfer IRPs fail, + // with an error then the original IRP will return 0 bytes transfered. + // This is an optimization for successful transfers. + // + + if (!NT_SUCCESS(status)) { + + originalIrp->IoStatus.Status = status; + originalIrp->IoStatus.Information = 0; + + // + // Set the hard error if necessary. + // + + if (IoIsErrorUserInduced(status)) { + + // + // Store DeviceObject for filesystem. + // + + IoSetHardErrorOrVerifyDevice(originalIrp, DeviceObject); + } } - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - PhysicalExtension = (PDEVICE_EXTENSION)DeviceExtension->PhysicalDevice->DeviceExtension; - SenseData = Srb->SenseInfoBuffer; - LogError = FALSE; - Retry = TRUE; + // + // Decrement and get the count of remaining IRPs. + // - if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && - (Srb->SenseInfoBufferLength > 0)) - { - /* Got valid sense data, interpret them */ + irpCount = InterlockedDecrement((PLONG)&irpStack->Parameters.Others.Argument1); - DPRINT("ErrorCode: %x\n", SenseData->ErrorCode); - DPRINT("SenseKey: %x\n", SenseData->SenseKey); - DPRINT("SenseCode: %x\n", SenseData->AdditionalSenseCode); + DebugPrint((2, "ScsiClassIoCompleteAssociated: Partial IRPs left %d\n", + irpCount)); - switch (SenseData->SenseKey & 0xf) - { - case SCSI_SENSE_NO_SENSE: - DPRINT("SCSI_SENSE_NO_SENSE\n"); - if (SenseData->IncorrectLength) - { - DPRINT("Incorrect block length\n"); - *Status = STATUS_INVALID_BLOCK_LENGTH; - Retry = FALSE; - } - else - { - DPRINT("Unspecified error\n"); - *Status = STATUS_IO_DEVICE_ERROR; - Retry = FALSE; - } - break; + // + // Old bug could cause irp count to negative + // - case SCSI_SENSE_RECOVERED_ERROR: - DPRINT("SCSI_SENSE_RECOVERED_ERROR\n"); - *Status = STATUS_SUCCESS; - Retry = FALSE; - break; + ASSERT(irpCount >= 0); - case SCSI_SENSE_NOT_READY: - DPRINT("SCSI_SENSE_NOT_READY\n"); - *Status = STATUS_DEVICE_NOT_READY; - switch (SenseData->AdditionalSenseCode) - { - case SCSI_ADSENSE_LUN_NOT_READY: - DPRINT("SCSI_ADSENSE_LUN_NOT_READY\n"); - break; + if (irpCount == 0) { - case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: - DPRINT("SCSI_ADSENSE_NO_MEDIA_IN_DEVICE\n"); - ScsiClassInvalidateMedia(DeviceObject, - Status); + // + // All partial IRPs have completed. + // - *Status = STATUS_NO_MEDIA_IN_DEVICE; - Retry = FALSE; + DebugPrint((2, + "ScsiClassIoCompleteAssociated: All partial IRPs complete %lx\n", + originalIrp)); - if((DeviceExtension->MediaChangeEvent != NULL) && - (!DeviceExtension->MediaChangeEvent)) - { - KeSetEvent(DeviceExtension->MediaChangeEvent, - 0, - FALSE); - DeviceExtension->MediaChangeNoMedia = TRUE; - } - break; - } - break; + IoCompleteRequest(originalIrp, IO_DISK_INCREMENT); - case SCSI_SENSE_MEDIUM_ERROR: - DPRINT("SCSI_SENSE_MEDIUM_ERROR\n"); - *Status = STATUS_DEVICE_DATA_ERROR; - Retry = FALSE; - break; + // + // If the class driver has supplied a startio, start the + // next request. + // - case SCSI_SENSE_HARDWARE_ERROR: - DPRINT("SCSI_SENSE_HARDWARE_ERROR\n"); - *Status = STATUS_IO_DEVICE_ERROR; - break; - - case SCSI_SENSE_ILLEGAL_REQUEST: - DPRINT("SCSI_SENSE_ILLEGAL_REQUEST\n"); - *Status = STATUS_INVALID_DEVICE_REQUEST; - switch (SenseData->AdditionalSenseCode) - { - case SCSI_ADSENSE_ILLEGAL_COMMAND: - DPRINT("SCSI_ADSENSE_ILLEGAL_COMMAND\n"); - Retry = FALSE; - break; - - case SCSI_ADSENSE_ILLEGAL_BLOCK: - DPRINT("SCSI_ADSENSE_ILLEGAL_BLOCK\n"); - *Status = STATUS_NONEXISTENT_SECTOR; - Retry = FALSE; - break; - - case SCSI_ADSENSE_INVALID_LUN: - DPRINT("SCSI_ADSENSE_INVALID_LUN\n"); - *Status = STATUS_NO_SUCH_DEVICE; - Retry = FALSE; - break; - - case SCSI_ADSENSE_MUSIC_AREA: - DPRINT("SCSI_ADSENSE_MUSIC_AREA\n"); - Retry = FALSE; - break; - - case SCSI_ADSENSE_DATA_AREA: - DPRINT("SCSI_ADSENSE_DATA_AREA\n"); - Retry = FALSE; - break; - - case SCSI_ADSENSE_VOLUME_OVERFLOW: - DPRINT("SCSI_ADSENSE_VOLUME_OVERFLOW\n"); - Retry = FALSE; - break; - - case SCSI_ADSENSE_INVALID_CDB: - DPRINT("SCSI_ADSENSE_INVALID_CDB\n"); - Retry = FALSE; - break; - } - break; - - case SCSI_SENSE_UNIT_ATTENTION: - DPRINT("SCSI_SENSE_UNIT_ATTENTION\n"); - switch (SenseData->AdditionalSenseCode) - { - case SCSI_ADSENSE_MEDIUM_CHANGED: - DPRINT("SCSI_ADSENSE_MEDIUM_CHANGED\n"); - if(DeviceExtension->MediaChangeEvent != NULL) - { - KeSetEvent(DeviceExtension->MediaChangeEvent, - 0, - FALSE); - DeviceExtension->MediaChangeNoMedia = FALSE; - } - break; - - case SCSI_ADSENSE_BUS_RESET: - DPRINT("SCSI_ADSENSE_BUS_RESET\n"); - break; - - default: - DPRINT("Unit attention\n"); - break; - } - - ScsiClassInvalidateMedia(DeviceObject, - Status); - Retry = FALSE; - break; - - case SCSI_SENSE_DATA_PROTECT: - DPRINT("SCSI_SENSE_DATA_PROTECT\n"); - *Status = STATUS_MEDIA_WRITE_PROTECTED; - Retry = FALSE; - break; - - case SCSI_SENSE_ABORTED_COMMAND: - DPRINT("SCSI_SENSE_ABORTED_COMMAND\n"); - *Status = STATUS_IO_DEVICE_ERROR; - break; - - default: - DPRINT("SCSI error (sense key: %x)\n", - SenseData->SenseKey & 0xf); - *Status = STATUS_IO_DEVICE_ERROR; - break; - } - } - else - { - /* Got no or invalid sense data, return generic error codes */ - switch (SRB_STATUS(Srb->SrbStatus)) - { - /* FIXME: add more srb status codes */ - - case SRB_STATUS_INVALID_PATH_ID: - case SRB_STATUS_INVALID_TARGET_ID: - case SRB_STATUS_INVALID_LUN: - case SRB_STATUS_NO_DEVICE: - case SRB_STATUS_NO_HBA: - *Status = STATUS_NO_SUCH_DEVICE; - Retry = FALSE; - break; - - case SRB_STATUS_BUSY: - *Status = STATUS_DEVICE_BUSY; - Retry = TRUE; - break; - - case SRB_STATUS_DATA_OVERRUN: - *Status = STATUS_DATA_OVERRUN; - Retry = FALSE; - break; - - case SRB_STATUS_INVALID_REQUEST: - *Status = STATUS_INVALID_DEVICE_REQUEST; - Retry = FALSE; - break; - - default: - DPRINT("SCSI error (SRB status: %x)\n", - SRB_STATUS(Srb->SrbStatus)); - LogError = TRUE; - *Status = STATUS_IO_DEVICE_ERROR; - break; - } + if (deviceExtension->ClassStartIo) { + IoStartNextPacket(DeviceObject, FALSE); + } } - /* Call the class driver specific error function */ - if (DeviceExtension->ClassError != NULL) - { - DeviceExtension->ClassError(DeviceObject, - Srb, - Status, - &Retry); - } + // + // Deallocate IRP and indicate the I/O system should not attempt any more + // processing. + // - if (LogError == TRUE) - { -#if 0 - /* Allocate error packet */ - LogPacket = IoAllocateErrorLogEntry (DeviceObject, - sizeof(IO_ERROR_LOG_PACKET) + - 5 * sizeof(ULONG)); - if (LogPacket == NULL) - { - DPRINT ("Failed to allocate a log packet!\n"); - return Retry; - } + IoFreeIrp(Irp); + return STATUS_MORE_PROCESSING_REQUIRED; - /* Initialize error packet */ - LogPacket->MajorFunctionCode = MajorFunctionCode; - LogPacket->RetryCount = (UCHAR)RetryCount; - LogPacket->DumpDataSize = 6 * sizeof(ULONG); - LogPacket->ErrorCode = 0; /* FIXME */ - LogPacket->FinalStatus = *Status; - LogPacket->IoControlCode = IoDeviceCode; - LogPacket->DeviceOffset.QuadPart = 0; /* FIXME */ - LogPacket->DumpData[0] = Srb->PathId; - LogPacket->DumpData[1] = Srb->TargetId; - LogPacket->DumpData[2] = Srb->Lun; - LogPacket->DumpData[3] = 0; - LogPacket->DumpData[4] = (Srb->SrbStatus << 8) | Srb->ScsiStatus; - if (SenseData != NULL) - { - LogPacket->DumpData[5] = (SenseData->SenseKey << 16) | - (SenseData->AdditionalSenseCode << 8) | - SenseData->AdditionalSenseCodeQualifier; - } +} // end ScsiClassIoCompleteAssociated() - /* Write error packet */ - IoWriteErrorLogEntry (LogPacket); -#endif - } + +NTSTATUS +STDCALL +ScsiClassSendSrbSynchronous( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + PVOID BufferAddress, + ULONG BufferLength, + BOOLEAN WriteToDevice + ) - DPRINT("ScsiClassInterpretSenseInfo() done\n"); +/*++ - return Retry; -} +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: + + DeviceObject - Supplies the device object which represents the logical + unit. + + Srb - Supplies a partially initialized SRB. The SRB cannot come from zone. + + BufferAddress - Supplies the address of the buffer. + + BufferLength - Supplies the length in bytes of the buffer. + + WriteToDevice - Indicates the data should be transfer to the device. + +Return Value: + + Nt status indicating the final results of the operation. + +--*/ -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassIoComplete(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context) { - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION IrpStack; - PSCSI_REQUEST_BLOCK Srb; - BOOLEAN Retry; - NTSTATUS Status; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + IO_STATUS_BLOCK ioStatus; + ULONG controlType; + PIRP irp; + PIO_STACK_LOCATION irpStack; + KEVENT event; + PUCHAR senseInfoBuffer; + ULONG retryCount = MAXIMUM_RETRIES; + NTSTATUS status; + BOOLEAN retry; - DPRINT("ScsiClassIoComplete(DeviceObject %p Irp %p Context %p) called\n", - DeviceObject, Irp, Context); + PAGED_CODE(); - DeviceExtension = DeviceObject->DeviceExtension; + // + // Write length to SRB. + // - IrpStack = IoGetCurrentIrpStackLocation(Irp); + Srb->Length = SCSI_REQUEST_BLOCK_SIZE; - /* - * BUGBUG -> Srb = IrpStack->Parameters.Scsi.Srb; - * Must pass Srb as Context arg!! See comment about Completion routines in - * IofCallDriver for more info. - */ + // + // Set SCSI bus address. + // - Srb = (PSCSI_REQUEST_BLOCK)Context; + Srb->PathId = deviceExtension->PathId; + Srb->TargetId = deviceExtension->TargetId; + Srb->Lun = deviceExtension->Lun; + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - DPRINT("Srb %p\n", Srb); + // + // NOTICE: The SCSI-II specification indicates that this field should be + // zero; however, some target controllers ignore the logical unit number + // in the INDENTIFY message and only look at the logical unit number field + // in the CDB. + // - if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) - { - Status = STATUS_SUCCESS; - } - else - { - /* Release the queue if it is frozen */ - if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) - { - ScsiClassReleaseQueue (DeviceObject); - } + Srb->Cdb[1] |= deviceExtension->Lun << 5; - /* Get more detailed status information */ - Retry = ScsiClassInterpretSenseInfo(DeviceObject, - Srb, - IrpStack->MajorFunction, - 0, - MAXIMUM_RETRIES - ((ULONG)IrpStack->Parameters.Others.Argument4), - &Status); + // + // Enable auto request sense. + // - /* Retry the request if verify should be overridden */ - if ((IrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) && - Status == STATUS_VERIFY_REQUIRED) - { - Status = STATUS_IO_DEVICE_ERROR; - Retry = TRUE; - } + Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; - /* Retry the request */ - if ((Retry) && - ((ULONG_PTR)IrpStack->Parameters.Others.Argument4 > 0)) - { - IrpStack->Parameters.Others.Argument4 = (PVOID) ((ULONG_PTR)IrpStack->Parameters.Others.Argument4 - 1); + // + // Sense buffer is in aligned nonpaged pool. + // - ScsiClassRetryRequest(DeviceObject, - Irp, - Srb, - FALSE); + senseInfoBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE); - return(STATUS_MORE_PROCESSING_REQUIRED); - } + if (senseInfoBuffer == NULL) { + + DebugPrint((1, + "ScsiClassSendSrbSynchronous: Can't allocate request sense buffer\n")); + return(STATUS_INSUFFICIENT_RESOURCES); } - /* Free the SRB */ - ExFreeToNPagedLookasideList(&DeviceExtension->SrbLookasideListHead, - Srb); + Srb->SenseInfoBuffer = senseInfoBuffer; + Srb->DataBuffer = BufferAddress; - Irp->IoStatus.Status = Status; - if (!NT_SUCCESS(Status)) - { - Irp->IoStatus.Information = 0; - if (IoIsErrorUserInduced(Status)) - { - IoSetHardErrorOrVerifyDevice(Irp, - DeviceObject); - } - } + // + // Start retries here. + // - if (Irp->PendingReturned) - { - IoMarkIrpPending (Irp); - } +retry: - if (DeviceExtension->ClassStartIo != NULL) - { - if (IrpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) - { - KIRQL oldIrql; - oldIrql = KeGetCurrentIrql(); - if (oldIrql < DISPATCH_LEVEL) - { - KeRaiseIrql (DISPATCH_LEVEL, &oldIrql); - IoStartNextPacket (DeviceObject, FALSE); - KeLowerIrql(oldIrql); - } - else - { - IoStartNextPacket (DeviceObject, FALSE); - } - } - } + // + // Set the event object to the unsignaled state. + // It will be used to signal request completion. + // - DPRINT("ScsiClassIoComplete() done (Status %lx)\n", Status); + KeInitializeEvent(&event, NotificationEvent, FALSE); - return(Status); -} + // + // Set controlType and Srb direction flags. + // + if (BufferAddress != NULL) { -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassIoCompleteAssociated(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context) -{ - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION IrpStack; - PSCSI_REQUEST_BLOCK Srb; - PIRP MasterIrp; - BOOLEAN Retry; - LONG RequestCount; - NTSTATUS Status; + if (WriteToDevice) { - DPRINT("ScsiClassIoCompleteAssociated(DeviceObject %p Irp %p Context %p) called\n", - DeviceObject, Irp, Context); + controlType = IOCTL_SCSI_EXECUTE_OUT; + Srb->SrbFlags = SRB_FLAGS_DATA_OUT; - MasterIrp = Irp->AssociatedIrp.MasterIrp; - DeviceExtension = DeviceObject->DeviceExtension; + } else { - IrpStack = IoGetCurrentIrpStackLocation(Irp); + controlType = IOCTL_SCSI_EXECUTE_IN; + Srb->SrbFlags = SRB_FLAGS_DATA_IN; - /* - * BUGBUG -> Srb = Srb = IrpStack->Parameters.Scsi.Srb; - * Must pass Srb as Context arg!! See comment about Completion routines in - * IofCallDriver for more info. - */ + } - Srb = (PSCSI_REQUEST_BLOCK)Context; + } else { - DPRINT("Srb %p\n", Srb); - - if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) - { - Status = STATUS_SUCCESS; - } - else - { - /* Release the queue if it is frozen */ - if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) - { - ScsiClassReleaseQueue (DeviceObject); - } - - /* Get more detailed status information */ - Retry = ScsiClassInterpretSenseInfo(DeviceObject, - Srb, - IrpStack->MajorFunction, - 0, - MAXIMUM_RETRIES - ((ULONG)IrpStack->Parameters.Others.Argument4), - &Status); - - /* Retry the request if verify should be overridden */ - if ((IrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) && - Status == STATUS_VERIFY_REQUIRED) - { - Status = STATUS_IO_DEVICE_ERROR; - Retry = TRUE; - } - - /* Retry the request */ - if ((Retry) && - ((ULONG_PTR)IrpStack->Parameters.Others.Argument4 > 0)) - { - IrpStack->Parameters.Others.Argument4 = (PVOID) ((ULONG_PTR)IrpStack->Parameters.Others.Argument4 - 1); - - ScsiClassRetryRequest(DeviceObject, - Irp, - Srb, - TRUE); - - return(STATUS_MORE_PROCESSING_REQUIRED); - } - } - - /* Free the SRB */ - ExFreeToNPagedLookasideList(&DeviceExtension->SrbLookasideListHead, - Srb); - - Irp->IoStatus.Status = Status; - - IrpStack = IoGetNextIrpStackLocation(MasterIrp); - if (!NT_SUCCESS(Status)) - { - MasterIrp->IoStatus.Status = Status; - MasterIrp->IoStatus.Information = 0; - - if (IoIsErrorUserInduced(Status)) - { - IoSetHardErrorOrVerifyDevice(MasterIrp, - DeviceObject); - } - } - - /* Decrement the request counter in the Master IRP */ - RequestCount = InterlockedDecrement((PLONG)&IrpStack->Parameters.Others.Argument1); - - if (RequestCount == 0) - { - /* Complete the Master IRP */ - IoCompleteRequest(MasterIrp, - IO_DISK_INCREMENT); - - if (DeviceExtension->ClassStartIo) - { - KIRQL oldIrql; - oldIrql = KeGetCurrentIrql(); - if (oldIrql < DISPATCH_LEVEL) - { - KeRaiseIrql (DISPATCH_LEVEL, &oldIrql); - IoStartNextPacket (DeviceObject, FALSE); - KeLowerIrql(oldIrql); - } - else - { - IoStartNextPacket (DeviceObject, FALSE); - } - } - } - - /* Free the current IRP */ - IoFreeIrp(Irp); - - return(STATUS_MORE_PROCESSING_REQUIRED); -} - - -/* - * @implemented - */ -ULONG STDCALL -ScsiClassModeSense(IN PDEVICE_OBJECT DeviceObject, - IN PCHAR ModeSenseBuffer, - IN ULONG Length, - IN UCHAR PageMode) -{ - PDEVICE_EXTENSION DeviceExtension; - SCSI_REQUEST_BLOCK Srb; - ULONG RetryCount; - PCDB Cdb; - NTSTATUS Status; - - DPRINT("ScsiClassModeSense() called\n"); - - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - RetryCount = 1; - - /* Initialize the SRB */ - RtlZeroMemory (&Srb, - sizeof(SCSI_REQUEST_BLOCK)); - Srb.CdbLength = 6; - Srb.TimeOutValue = DeviceExtension->TimeOutValue; - - /* Initialize the CDB */ - Cdb = (PCDB)&Srb.Cdb; - Cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; - Cdb->MODE_SENSE.PageCode = PageMode; - Cdb->MODE_SENSE.AllocationLength = (UCHAR)Length; - -TryAgain: - Status = ScsiClassSendSrbSynchronous (DeviceObject, - &Srb, - ModeSenseBuffer, - Length, - FALSE); - if (Status == STATUS_VERIFY_REQUIRED) - { - if (RetryCount != 0) - { - RetryCount--; - goto TryAgain; - } - } - else if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) - { - Status = STATUS_SUCCESS; - } - - if (!NT_SUCCESS(Status)) - { - return 0; - } - - return Srb.DataTransferLength; -} - - -/* - * @implemented - */ -ULONG STDCALL -ScsiClassQueryTimeOutRegistryValue(IN PUNICODE_STRING RegistryPath) -{ - PRTL_QUERY_REGISTRY_TABLE Table; - ULONG TimeOutValue; - ULONG ZeroTimeOut; - ULONG Size; - PWSTR Path; - NTSTATUS Status; - - if (RegistryPath == NULL) - { - return 0; - } - - TimeOutValue = 0; - ZeroTimeOut = 0; - - /* Allocate zero-terminated path string */ - Size = RegistryPath->Length + sizeof(WCHAR); - Path = (PWSTR)ExAllocatePool (NonPagedPool, - Size); - if (Path == NULL) - { - return 0; - } - RtlZeroMemory (Path, - Size); - RtlCopyMemory (Path, - RegistryPath->Buffer, - Size - sizeof(WCHAR)); - - /* Allocate query table */ - Size = sizeof(RTL_QUERY_REGISTRY_TABLE) * 2; - Table = (PRTL_QUERY_REGISTRY_TABLE)ExAllocatePool (NonPagedPool, - Size); - if (Table == NULL) - { - ExFreePool (Path); - return 0; - } - RtlZeroMemory (Table, - Size); - - Table[0].Flags = RTL_QUERY_REGISTRY_DIRECT; - Table[0].Name = L"TimeOutValue"; - Table[0].EntryContext = &TimeOutValue; - Table[0].DefaultType = REG_DWORD; - Table[0].DefaultData = &ZeroTimeOut; - Table[0].DefaultLength = sizeof(ULONG); - - Status = RtlQueryRegistryValues (RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, - Path, - Table, - NULL, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT("RtlQueryRegistryValue() failed (Status %lx)\n", Status); - TimeOutValue = 0; - } - - ExFreePool (Table); - ExFreePool (Path); - - DPRINT("TimeOut: %lu\n", TimeOutValue); - - return TimeOutValue; -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassReadDriveCapacity(IN PDEVICE_OBJECT DeviceObject) -{ - PDEVICE_EXTENSION DeviceExtension; - PREAD_CAPACITY_DATA CapacityBuffer; - SCSI_REQUEST_BLOCK Srb; - PCDB Cdb; - NTSTATUS Status; - ULONG LastSector; - ULONG SectorSize; - ULONG RetryCount = 1; - - DPRINT("ScsiClassReadDriveCapacity() called\n"); - - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - CapacityBuffer = ExAllocatePool(NonPagedPoolCacheAligned, - sizeof(READ_CAPACITY_DATA)); - if (CapacityBuffer == NULL) - { - return(STATUS_INSUFFICIENT_RESOURCES); - } - - RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK)); - RtlZeroMemory(CapacityBuffer, sizeof(READ_CAPACITY_DATA)); - - Srb.CdbLength = 10; - Srb.TimeOutValue = DeviceExtension->TimeOutValue; - - Cdb = (PCDB)Srb.Cdb; - Cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; - -TryAgain: - Status = ScsiClassSendSrbSynchronous(DeviceObject, - &Srb, - CapacityBuffer, - sizeof(READ_CAPACITY_DATA), - FALSE); - DPRINT("Status: %lx\n", Status); - DPRINT("Srb: %p\n", &Srb); - if (CapacityBuffer->BytesPerBlock == 0) Status = STATUS_NOT_SUPPORTED; - if (NT_SUCCESS(Status)) - { - SectorSize = (((PUCHAR)&CapacityBuffer->BytesPerBlock)[0] << 24) | - (((PUCHAR)&CapacityBuffer->BytesPerBlock)[1] << 16) | - (((PUCHAR)&CapacityBuffer->BytesPerBlock)[2] << 8) | - ((PUCHAR)&CapacityBuffer->BytesPerBlock)[3]; - - - LastSector = (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[0] << 24) | - (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[1] << 16) | - (((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[2] << 8) | - ((PUCHAR)&CapacityBuffer->LogicalBlockAddress)[3]; - - DeviceExtension->DiskGeometry->BytesPerSector = SectorSize; - - DeviceExtension->PartitionLength.QuadPart = (LONGLONG)(LastSector + 1); - WHICH_BIT(DeviceExtension->DiskGeometry->BytesPerSector, - DeviceExtension->SectorShift); - DeviceExtension->PartitionLength.QuadPart = - (DeviceExtension->PartitionLength.QuadPart << DeviceExtension->SectorShift); - - DeviceExtension->PartitionLength.QuadPart = - (DeviceExtension->PartitionLength.QuadPart - - DeviceExtension->StartingOffset.QuadPart); - - if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - { - DeviceExtension->DiskGeometry->MediaType = RemovableMedia; - } - else - { - DeviceExtension->DiskGeometry->MediaType = FixedMedia; - } - DeviceExtension->DiskGeometry->Cylinders.QuadPart = (LONGLONG)((LastSector + 1)/(32 * 64)); - DeviceExtension->DiskGeometry->SectorsPerTrack = 32; - DeviceExtension->DiskGeometry->TracksPerCylinder = 64; - - DPRINT("SectorSize: %lu SectorCount: %lu PartitionLenght %I64d\n", SectorSize, LastSector + 1, - DeviceExtension->PartitionLength.QuadPart / 512 ); - } - - /* Try again if device needs to be verified */ - if (Status == STATUS_VERIFY_REQUIRED) - { - if (RetryCount > 0) - { - RetryCount--; - goto TryAgain; - } - } - - if (!NT_SUCCESS(Status)) - { - /* Use default values if disk geometry cannot be read */ - RtlZeroMemory(DeviceExtension->DiskGeometry, - sizeof(DISK_GEOMETRY)); - DeviceExtension->DiskGeometry->BytesPerSector = 512; - DeviceExtension->SectorShift = 9; - DeviceExtension->PartitionLength.QuadPart = 0; - - if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - { - DeviceExtension->DiskGeometry->MediaType = RemovableMedia; - } - else - { - DeviceExtension->DiskGeometry->MediaType = FixedMedia; - } - - DPRINT("SectorSize: 512 SectorCount: 0\n"); - } - - ExFreePool(CapacityBuffer); - - DPRINT("ScsiClassReadDriveCapacity() done\n"); - - return(Status); -} - - -/* - * @implemented - */ -VOID STDCALL -ScsiClassReleaseQueue(IN PDEVICE_OBJECT DeviceObject) -{ - PDEVICE_EXTENSION DeviceExtension; - PCOMPLETION_CONTEXT Context; - PIO_STACK_LOCATION Stack; - PSCSI_REQUEST_BLOCK Srb; - KIRQL Irql; - PCDB Cdb; - PIRP Irp; - - DPRINT("ScsiClassReleaseQueue() called\n"); - - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - /* Allocate and initialize the completion context */ - Context = ExAllocatePool (NonPagedPoolMustSucceed, - sizeof (COMPLETION_CONTEXT)); - Context->DeviceObject = DeviceObject; - - /* Initialize the SRB */ - Srb = &Context->Srb; - RtlZeroMemory (Srb, - sizeof (SCSI_REQUEST_BLOCK)); - Srb->Length = sizeof (SCSI_REQUEST_BLOCK); - Srb->PathId = DeviceExtension->PathId; - Srb->TargetId = DeviceExtension->TargetId; - Srb->Lun = DeviceExtension->Lun; - Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - Srb->TimeOutValue = START_UNIT_TIMEOUT; - Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | - SRB_FLAGS_DISABLE_AUTOSENSE | - SRB_FLAGS_DISABLE_SYNCH_TRANSFER; - Srb->CdbLength = 6; - - /* Initialize the CDB */ - Cdb = (PCDB)&Srb->Cdb; - Cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; - Cdb->START_STOP.Start = 1; - Cdb->START_STOP.LogicalUnitNumber = Srb->Lun; - - /* Build the IRP */ - Irp = IoAllocateIrp (DeviceObject->StackSize, FALSE); - IoSetCompletionRoutine (Irp, - (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion, - Context, - NULL, - NULL, - NULL); - - /* Attach SRB to the IRP */ - Irp->Tail.Overlay.Thread = PsGetCurrentThread(); - Stack = IoGetNextIrpStackLocation(Irp); - Stack->MajorFunction = IRP_MJ_SCSI; - Stack->Parameters.Scsi.Srb = Srb; - Srb->OriginalRequest = Irp; - - /* Call the port driver */ - Irql = KeGetCurrentIrql (); - if (Irql < DISPATCH_LEVEL) - { - KeRaiseIrql (DISPATCH_LEVEL, &Irql); - IoCallDriver (DeviceExtension->PortDeviceObject, Irp); - KeLowerIrql (Irql); - } - else - { - IoCallDriver (DeviceExtension->PortDeviceObject, Irp); - } - - DPRINT("ScsiClassReleaseQueue() done\n"); -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassSendSrbAsynchronous(PDEVICE_OBJECT DeviceObject, - PSCSI_REQUEST_BLOCK Srb, - PIRP Irp, - PVOID BufferAddress, - ULONG BufferLength, - BOOLEAN WriteToDevice) -{ - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION Stack; - - DPRINT("ScsiClassSendSrbAsynchronous() called\n"); - - DeviceExtension = DeviceObject->DeviceExtension; - - /* Initialize the SRB */ - Srb->Length = SCSI_REQUEST_BLOCK_SIZE; - Srb->PathId = DeviceExtension->PathId; - Srb->TargetId = DeviceExtension->TargetId; - Srb->Lun = DeviceExtension->Lun; - Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - Srb->Cdb[1] |= DeviceExtension->Lun << 5; - - Srb->SenseInfoBuffer = (SENSE_DATA*)ROUND_UP((ULONG_PTR)(Srb + 1), SENSEINFO_ALIGNMENT); - Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; - - Srb->DataBuffer = BufferAddress; - Srb->DataTransferLength = BufferLength; - - Srb->ScsiStatus = 0; - Srb->SrbStatus = 0; - Srb->NextSrb = NULL; - - if (BufferAddress != NULL) - { - if (Irp->MdlAddress == NULL) - { - /* Allocate an MDL */ - if (!IoAllocateMdl(BufferAddress, - BufferLength, - FALSE, - FALSE, - Irp)) - { - DPRINT("Mdl-Allocation failed\n"); - return(STATUS_INSUFFICIENT_RESOURCES); - } - - MmBuildMdlForNonPagedPool(Irp->MdlAddress); - } - - /* Set data direction */ - Srb->SrbFlags = (WriteToDevice) ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN; - } - else - { - /* Set data direction */ - Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER; - } - - /* Set the retry counter */ - Stack = IoGetCurrentIrpStackLocation(Irp); - Stack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; - - /* Set the completion routine */ - IoSetCompletionRoutine(Irp, - ScsiClassIoComplete, - Srb, - TRUE, - TRUE, - TRUE); - - /* Attach Srb to the Irp */ - Stack = IoGetNextIrpStackLocation(Irp); - Stack->MajorFunction = IRP_MJ_SCSI; - Stack->Parameters.Scsi.Srb = Srb; - Srb->OriginalRequest = Irp; - - /* Call the port driver */ - return(IoCallDriver(DeviceExtension->PortDeviceObject, - Irp)); -} - - -/* - * @implemented - */ -NTSTATUS STDCALL -ScsiClassSendSrbSynchronous(PDEVICE_OBJECT DeviceObject, - PSCSI_REQUEST_BLOCK Srb, - PVOID BufferAddress, - ULONG BufferLength, - BOOLEAN WriteToDevice) -{ - PDEVICE_EXTENSION DeviceExtension; - IO_STATUS_BLOCK IoStatusBlock; - PIO_STACK_LOCATION IrpStack; - ULONG RequestType; - BOOLEAN Retry; - ULONG RetryCount; - KEVENT Event; - PIRP Irp; - NTSTATUS Status; - LARGE_INTEGER RetryWait; - - DPRINT("ScsiClassSendSrbSynchronous() called\n"); - - RetryCount = MAXIMUM_RETRIES; - DeviceExtension = DeviceObject->DeviceExtension; - - Srb->Length = SCSI_REQUEST_BLOCK_SIZE; - Srb->PathId = DeviceExtension->PathId; - Srb->TargetId = DeviceExtension->TargetId; - Srb->Lun = DeviceExtension->Lun; - Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - - Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; - Srb->SenseInfoBuffer = ExAllocatePool(NonPagedPool, - SENSE_BUFFER_SIZE); - if (Srb->SenseInfoBuffer == NULL) - return(STATUS_INSUFFICIENT_RESOURCES); - - if (BufferAddress == NULL) - { BufferLength = 0; - RequestType = IOCTL_SCSI_EXECUTE_NONE; + controlType = IOCTL_SCSI_EXECUTE_NONE; Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER; } - else - { - if (WriteToDevice == TRUE) - { - RequestType = IOCTL_SCSI_EXECUTE_IN; // needs _in_ to the device - Srb->SrbFlags = SRB_FLAGS_DATA_OUT; // needs _out_ from the caller - } - else - { - RequestType = IOCTL_SCSI_EXECUTE_OUT; - Srb->SrbFlags = SRB_FLAGS_DATA_IN; - } + + // + // Build device I/O control request with data transfer. + // + + irp = IoBuildDeviceIoControlRequest(controlType, + deviceExtension->PortDeviceObject, + NULL, + 0, + BufferAddress, + BufferLength, + TRUE, + &event, + &ioStatus); + + if (irp == NULL) { + ExFreePool(senseInfoBuffer); + DebugPrint((1, "ScsiClassSendSrbSynchronous: Can't allocate Irp\n")); + return(STATUS_INSUFFICIENT_RESOURCES); } - Srb->DataBuffer = BufferAddress; + // + // Disable synchronous transfer for these requests. + // -TryAgain: - Srb->DataTransferLength = BufferLength; - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); + Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; - Irp = IoBuildDeviceIoControlRequest(RequestType, - DeviceExtension->PortDeviceObject, - NULL, - 0, - BufferAddress, - BufferLength, - TRUE, - &Event, - &IoStatusBlock); - if (Irp == NULL) - { - DPRINT("IoBuildDeviceIoControlRequest() failed\n"); - ExFreePool(Srb->SenseInfoBuffer); - Srb->SenseInfoBuffer = NULL; - Srb->SenseInfoBufferLength = 0; - return(STATUS_INSUFFICIENT_RESOURCES); + // + // Set the transfer length. + // + + Srb->DataTransferLength = BufferLength; + + // + // Zero out status. + // + + Srb->ScsiStatus = Srb->SrbStatus = 0; + Srb->NextSrb = 0; + + // + // Get next stack location. + // + + irpStack = IoGetNextIrpStackLocation(irp); + + // + // Set up SRB for execute scsi request. Save SRB address in next stack + // for the port driver. + // + + irpStack->Parameters.Scsi.Srb = Srb; + + // + // Set up IRP Address. + // + + Srb->OriginalRequest = irp; + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(deviceExtension->PortDeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL); } - /* Attach Srb to the Irp */ - IrpStack = IoGetNextIrpStackLocation(Irp); - IrpStack->Parameters.Scsi.Srb = Srb; - Srb->OriginalRequest = Irp; + // + // Check that request completed without error. + // - /* Call the SCSI port driver */ - Status = IoCallDriver(DeviceExtension->PortDeviceObject, - Irp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, - Suspended, - KernelMode, - FALSE, - NULL); + if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + // + // Release the queue if it is frozen. + // + + if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { + ScsiClassReleaseQueue(DeviceObject); + } + + // + // Update status and determine if request should be retried. + // + + retry = ScsiClassInterpretSenseInfo(DeviceObject, + Srb, + IRP_MJ_SCSI, + 0, + MAXIMUM_RETRIES - retryCount, + &status); + + if (retry) { + + if ((status == STATUS_DEVICE_NOT_READY && ((PSENSE_DATA) senseInfoBuffer) + ->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) || + SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT) { + + LARGE_INTEGER delay; + + // + // Delay for 2 seconds. + // + + delay.QuadPart = (LONGLONG)( - 10 * 1000 * 1000 * 2 ); + + // + // Stall for a while to let the controller spinup. + // + + KeDelayExecutionThread(KernelMode, + FALSE, + &delay); + + } + + // + // If retries are not exhausted then retry this operation. + // + + if (retryCount--) { + goto retry; + } + } + + } else { + + status = STATUS_SUCCESS; } - if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) - { - /* Release the queue if it is frozen */ - if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) - { - ScsiClassReleaseQueue (DeviceObject); - } + ExFreePool(senseInfoBuffer); + return status; - /* Get more detailed status information */ - Retry = ScsiClassInterpretSenseInfo(DeviceObject, - Srb, - IRP_MJ_SCSI, - 0, - MAXIMUM_RETRIES - RetryCount, - &Status); - if (Retry == TRUE) - { - DPRINT("Try again (RetryCount %lu)\n", RetryCount); +} // end ScsiClassSendSrbSynchronous() - /* FIXME: Wait a little if we got a timeout error */ + +BOOLEAN +STDCALL +ScsiClassInterpretSenseInfo( + IN PDEVICE_OBJECT DeviceObject, + IN PSCSI_REQUEST_BLOCK Srb, + IN UCHAR MajorFunctionCode, + IN ULONG IoDeviceCode, + IN ULONG RetryCount, + OUT NTSTATUS *Status + ) - if (RetryCount--) - { - RetryWait.QuadPart = - RETRY_WAIT; - KeDelayExecutionThread(KernelMode, FALSE, &RetryWait); - goto TryAgain; - } - } - } - else - { - Status = STATUS_SUCCESS; - } +/*++ - ExFreePool(Srb->SenseInfoBuffer); +Routine Description: - DPRINT("ScsiClassSendSrbSynchronous() done\n"); + 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. - return(Status); -} +Arguments: + DeviceObject - Supplies the device object associated with this request. + + Srb - Supplies the scsi request block which failed. + + MajorFunctionCode - Supplies the function code to be used for logging. + + IoDeviceCode - Supplies the device code to be used for logging. + + Status - Returns the status for the request. + +Return Value: + + BOOLEAN TRUE: Drivers should retry this request. + FALSE: Drivers should not retry this request. + +--*/ -/* - * @implemented - */ -VOID STDCALL -ScsiClassSplitRequest(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN ULONG MaximumBytes) { - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION CurrentStack; - PIO_STACK_LOCATION NextStack; - PIO_STACK_LOCATION NewStack; - PSCSI_REQUEST_BLOCK Srb; - LARGE_INTEGER Offset; - PIRP NewIrp; - PVOID DataBuffer; - ULONG TransferLength; - ULONG RequestCount; - ULONG DataLength; - ULONG i; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION physicalExtension = deviceExtension->PhysicalDevice->DeviceExtension; + PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; + BOOLEAN retry = TRUE; + BOOLEAN logError = FALSE; + ULONG badSector = 0; + ULONG uniqueId; + NTSTATUS logStatus; + ULONG readSector; + ULONG index; + PIO_ERROR_LOG_PACKET errorLogEntry; +#if DBG + ULONG i; +#endif - DPRINT("ScsiClassSplitRequest(DeviceObject %lx Irp %lx MaximumBytes %lu)\n", - DeviceObject, Irp, MaximumBytes); - DeviceExtension = DeviceObject->DeviceExtension; - CurrentStack = IoGetCurrentIrpStackLocation(Irp); - NextStack = IoGetNextIrpStackLocation(Irp); - DataBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress); + // + // Check that request sense buffer is valid. + // - /* Initialize transfer data for first request */ - Offset = CurrentStack->Parameters.Read.ByteOffset; - TransferLength = CurrentStack->Parameters.Read.Length; - - /* Set the result length */ - Irp->IoStatus.Information = TransferLength; - - DataLength = MaximumBytes; - RequestCount = ROUND_UP(TransferLength, MaximumBytes) / MaximumBytes; - - /* Save request count in the original IRP */ - NextStack->Parameters.Others.Argument1 = (PVOID)RequestCount; - - DPRINT("RequestCount %lu\n", RequestCount); - - for (i = 0; i < RequestCount; i++) - { - /* Create a new IRP */ - NewIrp = IoAllocateIrp(DeviceObject->StackSize, - FALSE); - if (NewIrp == NULL) - { - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - - if (i == 0) - IoCompleteRequest(Irp, - IO_NO_INCREMENT); - return; - } - - /* Initialize the new IRP */ - NewIrp->MdlAddress = Irp->MdlAddress; - NewIrp->Tail.Overlay.Thread = PsGetCurrentThread(); - - IoSetNextIrpStackLocation(NewIrp); - NewStack = IoGetCurrentIrpStackLocation(NewIrp); - - NewStack->MajorFunction = CurrentStack->MajorFunction; - NewStack->Parameters.Read.ByteOffset = Offset; - NewStack->Parameters.Read.Length = DataLength; - NewStack->DeviceObject = DeviceObject; - - ScsiClassBuildRequest(DeviceObject, - NewIrp); - - NewStack = IoGetNextIrpStackLocation(NewIrp); - Srb = NewStack->Parameters.Others.Argument1; - Srb->DataBuffer = DataBuffer; - - NewIrp->AssociatedIrp.MasterIrp = Irp; - - /* Initialize completion routine */ - IoSetCompletionRoutine(NewIrp, - ScsiClassIoCompleteAssociated, - Srb, - TRUE, - TRUE, - TRUE); - - /* Send the new IRP down to the port driver */ - IoCallDriver(DeviceExtension->PortDeviceObject, - NewIrp); - - /* Adjust transfer data for next request */ - DataBuffer = (PCHAR)DataBuffer + MaximumBytes; - TransferLength -= MaximumBytes; - DataLength = (TransferLength > MaximumBytes) ? MaximumBytes : TransferLength; - Offset.QuadPart = Offset.QuadPart + MaximumBytes; +#if DBG + DebugPrint((3, "Opcode %x\nParameters: ",Srb->Cdb[0])); + for (i = 1; i < 12; i++) { + DebugPrint((3,"%x ",Srb->Cdb[i])); } -} + DebugPrint((3,"\n")); +#endif + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + Srb->SenseInfoBufferLength >= FIELD_OFFSET(SENSE_DATA, CommandSpecificInformation)) { -/* INTERNAL FUNCTIONS *******************************************************/ + DebugPrint((1,"ScsiClassInterpretSenseInfo: Error code is %x\n", + senseBuffer->ErrorCode)); + DebugPrint((1,"ScsiClassInterpretSenseInfo: Sense key is %x\n", + senseBuffer->SenseKey)); + DebugPrint((1, "ScsiClassInterpretSenseInfo: Additional sense code is %x\n", + senseBuffer->AdditionalSenseCode)); + DebugPrint((1, "ScsiClassInterpretSenseInfo: Additional sense code qualifier is %x\n", + senseBuffer->AdditionalSenseCodeQualifier)); + + // + // Zero the additional sense code and additional sense code qualifier + // if they were not returned by the device. + // + + readSector = senseBuffer->AdditionalSenseLength + + FIELD_OFFSET(SENSE_DATA, AdditionalSenseLength); + + if (readSector > Srb->SenseInfoBufferLength) { + readSector = Srb->SenseInfoBufferLength; + } + + if (readSector <= FIELD_OFFSET(SENSE_DATA, AdditionalSenseCode)) { + senseBuffer->AdditionalSenseCode = 0; + } + + if (readSector <= FIELD_OFFSET(SENSE_DATA, AdditionalSenseCodeQualifier)) { + senseBuffer->AdditionalSenseCodeQualifier = 0; + } + + switch (senseBuffer->SenseKey & 0xf) { + + case SCSI_SENSE_NOT_READY: + + DebugPrint((1,"ScsiClassInterpretSenseInfo: Device not ready\n")); + *Status = STATUS_DEVICE_NOT_READY; + + switch (senseBuffer->AdditionalSenseCode) { + + case SCSI_ADSENSE_LUN_NOT_READY: + + DebugPrint((1,"ScsiClassInterpretSenseInfo: Lun not ready\n")); + + switch (senseBuffer->AdditionalSenseCodeQualifier) { + + case SCSI_SENSEQ_BECOMING_READY: + + DebugPrint((1, "ScsiClassInterpretSenseInfo:" + " In process of becoming ready\n")); + break; + + case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: + + DebugPrint((1, "ScsiClassInterpretSenseInfo:" + " Manual intervention required\n")); + *Status = STATUS_NO_MEDIA_IN_DEVICE; + retry = FALSE; + break; + + case SCSI_SENSEQ_FORMAT_IN_PROGRESS: + + DebugPrint((1, "ScsiClassInterpretSenseInfo: Format in progress\n")); + retry = FALSE; + break; + + case SCSI_SENSEQ_INIT_COMMAND_REQUIRED: + + default: + + DebugPrint((1, "ScsiClassInterpretSenseInfo:" + " Initializing command required\n")); + + // + // This sense code/additional sense code + // combination may indicate that the device + // needs to be started. Send an start unit if this + // is a disk device. + // + + if (deviceExtension->DeviceFlags & DEV_SAFE_START_UNIT) { + StartUnit(DeviceObject); + } + + break; + + } // end switch (senseBuffer->AdditionalSenseCodeQualifier) + + break; + + case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: + + DebugPrint((1, + "ScsiClassInterpretSenseInfo:" + " No Media in device.\n")); + *Status = STATUS_NO_MEDIA_IN_DEVICE; + retry = FALSE; + + // + // signal autorun that there isn't any media in the device + // + + if((deviceExtension->MediaChangeEvent != NULL)&& + (!deviceExtension->MediaChangeNoMedia)) { + KeSetEvent(deviceExtension->MediaChangeEvent, + (KPRIORITY) 0, + FALSE); + DebugPrint((0, "ScsiClassInterpretSenseInfo:" + "Detected No Media In Device " + "[irp = 0x%lx]\n", Srb->OriginalRequest)); + deviceExtension->MediaChangeNoMedia = TRUE; + } + + break; + } // end switch (senseBuffer->AdditionalSenseCode) + + break; + + case SCSI_SENSE_DATA_PROTECT: + + DebugPrint((1, "ScsiClassInterpretSenseInfo: Media write protected\n")); + *Status = STATUS_MEDIA_WRITE_PROTECTED; + retry = FALSE; + break; + + case SCSI_SENSE_MEDIUM_ERROR: + + DebugPrint((1,"ScsiClassInterpretSenseInfo: Bad media\n")); + *Status = STATUS_DEVICE_DATA_ERROR; + + retry = FALSE; + logError = TRUE; + uniqueId = 256; + logStatus = 0;//IO_ERR_BAD_BLOCK; + break; + + case SCSI_SENSE_HARDWARE_ERROR: + + DebugPrint((1,"ScsiClassInterpretSenseInfo: Hardware error\n")); + *Status = STATUS_IO_DEVICE_ERROR; + + logError = TRUE; + uniqueId = 257; + logStatus = 0;//IO_ERR_CONTROLLER_ERROR; + + break; + + case SCSI_SENSE_ILLEGAL_REQUEST: + + DebugPrint((1, "ScsiClassInterpretSenseInfo: Illegal SCSI request\n")); + *Status = STATUS_INVALID_DEVICE_REQUEST; + + switch (senseBuffer->AdditionalSenseCode) { + + case SCSI_ADSENSE_ILLEGAL_COMMAND: + DebugPrint((1, "ScsiClassInterpretSenseInfo: Illegal command\n")); + retry = FALSE; + break; + + case SCSI_ADSENSE_ILLEGAL_BLOCK: + DebugPrint((1, "ScsiClassInterpretSenseInfo: Illegal block address\n")); + *Status = STATUS_NONEXISTENT_SECTOR; + retry = FALSE; + break; + + case SCSI_ADSENSE_INVALID_LUN: + DebugPrint((1,"ScsiClassInterpretSenseInfo: Invalid LUN\n")); + *Status = STATUS_NO_SUCH_DEVICE; + retry = FALSE; + break; + + case SCSI_ADSENSE_MUSIC_AREA: + DebugPrint((1,"ScsiClassInterpretSenseInfo: Music area\n")); + retry = FALSE; + break; + + case SCSI_ADSENSE_DATA_AREA: + DebugPrint((1,"ScsiClassInterpretSenseInfo: Data area\n")); + retry = FALSE; + break; + + case SCSI_ADSENSE_VOLUME_OVERFLOW: + DebugPrint((1, "ScsiClassInterpretSenseInfo: Volume overflow\n")); + retry = FALSE; + break; + + case SCSI_ADSENSE_INVALID_CDB: + DebugPrint((1, "ScsiClassInterpretSenseInfo: Invalid CDB\n")); + + // + // Check if write cache enabled. + // + + if (deviceExtension->DeviceFlags & DEV_WRITE_CACHE) { + + // + // Assume FUA is not supported. + // + + deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE; + retry = TRUE; + + } else { + retry = FALSE; + } + + break; + + } // end switch (senseBuffer->AdditionalSenseCode) + + break; + + case SCSI_SENSE_UNIT_ATTENTION: + + switch (senseBuffer->AdditionalSenseCode) { + case SCSI_ADSENSE_MEDIUM_CHANGED: + DebugPrint((1, "ScsiClassInterpretSenseInfo: Media changed\n")); + + if(deviceExtension->MediaChangeEvent != NULL) { + + KeSetEvent(deviceExtension->MediaChangeEvent, + (KPRIORITY) 0, + FALSE); + DebugPrint((0, "ScsiClassInterpretSenseInfo:" + "New Media Found - Setting MediaChanged event" + " [irp = 0x%lx]\n", Srb->OriginalRequest)); + deviceExtension->MediaChangeNoMedia = FALSE; + + } + break; + + case SCSI_ADSENSE_BUS_RESET: + DebugPrint((1,"ScsiClassInterpretSenseInfo: Bus reset\n")); + break; + + default: + DebugPrint((1,"ScsiClassInterpretSenseInfo: Unit attention\n")); + break; + + } // end switch (senseBuffer->AdditionalSenseCode) + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA && + DeviceObject->Vpb->Flags & VPB_MOUNTED) { + + // + // Set bit to indicate that media may have changed + // and volume needs verification. + // + + DeviceObject->Flags |= DO_VERIFY_VOLUME; + + *Status = STATUS_VERIFY_REQUIRED; + retry = FALSE; + + } else { + + *Status = STATUS_IO_DEVICE_ERROR; + + } + + // + // A media change may have occured so increment the change + // count for the physical device + // + + physicalExtension->MediaChangeCount++; + + DebugPrint((2, "ScsiClassInterpretSenseInfo - Media change " + "count for device %d is %d\n", + physicalExtension->DeviceNumber, + physicalExtension->MediaChangeCount)); + + break; + + case SCSI_SENSE_ABORTED_COMMAND: + + DebugPrint((1,"ScsiClassInterpretSenseInfo: Command aborted\n")); + *Status = STATUS_IO_DEVICE_ERROR; + break; + + case SCSI_SENSE_RECOVERED_ERROR: + + DebugPrint((1,"ScsiClassInterpretSenseInfo: Recovered error\n")); + *Status = STATUS_SUCCESS; + retry = FALSE; + logError = TRUE; + uniqueId = 258; + + switch(senseBuffer->AdditionalSenseCode) { + case SCSI_ADSENSE_SEEK_ERROR: + case SCSI_ADSENSE_TRACK_ERROR: + logStatus = 0;//IO_ERR_SEEK_ERROR; + break; + + case SCSI_ADSENSE_REC_DATA_NOECC: + case SCSI_ADSENSE_REC_DATA_ECC: + logStatus = 0;//IO_RECOVERED_VIA_ECC; + break; + + default: + logStatus = 0;//IO_ERR_CONTROLLER_ERROR; + break; + + } // end switch(senseBuffer->AdditionalSenseCode) + + if (senseBuffer->IncorrectLength) { + + DebugPrint((1, "ScsiClassInterpretSenseInfo: Incorrect length detected.\n")); + *Status = STATUS_INVALID_BLOCK_LENGTH ; + } + + break; + + case SCSI_SENSE_NO_SENSE: + + // + // Check other indicators. + // + + if (senseBuffer->IncorrectLength) { + + DebugPrint((1, "ScsiClassInterpretSenseInfo: Incorrect length detected.\n")); + *Status = STATUS_INVALID_BLOCK_LENGTH ; + retry = FALSE; + + } else { + + DebugPrint((1, "ScsiClassInterpretSenseInfo: No specific sense key\n")); + *Status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + break; + + default: + + DebugPrint((1, "ScsiClassInterpretSenseInfo: Unrecognized sense code\n")); + *Status = STATUS_IO_DEVICE_ERROR; + break; + + } // end switch (senseBuffer->SenseKey & 0xf) + + // + // Try to determine the bad sector from the inquiry data. + // + + if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_READ || + ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY || + ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_WRITE)) { + + for (index = 0; index < 4; index++) { + badSector = (badSector << 8) | senseBuffer->Information[index]; + } + + readSector = 0; + for (index = 0; index < 4; index++) { + readSector = (readSector << 8) | Srb->Cdb[index+2]; + } + + index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) | + ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb; + + // + // Make sure the bad sector is within the read sectors. + // + + if (!(badSector >= readSector && badSector < readSector + index)) { + badSector = readSector; + } + } + + } else { + + // + // Request sense buffer not valid. No sense information + // to pinpoint the error. Return general request fail. + // + + DebugPrint((1,"ScsiClassInterpretSenseInfo: Request sense info not valid. SrbStatus %2x\n", + SRB_STATUS(Srb->SrbStatus))); + retry = TRUE; + + switch (SRB_STATUS(Srb->SrbStatus)) { + case SRB_STATUS_INVALID_LUN: + case SRB_STATUS_INVALID_TARGET_ID: + case SRB_STATUS_NO_DEVICE: + case SRB_STATUS_NO_HBA: + case SRB_STATUS_INVALID_PATH_ID: + *Status = STATUS_NO_SUCH_DEVICE; + retry = FALSE; + break; + + case SRB_STATUS_COMMAND_TIMEOUT: + case SRB_STATUS_ABORTED: + case SRB_STATUS_TIMEOUT: + + // + // Update the error count for the device. + // + + deviceExtension->ErrorCount++; + *Status = STATUS_IO_TIMEOUT; + break; + + case SRB_STATUS_SELECTION_TIMEOUT: + logError = TRUE; + logStatus = 0;//IO_ERR_NOT_READY; + uniqueId = 260; + *Status = STATUS_DEVICE_NOT_CONNECTED; + retry = FALSE; + break; + + case SRB_STATUS_DATA_OVERRUN: + *Status = STATUS_DATA_OVERRUN; + retry = FALSE; + break; + + case SRB_STATUS_PHASE_SEQUENCE_FAILURE: + + // + // Update the error count for the device. + // + + deviceExtension->ErrorCount++; + *Status = STATUS_IO_DEVICE_ERROR; + + // + // If there was phase sequence error then limit the number of + // retries. + // + + if (RetryCount > 1 ) { + retry = FALSE; + } + + break; + + case SRB_STATUS_REQUEST_FLUSHED: + + // + // If the status needs verification bit is set. Then set + // the status to need verification and no retry; otherwise, + // just retry the request. + // + + if (DeviceObject->Flags & DO_VERIFY_VOLUME ) { + + *Status = STATUS_VERIFY_REQUIRED; + retry = FALSE; + } else { + *Status = STATUS_IO_DEVICE_ERROR; + } + + break; + + case SRB_STATUS_INVALID_REQUEST: + + // + // An invalid request was attempted. + // + + *Status = STATUS_INVALID_DEVICE_REQUEST; + retry = FALSE; + break; + + case SRB_STATUS_UNEXPECTED_BUS_FREE: + case SRB_STATUS_PARITY_ERROR: + + // + // Update the error count for the device. + // + + deviceExtension->ErrorCount++; + + // + // Fall through to below. + // + + case SRB_STATUS_BUS_RESET: + *Status = STATUS_IO_DEVICE_ERROR; + break; + + case SRB_STATUS_ERROR: + + *Status = STATUS_IO_DEVICE_ERROR; + if (Srb->ScsiStatus == 0) { + + // + // This is some strange return code. Update the error + // count for the device. + // + + deviceExtension->ErrorCount++; + + } if (Srb->ScsiStatus == SCSISTAT_BUSY) { + + *Status = STATUS_DEVICE_NOT_READY; + + } if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) { + + *Status = STATUS_DEVICE_BUSY; + retry = FALSE; + + } + + break; + + default: + logError = TRUE; + logStatus = 0;//IO_ERR_CONTROLLER_ERROR; + uniqueId = 259; + *Status = STATUS_IO_DEVICE_ERROR; + break; + + } + + // + // If the error count has exceeded the error limit, then disable + // any tagged queuing, multiple requests per lu queueing + // and sychronous data transfers. + // + + if (deviceExtension->ErrorCount == 4) { + + // + // Clearing the no queue freeze flag prevents the port driver + // from sending multiple requests per logical unit. + // + + deviceExtension->SrbFlags &= ~(SRB_FLAGS_QUEUE_ACTION_ENABLE | + SRB_FLAGS_NO_QUEUE_FREEZE); + + deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + DebugPrint((1, "ScsiClassInterpretSenseInfo: Too many errors disabling tagged queuing and synchronous data tranfers.\n")); + + } else if (deviceExtension->ErrorCount == 8) { + + // + // If a second threshold is reached, disable disconnects. + // + + deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT; + DebugPrint((1, "ScsiClassInterpretSenseInfo: Too many errors disabling disconnects.\n")); + } + } + + // + // If there is a class specific error handler call it. + // + + if (deviceExtension->ClassError != NULL) { + + deviceExtension->ClassError(DeviceObject, + Srb, + Status, + &retry); + } + + // + // Log an error if necessary. + // + + if (logError) { + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + sizeof(IO_ERROR_LOG_PACKET) + 5 * sizeof(ULONG)); + + if (errorLogEntry == NULL) { + + // + // Return if no packet could be allocated. + // + + return retry; + + } + + if (retry && RetryCount < MAXIMUM_RETRIES) { + errorLogEntry->FinalStatus = STATUS_SUCCESS; + } else { + errorLogEntry->FinalStatus = *Status; + } + + // + // Calculate the device offset if there is a geometry. + // + + if (deviceExtension->DiskGeometry != NULL) { + + errorLogEntry->DeviceOffset.QuadPart = (LONGLONG) badSector; + errorLogEntry->DeviceOffset = RtlExtendedIntegerMultiply( + errorLogEntry->DeviceOffset, + deviceExtension->DiskGeometry->BytesPerSector); + } + + errorLogEntry->ErrorCode = logStatus; + errorLogEntry->SequenceNumber = 0; + errorLogEntry->MajorFunctionCode = MajorFunctionCode; + errorLogEntry->IoControlCode = IoDeviceCode; + errorLogEntry->RetryCount = (UCHAR) RetryCount; + errorLogEntry->UniqueErrorValue = uniqueId; + errorLogEntry->DumpDataSize = 6 * sizeof(ULONG); + errorLogEntry->DumpData[0] = Srb->PathId; + errorLogEntry->DumpData[1] = Srb->TargetId; + errorLogEntry->DumpData[2] = Srb->Lun; + errorLogEntry->DumpData[3] = 0; + errorLogEntry->DumpData[4] = Srb->SrbStatus << 8 | Srb->ScsiStatus; + + if (senseBuffer != NULL) { + errorLogEntry->DumpData[5] = senseBuffer->SenseKey << 16 | + senseBuffer->AdditionalSenseCode << 8 | + senseBuffer->AdditionalSenseCodeQualifier; + + } + + // + // Write the error log packet. + // + + IoWriteErrorLogEntry(errorLogEntry); + } + + return retry; + +} // end ScsiClassInterpretSenseInfo() + + +VOID +STDCALL +RetryRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PSCSI_REQUEST_BLOCK Srb, + BOOLEAN Associated + ) + +/*++ + +Routine Description: + + This routine reinitalizes the necessary fields, and sends the request + to the port driver. + +Arguments: + + DeviceObject - Supplies the device object associated with this request. + + Irp - Supplies the request to be retried. + + Srb - Supplies a Pointer to the SCSI request block to be retied. + + Assocaiated - Indicates this is an assocatied Irp created by split request. + +Return Value: + + None + +--*/ -static NTSTATUS STDCALL -ScsiClassCreateClose(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) { - PDEVICE_EXTENSION DeviceExtension; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + ULONG transferByteCount; - DPRINT("ScsiClassCreateClose() called\n"); + // + // Determine the transfer count of the request. If this is a read or a + // write then the transfer count is in the Irp stack. Otherwise assume + // the MDL contains the correct length. If there is no MDL then the + // transfer length must be zero. + // - DeviceExtension = DeviceObject->DeviceExtension; + if (currentIrpStack->MajorFunction == IRP_MJ_READ || + currentIrpStack->MajorFunction == IRP_MJ_WRITE) { - if (DeviceExtension->ClassCreateClose) - return(DeviceExtension->ClassCreateClose(DeviceObject, - Irp)); + transferByteCount = currentIrpStack->Parameters.Read.Length; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); + } else if (Irp->MdlAddress != NULL) { - return(STATUS_SUCCESS); -} + // + // Note this assumes that only read and write requests are spilt and + // other request do not need to be. If the data buffer address in + // the MDL and the SRB don't match then transfer length is most + // likely incorrect. + // + + ASSERT(Srb->DataBuffer == MmGetMdlVirtualAddress(Irp->MdlAddress)); + transferByteCount = Irp->MdlAddress->ByteCount; + + } else { + + transferByteCount = 0; + } + + // + // Reset byte count of transfer in SRB Extension. + // + + Srb->DataTransferLength = transferByteCount; + + // + // Zero SRB statuses. + // + + Srb->SrbStatus = Srb->ScsiStatus = 0; + + // + // Set the no disconnect flag, disable synchronous data transfers and + // disable tagged queuing. This fixes some errors. + // + + Srb->SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT | + SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + Srb->SrbFlags &= ~SRB_FLAGS_QUEUE_ACTION_ENABLE; + Srb->QueueTag = SP_UNTAGGED; + + // + // Set up major SCSI function. + // + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + nextIrpStack->Parameters.Scsi.Srb = Srb; + + // + // Set up IoCompletion routine address. + // + + if (Associated) { + + IoSetCompletionRoutine(Irp, ScsiClassIoCompleteAssociated, Srb, TRUE, TRUE, TRUE); + + } else { + + IoSetCompletionRoutine(Irp, ScsiClassIoComplete, Srb, TRUE, TRUE, TRUE); + } + + // + // Pass the request to the port driver. + // + + (PVOID)IoCallDriver(deviceExtension->PortDeviceObject, Irp); + +} // end RetryRequest() + +VOID +STDCALL +ScsiClassBuildRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This routine allocates and builds an Srb for a read or write request. + The block address and length are supplied by the Irp. The retry count + is stored in the current stack for use by ScsiClassIoComplete which + processes these requests when they complete. The Irp is ready to be + passed to the port driver when this routine returns. + +Arguments: + + DeviceObject - Supplies the device object associated with this request. + + Irp - Supplies the request to be retried. + +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. -static NTSTATUS STDCALL -ScsiClassReadWrite(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) +Return Value: + + None. + +--*/ + { - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION IrpStack; - ULONG MaximumTransferLength; - ULONG CurrentTransferLength; - ULONG MaximumTransferPages; - ULONG CurrentTransferPages; - NTSTATUS Status; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + ULONG logicalBlockAddress; + USHORT transferBlocks; - DPRINT("ScsiClassReadWrite() called\n"); + // + // Calculate relative sector address. + // - DeviceExtension = DeviceObject->DeviceExtension; - IrpStack = IoGetCurrentIrpStackLocation(Irp); + logicalBlockAddress = (ULONG)(Int64ShrlMod32(startingOffset.QuadPart, deviceExtension->SectorShift)); - DPRINT("Relative Offset: %I64u Length: %lu\n", - IrpStack->Parameters.Read.ByteOffset.QuadPart, - IrpStack->Parameters.Read.Length); + // + // Allocate an Srb. + // - MaximumTransferLength = DeviceExtension->PortCapabilities->MaximumTransferLength; - MaximumTransferPages = DeviceExtension->PortCapabilities->MaximumPhysicalPages; + srb = ExAllocateFromNPagedLookasideList(&deviceExtension->SrbLookasideListHead); - CurrentTransferLength = IrpStack->Parameters.Read.Length; + srb->SrbFlags = 0; - if ((DeviceObject->Flags & DO_VERIFY_VOLUME) && - !(IrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) - { - IoSetHardErrorOrVerifyDevice(Irp, - DeviceObject); + // + // Write length to SRB. + // - Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED; - Irp->IoStatus.Information = 0; + srb->Length = SCSI_REQUEST_BLOCK_SIZE; - IoCompleteRequest(Irp, - IO_NO_INCREMENT); - return(STATUS_VERIFY_REQUIRED); + // + // Set up IRP Address. + // + + srb->OriginalRequest = Irp; + + // + // Set up target ID and logical unit number. + // + + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress); + + // + // Save byte count of transfer in SRB Extension. + // + + srb->DataTransferLength = currentIrpStack->Parameters.Read.Length; + + // + // Initialize the queue actions field. + // + + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + + // + // Queue sort key is Relative Block Address. + // + + srb->QueueSortKey = logicalBlockAddress; + + // + // Indicate auto request sense by specifying buffer and size. + // + + srb->SenseInfoBuffer = deviceExtension->SenseData; + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Set timeout value of one unit per 64k bytes of data. + // + + srb->TimeOutValue = ((srb->DataTransferLength + 0xFFFF) >> 16) * + deviceExtension->TimeOutValue; + + // + // Zero statuses. + // + + srb->SrbStatus = srb->ScsiStatus = 0; + srb->NextSrb = 0; + + // + // Indicate that 10-byte CDB's will be used. + // + + srb->CdbLength = 10; + + // + // Fill in CDB fields. + // + + cdb = (PCDB)srb->Cdb; + + // + // Zero 12 bytes for Atapi Packets + // + + RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE); + + cdb->CDB10.LogicalUnitNumber = deviceExtension->Lun; + transferBlocks = (USHORT)(currentIrpStack->Parameters.Read.Length >> deviceExtension->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.TransferBlocksMsb = ((PFOUR_BYTE)&transferBlocks)->Byte1; + cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&transferBlocks)->Byte0; + + // + // Set transfer direction flag and Cdb command. + // + + if (currentIrpStack->MajorFunction == IRP_MJ_READ) { + + DebugPrint((3, "ScsiClassBuildRequest: Read Command\n")); + + srb->SrbFlags |= SRB_FLAGS_DATA_IN; + cdb->CDB10.OperationCode = SCSIOP_READ; + + } else { + + DebugPrint((3, "ScsiClassBuildRequest: Write Command\n")); + + srb->SrbFlags |= SRB_FLAGS_DATA_OUT; + cdb->CDB10.OperationCode = SCSIOP_WRITE; } - /* Class driver verifies the IRP */ - Status = DeviceExtension->ClassReadWriteVerification(DeviceObject, - Irp); - if (!NT_SUCCESS(Status)) - { - IoCompleteRequest(Irp, - IO_NO_INCREMENT); - return(Status); - } - else if (Status == STATUS_PENDING) - { - IoMarkIrpPending(Irp); - return(STATUS_PENDING); + // + // If this is not a write-through request, then allow caching. + // + + if (!(currentIrpStack->Flags & SL_WRITE_THROUGH)) { + + srb->SrbFlags |= SRB_FLAGS_ADAPTER_CACHE_ENABLE; + + } else { + + // + // If write caching is enable then force media access in the + // cdb. + // + + if (deviceExtension->DeviceFlags & DEV_WRITE_CACHE) { + cdb->CDB10.ForceUnitAccess = TRUE; + } } - /* Finish a zero-byte transfer */ - if (CurrentTransferLength == 0) - { - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, - IO_NO_INCREMENT); - return(STATUS_SUCCESS); - } + // + // Or in the default flags from the device object. + // - if (DeviceExtension->ClassStartIo != NULL) - { - DPRINT("ScsiClassReadWrite() starting packet\n"); + srb->SrbFlags |= deviceExtension->SrbFlags; - IoMarkIrpPending(Irp); - IoStartPacket(DeviceObject, - Irp, - NULL, - NULL); + // + // Set up major SCSI function. + // - return(STATUS_PENDING); - } + nextIrpStack->MajorFunction = IRP_MJ_SCSI; - /* Adjust partition-relative starting offset to absolute offset */ - IrpStack->Parameters.Read.ByteOffset.QuadPart += - (DeviceExtension->StartingOffset.QuadPart + DeviceExtension->DMByteSkew); + // + // Save SRB address in next stack for port driver. + // - /* Calculate number of pages in this transfer */ - CurrentTransferPages = - ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress), - IrpStack->Parameters.Read.Length); + nextIrpStack->Parameters.Scsi.Srb = srb; - if (CurrentTransferLength > MaximumTransferLength || - CurrentTransferPages > MaximumTransferPages) - { - DPRINT("Split current request: MaximumTransferLength %lu CurrentTransferLength %lu\n", - MaximumTransferLength, CurrentTransferLength); + // + // Save retry count in current IRP stack. + // - /* Adjust the maximum transfer length */ - CurrentTransferPages = DeviceExtension->PortCapabilities->MaximumPhysicalPages; + currentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; - if (MaximumTransferLength > CurrentTransferPages * PAGE_SIZE) - MaximumTransferLength = CurrentTransferPages * PAGE_SIZE; + // + // Set up IoCompletion routine address. + // - if (MaximumTransferLength == 0) - MaximumTransferLength = PAGE_SIZE; + IoSetCompletionRoutine(Irp, ScsiClassIoComplete, srb, TRUE, TRUE, TRUE); - IoMarkIrpPending(Irp); + return; - /* Split current request */ - ScsiClassSplitRequest(DeviceObject, - Irp, - MaximumTransferLength); +} // end ScsiClassBuildRequest() + +ULONG +STDCALL +ScsiClassModeSense( + IN PDEVICE_OBJECT DeviceObject, + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode + ) - return(STATUS_PENDING); - } +/*++ - ScsiClassBuildRequest(DeviceObject, - Irp); +Routine Description: - DPRINT("ScsiClassReadWrite() done\n"); + This routine sends a mode sense command to a target ID and returns + when it is complete. - /* Call the port driver */ - return(IoCallDriver(DeviceExtension->PortDeviceObject, - Irp)); -} +Arguments: + DeviceObject - Supplies the device object associated with this request. -static NTSTATUS STDCALL -ScsiClassDeviceDispatch(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) + ModeSenseBuffer - Supplies a buffer to store the sense data. + + Length - Supplies the length in bytes of the mode sense buffer. + + PageMode - Supplies the page or pages of mode sense data to be retrived. + +Return Value: + + Length of the transferred data is returned. + +--*/ { - PDEVICE_EXTENSION DeviceExtension; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PCDB cdb; + SCSI_REQUEST_BLOCK srb; + ULONG retries = 1; + NTSTATUS status; - DPRINT("ScsiClassDeviceDispatch() called\n"); + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); - DeviceExtension = DeviceObject->DeviceExtension; - if (DeviceExtension->ClassDeviceControl) - { - return(DeviceExtension->ClassDeviceControl(DeviceObject, Irp)); + // + // Build the MODE SENSE CDB. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = deviceExtension->TimeOutValue; + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = PageMode; + cdb->MODE_SENSE.AllocationLength = (UCHAR)Length; + +Retry: + + status = ScsiClassSendSrbSynchronous(DeviceObject, + &srb, + ModeSenseBuffer, + Length, + FALSE); + + + if (status == STATUS_VERIFY_REQUIRED) { + + // + // Routine ScsiClassSendSrbSynchronous does not retry requests returned with + // this status. MODE SENSE commands should be retried anyway. + // + + if (retries--) { + + // + // Retry request. + // + + goto Retry; + } + + } else if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; } - Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; - IoCompleteRequest(Irp, IO_NO_INCREMENT); + if (NT_SUCCESS(status)) { + return(srb.DataTransferLength); + } else { + return(0); + } - return(STATUS_INVALID_DEVICE_REQUEST); -} +} // end ScsiClassModeSense() + +PVOID +STDCALL +ScsiClassFindModePage( + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode, + IN BOOLEAN Use6Byte + ) -static NTSTATUS STDCALL -ScsiClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) +/*++ + +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. + + Use6Byte - Indicates whether 6 or 10 byte mode sense was used. + +Return Value: + + A pointer to the the requested mode page. If the mode page was not found + then NULL is return. + +--*/ { - PDEVICE_EXTENSION DeviceExtension; + PUCHAR limit; + ULONG parameterHeaderLength; - DPRINT("ScsiClassShutdownFlush() called\n"); + limit = ModeSenseBuffer + Length; + parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10); - DeviceExtension = DeviceObject->DeviceExtension; - if (DeviceExtension->ClassShutdownFlush) - { - return(DeviceExtension->ClassShutdownFlush(DeviceObject, Irp)); + + // + // Skip the mode select header and block descriptors. + // + + if (Length < parameterHeaderLength) { + return(NULL); } - Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return(STATUS_INVALID_DEVICE_REQUEST); + + ModeSenseBuffer += parameterHeaderLength + ((Use6Byte) ? ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength : + ((PMODE_PARAMETER_HEADER10) ModeSenseBuffer)->BlockDescriptorLength[1]); + + // + // 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); + } + + // + // Advance to the next page. + // + + ModeSenseBuffer += ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength + 2; + } + + return(NULL); } + +NTSTATUS +STDCALL +ScsiClassSendSrbAsynchronous( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + PIRP Irp, + PVOID BufferAddress, + ULONG BufferLength, + BOOLEAN WriteToDevice + ) +/*++ +Routine Description: -static VOID -ScsiClassRetryRequest(PDEVICE_OBJECT DeviceObject, - PIRP Irp, - PSCSI_REQUEST_BLOCK Srb, - BOOLEAN Associated) + This routine takes a partially built Srb and an Irp and sends it down to + the port driver. + +Arguments: + DeviceObject - Supplies the device object for the orginal request. + + Srb - Supplies a paritally build ScsiRequestBlock. In particular, the + CDB and the SRB timeout value must be filled in. The SRB must not be + allocated from zone. + + Irp - Supplies the requesting Irp. + + BufferAddress - Supplies a pointer to the buffer to be transfered. + + BufferLength - Supplies the length of data transfer. + + WriteToDevice - Indicates the data transfer will be from system memory to + device. + +Return Value: + + Returns STATUS_INSUFFICIENT_RESOURCES or the status of IoCallDriver. + +--*/ { - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION CurrentIrpStack; - PIO_STACK_LOCATION NextIrpStack; - DPRINT ("ScsiPortRetryRequest() called\n"); + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpStack; - DeviceExtension = DeviceObject->DeviceExtension; - CurrentIrpStack = IoGetCurrentIrpStackLocation(Irp); - NextIrpStack = IoGetNextIrpStackLocation(Irp); + PAGED_CODE(); - if (CurrentIrpStack->MajorFunction == IRP_MJ_READ) - { - Srb->DataTransferLength = CurrentIrpStack->Parameters.Read.Length; - } - else if (CurrentIrpStack->MajorFunction == IRP_MJ_WRITE) - { - Srb->DataTransferLength = CurrentIrpStack->Parameters.Write.Length; - } - else if (Irp->MdlAddress != NULL) - { - Srb->DataTransferLength = Irp->MdlAddress->ByteCount; - } - else - { - Srb->DataTransferLength = 0; + // + // Write length to SRB. + // + + Srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set SCSI bus address. + // + + Srb->PathId = deviceExtension->PathId; + Srb->TargetId = deviceExtension->TargetId; + Srb->Lun = deviceExtension->Lun; + + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // This is a violation of the SCSI spec but it is required for + // some targets. + // + + Srb->Cdb[1] |= deviceExtension->Lun << 5; + + // + // Indicate auto request sense by specifying buffer and size. + // + + Srb->SenseInfoBuffer = deviceExtension->SenseData; + Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + Srb->DataBuffer = BufferAddress; + + if (BufferAddress != NULL) { + + // + // Build Mdl if necessary. + // + + if (Irp->MdlAddress == NULL) { + + if (IoAllocateMdl(BufferAddress, + BufferLength, + FALSE, + FALSE, + Irp) == NULL) { + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + MmBuildMdlForNonPagedPool(Irp->MdlAddress); + + } else { + + // + // Make sure the buffer requested matches the MDL. + // + + ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress)); + } + + // + // Set read flag. + // + + Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN; + + } else { + + // + // Clear flags. + // + + Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER; } - Srb->SrbStatus = 0; - Srb->ScsiStatus = 0; + // + // Disable synchronous transfer for these requests. + // - /* Don't modify the flags */ -// Srb->Flags = -// Srb->QueueTag = SP_UNTAGGED; + Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; - NextIrpStack->MajorFunction = IRP_MJ_SCSI; - NextIrpStack->Parameters.Scsi.Srb = Srb; + // + // Set the transfer length. + // - if (Associated == FALSE) - { - IoSetCompletionRoutine(Irp, - ScsiClassIoComplete, - Srb, - TRUE, - TRUE, - TRUE); - } - else - { - IoSetCompletionRoutine(Irp, - ScsiClassIoCompleteAssociated, - Srb, - TRUE, - TRUE, - TRUE); - } + Srb->DataTransferLength = BufferLength; - IoCallDriver(DeviceExtension->PortDeviceObject, - Irp); + // + // Zero out status. + // + + Srb->ScsiStatus = Srb->SrbStatus = 0; + + Srb->NextSrb = 0; + + // + // Save a few parameters in the current stack location. + // + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + // + // Save retry count in current Irp stack. + // + + irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, ScsiClassIoComplete, Srb, TRUE, TRUE, TRUE); + + // + // Get next stack location and + // set major function code. + // + + irpStack = IoGetNextIrpStackLocation(Irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = Srb; + + // + // Set up Irp Address. + // + + Srb->OriginalRequest = Irp; + + // + // Call the port driver to process the request. + // + + return(IoCallDriver(deviceExtension->PortDeviceObject, Irp)); - DPRINT("ScsiPortRetryRequest() done\n"); } + +NTSTATUS +STDCALL +ScsiClassDeviceControlDispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + The routine is the common class driver device control dispatch entry point. + This routine is invokes the device-specific drivers DeviceControl routine, + (which may call the Class driver's common DeviceControl routine). + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this request. + + Irp - Supplies the Irp making the request. + +Return Value: + + Returns the status returned from the device-specific driver. + +--*/ -static NTSTATUS STDCALL -ScsiClassCheckVerifyCompletion (IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context) { - PDEVICE_EXTENSION DeviceExtension; - PDEVICE_EXTENSION PhysicalExtension; - PIO_STACK_LOCATION Stack; - PIRP OrigIrp; - DPRINT ("ScsiClassCheckVerifyCompletion() called\n"); + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; - /* Get the physical device extension */ - DeviceExtension = DeviceObject->DeviceExtension; - PhysicalExtension = DeviceExtension->PhysicalDevice->DeviceExtension; - /* Get the original IRP */ - Stack = IoGetCurrentIrpStackLocation (Irp); - OrigIrp = (PIRP)Stack->Parameters.Others.Argument1; + // + // Call the class specific driver DeviceControl routine. + // If it doesn't handle it, it will call back into ScsiClassDeviceControl. + // - /* Copy the media change count */ - *((PULONG)(OrigIrp->AssociatedIrp.SystemBuffer)) = - PhysicalExtension->MediaChangeCount; + ASSERT(deviceExtension->ClassDeviceControl); - /* Complete the original IRP */ - OrigIrp->IoStatus.Status = Irp->IoStatus.Status; - OrigIrp->IoStatus.Information = sizeof(ULONG); - - IoCompleteRequest (OrigIrp, - IO_DISK_INCREMENT); - - /* Release the current IRP */ - IoFreeIrp (Irp); - - return STATUS_MORE_PROCESSING_REQUIRED; + return deviceExtension->ClassDeviceControl(DeviceObject,Irp); } -/* EOF */ + +NTSTATUS +STDCALL +ScsiClassDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + The routine is the common class driver device control dispatch function. + This routine is called by a class driver when it get an unrecognized + device control request. This routine will perform the correct action for + common requests such as lock media. If the device request is unknown it + passed down to the next level. + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this request. + + Irp - Supplies the Irp making the request. + +Return Value: + + Returns back a STATUS_PENDING or a completion status. + +--*/ + +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextStack; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + NTSTATUS status; + ULONG modifiedIoControlCode; + + // + // If this is a pass through I/O control, set the minor function code + // and device address and pass it to the port driver. + // + + if (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH + || irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) { + + PSCSI_PASS_THROUGH scsiPass; + + nextStack = IoGetNextIrpStackLocation(Irp); + + // + // Validiate the user buffer. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH)){ + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + status = STATUS_INVALID_PARAMETER; + goto SetStatusAndReturn; + } + + // + // Force the SCSI address to the correct value. + // + + scsiPass = Irp->AssociatedIrp.SystemBuffer; + scsiPass->PathId = deviceExtension->PathId; + scsiPass->TargetId = deviceExtension->TargetId; + scsiPass->Lun = deviceExtension->Lun; + + // + // NOTICE: The SCSI-II specificaiton indicates that this field + // should be zero; however, some target controllers ignore the logical + // unit number in the INDENTIFY message and only look at the logical + // unit number field in the CDB. + // + + scsiPass->Cdb[1] |= deviceExtension->Lun << 5; + + nextStack->Parameters = irpStack->Parameters; + nextStack->MajorFunction = irpStack->MajorFunction; + nextStack->MinorFunction = IRP_MN_SCSI_CLASS; + + status = IoCallDriver(deviceExtension->PortDeviceObject, Irp); + goto SetStatusAndReturn; + } + + if (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_GET_ADDRESS) { + + PSCSI_ADDRESS scsiAddress = Irp->AssociatedIrp.SystemBuffer; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(SCSI_ADDRESS)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + status = STATUS_BUFFER_TOO_SMALL; + goto SetStatusAndReturn; + + } + + scsiAddress->Length = sizeof(SCSI_ADDRESS); + scsiAddress->PortNumber = deviceExtension->PortNumber; + scsiAddress->PathId = deviceExtension->PathId; + scsiAddress->TargetId = deviceExtension->TargetId; + scsiAddress->Lun = deviceExtension->Lun; + Irp->IoStatus.Information = sizeof(SCSI_ADDRESS); + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + status = STATUS_SUCCESS; + goto SetStatusAndReturn; + } + + srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE); + + if (srb == NULL) { + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + status = STATUS_INSUFFICIENT_RESOURCES; + goto SetStatusAndReturn; + } + + // + // Write zeros to Srb. + // + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + cdb = (PCDB)srb->Cdb; + + // + // Change the device type to disk for the switch statement. + // + + modifiedIoControlCode = (irpStack->Parameters.DeviceIoControl.IoControlCode + & ~0xffff0000) | (IOCTL_DISK_BASE << 16); + + switch (modifiedIoControlCode) { + + case IOCTL_DISK_CHECK_VERIFY: { + + PIRP irp2 = NULL; + PIO_STACK_LOCATION newStack; + + DebugPrint((1,"ScsiDeviceIoControl: Check verify\n")); + + // + // If a buffer for a media change count was provided, make sure it's + // big enough to hold the result + // + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) { + + // + // If the buffer is too small to hold the media change count + // then return an error to the caller + // + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(ULONG)) { + + DebugPrint((3,"ScsiDeviceIoControl: media count " + "buffer too small\n")); + + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + ExFreePool(srb); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + status = STATUS_BUFFER_TOO_SMALL; + goto SetStatusAndReturn; + + } + + // + // The caller has provided a valid buffer. Allocate an additional + // irp and stick the CheckVerify completion routine on it. We will + // then send this down to the port driver instead of the irp the + // caller sent in + // + + DebugPrint((2,"ScsiDeviceIoControl: Check verify wants " + "media count\n")); + + // + // Allocate a new irp to send the TestUnitReady to the port driver + // + + irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE); + + if(irp2 == NULL) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + ExFreePool(srb); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + status = STATUS_INSUFFICIENT_RESOURCES; + goto SetStatusAndReturn; + + break; + } + + irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread; + IoSetNextIrpStackLocation(irp2); + + // + // Set the top stack location and shove the master Irp into the + // top location + // + + newStack = IoGetCurrentIrpStackLocation(irp2); + newStack->Parameters.Others.Argument1 = Irp; + newStack->DeviceObject = DeviceObject; + + // + // Stick the check verify completion routine onto the stack + // and prepare the irp for the port driver + // + + IoSetCompletionRoutine(irp2, + ScsiClassCheckVerifyComplete, + NULL, + TRUE, + TRUE, + TRUE); + + IoSetNextIrpStackLocation(irp2); + newStack = IoGetCurrentIrpStackLocation(irp2); + newStack->DeviceObject = DeviceObject; + + // + // Mark the master irp as pending - whether the lower level + // driver completes it immediately or not this should allow it + // to go all the way back up. + // + + IoMarkIrpPending(Irp); + + Irp = irp2; + + } + + // + // Test Unit Ready + // + + srb->CdbLength = 6; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + + // + // Set timeout value. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue; + + // + // Since this routine will always hand the request to the + // port driver if there isn't a data transfer to be done + // we don't have to worry about completing the request here + // on an error + // + + status = ScsiClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + break; + } + + case IOCTL_DISK_MEDIA_REMOVAL: { + + PPREVENT_MEDIA_REMOVAL MediaRemoval = Irp->AssociatedIrp.SystemBuffer; + + // + // Prevent/Allow media removal. + // + + DebugPrint((3,"DiskIoControl: Prevent/Allow media removal\n")); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(PREVENT_MEDIA_REMOVAL)) { + + // + // Indicate unsuccessful status and no data transferred. + // + + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + ExFreePool(srb); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + status = STATUS_BUFFER_TOO_SMALL; + goto SetStatusAndReturn; + } + + // + // Get physical device extension. This is where the + // lock count is stored. + // + + deviceExtension = deviceExtension->PhysicalDevice->DeviceExtension; + + // + // If command succeeded then increment or decrement lock counter. + // + + if (MediaRemoval->PreventMediaRemoval) { + + // + // This is a lock command. Reissue the command in case bus or device + // was reset and lock cleared. + // + + InterlockedIncrement(&deviceExtension->LockCount); + + DebugPrint((1, + "ScsiClassDeviceControl: Lock media, lock count %x on disk %x\n", + deviceExtension->LockCount, + deviceExtension->DeviceNumber)); + + } else { + + // + // This is an unlock command. + // + + if (!deviceExtension->LockCount || + (InterlockedDecrement(&deviceExtension->LockCount) != 0)) { + + DebugPrint((1, + "ScsiClassDeviceControl: Unlock media, lock count %x on disk %x\n", + deviceExtension->LockCount, + deviceExtension->DeviceNumber)); + + // + // Don't unlock because someone still wants it locked. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + ExFreePool(srb); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + status = STATUS_SUCCESS; + goto SetStatusAndReturn; + } + + DebugPrint((1, + "ScsiClassDeviceControl: Unlock media, lock count %x on disk %x\n", + deviceExtension->LockCount, + deviceExtension->DeviceNumber)); + } + + srb->CdbLength = 6; + + cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; + + // + // TRUE - prevent media removal. + // FALSE - allow media removal. + // + + cdb->MEDIA_REMOVAL.Prevent = MediaRemoval->PreventMediaRemoval; + + // + // Set timeout value. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue; + status = ScsiClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + // + // Some devices will not support lock/unlock. + // Pretend that it worked. + // + + break; + } + + case IOCTL_DISK_RESERVE: { + + // + // Reserve logical unit. + // + + srb->CdbLength = 6; + + cdb->CDB6GENERIC.OperationCode = SCSIOP_RESERVE_UNIT; + + // + // Set timeout value. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue; + + status = ScsiClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + break; + } + + case IOCTL_DISK_RELEASE: { + + // + // Release logical unit. + // + + srb->CdbLength = 6; + + cdb->CDB6GENERIC.OperationCode = SCSIOP_RELEASE_UNIT; + + // + // Set timeout value. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue; + + status = ScsiClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + break; + } + + case IOCTL_DISK_EJECT_MEDIA: { + + // + // Eject media. + // + + srb->CdbLength = 6; + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.LoadEject = 1; + cdb->START_STOP.Start = 0; + + // + // Set timeout value. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue; + status = ScsiClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + break; + } + + case IOCTL_DISK_LOAD_MEDIA: { + + // + // Load media. + // + + DebugPrint((3,"CdRomDeviceControl: Load media\n")); + + srb->CdbLength = 6; + + cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.LoadEject = 1; + cdb->START_STOP.Start = 1; + + // + // Set timeout value. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue; + status = ScsiClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + break; + } + + case IOCTL_DISK_FIND_NEW_DEVICES: { + + // + // Search for devices that have been powered on since the last + // device search or system initialization. + // + + DebugPrint((3,"CdRomDeviceControl: Find devices\n")); + status = DriverEntry(DeviceObject->DriverObject, + NULL); + + Irp->IoStatus.Status = status; + ExFreePool(srb); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + break; + } + + default: { + + DebugPrint((3,"ScsiIoDeviceControl: Unsupported device IOCTL\n")); + + // + // Pass the device control to the next driver. + // + + ExFreePool(srb); + + // + // Copy the Irp stack parameters to the next stack location. + // + + nextStack = IoGetNextIrpStackLocation(Irp); + nextStack->Parameters = irpStack->Parameters; + nextStack->MajorFunction = irpStack->MajorFunction; + nextStack->MinorFunction = irpStack->MinorFunction; + + status = IoCallDriver(deviceExtension->PortDeviceObject, Irp); + break; + } + + } // end switch( ... + +SetStatusAndReturn: + + return status; +} + + +NTSTATUS +STDCALL +ScsiClassShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called for a shutdown and flush IRPs. These are sent by the + system before it actually shuts down or when the file system does a flush. + If it exists, the device-specific driver's routine will be invoked. If there + wasn't one specified, the Irp will be completed with an Invalid device request. + +Arguments: + + DriverObject - Pointer to device object to being shutdown by system. + + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + + if (deviceExtension->ClassShutdownFlush) { + + // + // Call the device-specific driver's routine. + // + + return deviceExtension->ClassShutdownFlush(DeviceObject, Irp); + } + + // + // Device-specific driver doesn't support this. + // + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_INVALID_DEVICE_REQUEST; +} + + +ULONG +STDCALL +ScsiClassFindUnclaimedDevices( + IN PCLASS_INIT_DATA InitializationData, + IN PSCSI_ADAPTER_BUS_INFO AdapterInformation + ) + +{ + ULONG scsiBus,deviceCount = 0; + PCHAR buffer = (PCHAR)AdapterInformation; + PSCSI_INQUIRY_DATA lunInfo; + PINQUIRYDATA inquiryData; + + for (scsiBus=0; scsiBus < (ULONG)AdapterInformation->NumberOfBuses; scsiBus++) { + + // + // Get the SCSI bus scan data for this bus. + // + + lunInfo = (PVOID) (buffer + AdapterInformation->BusData[scsiBus].InquiryDataOffset); + + // + // Search list for unclaimed disk devices. + // + + while (AdapterInformation->BusData[scsiBus].InquiryDataOffset) { + + inquiryData = (PVOID)lunInfo->InquiryData; + + ASSERT(InitializationData->ClassFindDeviceCallBack); + + if ((InitializationData->ClassFindDeviceCallBack(inquiryData)) && (!lunInfo->DeviceClaimed)) { + + deviceCount++; + } + + if (lunInfo->NextInquiryDataOffset == 0) { + break; + } + + lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset); + } + } + return deviceCount; +} + + + +NTSTATUS +STDCALL +ScsiClassCreateDeviceObject( + IN PDRIVER_OBJECT DriverObject, + IN PCCHAR ObjectNameBuffer, + IN OPTIONAL PDEVICE_OBJECT PhysicalDeviceObject, + IN OUT PDEVICE_OBJECT *DeviceObject, + IN PCLASS_INIT_DATA InitializationData + ) + +/*++ + +Routine Description: + + This routine creates an object for the physical device specified and + sets up the deviceExtension's function pointers for each entry point + in the device-specific driver. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + ObjectNameBuffer - Dir. name of the object to create. + + PhysicalDeviceObject - Pointer to the physical (class) device object for + this logical unit or NULL if this is it. + + DeviceObject - Pointer to the device object pointer we will return. + + InitializationData - Pointer to the init data created by the device-specific driver. + +Return Value: + + NTSTATUS + +--*/ + +{ + STRING ntNameString; + UNICODE_STRING ntUnicodeString; + NTSTATUS status; + PDEVICE_OBJECT deviceObject = NULL; + + *DeviceObject = NULL; + + DebugPrint((2, + "ScsiClassCreateDeviceObject: Create device object %s\n", + ObjectNameBuffer)); + + RtlInitString(&ntNameString, + ObjectNameBuffer); + + status = RtlAnsiStringToUnicodeString(&ntUnicodeString, + &ntNameString, + TRUE); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, + "CreateDiskDeviceObjects: Cannot convert string %s\n", + ObjectNameBuffer)); + + ntUnicodeString.Buffer = NULL; + return status; + } + + status = IoCreateDevice(DriverObject, + InitializationData->DeviceExtensionSize, + &ntUnicodeString, + InitializationData->DeviceType, + InitializationData->DeviceCharacteristics, + FALSE, + &deviceObject); + + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, + "CreateDiskDeviceObjects: Can not create device object %s\n", + ObjectNameBuffer)); + + } else { + + PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension; + + // + // Fill in entry points + // + + deviceExtension->ClassError = InitializationData->ClassError; + deviceExtension->ClassReadWriteVerification = InitializationData->ClassReadWriteVerification; + deviceExtension->ClassFindDevices = InitializationData->ClassFindDevices; + deviceExtension->ClassDeviceControl = InitializationData->ClassDeviceControl; + deviceExtension->ClassShutdownFlush = InitializationData->ClassShutdownFlush; + deviceExtension->ClassCreateClose = InitializationData->ClassCreateClose; + deviceExtension->ClassStartIo = InitializationData->ClassStartIo; + + deviceExtension->MediaChangeCount = 0; + + // + // If a pointer to the physical device object was passed in then use + // that. If the value was NULL, then this is the physical device so + // use the pointer to the device we just created. + // + + if(ARGUMENT_PRESENT(PhysicalDeviceObject)) { + deviceExtension->PhysicalDevice = PhysicalDeviceObject; + } else { + deviceExtension->PhysicalDevice = deviceObject; + } + } + + *DeviceObject = deviceObject; + + RtlFreeUnicodeString(&ntUnicodeString); + + // + // Indicate the ntUnicodeString is free. + // + + ntUnicodeString.Buffer = NULL; + + return status; +} + + +NTSTATUS +STDCALL +ScsiClassClaimDevice( + IN PDEVICE_OBJECT PortDeviceObject, + IN PSCSI_INQUIRY_DATA LunInfo, + IN BOOLEAN Release, + OUT PDEVICE_OBJECT *NewPortDeviceObject OPTIONAL + ) +/*++ + +Routine Description: + + This function claims a device in the port driver. The port driver object + is updated with the correct driver object if the device is successfully + claimed. + +Arguments: + + PortDeviceObject - Supplies the base port device object. + + LunInfo - Supplies the logical unit inforamtion of the device to be claimed. + + Release - Indicates the logical unit should be released rather than claimed. + + NewPortDeviceObject - Returns the updated port device object to be used + for all future accesses. + +Return Value: + + Returns a status indicating success or failure of the operation. + +--*/ + +{ + IO_STATUS_BLOCK ioStatus; + PIRP irp; + PIO_STACK_LOCATION irpStack; + KEVENT event; + NTSTATUS status; + SCSI_REQUEST_BLOCK srb; + + PAGED_CODE(); + + if (NewPortDeviceObject != NULL) { + *NewPortDeviceObject = NULL; + } + + // + // Clear the SRB fields. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Write length to SRB. + // + + srb.Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set SCSI bus address. + // + + srb.PathId = LunInfo->PathId; + srb.TargetId = LunInfo->TargetId; + srb.Lun = LunInfo->Lun; + + srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE : + SRB_FUNCTION_CLAIM_DEVICE; + + // + // Set the event object to the unsignaled state. + // It will be used to signal request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // Build synchronous request with no transfer. + // + + irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE, + PortDeviceObject, + NULL, + 0, + NULL, + 0, + TRUE, + &event, + &ioStatus); + + if (irp == NULL) { + + DebugPrint((1, "ScsiClassClaimDevice: Can't allocate Irp\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + irpStack = IoGetNextIrpStackLocation(irp); + + // + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = &srb; + + // + // Set up IRP Address. + // + + srb.OriginalRequest = irp; + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(PortDeviceObject, irp); + if (status == STATUS_PENDING) { + + KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + // + // If this is a release request, then just decrement the reference count + // and return. The status does not matter. + // + + if (Release) { + + ObDereferenceObject(PortDeviceObject); + return STATUS_SUCCESS; + } + + if (!NT_SUCCESS(status)) { + return status; + } + + ASSERT(srb.DataBuffer != NULL); + + // + // Reference the new port driver object so that it will not go away while + // it is being used. + // + + status = ObReferenceObjectByPointer(srb.DataBuffer, + 0, + NULL, + KernelMode ); + + if (!NT_SUCCESS(status)) { + + return status; + } + + // + // Return the new port device object pointer. + // + + if (NewPortDeviceObject != NULL) { + *NewPortDeviceObject = srb.DataBuffer; + } + + return status; +} + + +NTSTATUS +STDCALL +ScsiClassInternalIoControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine passes internal device controls to the port driver. + Internal device controls are used by higher level class drivers to + send scsi requests to a device that are not normally sent by a generic + class driver. + + The path ID, target ID and logical unit ID are set in the srb so the + higher level driver does not have to figure out what values are actually + used. + +Arguments: + + DeviceObject - Supplies a pointer to the device object for this request. + + Irp - Supplies the Irp making the request. + +Return Value: + + Returns back a STATUS_PENDING or a completion status. + +--*/ +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PSCSI_REQUEST_BLOCK srb; + + // + // Get a pointer to the SRB. + // + + srb = irpStack->Parameters.Scsi.Srb; + + // + // Set SCSI bus address. + // + + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + + // + // NOTICE: The SCSI-II specificaiton indicates that this field should be + // zero; however, some target controllers ignore the logical unit number + // in the INDENTIFY message and only look at the logical unit number field + // in the CDB. + // + + srb->Cdb[1] |= deviceExtension->Lun << 5; + + // + // Set the parameters in the next stack location. + // + + irpStack = IoGetNextIrpStackLocation(Irp); + + irpStack->Parameters.Scsi.Srb = srb; + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + IoSetCompletionRoutine(Irp, ClassIoCompletion, NULL, TRUE, TRUE, TRUE); + return IoCallDriver(deviceExtension->PortDeviceObject, Irp); +} + +NTSTATUS +STDCALL +ClassIoCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when an internal device control I/O request + has completed. It marks the IRP pending if necessary and returns the + status of the request. + +Arguments: + + DeviceObject - Target device object. + + Irp - Completed request. + + Context - not used. + +Return Value: + + Returns the status of the completed request. + +--*/ + +{ + UNREFERENCED_PARAMETER(Context); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // If pending is returned for this Irp then mark current stack + // as pending + // + + if (Irp->PendingReturned) { + + IoMarkIrpPending( Irp ); + } + + return Irp->IoStatus.Status; +} + + +VOID +STDCALL +ScsiClassInitializeSrbLookasideList( + IN PDEVICE_EXTENSION DeviceExtension, + IN ULONG NumberElements + ) + +/*++ + +Routine Description: + + This routine sets up a lookaside listhead for srbs. + +Arguments: + + DeviceExtension - Pointer to the deviceExtension containing the listhead. + + NumberElements - Supplies the maximum depth of the lookaside list. + + +Return Value: + + None + +--*/ + +{ + ExInitializeNPagedLookasideList(&DeviceExtension->SrbLookasideListHead, + NULL, + NULL, + NonPagedPoolMustSucceed, + SCSI_REQUEST_BLOCK_SIZE, + 'HscS', + (USHORT)NumberElements); + +} + + +ULONG +STDCALL +ScsiClassQueryTimeOutRegistryValue( + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine determines whether a reg key for a user-specified timeout value exists. + +Arguments: + + RegistryPath - Pointer to the hardware reg. entry describing the key. + +Return Value: + + New default timeout for a class of devices. + +--*/ + +{ + // + // Find the appropriate reg. key + // + + PRTL_QUERY_REGISTRY_TABLE parameters = NULL; + PWSTR path; + NTSTATUS status; + LONG timeOut = 0; + ULONG zero = 0; + ULONG size; + + if (!RegistryPath) { + return 0; + } + + parameters = ExAllocatePool(NonPagedPool, + sizeof(RTL_QUERY_REGISTRY_TABLE)*2); + + if (!parameters) { + return 0; + } + + size = RegistryPath->MaximumLength + sizeof(WCHAR); + path = ExAllocatePool(NonPagedPool, size); + + if (!path) { + ExFreePool(parameters); + return 0; + } + + RtlZeroMemory(path,size); + RtlCopyMemory(path, RegistryPath->Buffer, size - sizeof(WCHAR)); + + + // + // Check for the Timeout value. + // + + RtlZeroMemory(parameters, + (sizeof(RTL_QUERY_REGISTRY_TABLE)*2)); + + parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + parameters[0].Name = L"TimeOutValue"; + parameters[0].EntryContext = &timeOut; + parameters[0].DefaultType = REG_DWORD; + parameters[0].DefaultData = &zero; + parameters[0].DefaultLength = sizeof(ULONG); + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + path, + parameters, + NULL, + NULL); + + if (!(NT_SUCCESS(status))) { + timeOut = 0; + } + + ExFreePool(parameters); + ExFreePool(path); + + DebugPrint((2, + "ScsiClassQueryTimeOutRegistryValue: Timeout value %d\n", + timeOut)); + + + return timeOut; + +} + +NTSTATUS +STDCALL +ScsiClassCheckVerifyComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine executes when the port driver has completed a check verify + ioctl. It will set the status of the master Irp, copy the media change + count and complete the request. + +Arguments: + + DeviceObject - Supplies the device object which represents the logical + unit. + + Irp - Supplies the Irp which has completed. + + Context - NULL + +Return Value: + + NT status + +--*/ + +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PDEVICE_EXTENSION physicalExtension = + deviceExtension->PhysicalDevice->DeviceExtension; + PIRP originalIrp; + + originalIrp = irpStack->Parameters.Others.Argument1; + + // + // Copy the media change count and status + // + + *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) = + physicalExtension->MediaChangeCount; + + DebugPrint((2, "ScsiClassInterpretSenseInfo - Media change count for" + "device %d is %d\n", + physicalExtension->DeviceNumber, + physicalExtension->MediaChangeCount)); + + originalIrp->IoStatus.Status = Irp->IoStatus.Status; + originalIrp->IoStatus.Information = sizeof(ULONG); + + IoCompleteRequest(originalIrp, IO_DISK_INCREMENT); + + IoFreeIrp(Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; +} diff --git a/reactos/drivers/storage/class2/class2.rbuild b/reactos/drivers/storage/class2/class2.rbuild index 324828f657d..27dee455f1b 100644 --- a/reactos/drivers/storage/class2/class2.rbuild +++ b/reactos/drivers/storage/class2/class2.rbuild @@ -1,9 +1,10 @@ - + ntoskrnl hal + scsiport .. class2.c class2.rc diff --git a/reactos/drivers/storage/class2/license.txt b/reactos/drivers/storage/class2/license.txt new file mode 100644 index 00000000000..29389c47231 --- /dev/null +++ b/reactos/drivers/storage/class2/license.txt @@ -0,0 +1,53 @@ +Window NT Device Driver Kit +END-USER LICENSE AGREEMENT FOR MICROSOFT SOFTWARE + +IMPORTANT-READ CAREFULLY: This Microsoft End-User License Agreement ("EULA") is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation for the Microsoft software product identified above, which includes computer software and associated media and printed materials, and may include "online" or electronic documentation ("SOFTWARE PRODUCT" or "SOFTWARE"). By installing, copying, or otherwise using the SOFTWARE PRODUCT, you agree to be bound by the terms of this EULA. If you do not agree to the terms of this EULA, promptly return the unused SOFTWARE PRODUCT to the place from which you obtained it for a full refund, or if you received the SOFTWARE PRODUCT as part of a subscription or other service from Microsoft, you may cancel the subscription and receive a refund of a prorata portion of the subscription price. + +SOFTWARE PRODUCT LICENSE +The SOFTWARE PRODUCT is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE PRODUCT is licensed, not sold. To develop Windows NT(tm) device drivers, you will need to utilize two computers, one for development and one for debugging. + +1. GRANT OF LICENSE. This EULA grants you the following limited, non-exclusive rights: +* SOFTWARE PRODUCT. (a) You may make two (2) copies of the SOFTWARE PRODUCT for installation and use on two (2) computers, one for use in development and one for use in debugging to design, develop, and test your software product(s), including but not limited to device driver(s) and other software products, for use with Microsoft(r) Windows(r) or Windows NT ("Software Product"). You may make an additional copy of the Windows NT Workstation operating system (licensed and provided separately) for use on a single computer used for debugging purposes. +* Microsoft Developer Network Subscriber. If you acquired the SOFTWARE PRODUCT through a subscription to the Microsoft Developer Network, and you are either an individual developer or an individual designated within a single entity, you are granted the following additional rights with respect to the SOFTWARE PRODUCT: (a) you may make and use copies of the SOFTWARE PRODUCT on up to ten (10) separate computers, provided that you are the only individual using the SOFTWARE PRODUCT on each such computer, and (b) if you are a single entity, you may designate one individual within your organization to have the right to use the SOFTWARE PRODUCT in the manner described herein. +* SAMPLE CODE. You may modify the sample source code ("Sample Code") to design, develop and test your Software Product, and reproduce and distribute the Sample Code with such modifications in source and object code forms, provided that you comply with the Distribution Requirements described below. +* DISTRIBUTION REQUIREMENTS. You may copy and redistribute the Sample Code as described above, provided that (a) you distribute the Sample Code only in conjunction with and as a part of your Software Product; (b) you do not make any statements to the effect or which imply that your Software Product is "certified" by Microsoft or that its performance is guaranteed by Microsoft; (c) you do not use Microsoft's name, logo, or trademarks to market your Software Product; (d) you include a valid copyright notice on your Software Product; and (e) you indemnify, hold harmless, and defend Microsoft from and against any claims or lawsuits, including attorneys' fees, that arise or result from the use or distribution of your Software Product. Contact Microsoft for the applicable royalties due and other licensing terms for all other uses and/or distribution of the Sample Code +* Microsoft reserves all rights not expressly granted to you. + +2. COPYRIGHT. All right, title, and copyrights in and to the SOFTWARE PRODUCT (including but not limited to any images, photographs, animations, video, audio, music, text, and "applets" incorporated into the SOFTWARE PRODUCT), and any copies of the SOFTWARE PRODUCT, are owned by Microsoft or its suppliers. The SOFTWARE PRODUCT is protected by copyright laws and international treaty provisions. Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted material except that you may either (a) make one copy of the SOFTWARE PRODUCT solely for backup or archival purposes, or (b) install the SOFTWARE PRODUCT on a single computer, provided you keep the original solely for backup or archival purposes. You may not copy the printed materials accompanying the SOFTWARE PRODUCT. + +3. PRERELEASE CODE. The SOFTWARE PRODUCT may contain PRERELEASE CODE that is not at the level of performance and compatibility of the final, generally available, product offering. These portions of the SOFTWARE PRODUCT may not operate correctly and may be substantially modified prior to first commercial shipment. Microsoft is not obligated to make this or any later version of the SOFTWARE PRODUCT commercially available. Microsoft grants you the right to distribute test versions of your Application created using the PRERELEASE CODE provided you comply with the Distribution Requirements described in Section 1 and the following additional provisions: (a) you must mark the test version of your Application "BETA" and (b) you are solely responsible for updating your customers with versions of your Application that operate satisfactorily with the final commercial release of the PRERELEASE CODE. + +4. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS. +( Limitations on Reverse Engineering, Decompilation, and Disassembly. You may not reverse-engineer, decompile, or disassemble the SOFTWARE PRODUCT, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation. +( Rental. You may not rent or lease the SOFTWARE PRODUCT. +( Software Transfer. You may permanently transfer all of your rights under this EULA, provided you retain no copies, you transfer all of the SOFTWARE PRODUCT (including all component parts, the media and printed materials, any upgrades, this EULA, and, if applicable, the Certificate of Authenticity), and the recipient agrees to the terms of this EULA. If the SOFTWARE PRODUCT is an upgrade, any transfer must include all prior versions of the SOFTWARE PRODUCT. +( Termination. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the SOFTWARE PRODUCT and all of its component parts. + +5. EXPORT RESTRICTIONS. You agree that neither you nor your customers intend to or will, directly or indirectly, export or transmit (a) the SOFTWARE PRODUCT or related documentation and technical data or (b) your Application as described in Section 1 of this Agreement (or any part thereof), or process, or service that is the direct product of the SOFTWARE PRODUCT to any country to which such export or transmission is restricted by any applicable U.S. regulation or statute, without the prior written consent, if required, of the Bureau of Export Administration of the U.S. Department of Commerce, or such other governmental entity as may have jurisdiction over such export or transmission. + +NO WARRANTIES. To the maximum extent permitted by applicable law, Microsoft expressly disclaims any warranty for the SOFTWARE PRODUCT. The SOFTWARE PRODUCT and any related documentation are provided "as is" without warranty of any kind, either express or implied, including, without limitation, the implied warranties of merchantability or fitness for a particular purpose. The entire risk arising out of use or performance of the SOFTWARE PRODUCT remains with you. + +LIMITATION OF LIABILITY. Microsoft's entire liability and your exclusive remedy under this EULA shall not exceed one hundred dollars (US$100.00). + +NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no event shall Microsoft or its suppliers be liable for any damages whatsoever (including, without limitation, damages for loss of business profit, business interruption, loss of business information, or any other pecuniary loss) arising out of the use or inability to use this Microsoft product, even if Microsoft has been advised of the possibility of such damages. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you. + +U.S. GOVERNMENT RESTRICTED RIGHTS.The SOFTWARE and documentation are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer Software -- Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer is Microsoft Corporation/One Microsoft Way/Redmond, WA 98052-6399. + +If you acquired this product in the United States, this Agreement is governed by the laws of the State of Washington. If you acquired this product outside the United States, local law may apply. + +Should you have any questions concerning this Agreement, if you desire rights to use the product beyond what is listed here, or if you desire to contact Microsoft for any reason, please contact your local Microsoft subsidiary or sales office or write to: Microsoft Sales and Service, One Microsoft Way, Redmond, WA 98052-6399. + +Si vous avez acquis votre produit Microsoft au CANADA, la garantie limitée suivante vous concerne : + +GARANTIE LIMITÉE + +EXCLUSION DE GARANTIES. Microsoft renonce entièrement à toute garantie pour le LOGICIEL. Le LOGICIEL et toute autre documentation s'y rapportant sont fournis «comme tels» sans aucune garantie quelle qu'elle soit, expresse ou implicite, y compris, mais ne se limitant pas, aux garanties implicites de la qualité marchande ou un usage particulier. Le risque total découlant de l'utilisation ou de la performance du LOGICIEL est entre vos mains. + +RESPONSABILITÉ LIMITÉE. La seule obligation de Microsoft et votre recours exclusif concernant ce contrat n'excèderont pas cent dollars (US$100.00). + +ABSENCE DE RESPONSABILITÉ POUR LES DOMMAGES INDIRECTS. Microsoft ou ses fournisseurs ne pourront être tenus responsables en aucune circonstance de tout dommage quel qu'il soit (y compris mais non de façon limitative aux dommages directs ou indirects causés par la perte de bénéfices commerciaux, l'interruption des affaires, la perte d'information commerciale ou toute autre perte pécuniaire) résultant de l'utilisation ou de l'impossibilité d'utilisation de ce produit, et ce, même si la société Microsoft a été avisée de l'éventualité de tels dommages. Certains états/juridictions ne permettent pas l'exclusion ou la limitation de responsabilité relative aux dommages indirects ou consécutifs, et la limitation ci-dessus peut ne pas s'appliquer à votre égard. + +La présente Convention est régie par les lois de la province d'Ontario, Canada. Chacune des parties à la présente reconnaît irrévocablement la compétence des tribunaux de la province d'Ontario et consent à instituer tout litige qui pourrait découler de la présente auprès des tribunaux situés dans le district judiciaire de York, province d'Ontario. + +Au cas où vous auriez des questions concernant cette licence ou que vous désiriez vous mettre en rapport avec Microsoft pour quelque raison que ce soit, veuillez contacter la succursale Microsoft desservant votre pays, dont l'adresse est fournie dans ce produit, ou écrire à : Microsoft Customer Sales and Service, One Microsoft Way, Redmond, Washington 98052-6399. + diff --git a/reactos/drivers/storage/directory.rbuild b/reactos/drivers/storage/directory.rbuild index 94658e533d5..809cf00a546 100644 --- a/reactos/drivers/storage/directory.rbuild +++ b/reactos/drivers/storage/directory.rbuild @@ -22,8 +22,8 @@ - - + + diff --git a/reactos/drivers/storage/disk/disk.c b/reactos/drivers/storage/disk/disk.c index 3f31ee658dd..c3e39b78bec 100644 --- a/reactos/drivers/storage/disk/disk.c +++ b/reactos/drivers/storage/disk/disk.c @@ -1,30 +1,10 @@ /* - * ReactOS kernel - * Copyright (C) 2001, 2002, 2003, 2004 ReactOS Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * PROJECT: ReactOS Storage Stack + * LICENSE: DDK - see license.txt in the root dir + * FILE: drivers/storage/disk/disk.c + * PURPOSE: Disk class driver + * PROGRAMMERS: Based on a source code sample from Microsoft NT4 DDK */ -/* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: services/storage/disk/disk.c - * PURPOSE: disk class driver - * PROGRAMMER: Eric Kohl - */ - -/* INCLUDES *****************************************************************/ #include #include @@ -35,2580 +15,4943 @@ #include #include -#define NDEBUG +//#define NDEBUG #include -#define VERSION "0.0.1" +#define IO_WRITE_CACHE_ENABLED ((NTSTATUS)0x80040020L) +#define IO_WRITE_CACHE_DISABLED ((NTSTATUS)0x80040022L) -#define SCSI_DISK_TIMEOUT 10 /* Default timeout: 10 seconds */ -#define MODE_DATA_SIZE 192 -#define MAX_PATH 260 +#ifdef POOL_TAGGING +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif +#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'DscS') +#endif +// +// Disk device data +// + +typedef struct _DISK_DATA { + + // + // Partition chain + // + + PDEVICE_EXTENSION NextPartition; + + // + // Disk signature (from MBR) + // + + ULONG Signature; + + // + // MBR checksum + // + + ULONG MbrCheckSum; + + // + // Number of hidden sectors for BPB. + // + + ULONG HiddenSectors; + + // + // Partition number of this device object + // + // This field is set during driver initialization or when the partition + // is created to identify a parition to the system. + // + + ULONG PartitionNumber; + + // + // This field is the ordinal of a partition as it appears on a disk. + // + + ULONG PartitionOrdinal; + + // + // Partition type of this device object + // + // This field is set by: + // + // 1) Initially set according to the partition list entry partition + // type returned by IoReadPartitionTable. + // + // 2) Subsequently set by the IOCTL_DISK_SET_PARTITION_INFORMATION + // I/O control function when IoSetPartitionInformation function + // successfully updates the partition type on the disk. + // + + UCHAR PartitionType; + + // + // Boot indicator - indicates whether this partition is a bootable (active) + // partition for this device + // + // This field is set according to the partition list entry boot indicator + // returned by IoReadPartitionTable. + // + + BOOLEAN BootIndicator; + + // + // DriveNotReady - inidicates that the this device is currenly not ready + // beacasue there is no media in the device. + // + + BOOLEAN DriveNotReady; -typedef struct _DISK_DATA -{ - PDEVICE_EXTENSION NextPartition; - ULONG Signature; - ULONG MbrCheckSum; - ULONG HiddenSectors; - ULONG PartitionNumber; - ULONG PartitionOrdinal; - UCHAR PartitionType; - BOOLEAN BootIndicator; - BOOLEAN DriveNotReady; } DISK_DATA, *PDISK_DATA; -BOOLEAN STDCALL -DiskClassFindDevices(PDRIVER_OBJECT DriverObject, - PUNICODE_STRING RegistryPath, - PCLASS_INIT_DATA InitializationData, - PDEVICE_OBJECT PortDeviceObject, - ULONG PortNumber); +// +// Define a general structure of identfing disk controllers with bad +// hardware. +// -BOOLEAN STDCALL -DiskClassCheckDevice(IN PINQUIRYDATA InquiryData); +typedef struct _BAD_CONTROLLER_INFORMATION { + PCHAR InquiryString; + BOOLEAN DisableTaggedQueuing; + BOOLEAN DisableSynchronousTransfers; + BOOLEAN DisableDisconnects; + BOOLEAN DisableWriteCache; +}BAD_CONTROLLER_INFORMATION, *PBAD_CONTROLLER_INFORMATION; -NTSTATUS STDCALL -DiskClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -static VOID -DiskClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension, - IN ULONG DeviceNumber); - -static NTSTATUS -DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath, /* what's this used for? */ - IN PDEVICE_OBJECT PortDeviceObject, - IN ULONG PortNumber, - IN ULONG DiskNumber, - IN PIO_SCSI_CAPABILITIES Capabilities, - IN PSCSI_INQUIRY_DATA InquiryData, - IN PCLASS_INIT_DATA InitializationData); - -NTSTATUS STDCALL -DiskClassDeviceControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -NTSTATUS STDCALL -DiskClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -static BOOLEAN -ScsiDiskSearchForDisk(IN PDEVICE_EXTENSION DeviceExtension, - IN HANDLE BusKey, - OUT PULONG DetectedDiskNumber); - -static VOID -DiskClassUpdatePartitionDeviceObjects (IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -static VOID -ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension); - -static BOOLEAN -ScsiDiskCalcMbrCheckSum(IN PDEVICE_EXTENSION DeviceExtension, - OUT PULONG Checksum); +BAD_CONTROLLER_INFORMATION const ScsiDiskBadControllers[] = { + { "TOSHIBA MK538FB 60", TRUE, FALSE, FALSE, FALSE }, + { "CONNER CP3500", FALSE, TRUE, FALSE, FALSE }, + { "OLIVETTICP3500", FALSE, TRUE, FALSE, FALSE }, + { "SyQuest SQ5110 CHC", TRUE, TRUE, FALSE, FALSE }, + { "SEAGATE ST41601N 0102", FALSE, TRUE, FALSE, FALSE }, + { "SEAGATE ST3655N", FALSE, FALSE, FALSE, TRUE }, + { "SEAGATE ST3390N", FALSE, FALSE, FALSE, TRUE }, + { "SEAGATE ST12550N", FALSE, FALSE, FALSE, TRUE }, + { "SEAGATE ST32430N", FALSE, FALSE, FALSE, TRUE }, + { "SEAGATE ST31230N", FALSE, FALSE, FALSE, TRUE }, + { "SEAGATE ST15230N", FALSE, FALSE, FALSE, TRUE }, + { "FUJITSU M2652S-512", TRUE, FALSE, FALSE, FALSE }, + { "MAXTOR MXT-540SL I1.2", TRUE, FALSE, FALSE, FALSE }, + { "COMPAQ PD-1", FALSE, TRUE, FALSE, FALSE } +}; -static NTSTATUS -DiskBuildPartitionTable(IN PDEVICE_OBJECT DiskDeviceObject, - IN PIRP Irp); +#define NUMBER_OF_BAD_CONTROLLERS (sizeof(ScsiDiskBadControllers) / sizeof(BAD_CONTROLLER_INFORMATION)) +#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + sizeof(DISK_DATA) +#define MODE_DATA_SIZE 192 +#define VALUE_BUFFER_SIZE 2048 +#define SCSI_DISK_TIMEOUT 10 +#define PARTITION0_LIST_SIZE 4 -/* FUNCTIONS ****************************************************************/ + +NTSTATUS +STDCALL +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); -/********************************************************************** - * NAME EXPORTED - * DriverEntry - * - * DESCRIPTION - * This function initializes the driver, locates and claims - * hardware resources, and creates various NT objects needed - * to process I/O requests. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DriverObject - * System allocated Driver Object for this driver - * - * RegistryPath - * Name of registry driver service key - * - * RETURN VALUE - * Status - */ +BOOLEAN +STDCALL +ScsiDiskDeviceVerification( + IN PINQUIRYDATA InquiryData + ); + +BOOLEAN +STDCALL +FindScsiDisks( + IN PDRIVER_OBJECT DriveObject, + IN PUNICODE_STRING RegistryPath, + IN PCLASS_INIT_DATA InitializationData, + IN PDEVICE_OBJECT PortDeviceObject, + IN ULONG PortNumber + ); + +NTSTATUS +STDCALL +ScsiDiskCreateClose ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +STDCALL +ScsiDiskReadWriteVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +STDCALL +ScsiDiskDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +STDCALL +ScsiDiskProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +NTSTATUS +STDCALL +ScsiDiskShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +STDCALL +DisableWriteCache( + IN PDEVICE_OBJECT DeviceObject, + IN PSCSI_INQUIRY_DATA LunInfo + ); + +BOOLEAN +STDCALL +ScsiDiskModeSelect( + IN PDEVICE_OBJECT DeviceObject, + IN PCHAR ModeSelectBuffer, + IN ULONG Length, + IN BOOLEAN SavePage + ); + +BOOLEAN +STDCALL +IsFloppyDevice( + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +STDCALL +CalculateMbrCheckSum( + IN PDEVICE_EXTENSION DeviceExtension, + OUT PULONG Checksum + ); + +BOOLEAN +STDCALL +EnumerateBusKey( + IN PDEVICE_EXTENSION DeviceExtension, + HANDLE BusKey, + PULONG DiskNumber + ); + +VOID +STDCALL +UpdateGeometry( + IN PDEVICE_EXTENSION DeviceExtension + ); + +NTSTATUS +STDCALL +UpdateRemovableGeometry ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +STDCALL +CreateDiskDeviceObject( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + IN PDEVICE_OBJECT PortDeviceObject, + IN ULONG PortNumber, + IN PULONG DeviceCount, + IN PIO_SCSI_CAPABILITIES PortCapabilities, + IN PSCSI_INQUIRY_DATA LunInfo, + IN PCLASS_INIT_DATA InitData + ); + +VOID +STDCALL +UpdateDeviceObjects( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +STDCALL +ScanForSpecial( + PDEVICE_OBJECT DeviceObject, + PSCSI_INQUIRY_DATA LunInfo, + PIO_SCSI_CAPABILITIES PortCapabilities + ); + +VOID +STDCALL +ResetScsiBus( + IN PDEVICE_OBJECT DeviceObject + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, DriverEntry) +#pragma alloc_text(PAGE, FindScsiDisks) +#pragma alloc_text(PAGE, CreateDiskDeviceObject) +#pragma alloc_text(PAGE, CalculateMbrCheckSum) +#pragma alloc_text(PAGE, EnumerateBusKey) +#pragma alloc_text(PAGE, UpdateGeometry) +#pragma alloc_text(PAGE, IsFloppyDevice) +#pragma alloc_text(PAGE, ScanForSpecial) +#pragma alloc_text(PAGE, ScsiDiskDeviceControl) +#pragma alloc_text(PAGE, ScsiDiskModeSelect) +#endif + + +NTSTATUS +STDCALL +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine initializes the SCSI hard disk class driver. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + RegistryPath - Pointer to the name of the services node for this driver. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ -NTSTATUS STDCALL -DriverEntry(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath) { - CLASS_INIT_DATA InitData; + CLASS_INIT_DATA InitializationData; - DPRINT("Disk Class Driver %s\n", - VERSION); - DPRINT("RegistryPath '%wZ'\n", - RegistryPath); + // + // Zero InitData + // - RtlZeroMemory(&InitData, - sizeof(CLASS_INIT_DATA)); + RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA)); - InitData.InitializationDataSize = sizeof(CLASS_INIT_DATA); - InitData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION) + sizeof(DISK_DATA); - InitData.DeviceType = FILE_DEVICE_DISK; - InitData.DeviceCharacteristics = 0; + // + // Set sizes + // - InitData.ClassError = NULL; // DiskClassProcessError; - InitData.ClassReadWriteVerification = DiskClassCheckReadWrite; - InitData.ClassFindDeviceCallBack = DiskClassCheckDevice; - InitData.ClassFindDevices = DiskClassFindDevices; - InitData.ClassDeviceControl = DiskClassDeviceControl; - InitData.ClassShutdownFlush = DiskClassShutdownFlush; - InitData.ClassCreateClose = NULL; - InitData.ClassStartIo = NULL; + InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA); + InitializationData.DeviceExtensionSize = DEVICE_EXTENSION_SIZE; - return(ScsiClassInitialize(DriverObject, - RegistryPath, - &InitData)); + InitializationData.DeviceType = FILE_DEVICE_DISK; + InitializationData.DeviceCharacteristics = 0; + + // + // Set entry points + // + + InitializationData.ClassError = ScsiDiskProcessError; + InitializationData.ClassReadWriteVerification = ScsiDiskReadWriteVerification; + InitializationData.ClassFindDevices = FindScsiDisks; + InitializationData.ClassFindDeviceCallBack = ScsiDiskDeviceVerification; + InitializationData.ClassDeviceControl = ScsiDiskDeviceControl; + InitializationData.ClassShutdownFlush = ScsiDiskShutdownFlush; + InitializationData.ClassCreateClose = NULL; + + // + // Call the class init routine + // + + return ScsiClassInitialize( DriverObject, RegistryPath, &InitializationData); + +} // end DriverEntry() + + + +BOOLEAN +STDCALL +ScsiDiskDeviceVerification( + IN PINQUIRYDATA InquiryData + ) + +/*++ + +Routine Description: + + This routine checks InquiryData for the correct device type and qualifier. + +Arguments: + + InquiryData - Pointer to the inquiry data for the device in question. + +Return Value: + + True is returned if the correct device type is found. + +--*/ +{ + + if (((InquiryData->DeviceType == DIRECT_ACCESS_DEVICE) || + (InquiryData->DeviceType == OPTICAL_DEVICE)) && + InquiryData->DeviceTypeQualifier == 0) { + + return TRUE; + + } else { + return FALSE; + } } + +BOOLEAN +STDCALL +FindScsiDisks( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + IN PCLASS_INIT_DATA InitializationData, + IN PDEVICE_OBJECT PortDeviceObject, + IN ULONG PortNumber + ) -/********************************************************************** - * NAME EXPORTED - * DiskClassFindDevices - * - * DESCRIPTION - * This function searches for device that are attached to the - * given scsi port. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DriverObject - * System allocated Driver Object for this driver - * - * RegistryPath - * Name of registry driver service key - * - * InitializationData - * Pointer to the main initialization data - * - * PortDeviceObject - * Pointer to the port Device Object - * - * PortNumber - * Port number - * - * RETURN VALUE - * TRUE: At least one disk drive was found - * FALSE: No disk drive found - */ +/*++ + +Routine Description: + + This routine gets a port drivers capabilities, obtains the + inquiry data, searches the SCSI bus for the port driver and creates + the device objects for the disks found. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + PortDeviceObject - Device object use to send requests to port driver. + + PortNumber - Number for port driver. Used to pass on to + CreateDiskDeviceObjects() and create device objects. + +Return Value: + + True is returned if one disk was found and successfully created. + +--*/ -BOOLEAN STDCALL -DiskClassFindDevices(PDRIVER_OBJECT DriverObject, - PUNICODE_STRING RegistryPath, - PCLASS_INIT_DATA InitializationData, - PDEVICE_OBJECT PortDeviceObject, - ULONG PortNumber) { - PCONFIGURATION_INFORMATION ConfigInfo; - PIO_SCSI_CAPABILITIES PortCapabilities; - PSCSI_ADAPTER_BUS_INFO AdapterBusInfo; - PSCSI_INQUIRY_DATA UnitInfo; - PINQUIRYDATA InquiryData; - PCHAR Buffer; - ULONG Bus; - ULONG DeviceCount; - BOOLEAN FoundDevice = FALSE; - NTSTATUS Status; + PIO_SCSI_CAPABILITIES portCapabilities; + PULONG diskCount; + PCONFIGURATION_INFORMATION configurationInformation; + PCHAR buffer; + PSCSI_INQUIRY_DATA lunInfo; + PSCSI_ADAPTER_BUS_INFO adapterInfo; + PINQUIRYDATA inquiryData; + ULONG scsiBus; + ULONG size; + ULONG sizeElement; + ULONG adapterDisk; + NTSTATUS status; + BOOLEAN foundOne = FALSE; - DPRINT("DiskClassFindDevices() called.\n"); + PAGED_CODE(); - /* Get port capabilities */ - Status = ScsiClassGetCapabilities(PortDeviceObject, - &PortCapabilities); - if (!NT_SUCCESS(Status)) - { - DPRINT("ScsiClassGetCapabilities() failed! (Status 0x%lX)\n", Status); - return(FALSE); + // + // Call port driver to get adapter capabilities. + // + + status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities); + + if (!NT_SUCCESS(status)) { + DebugPrint((1,"FindScsiDevices: ScsiClassGetCapabilities failed\n")); + return(FALSE); } - DPRINT("PortCapabilities: %p\n", PortCapabilities); - DPRINT("MaximumTransferLength: %lu\n", PortCapabilities->MaximumTransferLength); - DPRINT("MaximumPhysicalPages: %lu\n", PortCapabilities->MaximumPhysicalPages); + // + // Call port driver to get inquiry information to find disks. + // - /* Get inquiry data */ - Status = ScsiClassGetInquiryData(PortDeviceObject, - (PSCSI_ADAPTER_BUS_INFO *)&Buffer); - if (!NT_SUCCESS(Status)) - { - DPRINT("ScsiClassGetInquiryData() failed! (Status %x)\n", Status); - return(FALSE); + status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer); + + if (!NT_SUCCESS(status)) { + DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n")); + return(FALSE); } - /* Check whether there are unclaimed devices */ - AdapterBusInfo = (PSCSI_ADAPTER_BUS_INFO)Buffer; - DeviceCount = ScsiClassFindUnclaimedDevices(InitializationData, - AdapterBusInfo); - if (DeviceCount == 0) - { - DPRINT("No unclaimed devices!\n"); - return(FALSE); + // + // Do a quick scan of the devices on this adapter to determine how many + // disks are on this adapter. This is used to determine the number of + // SRB zone elements to allocate. + // + + adapterDisk = 0; + adapterInfo = (PVOID) buffer; + + adapterDisk = ScsiClassFindUnclaimedDevices(InitializationData, adapterInfo); + + // + // Allocate a zone of SRB for disks on this adapter. + // + + if (adapterDisk == 0) { + + // + // No free disks were found. + // + + return(FALSE); } - DPRINT("Found %lu unclaimed devices!\n", DeviceCount); + // + // Get the number of disks already initialized. + // - ConfigInfo = IoGetConfigurationInformation(); + configurationInformation = IoGetConfigurationInformation(); + diskCount = &configurationInformation->DiskCount; - /* Search each bus of this adapter */ - for (Bus = 0; Bus < (ULONG)AdapterBusInfo->NumberOfBuses; Bus++) - { - DPRINT("Searching bus %lu\n", Bus); + // + // For each SCSI bus this adapter supports ... + // - UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + AdapterBusInfo->BusData[Bus].InquiryDataOffset); + for (scsiBus=0; scsiBus < (ULONG)adapterInfo->NumberOfBuses; scsiBus++) { - while (AdapterBusInfo->BusData[Bus].InquiryDataOffset) - { - InquiryData = (PINQUIRYDATA)UnitInfo->InquiryData; + // + // Get the SCSI bus scan data for this bus. + // - DPRINT("Device type %u\n", InquiryData->DeviceType); + lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset); - if (((InquiryData->DeviceType == DIRECT_ACCESS_DEVICE) || - (InquiryData->DeviceType == OPTICAL_DEVICE)) && - (InquiryData->DeviceTypeQualifier == 0) && - (UnitInfo->DeviceClaimed == FALSE)) - { - DPRINT("Vendor: '%.24s'\n", - InquiryData->VendorId); + // + // Search list for unclaimed disk devices. + // - /* Create device objects for disk */ - Status = DiskClassCreateDeviceObject(DriverObject, - RegistryPath, - PortDeviceObject, - PortNumber, - ConfigInfo->DiskCount, - PortCapabilities, - UnitInfo, - InitializationData); - if (NT_SUCCESS(Status)) - { - ConfigInfo->DiskCount++; - FoundDevice = TRUE; - } - } + while (adapterInfo->BusData[scsiBus].InquiryDataOffset) { - if (UnitInfo->NextInquiryDataOffset == 0) - break; + inquiryData = (PVOID)lunInfo->InquiryData; - UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + UnitInfo->NextInquiryDataOffset); - } - } + if (((inquiryData->DeviceType == DIRECT_ACCESS_DEVICE) || + (inquiryData->DeviceType == OPTICAL_DEVICE)) && + inquiryData->DeviceTypeQualifier == 0 && + (!lunInfo->DeviceClaimed)) { - ExFreePool(Buffer); + DebugPrint((1, + "FindScsiDevices: Vendor string is %.24s\n", + inquiryData->VendorId)); - DPRINT("DiskClassFindDevices() done\n"); + // + // Create device objects for disk + // - return(FoundDevice); -} + status = CreateDiskDeviceObject(DriverObject, + RegistryPath, + PortDeviceObject, + PortNumber, + diskCount, + portCapabilities, + lunInfo, + InitializationData); + if (NT_SUCCESS(status)) { -static VOID -DiskClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension, - IN ULONG DeviceNumber) -{ - DeviceExtension->MediaChangeEvent = - IoCreateSynchronizationEvent (NULL, - &DeviceExtension->MediaChangeEventHandle); + // + // Increment system disk device count. + // - KeClearEvent (DeviceExtension->MediaChangeEvent); -} + (*diskCount)++; + foundOne = TRUE; - - -/********************************************************************** - * NAME EXPORTED - * DiskClassCheckDevice - * - * DESCRIPTION - * This function checks the InquiryData for the correct device - * type and qualifier. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * InquiryData - * Pointer to the inquiry data for the device in question. - * - * RETURN VALUE - * TRUE: A disk device was found. - * FALSE: Otherwise. - */ - -BOOLEAN STDCALL -DiskClassCheckDevice(IN PINQUIRYDATA InquiryData) -{ - return((InquiryData->DeviceType == DIRECT_ACCESS_DEVICE || - InquiryData->DeviceType == OPTICAL_DEVICE) && - InquiryData->DeviceTypeQualifier == 0); -} - - -/********************************************************************** - * NAME EXPORTED - * DiskClassCheckReadWrite - * - * DESCRIPTION - * This function checks the given IRP for correct data. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceObject - * Pointer to the device. - * - * Irp - * Irp to check. - * - * RETURN VALUE - * STATUS_SUCCESS: The IRP matches the requirements of the given device. - * Others: Failure. - */ - -NTSTATUS STDCALL -DiskClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION DeviceExtension; - PDISK_DATA DiskData; - PIO_STACK_LOCATION IrpStack; - LARGE_INTEGER EndingOffset; - - DPRINT("DiskClassCheckReadWrite() called\n"); - - DeviceExtension = DeviceObject->DeviceExtension; - DiskData = (PDISK_DATA)(DeviceExtension + 1); - - if (DiskData->DriveNotReady == TRUE) - { - Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY; - IoSetHardErrorOrVerifyDevice(Irp, - DeviceObject); - return(STATUS_INVALID_PARAMETER); - } - - - - IrpStack = IoGetCurrentIrpStackLocation(Irp); - EndingOffset.QuadPart = IrpStack->Parameters.Read.ByteOffset.QuadPart + - IrpStack->Parameters.Read.Length; - - - DPRINT("Ending %I64d, and RealEnding %I64d! PartSize %I64d\n",EndingOffset.QuadPart, - DeviceExtension->PartitionLength.QuadPart, - DeviceExtension->PartitionLength.QuadPart / - DeviceExtension->DiskGeometry->BytesPerSector); - - if ((DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) && - (DeviceExtension->DiskGeometry->MediaType == RemovableMedia)) - { -/* Assume if removable media and if Partition length is 0, Partition not built yet! */ - if (DeviceExtension->PartitionLength.QuadPart == 0) - return(STATUS_SUCCESS); - } - - if (EndingOffset.QuadPart > DeviceExtension->PartitionLength.QuadPart) - { - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - return(STATUS_INVALID_PARAMETER); - } - - return(STATUS_SUCCESS); -} - - -/********************************************************************** - * NAME INTERNAL - * DiskClassCreateDeviceObject - * - * DESCRIPTION - * Create the raw device and any partition devices on this drive - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DriverObject - * The system created driver object - * RegistryPath - * PortDeviceObject - * PortNumber - * DiskNumber - * Capabilities - * InquiryData - * InitialzationData - * - * RETURN VALUE - * STATUS_SUCCESS: Device objects for disk and partitions were created. - * Others: Failure. - */ - -static NTSTATUS -DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath, - IN PDEVICE_OBJECT PortDeviceObject, - IN ULONG PortNumber, - IN ULONG DiskNumber, - IN PIO_SCSI_CAPABILITIES Capabilities, - IN PSCSI_INQUIRY_DATA InquiryData, - IN PCLASS_INIT_DATA InitializationData) -{ - OBJECT_ATTRIBUTES ObjectAttributes; - UNICODE_STRING UnicodeDeviceDirName; - WCHAR NameBuffer[80]; - CHAR NameBuffer2[80]; - PDEVICE_OBJECT DiskDeviceObject; - PDEVICE_OBJECT PartitionDeviceObject; - PDEVICE_EXTENSION DiskDeviceExtension; /* defined in class2.h */ - PDEVICE_EXTENSION PartitionDeviceExtension; /* defined in class2.h */ - PDRIVE_LAYOUT_INFORMATION PartitionList = NULL; - HANDLE Handle; - PPARTITION_INFORMATION PartitionEntry; - PDISK_DATA DiskData; - ULONG PartitionNumber; - PVOID MbrBuffer; - NTSTATUS Status; - - DPRINT("DiskClassCreateDeviceObject() called\n"); - - /* Create the harddisk device directory */ - swprintf(NameBuffer, - L"\\Device\\Harddisk%lu", - DiskNumber); - RtlInitUnicodeString(&UnicodeDeviceDirName, - NameBuffer); - InitializeObjectAttributes(&ObjectAttributes, - &UnicodeDeviceDirName, - 0, - NULL, - NULL); - Status = ZwCreateDirectoryObject(&Handle, - 0, - &ObjectAttributes); - if (!NT_SUCCESS(Status)) - { - DPRINT("Could not create device dir object\n"); - return(Status); - } - - /* Claim the disk device */ - Status = ScsiClassClaimDevice(PortDeviceObject, - InquiryData, - FALSE, - &PortDeviceObject); - if (!NT_SUCCESS(Status)) - { - DPRINT("Could not claim disk device\n"); - - ZwMakeTemporaryObject(Handle); - ZwClose(Handle); - - return(Status); - } - - /* Create disk device (Partition 0) */ - sprintf(NameBuffer2, - "\\Device\\Harddisk%lu\\Partition0", - DiskNumber); - - Status = ScsiClassCreateDeviceObject(DriverObject, - NameBuffer2, - NULL, - &DiskDeviceObject, - InitializationData); - if (!NT_SUCCESS(Status)) - { - DPRINT("ScsiClassCreateDeviceObject() failed (Status %x)\n", Status); - - /* Release (unclaim) the disk */ - ScsiClassClaimDevice(PortDeviceObject, - InquiryData, - TRUE, - NULL); - - /* Delete the harddisk device directory */ - ZwMakeTemporaryObject(Handle); - ZwClose(Handle); - - return(Status); - } - - DiskDeviceObject->Flags |= DO_DIRECT_IO; - if (((PINQUIRYDATA)InquiryData->InquiryData)->RemovableMedia) - { - DiskDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; - } - DiskDeviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1; - - if (PortDeviceObject->AlignmentRequirement > DiskDeviceObject->AlignmentRequirement) - { - DiskDeviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement; - } - - DiskDeviceExtension = DiskDeviceObject->DeviceExtension; - DiskDeviceExtension->LockCount = 0; - DiskDeviceExtension->DeviceNumber = DiskNumber; - DiskDeviceExtension->DeviceObject = DiskDeviceObject; - DiskDeviceExtension->PortDeviceObject = PortDeviceObject; - DiskDeviceExtension->PhysicalDevice = DiskDeviceObject; - DiskDeviceExtension->PortCapabilities = Capabilities; - DiskDeviceExtension->StartingOffset.QuadPart = 0; - DiskDeviceExtension->PortNumber = (UCHAR)PortNumber; - DiskDeviceExtension->PathId = InquiryData->PathId; - DiskDeviceExtension->TargetId = InquiryData->TargetId; - DiskDeviceExtension->Lun = InquiryData->Lun; - DiskDeviceExtension->SrbFlags = 0; - - /* Enable the command queueing, if it possible */ - if (Capabilities->TaggedQueuing && - ((PINQUIRYDATA)InquiryData->InquiryData)->CommandQueue) - { - DiskDeviceExtension->SrbFlags |= SRB_FLAGS_QUEUE_ACTION_ENABLE; - } - - /* Get timeout value */ - DiskDeviceExtension->TimeOutValue = - ScsiClassQueryTimeOutRegistryValue(RegistryPath); - if (DiskDeviceExtension->TimeOutValue == 0) - DiskDeviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT; - - /* Initialize the lookaside list for SRBs */ - ScsiClassInitializeSrbLookasideList(DiskDeviceExtension, - 4); - - /* zero-out disk data */ - DiskData = (PDISK_DATA)(DiskDeviceExtension + 1); - RtlZeroMemory(DiskData, - sizeof(DISK_DATA)); - - /* Get disk geometry */ - DiskDeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool, - sizeof(DISK_GEOMETRY)); - if (DiskDeviceExtension->DiskGeometry == NULL) - { - DPRINT("Failed to allocate geometry buffer!\n"); - - ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead); - - IoDeleteDevice(DiskDeviceObject); - - /* Release (unclaim) the disk */ - ScsiClassClaimDevice(PortDeviceObject, - InquiryData, - TRUE, - NULL); - - /* Delete the harddisk device directory */ - ZwMakeTemporaryObject(Handle); - ZwClose(Handle); - - return(STATUS_INSUFFICIENT_RESOURCES); - } - - /* Allocate sense data buffer */ - DiskDeviceExtension->SenseData = ExAllocatePool(NonPagedPoolCacheAligned, - SENSE_BUFFER_SIZE); - if (DiskDeviceExtension->SenseData == NULL) - { - DPRINT("Failed to allocate sense data buffer!\n"); - - ExFreePool (DiskDeviceExtension->DiskGeometry); - - ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead); - - IoDeleteDevice(DiskDeviceObject); - - /* Release (unclaim) the disk */ - ScsiClassClaimDevice(PortDeviceObject, - InquiryData, - TRUE, - NULL); - - /* Delete the harddisk device directory */ - ZwMakeTemporaryObject(Handle); - ZwClose(Handle); - - return(STATUS_INSUFFICIENT_RESOURCES); - } - - /* Read the drive's capacity */ - Status = ScsiClassReadDriveCapacity(DiskDeviceObject); - if (!NT_SUCCESS(Status) && - (DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) == 0) - { - DPRINT("Failed to retrieve drive capacity!\n"); - return(STATUS_SUCCESS); - } - else - { - /* Clear the verify flag for removable media drives. */ - DiskDeviceObject->Flags &= ~DO_VERIFY_VOLUME; - } - - DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector); - - if ((DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) && - (DiskDeviceExtension->DiskGeometry->MediaType == RemovableMedia)) - { - DiskClassCreateMediaChangeEvent(DiskDeviceExtension,DiskNumber); - if (DiskDeviceExtension->MediaChangeEvent != NULL) - { - DPRINT("Allocated media change event!\n"); - } - } - - /* Check disk for presence of a disk manager */ - HalExamineMBR(DiskDeviceObject, - DiskDeviceExtension->DiskGeometry->BytesPerSector, - 0x54, - &MbrBuffer); - if (MbrBuffer != NULL) - { - /* Start disk at sector 63 if the Ontrack Disk Manager was found */ - DPRINT("Found 'Ontrack Disk Manager'!\n"); - - DiskDeviceExtension->DMSkew = 63; - DiskDeviceExtension->DMByteSkew = - 63 * DiskDeviceExtension->DiskGeometry->BytesPerSector; - DiskDeviceExtension->DMActive = TRUE; - - ExFreePool(MbrBuffer); - MbrBuffer = NULL; - } - - if ((DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) && - (DiskDeviceExtension->DiskGeometry->MediaType == RemovableMedia)) - { - /* Allocate a partition list for a single entry. */ - PartitionList = ExAllocatePool(NonPagedPool, - sizeof(DRIVE_LAYOUT_INFORMATION)); - if (PartitionList != NULL) - { - RtlZeroMemory(PartitionList, - sizeof(DRIVE_LAYOUT_INFORMATION)); - PartitionList->PartitionCount = 1; - - DiskData->DriveNotReady = TRUE; - Status = STATUS_SUCCESS; - } - } - else - { - /* Read partition table */ - Status = IoReadPartitionTable(DiskDeviceObject, - DiskDeviceExtension->DiskGeometry->BytesPerSector, - TRUE, - &PartitionList); - - DPRINT("IoReadPartitionTable(): Status: %lx\n", Status); - - if ((!NT_SUCCESS(Status) || PartitionList->PartitionCount == 0) && - DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - { - if (!NT_SUCCESS(Status)) - { - /* Drive is not ready. */ - DPRINT("Drive not ready\n"); - DiskData->DriveNotReady = TRUE; - } - else - { - ExFreePool(PartitionList); - } - - /* Allocate a partition list for a single entry. */ - PartitionList = ExAllocatePool(NonPagedPool, - sizeof(DRIVE_LAYOUT_INFORMATION)); - if (PartitionList != NULL) - { - RtlZeroMemory(PartitionList, - sizeof(DRIVE_LAYOUT_INFORMATION)); - PartitionList->PartitionCount = 1; - - Status = STATUS_SUCCESS; - } - } - } - - if (NT_SUCCESS(Status)) - { - DPRINT("Read partition table!\n"); - DPRINT(" Number of partitions: %u\n", PartitionList->PartitionCount); - - /* Set disk signature */ - DiskData->Signature = PartitionList->Signature; - - /* Calculate MBR checksum if disk got no signature */ - if (DiskData->Signature == 0) - { - if (!ScsiDiskCalcMbrCheckSum(DiskDeviceExtension, - &DiskData->MbrCheckSum)) - { - DPRINT("MBR checksum calculation failed for disk %lu\n", - DiskDeviceExtension->DeviceNumber); - } - else - { - DPRINT("MBR checksum for disk %lu is %lx\n", - DiskDeviceExtension->DeviceNumber, - DiskData->MbrCheckSum); - } - } - else - { - DPRINT("Signature on disk %lu is %lx\n", - DiskDeviceExtension->DeviceNumber, - DiskData->Signature); - } - - /* Update disk geometry if disk is visible to the BIOS */ - ScsiDiskUpdateFixedDiskGeometry(DiskDeviceExtension); - - for (PartitionNumber = 0; PartitionNumber < PartitionList->PartitionCount; PartitionNumber++) - { - PartitionEntry = &PartitionList->PartitionEntry[PartitionNumber]; - - DPRINT("Partition %02ld: nr: %d boot: %1x type: %x offset: %I64d size: %I64d\n", - PartitionNumber, - PartitionEntry->PartitionNumber, - PartitionEntry->BootIndicator, - PartitionEntry->PartitionType, - PartitionEntry->StartingOffset.QuadPart / - DiskDeviceExtension->DiskGeometry->BytesPerSector, - PartitionEntry->PartitionLength.QuadPart / - DiskDeviceExtension->DiskGeometry->BytesPerSector); - - /* Create partition device object */ - sprintf(NameBuffer2, - "\\Device\\Harddisk%lu\\Partition%lu", - DiskNumber, - PartitionNumber + 1); - - Status = ScsiClassCreateDeviceObject(DriverObject, - NameBuffer2, - DiskDeviceObject, - &PartitionDeviceObject , - InitializationData); - DPRINT("ScsiClassCreateDeviceObject(): Status %x\n", Status); - if (NT_SUCCESS(Status)) - { - PartitionDeviceObject->Flags = DiskDeviceObject->Flags; - PartitionDeviceObject->Characteristics = DiskDeviceObject->Characteristics; - PartitionDeviceObject->StackSize = DiskDeviceObject->StackSize; - PartitionDeviceObject->AlignmentRequirement = DiskDeviceObject->AlignmentRequirement; - - PartitionDeviceExtension = PartitionDeviceObject->DeviceExtension; - PartitionDeviceExtension->SenseData = DiskDeviceExtension->SenseData; - PartitionDeviceExtension->LockCount = 0; - PartitionDeviceExtension->DeviceNumber = DiskNumber; - PartitionDeviceExtension->DeviceObject = PartitionDeviceObject; - PartitionDeviceExtension->PortDeviceObject = PortDeviceObject; - PartitionDeviceExtension->DiskGeometry = DiskDeviceExtension->DiskGeometry; - PartitionDeviceExtension->PhysicalDevice = DiskDeviceExtension->PhysicalDevice; - PartitionDeviceExtension->PortCapabilities = Capabilities; - PartitionDeviceExtension->StartingOffset.QuadPart = - PartitionEntry->StartingOffset.QuadPart; - PartitionDeviceExtension->PartitionLength.QuadPart = - PartitionEntry->PartitionLength.QuadPart; - PartitionDeviceExtension->DMSkew = DiskDeviceExtension->DMSkew; - PartitionDeviceExtension->DMByteSkew = DiskDeviceExtension->DMByteSkew; - PartitionDeviceExtension->DMActive = DiskDeviceExtension->DMActive; - PartitionDeviceExtension->PortNumber = (UCHAR)PortNumber; - PartitionDeviceExtension->PathId = InquiryData->PathId; - PartitionDeviceExtension->TargetId = InquiryData->TargetId; - PartitionDeviceExtension->Lun = InquiryData->Lun; - PartitionDeviceExtension->SectorShift = DiskDeviceExtension->SectorShift; - PartitionDeviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT; - - /* Initialize lookaside list for SRBs */ - ScsiClassInitializeSrbLookasideList(PartitionDeviceExtension, - 8); - - /* Link current partition device extension to previous disk data */ - DiskData->NextPartition = PartitionDeviceExtension; - - /* Initialize current disk data */ - DiskData = (PDISK_DATA)(PartitionDeviceExtension + 1); - DiskData->NextPartition = NULL; - DiskData->PartitionType = PartitionEntry->PartitionType; - DiskData->PartitionNumber = PartitionNumber + 1; - DiskData->PartitionOrdinal = PartitionNumber + 1; - DiskData->HiddenSectors = PartitionEntry->HiddenSectors; - DiskData->BootIndicator = PartitionEntry->BootIndicator; - DiskData->DriveNotReady = FALSE; - - /* Tell mount manager that we have a new partition for him. This should be replaced - * by the registration of MOUNTDEV_MOUNTED_DEVICE_GUID interface on partition PDO. - */ - { - UNICODE_STRING MountManagerU = RTL_CONSTANT_STRING(MOUNTMGR_DEVICE_NAME); - IO_STATUS_BLOCK IoStatusBlock; - ULONG Length = strlen(NameBuffer2) * sizeof(WCHAR); - PMOUNTMGR_TARGET_NAME TargetName; - HANDLE hMountManager; - InitializeObjectAttributes( - &ObjectAttributes, - &MountManagerU, - 0, - NULL, - NULL); - Status = NtOpenFile(&hMountManager, FILE_READ_ACCESS, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN); - if (!NT_SUCCESS(Status)) - { - DPRINT("NtOpenFile(%wZ) failed with status 0x%08lx\n", &MountManagerU, Status); - } - else - { - TargetName = ExAllocatePool(PagedPool, FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + Length + sizeof(UNICODE_NULL)); - if (!TargetName) - { - DPRINT("ExAllocatePool() failed\n"); - } - else - { - TargetName->DeviceNameLength = Length; - swprintf( - TargetName->DeviceName, - L"\\Device\\Harddisk%lu\\Partition%lu", - DiskNumber, - PartitionNumber + 1); - Status = NtDeviceIoControlFile( - hMountManager, - NULL, - NULL, - NULL, - &IoStatusBlock, - IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, - TargetName, FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + Length, - NULL, 0); - if (NT_SUCCESS(Status)) - Status = NtWaitForSingleObject(hMountManager, FALSE, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT("NtDeviceIoControlFile() failed with status 0x%08lx\n", Status); - } - ExFreePool(TargetName); - } - NtClose(hMountManager); - } - } - } - else - { - DPRINT("ScsiClassCreateDeviceObject() failed to create partition device object (Status %x)\n", Status); - - break; - } - } - } - - if (PartitionList != NULL) - ExFreePool(PartitionList); - - DiskDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - - DPRINT("DiskClassCreateDeviceObjects() done\n"); - - return(STATUS_SUCCESS); -} - - -static NTSTATUS -DiskBuildPartitionTable(IN PDEVICE_OBJECT DiskDeviceObject, - IN PIRP Irp) -{ - PDRIVE_LAYOUT_INFORMATION PartitionList = NULL; - PDEVICE_EXTENSION DiskDeviceExtension, DDE; - PDISK_DATA DiskData, DD; - PPARTITION_INFORMATION PartitionEntry; - ULONG PartitionNumber; - NTSTATUS Status; - - DPRINT("DiskBuildPartitionTable() start\n"); - - DiskDeviceExtension = (PDEVICE_EXTENSION)DiskDeviceObject->DeviceExtension; - DiskData = (PDISK_DATA)(DiskDeviceExtension + 1); - - DDE = (PDEVICE_EXTENSION) DiskDeviceExtension->PhysicalDevice->DeviceExtension; - DD = (PDISK_DATA)(DDE +1); - - /* Clear flag for Partition0, just incase it was set. */ - DD->DriveNotReady = FALSE; - - Status = ScsiClassReadDriveCapacity(DiskDeviceObject); - if (!NT_SUCCESS(Status)) - { - /* Drive is not ready. */ - DPRINT("Drive not ready\n"); - DiskData->DriveNotReady = TRUE; - return Status; - } - - /* Read partition table */ - Status = IoReadPartitionTable(DiskDeviceExtension->PhysicalDevice, - DiskDeviceExtension->DiskGeometry->BytesPerSector, - TRUE, - &PartitionList); - - DPRINT("IoReadPartitionTable(): Status: %lx\n", Status); - - if (!NT_SUCCESS(Status)) - { - /* Drive is not ready. */ - DPRINT("Drive not ready\n"); - DiskData->DriveNotReady = TRUE; - if (PartitionList != NULL) - ExFreePool(PartitionList); - return Status; - } - - if (NT_SUCCESS(Status)) - { - DPRINT("Read partition table!\n"); - DPRINT(" Number of partitions: %u\n", PartitionList->PartitionCount); - - /* Set disk signature */ - DiskData->Signature = PartitionList->Signature; - - DiskData->NextPartition = NULL; - - if (PartitionList->PartitionCount) - { - for (PartitionNumber = 0; PartitionNumber < PartitionList->PartitionCount; PartitionNumber++) - { - PartitionEntry = &PartitionList->PartitionEntry[PartitionNumber]; - - DiskData->PartitionType = PartitionEntry->PartitionType; - DiskData->PartitionNumber = PartitionNumber + 1; - DiskData->PartitionOrdinal = PartitionNumber + 1; - DiskData->HiddenSectors = PartitionEntry->HiddenSectors; - DiskData->BootIndicator = PartitionEntry->BootIndicator; - DiskData->DriveNotReady = FALSE; - DiskDeviceExtension->StartingOffset = PartitionEntry->StartingOffset; - DiskDeviceExtension->PartitionLength = PartitionEntry->PartitionLength; - - DPRINT1("Partition %02ld: nr: %d boot: %1x type: %x offset: %I64d size: %I64d\n", - PartitionNumber, - DiskData->PartitionNumber, - DiskData->BootIndicator, - DiskData->PartitionType, - DiskDeviceExtension->StartingOffset.QuadPart / - DiskDeviceExtension->DiskGeometry->BytesPerSector, - DiskDeviceExtension->PartitionLength.QuadPart / - DiskDeviceExtension->DiskGeometry->BytesPerSector); - } - } - else - { - DiskData->PartitionType = 0; - DiskData->PartitionNumber = 1; - DiskData->PartitionOrdinal = 0; - DiskData->HiddenSectors = 0; - DiskData->BootIndicator = 0; - DiskData->DriveNotReady = FALSE; - DiskDeviceExtension->StartingOffset.QuadPart = 0; - DiskDeviceExtension->PartitionLength.QuadPart += DiskDeviceExtension->StartingOffset.QuadPart; - } - } - - DPRINT("DiskBuildPartitionTable() done\n"); - if (PartitionList != NULL) - ExFreePool(PartitionList); - return(STATUS_SUCCESS); -} - - -/********************************************************************** - * NAME EXPORTED - * DiskClassDeviceControl - * - * DESCRIPTION - * Answer requests for device control calls - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * Standard dispatch arguments - * - * RETURNS - * Status - */ - -NTSTATUS STDCALL -DiskClassDeviceControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION IrpStack; - ULONG ControlCode, InputLength, OutputLength; - PDISK_DATA DiskData; - ULONG Information; - NTSTATUS Status; - KEVENT Event; - IO_STATUS_BLOCK IoSB; - PIRP LocalIrp; - PSRB_IO_CONTROL SrbIoControl; - PSENDCMDINPARAMS InParams; - - DPRINT("DiskClassDeviceControl() called!\n"); - - Status = STATUS_INVALID_DEVICE_REQUEST; - Information = 0; - IrpStack = IoGetCurrentIrpStackLocation(Irp); - ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode; - InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; - OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; - DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - DiskData = (PDISK_DATA)(DeviceExtension + 1); - - switch (ControlCode) - { - case IOCTL_DISK_GET_DRIVE_GEOMETRY: - DPRINT("IOCTL_DISK_GET_DRIVE_GEOMETRY\n"); - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) - { - Status = STATUS_INVALID_PARAMETER; - break; - } - - if (DeviceExtension->DiskGeometry == NULL) - { - DPRINT("No disk geometry available!\n"); - DeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool, - sizeof(DISK_GEOMETRY)); - } - - if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - { - Status = ScsiClassReadDriveCapacity(DeviceObject); - DPRINT("ScsiClassReadDriveCapacity() returned (Status %lx)\n", Status); - if (!NT_SUCCESS(Status)) - { - /* Drive is not ready */ - DiskData->DriveNotReady = TRUE; - break; - } - - /* Drive is ready */ - DiskData->DriveNotReady = FALSE; - } - - RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, - DeviceExtension->DiskGeometry, - sizeof(DISK_GEOMETRY)); - - Status = STATUS_SUCCESS; - Information = sizeof(DISK_GEOMETRY); - break; - - case IOCTL_DISK_GET_PARTITION_INFO: - DPRINT("IOCTL_DISK_GET_PARTITION_INFO\n"); - - if ((DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) && - (DeviceExtension->DiskGeometry->MediaType == RemovableMedia)) - { - /* Update a partition list for a single entry. */ - Status = DiskBuildPartitionTable(DeviceObject,Irp); - } - - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(PARTITION_INFORMATION)) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - } - //else if (DiskData->PartitionNumber == 0) -// { -// Status = STATUS_INVALID_DEVICE_REQUEST; - //} - else - { - PPARTITION_INFORMATION PartitionInfo; - - PartitionInfo = (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; - - PartitionInfo->PartitionType = DiskData->PartitionType; - PartitionInfo->StartingOffset = DeviceExtension->StartingOffset; - PartitionInfo->PartitionLength = DeviceExtension->PartitionLength; - PartitionInfo->HiddenSectors = DiskData->HiddenSectors; - PartitionInfo->PartitionNumber = DiskData->PartitionNumber; - PartitionInfo->BootIndicator = DiskData->BootIndicator; - PartitionInfo->RewritePartition = FALSE; - PartitionInfo->RecognizedPartition = - IsRecognizedPartition(DiskData->PartitionType); - - Status = STATUS_SUCCESS; - Information = sizeof(PARTITION_INFORMATION); - } - break; - - case IOCTL_DISK_SET_PARTITION_INFO: - if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(SET_PARTITION_INFORMATION)) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - } - else if (DiskData->PartitionNumber == 0) - { - Status = STATUS_INVALID_DEVICE_REQUEST; - } - else - { - PSET_PARTITION_INFORMATION PartitionInfo; - - PartitionInfo = (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; - - Status = IoSetPartitionInformation(DeviceExtension->PhysicalDevice, - DeviceExtension->DiskGeometry->BytesPerSector, - DiskData->PartitionOrdinal, - PartitionInfo->PartitionType); - if (NT_SUCCESS(Status)) - { - DiskData->PartitionType = PartitionInfo->PartitionType; - } - } - break; - - case IOCTL_DISK_GET_DRIVE_LAYOUT: - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(DRIVE_LAYOUT_INFORMATION)) - { - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - PDRIVE_LAYOUT_INFORMATION PartitionList; - - Status = IoReadPartitionTable(DeviceExtension->PhysicalDevice, - DeviceExtension->DiskGeometry->BytesPerSector, - FALSE, - &PartitionList); - if (NT_SUCCESS(Status)) - { - ULONG BufferSize; - - BufferSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, - PartitionEntry[0]); - BufferSize += PartitionList->PartitionCount * sizeof(PARTITION_INFORMATION); - - if (BufferSize > IrpStack->Parameters.DeviceIoControl.OutputBufferLength) - { - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, - PartitionList, - BufferSize); - Status = STATUS_SUCCESS; - Information = BufferSize; - } - ExFreePool(PartitionList); - } - } - break; - - case IOCTL_DISK_SET_DRIVE_LAYOUT: - if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(DRIVE_LAYOUT_INFORMATION)) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - } - else if (DeviceExtension->PhysicalDevice->DeviceExtension != DeviceExtension) - { - Status = STATUS_INVALID_PARAMETER; - } - else - { - PDRIVE_LAYOUT_INFORMATION PartitionList; - ULONG TableSize; - - PartitionList = Irp->AssociatedIrp.SystemBuffer; - TableSize = sizeof(DRIVE_LAYOUT_INFORMATION) + - ((PartitionList->PartitionCount - 1) * sizeof(PARTITION_INFORMATION)); - - if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < TableSize) - { - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - /* Update partition device objects */ - DiskClassUpdatePartitionDeviceObjects (DeviceObject, - Irp); - - /* Write partition table */ - Status = IoWritePartitionTable(DeviceExtension->PhysicalDevice, - DeviceExtension->DiskGeometry->BytesPerSector, - DeviceExtension->DiskGeometry->SectorsPerTrack, - DeviceExtension->DiskGeometry->TracksPerCylinder, - PartitionList); - } - } - break; - - case IOCTL_DISK_IS_WRITABLE: - { - PMODE_PARAMETER_HEADER ModeData; - ULONG Length; - - ModeData = ExAllocatePool (NonPagedPool, - MODE_DATA_SIZE); - if (ModeData == NULL) - { - Status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - RtlZeroMemory (ModeData, - MODE_DATA_SIZE); - - Length = ScsiClassModeSense (DeviceObject, - (PVOID)ModeData, - MODE_DATA_SIZE, - MODE_SENSE_RETURN_ALL); - if (Length < sizeof(MODE_PARAMETER_HEADER)) - { - /* FIXME: Retry */ - Status = STATUS_IO_DEVICE_ERROR; - ExFreePool (ModeData); - break; - } - - if (ModeData->DeviceSpecificParameter & MODE_DSP_WRITE_PROTECT) - { - Status = STATUS_MEDIA_WRITE_PROTECTED; - } - else - { - Status = STATUS_SUCCESS; - } - ExFreePool (ModeData); - } - break; - - case IOCTL_DISK_VERIFY: - case IOCTL_DISK_FORMAT_TRACKS: - case IOCTL_DISK_PERFORMANCE: - case IOCTL_DISK_LOGGING: - case IOCTL_DISK_FORMAT_TRACKS_EX: - case IOCTL_DISK_HISTOGRAM_STRUCTURE: - case IOCTL_DISK_HISTOGRAM_DATA: - case IOCTL_DISK_HISTOGRAM_RESET: - case IOCTL_DISK_REQUEST_STRUCTURE: - case IOCTL_DISK_REQUEST_DATA: - /* If we get here, something went wrong. Inform the requestor */ - DPRINT("Unhandled control code: %lx\n", ControlCode); - Status = STATUS_INVALID_DEVICE_REQUEST; - Information = 0; - break; - - case SMART_GET_VERSION: - { - PGETVERSIONINPARAMS Version; - DPRINT("SMART_GET_VERSION\n"); - if (OutputLength < sizeof(GETVERSIONINPARAMS)) - { - Status = STATUS_BUFFER_TOO_SMALL; - Information = sizeof(GETVERSIONINPARAMS); - break; + } } - SrbIoControl = ExAllocatePool(NonPagedPool, sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS)); - if (SrbIoControl == NULL) - { - Status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - Version = (PGETVERSIONINPARAMS)(SrbIoControl + 1); - memset(SrbIoControl, 0, sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS)); + // + // Get next LunInfo. + // - SrbIoControl->HeaderLength = sizeof(SRB_IO_CONTROL); - memcpy(SrbIoControl->Signature, "ScsiDisk", 8); - SrbIoControl->Timeout = DeviceExtension->TimeOutValue * 4; - SrbIoControl->Length = sizeof(GETVERSIONINPARAMS); - SrbIoControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION; - - KeInitializeEvent(&Event, NotificationEvent, FALSE); - LocalIrp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT, - DeviceExtension->PortDeviceObject, - SrbIoControl, - sizeof(SRB_IO_CONTROL), - SrbIoControl, - sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS), - FALSE, - &Event, - &IoSB); - if (LocalIrp == NULL) - { - ExFreePool(SrbIoControl); - Status = STATUS_INSUFFICIENT_RESOURCES; - break; + if (lunInfo->NextInquiryDataOffset == 0) { + break; } - Status = IoCallDriver(DeviceExtension->PortDeviceObject, LocalIrp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); - Status = IoSB.Status; - } - if (NT_SUCCESS(Status)) - { - memcpy(Irp->AssociatedIrp.SystemBuffer, Version, sizeof(GETVERSIONINPARAMS)); - Information = sizeof(GETVERSIONINPARAMS); - } - ExFreePool(SrbIoControl); - break; + lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset); + + } + } + + // + // Buffer is allocated by ScsiClassGetInquiryData and must be free returning. + // + + ExFreePool(buffer); + + return(foundOne); + +} // end FindScsiDisks() + + +NTSTATUS +STDCALL +CreateDiskDeviceObject( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath, + IN PDEVICE_OBJECT PortDeviceObject, + IN ULONG PortNumber, + IN PULONG DeviceCount, + IN PIO_SCSI_CAPABILITIES PortCapabilities, + IN PSCSI_INQUIRY_DATA LunInfo, + IN PCLASS_INIT_DATA InitData + ) + +/*++ + +Routine Description: + + This routine creates an object for the physical device and then searches + the device for partitions and creates an object for each partition. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + PortDeviceObject - Miniport device object. + + PortNumber - port number. Used in creating disk objects. + + DeviceCount - Number of previously installed devices. + + PortCapabilities - Capabilities of this SCSI port. + + LunInfo - LUN specific information. + +Return Value: + + NTSTATUS + +--*/ +{ + CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH]; + STRING ntNameString; + UNICODE_STRING ntUnicodeString; + ULONG partitionNumber = 0; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE handle; + NTSTATUS status; + PDEVICE_OBJECT deviceObject = NULL; + PDEVICE_OBJECT physicalDevice; + PDISK_GEOMETRY diskGeometry = NULL; + PDRIVE_LAYOUT_INFORMATION partitionList; + PDEVICE_EXTENSION deviceExtension; + PDEVICE_EXTENSION physicalDeviceExtension; + PDISK_DATA diskData; + ULONG bytesPerSector; + UCHAR sectorShift; + UCHAR pathId = LunInfo->PathId; + UCHAR targetId = LunInfo->TargetId; + UCHAR lun = LunInfo->Lun; + BOOLEAN writeCache; + PVOID senseData = NULL; + ULONG srbFlags; + ULONG dmByteSkew; + PULONG dmSkew; + BOOLEAN dmActive = FALSE; + ULONG timeOut = 0; + ULONG numberListElements = 0; + BOOLEAN srbListInitialized = FALSE; + + + PAGED_CODE(); + + // + // Set up an object directory to contain the objects for this + // device and all its partitions. + // + + sprintf(ntNameBuffer, + "\\Device\\Harddisk%d", + *DeviceCount); + + RtlInitString(&ntNameString, + ntNameBuffer); + + status = RtlAnsiStringToUnicodeString(&ntUnicodeString, + &ntNameString, + TRUE); + + if (!NT_SUCCESS(status)) { + return(status); + } + + InitializeObjectAttributes(&objectAttributes, + &ntUnicodeString, + OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, + NULL, + NULL); + + status = ZwCreateDirectoryObject(&handle, + DIRECTORY_ALL_ACCESS, + &objectAttributes); + + RtlFreeUnicodeString(&ntUnicodeString); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, + "CreateDiskDeviceObjects: Could not create directory %s\n", + ntNameBuffer)); + + return(status); + } + + // + // Claim the device. + // + + status = ScsiClassClaimDevice(PortDeviceObject, + LunInfo, + FALSE, + &PortDeviceObject); + + if (!NT_SUCCESS(status)) { + ZwMakeTemporaryObject(handle); + ZwClose(handle); + return status; + } + + // + // Create a device object for this device. Each physical disk will + // have at least one device object. The required device object + // describes the entire device. Its directory path is + // \Device\HarddiskN\Partition0, where N = device number. + // + + sprintf(ntNameBuffer, + "\\Device\\Harddisk%d\\Partition0", + *DeviceCount); + + + status = ScsiClassCreateDeviceObject(DriverObject, + ntNameBuffer, + NULL, + &deviceObject, + InitData); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, + "CreateDiskDeviceObjects: Can not create device object %s\n", + ntNameBuffer)); + + goto CreateDiskDeviceObjectsExit; + } + + // + // Indicate that IRPs should include MDLs for data transfers. + // + + deviceObject->Flags |= DO_DIRECT_IO; + + // + // Check if this is during initialization. If not indicate that + // system initialization already took place and this disk is ready + // to be accessed. + // + + if (!RegistryPath) { + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + } + + // + // Check for removable media support. + // + + if (((PINQUIRYDATA)LunInfo->InquiryData)->RemovableMedia) { + deviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; + } + + // + // Set up required stack size in device object. + // + + deviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1; + + deviceExtension = deviceObject->DeviceExtension; + + // + // Allocate spinlock for split request completion. + // + + KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock); + + // + // Initialize lock count to zero. The lock count is used to + // disable the ejection mechanism on devices that support + // removable media. Only the lock count in the physical + // device extension is used. + // + + deviceExtension->LockCount = 0; + + // + // Save system disk number. + // + + deviceExtension->DeviceNumber = *DeviceCount; + + // + // Copy port device object pointer to the device extension. + // + + deviceExtension->PortDeviceObject = PortDeviceObject; + + // + // Set the alignment requirements for the device based on the + // host adapter requirements + // + + if (PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) { + deviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement; + } + + // + // This is the physical device object. + // + + physicalDevice = deviceObject; + physicalDeviceExtension = deviceExtension; + + // + // Save address of port driver capabilities. + // + + deviceExtension->PortCapabilities = PortCapabilities; + + // + // Build the lookaside list for srb's for the physical disk. Should only + // need a couple. + // + + ScsiClassInitializeSrbLookasideList(deviceExtension, + PARTITION0_LIST_SIZE); + + srbListInitialized = TRUE; + + // + // Initialize the srb flags. + // + + if (((PINQUIRYDATA)LunInfo->InquiryData)->CommandQueue && + PortCapabilities->TaggedQueuing) { + + deviceExtension->SrbFlags = SRB_FLAGS_QUEUE_ACTION_ENABLE; + + } else { + + deviceExtension->SrbFlags = 0; + + } + + // + // Allow queued requests if this is not removable media. + // + + if (!(deviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) { + + deviceExtension->SrbFlags |= SRB_FLAGS_NO_QUEUE_FREEZE; + + } + + // + // Look for controller that require special flags. + // + + ScanForSpecial(deviceObject, + LunInfo, + PortCapabilities); + + srbFlags = deviceExtension->SrbFlags; + + // + // Allocate buffer for drive geometry. + // + + diskGeometry = ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY)); + + if (diskGeometry == NULL) { + + DebugPrint((1, + "CreateDiskDeviceObjects: Can not allocate disk geometry buffer\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto CreateDiskDeviceObjectsExit; + } + + deviceExtension->DiskGeometry = diskGeometry; + + // + // Allocate request sense buffer. + // + + senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE); + + if (senseData == NULL) { + + // + // The buffer can not be allocated. + // + + DebugPrint((1, + "CreateDiskDeviceObjects: Can not allocate request sense buffer\n")); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto CreateDiskDeviceObjectsExit; + } + + // + // Set the sense data pointer in the device extension. + // + + deviceExtension->SenseData = senseData; + + // + // Physical device object will describe the entire + // device, starting at byte offset 0. + // + + deviceExtension->StartingOffset.QuadPart = (LONGLONG)(0); + + // + // TargetId/LUN describes a device location on the SCSI bus. + // This information comes from the inquiry buffer. + // + + deviceExtension->PortNumber = (UCHAR)PortNumber; + deviceExtension->PathId = pathId; + deviceExtension->TargetId = targetId; + deviceExtension->Lun = lun; + + // + // Set timeout value in seconds. + // + + timeOut = ScsiClassQueryTimeOutRegistryValue(RegistryPath); + if (timeOut) { + deviceExtension->TimeOutValue = timeOut; + } else { + deviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT; + } + + // + // Back pointer to device object. + // + + deviceExtension->DeviceObject = deviceObject; + + // + // If this is a removable device, then make sure it is not a floppy. + // Perform a mode sense command to determine the media type. Note + // IsFloppyDevice also checks for write cache enabled. + // + + if (IsFloppyDevice(deviceObject) && deviceObject->Characteristics & FILE_REMOVABLE_MEDIA && + (((PINQUIRYDATA)LunInfo->InquiryData)->DeviceType == DIRECT_ACCESS_DEVICE)) { + + status = STATUS_NO_SUCH_DEVICE; + goto CreateDiskDeviceObjectsExit; + } + + DisableWriteCache(deviceObject,LunInfo); + + writeCache = deviceExtension->DeviceFlags & DEV_WRITE_CACHE; + + // + // NOTE: At this point one device object has been successfully created. + // from here on out return success. + // + + // + // Do READ CAPACITY. This SCSI command + // returns the number of bytes on a device. + // Device extension is updated with device size. + // + + status = ScsiClassReadDriveCapacity(deviceObject); + + // + // If the read capcity failed then just return, unless this is a + // removable disk where a device object partition needs to be created. + // + + if (!NT_SUCCESS(status) && + !(deviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) { + + DebugPrint((1, + "CreateDiskDeviceObjects: Can't read capacity for device %s\n", + ntNameBuffer)); + + return(STATUS_SUCCESS); + + } else { + + // + // Make sure the volume verification bit is off so that + // IoReadPartitionTable will work. + // + + deviceObject->Flags &= ~DO_VERIFY_VOLUME; + } + + // + // Set up sector size fields. + // + // Stack variables will be used to update + // the partition device extensions. + // + // The device extension field SectorShift is + // used to calculate sectors in I/O transfers. + // + // The DiskGeometry structure is used to service + // IOCTls used by the format utility. + // + + bytesPerSector = diskGeometry->BytesPerSector; + + // + // Make sure sector size is not zero. + // + + if (bytesPerSector == 0) { + + // + // Default sector size for disk is 512. + // + + bytesPerSector = diskGeometry->BytesPerSector = 512; + } + + sectorShift = deviceExtension->SectorShift; + + // + // Set pointer to disk data area that follows device extension. + // + + diskData = (PDISK_DATA)(deviceExtension + 1); + + // + // Determine is DM Driver is loaded on an IDE drive that is + // under control of Atapi - this could be either a crashdump or + // an Atapi device is sharing the controller with an IDE disk. + // + + HalExamineMBR(deviceExtension->DeviceObject, + deviceExtension->DiskGeometry->BytesPerSector, + (ULONG)0x54, + &dmSkew); + + if (dmSkew) { + + // + // Update the device extension, so that the call to IoReadPartitionTable + // will get the correct information. Any I/O to this disk will have + // to be skewed by *dmSkew sectors aka DMByteSkew. + // + + deviceExtension->DMSkew = *dmSkew; + deviceExtension->DMActive = TRUE; + deviceExtension->DMByteSkew = deviceExtension->DMSkew * bytesPerSector; + + // + // Save away the infomation that we need, since this deviceExtension will soon be + // blown away. + // + + dmActive = TRUE; + dmByteSkew = deviceExtension->DMByteSkew; + + } + + // + // Create objects for all the partitions on the device. + // + + status = IoReadPartitionTable(deviceObject, + deviceExtension->DiskGeometry->BytesPerSector, + TRUE, + (PVOID)&partitionList); + + // + // If the I/O read partition table failed and this is a removable device, + // then fix up the partition list to make it look like there is one + // zero length partition. + // + + if ((!NT_SUCCESS(status) || partitionList->PartitionCount == 0) && + deviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { + + if (!NT_SUCCESS(status)) { + + // + // Remember this disk is not ready. + // + + diskData->DriveNotReady = TRUE; + + } else { + + // + // Free the partition list allocated by IoReadPartitionTable. + // + + ExFreePool(partitionList); } - case SMART_SEND_DRIVE_COMMAND: - { - DPRINT("SMART_SEND_DRIVE_COMMAND\n"); + // + // Allocate and zero a partition list. + // - if (InputLength < sizeof(SENDCMDINPARAMS) - 1) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - break; + partitionList = ExAllocatePool(NonPagedPool, sizeof(*partitionList )); + + + if (partitionList != NULL) { + + RtlZeroMemory( partitionList, sizeof( *partitionList )); + + // + // Set the partition count to one and the status to success + // so one device object will be created. Set the partition type + // to a bogus value. + // + + partitionList->PartitionCount = 1; + + status = STATUS_SUCCESS; + } + } + + if (NT_SUCCESS(status)) { + + // + // Record disk signature. + // + + diskData->Signature = partitionList->Signature; + + // + // If disk signature is zero, then calculate the MBR checksum. + // + + if (!diskData->Signature) { + + if (!CalculateMbrCheckSum(deviceExtension, + &diskData->MbrCheckSum)) { + + DebugPrint((1, + "SCSIDISK: Can't calculate MBR checksum for disk %x\n", + deviceExtension->DeviceNumber)); + } else { + + DebugPrint((2, + "SCSIDISK: MBR checksum for disk %x is %x\n", + deviceExtension->DeviceNumber, + diskData->MbrCheckSum)); + } + } + + + // + // Check the registry and determine if the BIOS knew about this drive. If + // it did then update the geometry with the BIOS information. + // + + UpdateGeometry(deviceExtension); + + // + // Create device objects for the device partitions (if any). + // PartitionCount includes physical device partition 0, + // so only one partition means no objects to create. + // + + DebugPrint((2, + "CreateDiskDeviceObjects: Number of partitions is %d\n", + partitionList->PartitionCount)); + + for (partitionNumber = 0; partitionNumber < + partitionList->PartitionCount; partitionNumber++) { + + // + // Create partition object and set up partition parameters. + // + + sprintf(ntNameBuffer, + "\\Device\\Harddisk%d\\Partition%d", + *DeviceCount, + partitionNumber + 1); + + DebugPrint((2, + "CreateDiskDeviceObjects: Create device object %s\n", + ntNameBuffer)); + + status = ScsiClassCreateDeviceObject(DriverObject, + ntNameBuffer, + physicalDevice, + &deviceObject, + InitData); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, "CreateDiskDeviceObjects: Can't create device object for %s\n", ntNameBuffer)); + + break; } - InParams = (PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer; - if (InParams->irDriveRegs.bCommandReg == SMART_CMD) - { - if (InParams->irDriveRegs.bFeaturesReg == ENABLE_DISABLE_AUTOSAVE) - { - DPRINT("IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE\n"); - ControlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; - Information = sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bFeaturesReg == SAVE_ATTRIBUTE_VALUES) - { - DPRINT("IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES\n"); - ControlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; - Information = sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bFeaturesReg == EXECUTE_OFFLINE_DIAGS) - { - DPRINT("IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS\n"); - ControlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; - Information = sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bFeaturesReg == SMART_WRITE_LOG) - { - DPRINT("IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG\n"); - ControlCode = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; - if (InputLength < sizeof(SENDCMDINPARAMS) - 1 + 512 * max(1, InParams->irDriveRegs.bSectorCountReg)) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - break; + // + // Set up device object fields. + // + + deviceObject->Flags |= DO_DIRECT_IO; + + // + // Check if this is during initialization. If not indicate that + // system initialization already took place and this disk is ready + // to be accessed. + // + + if (!RegistryPath) { + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + } + + deviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1; + + // + // Set up device extension fields. + // + + deviceExtension = deviceObject->DeviceExtension; + + if (dmActive) { + + // + // Restore any saved DM values. + // + + deviceExtension->DMByteSkew = dmByteSkew; + deviceExtension->DMSkew = *dmSkew; + deviceExtension->DMActive = TRUE; + + } + + // + // Link new device extension to previous disk data + // to support dynamic partitioning. + // + + diskData->NextPartition = deviceExtension; + + // + // Get pointer to new disk data. + // + + diskData = (PDISK_DATA)(deviceExtension + 1); + + // + // Set next partition pointer to NULL in case this is the + // last partition. + // + + diskData->NextPartition = NULL; + + // + // Allocate spinlock for zoning for split-request completion. + // + + KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock); + + // + // Copy port device object pointer to device extension. + // + + deviceExtension->PortDeviceObject = PortDeviceObject; + deviceExtension->PortNumber = (UCHAR)PortNumber; + + // + // Set the alignment requirements for the device based on the + // host adapter requirements + // + + if (PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) { + deviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement; + } + + + if (srbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) { + numberListElements = 30; + } else { + numberListElements = 8; + } + + // + // Build the lookaside list for srb's for this partition based on + // whether the adapter and disk can do tagged queueing. + // + + ScsiClassInitializeSrbLookasideList(deviceExtension, + numberListElements); + + deviceExtension->SrbFlags = srbFlags; + + // + // Set the sense-data pointer in the device extension. + // + + deviceExtension->SenseData = senseData; + deviceExtension->PortCapabilities = PortCapabilities; + deviceExtension->DiskGeometry = diskGeometry; + diskData->PartitionOrdinal = diskData->PartitionNumber = partitionNumber + 1; + diskData->PartitionType = partitionList->PartitionEntry[partitionNumber].PartitionType; + diskData->BootIndicator = partitionList->PartitionEntry[partitionNumber].BootIndicator; + + DebugPrint((2, "CreateDiskDeviceObjects: Partition type is %x\n", + diskData->PartitionType)); + + deviceExtension->StartingOffset = partitionList->PartitionEntry[partitionNumber].StartingOffset; + deviceExtension->PartitionLength = partitionList->PartitionEntry[partitionNumber].PartitionLength; + diskData->HiddenSectors = partitionList->PartitionEntry[partitionNumber].HiddenSectors; + deviceExtension->PortNumber = (UCHAR)PortNumber; + deviceExtension->PathId = pathId; + deviceExtension->TargetId = targetId; + deviceExtension->Lun = lun; + + // + // Check for removable media support. + // + + if (((PINQUIRYDATA)LunInfo->InquiryData)->RemovableMedia) { + deviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; + } + + // + // Set timeout value in seconds. + // + + deviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT; + deviceExtension->DiskGeometry->BytesPerSector = bytesPerSector; + deviceExtension->SectorShift = sectorShift; + deviceExtension->DeviceObject = deviceObject; + deviceExtension->DeviceFlags |= physicalDeviceExtension->DeviceFlags; + + } // end for (partitionNumber) ... + + // + // Free the buffer allocated by reading the + // partition table. + // + + ExFreePool(partitionList); + + } else { + + DebugPrint((1, + "CreateDiskDeviceObjects: IoReadPartitionTable failed\n")); + + } // end if...else + + return(STATUS_SUCCESS); + +CreateDiskDeviceObjectsExit: + + // + // Release the device since an error occurred. + // + + ScsiClassClaimDevice(PortDeviceObject, + LunInfo, + TRUE, + NULL); + + if (diskGeometry != NULL) { + ExFreePool(diskGeometry); + } + + if (senseData != NULL) { + ExFreePool(senseData); + } + + if (deviceObject != NULL) { + + if (srbListInitialized) { + ExDeleteNPagedLookasideList(&deviceExtension->SrbLookasideListHead); + } + + IoDeleteDevice(deviceObject); + } + + // + // Delete directory and return. + // + + if (!NT_SUCCESS(status)) { + ZwMakeTemporaryObject(handle); + } + + ZwClose(handle); + + return(status); + +} // end CreateDiskDeviceObjects() + + +NTSTATUS +STDCALL +ScsiDiskReadWriteVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + I/O System entry for read and write requests to SCSI disks. + +Arguments: + + DeviceObject - Pointer to driver object created by system. + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + LARGE_INTEGER startingOffset; + ULONG maximumTransferLength = + deviceExtension->PortCapabilities->MaximumTransferLength; + ULONG transferPages; + + // + // Verify parameters of this request. + // Check that ending sector is within partition and + // that number of bytes to transfer is a multiple of + // the sector size. + // + + startingOffset.QuadPart = (currentIrpStack->Parameters.Read.ByteOffset.QuadPart + + transferByteCount); + + if ((startingOffset.QuadPart > deviceExtension->PartitionLength.QuadPart) || + (transferByteCount & (deviceExtension->DiskGeometry->BytesPerSector - 1))) { + + // + // This error maybe caused by the fact that the drive is not ready. + // + + if (((PDISK_DATA)(deviceExtension + 1))->DriveNotReady) { + + // + // Flag this as a user errror so that a popup is generated. + // + + Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY; + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + + } else { + + // + // Note fastfat depends on this parameter to determine when to + // remount do to a sector size change. + // + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + } + + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; + +} // end ScsiDiskReadWrite() + + +NTSTATUS +STDCALL +ScsiDiskDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + I/O system entry for device controls to SCSI disks. + +Arguments: + + DeviceObject - Pointer to driver object created by system. + Irp - IRP involved. + +Return Value: + + Status is returned. + +--*/ + +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(deviceExtension + 1); + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + PMODE_PARAMETER_HEADER modeData; + PIRP irp2; + ULONG length; + NTSTATUS status; + KEVENT event; + IO_STATUS_BLOCK ioStatus; + + PAGED_CODE(); + + srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE); + + if (srb == NULL) { + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Write zeros to Srb. + // + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + cdb = (PCDB)srb->Cdb; + + switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { + + case SMART_GET_VERSION: { + + PUCHAR buffer; + PSRB_IO_CONTROL srbControl; + PGETVERSIONINPARAMS versionParams; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(GETVERSIONINPARAMS)) { + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Create notification event object to be used to signal the + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + srbControl = ExAllocatePool(NonPagedPool, + sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS)); + + if (!srbControl) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // fill in srbControl fields + // + + srbControl->HeaderLength = sizeof(SRB_IO_CONTROL); + RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8); + srbControl->Timeout = deviceExtension->TimeOutValue; + srbControl->Length = sizeof(GETVERSIONINPARAMS); + srbControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION; + + // + // Point to the 'buffer' portion of the SRB_CONTROL + // + + buffer = (PUCHAR)srbControl; + buffer = (ULONG)buffer + srbControl->HeaderLength; + + // + // Ensure correct target is set in the cmd parameters. + // + + versionParams = (PGETVERSIONINPARAMS)buffer; + versionParams->bIDEDeviceMap = deviceExtension->TargetId; + + // + // Copy the IOCTL parameters to the srb control buffer area. + // + + RtlMoveMemory(buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(GETVERSIONINPARAMS)); + + + irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT, + deviceExtension->PortDeviceObject, + srbControl, + sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS), + srbControl, + sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS), + FALSE, + &event, + &ioStatus); + + if (irp2 == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(deviceExtension->PortDeviceObject, irp2); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + // + // If successful, copy the data received into the output buffer. + // This should only fail in the event that the IDE driver is older than this driver. + // + + if (NT_SUCCESS(status)) { + + buffer = (PUCHAR)srbControl; + buffer = (ULONG)buffer + srbControl->HeaderLength; + + RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, sizeof(GETVERSIONINPARAMS)); + Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS); + } + + ExFreePool(srbControl); + break; + } + + case SMART_RCV_DRIVE_DATA: { + + PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer); + ULONG controlCode = 0; + PSRB_IO_CONTROL srbControl; + PUCHAR buffer; + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + (sizeof(SENDCMDINPARAMS) - 1)) { + status = STATUS_INVALID_PARAMETER; + break; + + } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + (sizeof(SENDCMDOUTPARAMS) + 512 - 1)) { + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Create notification event object to be used to signal the + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + if (cmdInParameters->irDriveRegs.bCommandReg == ID_CMD) { + + length = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS); + controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY; + + } else if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) { + switch (cmdInParameters->irDriveRegs.bFeaturesReg) { + case READ_ATTRIBUTES: + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; + length = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS); + break; + case READ_THRESHOLDS: + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; + length = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS); + break; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + } else { + + status = STATUS_INVALID_PARAMETER; + } + + if (controlCode == 0) { + status = STATUS_INVALID_PARAMETER; + break; + } + + srbControl = ExAllocatePool(NonPagedPool, + sizeof(SRB_IO_CONTROL) + length); + + if (!srbControl) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // fill in srbControl fields + // + + srbControl->HeaderLength = sizeof(SRB_IO_CONTROL); + RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8); + srbControl->Timeout = deviceExtension->TimeOutValue; + srbControl->Length = length; + srbControl->ControlCode = controlCode; + + // + // Point to the 'buffer' portion of the SRB_CONTROL + // + + buffer = (PUCHAR)srbControl; + buffer = (ULONG)buffer + srbControl->HeaderLength; + + // + // Ensure correct target is set in the cmd parameters. + // + + cmdInParameters->bDriveNumber = deviceExtension->TargetId; + + // + // Copy the IOCTL parameters to the srb control buffer area. + // + + RtlMoveMemory(buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1); + + irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT, + deviceExtension->PortDeviceObject, + srbControl, + sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1, + srbControl, + sizeof(SRB_IO_CONTROL) + length, + FALSE, + &event, + &ioStatus); + + if (irp2 == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(deviceExtension->PortDeviceObject, irp2); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + // + // If successful, copy the data received into the output buffer + // + + buffer = (PUCHAR)srbControl; + buffer = (ULONG)buffer + srbControl->HeaderLength; + + if (NT_SUCCESS(status)) { + + RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length - 1); + Irp->IoStatus.Information = length - 1; + + } else { + + RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, (sizeof(SENDCMDOUTPARAMS) - 1)); + Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1; + + } + + ExFreePool(srbControl); + break; + + } + + case SMART_SEND_DRIVE_COMMAND: { + + PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer); + PSRB_IO_CONTROL srbControl; + ULONG controlCode = 0; + PUCHAR buffer; + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + (sizeof(SENDCMDINPARAMS) - 1)) { + status = STATUS_INVALID_PARAMETER; + break; + + } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + (sizeof(SENDCMDOUTPARAMS) - 1)) { + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Create notification event object to be used to signal the + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + length = 0; + + if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) { + switch (cmdInParameters->irDriveRegs.bFeaturesReg) { + + case ENABLE_SMART: + controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART; + break; + + case DISABLE_SMART: + controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART; + break; + + case RETURN_SMART_STATUS: + + // + // Ensure bBuffer is at least 2 bytes (to hold the values of + // cylinderLow and cylinderHigh). + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + (sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS))) { + + status = STATUS_INVALID_PARAMETER; + break; } - Information = sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bFeaturesReg == ENABLE_SMART) - { - DPRINT("IOCTL_SCSI_MINIPORT_ENABLE_SMART\n"); - ControlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART; - Information = sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bFeaturesReg == DISABLE_SMART) - { - DPRINT("IOCTL_SCSI_MINIPORT_DISABLE_SMART\n"); - ControlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART; - Information = sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) - { - DPRINT("IOCTL_SCSI_MINIPORT_RETURN_STATUS\n"); - ControlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS; - Information = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS); - } - else if (InParams->irDriveRegs.bFeaturesReg == ENABLE_DISABLE_AUTO_OFFLINE) - { - DPRINT("IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS\n"); - ControlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; - Information = sizeof(SENDCMDOUTPARAMS) - 1; - } - else - { - DPRINT("%x\n", InParams->irDriveRegs.bFeaturesReg); - Status = STATUS_INVALID_PARAMETER; - break; - } - } - else - { - Status = STATUS_INVALID_PARAMETER; - break; - } - if (OutputLength < Information) - { - Status = STATUS_BUFFER_TOO_SMALL; - break; - } - - SrbIoControl = ExAllocatePool(NonPagedPool, sizeof(SRB_IO_CONTROL) + max(Information, sizeof(SENDCMDINPARAMS) - 1)); - if (SrbIoControl == NULL) - { - Status = STATUS_INSUFFICIENT_RESOURCES; - Information = 0; - break; - } - memset(SrbIoControl, 0, sizeof(SRB_IO_CONTROL) + max(Information, sizeof(SENDCMDINPARAMS) - 1)); - SrbIoControl->HeaderLength = sizeof(SRB_IO_CONTROL); - memcpy(SrbIoControl->Signature, "SCSIDISK", 8); - SrbIoControl->Timeout = DeviceExtension->TimeOutValue * 4; - SrbIoControl->Length = Information; - SrbIoControl->ControlCode = ControlCode; + controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS; + length = sizeof(IDEREGS); + break; - InParams = (PSENDCMDINPARAMS)(SrbIoControl + 1); + case ENABLE_DISABLE_AUTOSAVE: + controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; + break; - memcpy(InParams, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1); - - InParams->bDriveNumber = DeviceExtension->TargetId; + case SAVE_ATTRIBUTE_VALUES: + controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; + break; - KeInitializeEvent(&Event, NotificationEvent, FALSE); - LocalIrp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT, - DeviceExtension->PortDeviceObject, - SrbIoControl, - sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1, - SrbIoControl, - sizeof(SRB_IO_CONTROL) + Information, - FALSE, - &Event, - &IoSB); - if (LocalIrp == NULL) - { - ExFreePool(SrbIoControl); - Information = 0; - Status = STATUS_INSUFFICIENT_RESOURCES; - break; + case EXECUTE_OFFLINE_DIAGS: + controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; + + default: + status = STATUS_INVALID_PARAMETER; + break; } - Status = IoCallDriver(DeviceExtension->PortDeviceObject, LocalIrp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); - Status = IoSB.Status; - } - if (NT_SUCCESS(Status)) - { - Information = SrbIoControl->Length; - } - else - { - Information = sizeof(SENDCMDOUTPARAMS) - 1; - } - memcpy(Irp->AssociatedIrp.SystemBuffer, InParams, Information); - ExFreePool(SrbIoControl); - break; + } else { + + status = STATUS_INVALID_PARAMETER; } - case SMART_RCV_DRIVE_DATA: + if (controlCode == 0) { + status = STATUS_INVALID_PARAMETER; + break; + } + + length += (sizeof(SENDCMDOUTPARAMS) > sizeof(SENDCMDINPARAMS)) ? sizeof(SENDCMDOUTPARAMS) : sizeof(SENDCMDINPARAMS);; + srbControl = ExAllocatePool(NonPagedPool, + sizeof(SRB_IO_CONTROL) + length); + + if (!srbControl) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // fill in srbControl fields + // + + srbControl->HeaderLength = sizeof(SRB_IO_CONTROL); + RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8); + srbControl->Timeout = deviceExtension->TimeOutValue; + srbControl->Length = length; + + // + // Point to the 'buffer' portion of the SRB_CONTROL + // + + buffer = (PUCHAR)srbControl; + buffer = (ULONG)buffer + srbControl->HeaderLength; + + // + // Ensure correct target is set in the cmd parameters. + // + + cmdInParameters->bDriveNumber = deviceExtension->TargetId; + + // + // Copy the IOCTL parameters to the srb control buffer area. + // + + RtlMoveMemory(buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1); + + srbControl->ControlCode = controlCode; + + irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT, + deviceExtension->PortDeviceObject, + srbControl, + sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1, + srbControl, + sizeof(SRB_IO_CONTROL) + length, + FALSE, + &event, + &ioStatus); + + if (irp2 == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(deviceExtension->PortDeviceObject, irp2); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + // + // Copy the data received into the output buffer. Since the status buffer + // contains error information also, always perform this copy. IO will will + // either pass this back to the app, or zero it, in case of error. + // + + buffer = (PUCHAR)srbControl; + buffer = (ULONG)buffer + srbControl->HeaderLength; + + // + // Update the return buffer size based on the sub-command. + // + + if (cmdInParameters->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) { + length = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS); + } else { + length = sizeof(SENDCMDOUTPARAMS) - 1; + } + + RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length); + Irp->IoStatus.Information = length; + + ExFreePool(srbControl); + break; + + } + + case IOCTL_DISK_GET_DRIVE_GEOMETRY: + + if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof( DISK_GEOMETRY ) ) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { + + // + // Issue ReadCapacity to update device extension + // with information for current media. + // + + status = ScsiClassReadDriveCapacity(deviceExtension->PhysicalDevice); + + if (!NT_SUCCESS(status)) { + + // + // Note the drive is not ready. + // + + diskData->DriveNotReady = TRUE; + + break; + } + + // + // Note the drive is now ready. + // + + diskData->DriveNotReady = FALSE; + } + + // + // Copy drive geometry information from device extension. + // + + RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, + deviceExtension->DiskGeometry, + sizeof(DISK_GEOMETRY)); + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + + break; + + case IOCTL_DISK_VERIFY: + { - DPRINT("SMART_RCV_DRIVE_DATA\n"); - if (InputLength < sizeof(SENDCMDINPARAMS) - 1) - { - Status = STATUS_INFO_LENGTH_MISMATCH; - break; - } + PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer; + LARGE_INTEGER byteOffset; + ULONG sectorOffset; + USHORT sectorCount; - InParams = (PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer; - if (InParams->irDriveRegs.bCommandReg == ID_CMD) - { - DPRINT("IOCTL_SCSI_MINIPORT_IDENTIFY\n"); - ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY; - Information = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bCommandReg == SMART_CMD) - { - if (InParams->irDriveRegs.bFeaturesReg == READ_ATTRIBUTES) - { - DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS\n"); - ControlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; - Information = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bFeaturesReg == READ_THRESHOLDS) - { - DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS\n"); - ControlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; - Information = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1; - } - else if (InParams->irDriveRegs.bFeaturesReg == SMART_READ_LOG) - { - DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_LOG\n"); - ControlCode = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; - Information = sizeof(SENDCMDOUTPARAMS) - 1 + 512 * max(1, InParams->irDriveRegs.bSectorCountReg); - } - else - { - DPRINT("%x\n", InParams->irDriveRegs.bFeaturesReg); - Status = STATUS_INVALID_PARAMETER; - break; - } - } - else - { - DPRINT("%x\n", InParams->irDriveRegs.bCommandReg); - Status = STATUS_INVALID_PARAMETER; - break; - } - if (OutputLength < Information) - { - Status = STATUS_BUFFER_TOO_SMALL; - break; - } - SrbIoControl = ExAllocatePool(NonPagedPool, sizeof(SRB_IO_CONTROL) + Information); - if (SrbIoControl == NULL) - { - Status = STATUS_INSUFFICIENT_RESOURCES; - Information = 0; - break; - } - memset(SrbIoControl, 0, sizeof(SRB_IO_CONTROL) + Information); + // + // Validate buffer length. + // - SrbIoControl->HeaderLength = sizeof(SRB_IO_CONTROL); - memcpy(SrbIoControl->Signature, "SCSIDISK", 8); - SrbIoControl->Timeout = DeviceExtension->TimeOutValue * 4; - SrbIoControl->Length = Information; - SrbIoControl->ControlCode = ControlCode; + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(VERIFY_INFORMATION)) { - InParams = (PSENDCMDINPARAMS)(SrbIoControl + 1); - - memcpy(InParams, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1); - - InParams->bDriveNumber = DeviceExtension->TargetId; - - KeInitializeEvent(&Event, NotificationEvent, FALSE); - LocalIrp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT, - DeviceExtension->PortDeviceObject, - SrbIoControl, - sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1, - SrbIoControl, - sizeof(SRB_IO_CONTROL) + Information, - FALSE, - &Event, - &IoSB); - if (LocalIrp == NULL) - { - ExFreePool(SrbIoControl); - Information = 0; - Status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - Status = IoCallDriver(DeviceExtension->PortDeviceObject, LocalIrp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); - Status = IoSB.Status; - } - if (NT_SUCCESS(Status)) - { -#if 0 - CHAR Buffer[256]; - ULONG i, j; - UCHAR sum = 0; - memset(Buffer, 0, sizeof(Buffer)); - for (i = 0; i < 512; i += 16) - { - for (j = 0; j < 16 && i + j < 512; j++) - { - sprintf(&Buffer[3*j], "%02x ", ((PSENDCMDOUTPARAMS)(SrbIoControl + 1))->bBuffer[i + j]); - sum += ((PSENDCMDOUTPARAMS)(SrbIoControl + 1))->bBuffer[i + j]; - } - for (j = 0; j < 16 && i + j < 512; j++) - { - sprintf(&Buffer[3*16 + j], "%c", isprint(((PSENDCMDOUTPARAMS)(SrbIoControl + 1))->bBuffer[i + j]) ? ((PSENDCMDOUTPARAMS)(SrbIoControl + 1))->bBuffer[i + j] : '.'); - } - DPRINT1("%04x %s\n", i, Buffer); - } - DPRINT1("Sum %02x\n", sum); -#endif - Information = SrbIoControl->Length; - - } - else - { - Information = sizeof(SENDCMDOUTPARAMS) -1; - } - memcpy(Irp->AssociatedIrp.SystemBuffer, InParams, Information); - ExFreePool(SrbIoControl); - break; + status = STATUS_INFO_LENGTH_MISMATCH; + break; } - case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: - { - WCHAR NameBuffer[80]; - PMOUNTDEV_NAME pMountDevName; - ULONG NameLength; - DPRINT("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n"); - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) - { - Status = STATUS_BUFFER_TOO_SMALL; - Information = 0; - } - else - { - pMountDevName = (PMOUNTDEV_NAME)Irp->AssociatedIrp.SystemBuffer; - swprintf(NameBuffer, - L"\\Device\\Harddisk%lu\\Partition%lu", - DeviceExtension->DeviceNumber, - ((PDISK_DATA)(DeviceExtension + 1))->PartitionNumber); - NameLength = wcslen(NameBuffer) * sizeof(WCHAR); - pMountDevName->NameLength = NameLength; - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < FIELD_OFFSET(MOUNTDEV_NAME, Name) + NameLength) - { - Information = sizeof(MOUNTDEV_NAME); - Status = STATUS_BUFFER_OVERFLOW; - } - else - { - RtlCopyMemory(pMountDevName->Name, NameBuffer, NameLength); - Information = FIELD_OFFSET(MOUNTDEV_NAME, Name) + NameLength; - Status = STATUS_SUCCESS; - } - } - break; + // + // Verify sectors + // + + srb->CdbLength = 10; + + cdb->CDB10.OperationCode = SCSIOP_VERIFY; + + // + // Add disk offset to starting sector. + // + + byteOffset.QuadPart = deviceExtension->StartingOffset.QuadPart + + verifyInfo->StartingOffset.QuadPart; + + // + // Convert byte offset to sector offset. + // + + sectorOffset = (ULONG)(byteOffset.QuadPart >> deviceExtension->SectorShift); + + // + // Convert ULONG byte count to USHORT sector count. + // + + sectorCount = (USHORT)(verifyInfo->Length >> deviceExtension->SectorShift); + + // + // Move little endian values into CDB in big endian format. + // + + cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)§orOffset)->Byte3; + cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)§orOffset)->Byte2; + cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)§orOffset)->Byte1; + cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)§orOffset)->Byte0; + + cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)§orCount)->Byte1; + cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)§orCount)->Byte0; + + // + // The verify command is used by the NT FORMAT utility and + // requests are sent down for 5% of the volume size. The + // request timeout value is calculated based on the number of + // sectors verified. + // + + srb->TimeOutValue = ((sectorCount + 0x7F) >> 7) * + deviceExtension->TimeOutValue; + + status = ScsiClassSendSrbAsynchronous(DeviceObject, + srb, + Irp, + NULL, + 0, + FALSE); + + return(status); + } - case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: - { - WCHAR NameBuffer[80]; - PMOUNTDEV_UNIQUE_ID pMountDevUniqueId; - ULONG UniqueIdLength; - DPRINT("IOCTL_MOUNTDEV_QUERY_UNIQUE_ID\n"); - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) - { - Status = STATUS_BUFFER_TOO_SMALL; - Information = 0; - } - else - { - pMountDevUniqueId = (PMOUNTDEV_UNIQUE_ID)Irp->AssociatedIrp.SystemBuffer; - swprintf(NameBuffer, - L"\\Device\\Harddisk%lu\\Partition%lu", - DeviceExtension->DeviceNumber, - ((PDISK_DATA)(DeviceExtension + 1))->PartitionNumber); - UniqueIdLength = wcslen(NameBuffer) * sizeof(WCHAR); - pMountDevUniqueId->UniqueIdLength = UniqueIdLength; - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + UniqueIdLength) - { - Information = sizeof(MOUNTDEV_UNIQUE_ID); - Status = STATUS_BUFFER_OVERFLOW; - } - else - { - RtlCopyMemory(pMountDevUniqueId->UniqueId, NameBuffer, UniqueIdLength); - Information = FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + UniqueIdLength; - Status = STATUS_SUCCESS; - } - } - break; + case IOCTL_DISK_GET_PARTITION_INFO: + + // + // Return the information about the partition specified by the device + // object. Note that no information is ever returned about the size + // or partition type of the physical disk, as this doesn't make any + // sense. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + } +#if 0 // HACK: ReactOS partition numbers must be wrong + else if (diskData->PartitionNumber == 0) { - default: - /* Call the common device control function */ - return(ScsiClassDeviceControl(DeviceObject, Irp)); - } + // + // Paritition zero is not a partition so this is not a + // reasonable request. + // - /* Verify the device if the user caused the error */ - if (!NT_SUCCESS(Status) && IoIsErrorUserInduced(Status)) - { - IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); - } + status = STATUS_INVALID_DEVICE_REQUEST; - Irp->IoStatus.Status = Status; - Irp->IoStatus.Information = Information; - IoCompleteRequest(Irp, - IO_NO_INCREMENT); - - return(Status); -} - - -/********************************************************************** - * NAME EXPORTED - * DiskClassShutdownFlush - * - * DESCRIPTION - * Answer requests for shutdown and flush calls. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceObject - * Pointer to the device. - * - * Irp - * Pointer to the IRP - * - * RETURN VALUE - * Status - */ - -NTSTATUS STDCALL -DiskClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION DeviceExtension; - PIO_STACK_LOCATION IrpStack; - PSCSI_REQUEST_BLOCK Srb; - - DPRINT("DiskClassShutdownFlush() called!\n"); - - DeviceExtension = DeviceObject->DeviceExtension; - - /* Allocate SRB */ - Srb = ExAllocatePool(NonPagedPool, - sizeof(SCSI_REQUEST_BLOCK)); - if (Srb == NULL) - { - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return(STATUS_INSUFFICIENT_RESOURCES); - } - - RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK)); - - - /* Set timeout */ - Srb->TimeOutValue = DeviceExtension->TimeOutValue * 4; - - /* Flush write cache */ - Srb->CdbLength = 10; - Srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE; - ScsiClassSendSrbSynchronous(DeviceObject, - Srb, - NULL, - 0, - TRUE); - - /* Get current stack location */ - IrpStack = IoGetCurrentIrpStackLocation(Irp); - - /* FIXME: Unlock removable media upon shutdown */ - - - /* No retry */ - IrpStack->Parameters.Others.Argument4 = (PVOID)0; - - /* Send shutdown or flush request to the port driver */ - Srb->CdbLength = 0; - if (IrpStack->MajorFunction == IRP_MJ_SHUTDOWN) - Srb->Function = SRB_FUNCTION_SHUTDOWN; - else - Srb->Function = SRB_FUNCTION_FLUSH; - - /* Init completion routine */ - IoSetCompletionRoutine(Irp, - ScsiClassIoComplete, - Srb, - TRUE, - TRUE, - TRUE); - - /* Prepare next stack location for a call to the port driver */ - IrpStack = IoGetNextIrpStackLocation(Irp); - IrpStack->MajorFunction = IRP_MJ_SCSI; - IrpStack->Parameters.Scsi.Srb = Srb; - Srb->OriginalRequest = Irp; - - /* Call port driver */ - return(IoCallDriver(DeviceExtension->PortDeviceObject, Irp)); -} - - -/********************************************************************** - * NAME INTERNAL - * DiskClassUpdatePartitionDeviceObjects - * - * DESCRIPTION - * Deletes, modifies or creates partition device objects. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceObject - * Pointer to the device. - * - * Irp - * Pointer to the IRP - * - * RETURN VALUE - * None - */ - -static VOID -DiskClassUpdatePartitionDeviceObjects(IN PDEVICE_OBJECT DiskDeviceObject, - IN PIRP Irp) -{ - PDRIVE_LAYOUT_INFORMATION PartitionList; - PPARTITION_INFORMATION PartitionEntry; - PDEVICE_EXTENSION DeviceExtension; - PDEVICE_EXTENSION DiskDeviceExtension; - PDISK_DATA DiskData; - ULONG PartitionCount; - ULONG PartitionOrdinal; - ULONG PartitionNumber; - ULONG LastPartitionNumber; - ULONG i; - BOOLEAN Found; - WCHAR NameBuffer[MAX_PATH]; - UNICODE_STRING DeviceName; - PDEVICE_OBJECT DeviceObject; - NTSTATUS Status; - - DPRINT("ScsiDiskUpdatePartitionDeviceObjects() called\n"); - - /* Get partition list */ - PartitionList = Irp->AssociatedIrp.SystemBuffer; - - /* Round partition count up by 4 */ - PartitionCount = ((PartitionList->PartitionCount + 3) / 4) * 4; - - /* Remove the partition numbers from the partition list */ - for (i = 0; i < PartitionCount; i++) - { - PartitionList->PartitionEntry[i].PartitionNumber = 0; - } - - DiskDeviceExtension = DiskDeviceObject->DeviceExtension; - - /* Traverse on-disk partition list */ - LastPartitionNumber = 0; - DeviceExtension = DiskDeviceExtension; - DiskData = (PDISK_DATA)(DeviceExtension + 1); - while (TRUE) - { - DeviceExtension = DiskData->NextPartition; - if (DeviceExtension == NULL) - break; - - /* Get disk data */ - DiskData = (PDISK_DATA)(DeviceExtension + 1); - - /* Update last partition number */ - if (DiskData->PartitionNumber > LastPartitionNumber) - LastPartitionNumber = DiskData->PartitionNumber; - - /* Ignore unused on-disk partitions */ - if (DeviceExtension->PartitionLength.QuadPart == 0ULL) - continue; - - Found = FALSE; - PartitionOrdinal = 0; - for (i = 0; i < PartitionCount; i++) - { - /* Get current partition entry */ - PartitionEntry = &PartitionList->PartitionEntry[i]; - - /* Ignore empty (aka unused) or extended partitions */ - if (PartitionEntry->PartitionType == PARTITION_ENTRY_UNUSED || - IsContainerPartition (PartitionEntry->PartitionType)) - continue; - - PartitionOrdinal++; - - /* Check for matching partition start offset and length */ - if ((PartitionEntry->StartingOffset.QuadPart != - DeviceExtension->StartingOffset.QuadPart) || - (PartitionEntry->PartitionLength.QuadPart != - DeviceExtension->PartitionLength.QuadPart)) - continue; - - DPRINT("Found matching partition entry for partition %lu\n", - DiskData->PartitionNumber); - - /* Found matching partition */ - Found = TRUE; - - /* Update partition number in partition list */ - PartitionEntry->PartitionNumber = DiskData->PartitionNumber; - break; - } - - if (Found == TRUE) - { - /* Get disk data for current partition */ - DiskData = (PDISK_DATA)(DeviceExtension + 1); - - /* Update partition type if partiton will be rewritten */ - if (PartitionEntry->RewritePartition == TRUE) - DiskData->PartitionType = PartitionEntry->PartitionType; - - /* Assign new partiton ordinal */ - DiskData->PartitionOrdinal = PartitionOrdinal; - - DPRINT("Partition ordinal %lu was assigned to partition %lu\n", - DiskData->PartitionOrdinal, - DiskData->PartitionNumber); - } - else - { - /* Delete this partition */ - DeviceExtension->PartitionLength.QuadPart = 0ULL; - - DPRINT("Deleting partition %lu\n", - DiskData->PartitionNumber); - } - } - - /* Traverse partiton list and create new partiton devices */ - PartitionOrdinal = 0; - for (i = 0; i < PartitionCount; i++) - { - /* Get current partition entry */ - PartitionEntry = &PartitionList->PartitionEntry[i]; - - /* Ignore empty (aka unused) or extended partitions */ - if (PartitionEntry->PartitionType == PARTITION_ENTRY_UNUSED || - IsContainerPartition (PartitionEntry->PartitionType)) - continue; - - PartitionOrdinal++; - - /* Ignore unchanged partition entries */ - if (PartitionEntry->RewritePartition == FALSE) - continue; - - /* Check for an unused device object */ - PartitionNumber = 0; - DeviceExtension = DiskDeviceExtension; - DiskData = (PDISK_DATA)(DeviceExtension + 1); - while (TRUE) - { - DeviceExtension = DiskData->NextPartition; - if (DeviceExtension == NULL) - break; - - /* Get partition disk data */ - DiskData = (PDISK_DATA)(DeviceExtension + 1); - - /* Found a free (unused) partition (device object) */ - if (DeviceExtension->PartitionLength.QuadPart == 0ULL) - { - PartitionNumber = DiskData->PartitionNumber; - break; - } - } - - if (PartitionNumber == 0) - { - /* Create a new partition device object */ - DPRINT("Create new partition device object\n"); - - /* Get new partiton number */ - LastPartitionNumber++; - PartitionNumber = LastPartitionNumber; - - /* Create partition device object */ - swprintf(NameBuffer, - L"\\Device\\Harddisk%lu\\Partition%lu", - DiskDeviceExtension->DeviceNumber, - PartitionNumber); - RtlInitUnicodeString(&DeviceName, - NameBuffer); - - Status = IoCreateDevice(DiskDeviceObject->DriverObject, - sizeof(DEVICE_EXTENSION) + sizeof(DISK_DATA), - &DeviceName, - FILE_DEVICE_DISK, - 0, - FALSE, - &DeviceObject); - if (!NT_SUCCESS(Status)) - { - DPRINT("IoCreateDevice() failed (Status %lx)\n", Status); - continue; - } - - DeviceObject->Flags |= DO_DIRECT_IO; - DeviceObject->StackSize = DiskDeviceObject->StackSize; - DeviceObject->Characteristics = DiskDeviceObject->Characteristics; - DeviceObject->AlignmentRequirement = DiskDeviceObject->AlignmentRequirement; - - /* Initialize device extension */ - DeviceExtension = DeviceObject->DeviceExtension; - RtlCopyMemory(DeviceExtension, - DiskDeviceObject->DeviceExtension, - sizeof(DEVICE_EXTENSION)); - DeviceExtension->DeviceObject = DeviceObject; - - /* Initialize lookaside list for SRBs */ - ScsiClassInitializeSrbLookasideList(DeviceExtension, - 8); - - /* Link current partition device extension to previous disk data */ - DiskData->NextPartition = DeviceExtension; - DiskData = (PDISK_DATA)(DeviceExtension + 1); - DiskData->NextPartition = NULL; - - DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - } - else - { - /* Reuse an existing partition device object */ - DPRINT("Reuse an exisiting partition device object\n"); - DiskData = (PDISK_DATA)(DeviceExtension + 1); - } - - /* Update partition data and device extension */ - DiskData->PartitionNumber = PartitionNumber; - DiskData->PartitionOrdinal = PartitionOrdinal; - DiskData->PartitionType = PartitionEntry->PartitionType; - DiskData->BootIndicator = PartitionEntry->BootIndicator; - DiskData->HiddenSectors = PartitionEntry->HiddenSectors; - DeviceExtension->StartingOffset = PartitionEntry->StartingOffset; - DeviceExtension->PartitionLength = PartitionEntry->PartitionLength; - - /* Update partition number in the partition list */ - PartitionEntry->PartitionNumber = PartitionNumber; - - DPRINT("Partition ordinal %lu was assigned to partition %lu\n", - DiskData->PartitionOrdinal, - DiskData->PartitionNumber); - } - - DPRINT("ScsiDiskUpdatePartitionDeviceObjects() done\n"); -} - - -/********************************************************************** - * NAME INTERNAL - * ScsiDiskSearchForDisk - * - * DESCRIPTION - * Searches the hardware tree for the given disk. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceExtension - * Disk device extension. - * - * BusKey - * Handle to the hardware bus key. - * - * DetectedDiskNumber - * Returned disk number. - * - * RETURN VALUE - * TRUE: Disk was found. - * FALSE: Search failed. - */ - -static BOOLEAN -ScsiDiskSearchForDisk(IN PDEVICE_EXTENSION DeviceExtension, - IN HANDLE BusKey, - OUT PULONG DetectedDiskNumber) -{ - PKEY_VALUE_FULL_INFORMATION ValueData; - OBJECT_ATTRIBUTES ObjectAttributes; - PDISK_DATA DiskData; - UNICODE_STRING IdentifierString; - UNICODE_STRING NameString; - HANDLE BusInstanceKey; - HANDLE ControllerKey; - HANDLE DiskKey; - HANDLE DiskInstanceKey; - ULONG BusNumber; - ULONG ControllerNumber; - ULONG DiskNumber; - ULONG Length; - WCHAR Buffer[32]; - BOOLEAN DiskFound; - NTSTATUS Status; - - DPRINT("ScsiDiskSearchForDiskData() called\n"); - - DiskFound = FALSE; - - /* Enumerate buses */ - for (BusNumber = 0; ; BusNumber++) - { - /* Open bus instance subkey */ - swprintf(Buffer, - L"%lu", - BusNumber); - - RtlInitUnicodeString(&NameString, - Buffer); - - InitializeObjectAttributes(&ObjectAttributes, - &NameString, - OBJ_CASE_INSENSITIVE, - BusKey, - NULL); - - Status = ZwOpenKey(&BusInstanceKey, - KEY_READ, - &ObjectAttributes); - if (!NT_SUCCESS(Status)) - { - break; - } - - /* Open 'DiskController' subkey */ - RtlInitUnicodeString(&NameString, - L"DiskController"); - - InitializeObjectAttributes(&ObjectAttributes, - &NameString, - OBJ_CASE_INSENSITIVE, - BusInstanceKey, - NULL); - - Status = ZwOpenKey(&ControllerKey, - KEY_READ, - &ObjectAttributes); - if (!NT_SUCCESS(Status)) - { - ZwClose(BusInstanceKey); - continue; - } - - /* Enumerate controllers */ - for (ControllerNumber = 0; ; ControllerNumber++) - { - /* Open 'DiskPeripheral' subkey */ - swprintf(Buffer, - L"%lu\\DiskPeripheral", - ControllerNumber); - - RtlInitUnicodeString(&NameString, - Buffer); - - InitializeObjectAttributes(&ObjectAttributes, - &NameString, - OBJ_CASE_INSENSITIVE, - ControllerKey, - NULL); - - Status = ZwOpenKey(&DiskKey, - KEY_READ, - &ObjectAttributes); - if (!NT_SUCCESS(Status)) - { - break; - } - - /* Enumerate disks */ - for (DiskNumber = 0; ; DiskNumber++) - { - /* Open disk instance subkey */ - swprintf(Buffer, - L"%lu", - DiskNumber); - - RtlInitUnicodeString(&NameString, - Buffer); - - InitializeObjectAttributes(&ObjectAttributes, - &NameString, - OBJ_CASE_INSENSITIVE, - DiskKey, - NULL); - - Status = ZwOpenKey(&DiskInstanceKey, - KEY_READ, - &ObjectAttributes); - if (!NT_SUCCESS(Status)) - { - break; - } - - DPRINT("Found disk key: bus %lu controller %lu disk %lu\n", - BusNumber, - ControllerNumber, - DiskNumber); - - /* Allocate data buffer */ - ValueData = ExAllocatePool(PagedPool, - 2048); - if (ValueData == NULL) - { - ZwClose(DiskInstanceKey); - continue; - } - - /* Get the 'Identifier' value */ - RtlInitUnicodeString(&NameString, - L"Identifier"); - Status = ZwQueryValueKey(DiskInstanceKey, - &NameString, - KeyValueFullInformation, - ValueData, - 2048, - &Length); - - ZwClose(DiskInstanceKey); - if (!NT_SUCCESS(Status)) - { - ExFreePool(ValueData); - continue; - } - - IdentifierString.Buffer = - (PWSTR)((PUCHAR)ValueData + ValueData->DataOffset); - IdentifierString.Length = (USHORT)ValueData->DataLength - 2; - IdentifierString.MaximumLength = (USHORT)ValueData->DataLength; - - DPRINT("DiskIdentifier: %wZ\n", - &IdentifierString); - - DiskData = (PDISK_DATA)(DeviceExtension + 1); - if (DiskData->Signature != 0) - { - /* Comapre disk signature */ - swprintf(Buffer, - L"%08lx", - DiskData->Signature); - if (!_wcsnicmp(Buffer, &IdentifierString.Buffer[9], 8)) - { - DPRINT("Found disk %lu\n", DiskNumber); - DiskFound = TRUE; - *DetectedDiskNumber = DiskNumber; - } - } - else - { - /* Comapre mbr checksum */ - swprintf(Buffer, - L"%08lx", - DiskData->MbrCheckSum); - if (!_wcsnicmp(Buffer, &IdentifierString.Buffer[0], 8)) - { - DPRINT("Found disk %lu\n", DiskNumber); - DiskFound = TRUE; - *DetectedDiskNumber = DiskNumber; - } - } - - ExFreePool(ValueData); - - ZwClose(DiskInstanceKey); - - if (DiskFound == TRUE) - break; - } - - ZwClose(DiskKey); - } - - ZwClose(ControllerKey); - ZwClose(BusInstanceKey); - } - - DPRINT("ScsiDiskSearchForDisk() done\n"); - - return DiskFound; -} - - -/********************************************************************** - * NAME INTERNAL - * DiskClassUpdateFixedDiskGeometry - * - * DESCRIPTION - * Updated the geometry of a disk if the disk can be accessed - * by the BIOS. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceExtension - * Disk device extension. - * - * RETURN VALUE - * None - */ - -static VOID -ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension) -{ - PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor; - PCM_INT13_DRIVE_PARAMETER DriveParameters; - PKEY_VALUE_FULL_INFORMATION ValueBuffer; - OBJECT_ATTRIBUTES ObjectAttributes; - UNICODE_STRING KeyName; - UNICODE_STRING ValueName; - HANDLE SystemKey; - HANDLE BusKey; - ULONG DiskNumber = 0; - ULONG Length; -#if 0 - ULONG i; + } #endif - ULONG Cylinders; - ULONG Sectors; - ULONG SectorsPerTrack; - ULONG TracksPerCylinder; - NTSTATUS Status; + else { - DPRINT("ScsiDiskUpdateFixedDiskGeometry() called\n"); + PPARTITION_INFORMATION outputBuffer; - RtlInitUnicodeString(&KeyName, - L"\\Registry\\Machine\\Hardware\\Description\\System"); + // + // Update the geometry in case it has changed. + // - InitializeObjectAttributes(&ObjectAttributes, - &KeyName, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); + status = UpdateRemovableGeometry (DeviceObject, Irp); - /* Open the adapter key */ - Status = ZwOpenKey(&SystemKey, - KEY_READ, - &ObjectAttributes); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwOpenKey() failed (Status %lx)\n", Status); - return; + if (!NT_SUCCESS(status)) { + + // + // Note the drive is not ready. + // + + diskData->DriveNotReady = TRUE; + break; + } + + // + // Note the drive is now ready. + // + + diskData->DriveNotReady = FALSE; +// HACK: ReactOS partition numbers must be wrong (>0 part) + if (diskData->PartitionType == 0 && (diskData->PartitionNumber > 0)) { + + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + outputBuffer = + (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + outputBuffer->PartitionType = diskData->PartitionType; + outputBuffer->StartingOffset = deviceExtension->StartingOffset; + outputBuffer->PartitionLength = deviceExtension->PartitionLength; + outputBuffer->HiddenSectors = diskData->HiddenSectors; + outputBuffer->PartitionNumber = diskData->PartitionNumber; + outputBuffer->BootIndicator = diskData->BootIndicator; + outputBuffer->RewritePartition = FALSE; + outputBuffer->RecognizedPartition = + IsRecognizedPartition(diskData->PartitionType); + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + } + + break; + + case IOCTL_DISK_SET_PARTITION_INFO: + + if (diskData->PartitionNumber == 0) { + + status = STATUS_UNSUCCESSFUL; + + } else { + + PSET_PARTITION_INFORMATION inputBuffer = + (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + // + // Validate buffer length. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(SET_PARTITION_INFORMATION)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + // + // The HAL routines IoGet- and IoSetPartitionInformation were + // developed before support of dynamic partitioning and therefore + // don't distinguish between partition ordinal (that is the order + // of a partition on a disk) and the partition number. (The + // partition number is assigned to a partition to identify it to + // the system.) Use partition ordinals for these legacy calls. + // + + status = IoSetPartitionInformation( + deviceExtension->PhysicalDevice, + deviceExtension->DiskGeometry->BytesPerSector, + diskData->PartitionOrdinal, + inputBuffer->PartitionType); + + if (NT_SUCCESS(status)) { + + diskData->PartitionType = inputBuffer->PartitionType; + } + } + + break; + + case IOCTL_DISK_GET_DRIVE_LAYOUT: + + // + // Return the partition layout for the physical drive. Note that + // the layout is returned for the actual physical drive, regardless + // of which partition was specified for the request. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(DRIVE_LAYOUT_INFORMATION)) { + status = STATUS_INFO_LENGTH_MISMATCH; + + } else { + + PDRIVE_LAYOUT_INFORMATION partitionList; + PDEVICE_EXTENSION physicalExtension = deviceExtension; + PPARTITION_INFORMATION partitionEntry; + PDISK_DATA diskData; + ULONG tempSize; + ULONG i; + + // + // Read partition information. + // + + status = IoReadPartitionTable(deviceExtension->PhysicalDevice, + deviceExtension->DiskGeometry->BytesPerSector, + FALSE, + &partitionList); + + if (!NT_SUCCESS(status)) { + break; + } + + // + // The disk layout has been returned in the partitionList + // buffer. Determine its size and, if the data will fit + // into the intermediatery buffer, return it. + // + + tempSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION,PartitionEntry[0]); + tempSize += partitionList->PartitionCount * + sizeof(PARTITION_INFORMATION); + + if (tempSize > + irpStack->Parameters.DeviceIoControl.OutputBufferLength) { + + status = STATUS_BUFFER_TOO_SMALL; + ExFreePool(partitionList); + break; + } + + // + // Walk partition list to associate partition numbers with + // partition entries. + // + + for (i = 0; i < partitionList->PartitionCount; i++) { + + // + // Walk partition chain anchored at physical disk extension. + // + + deviceExtension = physicalExtension; + diskData = (PDISK_DATA)(deviceExtension + 1); + + do { + + deviceExtension = diskData->NextPartition; + + // + // Check if this is the last partition in the chain. + // + + if (!deviceExtension) { + break; + } + + // + // Get the partition device extension from disk data. + // + + diskData = (PDISK_DATA)(deviceExtension + 1); + + // + // Check if this partition is not currently being used. + // + + if (!deviceExtension->PartitionLength.QuadPart) { + continue; + } + + partitionEntry = &partitionList->PartitionEntry[i]; + + // + // Check if empty, or describes extended partiton or hasn't changed. + // + + if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(partitionEntry->PartitionType)) { + continue; + } + + // + // Check if new partition starts where this partition starts. + // + + if (partitionEntry->StartingOffset.QuadPart != + deviceExtension->StartingOffset.QuadPart) { + continue; + } + + // + // Check if partition length is the same. + // + + if (partitionEntry->PartitionLength.QuadPart == + deviceExtension->PartitionLength.QuadPart) { + + // + // Partitions match. Update partition number. + // + + partitionEntry->PartitionNumber = + diskData->PartitionNumber; + break; + } + + } while (TRUE); + } + + // + // Copy partition information to system buffer. + // + + RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, + partitionList, + tempSize); + status = STATUS_SUCCESS; + Irp->IoStatus.Information = tempSize; + + // + // Finally, free the buffer allocated by reading the + // partition table. + // + + ExFreePool(partitionList); + } + + break; + + case IOCTL_DISK_SET_DRIVE_LAYOUT: + + { + + // + // Update the disk with new partition information. + // + + PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer; + + // + // Validate buffer length. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DRIVE_LAYOUT_INFORMATION)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + length = sizeof(DRIVE_LAYOUT_INFORMATION) + + (partitionList->PartitionCount - 1) * sizeof(PARTITION_INFORMATION); + + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + length) { + + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + // + // Verify that device object is for physical disk. + // + + if (deviceExtension->PhysicalDevice->DeviceExtension != deviceExtension) { + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Walk through partition table comparing partitions to + // existing partitions to create, delete and change + // device objects as necessary. + // + + UpdateDeviceObjects(DeviceObject, + Irp); + + // + // Write changes to disk. + // + + status = IoWritePartitionTable( + deviceExtension->DeviceObject, + deviceExtension->DiskGeometry->BytesPerSector, + deviceExtension->DiskGeometry->SectorsPerTrack, + deviceExtension->DiskGeometry->TracksPerCylinder, + partitionList); + } + + // + // Update IRP with bytes returned. + // + + if (NT_SUCCESS(status)) { + Irp->IoStatus.Information = length; + } + + break; + + case IOCTL_DISK_REASSIGN_BLOCKS: + + // + // Map defective blocks to new location on disk. + // + + { + + PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer; + ULONG bufferSize; + ULONG blockNumber; + ULONG blockCount; + + // + // Validate buffer length. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(REASSIGN_BLOCKS)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + bufferSize = sizeof(REASSIGN_BLOCKS) + + (badBlocks->Count - 1) * sizeof(ULONG); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + bufferSize) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + // + // Build the data buffer to be transferred in the input buffer. + // The format of the data to the device is: + // + // 2 bytes Reserved + // 2 bytes Length + // x * 4 btyes Block Address + // + // All values are big endian. + // + + badBlocks->Reserved = 0; + blockCount = badBlocks->Count; + + // + // Convert # of entries to # of bytes. + // + + blockCount *= 4; + badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF); + badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00); + + // + // Convert back to number of entries. + // + + blockCount /= 4; + + for (; blockCount > 0; blockCount--) { + + blockNumber = badBlocks->BlockNumber[blockCount-1]; + + REVERSE_BYTES((PFOUR_BYTE) &badBlocks->BlockNumber[blockCount-1], + (PFOUR_BYTE) &blockNumber); + } + + srb->CdbLength = 6; + + cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS; + + // + // Set timeout value. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue; + + status = ScsiClassSendSrbSynchronous(DeviceObject, + srb, + badBlocks, + bufferSize, + TRUE); + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + ExFreePool(srb); + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + } + + return(status); + + case IOCTL_DISK_IS_WRITABLE: + + // + // Determine if the device is writable. + // + + modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE); + + if (modeData == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlZeroMemory(modeData, MODE_DATA_SIZE); + + length = ScsiClassModeSense(DeviceObject, + (PUCHAR) modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + // + // Retry the request in case of a check condition. + // + + length = ScsiClassModeSense(DeviceObject, + (PUCHAR) modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + status = STATUS_IO_DEVICE_ERROR; + ExFreePool(modeData); + break; + } + } + + if (modeData->DeviceSpecificParameter & MODE_DSP_WRITE_PROTECT) { + status = STATUS_MEDIA_WRITE_PROTECTED; + } else { + status = STATUS_SUCCESS; + } + + ExFreePool(modeData); + break; + + case IOCTL_DISK_INTERNAL_SET_VERIFY: + + // + // If the caller is kernel mode, set the verify bit. + // + + if (Irp->RequestorMode == KernelMode) { + DeviceObject->Flags |= DO_VERIFY_VOLUME; + } + status = STATUS_SUCCESS; + break; + + case IOCTL_DISK_INTERNAL_CLEAR_VERIFY: + + // + // If the caller is kernel mode, clear the verify bit. + // + + if (Irp->RequestorMode == KernelMode) { + DeviceObject->Flags &= ~DO_VERIFY_VOLUME; + } + status = STATUS_SUCCESS; + break; + + case IOCTL_DISK_FIND_NEW_DEVICES: + + // + // Search for devices that have been powered on since the last + // device search or system initialization. + // + + DebugPrint((3,"CdRomDeviceControl: Find devices\n")); + status = DriverEntry(DeviceObject->DriverObject, + NULL); + + Irp->IoStatus.Status = status; + ExFreePool(srb); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; + + case IOCTL_DISK_MEDIA_REMOVAL: + + // + // If the disk is not removable then don't allow this command. + // + + if (!(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + // + // Fall through and let the class driver process the request. + // + + default: + + // + // Free the Srb, since it is not needed. + // + + ExFreePool(srb); + + // + // Pass the request to the common device control routine. + // + + return(ScsiClassDeviceControl(DeviceObject, Irp)); + + break; + + } // end switch( ... + + Irp->IoStatus.Status = status; + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); } - /* Allocate value buffer */ - ValueBuffer = ExAllocatePool(PagedPool, - 1024); - if (ValueBuffer == NULL) - { - DPRINT("Failed to allocate value buffer\n"); - ZwClose(SystemKey); - return; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + ExFreePool(srb); + return(status); + +} // end ScsiDiskDeviceControl() + +NTSTATUS +STDCALL +ScsiDiskShutdownFlush ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called for a shutdown and flush IRPs. These are sent by the + system before it actually shuts down or when the file system does a flush. + A synchronize cache command is sent to the device if it is write caching. + If the device is removable an unlock command will be sent. This routine + will sent a shutdown or flush Srb to the port driver. + +Arguments: + + DriverObject - Pointer to device object to being shutdown by system. + + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpStack; + PSCSI_REQUEST_BLOCK srb; + NTSTATUS status; + PCDB cdb; + + // + // Allocate SCSI request block. + // + + srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK)); + + if (srb == NULL) { + + // + // Set the status and complete the request. + // + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return(STATUS_INSUFFICIENT_RESOURCES); } - RtlInitUnicodeString(&ValueName, - L"Configuration Data"); + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); - /* Query 'Configuration Data' value */ - Status = ZwQueryValueKey(SystemKey, - &ValueName, - KeyValueFullInformation, - ValueBuffer, - 1024, - &Length); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status); - ExFreePool(ValueBuffer); - ZwClose(SystemKey); - return; + // + // Write length to SRB. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set SCSI bus address. + // + + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + + // + // Set timeout value and mark the request as not being a tagged request. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue * 4; + srb->QueueTag = SP_UNTAGGED; + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->SrbFlags = deviceExtension->SrbFlags; + + // + // If the write cache is enabled then send a synchronize cache request. + // + + if (deviceExtension->DeviceFlags & DEV_WRITE_CACHE) { + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->CdbLength = 10; + + srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE; + + status = ScsiClassSendSrbSynchronous(DeviceObject, + srb, + NULL, + 0, + TRUE); + + DebugPrint((1, "ScsiDiskShutdownFlush: Synchonize cache sent. Status = %lx\n", status )); } - /* Open the 'MultifunctionAdapter' subkey */ - RtlInitUnicodeString(&KeyName, - L"MultifunctionAdapter"); + // + // Unlock the device if it is removable and this is a shutdown. + // - InitializeObjectAttributes(&ObjectAttributes, - &KeyName, - OBJ_CASE_INSENSITIVE, - SystemKey, - NULL); + irpStack = IoGetCurrentIrpStackLocation(Irp); - Status = ZwOpenKey(&BusKey, - KEY_READ, - &ObjectAttributes); - ZwClose(SystemKey); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status); - ExFreePool(ValueBuffer); - return; + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA && + irpStack->MajorFunction == IRP_MJ_SHUTDOWN) { + + srb->CdbLength = 6; + cdb = (PVOID) srb->Cdb; + cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; + cdb->MEDIA_REMOVAL.Prevent = FALSE; + + // + // Set timeout value. + // + + srb->TimeOutValue = deviceExtension->TimeOutValue; + status = ScsiClassSendSrbSynchronous(DeviceObject, + srb, + NULL, + 0, + TRUE); + + DebugPrint((1, "ScsiDiskShutdownFlush: Unlock device request sent. Status = %lx\n", status )); } - if (!ScsiDiskSearchForDisk(DeviceExtension, BusKey, &DiskNumber)) - { - DPRINT("ScsiDiskSearchForDisk() failed\n"); - ZwClose(BusKey); - ExFreePool(ValueBuffer); - return; + srb->CdbLength = 0; + + // + // Save a few parameters in the current stack location. + // + + srb->Function = irpStack->MajorFunction == IRP_MJ_SHUTDOWN ? + SRB_FUNCTION_SHUTDOWN : SRB_FUNCTION_FLUSH; + + // + // Set the retry count to zero. + // + + irpStack->Parameters.Others.Argument4 = (PVOID) 0; + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, ScsiClassIoComplete, srb, TRUE, TRUE, TRUE); + + // + // Get next stack location and + // set major function code. + // + + irpStack = IoGetNextIrpStackLocation(Irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Set up SRB for execute scsi request. + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = srb; + + // + // Set up Irp Address. + // + + srb->OriginalRequest = Irp; + + // + // Call the port driver to process the request. + // + + return(IoCallDriver(deviceExtension->PortDeviceObject, Irp)); + +} // end ScsiDiskShutdown() + + +BOOLEAN +STDCALL +IsFloppyDevice( + PDEVICE_OBJECT DeviceObject + ) +/*++ + +Routine Description: + + The routine performs the necessary functions to determine 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. Also a check is + made to see if the write cache is enabled. + +Arguments: + + DeviceObject - Supplies the device object to be tested. + +Return Value: + + Return TRUE if the indicated device is a floppy. + +--*/ +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PVOID modeData; + PUCHAR pageData; + ULONG length; + + PAGED_CODE(); + + modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE); + + if (modeData == NULL) { + return(FALSE); } - ZwClose(BusKey); + RtlZeroMemory(modeData, MODE_DATA_SIZE); - ResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR) - ((PUCHAR)ValueBuffer + ValueBuffer->DataOffset); + length = ScsiClassModeSense(DeviceObject, + modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); - DriveParameters = (PCM_INT13_DRIVE_PARAMETER) - ((PUCHAR)ResourceDescriptor + sizeof(CM_FULL_RESOURCE_DESCRIPTOR)); + if (length < sizeof(MODE_PARAMETER_HEADER)) { -#if 0 - for (i = 0; i< DriveParameters[0].NumberDrives; i++) - { - DPRINT("Drive %lu: %lu Cylinders %hu Heads %hu Sectors\n", - i, - DriveParameters[i].MaxCylinders, - DriveParameters[i].MaxHeads, - DriveParameters[i].SectorsPerTrack); + // + // Retry the request in case of a check condition. + // + + length = ScsiClassModeSense(DeviceObject, + modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + ExFreePool(modeData); + return(FALSE); + + } + } + + // + // If the length is greater than length indicated by the mode data reset + // the data to the mode data. + // + + if (length > (ULONG) ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) { + length = ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1; + } + + // + // Look for the flexible disk mode page. + // + + pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE, TRUE); + + if (pageData != NULL) { + + DebugPrint((1, "Scsidisk: Flexible disk page found, This is a floppy.\n")); + ExFreePool(modeData); + return(TRUE); + } + + // + // Check to see if the write cache is enabled. + // + + pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_CACHING, TRUE); + + // + // Assume that write cache is disabled or not supported. + // + + deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE; + + // + // Check if valid caching page exists. + // + + if (pageData != NULL) { + + // + // Check if write cache is disabled. + // + + if (((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable) { + + DebugPrint((1, + "SCSIDISK: Disk write cache enabled\n")); + + // + // Check if forced unit access (FUA) is supported. + // + + if (((PMODE_PARAMETER_HEADER)modeData)->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED) { + + deviceExtension->DeviceFlags |= DEV_WRITE_CACHE; + + } else { + + DebugPrint((1, + "SCSIDISK: Disk does not support FUA or DPO\n")); + + // + // TODO: Log this. + // + + } + } + } + + ExFreePool(modeData); + return(FALSE); + +} // end IsFloppyDevice() + + +BOOLEAN +STDCALL +ScsiDiskModeSelect( + IN PDEVICE_OBJECT DeviceObject, + IN PCHAR ModeSelectBuffer, + IN ULONG Length, + IN BOOLEAN SavePage + ) + +/*++ + +Routine Description: + + This routine sends a mode select command. + +Arguments: + + DeviceObject - Supplies the device object associated with this request. + + ModeSelectBuffer - Supplies a buffer containing the page data. + + Length - Supplies the length in bytes of the mode select buffer. + + SavePage - Indicates that parameters should be written to disk. + +Return Value: + + Length of the transferred data is returned. + +--*/ +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PCDB cdb; + SCSI_REQUEST_BLOCK srb; + ULONG retries = 1; + ULONG length2; + NTSTATUS status; + PULONG buffer; + PMODE_PARAMETER_BLOCK blockDescriptor; + + PAGED_CODE(); + + length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK); + + // + // Allocate buffer for mode select header, block descriptor, and mode page. + // + + buffer = ExAllocatePool(NonPagedPoolCacheAligned,length2); + + RtlZeroMemory(buffer, length2); + + // + // Set length in header to size of mode page. + // + + ((PMODE_PARAMETER_HEADER)buffer)->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK); + + blockDescriptor = (PULONG)(buffer + 1); + + // + // Set size + // + + blockDescriptor->BlockLength[1]=0x02; + + // + // Copy mode page to buffer. + // + + RtlCopyMemory(buffer + 3, ModeSelectBuffer, Length); + + // + // Zero SRB. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Build the MODE SELECT CDB. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = deviceExtension->TimeOutValue * 2; + + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.SPBit = SavePage; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)(length2); + +Retry: + + status = ScsiClassSendSrbSynchronous(DeviceObject, + &srb, + buffer, + length2, + TRUE); + + + if (status == STATUS_VERIFY_REQUIRED) { + + // + // Routine ScsiClassSendSrbSynchronous does not retry requests returned with + // this status. + // + + if (retries--) { + + // + // Retry request. + // + + goto Retry; + } + + } else if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + } + + ExFreePool(buffer); + + if (NT_SUCCESS(status)) { + return(TRUE); + } else { + return(FALSE); + } + +} // end SciDiskModeSelect() + + +VOID +STDCALL +DisableWriteCache( + IN PDEVICE_OBJECT DeviceObject, + IN PSCSI_INQUIRY_DATA LunInfo + ) + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PINQUIRYDATA InquiryData = (PINQUIRYDATA)LunInfo->InquiryData; + BAD_CONTROLLER_INFORMATION const *controller; + ULONG j,length; + PVOID modeData; + PUCHAR pageData; + + for (j = 0; j < NUMBER_OF_BAD_CONTROLLERS; j++) { + + controller = &ScsiDiskBadControllers[j]; + + if (!controller->DisableWriteCache || strncmp(controller->InquiryString, InquiryData->VendorId, strlen(controller->InquiryString))) { + continue; + } + + DebugPrint((1, "ScsiDisk.DisableWriteCache, Found bad controller! %s\n", controller->InquiryString)); + + modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE); + + if (modeData == NULL) { + + DebugPrint((1, + "ScsiDisk.DisableWriteCache: Check for write-cache enable failed\n")); + return; + } + + RtlZeroMemory(modeData, MODE_DATA_SIZE); + + length = ScsiClassModeSense(DeviceObject, + modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + // + // Retry the request in case of a check condition. + // + + length = ScsiClassModeSense(DeviceObject, + modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + + DebugPrint((1, + "ScsiDisk.DisableWriteCache: Mode Sense failed\n")); + + ExFreePool(modeData); + return; + + } + } + + // + // If the length is greater than length indicated by the mode data reset + // the data to the mode data. + // + + if (length > (ULONG) ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) { + length = ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1; + } + + // + // Check to see if the write cache is enabled. + // + + pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_CACHING, TRUE); + + // + // Assume that write cache is disabled or not supported. + // + + deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE; + + // + // Check if valid caching page exists. + // + + if (pageData != NULL) { + + BOOLEAN savePage = FALSE; + + savePage = (BOOLEAN)(((PMODE_CACHING_PAGE)pageData)->PageSavable); + + // + // Check if write cache is disabled. + // + + if (((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable) { + + PIO_ERROR_LOG_PACKET errorLogEntry; + LONG errorCode; + + + // + // Disable write cache and ensure necessary fields are zeroed. + // + + ((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable = FALSE; + ((PMODE_CACHING_PAGE)pageData)->Reserved = 0; + ((PMODE_CACHING_PAGE)pageData)->PageSavable = 0; + ((PMODE_CACHING_PAGE)pageData)->Reserved2 = 0; + + // + // Extract length from caching page. + // + + length = ((PMODE_CACHING_PAGE)pageData)->PageLength; + + // + // Compensate for page code and page length. + // + + length += 2; + + // + // Issue mode select to set the parameter. + // + + if (ScsiDiskModeSelect(DeviceObject, + pageData, + length, + savePage)) { + + DebugPrint((1, + "SCSIDISK: Disk write cache disabled\n")); + + deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE; + errorCode = IO_WRITE_CACHE_DISABLED; + + } else { + if (ScsiDiskModeSelect(DeviceObject, + pageData, + length, + savePage)) { + + DebugPrint((1, + "SCSIDISK: Disk write cache disabled\n")); + + + deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE; + errorCode = IO_WRITE_CACHE_DISABLED; + + } else { + + DebugPrint((1, + "SCSIDISK: Mode select to disable write cache failed\n")); + + deviceExtension->DeviceFlags |= DEV_WRITE_CACHE; + errorCode = IO_WRITE_CACHE_ENABLED; + } + } + + // + // Log the appropriate informational or error entry. + // + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DeviceObject, + sizeof(IO_ERROR_LOG_PACKET) + 3 + * sizeof(ULONG)); + + if (errorLogEntry != NULL) { + + errorLogEntry->FinalStatus = STATUS_SUCCESS; + errorLogEntry->ErrorCode = errorCode; + errorLogEntry->SequenceNumber = 0; + errorLogEntry->MajorFunctionCode = IRP_MJ_SCSI; + errorLogEntry->IoControlCode = 0; + errorLogEntry->RetryCount = 0; + errorLogEntry->UniqueErrorValue = 0x1; + errorLogEntry->DumpDataSize = 3 * sizeof(ULONG); + errorLogEntry->DumpData[0] = LunInfo->PathId; + errorLogEntry->DumpData[1] = LunInfo->TargetId; + errorLogEntry->DumpData[2] = LunInfo->Lun; + + // + // Write the error log packet. + // + + IoWriteErrorLogEntry(errorLogEntry); + } + } + } + + // + // Found device so exit the loop and return. + // + + break; + } + + return; +} + + +BOOLEAN +STDCALL +CalculateMbrCheckSum( + IN PDEVICE_EXTENSION DeviceExtension, + OUT PULONG Checksum + ) + +/*++ + +Routine Description: + + Read MBR and calculate checksum. + +Arguments: + + DeviceExtension - Supplies a pointer to the device information for disk. + Checksum - Memory location to return MBR checksum. + +Return Value: + + Returns TRUE if checksum is valid. + +--*/ +{ + LARGE_INTEGER sectorZero; + PIRP irp; + IO_STATUS_BLOCK ioStatus; + KEVENT event; + NTSTATUS status; + ULONG sectorSize; + PULONG mbr; + ULONG i; + + PAGED_CODE(); + sectorZero.QuadPart = (LONGLONG) 0; + + // + // Create notification event object to be used to signal the inquiry + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // Get sector size. + // + + sectorSize = DeviceExtension->DiskGeometry->BytesPerSector; + + // + // Make sure sector size is at least 512 bytes. + // + + if (sectorSize < 512) { + sectorSize = 512; + } + + // + // Allocate buffer for sector read. + // + + mbr = ExAllocatePool(NonPagedPoolCacheAligned, sectorSize); + + if (!mbr) { + return FALSE; + } + + // + // Build IRP to read MBR. + // + + irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, + DeviceExtension->DeviceObject, + mbr, + sectorSize, + §orZero, + &event, + &ioStatus ); + + if (!irp) { + ExFreePool(mbr); + return FALSE; + } + + // + // Pass request to port driver and wait for request to complete. + // + + status = IoCallDriver(DeviceExtension->DeviceObject, + irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, + Suspended, + KernelMode, + FALSE, + NULL); + status = ioStatus.Status; + } + + if (!NT_SUCCESS(status)) { + ExFreePool(mbr); + return FALSE; + } + + // + // Calculate MBR checksum. + // + + *Checksum = 0; + + for (i = 0; i < 128; i++) { + *Checksum += mbr[i]; + } + + *Checksum = ~*Checksum + 1; + + ExFreePool(mbr); + return TRUE; +} + + +BOOLEAN +STDCALL +EnumerateBusKey( + IN PDEVICE_EXTENSION DeviceExtension, + HANDLE BusKey, + PULONG DiskNumber + ) + +/*++ + +Routine Description: + + The routine queries the registry to determine if this disk is visible to + the BIOS. If the disk is visable to the BIOS, then the geometry information + is updated. + +Arguments: + + DeviceExtension - Supplies a pointer to the device information for disk. + Signature - Unique identifier recorded in MBR. + BusKey - Handle of bus key. + DiskNumber - Returns ordinal of disk as BIOS sees it. + +Return Value: + + TRUE is disk signature matched. + +--*/ +{ + PDISK_DATA diskData = (PDISK_DATA)(DeviceExtension + 1); + BOOLEAN diskFound = FALSE; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING unicodeString; + UNICODE_STRING identifier; + ULONG busNumber; + ULONG adapterNumber; + ULONG diskNumber; + HANDLE adapterKey; + HANDLE spareKey; + HANDLE diskKey; + HANDLE targetKey; + NTSTATUS status; + STRING string; + STRING anotherString; + ULONG length; + UCHAR buffer[20]; + PKEY_VALUE_FULL_INFORMATION keyData; + + PAGED_CODE(); + + for (busNumber = 0; ; busNumber++) { + + // + // Open controller name key. + // + + sprintf(buffer, + "%d", + busNumber); + + RtlInitString(&string, + buffer); + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &string, + TRUE); + + if (!NT_SUCCESS(status)){ + break; + } + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + BusKey, + (PSECURITY_DESCRIPTOR)NULL); + + status = ZwOpenKey(&spareKey, + KEY_READ, + &objectAttributes); + + RtlFreeUnicodeString(&unicodeString); + + if (!NT_SUCCESS(status)) { + break; + } + + // + // Open up controller ordinal key. + // + + RtlInitUnicodeString(&unicodeString, L"DiskController"); + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + spareKey, + (PSECURITY_DESCRIPTOR)NULL); + + status = ZwOpenKey(&adapterKey, + KEY_READ, + &objectAttributes); + + // + // This could fail even with additional adapters of this type + // to search. + // + + if (!NT_SUCCESS(status)) { + continue; + } + + for (adapterNumber = 0; ; adapterNumber++) { + + // + // Open disk key. + // + + sprintf(buffer, + "%d\\DiskPeripheral", + adapterNumber); + + RtlInitString(&string, + buffer); + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &string, + TRUE); + + if (!NT_SUCCESS(status)){ + break; + } + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + adapterKey, + (PSECURITY_DESCRIPTOR)NULL); + + status = ZwOpenKey(&diskKey, + KEY_READ, + &objectAttributes); + + RtlFreeUnicodeString(&unicodeString); + + if (!NT_SUCCESS(status)) { + break; + } + + for (diskNumber = 0; ; diskNumber++) { + + sprintf(buffer, + "%d", + diskNumber); + + RtlInitString(&string, + buffer); + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &string, + TRUE); + + if (!NT_SUCCESS(status)){ + break; + } + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + diskKey, + (PSECURITY_DESCRIPTOR)NULL); + + status = ZwOpenKey(&targetKey, + KEY_READ, + &objectAttributes); + + RtlFreeUnicodeString(&unicodeString); + + if (!NT_SUCCESS(status)) { + break; + } + + // + // Allocate buffer for registry query. + // + + keyData = ExAllocatePool(PagedPool, VALUE_BUFFER_SIZE); + + if (keyData == NULL) { + ZwClose(targetKey); + continue; + } + + // + // Get disk peripheral identifier. + // + + RtlInitUnicodeString(&unicodeString, L"Identifier"); + status = ZwQueryValueKey(targetKey, + &unicodeString, + KeyValueFullInformation, + keyData, + VALUE_BUFFER_SIZE, + &length); + + ZwClose(targetKey); + + if (!NT_SUCCESS(status)) { + continue; + } + + // + // Complete unicode string. + // + + identifier.Buffer = + (PWSTR)((PUCHAR)keyData + keyData->DataOffset); + identifier.Length = (USHORT)keyData->DataLength; + identifier.MaximumLength = (USHORT)keyData->DataLength; + + // + // Convert unicode identifier to ansi string. + // + + status = + RtlUnicodeStringToAnsiString(&anotherString, + &identifier, + TRUE); + + if (!NT_SUCCESS(status)) { + continue; + } + + // + // If checksum is zero, then the MBR is valid and + // the signature is meaningful. + // + + if (diskData->MbrCheckSum) { + + // + // Convert checksum to ansi string. + // + + sprintf(buffer, "%08x", diskData->MbrCheckSum); + + } else { + + // + // Convert signature to ansi string. + // + + sprintf(buffer, "%08x", diskData->Signature); + + // + // Make string point at signature. Can't use scan + // functions because they are not exported for driver use. + // + + anotherString.Buffer+=9; + } + + // + // Convert to ansi string. + // + + RtlInitString(&string, + buffer); + + + // + // Make string lengths equal. + // + + anotherString.Length = string.Length; + + // + // Check if strings match. + // + + if (RtlCompareString(&string, + &anotherString, + TRUE) == 0) { + + diskFound = TRUE; + *DiskNumber = diskNumber; + } + + ExFreePool(keyData); + + // + // Readjust indentifier string if necessary. + // + + if (!diskData->MbrCheckSum) { + anotherString.Buffer-=9; + } + + RtlFreeAnsiString(&anotherString); + + if (diskFound) { + break; + } + } + + ZwClose(diskKey); + } + + ZwClose(adapterKey); + } + + ZwClose(BusKey); + return diskFound; + +} // end EnumerateBusKey() + + +VOID +STDCALL +UpdateGeometry( + IN PDEVICE_EXTENSION DeviceExtension + ) +/*++ + +Routine Description: + + The routine queries the registry to determine if this disk is visible to + the BIOS. If the disk is visable to the BIOS, then the geometry information + is updated. + +Arguments: + + DeviceExtension - Supplies a pointer to the device information for disk. + +Return Value: + + None. + +--*/ + +{ + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING unicodeString; + NTSTATUS status; + HANDLE hardwareKey; + HANDLE busKey; + PCM_INT13_DRIVE_PARAMETER driveParameters; + PCM_FULL_RESOURCE_DESCRIPTOR resourceDescriptor; + PKEY_VALUE_FULL_INFORMATION keyData; + ULONG diskNumber; + PUCHAR buffer; + ULONG length; + ULONG numberOfDrives; + ULONG cylinders; + ULONG sectors; + ULONG sectorsPerTrack; + ULONG tracksPerCylinder; + BOOLEAN foundEZHooker; + PVOID tmpPtr; + + PAGED_CODE(); + + // + // Initialize the object for the key. + // + + InitializeObjectAttributes(&objectAttributes, + DeviceExtension->DeviceObject->DriverObject->HardwareDatabase, + OBJ_CASE_INSENSITIVE, + NULL, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Create the hardware base key. + // + + status = ZwOpenKey(&hardwareKey, + KEY_READ, + &objectAttributes); + + + if (!NT_SUCCESS(status)) { + DebugPrint((1, "ScsiDisk UpdateParameters: Cannot open hardware data. Name: %wZ\n", DeviceExtension->DeviceObject->DriverObject->HardwareDatabase)); + return; + } + + + // + // Get disk BIOS geometry information. + // + + RtlInitUnicodeString(&unicodeString, L"Configuration Data"); + + keyData = ExAllocatePool(PagedPool, VALUE_BUFFER_SIZE); + + if (keyData == NULL) { + ZwClose(hardwareKey); + return; + } + + status = ZwQueryValueKey(hardwareKey, + &unicodeString, + KeyValueFullInformation, + keyData, + VALUE_BUFFER_SIZE, + &length); + + if (!NT_SUCCESS(status)) { + DebugPrint((1, + "SCSIDISK: ExtractBiosGeometry: Can't query configuration data (%x)\n", + status)); + ExFreePool(keyData); + return; + } + + // + // Open EISA bus key. + // + + RtlInitUnicodeString(&unicodeString, L"EisaAdapter"); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + hardwareKey, + (PSECURITY_DESCRIPTOR)NULL); + + status = ZwOpenKey(&busKey, + KEY_READ, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + goto openMultiKey; + } + + DebugPrint((3, + "SCSIDISK: UpdateGeometry: Opened EisaAdapter key\n")); + if (EnumerateBusKey(DeviceExtension, + busKey, + &diskNumber)) { + + ZwClose(hardwareKey); + goto diskMatched; + } + +openMultiKey: + + // + // Open Multifunction bus key. + // + + RtlInitUnicodeString(&unicodeString, L"MultifunctionAdapter"); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + hardwareKey, + (PSECURITY_DESCRIPTOR)NULL); + + status = ZwOpenKey(&busKey, + KEY_READ, + &objectAttributes); + + ZwClose(hardwareKey); + if (NT_SUCCESS(status)) { + DebugPrint((3, + "SCSIDISK: UpdateGeometry: Opened MultifunctionAdapter key\n")); + if (EnumerateBusKey(DeviceExtension, + busKey, + &diskNumber)) { + + goto diskMatched; + } + } + + ExFreePool(keyData); + return; + +diskMatched: + + resourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PUCHAR)keyData + + keyData->DataOffset); + + // + // Check that the data is long enough to hold a full resource descriptor, + // and that the last resouce list is device-specific and long enough. + // + + if (keyData->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR) || + resourceDescriptor->PartialResourceList.Count == 0 || + resourceDescriptor->PartialResourceList.PartialDescriptors[0].Type != + CmResourceTypeDeviceSpecific || + resourceDescriptor->PartialResourceList.PartialDescriptors[0] + .u.DeviceSpecificData.DataSize < sizeof(ULONG)) { + + DebugPrint((1, "SCSIDISK: ExtractBiosGeometry: BIOS header data too small or invalid\n")); + ExFreePool(keyData); + return; + } + + length = + resourceDescriptor->PartialResourceList.PartialDescriptors[0].u.DeviceSpecificData.DataSize; + + // + // Point to the BIOS data. The BIOS data is located after the first + // partial Resource list which should be device specific data. + // + + buffer = (PUCHAR) keyData + keyData->DataOffset + + sizeof(CM_FULL_RESOURCE_DESCRIPTOR); + + + numberOfDrives = length / sizeof(CM_INT13_DRIVE_PARAMETER); + + // + // Use the defaults if the drive number is greater than the + // number of drives detected by the BIOS. + // + + if (numberOfDrives <= diskNumber) { + ExFreePool(keyData); + return; + } + + // + // Point to the array of drive parameters. + // + + driveParameters = (PCM_INT13_DRIVE_PARAMETER) buffer + diskNumber; + cylinders = driveParameters->MaxCylinders + 1; + sectorsPerTrack = driveParameters->SectorsPerTrack; + tracksPerCylinder = driveParameters->MaxHeads +1; + + // + // Calculate the actual number of sectors. + // + + sectors = (ULONG)(DeviceExtension->PartitionLength.QuadPart >> + DeviceExtension->SectorShift); + +#if DBG + if (sectors >= cylinders * tracksPerCylinder * sectorsPerTrack) { + DebugPrint((1, "ScsiDisk: UpdateGeometry: Disk smaller than BIOS indicated\n" + "SCSIDISK: Sectors: %x, Cylinders: %x, Track per Cylinder: %x Sectors per track: %x\n", + sectors, cylinders, tracksPerCylinder, sectorsPerTrack)); } #endif - Cylinders = DriveParameters[DiskNumber].MaxCylinders + 1; - TracksPerCylinder = DriveParameters[DiskNumber].MaxHeads +1; - SectorsPerTrack = DriveParameters[DiskNumber].SectorsPerTrack; + // + // Since the BIOS may not report the full drive, recalculate the drive + // size based on the volume size and the BIOS values for tracks per + // cylinder and sectors per track.. + // - DPRINT("BIOS geometry: %lu Cylinders %hu Heads %hu Sectors\n", - Cylinders, - TracksPerCylinder, - SectorsPerTrack); + length = tracksPerCylinder * sectorsPerTrack; - Sectors = (ULONG) - (DeviceExtension->PartitionLength.QuadPart >> DeviceExtension->SectorShift); + if (length == 0) { - DPRINT("Physical sectors: %lu\n", - Sectors); + // + // The BIOS information is bogus. + // - Length = TracksPerCylinder * SectorsPerTrack; - if (Length == 0) - { - DPRINT("Invalid track length 0\n"); - ExFreePool(ValueBuffer); - return; + DebugPrint((1, "ScsiDisk UpdateParameters: sectorPerTrack zero\n")); + ExFreePool(keyData); + return; } - Cylinders = Sectors / Length; + cylinders = sectors / length; - DPRINT("Logical geometry: %lu Cylinders %hu Heads %hu Sectors\n", - Cylinders, - TracksPerCylinder, - SectorsPerTrack); + // + // Update the actual geometry information. + // - /* Update the disk geometry */ - DeviceExtension->DiskGeometry->SectorsPerTrack = SectorsPerTrack; - DeviceExtension->DiskGeometry->TracksPerCylinder = TracksPerCylinder; - DeviceExtension->DiskGeometry->Cylinders.QuadPart = (ULONGLONG)Cylinders; + DeviceExtension->DiskGeometry->SectorsPerTrack = sectorsPerTrack; + DeviceExtension->DiskGeometry->TracksPerCylinder = tracksPerCylinder; + DeviceExtension->DiskGeometry->Cylinders.QuadPart = (LONGLONG)cylinders; - if (DeviceExtension->DMActive) - { - DPRINT("FIXME: Update geometry with respect to the installed disk manager!\n"); + DebugPrint((3, + "SCSIDISK: UpdateGeometry: BIOS spt %x, #heads %x, #cylinders %x\n", + sectorsPerTrack, + tracksPerCylinder, + cylinders)); - /* FIXME: Update geometry for disk managers */ + ExFreePool(keyData); + + foundEZHooker = FALSE; + + if (!DeviceExtension->DMActive) { + + HalExamineMBR(DeviceExtension->DeviceObject, + DeviceExtension->DiskGeometry->BytesPerSector, + (ULONG)0x55, + &tmpPtr + ); + + if (tmpPtr) { + + ExFreePool(tmpPtr); + foundEZHooker = TRUE; + + } } - ExFreePool(ValueBuffer); + if (DeviceExtension->DMActive || foundEZHooker) { - DPRINT("ScsiDiskUpdateFixedDiskGeometry() done\n"); -} + while (cylinders > 1024) { + + tracksPerCylinder = tracksPerCylinder*2; + cylinders = cylinders/2; + + } + + // + // int 13 values are always 1 less. + // + + tracksPerCylinder -= 1; + cylinders -= 1; + + // + // DM reserves the CE cylinder + // + + cylinders -= 1; + + DeviceExtension->DiskGeometry->Cylinders.QuadPart = cylinders + 1; + DeviceExtension->DiskGeometry->TracksPerCylinder = tracksPerCylinder + 1; + + DeviceExtension->PartitionLength.QuadPart = + DeviceExtension->DiskGeometry->Cylinders.QuadPart * + DeviceExtension->DiskGeometry->SectorsPerTrack * + DeviceExtension->DiskGeometry->BytesPerSector * + DeviceExtension->DiskGeometry->TracksPerCylinder; + + if (DeviceExtension->DMActive) { + + DeviceExtension->DMByteSkew = DeviceExtension->DMSkew * DeviceExtension->DiskGeometry->BytesPerSector; + + } + + } else { + + DeviceExtension->DMByteSkew = 0; + + } + + return; + +} // end UpdateGeometry() -/********************************************************************** - * NAME INTERNAL - * ScsiDiskCalcMbrCheckSum - * - * DESCRIPTION - * Calculates the Checksum from drives MBR. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceExtension - * Disk device extension. - * - * Checksum - * Pointer to the caller supplied cecksum variable. - * - * RETURN VALUE - * TRUE: Checksum was calculated. - * FALSE: Calculation failed. - */ + +NTSTATUS +STDCALL +UpdateRemovableGeometry ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) -static BOOLEAN -ScsiDiskCalcMbrCheckSum(IN PDEVICE_EXTENSION DeviceExtension, - OUT PULONG Checksum) +/*++ + +Routine Description: + + This routines updates the size and starting offset of the device. This is + used when the media on the device may have changed thereby changing the + size of the device. If this is the physical device then a + ScsiClassReadDriveCapacity is done; otherewise, a read partition table is done. + +Arguments: + + DeviceObject - Supplies the device object whos size needs to be updated. + + Irp - Supplies a reference where the status can be updated. + +Return Value: + + Returns the status of the opertion. + +--*/ { - IO_STATUS_BLOCK IoStatusBlock; - LARGE_INTEGER SectorOffset; - ULONG SectorSize; - PULONG MbrBuffer; - KEVENT Event; - PIRP Irp; - ULONG i; - ULONG Sum; - NTSTATUS Status; - KeInitializeEvent(&Event, - NotificationEvent, - FALSE); + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PDRIVE_LAYOUT_INFORMATION partitionList; + NTSTATUS status; + PDISK_DATA diskData; + ULONG partitionNumber; + + // + // Determine if the size of the partition may have changed because + // the media has changed. + // + + if (!(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) { + + return(STATUS_SUCCESS); - /* Get the disk sector size */ - SectorSize = DeviceExtension->DiskGeometry->BytesPerSector; - if (SectorSize < 512) - { - SectorSize = 512; } - /* Allocate MBR buffer */ - MbrBuffer = ExAllocatePool(NonPagedPool, - SectorSize); - if (MbrBuffer == NULL) - { - return FALSE; + // + // If this request is for partition zero then do a read drive + // capacity otherwise do a I/O read partition table. + // + + diskData = (PDISK_DATA) (deviceExtension + 1); + + // + // Read the drive capcity. If that fails, give up. + // + + status = ScsiClassReadDriveCapacity(deviceExtension->PhysicalDevice); + + if (!NT_SUCCESS(status)) { + return(status); } - /* Allocate an IRP */ - SectorOffset.QuadPart = 0ULL; - Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, - DeviceExtension->DeviceObject, - MbrBuffer, - SectorSize, - &SectorOffset, - &Event, - &IoStatusBlock); - if (Irp == NULL) - { - ExFreePool(MbrBuffer); - return FALSE; + // + // Read the partition table agian. + // + + status = IoReadPartitionTable(deviceExtension->PhysicalDevice, + deviceExtension->DiskGeometry->BytesPerSector, + TRUE, + &partitionList); + + + if (!NT_SUCCESS(status)) { + + // + // Fail the request. + // + + return(status); } - /* Call the miniport driver */ - Status = IoCallDriver(DeviceExtension->DeviceObject, - Irp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, - Suspended, - KernelMode, - FALSE, - NULL); - Status = IoStatusBlock.Status; + if (diskData->PartitionNumber != 0 && + diskData->PartitionNumber <= partitionList->PartitionCount ) { + + partitionNumber = diskData->PartitionNumber - 1; + + // + // Update the partition information for this parition. + // + + diskData->PartitionType = + partitionList->PartitionEntry[partitionNumber].PartitionType; + + diskData->BootIndicator = + partitionList->PartitionEntry[partitionNumber].BootIndicator; + + deviceExtension->StartingOffset = + partitionList->PartitionEntry[partitionNumber].StartingOffset; + + deviceExtension->PartitionLength = + partitionList->PartitionEntry[partitionNumber].PartitionLength; + + diskData->HiddenSectors = + partitionList->PartitionEntry[partitionNumber].HiddenSectors; + + deviceExtension->SectorShift = ((PDEVICE_EXTENSION) + deviceExtension->PhysicalDevice->DeviceExtension)->SectorShift; + + } else if (diskData->PartitionNumber != 0) { + + // + // The paritition does not exist. Zero all the data. + // + + diskData->PartitionType = 0; + diskData->BootIndicator = 0; + diskData->HiddenSectors = 0; + deviceExtension->StartingOffset.QuadPart = (LONGLONG)0; + deviceExtension->PartitionLength.QuadPart = (LONGLONG)0; } - if (!NT_SUCCESS(Status)) - { - ExFreePool(MbrBuffer); - return FALSE; - } + // + // Free the parition list allocate by I/O read partition table. + // - /* Calculate MBR checksum */ - Sum = 0; - for (i = 0; i < 128; i++) - { - Sum += MbrBuffer[i]; - } - *Checksum = ~Sum + 1; + ExFreePool(partitionList); - ExFreePool(MbrBuffer); - return TRUE; + return(STATUS_SUCCESS); } -/* EOF */ + +VOID +STDCALL +ScsiDiskProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ) +/*++ + +Routine Description: + + This routine checks the type of error. If the error indicates an underrun + then indicate the request should be retried. + +Arguments: + + DeviceObject - Supplies a pointer to the device object. + + Srb - Supplies a pointer to the failing Srb. + + Status - Status with which the IRP will be completed. + + Retry - Indication of whether the request will be retried. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + + if (*Status == STATUS_DATA_OVERRUN && + ( Srb->Cdb[0] == SCSIOP_WRITE || Srb->Cdb[0] == SCSIOP_READ)) { + + *Retry = TRUE; + + // + // Update the error count for the device. + // + + deviceExtension->ErrorCount++; + } + + if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_ERROR && + Srb->ScsiStatus == SCSISTAT_BUSY) { + + // + // The disk drive should never be busy this long. Reset the scsi bus + // maybe this will clear the condition. + // + + ResetScsiBus(DeviceObject); + + // + // Update the error count for the device. + // + + deviceExtension->ErrorCount++; + } +} + +VOID +STDCALL +ScanForSpecial( + PDEVICE_OBJECT DeviceObject, + PSCSI_INQUIRY_DATA LunInfo, + PIO_SCSI_CAPABILITIES PortCapabilities + ) + +/*++ + +Routine Description: + + This function checks to see if an SCSI logical unit requires speical + flags to be set. + +Arguments: + + DeviceObject - Supplies the device object to be tested. + + InquiryData - Supplies the inquiry data returned by the device of interest. + + PortCapabilities - Supplies the capabilities of the device object. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PINQUIRYDATA InquiryData = (PINQUIRYDATA)LunInfo->InquiryData; + BAD_CONTROLLER_INFORMATION const *controller; + ULONG j,length; + PVOID modeData; + PUCHAR pageData; + + for (j = 0; j < NUMBER_OF_BAD_CONTROLLERS; j++) { + + controller = &ScsiDiskBadControllers[j]; + + if (strncmp(controller->InquiryString, InquiryData->VendorId, strlen(controller->InquiryString))) { + continue; + } + + DebugPrint((1, "ScsiDisk ScanForSpecial, Found bad controller! %s\n", controller->InquiryString)); + + // + // Found a listed controller. Determine what must be done. + // + + if (controller->DisableTaggedQueuing) { + + // + // Disable tagged queuing. + // + + deviceExtension->SrbFlags &= ~SRB_FLAGS_QUEUE_ACTION_ENABLE; + } + + if (controller->DisableSynchronousTransfers) { + + // + // Disable synchronous data transfers. + // + + deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + } + + if (controller->DisableDisconnects) { + + // + // Disable disconnects. + // + + deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT; + + } + + // + // Found device so exit the loop and return. + // + + break; + } + + // + // Set the StartUnit flag appropriately. + // + + if (DeviceObject->DeviceType == FILE_DEVICE_DISK) { + deviceExtension->DeviceFlags |= DEV_SAFE_START_UNIT; + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { + if (_strnicmp(InquiryData->VendorId, "iomega", strlen("iomega"))) { + deviceExtension->DeviceFlags &= ~DEV_SAFE_START_UNIT; + } + } + } + + return; +} + +VOID +STDCALL +ResetScsiBus( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This command sends a reset bus command to the SCSI port driver. + +Arguments: + + DeviceObject - The device object for the logical unit with + hardware problem. + +Return Value: + + None. + +--*/ +{ + PIO_STACK_LOCATION irpStack; + PIRP irp; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCOMPLETION_CONTEXT context; + + DebugPrint((1, "ScsiDisk ResetScsiBus: Sending reset bus request to port driver.\n")); + + // + // Allocate Srb from nonpaged pool. + // + + context = ExAllocatePool(NonPagedPoolMustSucceed, + sizeof(COMPLETION_CONTEXT)); + + // + // Save the device object in the context for use by the completion + // routine. + // + + context->DeviceObject = DeviceObject; + srb = &context->Srb; + + // + // Zero out srb. + // + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + // + // Write length to SRB. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set up SCSI bus address. + // + + srb->PathId = deviceExtension->PathId; + srb->TargetId = deviceExtension->TargetId; + srb->Lun = deviceExtension->Lun; + + srb->Function = SRB_FUNCTION_RESET_BUS; + + // + // Build the asynchronous request to be sent to the port driver. + // Since this routine is called from a DPC the IRP should always be + // available. + // + + irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + + IoSetCompletionRoutine(irp, + (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion, + context, + TRUE, + TRUE, + TRUE); + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + srb->OriginalRequest = irp; + + // + // Store the SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = srb; + + // + // Call the port driver with the IRP. + // + + IoCallDriver(deviceExtension->PortDeviceObject, irp); + + return; + +} // end ResetScsiBus() + + +VOID +STDCALL +UpdateDeviceObjects( + IN PDEVICE_OBJECT PhysicalDisk, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine creates, deletes and changes device objects when + the IOCTL_SET_DRIVE_LAYOUT is called. This routine also updates + the drive layout information for the user. It is possible to + call this routine even in the GET_LAYOUT case because RewritePartition + will be false. + +Arguments: + + DeviceObject - Device object for physical disk. + Irp - IO Request Packet (IRP). + +Return Value: + + None. + +--*/ +{ + PDEVICE_EXTENSION physicalExtension = PhysicalDisk->DeviceExtension; + PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer; + ULONG partition; + ULONG partitionNumber; + ULONG partitionCount; + ULONG lastPartition; + ULONG partitionOrdinal; + PPARTITION_INFORMATION partitionEntry; + CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH]; + STRING ntNameString; + UNICODE_STRING ntUnicodeString; + PDEVICE_OBJECT deviceObject; + PDEVICE_EXTENSION deviceExtension; + PDISK_DATA diskData; + NTSTATUS status; + ULONG numberListElements; + BOOLEAN found; + + partitionCount = ((partitionList->PartitionCount + 3) / 4) * 4; + + // + // Zero all of the partition numbers. + // + + for (partition = 0; partition < partitionCount; partition++) { + partitionEntry = &partitionList->PartitionEntry[partition]; + partitionEntry->PartitionNumber = 0; + } + + // + // Walk through chain of partitions for this disk to determine + // which existing partitions have no match. + // + + deviceExtension = physicalExtension; + diskData = (PDISK_DATA)(deviceExtension + 1); + lastPartition = 0; + + do { + + deviceExtension = diskData->NextPartition; + + // + // Check if this is the last partition in the chain. + // + + if (!deviceExtension) { + break; + } + + // + // Get the partition device extension from disk data. + // + + diskData = (PDISK_DATA)(deviceExtension + 1); + + // + // Check for highest partition number this far. + // + + if (diskData->PartitionNumber > lastPartition) { + lastPartition = diskData->PartitionNumber; + } + + // + // Check if this partition is not currently being used. + // + + if (!deviceExtension->PartitionLength.QuadPart) { + continue; + } + + // + // Loop through partition information to look for match. + // + + found = FALSE; + partitionOrdinal = 0; + + for (partition = 0; partition < partitionCount; partition++) { + + // + // Get partition descriptor. + // + + partitionEntry = &partitionList->PartitionEntry[partition]; + + // + // Check if empty, or describes extended partiton or hasn't changed. + // + + if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(partitionEntry->PartitionType)) { + continue; + } + + // + // Advance partition ordinal. + // + + partitionOrdinal++; + + // + // Check if new partition starts where this partition starts. + // + + if (partitionEntry->StartingOffset.QuadPart != + deviceExtension->StartingOffset.QuadPart) { + continue; + } + + // + // Check if partition length is the same. + // + + if (partitionEntry->PartitionLength.QuadPart == + deviceExtension->PartitionLength.QuadPart) { + + DebugPrint((3, + "UpdateDeviceObjects: Found match for \\Harddisk%d\\Partition%d\n", + physicalExtension->DeviceNumber, + diskData->PartitionNumber)); + + // + // Indicate match is found and set partition number + // in user buffer. + // + + found = TRUE; + partitionEntry->PartitionNumber = diskData->PartitionNumber; + break; + } + } + + if (found) { + + // + // A match is found. + // + + diskData = (PDISK_DATA)(deviceExtension + 1); + + // + // If this partition is marked for update then update partition type. + // + + if (partitionEntry->RewritePartition) { + diskData->PartitionType = partitionEntry->PartitionType; + } + + // + // Update partitional ordinal for calls to HAL routine + // IoSetPartitionInformation. + // + + diskData->PartitionOrdinal = partitionOrdinal; + + DebugPrint((1, + "UpdateDeviceObjects: Disk %d ordinal %d is partition %d\n", + physicalExtension->DeviceNumber, + diskData->PartitionOrdinal, + diskData->PartitionNumber)); + + } else { + + // + // no match was found, indicate this partition is gone. + // + + DebugPrint((1, + "UpdateDeviceObjects: Deleting \\Device\\Harddisk%x\\Partition%x\n", + physicalExtension->DeviceNumber, + diskData->PartitionNumber)); + + deviceExtension->PartitionLength.QuadPart = (LONGLONG) 0; + } + + } while (TRUE); + + // + // Walk through partition loop to find new partitions and set up + // device extensions to describe them. In some cases new device + // objects will be created. + // + + partitionOrdinal = 0; + + for (partition = 0; + partition < partitionCount; + partition++) { + + // + // Get partition descriptor. + // + + partitionEntry = &partitionList->PartitionEntry[partition]; + + // + // Check if empty, or describes an extended partiton. + // + + if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(partitionEntry->PartitionType)) { + continue; + } + + // + // Keep track of position on the disk for calls to IoSetPartitionInformation. + // + + partitionOrdinal++; + + // + // Check if this entry should be rewritten. + // + + if (!partitionEntry->RewritePartition) { + continue; + } + + if (partitionEntry->PartitionNumber) { + + // + // Partition is an exact match with an existing partition, but is + // being written anyway. + // + + continue; + } + + // + // Check first if existing device object is available by + // walking partition extension list. + // + + partitionNumber = 0; + deviceExtension = physicalExtension; + diskData = (PDISK_DATA)(deviceExtension + 1); + + do { + + // + // Get next partition device extension from disk data. + // + + deviceExtension = diskData->NextPartition; + + if (!deviceExtension) { + break; + } + + diskData = (PDISK_DATA)(deviceExtension + 1); + + // + // A device object is free if the partition length is set to zero. + // + + if (!deviceExtension->PartitionLength.QuadPart) { + partitionNumber = diskData->PartitionNumber; + break; + } + + } while (TRUE); + + // + // If partition number is still zero then a new device object + // must be created. + // + + if (partitionNumber == 0) { + + lastPartition++; + partitionNumber = lastPartition; + + // + // Get or create partition object and set up partition parameters. + // + + sprintf(ntNameBuffer, + "\\Device\\Harddisk%d\\Partition%d", + physicalExtension->DeviceNumber, + partitionNumber); + + RtlInitString(&ntNameString, + ntNameBuffer); + + status = RtlAnsiStringToUnicodeString(&ntUnicodeString, + &ntNameString, + TRUE); + + if (!NT_SUCCESS(status)) { + continue; + } + + DebugPrint((3, + "UpdateDeviceObjects: Create device object %s\n", + ntNameBuffer)); + + // + // This is a new name. Create the device object to represent it. + // + + status = IoCreateDevice(PhysicalDisk->DriverObject, + DEVICE_EXTENSION_SIZE, + &ntUnicodeString, + FILE_DEVICE_DISK, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + DebugPrint((1, + "UpdateDeviceObjects: Can't create device %s\n", + ntNameBuffer)); + RtlFreeUnicodeString(&ntUnicodeString); + continue; + } + + // + // Set up device object fields. + // + + deviceObject->Flags |= DO_DIRECT_IO; + deviceObject->StackSize = PhysicalDisk->StackSize; + + // + // Set up device extension fields. + // + + deviceExtension = deviceObject->DeviceExtension; + + // + // Copy physical disk extension to partition extension. + // + + RtlMoveMemory(deviceExtension, + physicalExtension, + sizeof(DEVICE_EXTENSION)); + + // + // Initialize the new S-List. + // + + if (deviceExtension->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) { + numberListElements = 30; + } else { + numberListElements = 8; + } + + // + // Build the lookaside list for srb's for this partition based on + // whether the adapter and disk can do tagged queueing. + // + + ScsiClassInitializeSrbLookasideList(deviceExtension, + numberListElements); + + // + // Allocate spinlock for zoning for split-request completion. + // + + KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock); + + // + // Write back partition number used in creating object name. + // + + partitionEntry->PartitionNumber = partitionNumber; + + // + // Clear flags initializing bit. + // + + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + // + // Point back at device object. + // + + deviceExtension->DeviceObject = deviceObject; + + RtlFreeUnicodeString(&ntUnicodeString); + + // + // Link to end of partition chain using previous disk data. + // + + diskData->NextPartition = deviceExtension; + + // + // Get new disk data and zero next partition pointer. + // + + diskData = (PDISK_DATA)(deviceExtension + 1); + diskData->NextPartition = NULL; + + } else { + + // + // Set pointer to disk data area that follows device extension. + // + + diskData = (PDISK_DATA)(deviceExtension + 1); + + DebugPrint((1, + "UpdateDeviceObjects: Used existing device object \\Device\\Harddisk%x\\Partition%x\n", + physicalExtension->DeviceNumber, + partitionNumber)); + } + + // + // Update partition information in partition device extension. + // + + diskData->PartitionNumber = partitionNumber; + diskData->PartitionType = partitionEntry->PartitionType; + diskData->BootIndicator = partitionEntry->BootIndicator; + deviceExtension->StartingOffset = partitionEntry->StartingOffset; + deviceExtension->PartitionLength = partitionEntry->PartitionLength; + diskData->HiddenSectors = partitionEntry->HiddenSectors; + diskData->PartitionOrdinal = partitionOrdinal; + + DebugPrint((1, + "UpdateDeviceObjects: Ordinal %d is partition %d\n", + diskData->PartitionOrdinal, + diskData->PartitionNumber)); + + // + // Update partition number passed in to indicate the + // device name for this partition. + // + + partitionEntry->PartitionNumber = partitionNumber; + } + +} // end UpdateDeviceObjects() diff --git a/reactos/drivers/storage/disk/disk.rbuild b/reactos/drivers/storage/disk/disk.rbuild index 41a1ca04277..c87eca8b314 100644 --- a/reactos/drivers/storage/disk/disk.rbuild +++ b/reactos/drivers/storage/disk/disk.rbuild @@ -5,6 +5,7 @@ ntoskrnl hal class2 + scsiport .. disk.c disk.rc diff --git a/reactos/drivers/storage/disk/license.txt b/reactos/drivers/storage/disk/license.txt new file mode 100644 index 00000000000..29389c47231 --- /dev/null +++ b/reactos/drivers/storage/disk/license.txt @@ -0,0 +1,53 @@ +Window NT Device Driver Kit +END-USER LICENSE AGREEMENT FOR MICROSOFT SOFTWARE + +IMPORTANT-READ CAREFULLY: This Microsoft End-User License Agreement ("EULA") is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation for the Microsoft software product identified above, which includes computer software and associated media and printed materials, and may include "online" or electronic documentation ("SOFTWARE PRODUCT" or "SOFTWARE"). By installing, copying, or otherwise using the SOFTWARE PRODUCT, you agree to be bound by the terms of this EULA. If you do not agree to the terms of this EULA, promptly return the unused SOFTWARE PRODUCT to the place from which you obtained it for a full refund, or if you received the SOFTWARE PRODUCT as part of a subscription or other service from Microsoft, you may cancel the subscription and receive a refund of a prorata portion of the subscription price. + +SOFTWARE PRODUCT LICENSE +The SOFTWARE PRODUCT is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE PRODUCT is licensed, not sold. To develop Windows NT(tm) device drivers, you will need to utilize two computers, one for development and one for debugging. + +1. GRANT OF LICENSE. This EULA grants you the following limited, non-exclusive rights: +* SOFTWARE PRODUCT. (a) You may make two (2) copies of the SOFTWARE PRODUCT for installation and use on two (2) computers, one for use in development and one for use in debugging to design, develop, and test your software product(s), including but not limited to device driver(s) and other software products, for use with Microsoft(r) Windows(r) or Windows NT ("Software Product"). You may make an additional copy of the Windows NT Workstation operating system (licensed and provided separately) for use on a single computer used for debugging purposes. +* Microsoft Developer Network Subscriber. If you acquired the SOFTWARE PRODUCT through a subscription to the Microsoft Developer Network, and you are either an individual developer or an individual designated within a single entity, you are granted the following additional rights with respect to the SOFTWARE PRODUCT: (a) you may make and use copies of the SOFTWARE PRODUCT on up to ten (10) separate computers, provided that you are the only individual using the SOFTWARE PRODUCT on each such computer, and (b) if you are a single entity, you may designate one individual within your organization to have the right to use the SOFTWARE PRODUCT in the manner described herein. +* SAMPLE CODE. You may modify the sample source code ("Sample Code") to design, develop and test your Software Product, and reproduce and distribute the Sample Code with such modifications in source and object code forms, provided that you comply with the Distribution Requirements described below. +* DISTRIBUTION REQUIREMENTS. You may copy and redistribute the Sample Code as described above, provided that (a) you distribute the Sample Code only in conjunction with and as a part of your Software Product; (b) you do not make any statements to the effect or which imply that your Software Product is "certified" by Microsoft or that its performance is guaranteed by Microsoft; (c) you do not use Microsoft's name, logo, or trademarks to market your Software Product; (d) you include a valid copyright notice on your Software Product; and (e) you indemnify, hold harmless, and defend Microsoft from and against any claims or lawsuits, including attorneys' fees, that arise or result from the use or distribution of your Software Product. Contact Microsoft for the applicable royalties due and other licensing terms for all other uses and/or distribution of the Sample Code +* Microsoft reserves all rights not expressly granted to you. + +2. COPYRIGHT. All right, title, and copyrights in and to the SOFTWARE PRODUCT (including but not limited to any images, photographs, animations, video, audio, music, text, and "applets" incorporated into the SOFTWARE PRODUCT), and any copies of the SOFTWARE PRODUCT, are owned by Microsoft or its suppliers. The SOFTWARE PRODUCT is protected by copyright laws and international treaty provisions. Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted material except that you may either (a) make one copy of the SOFTWARE PRODUCT solely for backup or archival purposes, or (b) install the SOFTWARE PRODUCT on a single computer, provided you keep the original solely for backup or archival purposes. You may not copy the printed materials accompanying the SOFTWARE PRODUCT. + +3. PRERELEASE CODE. The SOFTWARE PRODUCT may contain PRERELEASE CODE that is not at the level of performance and compatibility of the final, generally available, product offering. These portions of the SOFTWARE PRODUCT may not operate correctly and may be substantially modified prior to first commercial shipment. Microsoft is not obligated to make this or any later version of the SOFTWARE PRODUCT commercially available. Microsoft grants you the right to distribute test versions of your Application created using the PRERELEASE CODE provided you comply with the Distribution Requirements described in Section 1 and the following additional provisions: (a) you must mark the test version of your Application "BETA" and (b) you are solely responsible for updating your customers with versions of your Application that operate satisfactorily with the final commercial release of the PRERELEASE CODE. + +4. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS. +( Limitations on Reverse Engineering, Decompilation, and Disassembly. You may not reverse-engineer, decompile, or disassemble the SOFTWARE PRODUCT, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation. +( Rental. You may not rent or lease the SOFTWARE PRODUCT. +( Software Transfer. You may permanently transfer all of your rights under this EULA, provided you retain no copies, you transfer all of the SOFTWARE PRODUCT (including all component parts, the media and printed materials, any upgrades, this EULA, and, if applicable, the Certificate of Authenticity), and the recipient agrees to the terms of this EULA. If the SOFTWARE PRODUCT is an upgrade, any transfer must include all prior versions of the SOFTWARE PRODUCT. +( Termination. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the SOFTWARE PRODUCT and all of its component parts. + +5. EXPORT RESTRICTIONS. You agree that neither you nor your customers intend to or will, directly or indirectly, export or transmit (a) the SOFTWARE PRODUCT or related documentation and technical data or (b) your Application as described in Section 1 of this Agreement (or any part thereof), or process, or service that is the direct product of the SOFTWARE PRODUCT to any country to which such export or transmission is restricted by any applicable U.S. regulation or statute, without the prior written consent, if required, of the Bureau of Export Administration of the U.S. Department of Commerce, or such other governmental entity as may have jurisdiction over such export or transmission. + +NO WARRANTIES. To the maximum extent permitted by applicable law, Microsoft expressly disclaims any warranty for the SOFTWARE PRODUCT. The SOFTWARE PRODUCT and any related documentation are provided "as is" without warranty of any kind, either express or implied, including, without limitation, the implied warranties of merchantability or fitness for a particular purpose. The entire risk arising out of use or performance of the SOFTWARE PRODUCT remains with you. + +LIMITATION OF LIABILITY. Microsoft's entire liability and your exclusive remedy under this EULA shall not exceed one hundred dollars (US$100.00). + +NO LIABILITY FOR CONSEQUENTIAL DAMAGES. To the maximum extent permitted by applicable law, in no event shall Microsoft or its suppliers be liable for any damages whatsoever (including, without limitation, damages for loss of business profit, business interruption, loss of business information, or any other pecuniary loss) arising out of the use or inability to use this Microsoft product, even if Microsoft has been advised of the possibility of such damages. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you. + +U.S. GOVERNMENT RESTRICTED RIGHTS.The SOFTWARE and documentation are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer Software -- Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer is Microsoft Corporation/One Microsoft Way/Redmond, WA 98052-6399. + +If you acquired this product in the United States, this Agreement is governed by the laws of the State of Washington. If you acquired this product outside the United States, local law may apply. + +Should you have any questions concerning this Agreement, if you desire rights to use the product beyond what is listed here, or if you desire to contact Microsoft for any reason, please contact your local Microsoft subsidiary or sales office or write to: Microsoft Sales and Service, One Microsoft Way, Redmond, WA 98052-6399. + +Si vous avez acquis votre produit Microsoft au CANADA, la garantie limitée suivante vous concerne : + +GARANTIE LIMITÉE + +EXCLUSION DE GARANTIES. Microsoft renonce entièrement à toute garantie pour le LOGICIEL. Le LOGICIEL et toute autre documentation s'y rapportant sont fournis «comme tels» sans aucune garantie quelle qu'elle soit, expresse ou implicite, y compris, mais ne se limitant pas, aux garanties implicites de la qualité marchande ou un usage particulier. Le risque total découlant de l'utilisation ou de la performance du LOGICIEL est entre vos mains. + +RESPONSABILITÉ LIMITÉE. La seule obligation de Microsoft et votre recours exclusif concernant ce contrat n'excèderont pas cent dollars (US$100.00). + +ABSENCE DE RESPONSABILITÉ POUR LES DOMMAGES INDIRECTS. Microsoft ou ses fournisseurs ne pourront être tenus responsables en aucune circonstance de tout dommage quel qu'il soit (y compris mais non de façon limitative aux dommages directs ou indirects causés par la perte de bénéfices commerciaux, l'interruption des affaires, la perte d'information commerciale ou toute autre perte pécuniaire) résultant de l'utilisation ou de l'impossibilité d'utilisation de ce produit, et ce, même si la société Microsoft a été avisée de l'éventualité de tels dommages. Certains états/juridictions ne permettent pas l'exclusion ou la limitation de responsabilité relative aux dommages indirects ou consécutifs, et la limitation ci-dessus peut ne pas s'appliquer à votre égard. + +La présente Convention est régie par les lois de la province d'Ontario, Canada. Chacune des parties à la présente reconnaît irrévocablement la compétence des tribunaux de la province d'Ontario et consent à instituer tout litige qui pourrait découler de la présente auprès des tribunaux situés dans le district judiciaire de York, province d'Ontario. + +Au cas où vous auriez des questions concernant cette licence ou que vous désiriez vous mettre en rapport avec Microsoft pour quelque raison que ce soit, veuillez contacter la succursale Microsoft desservant votre pays, dont l'adresse est fournie dans ce produit, ou écrire à : Microsoft Customer Sales and Service, One Microsoft Way, Redmond, Washington 98052-6399. + diff --git a/reactos/drivers/storage/include/class2.h b/reactos/drivers/storage/include/class2.h index e6650c75731..37aa1fa1fdf 100644 --- a/reactos/drivers/storage/include/class2.h +++ b/reactos/drivers/storage/include/class2.h @@ -16,6 +16,34 @@ #define MAXIMUM_RETRIES 15 #define RETRY_WAIT 2000000 /* 200 ms in units of 100 ns */ +// +// Indicates that the device has write caching enabled. +// + +#define DEV_WRITE_CACHE 0x00000001 + + +// +// Build SCSI 1 or SCSI 2 CDBs +// + +#define DEV_USE_SCSI1 0x00000002 + +// +// Indicates whether is is safe to send StartUnit commands +// to this device. It will only be off for some removeable devices. +// + +#define DEV_SAFE_START_UNIT 0x00000004 + +// +// Indicates whether it is unsafe to send SCSIOP_MECHANISM_STATUS commands to +// this device. Some devices don't like these 12 byte commands +// + +#define DEV_NO_12BYTE_CDB 0x00000008 + + struct _CLASS_INIT_DATA; typedef VOID @@ -228,6 +256,15 @@ ScsiClassSplitRequest(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG MaximumBytes); +NTSTATUS +STDCALL +ScsiClassCheckVerifyComplete( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + + #endif /* __INCLUDE_DDK_CLASS2_H */ /* EOF */ diff --git a/reactos/drivers/storage/scsiport-new/scsiport.rbuild b/reactos/drivers/storage/scsiport-new/scsiport.rbuild index 1b5b1f5359d..75425c59b2c 100644 --- a/reactos/drivers/storage/scsiport-new/scsiport.rbuild +++ b/reactos/drivers/storage/scsiport-new/scsiport.rbuild @@ -1,4 +1,4 @@ - + diff --git a/reactos/drivers/storage/scsiport/scsiport.c b/reactos/drivers/storage/scsiport/scsiport.c deleted file mode 100644 index b69e957558c..00000000000 --- a/reactos/drivers/storage/scsiport/scsiport.c +++ /dev/null @@ -1,3282 +0,0 @@ -/* - * ReactOS kernel - * Copyright (C) 2001, 2002 ReactOS Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: services/storage/scsiport/scsiport.c - * PURPOSE: SCSI port driver - * PROGRAMMER: Eric Kohl (ekohl@rz-online.de) - */ - -/* INCLUDES *****************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define NDEBUG -#include - -#include "scsiport_int.h" - -/* TYPES *********************************************************************/ - -#define IRP_FLAG_COMPLETE 0x00000001 -#define IRP_FLAG_NEXT 0x00000002 -#define IRP_FLAG_NEXT_LU 0x00000004 - -/* GLOBALS *******************************************************************/ - -static ULONG InternalDebugLevel = 0; - -static VOID -SpiProcessRequests(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PIRP NextIrp); - -static VOID -SpiStartIo(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PIRP Irp); - -static BOOLEAN -SpiGetPciConfigData (IN struct _HW_INITIALIZATION_DATA *HwInitializationData, - IN OUT PPORT_CONFIGURATION_INFORMATION PortConfig, - IN ULONG BusNumber, - IN OUT PPCI_SLOT_NUMBER NextSlotNumber); - -static NTSTATUS STDCALL -ScsiPortCreateClose(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -static NTSTATUS STDCALL -ScsiPortDispatchScsi(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -static NTSTATUS STDCALL -ScsiPortDeviceControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -static BOOLEAN STDCALL -ScsiPortStartPacket(IN OUT PVOID Context); - - -static PSCSI_PORT_LUN_EXTENSION -SpiAllocateLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - IN UCHAR PathId, - IN UCHAR TargetId, - IN UCHAR Lun); - -static VOID -SpiRemoveLunExtension (IN PSCSI_PORT_LUN_EXTENSION LunExtension); - -static PSCSI_PORT_LUN_EXTENSION -SpiGetLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - IN UCHAR PathId, - IN UCHAR TargetId, - IN UCHAR Lun); - -static NTSTATUS -SpiSendInquiry (IN PDEVICE_OBJECT DeviceObject, - IN OUT PSCSI_REQUEST_BLOCK Srb, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN OUT PKEVENT Event); - -static VOID -SpiScanAdapter (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension); - -static ULONG -SpiGetInquiryData (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - OUT PSCSI_ADAPTER_BUS_INFO AdapterBusInfo); - -static BOOLEAN STDCALL -ScsiPortIsr(IN PKINTERRUPT Interrupt, - IN PVOID ServiceContext); - -static VOID STDCALL -ScsiPortDpc(IN PKDPC Dpc, - IN PDEVICE_OBJECT DpcDeviceObject, - IN PIRP DpcIrp, - IN PVOID DpcContext); - -static VOID STDCALL -ScsiPortIoTimer(PDEVICE_OBJECT DeviceObject, - PVOID Context); - -static PSCSI_REQUEST_BLOCK -ScsiPortInitSenseRequestSrb(PSCSI_REQUEST_BLOCK OriginalSrb); - -static NTSTATUS -SpiBuildDeviceMap (PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PUNICODE_STRING RegistryPath); - -static VOID -SpiAllocateSrbExtension(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb); - -static VOID -SpiFreeSrbExtension(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb); - - -static NTSTATUS -SpiScsiMiniport(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -/* FUNCTIONS *****************************************************************/ - -/********************************************************************** - * NAME EXPORTED - * DriverEntry - * - * DESCRIPTION - * This function initializes the driver. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DriverObject - * System allocated Driver Object for this driver. - * - * RegistryPath - * Name of registry driver service key. - * - * RETURN VALUE - * Status. - */ - -NTSTATUS STDCALL -DriverEntry(IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath) -{ - DPRINT("ScsiPort Driver %s\n", VERSION); - return(STATUS_SUCCESS); -} - - -/********************************************************************** - * NAME EXPORTED - * ScsiDebugPrint - * - * DESCRIPTION - * Prints debugging messages. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DebugPrintLevel - * Debug level of the given message. - * - * DebugMessage - * Pointer to printf()-compatible format string. - * - * ... - Additional output data (see printf()). - * - * RETURN VALUE - * None. - * - * @implemented - */ - -VOID -ScsiDebugPrint(IN ULONG DebugPrintLevel, - IN PCHAR DebugMessage, - ...) -{ - char Buffer[256]; - va_list ap; - - if (DebugPrintLevel >= InternalDebugLevel) - return; - - va_start(ap, DebugMessage); - vsprintf(Buffer, DebugMessage, ap); - va_end(ap); - - DbgPrint(Buffer); -} - - -/* - * @unimplemented - */ -VOID STDCALL -ScsiPortCompleteRequest(IN PVOID HwDeviceExtension, - IN UCHAR PathId, - IN UCHAR TargetId, - IN UCHAR Lun, - IN UCHAR SrbStatus) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - PSCSI_PORT_LUN_EXTENSION LunExtension; - PLIST_ENTRY Entry; - PIRP Irp; - PSCSI_REQUEST_BLOCK Srb; - - DPRINT("ScsiPortCompleteRequest(HwDeviceExtension %x, PathId %d, TargetId %d, Lun %d, SrbStatus %x)\n", - HwDeviceExtension, PathId, TargetId, Lun, SrbStatus); - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - - Entry = DeviceExtension->LunExtensionListHead.Flink; - while (Entry != &DeviceExtension->LunExtensionListHead) - { - LunExtension = CONTAINING_RECORD(Entry, - SCSI_PORT_LUN_EXTENSION, - List); - - - - if (PathId == (UCHAR)SP_UNTAGGED || - (PathId == LunExtension->PathId && TargetId == (UCHAR)SP_UNTAGGED) || - (PathId == LunExtension->PathId && TargetId == LunExtension->TargetId && Lun == (UCHAR)SP_UNTAGGED) || - (PathId == LunExtension->PathId && TargetId == LunExtension->TargetId && Lun == LunExtension->Lun)) - { - Irp = LunExtension->NextIrp; - while (Irp) - { - Srb = (PSCSI_REQUEST_BLOCK)Irp->Tail.Overlay.DriverContext[3]; - if (Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE) - { - Srb->SrbStatus = SrbStatus; - ScsiPortNotification(RequestComplete, - HwDeviceExtension, - Srb); - } - Irp = Irp->Tail.Overlay.DriverContext[1]; - } - } - Entry = Entry->Flink; - } -} - - -/* - * @implemented - */ -#undef ScsiPortConvertPhysicalAddressToUlong -ULONG STDCALL -ScsiPortConvertPhysicalAddressToUlong(IN SCSI_PHYSICAL_ADDRESS Address) -{ - DPRINT("ScsiPortConvertPhysicalAddressToUlong()\n"); - return(Address.u.LowPart); -} - - -/* - * @unimplemented - */ -VOID STDCALL -ScsiPortFlushDma(IN PVOID HwDeviceExtension) -{ - DPRINT("ScsiPortFlushDma()\n"); - UNIMPLEMENTED; -} - - -/* - * @implemented - */ -VOID STDCALL -ScsiPortFreeDeviceBase(IN PVOID HwDeviceExtension, - IN PVOID MappedAddress) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - PSCSI_PORT_DEVICE_BASE DeviceBase; - PLIST_ENTRY Entry; - - DPRINT("ScsiPortFreeDeviceBase() called\n"); - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - if (IsListEmpty(&DeviceExtension->DeviceBaseListHead)) - return; - - Entry = DeviceExtension->DeviceBaseListHead.Flink; - while (Entry != &DeviceExtension->DeviceBaseListHead) - { - DeviceBase = CONTAINING_RECORD(Entry, - SCSI_PORT_DEVICE_BASE, - List); - if (DeviceBase->MappedAddress == MappedAddress) - { - MmUnmapIoSpace(DeviceBase->MappedAddress, - DeviceBase->NumberOfBytes); - RemoveEntryList(Entry); - ExFreePool(DeviceBase); - - return; - } - - Entry = Entry->Flink; - } -} - - -/* - * @implemented - */ -ULONG STDCALL -ScsiPortGetBusData(IN PVOID DeviceExtension, - IN ULONG BusDataType, - IN ULONG SystemIoBusNumber, - IN ULONG SlotNumber, - IN PVOID Buffer, - IN ULONG Length) -{ - return(HalGetBusData(BusDataType, - SystemIoBusNumber, - SlotNumber, - Buffer, - Length)); -} - - -/* - * @implemented - */ -PVOID STDCALL -ScsiPortGetDeviceBase(IN PVOID HwDeviceExtension, - IN INTERFACE_TYPE BusType, - IN ULONG SystemIoBusNumber, - IN SCSI_PHYSICAL_ADDRESS IoAddress, - IN ULONG NumberOfBytes, - IN BOOLEAN InIoSpace) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - PHYSICAL_ADDRESS TranslatedAddress; - PSCSI_PORT_DEVICE_BASE DeviceBase; - ULONG AddressSpace; - PVOID MappedAddress; - - DPRINT ("ScsiPortGetDeviceBase() called\n"); - - AddressSpace = (ULONG)InIoSpace; - if (HalTranslateBusAddress(BusType, - SystemIoBusNumber, - IoAddress, - &AddressSpace, - &TranslatedAddress) == FALSE) - return NULL; - - /* i/o space */ - if (AddressSpace != 0) - return((PVOID)TranslatedAddress.u.LowPart); - - MappedAddress = MmMapIoSpace(TranslatedAddress, - NumberOfBytes, - MmNonCached); - - DeviceBase = ExAllocatePool(NonPagedPool, - sizeof(SCSI_PORT_DEVICE_BASE)); - if (DeviceBase == NULL) - return(MappedAddress); - - DeviceBase->MappedAddress = MappedAddress; - DeviceBase->NumberOfBytes = NumberOfBytes; - DeviceBase->IoAddress = IoAddress; - DeviceBase->SystemIoBusNumber = SystemIoBusNumber; - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - - InsertHeadList(&DeviceExtension->DeviceBaseListHead, - &DeviceBase->List); - - return(MappedAddress); -} - - -/* - * @implemented - */ -PVOID STDCALL -ScsiPortGetLogicalUnit(IN PVOID HwDeviceExtension, - IN UCHAR PathId, - IN UCHAR TargetId, - IN UCHAR Lun) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - PSCSI_PORT_LUN_EXTENSION LunExtension; - PLIST_ENTRY Entry; - - DPRINT("ScsiPortGetLogicalUnit() called\n"); - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - if (IsListEmpty(&DeviceExtension->LunExtensionListHead)) - return NULL; - - Entry = DeviceExtension->LunExtensionListHead.Flink; - while (Entry != &DeviceExtension->LunExtensionListHead) - { - LunExtension = CONTAINING_RECORD(Entry, - SCSI_PORT_LUN_EXTENSION, - List); - if (LunExtension->PathId == PathId && - LunExtension->TargetId == TargetId && - LunExtension->Lun == Lun) - { - return (PVOID)&LunExtension->MiniportLunExtension; - } - - Entry = Entry->Flink; - } - - return NULL; -} - - -/* - * @unimplemented - */ -SCSI_PHYSICAL_ADDRESS STDCALL -ScsiPortGetPhysicalAddress(IN PVOID HwDeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb OPTIONAL, - IN PVOID VirtualAddress, - OUT ULONG *Length) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - SCSI_PHYSICAL_ADDRESS PhysicalAddress; - SCSI_PHYSICAL_ADDRESS NextPhysicalAddress; - ULONG BufferLength = 0; - ULONG Offset; - PVOID EndAddress; - - DPRINT("ScsiPortGetPhysicalAddress(%p %p %p %p)\n", - HwDeviceExtension, Srb, VirtualAddress, Length); - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - - if (Length != NULL) - { - *Length = 0; - } - if (Srb == NULL) - { - EndAddress = (PVOID)((ULONG_PTR)DeviceExtension->VirtualAddress + DeviceExtension->CommonBufferLength); - if (VirtualAddress >= DeviceExtension->VirtualAddress && VirtualAddress < EndAddress) - { - Offset = (ULONG_PTR)VirtualAddress - (ULONG_PTR)DeviceExtension->VirtualAddress; - PhysicalAddress.QuadPart = DeviceExtension->PhysicalAddress.QuadPart + Offset; - BufferLength = (ULONG_PTR)EndAddress - (ULONG_PTR)VirtualAddress; - } - else - { - /* - * The given virtual address is not within the range - * of the drivers uncached extension or srb extension. - */ - /* - * FIXME: - * Check if the address is a sense info buffer of an active srb. - */ - PhysicalAddress = MmGetPhysicalAddress(VirtualAddress); - if (PhysicalAddress.QuadPart == 0LL) - { - CHECKPOINT; - return PhysicalAddress; - } - BufferLength = PAGE_SIZE - PhysicalAddress.u.LowPart % PAGE_SIZE; - } - } - else - { - EndAddress = (PVOID)((ULONG_PTR)Srb->DataBuffer + Srb->DataTransferLength); - if (VirtualAddress == NULL) - { - VirtualAddress = Srb->DataBuffer; - } - else if (VirtualAddress < Srb->DataBuffer || VirtualAddress >= EndAddress) - { - EndAddress = (PVOID)((ULONG_PTR)Srb->SenseInfoBuffer + Srb->SenseInfoBufferLength); - if (VirtualAddress < Srb->SenseInfoBuffer || VirtualAddress >= EndAddress) - { - PhysicalAddress.QuadPart = 0LL; - CHECKPOINT; - return PhysicalAddress; - } - } - - PhysicalAddress = MmGetPhysicalAddress(VirtualAddress); - if (PhysicalAddress.QuadPart == 0LL) - { - CHECKPOINT; - return PhysicalAddress; - } - - BufferLength = PAGE_SIZE - (ULONG_PTR)VirtualAddress % PAGE_SIZE; - while ((ULONG_PTR)VirtualAddress + BufferLength < (ULONG_PTR)EndAddress) - { - NextPhysicalAddress = MmGetPhysicalAddress((PVOID)((ULONG_PTR)VirtualAddress + BufferLength)); - if (PhysicalAddress.QuadPart + BufferLength != NextPhysicalAddress.QuadPart) - { - break; - } - BufferLength += PAGE_SIZE; - } - if ((ULONG_PTR)VirtualAddress + BufferLength >= (ULONG_PTR)EndAddress) - { - BufferLength = (ULONG)((ULONG_PTR)EndAddress - (ULONG_PTR)VirtualAddress); - } - } - if (Length != NULL) - { - *Length = BufferLength; - } - DPRINT("Address %I64x, Length %d\n", PhysicalAddress.QuadPart, BufferLength); - return PhysicalAddress; -} - - -/* - * @unimplemented - */ -PSCSI_REQUEST_BLOCK STDCALL -ScsiPortGetSrb(IN PVOID HwDeviceExtension, - IN UCHAR PathId, - IN UCHAR TargetId, - IN UCHAR Lun, - IN LONG QueueTag) -{ - DPRINT1("ScsiPortGetSrb() unimplemented\n"); - UNIMPLEMENTED; - return NULL; -} - - -/* - * @implemented - */ -PVOID STDCALL -ScsiPortGetUncachedExtension(IN PVOID HwDeviceExtension, - IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, - IN ULONG NumberOfBytes) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - DEVICE_DESCRIPTION DeviceDescription; - - DPRINT("ScsiPortGetUncachedExtension(%p %p %lu)\n", - HwDeviceExtension, ConfigInfo, NumberOfBytes); - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - - /* Check for allocated common DMA buffer */ - if (DeviceExtension->VirtualAddress != NULL) - { - DPRINT1("The HBA has already got a common DMA buffer!\n"); - return NULL; - } - - /* Check for DMA adapter object */ - if (DeviceExtension->AdapterObject == NULL) - { - /* Initialize DMA adapter description */ - RtlZeroMemory(&DeviceDescription, - sizeof(DEVICE_DESCRIPTION)); - DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION; - DeviceDescription.Master = ConfigInfo->Master; - DeviceDescription.ScatterGather = ConfigInfo->ScatterGather; - DeviceDescription.DemandMode = ConfigInfo->DemandMode; - DeviceDescription.Dma32BitAddresses = ConfigInfo->Dma32BitAddresses; - DeviceDescription.BusNumber = ConfigInfo->SystemIoBusNumber; - DeviceDescription.DmaChannel = ConfigInfo->DmaChannel; - DeviceDescription.InterfaceType = ConfigInfo->AdapterInterfaceType; - DeviceDescription.DmaWidth = ConfigInfo->DmaWidth; - DeviceDescription.DmaSpeed = ConfigInfo->DmaSpeed; - DeviceDescription.MaximumLength = ConfigInfo->MaximumTransferLength; - DeviceDescription.DmaPort = ConfigInfo->DmaPort; - - /* Get a DMA adapter object */ - DeviceExtension->AdapterObject = HalGetAdapter(&DeviceDescription, - &DeviceExtension->MapRegisterCount); - if (DeviceExtension->AdapterObject == NULL) - { - DPRINT1("HalGetAdapter() failed\n"); - return NULL; - } - } - if (DeviceExtension->SrbExtensionSize > 0) - { - PVOID Buffer; - DeviceExtension->CurrentSrbExtensions = 0; - if (DeviceExtension->PortConfig->MultipleRequestPerLu) - { - DeviceExtension->MaxSrbExtensions = 1024; - } - else - { - DeviceExtension->MaxSrbExtensions = 32; - } - Buffer = ExAllocatePool(NonPagedPool, ROUND_UP(DeviceExtension->MaxSrbExtensions / 8, sizeof(ULONG))); - if (Buffer == NULL) - { - KEBUGCHECK(0); - return NULL; - } - RtlInitializeBitMap(&DeviceExtension->SrbExtensionAllocMap, Buffer, DeviceExtension->MaxSrbExtensions); - RtlClearAllBits(&DeviceExtension->SrbExtensionAllocMap); - } - - /* Allocate a common DMA buffer */ - DeviceExtension->CommonBufferLength = - NumberOfBytes + PAGE_ROUND_UP(DeviceExtension->SrbExtensionSize * DeviceExtension->MaxSrbExtensions); - DeviceExtension->VirtualAddress = - HalAllocateCommonBuffer(DeviceExtension->AdapterObject, - DeviceExtension->CommonBufferLength, - &DeviceExtension->PhysicalAddress, - FALSE); - if (DeviceExtension->VirtualAddress == NULL) - { - DPRINT1("HalAllocateCommonBuffer() failed!\n"); - DeviceExtension->CommonBufferLength = 0; - return NULL; - } - - return (PVOID)((ULONG_PTR)DeviceExtension->VirtualAddress + - PAGE_ROUND_UP(DeviceExtension->SrbExtensionSize * DeviceExtension->MaxSrbExtensions)); -} - - -/* - * @implemented - */ -PVOID STDCALL -ScsiPortGetVirtualAddress(IN PVOID HwDeviceExtension, - IN SCSI_PHYSICAL_ADDRESS PhysicalAddress) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - ULONG Offset; - - DPRINT("ScsiPortGetVirtualAddress(%p %I64x)\n", - HwDeviceExtension, PhysicalAddress.QuadPart); - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - - if (DeviceExtension->PhysicalAddress.QuadPart > PhysicalAddress.QuadPart) - return NULL; - - Offset = (ULONG)(PhysicalAddress.QuadPart - DeviceExtension->PhysicalAddress.QuadPart); - if (Offset >= DeviceExtension->CommonBufferLength) - return NULL; - - return (PVOID)((ULONG_PTR)DeviceExtension->VirtualAddress + Offset); -} - - -/********************************************************************** - * NAME EXPORTED - * ScsiPortInitialize - * - * DESCRIPTION - * Initializes SCSI port driver specific data. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * Argument1 - * Pointer to the miniport driver's driver object. - * - * Argument2 - * Pointer to the miniport driver's registry path. - * - * HwInitializationData - * Pointer to port driver specific configuration data. - * - * HwContext - Miniport driver specific context. - * - * RETURN VALUE - * Status. - * - * @implemented - */ - -ULONG STDCALL -ScsiPortInitialize(IN PVOID Argument1, - IN PVOID Argument2, - IN struct _HW_INITIALIZATION_DATA *HwInitializationData, - IN PVOID HwContext) -{ - PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Argument1; -// PUNICODE_STRING RegistryPath = (PUNICODE_STRING)Argument2; - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - PCONFIGURATION_INFORMATION SystemConfig; - PPORT_CONFIGURATION_INFORMATION PortConfig; - ULONG DeviceExtensionSize; - ULONG PortConfigSize; - BOOLEAN Again; - BOOLEAN DeviceFound = FALSE; - ULONG i; - ULONG Result; - NTSTATUS Status; - ULONG MaxBus; - ULONG BusNumber; - PCI_SLOT_NUMBER SlotNumber; - - PDEVICE_OBJECT PortDeviceObject; - WCHAR NameBuffer[80]; - UNICODE_STRING DeviceName; - WCHAR DosNameBuffer[80]; - UNICODE_STRING DosDeviceName; - PIO_SCSI_CAPABILITIES PortCapabilities; - ULONG MappedIrq; - KIRQL Dirql; - KAFFINITY Affinity; - - - DPRINT ("ScsiPortInitialize() called!\n"); -#if 0 - DPRINT1("HwInitializationDataSize: %d\n", HwInitializationData->HwInitializationDataSize); - DPRINT1("AdapterInterfaceType: %d\n", HwInitializationData->AdapterInterfaceType); - DPRINT1("HwInitialize: %x\n", HwInitializationData->HwInitialize); - DPRINT1("HwStartIo: %x\n", HwInitializationData->HwStartIo); - DPRINT1("HwInterrupt: %x\n", HwInitializationData->HwInterrupt); - DPRINT1("HwFindAdapter: %x\n", HwInitializationData->HwFindAdapter); - DPRINT1("HwResetBus: %x\n", HwInitializationData->HwResetBus); - DPRINT1("HwDmaStarted: %x\n", HwInitializationData->HwDmaStarted); - DPRINT1("HwAdapterState: %x\n", HwInitializationData->HwAdapterState); - DPRINT1("DeviceExtensionSize: %d\n", HwInitializationData->DeviceExtensionSize); - DPRINT1("SpecificLuExtensionSize: %d\n", HwInitializationData->SpecificLuExtensionSize); - DPRINT1("SrbExtensionSize: %d\n", HwInitializationData->SrbExtensionSize); - DPRINT1("NumberOfAccessRanges: %d\n", HwInitializationData->NumberOfAccessRanges); - DPRINT1("Reserved: %x\n", HwInitializationData->Reserved); - DPRINT1("MapBuffers: %d\n", HwInitializationData->MapBuffers); - DPRINT1("NeedPhysicalAddresses: %d\n", HwInitializationData->NeedPhysicalAddresses); - DPRINT1("TaggedQueueing: %d\n", HwInitializationData->TaggedQueueing); - DPRINT1("AutoRequestSense: %d\n", HwInitializationData->AutoRequestSense); - DPRINT1("MultipleRequestPerLu: %d\n", HwInitializationData->MultipleRequestPerLu); - DPRINT1("ReceiveEvent: %d\n", HwInitializationData->ReceiveEvent); - DPRINT1("VendorIdLength: %d\n", HwInitializationData->VendorIdLength); - DPRINT1("VendorId: %x\n", HwInitializationData->VendorId); - DPRINT1("ReservedUshort: %d\n", HwInitializationData->ReservedUshort); - DPRINT1("DeviceIdLength: %d\n", HwInitializationData->DeviceIdLength); - DPRINT1("DeviceId: %x\n", HwInitializationData->DeviceId); -#endif - if ((HwInitializationData->HwInitialize == NULL) || - (HwInitializationData->HwStartIo == NULL) || - (HwInitializationData->HwInterrupt == NULL) || - (HwInitializationData->HwFindAdapter == NULL) || - (HwInitializationData->HwResetBus == NULL)) - return(STATUS_INVALID_PARAMETER); - - DriverObject->DriverStartIo = NULL; - DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiPortCreateClose; - DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiPortCreateClose; - DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiPortDeviceControl; - DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiPortDispatchScsi; - - SystemConfig = IoGetConfigurationInformation(); - - DeviceExtensionSize = sizeof(SCSI_PORT_DEVICE_EXTENSION) + - HwInitializationData->DeviceExtensionSize; - PortConfigSize = sizeof(PORT_CONFIGURATION_INFORMATION) + - HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE); - - - MaxBus = (HwInitializationData->AdapterInterfaceType == PCIBus) ? 8 : 1; - DPRINT("MaxBus: %lu\n", MaxBus); - - PortDeviceObject = NULL; - BusNumber = 0; - SlotNumber.u.AsULONG = 0; - while (TRUE) - { - /* Create a unicode device name */ - swprintf (NameBuffer, - L"\\Device\\ScsiPort%lu", - SystemConfig->ScsiPortCount); - RtlInitUnicodeString (&DeviceName, - NameBuffer); - - DPRINT("Creating device: %wZ\n", &DeviceName); - - /* Create the port device */ - Status = IoCreateDevice (DriverObject, - DeviceExtensionSize, - &DeviceName, - FILE_DEVICE_CONTROLLER, - 0, - FALSE, - &PortDeviceObject); - if (!NT_SUCCESS(Status)) - { - DbgPrint ("IoCreateDevice call failed! (Status 0x%lX)\n", Status); - PortDeviceObject = NULL; - goto ByeBye; - } - - DPRINT ("Created device: %wZ (%p)\n", &DeviceName, PortDeviceObject); - - /* Increase the stacksize. We reenter our device on IOCTL_SCSI_MINIPORT */ - PortDeviceObject->StackSize++; - - /* Set the buffering strategy here... */ - PortDeviceObject->Flags |= DO_DIRECT_IO; - PortDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; - - DeviceExtension = PortDeviceObject->DeviceExtension; - RtlZeroMemory(DeviceExtension, DeviceExtensionSize); - DeviceExtension->Length = DeviceExtensionSize; - DeviceExtension->DeviceObject = PortDeviceObject; - DeviceExtension->PortNumber = SystemConfig->ScsiPortCount; - - DeviceExtension->MiniPortExtensionSize = HwInitializationData->DeviceExtensionSize; - DeviceExtension->LunExtensionSize = HwInitializationData->SpecificLuExtensionSize; - DeviceExtension->SrbExtensionSize = HwInitializationData->SrbExtensionSize; - DeviceExtension->HwStartIo = HwInitializationData->HwStartIo; - DeviceExtension->HwInterrupt = HwInitializationData->HwInterrupt; - - DeviceExtension->AdapterObject = NULL; - DeviceExtension->MapRegisterCount = 0; - DeviceExtension->PhysicalAddress.QuadPart = 0ULL; - DeviceExtension->VirtualAddress = NULL; - DeviceExtension->CommonBufferLength = 0; - - /* Initialize the device base list */ - InitializeListHead (&DeviceExtension->DeviceBaseListHead); - - /* Initialize the irp lists */ - InitializeListHead (&DeviceExtension->PendingIrpListHead); - DeviceExtension->NextIrp = NULL; - DeviceExtension->PendingIrpCount = 0; - DeviceExtension->ActiveIrpCount = 0; - - /* Initialize LUN-Extension list */ - InitializeListHead (&DeviceExtension->LunExtensionListHead); - - /* Initialize the spin lock in the controller extension */ - KeInitializeSpinLock (&DeviceExtension->Lock); - - /* Initialize the DPC object */ - IoInitializeDpcRequest (PortDeviceObject, - ScsiPortDpc); - - /* Initialize the device timer */ - DeviceExtension->TimerState = IDETimerIdle; - DeviceExtension->TimerCount = 0; - IoInitializeTimer (PortDeviceObject, - ScsiPortIoTimer, - DeviceExtension); - - /* Allocate and initialize port configuration info */ - DeviceExtension->PortConfig = ExAllocatePool (NonPagedPool, - PortConfigSize); - if (DeviceExtension->PortConfig == NULL) - { - Status = STATUS_INSUFFICIENT_RESOURCES; - goto ByeBye; - } - RtlZeroMemory (DeviceExtension->PortConfig, - PortConfigSize); - - PortConfig = DeviceExtension->PortConfig; - PortConfig->Length = sizeof(PORT_CONFIGURATION_INFORMATION); - PortConfig->SystemIoBusNumber = BusNumber; - PortConfig->AdapterInterfaceType = HwInitializationData->AdapterInterfaceType; - PortConfig->InterruptMode = - (PortConfig->AdapterInterfaceType == PCIBus) ? LevelSensitive : Latched; - PortConfig->MaximumTransferLength = SP_UNINITIALIZED_VALUE; - PortConfig->NumberOfPhysicalBreaks = SP_UNINITIALIZED_VALUE; - PortConfig->DmaChannel = SP_UNINITIALIZED_VALUE; - PortConfig->DmaPort = SP_UNINITIALIZED_VALUE; - PortConfig->DmaWidth = 0; - PortConfig->DmaSpeed = Compatible; - PortConfig->AlignmentMask = 0; - PortConfig->NumberOfAccessRanges = HwInitializationData->NumberOfAccessRanges; - PortConfig->NumberOfBuses = 0; - - for (i = 0; i < SCSI_MAXIMUM_BUSES; i++) - PortConfig->InitiatorBusId[i] = 255; - - PortConfig->ScatterGather = FALSE; - PortConfig->Master = FALSE; - PortConfig->CachesData = FALSE; - PortConfig->AdapterScansDown = FALSE; - PortConfig->AtdiskPrimaryClaimed = SystemConfig->AtDiskPrimaryAddressClaimed; - PortConfig->AtdiskSecondaryClaimed = SystemConfig->AtDiskSecondaryAddressClaimed; - PortConfig->Dma32BitAddresses = FALSE; - PortConfig->DemandMode = FALSE; - PortConfig->MapBuffers = HwInitializationData->MapBuffers; - PortConfig->NeedPhysicalAddresses = HwInitializationData->NeedPhysicalAddresses; - PortConfig->TaggedQueuing = HwInitializationData->TaggedQueuing; - PortConfig->AutoRequestSense = HwInitializationData->AutoRequestSense; - PortConfig->MultipleRequestPerLu = HwInitializationData->MultipleRequestPerLu; - PortConfig->ReceiveEvent = HwInitializationData->ReceiveEvent; - PortConfig->RealModeInitialized = FALSE; - PortConfig->BufferAccessScsiPortControlled = FALSE; - PortConfig->MaximumNumberOfTargets = SCSI_MAXIMUM_TARGETS; -// PortConfig->MaximumNumberOfLogicalUnits = SCSI_MAXIMUM_LOGICAL_UNITS; - - PortConfig->SrbExtensionSize = HwInitializationData->SrbExtensionSize; - PortConfig->SpecificLuExtensionSize = HwInitializationData->SpecificLuExtensionSize; - - PortConfig->AccessRanges = (ACCESS_RANGE(*)[])(PortConfig + 1); - - /* Search for matching PCI device */ - if ((HwInitializationData->AdapterInterfaceType == PCIBus) && - (HwInitializationData->VendorIdLength > 0) && - (HwInitializationData->VendorId != NULL) && - (HwInitializationData->DeviceIdLength > 0) && - (HwInitializationData->DeviceId != NULL)) - { - /* Get PCI device data */ - DPRINT("VendorId '%.*s' DeviceId '%.*s'\n", - HwInitializationData->VendorIdLength, - HwInitializationData->VendorId, - HwInitializationData->DeviceIdLength, - HwInitializationData->DeviceId); - - if (!SpiGetPciConfigData (HwInitializationData, - PortConfig, - BusNumber, - &SlotNumber)) - { - Status = STATUS_UNSUCCESSFUL; - goto ByeBye; - } - } - - /* Note: HwFindAdapter is called once for each bus */ - Again = FALSE; - DPRINT("Calling HwFindAdapter() for Bus %lu\n", PortConfig->SystemIoBusNumber); - Result = (HwInitializationData->HwFindAdapter)(&DeviceExtension->MiniPortDeviceExtension, - HwContext, - 0, /* BusInformation */ - "", /* ArgumentString */ - PortConfig, - &Again); - DPRINT("HwFindAdapter() Result: %lu Again: %s\n", - Result, (Again) ? "True" : "False"); - - if (Result == SP_RETURN_FOUND) - { - DPRINT("ScsiPortInitialize(): Found HBA! (%x)\n", PortConfig->BusInterruptVector); - -#if 0 - DPRINT1("SystemIoBusNumber: %x\n", PortConfig->SystemIoBusNumber); - DPRINT1("AdapterInterfaceType: %x\n", PortConfig->AdapterInterfaceType); - DPRINT1("BusInterruptLevel: %x\n", PortConfig->BusInterruptLevel); - DPRINT1("BusInterruptVector: %x\n", PortConfig->BusInterruptVector); - DPRINT1("InterruptMode: %x\n", PortConfig->InterruptMode); - DPRINT1("MaximumTransferLength: %x\n", PortConfig->MaximumTransferLength); - DPRINT1("NumberOfPhysicalBreaks: %x\n", PortConfig->NumberOfPhysicalBreaks); - DPRINT1("DmaChannel: %x\n", PortConfig->DmaChannel); - DPRINT1("DmaPort: %d\n", PortConfig->DmaPort); - DPRINT1("DmaWidth: %d\n", PortConfig->DmaWidth); - DPRINT1("DmaSpeed: %d\n", PortConfig->DmaSpeed); - DPRINT1("AlignmentMask: %d\n", PortConfig->AlignmentMask); - DPRINT1("NumberOfAccessRanges: %d\n", PortConfig->NumberOfAccessRanges); - DPRINT1("NumberOfBuses: %d\n", PortConfig->NumberOfBuses); - DPRINT1("ScatterGather: %d\n", PortConfig->ScatterGather); - DPRINT1("Master: %d\n", PortConfig->Master); - DPRINT1("CachesData: %d\n", PortConfig->CachesData); - DPRINT1("AdapterScansDown: %d\n", PortConfig->AdapterScansDown); - DPRINT1("AtdiskPrimaryClaimed: %d\n", PortConfig->AtdiskPrimaryClaimed); - DPRINT1("AtdiskSecondaryClaimed: %d\n", PortConfig->AtdiskSecondaryClaimed); - DPRINT1("Dma32BitAddresses: %d\n", PortConfig->Dma32BitAddresses); - DPRINT1("DemandMode: %d\n", PortConfig->DemandMode); - DPRINT1("MapBuffers: %d\n", PortConfig->MapBuffers); - DPRINT1("NeedPhysicalAddresses: %d\n", PortConfig->NeedPhysicalAddresses); - DPRINT1("TaggedQueuing: %d\n", PortConfig->TaggedQueuing); - DPRINT1("AutoRequestSense: %d\n", PortConfig->AutoRequestSense); - DPRINT1("MultipleRequestPerLu: %d\n", PortConfig->MultipleRequestPerLu); - DPRINT1("ReceiveEvent: %d\n", PortConfig->ReceiveEvent); - DPRINT1("RealModeInitialized: %d\n", PortConfig->RealModeInitialized); - DPRINT1("BufferAccessScsiPortControlled: %d\n", PortConfig->BufferAccessScsiPortControlled); - DPRINT1("MaximumNumberOfTargets: %d\n", PortConfig->MaximumNumberOfTargets); - DPRINT1("SlotNumber: %d\n", PortConfig->SlotNumber); - DPRINT1("BusInterruptLevel2: %x\n", PortConfig->BusInterruptLevel2); - DPRINT1("BusInterruptVector2: %x\n", PortConfig->BusInterruptVector2); - DPRINT1("InterruptMode2: %x\n", PortConfig->InterruptMode2); - DPRINT1("DmaChannel2: %d\n", PortConfig->DmaChannel2); - DPRINT1("DmaPort2: %d\n", PortConfig->DmaPort2); - DPRINT1("DmaWidth2: %d\n", PortConfig->DmaWidth2); - DPRINT1("DmaSpeed2: %d\n", PortConfig->DmaSpeed2); - DPRINT1("DeviceExtensionSize: %d\n", PortConfig->DeviceExtensionSize); - DPRINT1("SpecificLuExtensionSize: %d\n", PortConfig->SpecificLuExtensionSize); - DPRINT1("SrbExtensionSize: %d\n", PortConfig->SrbExtensionSize); - -#endif - - if (DeviceExtension->VirtualAddress == NULL && DeviceExtension->SrbExtensionSize) - { - ScsiPortGetUncachedExtension(&DeviceExtension->MiniPortDeviceExtension, - PortConfig, - 0); - } - - /* Register an interrupt handler for this device */ - MappedIrq = HalGetInterruptVector(PortConfig->AdapterInterfaceType, - PortConfig->SystemIoBusNumber, - PortConfig->BusInterruptLevel, -#if 1 -/* - * FIXME: - * Something is wrong in our interrupt conecting code. - * The promise Ultra100TX driver returns 0 for BusInterruptVector - * and a nonzero value for BusInterruptLevel. The driver does only - * work with this fix. - */ - PortConfig->BusInterruptLevel, -#else - PortConfig->BusInterruptVector, -#endif - &Dirql, - &Affinity); - DPRINT("AdapterInterfaceType %x, SystemIoBusNumber %x, BusInterruptLevel %x, BusInterruptVector %x\n", - PortConfig->AdapterInterfaceType, PortConfig->SystemIoBusNumber, - PortConfig->BusInterruptLevel, PortConfig->BusInterruptVector); - Status = IoConnectInterrupt(&DeviceExtension->Interrupt, - ScsiPortIsr, - DeviceExtension, - NULL, - MappedIrq, - Dirql, - Dirql, - PortConfig->InterruptMode, - TRUE, - Affinity, - FALSE); - if (!NT_SUCCESS(Status)) - { - DbgPrint("Could not connect interrupt %d\n", - PortConfig->BusInterruptVector); - goto ByeBye; - } - - if (!(HwInitializationData->HwInitialize)(&DeviceExtension->MiniPortDeviceExtension)) - { - DbgPrint("HwInitialize() failed!"); - Status = STATUS_UNSUCCESSFUL; - goto ByeBye; - } - - /* Initialize port capabilities */ - DeviceExtension->PortCapabilities = ExAllocatePool(NonPagedPool, - sizeof(IO_SCSI_CAPABILITIES)); - if (DeviceExtension->PortCapabilities == NULL) - { - DbgPrint("Failed to allocate port capabilities!\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto ByeBye; - } - - PortCapabilities = DeviceExtension->PortCapabilities; - PortCapabilities->Length = sizeof(IO_SCSI_CAPABILITIES); - if (PortConfig->ScatterGather == FALSE || - PortConfig->NumberOfPhysicalBreaks >= (0x100000000LL >> PAGE_SHIFT) || - PortConfig->MaximumTransferLength < PortConfig->NumberOfPhysicalBreaks * PAGE_SIZE) - { - PortCapabilities->MaximumTransferLength = - PortConfig->MaximumTransferLength; - } - else - { - PortCapabilities->MaximumTransferLength = - PortConfig->NumberOfPhysicalBreaks * PAGE_SIZE; - } - - PortCapabilities->MaximumPhysicalPages = - PortCapabilities->MaximumTransferLength / PAGE_SIZE; - PortCapabilities->SupportedAsynchronousEvents = 0; /* FIXME */ - PortCapabilities->AlignmentMask = - PortConfig->AlignmentMask; - PortCapabilities->TaggedQueuing = - PortConfig->TaggedQueuing; - PortCapabilities->AdapterScansDown = - PortConfig->AdapterScansDown; - PortCapabilities->AdapterUsesPio = TRUE; /* FIXME */ - - /* Scan the adapter for devices */ - SpiScanAdapter (DeviceExtension); - - /* Build the registry device map */ - SpiBuildDeviceMap (DeviceExtension, - (PUNICODE_STRING)Argument2); - - /* Create the dos device link */ - swprintf(DosNameBuffer, - L"\\??\\Scsi%lu:", - SystemConfig->ScsiPortCount); - RtlInitUnicodeString(&DosDeviceName, - DosNameBuffer); - IoCreateSymbolicLink(&DosDeviceName, - &DeviceName); - - /* Update the system configuration info */ - if (PortConfig->AtdiskPrimaryClaimed == TRUE) - SystemConfig->AtDiskPrimaryAddressClaimed = TRUE; - if (PortConfig->AtdiskSecondaryClaimed == TRUE) - SystemConfig->AtDiskSecondaryAddressClaimed = TRUE; - - SystemConfig->ScsiPortCount++; - PortDeviceObject = NULL; - DeviceFound = TRUE; - } - else - { - DPRINT("HwFindAdapter() Result: %lu\n", Result); - - ExFreePool (PortConfig); - IoDeleteDevice (PortDeviceObject); - PortDeviceObject = NULL; - } - - DPRINT("Bus: %lu MaxBus: %lu\n", BusNumber, MaxBus); - if (BusNumber >= MaxBus) - { - DPRINT("Scanned all buses!\n"); - Status = STATUS_SUCCESS; - goto ByeBye; - } - - if (Again == FALSE) - { - BusNumber++; - SlotNumber.u.AsULONG = 0; - } - } - -ByeBye: - /* Clean up the mess */ - if (PortDeviceObject != NULL) - { - DPRINT("Delete device: %p\n", PortDeviceObject); - - DeviceExtension = PortDeviceObject->DeviceExtension; - - if (DeviceExtension->PortCapabilities != NULL) - { - IoDisconnectInterrupt (DeviceExtension->Interrupt); - ExFreePool (DeviceExtension->PortCapabilities); - } - - if (DeviceExtension->PortConfig != NULL) - { - ExFreePool (DeviceExtension->PortConfig); - } - - IoDeleteDevice (PortDeviceObject); - } - - DPRINT("ScsiPortInitialize() done!\n"); - - return (DeviceFound == FALSE) ? Status : STATUS_SUCCESS; -} - - -/* - * @unimplemented - */ -VOID STDCALL -ScsiPortIoMapTransfer(IN PVOID HwDeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb, - IN PVOID LogicalAddress, - IN ULONG Length) -{ - DPRINT1("ScsiPortIoMapTransfer()\n"); - UNIMPLEMENTED; -} - - -/* - * @unimplemented - */ -VOID STDCALL -ScsiPortLogError(IN PVOID HwDeviceExtension, - IN PSCSI_REQUEST_BLOCK Srb OPTIONAL, - IN UCHAR PathId, - IN UCHAR TargetId, - IN UCHAR Lun, - IN ULONG ErrorCode, - IN ULONG UniqueId) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - - DPRINT1("ScsiPortLogError() called\n"); - DPRINT1("Srb %x, PathId %d, TargetId %d, Lun %d, ErrorCode %x, UniqueId %x\n", - Srb, PathId, TargetId, Lun, ErrorCode, UniqueId); - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - - - DPRINT("ScsiPortLogError() done\n"); -} - - -/* - * @implemented - */ -VOID STDCALL -ScsiPortMoveMemory(OUT PVOID Destination, - IN PVOID Source, - IN ULONG Length) -{ - RtlMoveMemory(Destination, - Source, - Length); -} - - -/* - * @implemented - */ -VOID -ScsiPortNotification(IN SCSI_NOTIFICATION_TYPE NotificationType, - IN PVOID HwDeviceExtension, - ...) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - va_list ap; - - DPRINT("ScsiPortNotification() called\n"); - - DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, - SCSI_PORT_DEVICE_EXTENSION, - MiniPortDeviceExtension); - - DPRINT("DeviceExtension %p\n", DeviceExtension); - - va_start(ap, HwDeviceExtension); - - switch (NotificationType) - { - case RequestComplete: - { - PSCSI_REQUEST_BLOCK Srb; - - Srb = (PSCSI_REQUEST_BLOCK) va_arg (ap, PSCSI_REQUEST_BLOCK); - - DPRINT("Notify: RequestComplete (Srb %p)\n", Srb); - DeviceExtension->Flags |= IRP_FLAG_COMPLETE; - Srb->SrbFlags &= ~SRB_FLAGS_IS_ACTIVE; - } - break; - - case NextRequest: - DPRINT("Notify: NextRequest\n"); - DeviceExtension->Flags |= IRP_FLAG_NEXT; - break; - - case NextLuRequest: - { - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - PSCSI_PORT_LUN_EXTENSION LunExtension; - - PathId = (UCHAR) va_arg (ap, int); - TargetId = (UCHAR) va_arg (ap, int); - Lun = (UCHAR) va_arg (ap, int); - - DPRINT ("Notify: NextLuRequest(PathId %u TargetId %u Lun %u)\n", - PathId, TargetId, Lun); - - LunExtension = SpiGetLunExtension(DeviceExtension, - PathId, - TargetId, - Lun); - if (LunExtension) - { - DeviceExtension->Flags |= IRP_FLAG_NEXT_LU; - LunExtension->Flags |= IRP_FLAG_NEXT_LU; - } - } - break; - - case ResetDetected: - DPRINT1("Notify: ResetDetected\n"); - /* FIXME: ??? */ - break; - - default: - DPRINT1 ("Unsupported notification %lu\n", NotificationType); - break; - } - if (KeGetCurrentIrql() >= DISPATCH_LEVEL) - { - IoRequestDpc(DeviceExtension->DeviceObject, - NULL, - DeviceExtension); - } - else - { - SpiProcessRequests(DeviceExtension, NULL); - } - - va_end(ap); -} - - -/* - * @implemented - */ -ULONG STDCALL -ScsiPortSetBusDataByOffset(IN PVOID DeviceExtension, - IN ULONG BusDataType, - IN ULONG SystemIoBusNumber, - IN ULONG SlotNumber, - IN PVOID Buffer, - IN ULONG Offset, - IN ULONG Length) -{ - DPRINT("ScsiPortSetBusDataByOffset()\n"); - return(HalSetBusDataByOffset(BusDataType, - SystemIoBusNumber, - SlotNumber, - Buffer, - Offset, - Length)); -} - - -/* - * @implemented - */ -BOOLEAN STDCALL -ScsiPortValidateRange(IN PVOID HwDeviceExtension, - IN INTERFACE_TYPE BusType, - IN ULONG SystemIoBusNumber, - IN SCSI_PHYSICAL_ADDRESS IoAddress, - IN ULONG NumberOfBytes, - IN BOOLEAN InIoSpace) -{ - DPRINT("ScsiPortValidateRange()\n"); - return(TRUE); -} - - -/* INTERNAL FUNCTIONS ********************************************************/ - - -static BOOLEAN -SpiGetPciConfigData (IN struct _HW_INITIALIZATION_DATA *HwInitializationData, - IN OUT PPORT_CONFIGURATION_INFORMATION PortConfig, - IN ULONG BusNumber, - IN OUT PPCI_SLOT_NUMBER NextSlotNumber) -{ - PCI_COMMON_CONFIG PciConfig; - PCI_SLOT_NUMBER SlotNumber; - ULONG DataSize; - ULONG DeviceNumber; - ULONG FunctionNumber; - CHAR VendorIdString[8]; - CHAR DeviceIdString[8]; - ULONG i; - ULONG RangeLength; - - DPRINT ("SpiGetPciConfiguration() called\n"); - - if (NextSlotNumber->u.bits.FunctionNumber >= PCI_MAX_FUNCTION) - { - NextSlotNumber->u.bits.FunctionNumber = 0; - NextSlotNumber->u.bits.DeviceNumber++; - } - - if (NextSlotNumber->u.bits.DeviceNumber >= PCI_MAX_DEVICES) - { - NextSlotNumber->u.bits.DeviceNumber = 0; - return FALSE; - } - - for (DeviceNumber = NextSlotNumber->u.bits.DeviceNumber; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber++) - { - SlotNumber.u.bits.DeviceNumber = DeviceNumber; - - for (FunctionNumber = NextSlotNumber->u.bits.FunctionNumber; FunctionNumber < PCI_MAX_FUNCTION; FunctionNumber++) - { - SlotNumber.u.bits.FunctionNumber = FunctionNumber; - - DataSize = HalGetBusData (PCIConfiguration, - BusNumber, - SlotNumber.u.AsULONG, - &PciConfig, - PCI_COMMON_HDR_LENGTH); - if (DataSize != PCI_COMMON_HDR_LENGTH) - { - if (FunctionNumber == 0) - { - break; - } - else - { - continue; - } - } - - sprintf (VendorIdString, "%04hx", PciConfig.VendorID); - sprintf (DeviceIdString, "%04hx", PciConfig.DeviceID); - - if (!_strnicmp(VendorIdString, HwInitializationData->VendorId, HwInitializationData->VendorIdLength) && - !_strnicmp(DeviceIdString, HwInitializationData->DeviceId, HwInitializationData->DeviceIdLength)) - { - DPRINT ("Found device 0x%04hx 0x%04hx at %1lu %2lu %1lu\n", - PciConfig.VendorID, - PciConfig.DeviceID, - BusNumber, - SlotNumber.u.bits.DeviceNumber, - SlotNumber.u.bits.FunctionNumber); - - PortConfig->BusInterruptLevel = - PortConfig->BusInterruptVector = PciConfig.u.type0.InterruptLine; - PortConfig->SlotNumber = SlotNumber.u.AsULONG; - - /* Initialize access ranges */ - if (PortConfig->NumberOfAccessRanges > 0) - { - if (PortConfig->NumberOfAccessRanges > PCI_TYPE0_ADDRESSES) - PortConfig->NumberOfAccessRanges = PCI_TYPE0_ADDRESSES; - - for (i = 0; i < PortConfig->NumberOfAccessRanges; i++) - { - (*PortConfig->AccessRanges)[i].RangeStart.QuadPart = - PciConfig.u.type0.BaseAddresses[i] & PCI_ADDRESS_IO_ADDRESS_MASK; - if ((*PortConfig->AccessRanges)[i].RangeStart.QuadPart != 0) - { - RangeLength = (ULONG)-1; - HalSetBusDataByOffset (PCIConfiguration, - BusNumber, - SlotNumber.u.AsULONG, - (PVOID)&RangeLength, - 0x10 + (i * sizeof(ULONG)), - sizeof(ULONG)); - - HalGetBusDataByOffset (PCIConfiguration, - BusNumber, - SlotNumber.u.AsULONG, - (PVOID)&RangeLength, - 0x10 + (i * sizeof(ULONG)), - sizeof(ULONG)); - - HalSetBusDataByOffset (PCIConfiguration, - BusNumber, - SlotNumber.u.AsULONG, - (PVOID)&PciConfig.u.type0.BaseAddresses[i], - 0x10 + (i * sizeof(ULONG)), - sizeof(ULONG)); - if (RangeLength != 0) - { - (*PortConfig->AccessRanges)[i].RangeLength = - -(RangeLength & PCI_ADDRESS_IO_ADDRESS_MASK); - (*PortConfig->AccessRanges)[i].RangeInMemory = - !(PciConfig.u.type0.BaseAddresses[i] & PCI_ADDRESS_IO_SPACE); - - DPRINT("RangeStart 0x%lX RangeLength 0x%lX RangeInMemory %s\n", - PciConfig.u.type0.BaseAddresses[i] & PCI_ADDRESS_IO_ADDRESS_MASK, - -(RangeLength & PCI_ADDRESS_IO_ADDRESS_MASK), - (PciConfig.u.type0.BaseAddresses[i] & PCI_ADDRESS_IO_SPACE)?"FALSE":"TRUE"); - } - } - } - } - - NextSlotNumber->u.bits.DeviceNumber = DeviceNumber; - NextSlotNumber->u.bits.FunctionNumber = FunctionNumber; - - PortConfig->SlotNumber = NextSlotNumber->u.AsULONG; - - NextSlotNumber->u.bits.FunctionNumber += 1; - - return TRUE; - } - - - if (FunctionNumber == 0 && !(PciConfig.HeaderType & PCI_MULTIFUNCTION)) - { - break; - } - } - NextSlotNumber->u.bits.FunctionNumber = 0; - } - - DPRINT ("No device found\n"); - - return FALSE; -} - - - -/********************************************************************** - * NAME INTERNAL - * ScsiPortCreateClose - * - * DESCRIPTION - * Answer requests for Create/Close calls: a null operation. - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceObject - * Pointer to a device object. - * - * Irp - * Pointer to an IRP. - * - * RETURN VALUE - * Status. - */ - -static NTSTATUS STDCALL -ScsiPortCreateClose(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - DPRINT("ScsiPortCreateClose()\n"); - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = FILE_OPENED; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return(STATUS_SUCCESS); -} - - -/********************************************************************** - * NAME INTERNAL - * ScsiPortDispatchScsi - * - * DESCRIPTION - * Answer requests for SCSI calls - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * Standard dispatch arguments - * - * RETURNS - * NTSTATUS - */ - -static NTSTATUS STDCALL -ScsiPortDispatchScsi(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - PSCSI_PORT_LUN_EXTENSION LunExtension; - PIO_STACK_LOCATION Stack; - PSCSI_REQUEST_BLOCK Srb; - NTSTATUS Status = STATUS_SUCCESS; - ULONG DataSize = 0; - - DPRINT("ScsiPortDispatchScsi(DeviceObject %p Irp %p)\n", - DeviceObject, Irp); - - DeviceExtension = DeviceObject->DeviceExtension; - Stack = IoGetCurrentIrpStackLocation(Irp); - - Srb = Stack->Parameters.Scsi.Srb; - if (Srb == NULL) - { - Status = STATUS_UNSUCCESSFUL; - - Irp->IoStatus.Status = Status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return(Status); - } - - DPRINT("Srb: %p\n", Srb); - DPRINT("Srb->Function: %lu\n", Srb->Function); - DPRINT("PathId: %lu TargetId: %lu Lun: %lu\n", Srb->PathId, Srb->TargetId, Srb->Lun); - - LunExtension = SpiGetLunExtension(DeviceExtension, - Srb->PathId, - Srb->TargetId, - Srb->Lun); - if (LunExtension == NULL) - { - Status = STATUS_NO_SUCH_DEVICE; - - Srb->SrbStatus = SRB_STATUS_NO_DEVICE; - Irp->IoStatus.Status = Status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return(Status); - } - - switch (Srb->Function) - { - case SRB_FUNCTION_EXECUTE_SCSI: - case SRB_FUNCTION_IO_CONTROL: - IoMarkIrpPending(Irp); - Srb->OriginalRequest = LunExtension; - Irp->Tail.Overlay.DriverContext[3] = Srb; - SpiProcessRequests(DeviceExtension, Irp); - return(STATUS_PENDING); - - case SRB_FUNCTION_SHUTDOWN: - case SRB_FUNCTION_FLUSH: - if (DeviceExtension->PortConfig->CachesData == TRUE) - { - IoMarkIrpPending(Irp); - Srb->OriginalRequest = LunExtension; - Irp->Tail.Overlay.DriverContext[3] = Srb; - SpiProcessRequests(DeviceExtension, Irp); - return(STATUS_PENDING); - } - break; - - case SRB_FUNCTION_CLAIM_DEVICE: - DPRINT (" SRB_FUNCTION_CLAIM_DEVICE\n"); - - /* Reference device object and keep the device object */ - ObReferenceObject(DeviceObject); - LunExtension->DeviceObject = DeviceObject; - LunExtension->DeviceClaimed = TRUE; - Srb->DataBuffer = DeviceObject; - break; - - case SRB_FUNCTION_RELEASE_DEVICE: - DPRINT (" SRB_FUNCTION_RELEASE_DEVICE\n"); - DPRINT ("PathId: %lu TargetId: %lu Lun: %lu\n", - Srb->PathId, Srb->TargetId, Srb->Lun); - - /* Dereference device object and clear the device object */ - ObDereferenceObject(LunExtension->DeviceObject); - LunExtension->DeviceObject = NULL; - LunExtension->DeviceClaimed = FALSE; - break; - - default: - DPRINT1("SRB function not implemented (Function %lu)\n", Srb->Function); - Status = STATUS_NOT_IMPLEMENTED; - break; - } - - Irp->IoStatus.Status = Status; - Irp->IoStatus.Information = DataSize; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return(Status); -} - - -/********************************************************************** - * NAME INTERNAL - * ScsiPortDeviceControl - * - * DESCRIPTION - * Answer requests for device control calls - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * Standard dispatch arguments - * - * RETURNS - * NTSTATUS - */ - -static NTSTATUS STDCALL -ScsiPortDeviceControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PIO_STACK_LOCATION Stack; - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - NTSTATUS Status = STATUS_SUCCESS; - - DPRINT("ScsiPortDeviceControl()\n"); - - Irp->IoStatus.Information = 0; - - - Stack = IoGetCurrentIrpStackLocation(Irp); - DeviceExtension = DeviceObject->DeviceExtension; - - switch (Stack->Parameters.DeviceIoControl.IoControlCode) - { - case IOCTL_SCSI_GET_DUMP_POINTERS: - { - PDUMP_POINTERS DumpPointers; - DPRINT(" IOCTL_SCSI_GET_DUMP_POINTERS\n"); - DumpPointers = (PDUMP_POINTERS)Irp->AssociatedIrp.SystemBuffer; - DumpPointers->DeviceObject = DeviceObject; - - Irp->IoStatus.Information = sizeof(DUMP_POINTERS); - } - break; - - case IOCTL_SCSI_GET_CAPABILITIES: - { - DPRINT(" IOCTL_SCSI_GET_CAPABILITIES\n"); - - *((PIO_SCSI_CAPABILITIES *)Irp->AssociatedIrp.SystemBuffer) = - DeviceExtension->PortCapabilities; - - Irp->IoStatus.Information = sizeof(PIO_SCSI_CAPABILITIES); - } - break; - - case IOCTL_SCSI_GET_INQUIRY_DATA: - { - DPRINT(" IOCTL_SCSI_GET_INQUIRY_DATA\n"); - - /* Copy inquiry data to the port device extension */ - Irp->IoStatus.Information = - SpiGetInquiryData(DeviceExtension, - Irp->AssociatedIrp.SystemBuffer); - DPRINT("Inquiry data size: %lu\n", Irp->IoStatus.Information); - } - break; - - case IOCTL_SCSI_PASS_THROUGH: - DPRINT(" IOCTL_SCSI_PASS_THROUGH\n"); - Status = STATUS_NOT_IMPLEMENTED; - break; - - case IOCTL_SCSI_PASS_THROUGH_DIRECT: - DPRINT(" IOCTL_SCSI_PASS_THROUGH_DIRECT\n"); - Status = STATUS_NOT_IMPLEMENTED; - break; - - case IOCTL_SCSI_MINIPORT: - DPRINT(" IOCTL_SCSI_MINIPORT\n"); - DPRINT(" Signature: %.8s\n", ((PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer)->Signature); - DPRINT(" ControlCode: 0x%lX\n", ((PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer)->ControlCode); - return SpiScsiMiniport(DeviceObject, Irp); - - default: - DPRINT1(" unknown ioctl code: 0x%lX\n", - Stack->Parameters.DeviceIoControl.IoControlCode); - Status = STATUS_INVALID_DEVICE_REQUEST; - break; - } - - Irp->IoStatus.Status = Status; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return Status; -} - -static NTSTATUS STDCALL -SpiCompletion(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context) -{ - PSCSI_REQUEST_BLOCK Srb; - - DPRINT("SpiCompletion(DeviceObject %x, Irp %x, Context %x)\n", - DeviceObject, Irp, Context); - - Srb = (PSCSI_REQUEST_BLOCK)Context; - Irp->IoStatus.Information = 0; - - switch (SRB_STATUS(Srb->SrbStatus)) - { - case SRB_STATUS_SUCCESS: - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = Srb->DataTransferLength; - break; - - case SRB_STATUS_INVALID_PATH_ID: - case SRB_STATUS_INVALID_TARGET_ID: - case SRB_STATUS_INVALID_LUN: - case SRB_STATUS_NO_HBA: - Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; - break; - - case SRB_STATUS_NO_DEVICE: - Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED; - break; - - case SRB_STATUS_BUSY: - Irp->IoStatus.Status = STATUS_DEVICE_BUSY; - break; - - case SRB_STATUS_DATA_OVERRUN: - Irp->IoStatus.Status = STATUS_DATA_OVERRUN; - Irp->IoStatus.Information = Srb->DataTransferLength; - break; - - case SRB_STATUS_INVALID_REQUEST: - Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; - break; - - default: - Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; - break; - } - - ExFreePool(Srb); - - if (Irp->PendingReturned) - { - IoMarkIrpPending (Irp); - } - return Irp->IoStatus.Status; -} - -static NTSTATUS -SpiScsiMiniport(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - PSRB_IO_CONTROL SrbIoControl; - PIO_STACK_LOCATION IrpStack; - PSCSI_REQUEST_BLOCK Srb; - PSCSI_PORT_LUN_EXTENSION LunExtension; - - DPRINT("SpiScsiMiniport(DeviceObject %x, Irp %x)\n", - DeviceObject, Irp); - - DeviceExtension = DeviceObject->DeviceExtension; - SrbIoControl = (PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer; - - IrpStack = IoGetCurrentIrpStackLocation(Irp); - if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SRB_IO_CONTROL)) - { - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_INVALID_PARAMETER; - } - if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SRB_IO_CONTROL) + SrbIoControl->Length) - { - Irp->IoStatus.Information = sizeof(SRB_IO_CONTROL) + SrbIoControl->Length; - Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_BUFFER_TOO_SMALL; - } - if (IsListEmpty(&DeviceExtension->LunExtensionListHead)) - { - Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_NO_SUCH_DEVICE; - } - - Srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK)); - if (Srb == NULL) - { - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_INSUFFICIENT_RESOURCES; - } - RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK)); - Srb->Length = sizeof(SCSI_REQUEST_BLOCK); - Srb->OriginalRequest = Irp; - Srb->Function = SRB_FUNCTION_IO_CONTROL; - Srb->DataBuffer = (PVOID)SrbIoControl; - Srb->DataTransferLength = sizeof(SRB_IO_CONTROL) + SrbIoControl->Length; - - /* We using the first extension from the list. The miniport driver is responsible to find the correct device. */ - LunExtension = CONTAINING_RECORD(DeviceExtension->LunExtensionListHead.Flink, - SCSI_PORT_LUN_EXTENSION, - List); - Srb->PathId = LunExtension->PathId; - Srb->TargetId = LunExtension->TargetId; - Srb->Lun = LunExtension->Lun; - - IrpStack = IoGetNextIrpStackLocation(Irp); - - IrpStack->MajorFunction = IRP_MJ_SCSI; - IrpStack->Parameters.Scsi.Srb = Srb; - - IoSetCompletionRoutine(Irp, - SpiCompletion, - Srb, - TRUE, - TRUE, - TRUE); - - return IoCallDriver(DeviceObject, Irp); -} - -static VOID -SpiAllocateSrbExtension(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb) -{ - ULONG index; - - DPRINT("SpiAllocateSrbExtension\n"); - - DPRINT("DeviceExtension->VirtualAddress %x, DeviceExtension->SrbExtensionSize %x\n", - DeviceExtension->VirtualAddress, DeviceExtension->SrbExtensionSize); - - Srb->SrbExtension = NULL; - if (DeviceExtension->VirtualAddress != NULL && - DeviceExtension->SrbExtensionSize > 0) - { - index = RtlFindClearBitsAndSet(&DeviceExtension->SrbExtensionAllocMap, 1, 0); - if (index != 0xffffffff) - { - DeviceExtension->CurrentSrbExtensions++; - Srb->SrbExtension = (PVOID)((ULONG_PTR)DeviceExtension->VirtualAddress + index * DeviceExtension->SrbExtensionSize); - } - } - DPRINT("%x\n", Srb->SrbExtension); -} - -static VOID -SpiFreeSrbExtension(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PSCSI_REQUEST_BLOCK Srb) -{ - ULONG index; - - if (DeviceExtension->VirtualAddress != NULL && - DeviceExtension->SrbExtensionSize > 0 && - Srb->SrbExtension != NULL) - { - index = ((ULONG_PTR)Srb->SrbExtension - (ULONG_PTR)DeviceExtension->VirtualAddress) / DeviceExtension->SrbExtensionSize; - RtlClearBits(&DeviceExtension->SrbExtensionAllocMap, index, 1); - DeviceExtension->CurrentSrbExtensions--; - } - Srb->SrbExtension = NULL; -} - - -static BOOLEAN STDCALL -ScsiPortStartPacket(IN OUT PVOID Context) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - PSCSI_REQUEST_BLOCK Srb; - PIRP Irp; - PIO_STACK_LOCATION IrpStack; - - DPRINT("ScsiPortStartPacket(Context %x) called\n", Context); - - Srb = (PSCSI_REQUEST_BLOCK)Context; - Irp = (PIRP)Srb->OriginalRequest; - IrpStack = IoGetCurrentIrpStackLocation(Irp); - DeviceExtension = IrpStack->DeviceObject->DeviceExtension; - - return(DeviceExtension->HwStartIo(&DeviceExtension->MiniPortDeviceExtension, - Srb)); -} - - -static PSCSI_PORT_LUN_EXTENSION -SpiAllocateLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - IN UCHAR PathId, - IN UCHAR TargetId, - IN UCHAR Lun) -{ - PSCSI_PORT_LUN_EXTENSION LunExtension; - ULONG LunExtensionSize; - - DPRINT("SpiAllocateLunExtension (%p %u %u %u)\n", - DeviceExtension, PathId, TargetId, Lun); - - LunExtensionSize = - sizeof(SCSI_PORT_LUN_EXTENSION) + DeviceExtension->LunExtensionSize; - DPRINT("LunExtensionSize %lu\n", LunExtensionSize); - - LunExtension = ExAllocatePool(NonPagedPool, - LunExtensionSize); - if (LunExtension == NULL) - { - return NULL; - } - - RtlZeroMemory(LunExtension, - LunExtensionSize); - - InsertTailList(&DeviceExtension->LunExtensionListHead, - &LunExtension->List); - - LunExtension->PathId = PathId; - LunExtension->TargetId = TargetId; - LunExtension->Lun = Lun; - - LunExtension->PendingIrpCount = 0; - LunExtension->ActiveIrpCount = 0; - - LunExtension->NextIrp = NULL; - - return LunExtension; -} - - -static VOID -SpiRemoveLunExtension (IN PSCSI_PORT_LUN_EXTENSION LunExtension) -{ - DPRINT("SpiRemoveLunExtension(%p) called\n", - LunExtension); - - if (LunExtension == NULL) - return; - - RemoveEntryList (&LunExtension->List); - - - /* Release LUN extersion data */ - - - ExFreePool (LunExtension); - - return; -} - - -static PSCSI_PORT_LUN_EXTENSION -SpiGetLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - IN UCHAR PathId, - IN UCHAR TargetId, - IN UCHAR Lun) -{ - PSCSI_PORT_LUN_EXTENSION LunExtension; - PLIST_ENTRY Entry; - - DPRINT("SpiGetLunExtension(%p %u %u %u) called\n", - DeviceExtension, PathId, TargetId, Lun); - - if (IsListEmpty(&DeviceExtension->LunExtensionListHead)) - return NULL; - - Entry = DeviceExtension->LunExtensionListHead.Flink; - while (Entry != &DeviceExtension->LunExtensionListHead) - { - LunExtension = CONTAINING_RECORD(Entry, - SCSI_PORT_LUN_EXTENSION, - List); - if (LunExtension->PathId == PathId && - LunExtension->TargetId == TargetId && - LunExtension->Lun == Lun) - { - return LunExtension; - } - - Entry = Entry->Flink; - } - - return NULL; -} - - -static NTSTATUS -SpiSendInquiry (IN PDEVICE_OBJECT DeviceObject, - IN OUT PSCSI_REQUEST_BLOCK Srb, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN OUT PKEVENT Event) -{ - PIO_STACK_LOCATION IrpStack; - PIRP Irp; - NTSTATUS Status; - - DPRINT ("SpiSendInquiry() called\n"); - - - KeInitializeEvent (Event, - NotificationEvent, - FALSE); - - Irp = IoBuildDeviceIoControlRequest (IOCTL_SCSI_EXECUTE_OUT, - DeviceObject, - NULL, - 0, - Srb->DataBuffer, - Srb->DataTransferLength, - TRUE, - Event, - IoStatusBlock); - if (Irp == NULL) - { - DPRINT("IoBuildDeviceIoControlRequest() failed\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - /* Attach Srb to the Irp */ - IrpStack = IoGetNextIrpStackLocation (Irp); - IrpStack->Parameters.Scsi.Srb = Srb; - Srb->OriginalRequest = Irp; - - /* Call the driver */ - Status = IoCallDriver (DeviceObject, - Irp); - - return Status; -} - -static VOID -SpiScanAdapter (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension) -{ - PSCSI_REQUEST_BLOCK Srb; - PCDB Cdb; - ULONG Bus; - ULONG Target; - PSCSI_PORT_SCAN_ADAPTER ScanDataArray; - PSCSI_PORT_SCAN_ADAPTER ScanData; - ULONG i; - ULONG MaxCount; - ULONG WaitCount; - ULONG ActiveCount; - PVOID* EventArray; - PKWAIT_BLOCK WaitBlockArray; - - DPRINT ("SpiScanAdapter() called\n"); - - MaxCount = DeviceExtension->PortConfig->NumberOfBuses * - DeviceExtension->PortConfig->MaximumNumberOfTargets; - - ScanDataArray = ExAllocatePool(NonPagedPool, MaxCount * (sizeof(SCSI_PORT_SCAN_ADAPTER) + sizeof(PVOID) + sizeof(KWAIT_BLOCK))); - if (ScanDataArray == NULL) - { - return; - } - EventArray = (PVOID*)((PUCHAR)ScanDataArray + MaxCount * sizeof(SCSI_PORT_SCAN_ADAPTER)); - WaitBlockArray = (PKWAIT_BLOCK)((PUCHAR)EventArray + MaxCount * sizeof(PVOID)); - - for (Bus = 0; Bus < DeviceExtension->PortConfig->NumberOfBuses; Bus++) - { - for (Target = 0; Target < DeviceExtension->PortConfig->MaximumNumberOfTargets; Target++) - { - ScanData = &ScanDataArray[Bus * DeviceExtension->PortConfig->MaximumNumberOfTargets + Target]; - ScanData->Bus = Bus; - ScanData->Target = Target; - ScanData->Lun = 0; - ScanData->Active = FALSE; - } - } - do - { - ActiveCount = 0; - WaitCount = 0; - for (i = 0; i < MaxCount; i++) - { - ScanData = &ScanDataArray[i]; - Srb = &ScanData->Srb; - if (ScanData->Active) - { - if (ScanData->Status == STATUS_PENDING && - 0 == KeReadStateEvent(&ScanData->Event)) - { - ActiveCount++; - continue; - } - else - { - ScanData->Status = ScanData->IoStatusBlock.Status; - } - ScanData->Active = FALSE; - DPRINT ("Target %lu Lun %lu\n", ScanData->Target, ScanData->Lun); - DPRINT ("Status %lx Srb.SrbStatus %x\n", ScanData->Status, Srb->SrbStatus); - DPRINT ("DeviceTypeQualifier %x\n", ((PINQUIRYDATA)Srb->DataBuffer)->DeviceTypeQualifier); - - if (NT_SUCCESS(ScanData->Status) && - (Srb->SrbStatus == SRB_STATUS_SUCCESS || - (Srb->SrbStatus == SRB_STATUS_DATA_OVERRUN && - /* - * FIXME: - * The NT 4.0 driver from an inic950 based scsi controller - * returns only 4 byte of inquiry data, but the device name - * is visible on NT 4.0. We must implement an other way - * to get the complete inquiry data. - */ - Srb->DataTransferLength >= /*INQUIRYDATABUFFERSIZE*/4)) && - ((PINQUIRYDATA)Srb->DataBuffer)->DeviceTypeQualifier == 0) - { - /* Copy inquiry data */ - RtlCopyMemory (&ScanData->LunExtension->InquiryData, - Srb->DataBuffer, - min(sizeof(INQUIRYDATA), Srb->DataTransferLength)); - ScanData->Lun++; - } - else - { - SpiRemoveLunExtension (ScanData->LunExtension); - ScanData->Lun = SCSI_MAXIMUM_LOGICAL_UNITS; - } - } - if (ScanData->Lun >= SCSI_MAXIMUM_LOGICAL_UNITS) - { - continue; - } - RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK)); - Srb->SrbFlags = SRB_FLAGS_DATA_IN; - Srb->DataBuffer = ScanData->DataBuffer; - Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - Srb->DataTransferLength = 255; //256; - Srb->CdbLength = 6; - Srb->Lun = ScanData->Lun; - Srb->PathId = ScanData->Bus; - Srb->TargetId = ScanData->Target; - Srb->SrbStatus = SRB_STATUS_SUCCESS; - Srb->TimeOutValue = 2; - Cdb = (PCDB) &Srb->Cdb; - - Cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; - Cdb->CDB6INQUIRY.AllocationLength = 255; - Cdb->CDB6INQUIRY.LogicalUnitNumber = ScanData->Lun; - - RtlZeroMemory(Srb->DataBuffer, 256); - - ScanData->LunExtension = SpiAllocateLunExtension (DeviceExtension, - ScanData->Bus, - ScanData->Target, - ScanData->Lun); - if (ScanData->LunExtension == NULL) - { - DPRINT1("Failed to allocate the LUN extension!\n"); - ScanData->Lun = SCSI_MAXIMUM_LOGICAL_UNITS; - continue; - } - ScanData->Status = SpiSendInquiry (DeviceExtension->DeviceObject, - Srb, - &ScanData->IoStatusBlock, - &ScanData->Event); - ScanData->Active = TRUE; - ActiveCount++; - if (ScanData->Status == STATUS_PENDING) - { - EventArray[WaitCount] = &ScanData->Event; - WaitCount++; - } - } - if (WaitCount > 0 && WaitCount == ActiveCount) - { - KeWaitForMultipleObjects(WaitCount, - EventArray, - WaitAny, - Executive, - KernelMode, - FALSE, - NULL, - WaitBlockArray); - } - } - while (ActiveCount > 0); - - ExFreePool(ScanDataArray); - - DPRINT ("SpiScanAdapter() done\n"); -} - - -static ULONG -SpiGetInquiryData(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - OUT PSCSI_ADAPTER_BUS_INFO AdapterBusInfo) -{ - PSCSI_PORT_LUN_EXTENSION LunExtension; - PSCSI_INQUIRY_DATA UnitInfo, PrevUnit; - ULONG Bus; - ULONG Target; - ULONG Lun; - ULONG UnitCount; - - DPRINT("SpiGetInquiryData() called\n"); - - /* Copy inquiry data to the port device extension */ - AdapterBusInfo->NumberOfBuses = DeviceExtension->PortConfig->NumberOfBuses; - - UnitInfo = (PSCSI_INQUIRY_DATA) - ((PUCHAR)AdapterBusInfo + sizeof(SCSI_ADAPTER_BUS_INFO) + - (sizeof(SCSI_BUS_DATA) * (AdapterBusInfo->NumberOfBuses - 1))); - - for (Bus = 0; Bus < AdapterBusInfo->NumberOfBuses; Bus++) - { - AdapterBusInfo->BusData[Bus].InitiatorBusId = - DeviceExtension->PortConfig->InitiatorBusId[Bus]; - AdapterBusInfo->BusData[Bus].InquiryDataOffset = - (ULONG)((PUCHAR)UnitInfo - (PUCHAR)AdapterBusInfo); - - PrevUnit = NULL; - UnitCount = 0; - - for (Target = 0; Target < DeviceExtension->PortConfig->MaximumNumberOfTargets; Target++) - { - for (Lun = 0; Lun < SCSI_MAXIMUM_LOGICAL_UNITS; Lun++) - { - LunExtension = SpiGetLunExtension(DeviceExtension, - Bus, - Target, - Lun); - if (LunExtension != NULL) - { - DPRINT("(Bus %lu Target %lu Lun %lu)\n", - Bus, Target, Lun); - RtlZeroMemory(UnitInfo, sizeof(*UnitInfo)); - UnitInfo->PathId = Bus; - UnitInfo->TargetId = Target; - UnitInfo->Lun = Lun; - UnitInfo->InquiryDataLength = INQUIRYDATABUFFERSIZE; - RtlCopyMemory (&UnitInfo->InquiryData, - &LunExtension->InquiryData, - INQUIRYDATABUFFERSIZE); - if (PrevUnit != NULL) - { - PrevUnit->NextInquiryDataOffset = - (ULONG)((ULONG_PTR)UnitInfo-(ULONG_PTR)AdapterBusInfo); - } - PrevUnit = UnitInfo; - UnitInfo = (PSCSI_INQUIRY_DATA)((PUCHAR)UnitInfo + sizeof(SCSI_INQUIRY_DATA)+INQUIRYDATABUFFERSIZE-1); - UnitCount++; - } - } - } - DPRINT("UnitCount: %lu\n", UnitCount); - AdapterBusInfo->BusData[Bus].NumberOfLogicalUnits = UnitCount; - if (UnitCount == 0) - { - AdapterBusInfo->BusData[Bus].InquiryDataOffset = 0; - } - } - - DPRINT("Data size: %lu\n", (ULONG)UnitInfo - (ULONG)AdapterBusInfo); - - return (ULONG)((PUCHAR)UnitInfo-(PUCHAR)AdapterBusInfo); -} - - -static BOOLEAN STDCALL -ScsiPortIsr(IN PKINTERRUPT Interrupt, - IN PVOID ServiceContext) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - - DPRINT("ScsiPortIsr() called!\n"); - - DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)ServiceContext; - - return DeviceExtension->HwInterrupt(&DeviceExtension->MiniPortDeviceExtension); -} - - -// ScsiPortDpc -// DESCRIPTION: -// -// RUN LEVEL: -// -// ARGUMENTS: -// IN PKDPC Dpc -// IN PDEVICE_OBJECT DpcDeviceObject -// IN PIRP DpcIrp -// IN PVOID DpcContext -// -static VOID STDCALL -ScsiPortDpc(IN PKDPC Dpc, - IN PDEVICE_OBJECT DpcDeviceObject, - IN PIRP DpcIrp, - IN PVOID DpcContext) -{ - PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; - - DPRINT("ScsiPortDpc(Dpc %p DpcDeviceObject %p DpcIrp %p DpcContext %p)\n", - Dpc, DpcDeviceObject, DpcIrp, DpcContext); - - DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)DpcContext; - - SpiProcessRequests(DeviceExtension, NULL); - - DPRINT("ScsiPortDpc() done\n"); -} - - -// ScsiPortIoTimer -// DESCRIPTION: -// This function handles timeouts and other time delayed processing -// -// RUN LEVEL: -// -// ARGUMENTS: -// IN PDEVICE_OBJECT DeviceObject Device object registered with timer -// IN PVOID Context the Controller extension for the -// controller the device is on -// -static VOID STDCALL -ScsiPortIoTimer(PDEVICE_OBJECT DeviceObject, - PVOID Context) -{ - DPRINT1("ScsiPortIoTimer()\n"); -} - - -static PSCSI_REQUEST_BLOCK -ScsiPortInitSenseRequestSrb(PSCSI_REQUEST_BLOCK OriginalSrb) -{ - PSCSI_REQUEST_BLOCK Srb; - ULONG Length; - PCDB Cdb; - - Length = sizeof(SCSI_REQUEST_BLOCK) + sizeof(SENSE_DATA) + 32; - Srb = ExAllocatePoolWithTag(NonPagedPool, - Length, - TAG('S', 'S', 'r', 'b')); - if (Srb == NULL) - { - return NULL; - } - - RtlZeroMemory(Srb, Length); - - Srb->PathId = OriginalSrb->PathId; - Srb->TargetId = OriginalSrb->TargetId; - Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - Srb->Length = sizeof(SCSI_REQUEST_BLOCK); - Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; - Srb->OriginalRequest = OriginalSrb->OriginalRequest; - - Srb->TimeOutValue = 4; - - Srb->CdbLength = 6; - /* The DataBuffer must be located in contiguous physical memory if - * the miniport driver uses dma for the sense info. The size of - * the sense data is 18 byte. If the buffer starts at a 32 byte - * boundary than is the buffer always in one memory page. - */ - Srb->DataBuffer = (PVOID)ROUND_UP((ULONG_PTR)(Srb + 1), 32); - Srb->DataTransferLength = sizeof(SENSE_DATA); - - Cdb = (PCDB)Srb->Cdb; - Cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE; - Cdb->CDB6INQUIRY.AllocationLength = sizeof(SENSE_DATA); - - return(Srb); -} - -/********************************************************************** - * NAME INTERNAL - * SpiBuildDeviceMap - * - * DESCRIPTION - * Builds the registry device map of all device which are attached - * to the given SCSI HBA port. The device map is located at: - * \Registry\Machine\DeviceMap\Scsi - * - * RUN LEVEL - * PASSIVE_LEVEL - * - * ARGUMENTS - * DeviceExtension - * ... - * - * RegistryPath - * Name of registry driver service key. - * - * RETURNS - * NTSTATUS - */ - -static NTSTATUS -SpiBuildDeviceMap (PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PUNICODE_STRING RegistryPath) -{ - PSCSI_PORT_LUN_EXTENSION LunExtension; - OBJECT_ATTRIBUTES ObjectAttributes; - UNICODE_STRING KeyName = - RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi"); - UNICODE_STRING ValueName; - WCHAR NameBuffer[64]; - ULONG Disposition; - HANDLE ScsiKey; - HANDLE ScsiPortKey = NULL; - HANDLE ScsiBusKey = NULL; - HANDLE ScsiInitiatorKey = NULL; - HANDLE ScsiTargetKey = NULL; - HANDLE ScsiLunKey = NULL; - ULONG BusNumber; - ULONG Target; - ULONG CurrentTarget; - ULONG Lun; - PWCHAR DriverName; - ULONG UlongData; - PWCHAR TypeName; - NTSTATUS Status; - - DPRINT("SpiBuildDeviceMap() called\n"); - - if (DeviceExtension == NULL || RegistryPath == NULL) - { - DPRINT1("Invalid parameter\n"); - return(STATUS_INVALID_PARAMETER); - } - - /* Open or create the 'Scsi' subkey */ - InitializeObjectAttributes(&ObjectAttributes, - &KeyName, - OBJ_CASE_INSENSITIVE | OBJ_OPENIF, - 0, - NULL); - Status = ZwCreateKey(&ScsiKey, - KEY_ALL_ACCESS, - &ObjectAttributes, - 0, - NULL, - REG_OPTION_VOLATILE, - &Disposition); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); - return(Status); - } - - /* Create new 'Scsi Port X' subkey */ - DPRINT("Scsi Port %lu\n", - DeviceExtension->PortNumber); - - swprintf(NameBuffer, - L"Scsi Port %lu", - DeviceExtension->PortNumber); - RtlInitUnicodeString(&KeyName, - NameBuffer); - InitializeObjectAttributes(&ObjectAttributes, - &KeyName, - 0, - ScsiKey, - NULL); - Status = ZwCreateKey(&ScsiPortKey, - KEY_ALL_ACCESS, - &ObjectAttributes, - 0, - NULL, - REG_OPTION_VOLATILE, - &Disposition); - ZwClose(ScsiKey); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); - return(Status); - } - - /* - * Create port-specific values - */ - - /* Set 'DMA Enabled' (REG_DWORD) value */ - UlongData = (ULONG)!DeviceExtension->PortCapabilities->AdapterUsesPio; - DPRINT(" DMA Enabled = %s\n", (UlongData) ? "TRUE" : "FALSE"); - RtlInitUnicodeString(&ValueName, - L"DMA Enabled"); - Status = ZwSetValueKey(ScsiPortKey, - &ValueName, - 0, - REG_DWORD, - &UlongData, - sizeof(ULONG)); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwSetValueKey('DMA Enabled') failed (Status %lx)\n", Status); - ZwClose(ScsiPortKey); - return(Status); - } - - /* Set 'Driver' (REG_SZ) value */ - DriverName = wcsrchr(RegistryPath->Buffer, L'\\') + 1; - RtlInitUnicodeString(&ValueName, - L"Driver"); - Status = ZwSetValueKey(ScsiPortKey, - &ValueName, - 0, - REG_SZ, - DriverName, - (wcslen(DriverName) + 1) * sizeof(WCHAR)); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwSetValueKey('Driver') failed (Status %lx)\n", Status); - ZwClose(ScsiPortKey); - return(Status); - } - - /* Set 'Interrupt' (REG_DWORD) value (NT4 only) */ - UlongData = (ULONG)DeviceExtension->PortConfig->BusInterruptLevel; - DPRINT(" Interrupt = %lu\n", UlongData); - RtlInitUnicodeString(&ValueName, - L"Interrupt"); - Status = ZwSetValueKey(ScsiPortKey, - &ValueName, - 0, - REG_DWORD, - &UlongData, - sizeof(ULONG)); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwSetValueKey('Interrupt') failed (Status %lx)\n", Status); - ZwClose(ScsiPortKey); - return(Status); - } - - /* Set 'IOAddress' (REG_DWORD) value (NT4 only) */ - UlongData = ScsiPortConvertPhysicalAddressToUlong((*DeviceExtension->PortConfig->AccessRanges)[0].RangeStart); - DPRINT(" IOAddress = %lx\n", UlongData); - RtlInitUnicodeString(&ValueName, - L"IOAddress"); - Status = ZwSetValueKey(ScsiPortKey, - &ValueName, - 0, - REG_DWORD, - &UlongData, - sizeof(ULONG)); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwSetValueKey('IOAddress') failed (Status %lx)\n", Status); - ZwClose(ScsiPortKey); - return(Status); - } - - /* Enumerate buses */ - for (BusNumber = 0; BusNumber < DeviceExtension->PortConfig->NumberOfBuses; BusNumber++) - { - /* Create 'Scsi Bus X' key */ - DPRINT(" Scsi Bus %lu\n", BusNumber); - swprintf(NameBuffer, - L"Scsi Bus %lu", - BusNumber); - RtlInitUnicodeString(&KeyName, - NameBuffer); - InitializeObjectAttributes(&ObjectAttributes, - &KeyName, - 0, - ScsiPortKey, - NULL); - Status = ZwCreateKey(&ScsiBusKey, - KEY_ALL_ACCESS, - &ObjectAttributes, - 0, - NULL, - REG_OPTION_VOLATILE, - &Disposition); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); - goto ByeBye; - } - - /* Create 'Initiator Id X' key */ - DPRINT(" Initiator Id %u\n", - DeviceExtension->PortConfig->InitiatorBusId[BusNumber]); - swprintf(NameBuffer, - L"Initiator Id %u", - (unsigned int)(UCHAR)DeviceExtension->PortConfig->InitiatorBusId[BusNumber]); - RtlInitUnicodeString(&KeyName, - NameBuffer); - InitializeObjectAttributes(&ObjectAttributes, - &KeyName, - 0, - ScsiBusKey, - NULL); - Status = ZwCreateKey(&ScsiInitiatorKey, - KEY_ALL_ACCESS, - &ObjectAttributes, - 0, - NULL, - REG_OPTION_VOLATILE, - &Disposition); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); - goto ByeBye; - } - - /* FIXME: Are there any initiator values (??) */ - - ZwClose(ScsiInitiatorKey); - ScsiInitiatorKey = NULL; - - - /* Enumerate targets */ - CurrentTarget = (ULONG)-1; - ScsiTargetKey = NULL; - for (Target = 0; Target < DeviceExtension->PortConfig->MaximumNumberOfTargets; Target++) - { - for (Lun = 0; Lun < SCSI_MAXIMUM_LOGICAL_UNITS; Lun++) - { - LunExtension = SpiGetLunExtension(DeviceExtension, - BusNumber, - Target, - Lun); - if (LunExtension != NULL) - { - if (Target != CurrentTarget) - { - /* Close old target key */ - if (ScsiTargetKey != NULL) - { - ZwClose(ScsiTargetKey); - ScsiTargetKey = NULL; - } - - /* Create 'Target Id X' key */ - DPRINT(" Target Id %lu\n", Target); - swprintf(NameBuffer, - L"Target Id %lu", - Target); - RtlInitUnicodeString(&KeyName, - NameBuffer); - InitializeObjectAttributes(&ObjectAttributes, - &KeyName, - 0, - ScsiBusKey, - NULL); - Status = ZwCreateKey(&ScsiTargetKey, - KEY_ALL_ACCESS, - &ObjectAttributes, - 0, - NULL, - REG_OPTION_VOLATILE, - &Disposition); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); - goto ByeBye; - } - - CurrentTarget = Target; - } - - /* Create 'Logical Unit Id X' key */ - DPRINT(" Logical Unit Id %lu\n", Lun); - swprintf(NameBuffer, - L"Logical Unit Id %lu", - Lun); - RtlInitUnicodeString(&KeyName, - NameBuffer); - InitializeObjectAttributes(&ObjectAttributes, - &KeyName, - 0, - ScsiTargetKey, - NULL); - Status = ZwCreateKey(&ScsiLunKey, - KEY_ALL_ACCESS, - &ObjectAttributes, - 0, - NULL, - REG_OPTION_VOLATILE, - &Disposition); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); - goto ByeBye; - } - - /* Set 'Identifier' (REG_SZ) value */ - swprintf(NameBuffer, - L"%.8S%.16S%.4S", - LunExtension->InquiryData.VendorId, - LunExtension->InquiryData.ProductId, - LunExtension->InquiryData.ProductRevisionLevel); - DPRINT(" Identifier = '%S'\n", NameBuffer); - RtlInitUnicodeString(&ValueName, - L"Identifier"); - Status = ZwSetValueKey(ScsiLunKey, - &ValueName, - 0, - REG_SZ, - NameBuffer, - (wcslen(NameBuffer) + 1) * sizeof(WCHAR)); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwSetValueKey('Identifier') failed (Status %lx)\n", Status); - goto ByeBye; - } - - /* Set 'Type' (REG_SZ) value */ - switch (LunExtension->InquiryData.DeviceType) - { - case 0: - TypeName = L"DiskPeripheral"; - break; - case 1: - TypeName = L"TapePeripheral"; - break; - case 2: - TypeName = L"PrinterPeripheral"; - break; - case 4: - TypeName = L"WormPeripheral"; - break; - case 5: - TypeName = L"CdRomPeripheral"; - break; - case 6: - TypeName = L"ScannerPeripheral"; - break; - case 7: - TypeName = L"OpticalDiskPeripheral"; - break; - case 8: - TypeName = L"MediumChangerPeripheral"; - break; - case 9: - TypeName = L"CommunicationPeripheral"; - break; - default: - TypeName = L"OtherPeripheral"; - break; - } - DPRINT(" Type = '%S'\n", TypeName); - RtlInitUnicodeString(&ValueName, - L"Type"); - Status = ZwSetValueKey(ScsiLunKey, - &ValueName, - 0, - REG_SZ, - TypeName, - (wcslen(TypeName) + 1) * sizeof(WCHAR)); - if (!NT_SUCCESS(Status)) - { - DPRINT("ZwSetValueKey('Type') failed (Status %lx)\n", Status); - goto ByeBye; - } - - ZwClose(ScsiLunKey); - ScsiLunKey = NULL; - } - } - - /* Close old target key */ - if (ScsiTargetKey != NULL) - { - ZwClose(ScsiTargetKey); - ScsiTargetKey = NULL; - } - } - - ZwClose(ScsiBusKey); - ScsiBusKey = NULL; - } - -ByeBye: - if (ScsiLunKey != NULL) - ZwClose (ScsiLunKey); - - if (ScsiInitiatorKey != NULL) - ZwClose (ScsiInitiatorKey); - - if (ScsiTargetKey != NULL) - ZwClose (ScsiTargetKey); - - if (ScsiBusKey != NULL) - ZwClose (ScsiBusKey); - - if (ScsiPortKey != NULL) - ZwClose (ScsiPortKey); - - DPRINT("SpiBuildDeviceMap() done\n"); - - return Status; -} - -static VOID -SpiRemoveActiveIrp(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PIRP Irp, - PIRP PrevIrp) -{ - PSCSI_PORT_LUN_EXTENSION LunExtension; - PIRP CurrentIrp; - LunExtension = Irp->Tail.Overlay.DriverContext[2]; - InterlockedDecrement((PLONG)&LunExtension->ActiveIrpCount); - InterlockedDecrement((PLONG)&DeviceExtension->ActiveIrpCount); - if (PrevIrp) - { - (void)InterlockedExchangePointer(&PrevIrp->Tail.Overlay.DriverContext[0], - Irp->Tail.Overlay.DriverContext[0]); - } - else - { - (void)InterlockedExchangePointer(&DeviceExtension->NextIrp, - Irp->Tail.Overlay.DriverContext[0]); - } - if (LunExtension->NextIrp == Irp) - { - (void)InterlockedExchangePointer(&LunExtension->NextIrp, - Irp->Tail.Overlay.DriverContext[1]); - return; - } - else - { - CurrentIrp = LunExtension->NextIrp; - while (CurrentIrp) - { - if (CurrentIrp->Tail.Overlay.DriverContext[1] == Irp) - { - (void)InterlockedExchangePointer(&CurrentIrp->Tail.Overlay.DriverContext[1], - Irp->Tail.Overlay.DriverContext[1]); - return; - } - CurrentIrp = CurrentIrp->Tail.Overlay.DriverContext[1]; - } - KEBUGCHECK(0); - } -} - -static VOID -SpiAddActiveIrp(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - PIRP Irp) -{ - PSCSI_PORT_LUN_EXTENSION LunExtension; - PSCSI_REQUEST_BLOCK Srb; - LunExtension = Irp->Tail.Overlay.DriverContext[2]; - Srb = Irp->Tail.Overlay.DriverContext[3]; - Irp->Tail.Overlay.DriverContext[0] = (PVOID)DeviceExtension->NextIrp; - (void)InterlockedExchangePointer(&DeviceExtension->NextIrp, Irp); - Irp->Tail.Overlay.DriverContext[1] = (PVOID)LunExtension->NextIrp; - (void)InterlockedExchangePointer(&LunExtension->NextIrp, Irp); -} - -static VOID -SpiProcessRequests(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - IN PIRP NextIrp) -{ - /* - * Using of some fields from Srb and Irp while processing requests: - * - * NextIrp on entry: - * Srb->OriginalRequest -> LunExtension - * Irp->Tail.Overlay.DriverContext[3] -> original Srb - * IoStack->Parameters.Scsi.Srb -> original Srb - * - * Irp is within the pending irp list: - * Srb->OriginalRequest -> LunExtension - * Irp->Tail.Overlay.DriverContext[0] and DriverContext[1] -> ListEntry for queue - * Irp->Tail.Overlay.DriverContext[2] -> sort key (from Srb->QueueSortKey) - * Irp->Tail.Overlay.DriverContext[3] -> current Srb (original or sense request) - * IoStack->Parameters.Scsi.Srb -> original Srb - * - * Irp is within the active irp list or while other processing: - * Srb->OriginalRequest -> Irp - * Irp->Tail.Overlay.DriverContext[0] -> next irp, DeviceExtension->NextIrp is head. - * Irp->Tail.Overlay.DriverContext[1] -> next irp, LunExtension->NextIrp is head. - * Irp->Tail.Overlay.DriverContext[2] -> LunExtension - * Irp->Tail.Overlay.DriverContext[3] -> current Srb (original or sense request) - * IoStack->Parameters.Scsi.Srb -> original Srb - */ - PIO_STACK_LOCATION IrpStack; - PSCSI_PORT_LUN_EXTENSION LunExtension; - PLIST_ENTRY ListEntry; - KIRQL oldIrql; - PIRP Irp; - LIST_ENTRY NextIrpListHead; - LIST_ENTRY CompleteIrpListHead; - PSCSI_REQUEST_BLOCK Srb; - PSCSI_REQUEST_BLOCK OriginalSrb; - PIRP PrevIrp; - - DPRINT("SpiProcessRequests() called\n"); - - InitializeListHead(&NextIrpListHead); - InitializeListHead(&CompleteIrpListHead); - - KeAcquireSpinLock(&DeviceExtension->Lock, &oldIrql); - - if (NextIrp) - { - Srb = NextIrp->Tail.Overlay.DriverContext[3]; - /* - * FIXME: - * Is this the right place to set this flag ? - */ - NextIrp->Tail.Overlay.DriverContext[2] = (PVOID)Srb->QueueSortKey; - LunExtension = Srb->OriginalRequest; - - ListEntry = DeviceExtension->PendingIrpListHead.Flink; - while (ListEntry != &DeviceExtension->PendingIrpListHead) - { - Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.DriverContext[0]); - if ((ULONG)Irp->Tail.Overlay.DriverContext[2] > Srb->QueueSortKey) - { - break; - } - ListEntry = ListEntry->Flink; - } - InsertTailList(ListEntry, (PLIST_ENTRY)&NextIrp->Tail.Overlay.DriverContext[0]); - DeviceExtension->PendingIrpCount++; - LunExtension->PendingIrpCount++; - } - - while (DeviceExtension->Flags & IRP_FLAG_COMPLETE || - (((DeviceExtension->SrbExtensionSize == 0 || DeviceExtension->CurrentSrbExtensions < DeviceExtension->MaxSrbExtensions) && - DeviceExtension->PendingIrpCount > 0 && - (DeviceExtension->Flags & (IRP_FLAG_NEXT|IRP_FLAG_NEXT_LU) || DeviceExtension->NextIrp == NULL)))) - { - DPRINT ("RequestComplete %d, NextRequest %d, NextLuRequest %d, PendingIrpCount %d, ActiveIrpCount %d\n", - DeviceExtension->Flags & IRP_FLAG_COMPLETE ? 1 : 0, - DeviceExtension->Flags & IRP_FLAG_NEXT ? 1 : 0, - DeviceExtension->Flags & IRP_FLAG_NEXT_LU ? 1 : 0, - DeviceExtension->PendingIrpCount, - DeviceExtension->ActiveIrpCount); - - - if (DeviceExtension->Flags & IRP_FLAG_COMPLETE) - { - DeviceExtension->Flags &= ~IRP_FLAG_COMPLETE; - PrevIrp = NULL; - Irp = DeviceExtension->NextIrp; - while (Irp) - { - NextIrp = (PIRP)Irp->Tail.Overlay.DriverContext[0]; - Srb = Irp->Tail.Overlay.DriverContext[3]; - if (!(Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) - { - BOOLEAN CompleteThisRequest; - LunExtension = Irp->Tail.Overlay.DriverContext[2]; - IrpStack = IoGetCurrentIrpStackLocation(Irp); - OriginalSrb = IrpStack->Parameters.Scsi.Srb; - - if (Srb->SrbStatus == SRB_STATUS_BUSY) - { - CompleteThisRequest = FALSE; - Irp->Tail.Overlay.DriverContext[3] = Srb; - - SpiRemoveActiveIrp(DeviceExtension, Irp, PrevIrp); - SpiFreeSrbExtension(DeviceExtension, OriginalSrb); - - Srb->OriginalRequest = LunExtension; - Irp->Tail.Overlay.DriverContext[2] = 0; - - InsertHeadList(&DeviceExtension->PendingIrpListHead, (PLIST_ENTRY)&Irp->Tail.Overlay.DriverContext[0]); - DeviceExtension->PendingIrpCount++; - LunExtension->PendingIrpCount++; - Irp = NextIrp; - continue; - } - - if (OriginalSrb != Srb) - { - SENSE_DATA* SenseInfoBuffer; - - SenseInfoBuffer = Srb->DataBuffer; - - DPRINT("Got sense data!\n"); - - DPRINT("Valid: %x\n", SenseInfoBuffer->Valid); - DPRINT("ErrorCode: %x\n", SenseInfoBuffer->ErrorCode); - DPRINT("SenseKey: %x\n", SenseInfoBuffer->SenseKey); - DPRINT("SenseCode: %x\n", SenseInfoBuffer->AdditionalSenseCode); - - /* Copy sense data */ - RtlCopyMemory(OriginalSrb->SenseInfoBuffer, - SenseInfoBuffer, - sizeof(SENSE_DATA)); - OriginalSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; - OriginalSrb->SrbExtension = Srb->SrbExtension; - ExFreePool(Srb); - CompleteThisRequest = TRUE; - } - else if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS && - Srb->ScsiStatus == SCSISTAT_CHECK_CONDITION && - Srb->SenseInfoBuffer != NULL && - Srb->SenseInfoBufferLength >= sizeof(SENSE_DATA) && - !(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) - { - DPRINT("SCSIOP_REQUEST_SENSE required!\n"); - - Srb = ScsiPortInitSenseRequestSrb(OriginalSrb); - - if (Srb) - { - CompleteThisRequest = FALSE; - Irp->Tail.Overlay.DriverContext[3] = Srb; - SpiRemoveActiveIrp(DeviceExtension, Irp, PrevIrp); - SpiFreeSrbExtension(DeviceExtension, OriginalSrb); - - Srb->OriginalRequest = LunExtension; - Irp->Tail.Overlay.DriverContext[2] = 0; - - InsertHeadList(&DeviceExtension->PendingIrpListHead, (PLIST_ENTRY)&Irp->Tail.Overlay.DriverContext[0]); - DeviceExtension->PendingIrpCount++; - LunExtension->PendingIrpCount++; - Irp = NextIrp; - continue; - } - else - { - CompleteThisRequest = TRUE; - } - } - else - { - DPRINT("Complete Request\n"); - CompleteThisRequest = TRUE; - } - if (CompleteThisRequest) - { - SpiRemoveActiveIrp(DeviceExtension, Irp, PrevIrp); - InsertHeadList(&CompleteIrpListHead, (PLIST_ENTRY)&Irp->Tail.Overlay.DriverContext[0]); - SpiFreeSrbExtension(DeviceExtension, OriginalSrb); - } - else - { - PrevIrp = Irp; - } - Irp = NextIrp; - continue; - } - PrevIrp = Irp; - Irp = NextIrp; - } - } - if (!IsListEmpty(&CompleteIrpListHead)) - { - KeReleaseSpinLockFromDpcLevel(&DeviceExtension->Lock); - while (!IsListEmpty(&CompleteIrpListHead)) - { - ListEntry = RemoveTailList(&CompleteIrpListHead); - Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.DriverContext[0]); - IoCompleteRequest(Irp, IO_NO_INCREMENT); - } - KeAcquireSpinLockAtDpcLevel(&DeviceExtension->Lock); - } - if (DeviceExtension->Flags & (IRP_FLAG_NEXT|IRP_FLAG_NEXT_LU) && - (DeviceExtension->SrbExtensionSize == 0 || DeviceExtension->CurrentSrbExtensions < DeviceExtension->MaxSrbExtensions)) - { - BOOLEAN StartThisRequest; - ListEntry = DeviceExtension->PendingIrpListHead.Flink; - while (ListEntry != &DeviceExtension->PendingIrpListHead) - { - Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.DriverContext[0]); - ListEntry = ListEntry->Flink; - Srb = Irp->Tail.Overlay.DriverContext[3]; - LunExtension = Srb->OriginalRequest; - if (DeviceExtension->SrbExtensionSize > 0 && - DeviceExtension->CurrentSrbExtensions >= DeviceExtension->MaxSrbExtensions) - { - break; - } - if (LunExtension->Flags & IRP_FLAG_NEXT_LU) - { - StartThisRequest = TRUE; - LunExtension->Flags &= ~IRP_FLAG_NEXT_LU; - DeviceExtension->Flags &= ~IRP_FLAG_NEXT_LU; - } - else if (DeviceExtension->Flags & IRP_FLAG_NEXT && - LunExtension->ActiveIrpCount == 0) - { - StartThisRequest = TRUE; - DeviceExtension->Flags &= ~IRP_FLAG_NEXT; - } - else - { - StartThisRequest = FALSE; - } - if (StartThisRequest) - { - LunExtension->PendingIrpCount--; - DeviceExtension->PendingIrpCount--; - Srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE; - LunExtension->ActiveIrpCount++; - DeviceExtension->ActiveIrpCount++; - - RemoveEntryList((PLIST_ENTRY)&Irp->Tail.Overlay.DriverContext[0]); - Irp->Tail.Overlay.DriverContext[2] = LunExtension; - Srb->OriginalRequest = Irp; - SpiAllocateSrbExtension(DeviceExtension, Srb); - - InsertHeadList(&NextIrpListHead, (PLIST_ENTRY)&Irp->Tail.Overlay.DriverContext[0]); - } - } - } - - if (!IsListEmpty(&NextIrpListHead)) - { - while (!IsListEmpty(&NextIrpListHead)) - { - ListEntry = RemoveTailList(&NextIrpListHead); - Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.DriverContext[0]); - KeReleaseSpinLockFromDpcLevel(&DeviceExtension->Lock); - - // Start this Irp - SpiStartIo(DeviceExtension, Irp); - KeAcquireSpinLockAtDpcLevel(&DeviceExtension->Lock); - } - } - - if (!IsListEmpty(&DeviceExtension->PendingIrpListHead) && - DeviceExtension->NextIrp == NULL && - (DeviceExtension->SrbExtensionSize == 0 || DeviceExtension->CurrentSrbExtensions < DeviceExtension->MaxSrbExtensions)) - { - ListEntry = RemoveHeadList(&DeviceExtension->PendingIrpListHead); - Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.DriverContext[0]); - Srb = Irp->Tail.Overlay.DriverContext[3]; - LunExtension = Srb->OriginalRequest; - Irp->Tail.Overlay.DriverContext[2] = LunExtension; - Srb->OriginalRequest = Irp; - - LunExtension->PendingIrpCount--; - DeviceExtension->PendingIrpCount--; - Srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE; - LunExtension->ActiveIrpCount++; - DeviceExtension->ActiveIrpCount++; - - SpiAllocateSrbExtension(DeviceExtension, Srb); - KeReleaseSpinLockFromDpcLevel(&DeviceExtension->Lock); - - /* Start this irp */ - SpiStartIo(DeviceExtension, Irp); - KeAcquireSpinLockAtDpcLevel(&DeviceExtension->Lock); - } - } - KeReleaseSpinLock(&DeviceExtension->Lock, oldIrql); - - DPRINT("SpiProcessRequests() done\n"); - } - -static VOID -SpiStartIo(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, - IN PIRP Irp) -{ - PSCSI_PORT_LUN_EXTENSION LunExtension; - PSCSI_REQUEST_BLOCK Srb; - - DPRINT("SpiStartIo() called!\n"); - - assert(KeGetCurrentIrql() == DISPATCH_LEVEL); - - Srb = Irp->Tail.Overlay.DriverContext[3]; - LunExtension = Irp->Tail.Overlay.DriverContext[2]; - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = Srb->DataTransferLength; - - SpiAddActiveIrp(DeviceExtension, Irp); - - if (!KeSynchronizeExecution(DeviceExtension->Interrupt, - ScsiPortStartPacket, - Srb)) - { - DPRINT1("Synchronization failed!\n"); - DPRINT1("Irp %x, Srb->Function %02x, Srb->Cdb[0] %02x, Srb->SrbStatus %02x\n", Irp, Srb->Function, Srb->Cdb[0], Srb->SrbStatus); - ScsiPortNotification(RequestComplete, - &DeviceExtension->MiniPortDeviceExtension, - Srb); - } - - DPRINT("SpiStartIo() done\n"); -} - - - -/* EOF */ diff --git a/reactos/drivers/storage/scsiport/scsiport.def b/reactos/drivers/storage/scsiport/scsiport.def deleted file mode 100644 index 0ffd933c7f8..00000000000 --- a/reactos/drivers/storage/scsiport/scsiport.def +++ /dev/null @@ -1,52 +0,0 @@ -; $Id$ -; -; scsiport.def - export definition file for scsiport driver -; -LIBRARY SCSIPORT.SYS -EXPORTS -ScsiDebugPrint -ScsiPortCompleteRequest@20 -ScsiPortConvertPhysicalAddressToUlong@8 -ScsiPortConvertUlongToPhysicalAddress@4=NTOSKRNL.RtlConvertUlongToLargeInteger -ScsiPortFlushDma@4 -ScsiPortFreeDeviceBase@8 -ScsiPortGetBusData@24 -ScsiPortGetDeviceBase@28 -ScsiPortGetLogicalUnit@16 -ScsiPortGetPhysicalAddress@16 -ScsiPortGetSrb@20 -ScsiPortGetUncachedExtension@12 -ScsiPortGetVirtualAddress@12 -ScsiPortInitialize@16 -ScsiPortIoMapTransfer@16 -ScsiPortLogError@28 -ScsiPortMoveMemory@12 -ScsiPortNotification -ScsiPortReadPortBufferUchar@12=HAL.READ_PORT_BUFFER_UCHAR -ScsiPortReadPortBufferUshort@12=HAL.READ_PORT_BUFFER_USHORT -ScsiPortReadPortBufferUlong@12=HAL.READ_PORT_BUFFER_ULONG -ScsiPortReadPortUchar@4=HAL.READ_PORT_UCHAR -ScsiPortReadPortUshort@4=HAL.READ_PORT_USHORT -ScsiPortReadPortUlong@4=HAL.READ_PORT_ULONG -ScsiPortReadRegisterBufferUchar@12=NTOSKRNL.READ_REGISTER_BUFFER_UCHAR -ScsiPortReadRegisterBufferUshort@12=NTOSKRNL.READ_REGISTER_BUFFER_USHORT -ScsiPortReadRegisterBufferUlong@12=NTOSKRNL.READ_REGISTER_BUFFER_ULONG -ScsiPortReadRegisterUchar@4=NTOSKRNL.READ_REGISTER_UCHAR -ScsiPortReadRegisterUshort@4=NTOSKRNL.READ_REGISTER_USHORT -ScsiPortReadRegisterUlong@4=NTOSKRNL.READ_REGISTER_ULONG -ScsiPortSetBusDataByOffset@28 -ScsiPortStallExecution@4=HAL.KeStallExecutionProcessor -ScsiPortValidateRange@28 -ScsiPortWritePortUchar@8=HAL.WRITE_PORT_UCHAR -ScsiPortWritePortUshort@8=HAL.WRITE_PORT_USHORT -ScsiPortWritePortUlong@8=HAL.WRITE_PORT_ULONG -ScsiPortWritePortBufferUchar@12=HAL.WRITE_PORT_BUFFER_UCHAR -ScsiPortWritePortBufferUshort@12=HAL.WRITE_PORT_BUFFER_USHORT -ScsiPortWritePortBufferUlong@12=HAL.WRITE_PORT_BUFFER_ULONG -ScsiPortWriteRegisterBufferUchar@12=NTOSKRNL.WRITE_REGISTER_BUFFER_UCHAR -ScsiPortWriteRegisterBufferUshort@12=NTOSKRNL.WRITE_REGISTER_BUFFER_USHORT -ScsiPortWriteRegisterBufferUlong@12=NTOSKRNL.WRITE_REGISTER_BUFFER_ULONG -ScsiPortWriteRegisterUchar@8=NTOSKRNL.WRITE_REGISTER_UCHAR -ScsiPortWriteRegisterUshort@8=NTOSKRNL.WRITE_REGISTER_USHORT -ScsiPortWriteRegisterUlong@8=NTOSKRNL.WRITE_REGISTER_ULONG -;EOF diff --git a/reactos/drivers/storage/scsiport/scsiport.rbuild b/reactos/drivers/storage/scsiport/scsiport.rbuild deleted file mode 100644 index c84dce84d5d..00000000000 --- a/reactos/drivers/storage/scsiport/scsiport.rbuild +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - . - ntoskrnl - hal - scsiport.c - scsiport.rc - diff --git a/reactos/drivers/storage/scsiport/scsiport.rc b/reactos/drivers/storage/scsiport/scsiport.rc deleted file mode 100644 index 7d532f37bd9..00000000000 --- a/reactos/drivers/storage/scsiport/scsiport.rc +++ /dev/null @@ -1,7 +0,0 @@ -/* $Id$ */ - -#define REACTOS_VERSION_DLL -#define REACTOS_STR_FILE_DESCRIPTION "SCSI Port Driver\0" -#define REACTOS_STR_INTERNAL_NAME "scsiport\0" -#define REACTOS_STR_ORIGINAL_FILENAME "scsiport.sys\0" -#include diff --git a/reactos/drivers/storage/scsiport/scsiport_int.h b/reactos/drivers/storage/scsiport/scsiport_int.h deleted file mode 100644 index 0caa511af2d..00000000000 --- a/reactos/drivers/storage/scsiport/scsiport_int.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * SCSI_PORT_TIMER_STATES - * - * DESCRIPTION - * An enumeration containing the states in the timer DFA - */ - -#define VERSION "0.0.3" - -#ifndef PAGE_ROUND_UP -#define PAGE_ROUND_UP(x) ( (((ULONG_PTR)x)%PAGE_SIZE) ? ((((ULONG_PTR)x)&(~(PAGE_SIZE-1)))+PAGE_SIZE) : ((ULONG_PTR)x) ) -#endif -#ifndef ROUND_UP -#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) -#endif - -#define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24)) - -typedef enum _SCSI_PORT_TIMER_STATES -{ - IDETimerIdle, - IDETimerCmdWait, - IDETimerResetWaitForBusyNegate, - IDETimerResetWaitForDrdyAssert -} SCSI_PORT_TIMER_STATES; - - -typedef struct _SCSI_PORT_DEVICE_BASE -{ - LIST_ENTRY List; - - PVOID MappedAddress; - ULONG NumberOfBytes; - SCSI_PHYSICAL_ADDRESS IoAddress; - ULONG SystemIoBusNumber; -} SCSI_PORT_DEVICE_BASE, *PSCSI_PORT_DEVICE_BASE; - - -typedef struct _SCSI_PORT_LUN_EXTENSION -{ - LIST_ENTRY List; - - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - - BOOLEAN DeviceClaimed; - PDEVICE_OBJECT DeviceObject; - - INQUIRYDATA InquiryData; - - ULONG PendingIrpCount; - ULONG ActiveIrpCount; - - PIRP NextIrp; - ULONG Flags; - - /* More data? */ - - UCHAR MiniportLunExtension[1]; /* must be the last entry */ -} SCSI_PORT_LUN_EXTENSION, *PSCSI_PORT_LUN_EXTENSION; - - -/* - * SCSI_PORT_DEVICE_EXTENSION - * - * DESCRIPTION - * First part of the port objects device extension. The second - * part is the miniport-specific device extension. - */ - -typedef struct _SCSI_PORT_DEVICE_EXTENSION -{ - ULONG Length; - ULONG MiniPortExtensionSize; - PPORT_CONFIGURATION_INFORMATION PortConfig; - ULONG PortNumber; - - KSPIN_LOCK Lock; - ULONG Flags; - - PKINTERRUPT Interrupt; - - SCSI_PORT_TIMER_STATES TimerState; - LONG TimerCount; - - LIST_ENTRY DeviceBaseListHead; - - ULONG LunExtensionSize; - LIST_ENTRY LunExtensionListHead; - - ULONG SrbExtensionSize; - - PIO_SCSI_CAPABILITIES PortCapabilities; - - PDEVICE_OBJECT DeviceObject; - PCONTROLLER_OBJECT ControllerObject; - - PHW_STARTIO HwStartIo; - PHW_INTERRUPT HwInterrupt; - - - /* DMA related stuff */ - PADAPTER_OBJECT AdapterObject; - ULONG MapRegisterCount; - - PHYSICAL_ADDRESS PhysicalAddress; - PVOID VirtualAddress; - RTL_BITMAP SrbExtensionAllocMap; - ULONG MaxSrbExtensions; - ULONG CurrentSrbExtensions; - ULONG CommonBufferLength; - - LIST_ENTRY PendingIrpListHead; - PIRP NextIrp; - ULONG PendingIrpCount; - ULONG ActiveIrpCount; - - UCHAR MiniPortDeviceExtension[1]; /* must be the last entry */ -} SCSI_PORT_DEVICE_EXTENSION, *PSCSI_PORT_DEVICE_EXTENSION; - -typedef struct _SCSI_PORT_SCAN_ADAPTER -{ - KEVENT Event; - IO_STATUS_BLOCK IoStatusBlock; - NTSTATUS Status; - PSCSI_PORT_LUN_EXTENSION LunExtension; - ULONG Lun; - ULONG Bus; - ULONG Target; - SCSI_REQUEST_BLOCK Srb; - UCHAR DataBuffer[256]; - BOOLEAN Active; -} SCSI_PORT_SCAN_ADAPTER, *PSCSI_PORT_SCAN_ADAPTER; - -