From 1ea7189ed999750eea63bc4bef8da247a0dfe9bc Mon Sep 17 00:00:00 2001 From: Eric Kohl Date: Sun, 9 Sep 2001 21:32:28 +0000 Subject: [PATCH] Added new atapi driver. It can only scan the pci busses and create scsi port devices for identified ide channels. svn path=/trunk/; revision=2252 --- reactos/Makefile | 2 +- reactos/drivers/storage/atapi/.cvsignore | 5 + reactos/drivers/storage/atapi/atapi.c | 2211 ++++++++++++++++++++++ reactos/drivers/storage/atapi/atapi.h | 293 +++ reactos/drivers/storage/atapi/atapi.rc | 39 + reactos/drivers/storage/atapi/makefile | 17 + reactos/drivers/storage/atapi/partitio.h | 41 + 7 files changed, 2607 insertions(+), 1 deletion(-) create mode 100644 reactos/drivers/storage/atapi/.cvsignore create mode 100644 reactos/drivers/storage/atapi/atapi.c create mode 100644 reactos/drivers/storage/atapi/atapi.h create mode 100644 reactos/drivers/storage/atapi/atapi.rc create mode 100644 reactos/drivers/storage/atapi/makefile create mode 100644 reactos/drivers/storage/atapi/partitio.h diff --git a/reactos/Makefile b/reactos/Makefile index 3d000cbefe5..0610e2800b8 100644 --- a/reactos/Makefile +++ b/reactos/Makefile @@ -49,7 +49,7 @@ NET_DEVICE_DRIVERS = ne2000 # # storage drivers (don't change the order) # -STORAGE_DRIVERS = class2 scsiport disk +STORAGE_DRIVERS = class2 scsiport atapi disk # # system applications (required for startup) diff --git a/reactos/drivers/storage/atapi/.cvsignore b/reactos/drivers/storage/atapi/.cvsignore new file mode 100644 index 00000000000..b44c0682e06 --- /dev/null +++ b/reactos/drivers/storage/atapi/.cvsignore @@ -0,0 +1,5 @@ +base.tmp +junk.tmp +temp.exp +atapi.coff +atapi.sys.unstripped diff --git a/reactos/drivers/storage/atapi/atapi.c b/reactos/drivers/storage/atapi/atapi.c new file mode 100644 index 00000000000..c916ca5eb82 --- /dev/null +++ b/reactos/drivers/storage/atapi/atapi.c @@ -0,0 +1,2211 @@ +/* $Id: atapi.c,v 1.1 2001/09/09 21:31:13 ekohl Exp $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS ATAPI miniport driver + * FILE: services/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: + * - Use scsiport driver. + */ + +// ------------------------------------------------------------------------- + +#include + +#define NDEBUG +#include + +#include "atapi.h" +#include "partitio.h" + +#define VERSION "V0.0.1" + + +// ------------------------------------------------------- File Static Data + +typedef struct _IDE_CONTROLLER_PARAMETERS +{ + int CommandPortBase; + int CommandPortSpan; + int ControlPortBase; + int ControlPortSpan; + int Vector; + int IrqL; + int SynchronizeIrqL; + KINTERRUPT_MODE InterruptMode; + KAFFINITY Affinity; +} IDE_CONTROLLER_PARAMETERS, *PIDE_CONTROLLER_PARAMETERS; + +// NOTE: Do not increase max drives above 2 + +#define IDE_MAX_DRIVES 2 + +#define IDE_MAX_CONTROLLERS 2 +IDE_CONTROLLER_PARAMETERS Controllers[IDE_MAX_CONTROLLERS] = +{ + {0x01f0, 8, 0x03f6, 1, 14, 14, 15, LevelSensitive, 0xffff}, + {0x0170, 8, 0x0376, 1, 15, 15, 15, LevelSensitive, 0xffff} + /*{0x01E8, 8, 0x03ee, 1, 11, 11, 15, LevelSensitive, 0xffff}, + {0x0168, 8, 0x036e, 1, 10, 10, 15, LevelSensitive, 0xffff}*/ +}; + +static BOOLEAN IDEInitialized = FALSE; + +// ----------------------------------------------- Discardable Declarations + +#ifdef ALLOC_PRAGMA + +// make the initialization routines discardable, so that they +// don't waste space + +#pragma alloc_text(init, DriverEntry) +#pragma alloc_text(init, IDECreateController) +#pragma alloc_text(init, IDECreateDevices) +#pragma alloc_text(init, IDECreateDevice) +#pragma alloc_text(init, IDEPolledRead) + +// make the PASSIVE_LEVEL routines pageable, so that they don't +// waste nonpaged memory + +#pragma alloc_text(page, IDEShutdown) +#pragma alloc_text(page, IDEDispatchOpenClose) +#pragma alloc_text(page, IDEDispatchRead) +#pragma alloc_text(page, IDEDispatchWrite) + +#endif /* ALLOC_PRAGMA */ + +// ---------------------------------------------------- Forward Declarations + +static NTSTATUS +AtapiFindControllers(IN PDRIVER_OBJECT DriverObject); + +static NTSTATUS +AtapiCreateController(IN PDRIVER_OBJECT DriverObject, + IN PIDE_CONTROLLER_PARAMETERS ControllerParams, + IN ULONG ControllerIdx); + +static NTSTATUS +AtapiCreatePortDevice(IN PDRIVER_OBJECT DriverObject, + IN PCONTROLLER_OBJECT ControllerObject, + IN ULONG ControllerNumber); + +static BOOLEAN IDEResetController(IN WORD CommandPort, IN WORD ControlPort); +static BOOLEAN IDECreateDevices(IN PDRIVER_OBJECT DriverObject, + IN PCONTROLLER_OBJECT ControllerObject, + IN PIDE_CONTROLLER_EXTENSION ControllerExtension, + IN int DriveIdx, + IN int HarddiskIdx); +static BOOLEAN IDEGetDriveIdentification(IN int CommandPort, + IN int DriveNum, + OUT PIDE_DRIVE_IDENTIFY DrvParms); +static NTSTATUS IDECreateDevice(IN PDRIVER_OBJECT DriverObject, + OUT PDEVICE_OBJECT *DeviceObject, + IN PCONTROLLER_OBJECT ControllerObject, + IN int UnitNumber, + IN ULONG DiskNumber, + IN PIDE_DRIVE_IDENTIFY DrvParms, + IN ULONG PartitionIdx, + IN ULONGLONG Offset, + IN ULONGLONG Size); +static int IDEPolledRead(IN WORD Address, + IN BYTE PreComp, + IN BYTE SectorCnt, + IN BYTE SectorNum , + IN BYTE CylinderLow, + IN BYTE CylinderHigh, + IN BYTE DrvHead, + IN BYTE Command, + OUT BYTE *Buffer); +static NTSTATUS STDCALL IDEDispatchOpenClose(IN PDEVICE_OBJECT pDO, IN PIRP Irp); +static NTSTATUS STDCALL IDEDispatchReadWrite(IN PDEVICE_OBJECT pDO, IN PIRP Irp); +static NTSTATUS STDCALL IDEDispatchDeviceControl(IN PDEVICE_OBJECT pDO, IN PIRP Irp); +static VOID STDCALL IDEStartIo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); + +static IO_ALLOCATION_ACTION STDCALL +IDEAllocateController(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MapRegisterBase, + IN PVOID Ccontext); + +static BOOLEAN STDCALL +IDEStartController(IN OUT PVOID Context); + +VOID IDEBeginControllerReset(PIDE_CONTROLLER_EXTENSION ControllerExtension); + +static BOOLEAN STDCALL +IDEIsr(IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext); + +static VOID IDEDpcForIsr(IN PKDPC Dpc, + IN PDEVICE_OBJECT DpcDeviceObject, + IN PIRP DpcIrp, + IN PVOID DpcContext); +static VOID IDEFinishOperation(PIDE_CONTROLLER_EXTENSION ControllerExtension); +static VOID STDCALL IDEIoTimer(PDEVICE_OBJECT DeviceObject, PVOID Context); + + + + +#define PCI_TYPE0_ADDRESSES 6 +#define PCI_TYPE1_ADDRESSES 2 + +typedef struct _PCI_COMMON_CONFIG +{ + USHORT VendorID; // (ro) + USHORT DeviceID; // (ro) + USHORT Command; // Device control + USHORT Status; + UCHAR RevisionID; // (ro) + UCHAR ProgIf; // (ro) + UCHAR SubClass; // (ro) + UCHAR BaseClass; // (ro) + UCHAR CacheLineSize; // (ro+) + UCHAR LatencyTimer; // (ro+) + UCHAR HeaderType; // (ro) + UCHAR BIST; // Built in self test + union + { + struct _PCI_HEADER_TYPE_0 + { + ULONG BaseAddresses[PCI_TYPE0_ADDRESSES]; + ULONG CIS; + USHORT SubVendorID; + USHORT SubSystemID; + ULONG ROMBaseAddress; + ULONG Reserved2[2]; + + UCHAR InterruptLine; // + UCHAR InterruptPin; // (ro) + UCHAR MinimumGrant; /* read-only */ + UCHAR MaximumLatency; /* read-only */ + } type0; + } u; + UCHAR DeviceSpecific[192]; +} PCI_COMMON_CONFIG, *PPCI_COMMON_CONFIG; + + + +// ---------------------------------------------------------------- Inlines + +void +IDESwapBytePairs(char *Buf, + int Cnt) +{ + char t; + int i; + + for (i = 0; i < Cnt; i += 2) + { + t = Buf[i]; + Buf[i] = Buf[i+1]; + Buf[i+1] = t; + } +} + +// ------------------------------------------------------- Public Interface + +// 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 + +STDCALL NTSTATUS +DriverEntry(IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath) +{ + NTSTATUS Status; + + DbgPrint("ATAPI Driver %s\n", VERSION); + + /* Export other driver entry points... */ + DriverObject->DriverStartIo = IDEStartIo; + DriverObject->MajorFunction[IRP_MJ_CREATE] = IDEDispatchOpenClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = IDEDispatchOpenClose; + DriverObject->MajorFunction[IRP_MJ_READ] = IDEDispatchReadWrite; + DriverObject->MajorFunction[IRP_MJ_WRITE] = IDEDispatchReadWrite; +// DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = IDEDispatchQueryInformation; +// DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = IDEDispatchSetInformation; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IDEDispatchDeviceControl; + + Status = AtapiFindControllers(DriverObject); + if (NT_SUCCESS(Status)) + { + IDEInitialized = TRUE; + } + + DPRINT( "Returning from DriverEntry\n" ); + return Status; +} + + +static NTSTATUS +AtapiFindControllers(IN PDRIVER_OBJECT DriverObject) +{ + PCI_COMMON_CONFIG PciConfig; + ULONG Bus; + ULONG Slot; + ULONG Size; + ULONG i; + NTSTATUS ReturnedStatus = STATUS_NO_SUCH_DEVICE; + NTSTATUS Status; + PCONFIGURATION_INFORMATION ConfigInfo; + + DPRINT1("AtapiFindControllers() called!\n"); + + ConfigInfo = IoGetConfigurationInformation(); + + /* Search PCI busses for IDE controllers */ + for (Bus = 0; Bus < 8; Bus++) + { + for (Slot = 0; Slot < 256; Slot++) + { + Size = HalGetBusData(PCIConfiguration, + Bus, + Slot, + &PciConfig, + sizeof(PCI_COMMON_CONFIG)); + if (Size != 0) + { + if ((PciConfig.BaseClass == 0x01) && + (PciConfig.SubClass == 0x01)) + { + DPRINT1("IDE controller found!\n"); + + DPRINT1("Bus %1lu Device %2lu Func %1lu VenID 0x%04hx DevID 0x%04hx\n", + Bus, + Slot>>3, + Slot & 0x07, + PciConfig.VendorID, + PciConfig.DeviceID); + if ((PciConfig.HeaderType & 0x7FFFFFFF) == 0) + { + DPRINT1(" IPR 0x%X ILR 0x%X\n", + PciConfig.u.type0.InterruptPin, + PciConfig.u.type0.InterruptLine); + } + + if (PciConfig.ProgIf & 0x01) + { + DPRINT1("Primary channel: PCI native mode\n"); + } + else + { + DPRINT1("Primary channel: Compatibility mode\n"); + if (ConfigInfo->AtDiskPrimaryAddressClaimed == FALSE) + { + Status = AtapiCreateController(DriverObject, + &Controllers[0], + ConfigInfo->ScsiPortCount); + if (NT_SUCCESS(Status)) + { + ConfigInfo->AtDiskPrimaryAddressClaimed = TRUE; + ConfigInfo->ScsiPortCount++; + ReturnedStatus = Status; + } + } + else + { + /* + * FIXME: Switch controller to native pci mode + * if it is programmable. + */ + } + } + if (PciConfig.ProgIf & 0x02) + { + DPRINT1("Primary programmable: 1\n"); + } + else + { + DPRINT1("Primary programmable: 0\n"); + } + + if (PciConfig.ProgIf & 0x04) + { + DPRINT1("Secondary channel: PCI native mode\n"); + } + else + { + DPRINT1("Secondary channel: Compatibility mode\n"); + if (ConfigInfo->AtDiskSecondaryAddressClaimed == FALSE) + { + Status = AtapiCreateController(DriverObject, + &Controllers[1], + ConfigInfo->ScsiPortCount); + if (NT_SUCCESS(Status)) + { + ConfigInfo->AtDiskSecondaryAddressClaimed = TRUE; + ConfigInfo->ScsiPortCount++; + ReturnedStatus = Status; + } + } + else + { + /* + * FIXME: Switch controller to native pci mode + * if it is programmable. + */ + } + } + if (PciConfig.ProgIf & 0x08) + { + DPRINT1("Secondary programmable: 1\n"); + } + else + { + DPRINT1("Secondary programmable: 0\n"); + } + + if (PciConfig.ProgIf & 0x80) + { + DPRINT1("Master IDE device: 1\n"); + } + else + { + DPRINT1("Master IDE device: 0\n"); + } + + for (i = 0; i < PCI_TYPE0_ADDRESSES; i++) + { + DPRINT1("BaseAddress: 0x%08X\n", PciConfig.u.type0.BaseAddresses[i]); + } + } + } + } + } + + /* Search for ISA IDE controller if no primary controller was found */ + if (ConfigInfo->AtDiskPrimaryAddressClaimed == FALSE) + { + DPRINT1("Searching for primary ISA IDE controller!\n"); + + if (IDEResetController(Controllers[0].CommandPortBase, + Controllers[0].ControlPortBase)) + { + Status = AtapiCreateController(DriverObject, + &Controllers[0], + ConfigInfo->ScsiPortCount); + if (NT_SUCCESS(Status)) + { + DPRINT1(" Found primary ISA IDE controller!\n"); + ConfigInfo->AtDiskPrimaryAddressClaimed = TRUE; + ConfigInfo->ScsiPortCount++; + ReturnedStatus = Status; + } + } + } + + if (ConfigInfo->AtDiskSecondaryAddressClaimed == FALSE) + { + DPRINT1("Searching for secondary ISA IDE controller!\n"); + + if (IDEResetController(Controllers[1].CommandPortBase, + Controllers[1].ControlPortBase)) + { + Status = AtapiCreateController(DriverObject, + &Controllers[1], + ConfigInfo->ScsiPortCount); + if (NT_SUCCESS(Status)) + { + DPRINT1(" Found secondary ISA IDE controller!\n"); + ConfigInfo->AtDiskSecondaryAddressClaimed = TRUE; + ConfigInfo->ScsiPortCount++; + ReturnedStatus = Status; + } + } + } + + DPRINT1("AtapiFindControllers() done!\n"); +for(;;); + return(ReturnedStatus); +} + + +// ---------------------------------------------------- Discardable statics + +// IDECreateController +// +// DESCRIPTION: +// Creates a controller object and a device object for each valid +// device on the controller +// +// RUN LEVEL: +// PASSIVE LEVEL +// +// ARGUMENTS: +// IN PDRIVER_OBJECT DriverObject The system created driver object +// IN PIDE_CONTROLLER_PARAMETERS The parameter block for this +// ControllerParams controller +// IN ULONG ControllerIdx The index of this controller +// +// RETURNS: +// TRUE Devices where found on this controller +// FALSE The controller does not respond or there are no devices on it +// + +static NTSTATUS +AtapiCreateController(IN PDRIVER_OBJECT DriverObject, + IN PIDE_CONTROLLER_PARAMETERS ControllerParams, + IN ULONG ControllerIdx) +{ + BOOLEAN CreatedDevices, ThisDriveExists; + int DriveIdx; + NTSTATUS Status; + PCONTROLLER_OBJECT ControllerObject; + PIDE_CONTROLLER_EXTENSION ControllerExtension; + + ControllerObject = IoCreateController(sizeof(IDE_CONTROLLER_EXTENSION)); + if (ControllerObject == NULL) + { + DbgPrint ("Could not create controller object for controller %d\n", + ControllerIdx); + return(STATUS_NO_SUCH_DEVICE); + } + + /* Fill out Controller extension data */ + ControllerExtension = (PIDE_CONTROLLER_EXTENSION) + ControllerObject->ControllerExtension; + ControllerExtension->Number = ControllerIdx; + ControllerExtension->CommandPortBase = ControllerParams->CommandPortBase; + ControllerExtension->ControlPortBase = ControllerParams->ControlPortBase; + ControllerExtension->Vector = ControllerParams->Vector; + ControllerExtension->DMASupported = FALSE; + ControllerExtension->ControllerInterruptBug = FALSE; + ControllerExtension->OperationInProgress = FALSE; + + /* Initialize the spin lock in the controller extension */ + KeInitializeSpinLock(&ControllerExtension->SpinLock); + + /* Register an interrupt handler for this controller */ + Status = IoConnectInterrupt(&ControllerExtension->Interrupt, + IDEIsr, + ControllerExtension, + &ControllerExtension->SpinLock, + ControllerExtension->Vector, + ControllerParams->IrqL, + ControllerParams->SynchronizeIrqL, + ControllerParams->InterruptMode, + FALSE, + ControllerParams->Affinity, + FALSE); + if (!NT_SUCCESS(Status)) + { + DbgPrint ("Could not Connect Interrupt %d\n", + ControllerExtension->Vector); + IoDeleteController (ControllerObject); + return(Status); + } + + Status = AtapiCreatePortDevice(DriverObject, + ControllerObject, + ControllerIdx); + if (!NT_SUCCESS(Status)) + { + DbgPrint("Could not create port device %d\n", + ControllerIdx); + IoDisconnectInterrupt(ControllerExtension->Interrupt); + IoDeleteController(ControllerObject); + return(Status); + } + + return(STATUS_SUCCESS); +} + + +static NTSTATUS +AtapiCreatePortDevice(IN PDRIVER_OBJECT DriverObject, + IN PCONTROLLER_OBJECT ControllerObject, + IN ULONG ControllerNumber) +{ + PATAPI_PORT_EXTENSION DeviceExtension; + PDEVICE_OBJECT PortDeviceObject; + WCHAR NameBuffer[IDE_MAX_NAME_LENGTH]; + UNICODE_STRING DeviceName; + NTSTATUS Status; + PIDE_CONTROLLER_EXTENSION ControllerExtension; + + /* Create a unicode device name */ + swprintf(NameBuffer, + L"\\Device\\ScsiPort%lu", + ControllerNumber); + RtlInitUnicodeString(&DeviceName, + NameBuffer); + + /* Create the port device */ + Status = IoCreateDevice(DriverObject, + sizeof(ATAPI_PORT_EXTENSION), + &DeviceName, + FILE_DEVICE_DISK, + 0, + TRUE, + &PortDeviceObject); + if (!NT_SUCCESS(Status)) + { + DbgPrint("IoCreateDevice call failed\n"); + return(Status); + } + DPRINT1("Created device: %wZ\n", &DeviceName); + + /* Set the buffering strategy here... */ + PortDeviceObject->Flags |= DO_DIRECT_IO; + PortDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; + + /* Fill out Device extension data */ + DeviceExtension = (PATAPI_PORT_EXTENSION)PortDeviceObject->DeviceExtension; + DeviceExtension->DeviceObject = PortDeviceObject; + DeviceExtension->ControllerObject = ControllerObject; + DeviceExtension->PortNumber = ControllerNumber; + + + /* Initialize the DPC object here */ + IoInitializeDpcRequest(PortDeviceObject, + IDEDpcForIsr); + + /* + * Initialize the controller timer here + * (since it has to be tied to a device) + */ + ControllerExtension = (PIDE_CONTROLLER_EXTENSION) + ControllerObject->ControllerExtension; + ControllerExtension->TimerState = IDETimerIdle; + ControllerExtension->TimerCount = 0; + ControllerExtension->TimerDevice = PortDeviceObject; + IoInitializeTimer(PortDeviceObject, + IDEIoTimer, + ControllerExtension); + + return(Status); +} + + +// IDEResetController +// +// DESCRIPTION: +// Reset the controller and report completion status +// +// RUN LEVEL: +// PASSIVE_LEVEL +// +// ARGUMENTS: +// IN WORD CommandPort The address of the command port +// IN WORD ControlPort The address of the control port +// +// RETURNS: +// + +BOOLEAN +IDEResetController(IN WORD CommandPort, + IN WORD ControlPort) +{ + int Retries; + + // Assert drive reset line + IDEWriteDriveControl(ControlPort, IDE_DC_SRST); + + // Wait for min. 25 microseconds + KeStallExecutionProcessor(IDE_RESET_PULSE_LENGTH); + + // Negate drive reset line + IDEWriteDriveControl(ControlPort, 0); + + // Wait for BUSY negation + for (Retries = 0; Retries < IDE_RESET_BUSY_TIMEOUT * 1000; Retries++) + { + if (!(IDEReadStatus(CommandPort) & IDE_SR_BUSY)) + { + break; + } + KeStallExecutionProcessor(10); + } + CHECKPOINT; + if (Retries >= IDE_RESET_BUSY_TIMEOUT * 1000) + { + return FALSE; + } + CHECKPOINT; + // return TRUE if controller came back to life. and + // the registers are initialized correctly + return IDEReadError(CommandPort) == 1; +} + +// IDECreateDevices +// +// DESCRIPTION: +// Create the raw device and any partition devices on this drive +// +// RUN LEVEL: +// PASSIVE_LEVEL +// +// ARGUMENTS: +// IN PDRIVER_OBJECT DriverObject The system created driver object +// IN PCONTROLLER_OBJECT ControllerObject +// IN PIDE_CONTROLLER_EXTENSION ControllerExtension +// The IDE controller extension for +// this device +// IN int DriveIdx The index of the drive on this +// controller +// IN int HarddiskIdx The NT device number for this +// drive +// +// RETURNS: +// TRUE Drive exists and devices were created +// FALSE no devices were created for this device +// + +BOOLEAN +IDECreateDevices(IN PDRIVER_OBJECT DriverObject, + IN PCONTROLLER_OBJECT ControllerObject, + IN PIDE_CONTROLLER_EXTENSION ControllerExtension, + IN int DriveIdx, + IN int HarddiskIdx) +{ + WCHAR NameBuffer[IDE_MAX_NAME_LENGTH]; + int CommandPort; + NTSTATUS Status; + IDE_DRIVE_IDENTIFY DrvParms; + PDEVICE_OBJECT DiskDeviceObject; + PDEVICE_OBJECT PartitionDeviceObject; + PIDE_DEVICE_EXTENSION DiskDeviceExtension; + PIDE_DEVICE_EXTENSION PartitionDeviceExtension; + UNICODE_STRING UnicodeDeviceDirName; + OBJECT_ATTRIBUTES DeviceDirAttributes; + HANDLE Handle; + ULONG SectorCount = 0; + PDRIVE_LAYOUT_INFORMATION PartitionList = NULL; + PPARTITION_INFORMATION PartitionEntry; + ULONG i; + + // Copy I/O port offsets for convenience + CommandPort = ControllerExtension->CommandPortBase; +// ControlPort = ControllerExtension->ControlPortBase; + DPRINT("probing IDE controller %d Addr %04lx Drive %d\n", + ControllerExtension->Number, + CommandPort, + DriveIdx); + + /* Get the Drive Identification Data */ + if (!IDEGetDriveIdentification(CommandPort, DriveIdx, &DrvParms)) + { + DPRINT("Giving up on drive %d on controller %d...\n", + DriveIdx, + ControllerExtension->Number); + return FALSE; + } + + /* Create the harddisk device directory */ + swprintf (NameBuffer, + L"\\Device\\Harddisk%d", + HarddiskIdx); + RtlInitUnicodeString(&UnicodeDeviceDirName, + NameBuffer); + InitializeObjectAttributes(&DeviceDirAttributes, + &UnicodeDeviceDirName, + 0, + NULL, + NULL); + Status = ZwCreateDirectoryObject(&Handle, 0, &DeviceDirAttributes); + if (!NT_SUCCESS(Status)) + { + DbgPrint("Could not create device dir object\n"); + return FALSE; + } + + /* Create the disk device */ + if (DrvParms.Capabilities & IDE_DRID_LBA_SUPPORTED) + { + SectorCount = + (ULONG)((DrvParms.TMSectorCountHi << 16) + DrvParms.TMSectorCountLo); + } + else + { + SectorCount = + (ULONG)(DrvParms.LogicalCyls * DrvParms.LogicalHeads * DrvParms.SectorsPerTrack); + } + DPRINT("SectorCount %lu\n", SectorCount); + + Status = IDECreateDevice(DriverObject, + &DiskDeviceObject, + ControllerObject, + DriveIdx, + HarddiskIdx, + &DrvParms, + 0, + 0, + SectorCount); + if (!NT_SUCCESS(Status)) + { + DbgPrint("IDECreateDevice call failed for raw device\n"); + return FALSE; + } + + /* Increase number of available physical disk drives */ + IoGetConfigurationInformation()->DiskCount++; + + DiskDeviceExtension = (PIDE_DEVICE_EXTENSION)DiskDeviceObject->DeviceExtension; + DiskDeviceExtension->DiskExtension = (PVOID)DiskDeviceExtension; + + /* + * Initialize the controller timer here + * (since it has to be tied to a device) + */ + if (DriveIdx == 0) + { + ControllerExtension->TimerState = IDETimerIdle; + ControllerExtension->TimerCount = 0; + ControllerExtension->TimerDevice = DiskDeviceObject; + IoInitializeTimer(DiskDeviceObject, + IDEIoTimer, + ControllerExtension); + } + + DPRINT("DrvParms.BytesPerSector %ld\n",DrvParms.BytesPerSector); + + /* Read partition table */ + Status = IoReadPartitionTable(DiskDeviceObject, + DrvParms.BytesPerSector, + TRUE, + &PartitionList); + if (!NT_SUCCESS(Status)) + { + DbgPrint("IoReadPartitionTable() failed\n"); + return FALSE; + } + + DPRINT(" Number of partitions: %u\n", PartitionList->PartitionCount); + for (i=0;i < PartitionList->PartitionCount; i++) + { + PartitionEntry = &PartitionList->PartitionEntry[i]; + + DPRINT("Partition %02ld: nr: %d boot: %1x type: %x offset: %I64d size: %I64d\n", + i, + PartitionEntry->PartitionNumber, + PartitionEntry->BootIndicator, + PartitionEntry->PartitionType, + PartitionEntry->StartingOffset.QuadPart / 512 /*DrvParms.BytesPerSector*/, + PartitionEntry->PartitionLength.QuadPart / 512 /* DrvParms.BytesPerSector*/); + + /* Create device for partition */ + Status = IDECreateDevice(DriverObject, + &PartitionDeviceObject, + ControllerObject, + DriveIdx, + HarddiskIdx, + &DrvParms, + PartitionEntry->PartitionNumber, + PartitionEntry->StartingOffset.QuadPart / 512 /* DrvParms.BytesPerSector*/, + PartitionEntry->PartitionLength.QuadPart / 512 /*DrvParms.BytesPerSector*/); + if (!NT_SUCCESS(Status)) + { + DbgPrint("IDECreateDevice() failed\n"); + break; + } + + /* Initialize pointer to disk device extension */ + PartitionDeviceExtension = (PIDE_DEVICE_EXTENSION)PartitionDeviceObject->DeviceExtension; + PartitionDeviceExtension->DiskExtension = (PVOID)DiskDeviceExtension; + } + + if (PartitionList != NULL) + ExFreePool(PartitionList); + + return TRUE; +} + +// IDEGetDriveIdentification +// +// DESCRIPTION: +// Get the identification block from the drive +// +// RUN LEVEL: +// PASSIVE_LEVEL +// +// ARGUMENTS: +// IN int CommandPort Address of the command port +// IN int DriveNum The drive index (0,1) +// OUT PIDE_DRIVE_IDENTIFY DrvParms Address to write drive ident block +// +// RETURNS: +// TRUE The drive identification block was retrieved successfully +// + +BOOLEAN +IDEGetDriveIdentification(IN int CommandPort, + IN int DriveNum, + OUT PIDE_DRIVE_IDENTIFY DrvParms) +{ + + // Get the Drive Identify block from drive or die + if (IDEPolledRead(CommandPort, 0, 0, 0, 0, 0, (DriveNum ? IDE_DH_DRV1 : 0), + IDE_CMD_IDENT_DRV, (BYTE *)DrvParms) != 0) + { + return FALSE; + } + + // Report on drive parameters if debug mode + IDESwapBytePairs(DrvParms->SerialNumber, 20); + IDESwapBytePairs(DrvParms->FirmwareRev, 8); + IDESwapBytePairs(DrvParms->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, + DrvParms->SerialNumber); + DPRINT("Cntlr type:%2d BufSiz:%5d ECC bytes:%3d Firmware Rev:[%.8s]\n", + DrvParms->ControllerType, + DrvParms->BufferSize * IDE_SECTOR_BUF_SZ, + DrvParms->ECCByteCnt, + DrvParms->FirmwareRev); + DPRINT("Model:[%.40s]\n", DrvParms->ModelNumber); + DPRINT("RWMult?:%02x LBA:%d DMA:%d MinPIO:%d ns MinDMA:%d ns\n", + (DrvParms->RWMultImplemented) & 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("BytesPerSector %d\n", DrvParms->BytesPerSector); + DrvParms->BytesPerSector = 512; /* FIXME !!!*/ + return TRUE; +} + + +// IDECreateDevice +// +// DESCRIPTION: +// Creates a device by calling IoCreateDevice and a sylbolic link for Win32 +// +// RUN LEVEL: +// PASSIVE_LEVEL +// +// ARGUMENTS: +// IN PDRIVER_OBJECT DriverObject The system supplied driver object +// OUT PDEVICE_OBJECT *DeviceObject The created device object +// IN PCONTROLLER_OBJECT ControllerObject The Controller for the device +// IN BOOLEAN LBASupported Does the drive support LBA addressing? +// IN BOOLEAN DMASupported Does the drive support DMA? +// IN int SectorsPerLogCyl Sectors per cylinder +// IN int SectorsPerLogTrk Sectors per track +// IN DWORD Offset First valid sector for this device +// IN DWORD Size Count of valid sectors for this device +// +// RETURNS: +// NTSTATUS +// + +NTSTATUS +IDECreateDevice(IN PDRIVER_OBJECT DriverObject, + OUT PDEVICE_OBJECT *DeviceObject, + IN PCONTROLLER_OBJECT ControllerObject, + IN int UnitNumber, + IN ULONG DiskNumber, + IN PIDE_DRIVE_IDENTIFY DrvParms, + IN ULONG PartitionNumber, + IN ULONGLONG Offset, + IN ULONGLONG Size) +{ + WCHAR NameBuffer[IDE_MAX_NAME_LENGTH]; + WCHAR ArcNameBuffer[IDE_MAX_NAME_LENGTH + 15]; + UNICODE_STRING DeviceName; + UNICODE_STRING ArcName; + NTSTATUS RC; + PIDE_DEVICE_EXTENSION DeviceExtension; + + // Create a unicode device name + swprintf(NameBuffer, + L"\\Device\\Harddisk%d\\Partition%d", + DiskNumber, + PartitionNumber); + RtlInitUnicodeString(&DeviceName, + NameBuffer); + + // Create the device + RC = IoCreateDevice(DriverObject, sizeof(IDE_DEVICE_EXTENSION), + &DeviceName, FILE_DEVICE_DISK, 0, TRUE, DeviceObject); + if (!NT_SUCCESS(RC)) + { + DbgPrint ("IoCreateDevice call failed\n"); + return RC; + } + + // Set the buffering strategy here... + (*DeviceObject)->Flags |= DO_DIRECT_IO; + (*DeviceObject)->AlignmentRequirement = FILE_WORD_ALIGNMENT; + + // Fill out Device extension data + DeviceExtension = (PIDE_DEVICE_EXTENSION) (*DeviceObject)->DeviceExtension; + DeviceExtension->DeviceObject = (*DeviceObject); + DeviceExtension->ControllerObject = ControllerObject; + DeviceExtension->DiskExtension = NULL; + DeviceExtension->UnitNumber = UnitNumber; + DeviceExtension->LBASupported = + (DrvParms->Capabilities & IDE_DRID_LBA_SUPPORTED) ? 1 : 0; + DeviceExtension->DMASupported = + (DrvParms->Capabilities & IDE_DRID_DMA_SUPPORTED) ? 1 : 0; + // FIXME: deal with bizarre sector sizes + DeviceExtension->BytesPerSector = 512 /* DrvParms->BytesPerSector */; + DeviceExtension->SectorsPerLogCyl = DrvParms->LogicalHeads * + DrvParms->SectorsPerTrack; + DeviceExtension->SectorsPerLogTrk = DrvParms->SectorsPerTrack; + DeviceExtension->LogicalHeads = DrvParms->LogicalHeads; + DeviceExtension->LogicalCylinders = + (DrvParms->Capabilities & IDE_DRID_LBA_SUPPORTED) ? DrvParms->TMCylinders : DrvParms->LogicalCyls; + DeviceExtension->Offset = Offset; + DeviceExtension->Size = Size; + DPRINT("%wZ: offset %lu size %lu \n", + &DeviceName, + DeviceExtension->Offset, + DeviceExtension->Size); + + // Initialize the DPC object here + IoInitializeDpcRequest(*DeviceObject, IDEDpcForIsr); + + if (PartitionNumber != 0) + { + DbgPrint("%wZ %luMB\n", &DeviceName, Size / 2048); + } + + /* assign arc name */ + if (PartitionNumber == 0) + { + swprintf(ArcNameBuffer, + L"\\ArcName\\multi(0)disk(0)rdisk(%d)", + DiskNumber); + } + else + { + swprintf(ArcNameBuffer, + L"\\ArcName\\multi(0)disk(0)rdisk(%d)partition(%d)", + DiskNumber, + PartitionNumber); + } + RtlInitUnicodeString (&ArcName, + ArcNameBuffer); + DPRINT("%wZ ==> %wZ\n", &ArcName, &DeviceName); + RC = IoAssignArcName (&ArcName, + &DeviceName); + if (!NT_SUCCESS(RC)) + { + DbgPrint ("IoAssignArcName (%wZ) failed (Status %x)\n", &ArcName, RC); + } + + return RC; +} + + +// IDEPolledRead +// +// DESCRIPTION: +// Read a sector of data from the drive in a polled fashion. +// +// RUN LEVEL: +// PASSIVE_LEVEL +// +// ARGUMENTS: +// IN WORD Address Address of command port for drive +// IN BYTE PreComp Value to write to precomp register +// IN BYTE SectorCnt Value to write to sectorCnt register +// IN BYTE SectorNum Value to write to sectorNum register +// IN BYTE CylinderLow Value to write to CylinderLow register +// IN BYTE CylinderHigh Value to write to CylinderHigh register +// IN BYTE DrvHead Value to write to Drive/Head register +// IN BYTE Command Value to write to Command register +// OUT BYTE *Buffer Buffer for output data +// +// RETURNS: +// int 0 is success, non 0 is an error code +// + +static int +IDEPolledRead(IN WORD Address, + IN BYTE PreComp, + IN BYTE SectorCnt, + IN BYTE SectorNum , + IN BYTE CylinderLow, + IN BYTE CylinderHigh, + IN BYTE DrvHead, + IN BYTE Command, + OUT BYTE *Buffer) +{ + BYTE Status; + int RetryCount; + + /* Wait for STATUS.BUSY to clear */ + for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++) + { + Status = IDEReadStatus(Address); + if (!(Status & IDE_SR_BUSY)) + { + break; + } + KeStallExecutionProcessor(10); + } + if (RetryCount == IDE_MAX_BUSY_RETRIES) + { + return IDE_ER_ABRT; + } + + /* Write Drive/Head to select drive */ + IDEWriteDriveHead(Address, IDE_DH_FIXED | DrvHead); + + /* Wait for STATUS.BUSY to clear and STATUS.DRDY to assert */ + for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES; RetryCount++) + { + Status = IDEReadStatus(Address); + if (!(Status & IDE_SR_BUSY) && (Status & IDE_SR_DRDY)) + { + break; + } + KeStallExecutionProcessor(10); + } + if (RetryCount == IDE_MAX_BUSY_RETRIES) + { + return IDE_ER_ABRT; + } + + /* 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(Address, PreComp); + IDEWriteSectorCount(Address, SectorCnt); + IDEWriteSectorNum(Address, SectorNum); + IDEWriteCylinderHigh(Address, CylinderHigh); + IDEWriteCylinderLow(Address, CylinderLow); + IDEWriteDriveHead(Address, IDE_DH_FIXED | DrvHead); + + /* Issue the command */ + IDEWriteCommand(Address, Command); + KeStallExecutionProcessor(50); + + while (1) + { + + // wait for DRQ or error + for (RetryCount = 0; RetryCount < IDE_MAX_POLL_RETRIES; RetryCount++) + { + Status = IDEReadStatus(Address); + if (!(Status & IDE_SR_BUSY)) + { + if (Status & IDE_SR_ERR) + { + BYTE Err = IDEReadError(Address); + return Err; + } + else if (Status & IDE_SR_DRQ) + { + break; + } + } + KeStallExecutionProcessor(10); + } + if (RetryCount >= IDE_MAX_POLL_RETRIES) + { + return IDE_ER_ABRT; + } + + // Read data into buffer + IDEReadBlock(Address, Buffer, IDE_SECTOR_BUF_SZ); + Buffer += IDE_SECTOR_BUF_SZ; + + // Check for more sectors to read + for (RetryCount = 0; RetryCount < IDE_MAX_BUSY_RETRIES && + (IDEReadStatus(Address) & IDE_SR_DRQ); RetryCount++) + ; + if (!(IDEReadStatus(Address) & IDE_SR_BUSY)) + { + return 0; + } + } +} + +// ------------------------------------------- Nondiscardable statics + +// IDEDispatchOpenClose +// +// DESCRIPTION: +// Answer requests for Open/Close calls: a null operation +// +// RUN LEVEL: +// PASSIVE_LEVEL +// +// ARGUMENTS: +// Standard dispatch arguments +// +// RETURNS: +// NTSTATUS +// + +static NTSTATUS STDCALL +IDEDispatchOpenClose(IN PDEVICE_OBJECT pDO, + IN PIRP Irp) +{ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +// IDEDispatchReadWrite +// +// DESCRIPTION: +// Answer requests for reads and writes +// +// RUN LEVEL: +// PASSIVE_LEVEL +// +// ARGUMENTS: +// Standard dispatch arguments +// +// RETURNS: +// NTSTATUS +// + +static NTSTATUS STDCALL +IDEDispatchReadWrite(IN PDEVICE_OBJECT pDO, + IN PIRP Irp) +{ + ULONG IrpInsertKey; + LARGE_INTEGER AdjustedOffset, AdjustedExtent, PartitionExtent, InsertKeyLI; + PIO_STACK_LOCATION IrpStack; + PIDE_DEVICE_EXTENSION DeviceExtension; + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + DeviceExtension = (PIDE_DEVICE_EXTENSION)pDO->DeviceExtension; + + // Validate operation parameters + AdjustedOffset = RtlEnlargedIntegerMultiply(DeviceExtension->Offset, + DeviceExtension->BytesPerSector); +DPRINT("Offset:%ld * BytesPerSector:%ld = AdjOffset:%ld:%ld\n", + DeviceExtension->Offset, + DeviceExtension->BytesPerSector, + (unsigned long) AdjustedOffset.u.HighPart, + (unsigned long) AdjustedOffset.u.LowPart); +DPRINT("AdjOffset:%ld:%ld + ByteOffset:%ld:%ld\n", + (unsigned long) AdjustedOffset.u.HighPart, + (unsigned long) AdjustedOffset.u.LowPart, + (unsigned long) IrpStack->Parameters.Read.ByteOffset.u.HighPart, + (unsigned long) IrpStack->Parameters.Read.ByteOffset.u.LowPart); + AdjustedOffset = RtlLargeIntegerAdd(AdjustedOffset, + IrpStack->Parameters.Read.ByteOffset); +DPRINT(" = AdjOffset:%ld:%ld\n", + (unsigned long) AdjustedOffset.u.HighPart, + (unsigned long) AdjustedOffset.u.LowPart); + AdjustedExtent = RtlLargeIntegerAdd(AdjustedOffset, + RtlConvertLongToLargeInteger(IrpStack->Parameters.Read.Length)); +DPRINT("AdjOffset:%ld:%ld + Length:%ld = AdjExtent:%ld:%ld\n", + (unsigned long) AdjustedOffset.u.HighPart, + (unsigned long) AdjustedOffset.u.LowPart, + IrpStack->Parameters.Read.Length, + (unsigned long) AdjustedExtent.u.HighPart, + (unsigned long) AdjustedExtent.u.LowPart); + /*FIXME: this assumption will fail on drives bigger than 1TB */ + PartitionExtent.QuadPart = DeviceExtension->Offset + DeviceExtension->Size; + PartitionExtent = RtlExtendedIntegerMultiply(PartitionExtent, + DeviceExtension->BytesPerSector); + if ((AdjustedExtent.QuadPart > PartitionExtent.QuadPart) || + (IrpStack->Parameters.Read.Length & (DeviceExtension->BytesPerSector - 1))) + { + DPRINT("Request failed on bad parameters\n",0); + DPRINT("AdjustedExtent=%d:%d PartitionExtent=%d:%d ReadLength=%d\n", + (unsigned int) AdjustedExtent.u.HighPart, + (unsigned int) AdjustedExtent.u.LowPart, + (unsigned int) PartitionExtent.u.HighPart, + (unsigned int) PartitionExtent.u.LowPart, + IrpStack->Parameters.Read.Length); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + // Adjust operation to absolute sector offset + IrpStack->Parameters.Read.ByteOffset = AdjustedOffset; + + // Start the packet and insert the request in order of sector offset + assert(DeviceExtension->BytesPerSector == 512); + InsertKeyLI = RtlLargeIntegerShiftRight(IrpStack->Parameters.Read.ByteOffset, 9); + IrpInsertKey = InsertKeyLI.u.LowPart; + IoStartPacket(DeviceExtension->DeviceObject, Irp, &IrpInsertKey, NULL); + + DPRINT("Returning STATUS_PENDING\n"); + return STATUS_PENDING; +} + +// IDEDispatchDeviceControl +// +// DESCRIPTION: +// Answer requests for device control calls +// +// RUN LEVEL: +// PASSIVE_LEVEL +// +// ARGUMENTS: +// Standard dispatch arguments +// +// RETURNS: +// NTSTATUS +// + +static NTSTATUS STDCALL +IDEDispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + NTSTATUS RC; + ULONG ControlCode, InputLength, OutputLength; + PIO_STACK_LOCATION IrpStack; + PIDE_DEVICE_EXTENSION DeviceExtension; + PIDE_DEVICE_EXTENSION DiskDeviceExtension; + CCHAR Increment; + + RC = STATUS_SUCCESS; + IrpStack = IoGetCurrentIrpStackLocation(Irp); + ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode; + InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; + OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; + DeviceExtension = (PIDE_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + DiskDeviceExtension = (PIDE_DEVICE_EXTENSION)DeviceExtension->DiskExtension; + Increment = IO_NO_INCREMENT; + + // A huge switch statement in a Windows program?! who would have thought? + switch (ControlCode) + { + case IOCTL_DISK_GET_DRIVE_GEOMETRY: + if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(DISK_GEOMETRY)) + { + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + } + else + { + PDISK_GEOMETRY Geometry; + + Geometry = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer; + + Geometry->MediaType = FixedMedia; + Geometry->Cylinders.QuadPart = DiskDeviceExtension->LogicalCylinders; + Geometry->TracksPerCylinder = DiskDeviceExtension->SectorsPerLogTrk / + DiskDeviceExtension->SectorsPerLogCyl; + Geometry->SectorsPerTrack = DiskDeviceExtension->SectorsPerLogTrk; + Geometry->BytesPerSector = DiskDeviceExtension->BytesPerSector; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + } + break; + + case IOCTL_DISK_GET_PARTITION_INFO: + if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION)) + { + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + } + else if (DeviceExtension != DiskDeviceExtension) + { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + } + else + { + PPARTITION_INFORMATION Buffer; + + Buffer = (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + Buffer->PartitionType = DeviceExtension->PartitionInfo.PartitionType; + Buffer->BootIndicator = DeviceExtension->PartitionInfo.BootIndicator; + Buffer->PartitionNumber = DeviceExtension->PartitionInfo.PartitionNumber; + Buffer->StartingOffset = DeviceExtension->PartitionInfo.StartingOffset; + Buffer->PartitionLength = DeviceExtension->PartitionInfo.PartitionLength; + Buffer->HiddenSectors = DeviceExtension->PartitionInfo.HiddenSectors; + Buffer->RecognizedPartition = TRUE; + Buffer->RewritePartition = FALSE; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + } + + case IOCTL_DISK_SET_PARTITION_INFO: + RC = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Status = RC; + Irp->IoStatus.Information = 0; + break; + + case IOCTL_DISK_GET_DRIVE_LAYOUT: + if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(DRIVE_LAYOUT_INFORMATION)) + { + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + } + else + { + PDRIVE_LAYOUT_INFORMATION PartitionList; + + RC = IoReadPartitionTable(DiskDeviceExtension->DeviceObject, + DiskDeviceExtension->BytesPerSector, + FALSE, + &PartitionList); + if (!NT_SUCCESS(RC)) + { + Irp->IoStatus.Status = RC; + } + else + { + ULONG BufferSize; + + BufferSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, + PartitionEntry[0]); + BufferSize += PartitionList->PartitionCount * sizeof(PARTITION_INFORMATION); + + if (BufferSize > IrpStack->Parameters.DeviceIoControl.OutputBufferLength) + { + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + } + else + { + RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, + PartitionList, + BufferSize); + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = BufferSize; + } + ExFreePool(PartitionList); + } + } + Increment = IO_DISK_INCREMENT; + break; + + + case IOCTL_DISK_SET_DRIVE_LAYOUT: + case IOCTL_DISK_VERIFY: + case IOCTL_DISK_FORMAT_TRACKS: + case IOCTL_DISK_PERFORMANCE: + case IOCTL_DISK_IS_WRITABLE: + 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 + default: + RC = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Status = RC; + Irp->IoStatus.Information = 0; + break; + } + + IoCompleteRequest(Irp, Increment); + + return RC; +} + +// IDEStartIo +// +// DESCRIPTION: +// Get the next requested I/O packet started +// +// RUN LEVEL: +// DISPATCH_LEVEL +// +// ARGUMENTS: +// Dispatch routine standard arguments +// +// RETURNS: +// NTSTATUS +// + +static VOID +STDCALL IDEStartIo(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + LARGE_INTEGER SectorLI; + PIO_STACK_LOCATION IrpStack; + PIDE_DEVICE_EXTENSION DeviceExtension; + KIRQL OldIrql; + + DPRINT("IDEStartIo() called!\n"); + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + DeviceExtension = (PIDE_DEVICE_EXTENSION) DeviceObject->DeviceExtension; + + // FIXME: implement the supported functions + + switch (IrpStack->MajorFunction) + { + case IRP_MJ_READ: + case IRP_MJ_WRITE: + DeviceExtension->Operation = IrpStack->MajorFunction; + DeviceExtension->BytesRequested = IrpStack->Parameters.Read.Length; + assert(DeviceExtension->BytesPerSector == 512); + SectorLI = RtlLargeIntegerShiftRight(IrpStack->Parameters.Read.ByteOffset, 9); + DeviceExtension->StartingSector = SectorLI.u.LowPart; + if (DeviceExtension->BytesRequested > DeviceExtension->BytesPerSector * + IDE_MAX_SECTORS_PER_XFER) + { + DeviceExtension->BytesToTransfer = DeviceExtension->BytesPerSector * + IDE_MAX_SECTORS_PER_XFER; + } + else + { + DeviceExtension->BytesToTransfer = DeviceExtension->BytesRequested; + } + DeviceExtension->BytesRequested -= DeviceExtension->BytesToTransfer; + DeviceExtension->SectorsTransferred = 0; + DeviceExtension->TargetAddress = (BYTE *)MmGetSystemAddressForMdl(Irp->MdlAddress); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + IoAllocateController(DeviceExtension->ControllerObject, + DeviceObject, + IDEAllocateController, + NULL); + KeLowerIrql(OldIrql); + break; + + default: + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + Irp->IoStatus.Information = 0; + KeBugCheck((ULONG)Irp); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + IoStartNextPacket(DeviceObject, FALSE); + break; + } + DPRINT("IDEStartIo() finished!\n"); +} + +// IDEAllocateController + +static IO_ALLOCATION_ACTION STDCALL +IDEAllocateController(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MapRegisterBase, + IN PVOID Ccontext) +{ + PIDE_DEVICE_EXTENSION DeviceExtension; + PIDE_CONTROLLER_EXTENSION ControllerExtension; + + DeviceExtension = (PIDE_DEVICE_EXTENSION) DeviceObject->DeviceExtension; + ControllerExtension = (PIDE_CONTROLLER_EXTENSION) + DeviceExtension->ControllerObject->ControllerExtension; + ControllerExtension->CurrentIrp = Irp; + ControllerExtension->Retries = 0; + return KeSynchronizeExecution(ControllerExtension->Interrupt, + IDEStartController, + DeviceExtension) ? KeepObject : + DeallocateObject; +} + +// IDEStartController + +static BOOLEAN STDCALL +IDEStartController(IN OUT PVOID Context) +{ + BYTE SectorCnt, SectorNum, CylinderLow, CylinderHigh; + BYTE DrvHead, Command; + BYTE Status; + int Retries; + ULONG StartingSector; + PIDE_DEVICE_EXTENSION DeviceExtension; + PIDE_CONTROLLER_EXTENSION ControllerExtension; + PIRP Irp; + + DeviceExtension = (PIDE_DEVICE_EXTENSION) Context; + ControllerExtension = (PIDE_CONTROLLER_EXTENSION) + DeviceExtension->ControllerObject->ControllerExtension; + ControllerExtension->OperationInProgress = TRUE; + ControllerExtension->DeviceForOperation = DeviceExtension; + + // Write controller registers to start opteration + StartingSector = DeviceExtension->StartingSector; + SectorCnt = DeviceExtension->BytesToTransfer / + DeviceExtension->BytesPerSector; + if (DeviceExtension->LBASupported) + { + SectorNum = StartingSector & 0xff; + CylinderLow = (StartingSector >> 8) & 0xff; + CylinderHigh = (StartingSector >> 16) & 0xff; + DrvHead = ((StartingSector >> 24) & 0x0f) | + (DeviceExtension->UnitNumber ? IDE_DH_DRV1 : 0) | + IDE_DH_LBA; + } + else + { + SectorNum = (StartingSector % DeviceExtension->SectorsPerLogTrk) + 1; + StartingSector /= DeviceExtension->SectorsPerLogTrk; + DrvHead = (StartingSector % DeviceExtension->LogicalHeads) | + (DeviceExtension->UnitNumber ? IDE_DH_DRV1 : 0); + StartingSector /= DeviceExtension->LogicalHeads; + CylinderLow = StartingSector & 0xff; + CylinderHigh = StartingSector >> 8; + } + Command = DeviceExtension->Operation == IRP_MJ_READ ? + IDE_CMD_READ : IDE_CMD_WRITE; + if (DrvHead & IDE_DH_LBA) + { + DPRINT("%s:BUS=%04x:DRV=%d:LBA=1:BLK=%08d:SC=%02x:CM=%02x\n", + DeviceExtension->Operation == IRP_MJ_READ ? "READ" : "WRITE", + ControllerExtension->CommandPortBase, + DrvHead & IDE_DH_DRV1 ? 1 : 0, + ((DrvHead & 0x0f) << 24) + + (CylinderHigh << 16) + (CylinderLow << 8) + SectorNum, + SectorCnt, + Command); + } + else + { + DPRINT("%s:BUS=%04x:DRV=%d:LBA=0:CH=%02x:CL=%02x:HD=%01x:SN=%02x:SC=%02x:CM=%02x\n", + DeviceExtension->Operation == IRP_MJ_READ ? "READ" : "WRITE", + ControllerExtension->CommandPortBase, + DrvHead & IDE_DH_DRV1 ? 1 : 0, + CylinderHigh, + CylinderLow, + DrvHead & 0x0f, + SectorNum, + SectorCnt, + Command); + } + + /* wait for BUSY to clear */ + for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) + { + Status = IDEReadStatus(ControllerExtension->CommandPortBase); + if (!(Status & IDE_SR_BUSY)) + { + break; + } + KeStallExecutionProcessor(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"); + if (++ControllerExtension->Retries > IDE_MAX_CMD_RETRIES) + { + DbgPrint ("Max Retries on Drive reset reached, returning failure\n"); + Irp = ControllerExtension->CurrentIrp; + Irp->IoStatus.Status = STATUS_DISK_OPERATION_FAILED; + Irp->IoStatus.Information = 0; + + return FALSE; + } + else + { + DPRINT ("Beginning drive reset sequence\n"); + IDEBeginControllerReset(ControllerExtension); + + return TRUE; + } + } + + /* Select the desired drive */ + IDEWriteDriveHead(ControllerExtension->CommandPortBase, IDE_DH_FIXED | DrvHead); + + /* wait for BUSY to clear and DRDY to assert */ + for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++) + { + Status = IDEReadStatus(ControllerExtension->CommandPortBase); + if (!(Status & IDE_SR_BUSY) && (Status & IDE_SR_DRDY)) + { + break; + } + KeStallExecutionProcessor(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"); + if (ControllerExtension->Retries++ > IDE_MAX_CMD_RETRIES) + { + DbgPrint ("Max Retries on Drive reset reached, returning failure\n"); + Irp = ControllerExtension->CurrentIrp; + Irp->IoStatus.Status = STATUS_DISK_OPERATION_FAILED; + Irp->IoStatus.Information = 0; + + return FALSE; + } + else + { + DPRINT ("Beginning drive reset sequence\n"); + IDEBeginControllerReset(ControllerExtension); + + return TRUE; + } + } + + /* Setup command parameters */ + IDEWritePrecomp(ControllerExtension->CommandPortBase, 0); + IDEWriteSectorCount(ControllerExtension->CommandPortBase, SectorCnt); + IDEWriteSectorNum(ControllerExtension->CommandPortBase, SectorNum); + IDEWriteCylinderHigh(ControllerExtension->CommandPortBase, CylinderHigh); + IDEWriteCylinderLow(ControllerExtension->CommandPortBase, CylinderLow); + IDEWriteDriveHead(ControllerExtension->CommandPortBase, IDE_DH_FIXED | DrvHead); + + /* Issue command to drive */ + IDEWriteCommand(ControllerExtension->CommandPortBase, Command); + ControllerExtension->TimerState = IDETimerCmdWait; + ControllerExtension->TimerCount = IDE_CMD_TIMEOUT; + + if (DeviceExtension->Operation == IRP_MJ_WRITE) + { + + // Wait for controller ready + for (Retries = 0; Retries < IDE_MAX_WRITE_RETRIES; Retries++) + { + BYTE Status = IDEReadStatus(ControllerExtension->CommandPortBase); + if (!(Status & IDE_SR_BUSY) || (Status & IDE_SR_ERR)) + { + break; + } + KeStallExecutionProcessor(10); + } + if (Retries >= IDE_MAX_BUSY_RETRIES) + { + if (ControllerExtension->Retries++ > IDE_MAX_CMD_RETRIES) + { + Irp = ControllerExtension->CurrentIrp; + Irp->IoStatus.Status = STATUS_DISK_OPERATION_FAILED; + Irp->IoStatus.Information = 0; + + return FALSE; + } + else + { + IDEBeginControllerReset(ControllerExtension); + + return TRUE; + } + } + + // Load the first sector of data into the controller + IDEWriteBlock(ControllerExtension->CommandPortBase, + DeviceExtension->TargetAddress, + IDE_SECTOR_BUF_SZ); + DeviceExtension->TargetAddress += IDE_SECTOR_BUF_SZ; + DeviceExtension->BytesToTransfer -= DeviceExtension->BytesPerSector; + DeviceExtension->SectorsTransferred++; + } + DPRINT ("Command issued to drive, IDEStartController done\n"); + + return TRUE; +} + +// IDEBeginControllerReset + +VOID +IDEBeginControllerReset(PIDE_CONTROLLER_EXTENSION ControllerExtension) +{ + int Retries; + + DPRINT("Controller Reset initiated on %04x\n", + ControllerExtension->ControlPortBase); + + /* Assert drive reset line */ + DPRINT("Asserting reset line\n"); + IDEWriteDriveControl(ControllerExtension->ControlPortBase, IDE_DC_SRST); + + /* Wait for BSY assertion */ + DPRINT("Waiting for BSY assertion\n"); + for (Retries = 0; Retries < IDE_MAX_RESET_RETRIES; Retries++) + { + BYTE Status = IDEReadStatus(ControllerExtension->CommandPortBase); + if ((Status & IDE_SR_BUSY)) + { + break; + } + KeStallExecutionProcessor(10); + } + if (Retries == IDE_MAX_RESET_RETRIES) + { + DPRINT("Timeout on BSY assertion\n"); + } + + /* Negate drive reset line */ + DPRINT("Negating reset line\n"); + IDEWriteDriveControl(ControllerExtension->ControlPortBase, 0); + + // FIXME: handle case of no device 0 + + /* Set timer to check for end of reset */ + ControllerExtension->TimerState = IDETimerResetWaitForBusyNegate; + ControllerExtension->TimerCount = IDE_RESET_BUSY_TIMEOUT; +} + +// IDEIsr +// +// DESCIPTION: +// Handle interrupts for IDE devices +// +// RUN LEVEL: +// DIRQL +// +// ARGUMENTS: +// IN PKINTERRUPT Interrupt The interrupt level in effect +// IN PVOID ServiceContext The driver supplied context +// (the controller extension) +// RETURNS: +// TRUE This ISR handled the interrupt +// FALSE Another ISR must handle this interrupt + +static BOOLEAN STDCALL +IDEIsr(IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext) +{ + BOOLEAN IsLastBlock, AnErrorOccured, RequestIsComplete; + BYTE *TargetAddress; + int Retries; + NTSTATUS ErrorStatus; + ULONG ErrorInformation; + PIRP Irp; + PIDE_DEVICE_EXTENSION DeviceExtension; + PIDE_CONTROLLER_EXTENSION ControllerExtension; + + if (IDEInitialized == FALSE) + { + return FALSE; + } + DPRINT ("IDEIsr called\n"); + + ControllerExtension = (PIDE_CONTROLLER_EXTENSION) ServiceContext; + + // Read the status port to clear the interrtupt (even if it's not ours). + ControllerExtension->DeviceStatus = IDEReadStatus(ControllerExtension->CommandPortBase); + + // If the interrupt is not ours, get the heck outta dodge. + if (!ControllerExtension->OperationInProgress) + { + return FALSE; + } + + DeviceExtension = ControllerExtension->DeviceForOperation; + IsLastBlock = FALSE; + AnErrorOccured = FALSE; + RequestIsComplete = FALSE; + ErrorStatus = STATUS_SUCCESS; + ErrorInformation = 0; + + // Handle error condition if it exists + if (ControllerExtension->DeviceStatus & IDE_SR_ERR) + { + BYTE ErrorReg, SectorCount, SectorNum, CylinderLow, CylinderHigh; + BYTE DriveHead; + + // Log the error + ErrorReg = IDEReadError(ControllerExtension->CommandPortBase); + CylinderLow = IDEReadCylinderLow(ControllerExtension->CommandPortBase); + CylinderHigh = IDEReadCylinderHigh(ControllerExtension->CommandPortBase); + DriveHead = IDEReadDriveHead(ControllerExtension->CommandPortBase); + SectorCount = IDEReadSectorCount(ControllerExtension->CommandPortBase); + SectorNum = IDEReadSectorNum(ControllerExtension->CommandPortBase); + // FIXME: should use the NT error logging facility + DbgPrint ("IDE Error: OP:%02x STAT:%02x ERR:%02x CYLLO:%02x CYLHI:%02x SCNT:%02x SNUM:%02x\n", + DeviceExtension->Operation, + ControllerExtension->DeviceStatus, + ErrorReg, + CylinderLow, + CylinderHigh, + SectorCount, + SectorNum); + + // FIXME: should retry the command and perhaps recalibrate the drive + + // Set error status information + AnErrorOccured = TRUE; + ErrorStatus = STATUS_DISK_OPERATION_FAILED; + ErrorInformation = + (((((((CylinderHigh << 8) + CylinderLow) * + DeviceExtension->LogicalHeads) + + (DriveHead % DeviceExtension->LogicalHeads)) * + DeviceExtension->SectorsPerLogTrk) + SectorNum - 1) - + DeviceExtension->StartingSector) * DeviceExtension->BytesPerSector; + } + else + { + + // Check controller and setup for next transfer + switch (DeviceExtension->Operation) + { + case IRP_MJ_READ: + + // Update controller/device state variables + TargetAddress = DeviceExtension->TargetAddress; + DeviceExtension->TargetAddress += DeviceExtension->BytesPerSector; + DeviceExtension->BytesToTransfer -= DeviceExtension->BytesPerSector; + DeviceExtension->SectorsTransferred++; + + // Remember whether DRQ should be low at end (last block read) + IsLastBlock = DeviceExtension->BytesToTransfer == 0; + + // Wait for DRQ assertion + for (Retries = 0; Retries < IDE_MAX_DRQ_RETRIES && + !(IDEReadStatus(ControllerExtension->CommandPortBase) & IDE_SR_DRQ); + Retries++) + { + KeStallExecutionProcessor(10); + } + + // Copy the block of data + IDEReadBlock(ControllerExtension->CommandPortBase, + TargetAddress, + IDE_SECTOR_BUF_SZ); + + // check DRQ + if (IsLastBlock) + { + for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES && + (IDEReadStatus(ControllerExtension->CommandPortBase) & IDE_SR_BUSY); + Retries++) + { + KeStallExecutionProcessor(10); + } + + // Check for data overrun + if (IDEReadStatus(ControllerExtension->CommandPortBase) & IDE_SR_DRQ) + { + AnErrorOccured = TRUE; + ErrorStatus = STATUS_DATA_OVERRUN; + ErrorInformation = 0; + } + else + { + + // Setup next transfer or set RequestIsComplete + if (DeviceExtension->BytesRequested > + DeviceExtension->BytesPerSector * IDE_MAX_SECTORS_PER_XFER) + { + DeviceExtension->StartingSector += DeviceExtension->SectorsTransferred; + DeviceExtension->SectorsTransferred = 0; + DeviceExtension->BytesToTransfer = + DeviceExtension->BytesPerSector * IDE_MAX_SECTORS_PER_XFER; + DeviceExtension->BytesRequested -= DeviceExtension->BytesToTransfer; + } + else if (DeviceExtension->BytesRequested > 0) + { + DeviceExtension->StartingSector += DeviceExtension->SectorsTransferred; + DeviceExtension->SectorsTransferred = 0; + DeviceExtension->BytesToTransfer = DeviceExtension->BytesRequested; + DeviceExtension->BytesRequested -= DeviceExtension->BytesToTransfer; + } + else + { + RequestIsComplete = TRUE; + } + } + } + break; + + case IRP_MJ_WRITE: + + // check DRQ + if (DeviceExtension->BytesToTransfer == 0) + { + for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES && + (IDEReadStatus(ControllerExtension->CommandPortBase) & IDE_SR_BUSY); + Retries++) + { + KeStallExecutionProcessor(10); + } + + // Check for data overrun + if (IDEReadStatus(ControllerExtension->CommandPortBase) & IDE_SR_DRQ) + { + AnErrorOccured = TRUE; + ErrorStatus = STATUS_DATA_OVERRUN; + ErrorInformation = 0; + } + else + { + + // Setup next transfer or set RequestIsComplete + IsLastBlock = TRUE; + if (DeviceExtension->BytesRequested > + DeviceExtension->BytesPerSector * IDE_MAX_SECTORS_PER_XFER) + { + DeviceExtension->StartingSector += DeviceExtension->SectorsTransferred; + DeviceExtension->SectorsTransferred = 0; + DeviceExtension->BytesToTransfer = + DeviceExtension->BytesPerSector * IDE_MAX_SECTORS_PER_XFER; + DeviceExtension->BytesRequested -= DeviceExtension->BytesToTransfer; + } + else if (DeviceExtension->BytesRequested > 0) + { + DeviceExtension->StartingSector += DeviceExtension->SectorsTransferred; + DeviceExtension->SectorsTransferred = 0; + DeviceExtension->BytesToTransfer = DeviceExtension->BytesRequested; + DeviceExtension->BytesRequested -= DeviceExtension->BytesToTransfer; + } + else + { + RequestIsComplete = TRUE; + } + } + } + else + { + + // Update controller/device state variables + TargetAddress = DeviceExtension->TargetAddress; + DeviceExtension->TargetAddress += DeviceExtension->BytesPerSector; + DeviceExtension->BytesToTransfer -= DeviceExtension->BytesPerSector; + DeviceExtension->SectorsTransferred++; + + // Write block to controller + IDEWriteBlock(ControllerExtension->CommandPortBase, + TargetAddress, + IDE_SECTOR_BUF_SZ); + } + break; + } + } + + // If there was an error or the request is done, complete the packet + if (AnErrorOccured || RequestIsComplete) + { + // Set the return status and info values + Irp = ControllerExtension->CurrentIrp; + Irp->IoStatus.Status = ErrorStatus; + Irp->IoStatus.Information = ErrorInformation; + + // Clear out controller fields + ControllerExtension->OperationInProgress = FALSE; + ControllerExtension->DeviceStatus = 0; + + // Queue the Dpc to finish up + IoRequestDpc(DeviceExtension->DeviceObject, + Irp, + ControllerExtension); + } + else if (IsLastBlock) + { + // Else more data is needed, setup next device I/O + IDEStartController((PVOID)DeviceExtension); + } + + return TRUE; +} + +// IDEDpcForIsr +// DESCRIPTION: +// +// RUN LEVEL: +// +// ARGUMENTS: +// IN PKDPC Dpc +// IN PDEVICE_OBJECT DpcDeviceObject +// IN PIRP DpcIrp +// IN PVOID DpcContext +// +static VOID +IDEDpcForIsr(IN PKDPC Dpc, + IN PDEVICE_OBJECT DpcDeviceObject, + IN PIRP DpcIrp, + IN PVOID DpcContext) +{ + DPRINT("IDEDpcForIsr()\n"); + IDEFinishOperation((PIDE_CONTROLLER_EXTENSION) DpcContext); +} + +// IDEFinishOperation + +static VOID +IDEFinishOperation(PIDE_CONTROLLER_EXTENSION ControllerExtension) +{ + PIDE_DEVICE_EXTENSION DeviceExtension; + PIRP Irp; + ULONG Operation; + + DeviceExtension = ControllerExtension->DeviceForOperation; + Irp = ControllerExtension->CurrentIrp; + Operation = DeviceExtension->Operation; + ControllerExtension->OperationInProgress = FALSE; + ControllerExtension->DeviceForOperation = 0; + ControllerExtension->CurrentIrp = 0; + + // Deallocate the controller + IoFreeController(DeviceExtension->ControllerObject); + + // Start the next packet + IoStartNextPacketByKey(DeviceExtension->DeviceObject, + FALSE, + DeviceExtension->StartingSector); + + // Issue completion of the current packet + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + // Flush cache if necessary + if (Operation == IRP_MJ_READ) + { + KeFlushIoBuffers(Irp->MdlAddress, TRUE, FALSE); + } +} + +// IDEIoTimer +// 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 +IDEIoTimer(PDEVICE_OBJECT DeviceObject, + PVOID Context) +{ + PIDE_CONTROLLER_EXTENSION ControllerExtension; + + // Setup Extension pointer + ControllerExtension = (PIDE_CONTROLLER_EXTENSION) Context; + DPRINT("Timer activated for %04lx\n", ControllerExtension->CommandPortBase); + + // Handle state change if necessary + switch (ControllerExtension->TimerState) + { + case IDETimerResetWaitForBusyNegate: + if (!(IDEReadStatus(ControllerExtension->CommandPortBase) & + IDE_SR_BUSY)) + { + DPRINT("Busy line has negated, waiting for DRDY assert\n"); + ControllerExtension->TimerState = IDETimerResetWaitForDrdyAssert; + ControllerExtension->TimerCount = IDE_RESET_DRDY_TIMEOUT; + return; + } + break; + + case IDETimerResetWaitForDrdyAssert: + if (IDEReadStatus(ControllerExtension->CommandPortBase) & + IDE_SR_DRQ) + { + DPRINT("DRDY has asserted, reset complete\n"); + ControllerExtension->TimerState = IDETimerIdle; + ControllerExtension->TimerCount = 0; + + // FIXME: get diagnostic code from drive 0 + + /* Start current packet command again */ + if (!KeSynchronizeExecution(ControllerExtension->Interrupt, + IDEStartController, + ControllerExtension->DeviceForOperation)) + { + IDEFinishOperation(ControllerExtension); + } + return; + } + break; + + default: + break; + } + + // If we're counting down, then count. + if (ControllerExtension->TimerCount > 0) + { + ControllerExtension->TimerCount--; + + // Else we'll check the state and process if necessary + } + else + { + switch (ControllerExtension->TimerState) + { + case IDETimerIdle: + break; + + case IDETimerCmdWait: + /* Command timed out, reset drive and try again or fail */ + DPRINT("Timeout waiting for command completion\n"); + if (++ControllerExtension->Retries > IDE_MAX_CMD_RETRIES) + { + if (ControllerExtension->CurrentIrp != NULL) + { + DbgPrint ("Max retries has been reached, IRP finished with error\n"); + ControllerExtension->CurrentIrp->IoStatus.Status = STATUS_IO_TIMEOUT; + ControllerExtension->CurrentIrp->IoStatus.Information = 0; + IDEFinishOperation(ControllerExtension); + } + ControllerExtension->TimerState = IDETimerIdle; + ControllerExtension->TimerCount = 0; + } + else + { + IDEBeginControllerReset(ControllerExtension); + } + break; + + case IDETimerResetWaitForBusyNegate: + case IDETimerResetWaitForDrdyAssert: + if (ControllerExtension->CurrentIrp != NULL) + { + DbgPrint ("Timeout waiting for drive reset, giving up on IRP\n"); + ControllerExtension->CurrentIrp->IoStatus.Status = + STATUS_IO_TIMEOUT; + ControllerExtension->CurrentIrp->IoStatus.Information = 0; + IDEFinishOperation(ControllerExtension); + } + ControllerExtension->TimerState = IDETimerIdle; + ControllerExtension->TimerCount = 0; + break; + } + } +} + + diff --git a/reactos/drivers/storage/atapi/atapi.h b/reactos/drivers/storage/atapi/atapi.h new file mode 100644 index 00000000000..817ceb84a7b --- /dev/null +++ b/reactos/drivers/storage/atapi/atapi.h @@ -0,0 +1,293 @@ +// +// IDE.H - defines and typedefs for the IDE Driver module. +// + +#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_DRQ 0x08 +#define IDE_SR_ERR 0x01 +#define IDE_REG_COMMAND 0x0007 +#define IDE_CMD_READ 0x20 +#define IDE_CMD_READ_RETRY 0x21 +#define IDE_CMD_WRITE 0x30 +#define IDE_CMD_WRITE_RETRY 0x31 +#define IDE_CMD_IDENT_DRV 0xEC + +// +// Access macros for command registers +// Each macro takes an address of the command port block, and data +// +#define IDEReadError(Address) \ + (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ERROR))) +#define IDEWritePrecomp(Address, Data) \ + (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_PRECOMP), (Data))) +#define IDEReadSectorCount(Address) \ + (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT))) +#define IDEWriteSectorCount(Address, Data) \ + (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT), (Data))) +#define IDEReadSectorNum(Address) \ + (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM))) +#define IDEWriteSectorNum(Address, Data) \ + (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM), (Data))) +#define IDEReadCylinderLow(Address) \ + (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW))) +#define IDEWriteCylinderLow(Address, Data) \ + (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW), (Data))) +#define IDEReadCylinderHigh(Address) \ + (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH))) +#define IDEWriteCylinderHigh(Address, Data) \ + (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH), (Data))) +#define IDEReadDriveHead(Address) \ + (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD))) +#define IDEWriteDriveHead(Address, Data) \ + (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD), (Data))) +#define IDEReadStatus(Address) \ + (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_STATUS))) +#define IDEWriteCommand(Address, Data) \ + (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_COMMAND), (Data))) + + +// +// Data block read and write commands +// +#define IDEReadBlock(Address, Buffer, Count) \ + (READ_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2)) +#define IDEWriteBlock(Address, Buffer, Count) \ + (WRITE_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2)) + +// +// Access macros for control registers +// Each macro takes an address of the control port blank and data +// +#define IDEWriteDriveControl(Address, Data) \ + (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DEV_CNTRL), (Data))) + +// IDE_DEVICE_EXTENSION +// +// DESCRIPTION: +// Extension to be placed in each device object +// +// ACCESS: +// Allocated from NON-PAGED POOL +// Available at any IRQL +// + +typedef struct _IDE_DEVICE_EXTENSION +{ + PDEVICE_OBJECT DeviceObject; + PCONTROLLER_OBJECT ControllerObject; + struct _IDE_DEVICE_EXTESION *DiskExtension; + PARTITION_INFORMATION PartitionInfo; + int UnitNumber; + BOOLEAN LBASupported; + BOOLEAN DMASupported; + int BytesPerSector; + int LogicalHeads; + int LogicalCylinders; + int SectorsPerLogCyl; + int SectorsPerLogTrk; + int Offset; + int Size; + + int Operation; + ULONG BytesRequested; + ULONG BytesToTransfer; + ULONG BytesRemaining; + ULONG StartingSector; + int SectorsTransferred; + BYTE *TargetAddress; + +} IDE_DEVICE_EXTENSION, *PIDE_DEVICE_EXTENSION; + + +// ATAPI_PORT_EXTENSION +// +// DESCRIPTION: +// Extension to be placed in each device object +// +// ACCESS: +// Allocated from NON-PAGED POOL +// Available at any IRQL +// + +typedef struct _ATAPI_PORT_EXTENSION +{ + PDEVICE_OBJECT DeviceObject; + PCONTROLLER_OBJECT ControllerObject; + ULONG PortNumber; +#if 0 + struct _IDE_DEVICE_EXTESION *DiskExtension; + PARTITION_INFORMATION PartitionInfo; + int UnitNumber; + BOOLEAN LBASupported; + BOOLEAN DMASupported; + int BytesPerSector; + int LogicalHeads; + int LogicalCylinders; + int SectorsPerLogCyl; + int SectorsPerLogTrk; + int Offset; + int Size; + + int Operation; + ULONG BytesRequested; + ULONG BytesToTransfer; + ULONG BytesRemaining; + ULONG StartingSector; + int SectorsTransferred; + BYTE *TargetAddress; +#endif +} ATAPI_PORT_EXTENSION, *PATAPI_PORT_EXTENSION; + + +// IDE_TIMER_STATES +// +// DESCRIPTION: +// An enumeration containing the states in the timer DFA +// + +typedef enum _IDE_TIMER_STATES { + IDETimerIdle, + IDETimerCmdWait, + IDETimerResetWaitForBusyNegate, + IDETimerResetWaitForDrdyAssert +} IDE_TIMER_STATES; + +// IDE_CONTROLLER_EXTENSION +// +// DESCRIPTION: +// Driver-defined structure used to hold miscellaneous controller information. +// +// ACCESS: +// Allocated from NON-PAGED POOL +// Available at any IRQL +// + +typedef struct _IDE_CONTROLLER_EXTENSION { + KSPIN_LOCK SpinLock; + int Number; + int Vector; + int CommandPortBase; + int ControlPortBase; + BOOLEAN DMASupported; + BOOLEAN ControllerInterruptBug; + PKINTERRUPT Interrupt; + + BOOLEAN OperationInProgress; + BYTE DeviceStatus; + PIDE_DEVICE_EXTENSION DeviceForOperation; + PIRP CurrentIrp; + int Retries; + + IDE_TIMER_STATES TimerState; + LONG TimerCount; + PDEVICE_OBJECT TimerDevice; + +} IDE_CONTROLLER_EXTENSION, *PIDE_CONTROLLER_EXTENSION; + +// IDE_DRIVE_IDENTIFY + +typedef struct _IDE_DRIVE_IDENTIFY { + WORD ConfigBits; /*00*/ + WORD LogicalCyls; /*01*/ + WORD Reserved02; /*02*/ + WORD LogicalHeads; /*03*/ + WORD BytesPerTrack; /*04*/ + WORD BytesPerSector; /*05*/ + WORD SectorsPerTrack; /*06*/ + BYTE InterSectorGap; /*07*/ + BYTE InterSectorGapSize; + BYTE Reserved08H; /*08*/ + BYTE BytesInPLO; + WORD VendorUniqueCnt; /*09*/ + char SerialNumber[20]; /*10*/ + WORD ControllerType; /*20*/ + WORD BufferSize; /*21*/ + WORD ECCByteCnt; /*22*/ + char FirmwareRev[8]; /*23*/ + char ModelNumber[40]; /*27*/ + WORD RWMultImplemented; /*47*/ + WORD Reserved48; /*48*/ + WORD 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 + WORD Reserved50; /*50*/ + WORD MinPIOTransTime; /*51*/ + WORD MinDMATransTime; /*52*/ + WORD TMFieldsValid; /*53*/ + WORD TMCylinders; /*54*/ + WORD TMHeads; /*55*/ + WORD TMSectorsPerTrk; /*56*/ + WORD TMCapacityLo; /*57*/ + WORD TMCapacityHi; /*58*/ + WORD Reserved59; /*59*/ + WORD TMSectorCountLo; /*60*/ + WORD TMSectorCountHi; /*61*/ + WORD Reserved62[194]; /*62*/ +} IDE_DRIVE_IDENTIFY, *PIDE_DRIVE_IDENTIFY; + + +#ifdef __cplusplus +} +#endif + +#endif /* __ATAPT_H */ + + diff --git a/reactos/drivers/storage/atapi/atapi.rc b/reactos/drivers/storage/atapi/atapi.rc new file mode 100644 index 00000000000..407833ab9a6 --- /dev/null +++ b/reactos/drivers/storage/atapi/atapi.rc @@ -0,0 +1,39 @@ + +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "ATAPI Device Driver\0" + VALUE "FileVersion", "0.0.1\0" + VALUE "InternalName", "atapi\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalFilename", "atapi.sys\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/reactos/drivers/storage/atapi/makefile b/reactos/drivers/storage/atapi/makefile new file mode 100644 index 00000000000..e9c1e090d0d --- /dev/null +++ b/reactos/drivers/storage/atapi/makefile @@ -0,0 +1,17 @@ +# $Id: makefile,v 1.1 2001/09/09 21:31:13 ekohl Exp $ + +PATH_TO_TOP = ../../.. + +TARGET_TYPE = driver + +TARGET_NAME = atapi + +TARGET_DDKLIBS = scsiport.a + +TARGET_OBJECTS = $(TARGET_NAME).o + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +# EOF diff --git a/reactos/drivers/storage/atapi/partitio.h b/reactos/drivers/storage/atapi/partitio.h new file mode 100644 index 00000000000..06841ee39fa --- /dev/null +++ b/reactos/drivers/storage/atapi/partitio.h @@ -0,0 +1,41 @@ +/** +*** Partition.h - defines and structs for harddrive partition info +*** +*** 05/30/98 RJJ Created +**/ + +#ifndef __PARTITION_H +#define __PARTITION_H + +#define PARTITION_MAGIC 0xaa55 +#define PART_MAGIC_OFFSET 0x01fe +#define PARTITION_OFFSET 0x01be +#define PARTITION_TBL_SIZE 4 +#define PTCHSToLBA(c, h, s, scnt, hcnt) ((s) & 0x3f) + \ + (scnt) * ( (h) + (hcnt) * ((c) | (((s) & 0xc0) << 2))) +#define PTLBAToCHS(lba, c, h, s, scnt, hcnt) ( \ + (s) = (lba) % (scnt) + 1, \ + (lba) /= (scnt), \ + (h) = (lba) % (hcnt), \ + (lba) /= (heads), \ + (c) = (lba) & 0xff, \ + (s) |= ((lba) >> 2) & 0xc0) + + +typedef struct Partition { + unsigned char BootFlags; + unsigned char StartingHead; + unsigned char StartingSector; + unsigned char StartingCylinder; + unsigned char PartitionType; + unsigned char EndingHead; + unsigned char EndingSector; + unsigned char EndingCylinder; + unsigned int StartingBlock; + unsigned int SectorCount; + +} PARTITION; + +#endif // PARTITION_H + +