/*++ Copyright (C) Microsoft Corporation, 1991 - 1999 Module Name: floppy.c Abstract: This is Intel 82077 (aka MIPS) floppy diskette driver for NT. Environment: Kernel mode only. --*/ // // Include files. // #include "stdio.h" #include "ntddk.h" // various NT definitions #include "ntdddisk.h" // disk device driver I/O control codes #include "ntddfdc.h" // fdc I/O control codes and parameters #include "initguid.h" #include "ntddstor.h" #include "mountdev.h" #include "acpiioct.h" #include // this driver's data declarations // // This is the actual definition of FloppyDebugLevel. // Note that it is only defined if this is a "debug" // build. // #if DBG extern ULONG FloppyDebugLevel = 0; #endif #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,DriverEntry) #pragma alloc_text(PAGE,FloppyAddDevice) #pragma alloc_text(PAGE,FloppyPnp) #pragma alloc_text(PAGE,FloppyPower) #pragma alloc_text(PAGE,FlConfigCallBack) #pragma alloc_text(PAGE,FlInitializeControllerHardware) #pragma alloc_text(PAGE,FlInterpretError) #pragma alloc_text(PAGE,FlDatarateSpecifyConfigure) #pragma alloc_text(PAGE,FlRecalibrateDrive) #pragma alloc_text(PAGE,FlDetermineMediaType) #pragma alloc_text(PAGE,FlCheckBootSector) #pragma alloc_text(PAGE,FlConsolidateMediaTypeWithBootSector) #pragma alloc_text(PAGE,FlIssueCommand) #pragma alloc_text(PAGE,FlReadWriteTrack) #pragma alloc_text(PAGE,FlReadWrite) #pragma alloc_text(PAGE,FlFormat) #pragma alloc_text(PAGE,FlFinishOperation) #pragma alloc_text(PAGE,FlStartDrive) #pragma alloc_text(PAGE,FloppyThread) #pragma alloc_text(PAGE,FlAllocateIoBuffer) #pragma alloc_text(PAGE,FlFreeIoBuffer) #pragma alloc_text(PAGE,FloppyCreateClose) #pragma alloc_text(PAGE,FloppyDeviceControl) #pragma alloc_text(PAGE,FloppyReadWrite) #pragma alloc_text(PAGE,FlCheckFormatParameters) #pragma alloc_text(PAGE,FlFdcDeviceIo) #pragma alloc_text(PAGE,FloppySystemControl) #endif #ifdef POOL_TAGGING #ifdef ExAllocatePool #undef ExAllocatePool #endif #define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'polF') #endif // #define KEEP_COUNTERS 1 #ifdef KEEP_COUNTERS ULONG FloppyUsedSeek = 0; ULONG FloppyNoSeek = 0; #endif // // Used for paging the driver. // ULONG PagingReferenceCount = 0; PFAST_MUTEX PagingMutex = NULL; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine is the driver's entry point, called by the I/O system to load the driver. The driver's entry points are initialized and a mutex to control paging is initialized. In DBG mode, this routine also examines the registry for special debug parameters. Arguments: DriverObject - a pointer to the object that represents this device driver. RegistryPath - a pointer to this driver's key in the Services tree. Return Value: STATUS_SUCCESS unless we can't allocate a mutex. --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; #if DBG // // We use this to query into the registry as to whether we // should break at driver entry. // RTL_QUERY_REGISTRY_TABLE paramTable[3]; ULONG zero = 0; ULONG one = 1; ULONG debugLevel = 0; ULONG shouldBreak = 0; ULONG notConfigurable = 0; PWCHAR path; ULONG pathLength; // // Since the registry path parameter is a "counted" UNICODE string, it // might not be zero terminated. For a very short time allocate memory // to hold the registry path zero terminated so that we can use it to // delve into the registry. // // NOTE NOTE!!!! This is not an architected way of breaking into // a driver. It happens to work for this driver because the author // likes to do things this way. // pathLength = RegistryPath->Length + sizeof(WCHAR); if ( path = ExAllocatePool(PagedPool, pathLength) ) { RtlZeroMemory( ¶mTable[0], sizeof(paramTable) ); RtlZeroMemory( path, pathLength); RtlMoveMemory( path, RegistryPath->Buffer, RegistryPath->Length ); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"BreakOnEntry"; paramTable[0].EntryContext = &shouldBreak; paramTable[0].DefaultType = REG_DWORD; paramTable[0].DefaultData = &zero; paramTable[0].DefaultLength = sizeof(ULONG); paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[1].Name = L"DebugLevel"; paramTable[1].EntryContext = &debugLevel; paramTable[1].DefaultType = REG_DWORD; paramTable[1].DefaultData = &zero; paramTable[1].DefaultLength = sizeof(ULONG); if (!NT_SUCCESS(RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, path, ¶mTable[0], NULL, NULL))) { shouldBreak = 0; debugLevel = 0; } ExFreePool( path ); } FloppyDebugLevel = debugLevel; if ( shouldBreak ) { DbgBreakPoint(); } #endif FloppyDump(FLOPSHOW, ("Floppy: DriverEntry\n") ); // // Initialize the driver object with this driver's entry points. // DriverObject->MajorFunction[IRP_MJ_CREATE] = FloppyCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = FloppyCreateClose; DriverObject->MajorFunction[IRP_MJ_READ] = FloppyReadWrite; DriverObject->MajorFunction[IRP_MJ_WRITE] = FloppyReadWrite; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FloppyDeviceControl; DriverObject->MajorFunction[IRP_MJ_PNP] = FloppyPnp; DriverObject->MajorFunction[IRP_MJ_POWER] = FloppyPower; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = FloppySystemControl; DriverObject->DriverUnload = FloppyUnload; DriverObject->DriverExtension->AddDevice = FloppyAddDevice; // // Allocate and initialize a mutex for paging the driver. // PagingMutex = ExAllocatePool( NonPagedPool, sizeof(FAST_MUTEX) ); if ( PagingMutex == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } ExInitializeFastMutex(PagingMutex); // // Now page out the driver and wait for a call to FloppyAddDevice. // MmPageEntireDriver(DriverEntry); DriveMediaLimits = &_DriveMediaLimits[0]; DriveMediaConstants = &_DriveMediaConstants[0]; return ntStatus; } VOID FloppyUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Unload the driver from the system. The paging mutex is freed before final unload. Arguments: DriverObject - a pointer to the object that represents this device driver. Return Value: none --*/ { FloppyDump( FLOPSHOW, ("FloppyUnload:\n")); // // The device object(s) should all be gone by now. // ASSERT( DriverObject->DeviceObject == NULL ); // // Free the paging mutex that was allocated in DriverEntry. // if (PagingMutex != NULL) { ExFreePool(PagingMutex); PagingMutex = NULL; } return; } NTSTATUS FloppyAddDevice( IN PDRIVER_OBJECT DriverObject, IN OUT PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: This routine is the driver's pnp add device entry point. It is called by the pnp manager to initialize the driver. Add device creates and initializes a device object for this FDO and attaches to the underlying PDO. Arguments: DriverObject - a pointer to the object that represents this device driver. PhysicalDeviceObject - a pointer to the underlying PDO to which this new device will attach. Return Value: If we successfully create a device object, STATUS_SUCCESS is returned. Otherwise, return the appropriate error code. --*/ { NTSTATUS ntStatus; PDEVICE_OBJECT deviceObject; PDISKETTE_EXTENSION disketteExtension; FDC_INFO fdcInfo; UCHAR arcNameBuffer[256]; STRING arcNameString; WCHAR deviceNameBuffer[20]; UNICODE_STRING deviceName; ntStatus = STATUS_SUCCESS; FloppyDump( FLOPSHOW, ("FloppyAddDevice: CreateDeviceObject\n")); // // Get some device information from the underlying PDO. // fdcInfo.BufferCount = 0; fdcInfo.BufferSize = 0; ntStatus = FlFdcDeviceIo( PhysicalDeviceObject, IOCTL_DISK_INTERNAL_GET_FDC_INFO, &fdcInfo ); if ( NT_SUCCESS(ntStatus) ) { USHORT i = 0; // // Create a device. We will use the first available device name for // this device. // do { swprintf( deviceNameBuffer, L"\\Device\\Floppy%d", i++ ); RtlInitUnicodeString( &deviceName, deviceNameBuffer ); ntStatus = IoCreateDevice( DriverObject, sizeof( DISKETTE_EXTENSION ), &deviceName, FILE_DEVICE_DISK, (FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE | FILE_DEVICE_SECURE_OPEN), FALSE, &deviceObject ); } while ( ntStatus == STATUS_OBJECT_NAME_COLLISION ); if ( NT_SUCCESS(ntStatus) ) { disketteExtension = (PDISKETTE_EXTENSION)deviceObject->DeviceExtension; // // Save the device name. // FloppyDump( FLOPSHOW | FLOPPNP, ("FloppyAddDevice - Device Object Name - %S\n", deviceNameBuffer) ); disketteExtension->DeviceName.Buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, deviceName.Length ); if ( disketteExtension->DeviceName.Buffer == NULL ) { IoDeleteDevice( deviceObject ); return STATUS_INSUFFICIENT_RESOURCES; } disketteExtension->DeviceName.Length = 0; disketteExtension->DeviceName.MaximumLength = deviceName.Length; RtlCopyUnicodeString( &disketteExtension->DeviceName, &deviceName ); IoGetConfigurationInformation()->FloppyCount++; // // Create a symbolic link from the disk name to the corresponding // ARC name, to be used if we're booting off the disk. This will // if it's not system initialization time; that's fine. The ARC // name looks something like \ArcName\multi(0)disk(0)rdisk(0). // sprintf( arcNameBuffer, "%s(%d)disk(%d)fdisk(%d)", "\\ArcName\\multi", fdcInfo.BusNumber, fdcInfo.ControllerNumber, fdcInfo.PeripheralNumber ); RtlInitString( &arcNameString, arcNameBuffer ); ntStatus = RtlAnsiStringToUnicodeString( &disketteExtension->ArcName, &arcNameString, TRUE ); if ( NT_SUCCESS( ntStatus ) ) { IoAssignArcName( &disketteExtension->ArcName, &deviceName ); } deviceObject->Flags |= DO_DIRECT_IO | DO_POWER_PAGABLE; if ( deviceObject->AlignmentRequirement < FILE_WORD_ALIGNMENT ) { deviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; } deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; disketteExtension->DriverObject = DriverObject; // Set the PDO for use with PlugPlay functions disketteExtension->UnderlyingPDO = PhysicalDeviceObject; FloppyDump( FLOPSHOW, ("FloppyAddDevice: Attaching %p to %p\n", deviceObject, PhysicalDeviceObject)); disketteExtension->TargetObject = IoAttachDeviceToDeviceStack( deviceObject, PhysicalDeviceObject ); FloppyDump( FLOPSHOW, ("FloppyAddDevice: TargetObject = %p\n", disketteExtension->TargetObject) ); KeInitializeSemaphore( &disketteExtension->RequestSemaphore, 0L, MAXLONG ); ExInitializeFastMutex( &disketteExtension->PowerDownMutex ); KeInitializeSpinLock( &disketteExtension->ListSpinLock ); ExInitializeFastMutex( &disketteExtension->ThreadReferenceMutex ); ExInitializeFastMutex( &disketteExtension->HoldNewReqMutex ); InitializeListHead( &disketteExtension->ListEntry ); disketteExtension->ThreadReferenceCount = -1; disketteExtension->IsStarted = FALSE; disketteExtension->IsRemoved = FALSE; disketteExtension->HoldNewRequests = FALSE; InitializeListHead( &disketteExtension->NewRequestQueue ); KeInitializeSpinLock( &disketteExtension->NewRequestQueueSpinLock ); KeInitializeSpinLock( &disketteExtension->FlCancelSpinLock ); KeInitializeEvent(&disketteExtension->QueryPowerEvent, SynchronizationEvent, FALSE); disketteExtension->FloppyControllerAllocated = FALSE; disketteExtension->ReleaseFdcWithMotorRunning = FALSE; disketteExtension->DeviceObject = deviceObject; disketteExtension->IsReadOnly = FALSE; disketteExtension->MediaType = Undetermined; disketteExtension->ControllerConfigurable = TRUE; } } return ntStatus; } NTSTATUS FloppySystemControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*+++ Routine Description ; This is the dispatch routine for IRP_MJ_SYSTEM_CONTROL IRPs. Currently we don't handle it. Just pass it down to the lower device. Arguments: DeviceObject - a pointer to the object that represents the device Irp - a pointer to the I/O Request Packet for this request. Return Value : Status returned by lower device. --*/ { PDISKETTE_EXTENSION disketteExtension = DeviceObject->DeviceExtension; IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(disketteExtension->TargetObject, Irp); } NTSTATUS FlConfigCallBack( IN PVOID Context, IN PUNICODE_STRING PathName, IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN PKEY_VALUE_FULL_INFORMATION *BusInformation, IN CONFIGURATION_TYPE ControllerType, IN ULONG ControllerNumber, IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation, IN CONFIGURATION_TYPE PeripheralType, IN ULONG PeripheralNumber, IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation ) /*++ Routine Description: This routine is used to acquire all of the configuration information for each floppy disk controller and the peripheral driver attached to that controller. Arguments: Context - Pointer to the confuration information we are building up. PathName - unicode registry path. Not Used. BusType - Internal, Isa, ... BusNumber - Which bus if we are on a multibus system. BusInformation - Configuration information about the bus. Not Used. ControllerType - Should always be DiskController. ControllerNumber - Which controller if there is more than one controller in the system. ControllerInformation - Array of pointers to the three pieces of registry information. PeripheralType - Should always be FloppyDiskPeripheral. PeripheralNumber - Which floppy if this controller is maintaining more than one. PeripheralInformation - Arrya of pointers to the three pieces of registry information. Return Value: STATUS_SUCCESS if everything went ok, or STATUS_INSUFFICIENT_RESOURCES if it couldn't map the base csr or acquire the adapter object, or all of the resource information couldn't be acquired. --*/ { // // So we don't have to typecast the context. // PDISKETTE_EXTENSION disketteExtension = Context; // // Simple iteration variable. // ULONG i; PCM_FULL_RESOURCE_DESCRIPTOR peripheralData; NTSTATUS ntStatus; ASSERT(ControllerType == DiskController); ASSERT(PeripheralType == FloppyDiskPeripheral); // // Check if the infprmation from the registry for this device // is valid. // if (!(((PUCHAR)PeripheralInformation[IoQueryDeviceConfigurationData]) + PeripheralInformation[IoQueryDeviceConfigurationData]->DataLength)) { ASSERT(FALSE); return STATUS_INVALID_PARAMETER; } peripheralData = (PCM_FULL_RESOURCE_DESCRIPTOR) (((PUCHAR)PeripheralInformation[IoQueryDeviceConfigurationData]) + PeripheralInformation[IoQueryDeviceConfigurationData]->DataOffset); // // With Version 2.0 or greater for this resource list, we will get // the full int13 information for the drive. So get that if available. // // Otherwise, the only thing that we want out of the peripheral information // is the maximum drive capacity. // // Drop any information on the floor other than the // device specfic floppy information. // for ( i = 0; i < peripheralData->PartialResourceList.Count; i++ ) { PCM_PARTIAL_RESOURCE_DESCRIPTOR partial = &peripheralData->PartialResourceList.PartialDescriptors[i]; if ( partial->Type == CmResourceTypeDeviceSpecific ) { // // Point to right after this partial. This will take // us to the beginning of the "real" device specific. // PCM_FLOPPY_DEVICE_DATA fDeviceData; UCHAR driveType; PDRIVE_MEDIA_CONSTANTS biosDriveMediaConstants = &(disketteExtension->BiosDriveMediaConstants); fDeviceData = (PCM_FLOPPY_DEVICE_DATA)(partial + 1); // // Get the driver density // switch ( fDeviceData->MaxDensity ) { case 360: driveType = DRIVE_TYPE_0360; break; case 1200: driveType = DRIVE_TYPE_1200; break; case 1185: driveType = DRIVE_TYPE_1200; break; case 1423: driveType = DRIVE_TYPE_1440; break; case 1440: driveType = DRIVE_TYPE_1440; break; case 2880: driveType = DRIVE_TYPE_2880; break; default: FloppyDump( FLOPDBGP, ("Floppy: Bad DriveCapacity!\n" "------ density is %d\n", fDeviceData->MaxDensity) ); driveType = DRIVE_TYPE_1200; FloppyDump( FLOPDBGP, ("Floppy: run a setup program to set the floppy\n" "------ drive type; assuming 1.2mb\n" "------ (type is %x)\n",fDeviceData->MaxDensity) ); break; } disketteExtension->DriveType = driveType; // // Pick up all the default from our own table and override // with the BIOS information // *biosDriveMediaConstants = DriveMediaConstants[ DriveMediaLimits[driveType].HighestDriveMediaType]; // // If the version is high enough, get the rest of the // information. DeviceSpecific information with a version >= 2 // should have this information // if ( fDeviceData->Version >= 2 ) { // biosDriveMediaConstants->MediaType = biosDriveMediaConstants->StepRateHeadUnloadTime = fDeviceData->StepRateHeadUnloadTime; biosDriveMediaConstants->HeadLoadTime = fDeviceData->HeadLoadTime; biosDriveMediaConstants->MotorOffTime = fDeviceData->MotorOffTime; biosDriveMediaConstants->SectorLengthCode = fDeviceData->SectorLengthCode; // biosDriveMediaConstants->BytesPerSector = if (fDeviceData->SectorPerTrack == 0) { // This is not a valid sector per track value. // We don't recognize this drive. This bogus // value is often returned by SCSI floppies. return STATUS_SUCCESS; } if (fDeviceData->MaxDensity == 0 ) { // // This values are returned by the LS-120 atapi drive. // BIOS function 8, in int 13 is returned in bl, which // is mapped to this field. The LS-120 returns 0x10 // which is mapped to 0. Thats why we wont pick it up // as a normal floppy. // return STATUS_SUCCESS; } biosDriveMediaConstants->SectorsPerTrack = fDeviceData->SectorPerTrack; biosDriveMediaConstants->ReadWriteGapLength = fDeviceData->ReadWriteGapLength; biosDriveMediaConstants->FormatGapLength = fDeviceData->FormatGapLength; biosDriveMediaConstants->FormatFillCharacter = fDeviceData->FormatFillCharacter; biosDriveMediaConstants->HeadSettleTime = fDeviceData->HeadSettleTime; biosDriveMediaConstants->MotorSettleTimeRead = fDeviceData->MotorSettleTime * 1000 / 8; biosDriveMediaConstants->MotorSettleTimeWrite = fDeviceData->MotorSettleTime * 1000 / 8; if (fDeviceData->MaximumTrackValue == 0) { // This is not a valid maximum track value. // We don't recognize this drive. This bogus // value is often returned by SCSI floppies. return STATUS_SUCCESS; } biosDriveMediaConstants->MaximumTrack = fDeviceData->MaximumTrackValue; biosDriveMediaConstants->DataLength = fDeviceData->DataTransferLength; } } } return STATUS_SUCCESS; } NTSTATUS FlAcpiConfigureFloppy( PDISKETTE_EXTENSION DisketteExtension, PFDC_INFO FdcInfo ) /*++ Routine Description: Arguments: Return Value: --*/ { UCHAR driveType; PDRIVE_MEDIA_CONSTANTS biosDriveMediaConstants = &(DisketteExtension->BiosDriveMediaConstants); if ( !FdcInfo->AcpiFdiSupported ) { return STATUS_UNSUCCESSFUL; } // // Get the driver density // // JB:TBD - review this drive type list. // switch ( (ACPI_FDI_DEVICE_TYPE)FdcInfo->AcpiFdiData.DeviceType ) { case Form525Capacity360: driveType = DRIVE_TYPE_0360; break; case Form525Capacity1200: driveType = DRIVE_TYPE_1200; break; case Form35Capacity720: driveType = DRIVE_TYPE_0720; break; case Form35Capacity1440: driveType = DRIVE_TYPE_1440; break; case Form35Capacity2880: driveType = DRIVE_TYPE_2880; break; default: driveType = DRIVE_TYPE_1200; break; } DisketteExtension->DriveType = driveType; // // Pick up all the default from our own table and override // with the BIOS information // *biosDriveMediaConstants = DriveMediaConstants[ DriveMediaLimits[driveType].HighestDriveMediaType]; biosDriveMediaConstants->StepRateHeadUnloadTime = (UCHAR) FdcInfo->AcpiFdiData.StepRateHeadUnloadTime; biosDriveMediaConstants->HeadLoadTime = (UCHAR) FdcInfo->AcpiFdiData.HeadLoadTime; biosDriveMediaConstants->MotorOffTime = (UCHAR) FdcInfo->AcpiFdiData.MotorOffTime; biosDriveMediaConstants->SectorLengthCode = (UCHAR) FdcInfo->AcpiFdiData.SectorLengthCode; biosDriveMediaConstants->SectorsPerTrack = (UCHAR) FdcInfo->AcpiFdiData.SectorPerTrack; biosDriveMediaConstants->ReadWriteGapLength = (UCHAR) FdcInfo->AcpiFdiData.ReadWriteGapLength; biosDriveMediaConstants->FormatGapLength = (UCHAR) FdcInfo->AcpiFdiData.FormatGapLength; biosDriveMediaConstants->FormatFillCharacter = (UCHAR) FdcInfo->AcpiFdiData.FormatFillCharacter; biosDriveMediaConstants->HeadSettleTime = (UCHAR) FdcInfo->AcpiFdiData.HeadSettleTime; biosDriveMediaConstants->MotorSettleTimeRead = (UCHAR) FdcInfo->AcpiFdiData.MotorSettleTime * 1000 / 8; biosDriveMediaConstants->MotorSettleTimeWrite = (USHORT) FdcInfo->AcpiFdiData.MotorSettleTime * 1000 / 8; biosDriveMediaConstants->MaximumTrack = (UCHAR) FdcInfo->AcpiFdiData.MaxCylinderNumber; biosDriveMediaConstants->DataLength = (UCHAR) FdcInfo->AcpiFdiData.DataTransferLength; return STATUS_SUCCESS; } NTSTATUS FlQueueIrpToThread( IN OUT PIRP Irp, IN OUT PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: This routine queues the given irp to be serviced by the controller's thread. If the thread is down then this routine creates the thread. Arguments: Irp - Supplies the IRP to queue to the controller's thread. ControllerData - Supplies the controller data. Return Value: May return an error if PsCreateSystemThread fails. Otherwise returns STATUS_PENDING and marks the IRP pending. --*/ { KIRQL oldIrql; NTSTATUS status; HANDLE threadHandle; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp ); // // Verify if the system is powering down. If so we fail // the irps. // ExAcquireFastMutex(&DisketteExtension->PowerDownMutex); if (DisketteExtension->PoweringDown == TRUE) { ExReleaseFastMutex(&DisketteExtension->PowerDownMutex); FloppyDump( FLOPDBGP, ("Queue IRP: Bailing out since power irp is waiting.\n")); Irp->IoStatus.Status = STATUS_POWER_STATE_INVALID; Irp->IoStatus.Information = 0; return STATUS_POWER_STATE_INVALID; } ExReleaseFastMutex(&DisketteExtension->PowerDownMutex); FloppyDump( FLOPSHOW, ("Queue IRP: No power irp waiting.\n")); ExAcquireFastMutex(&DisketteExtension->ThreadReferenceMutex); if (++(DisketteExtension->ThreadReferenceCount) == 0) { OBJECT_ATTRIBUTES ObjAttributes; DisketteExtension->ThreadReferenceCount++; FloppyResetDriverPaging(); // // Create the thread. // ASSERT(DisketteExtension->FloppyThread == NULL); InitializeObjectAttributes(&ObjAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); status = PsCreateSystemThread(&threadHandle, (ACCESS_MASK) 0L, &ObjAttributes, (HANDLE) 0L, NULL, FloppyThread, DisketteExtension); if (!NT_SUCCESS(status)) { DisketteExtension->ThreadReferenceCount = -1; FloppyPageEntireDriver(); ExReleaseFastMutex(&DisketteExtension->ThreadReferenceMutex); return status; } status = ObReferenceObjectByHandle( threadHandle, SYNCHRONIZE, NULL, KernelMode, &DisketteExtension->FloppyThread, NULL ); ZwClose(threadHandle); if (!NT_SUCCESS(status)) { DisketteExtension->ThreadReferenceCount = -1; DisketteExtension->FloppyThread = NULL; FloppyPageEntireDriver(); ExReleaseFastMutex(&DisketteExtension->ThreadReferenceMutex); return status; } ExReleaseFastMutex(&DisketteExtension->ThreadReferenceMutex); } else { ExReleaseFastMutex(&DisketteExtension->ThreadReferenceMutex); } IoMarkIrpPending(Irp); ExInterlockedInsertTailList( &DisketteExtension->ListEntry, &Irp->Tail.Overlay.ListEntry, &DisketteExtension->ListSpinLock ); KeReleaseSemaphore( &DisketteExtension->RequestSemaphore, (KPRIORITY) 0, 1, FALSE ); return STATUS_PENDING; } NTSTATUS FloppyCreateClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is called only rarely by the I/O system; it's mainly for layered drivers to call. All it does is complete the IRP successfully. Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: Always returns STATUS_SUCCESS, since this is a null operation. --*/ { UNREFERENCED_PARAMETER( DeviceObject ); FloppyDump(FLOPSHOW, ("FloppyCreateClose...\n")); // // Null operation. Do not give an I/O boost since // no I/O was actually done. IoStatus.Information should be // FILE_OPENED for an open; it's undefined for a close. // Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_SUCCESS; } NTSTATUS FloppyDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system to perform a device I/O control function. Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: STATUS_SUCCESS or STATUS_PENDING if recognized I/O control code, STATUS_INVALID_DEVICE_REQUEST otherwise. --*/ { PIO_STACK_LOCATION irpSp; PDISKETTE_EXTENSION disketteExtension; PDISK_GEOMETRY outputBuffer; NTSTATUS ntStatus; ULONG outputBufferLength; UCHAR i; DRIVE_MEDIA_TYPE lowestDriveMediaType; DRIVE_MEDIA_TYPE highestDriveMediaType; ULONG formatExParametersSize; PFORMAT_EX_PARAMETERS formatExParameters; FloppyDump( FLOPSHOW, ("FloppyDeviceControl...\n") ); disketteExtension = DeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation( Irp ); Irp->IoStatus.Information = 0; // // We need to check if we are currently holding requests. // ExAcquireFastMutex(&(disketteExtension->HoldNewReqMutex)); if (disketteExtension->HoldNewRequests) { // // Queue request only if this is not an ACPI exec method. There is // a nasty recursion with ACPI and fdc/flpy that requires that these // requests get through in order to avoid a deadlock. // if (irpSp->Parameters.DeviceIoControl.IoControlCode != IOCTL_ACPI_ASYNC_EVAL_METHOD) { ntStatus = FloppyQueueRequest( disketteExtension, Irp ); ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); return ntStatus; } } // // If the device has been removed we will just fail this request outright. // if ( disketteExtension->IsRemoved ) { ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_DELETE_PENDING; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_DELETE_PENDING; } // // If the device hasn't been started we will let the IOCTL through. This // is another hack for ACPI. // if (!disketteExtension->IsStarted) { ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); IoSkipCurrentIrpStackLocation( Irp ); return IoCallDriver( disketteExtension->TargetObject, Irp ); } switch( irpSp->Parameters.DeviceIoControl.IoControlCode ) { case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: { PMOUNTDEV_NAME mountName; FloppyDump( FLOPSHOW, ("FloppyDeviceControl: IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n") ); ASSERT(disketteExtension->DeviceName.Buffer); if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME) ) { ntStatus = STATUS_INVALID_PARAMETER; break; } mountName = Irp->AssociatedIrp.SystemBuffer; mountName->NameLength = disketteExtension->DeviceName.Length; if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USHORT) + mountName->NameLength) { ntStatus = STATUS_BUFFER_OVERFLOW; Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); break; } RtlCopyMemory( mountName->Name, disketteExtension->DeviceName.Buffer, mountName->NameLength); ntStatus = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(USHORT) + mountName->NameLength; break; } case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: { PMOUNTDEV_UNIQUE_ID uniqueId; FloppyDump( FLOPSHOW, ("FloppyDeviceControl: IOCTL_MOUNTDEV_QUERY_UNIQUE_ID\n") ); if ( !disketteExtension->InterfaceString.Buffer || irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) { ntStatus = STATUS_INVALID_PARAMETER; break; } uniqueId = Irp->AssociatedIrp.SystemBuffer; uniqueId->UniqueIdLength = disketteExtension->InterfaceString.Length; if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USHORT) + uniqueId->UniqueIdLength) { ntStatus = STATUS_BUFFER_OVERFLOW; Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); break; } RtlCopyMemory( uniqueId->UniqueId, disketteExtension->InterfaceString.Buffer, uniqueId->UniqueIdLength ); ntStatus = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(USHORT) + uniqueId->UniqueIdLength; break; } case IOCTL_DISK_FORMAT_TRACKS: case IOCTL_DISK_FORMAT_TRACKS_EX: // // Make sure that we got all the necessary format parameters. // if ( irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof( FORMAT_PARAMETERS ) ) { FloppyDump(FLOPDBGP, ("Floppy: invalid FORMAT buffer length\n")); ntStatus = STATUS_INVALID_PARAMETER; break; } // // Make sure the parameters we got are reasonable. // if ( !FlCheckFormatParameters( disketteExtension, (PFORMAT_PARAMETERS) Irp->AssociatedIrp.SystemBuffer ) ) { FloppyDump(FLOPDBGP, ("Floppy: invalid FORMAT parameters\n")); ntStatus = STATUS_INVALID_PARAMETER; break; } // // If this is an EX request then make a couple of extra checks // if (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_FORMAT_TRACKS_EX) { if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(FORMAT_EX_PARAMETERS)) { ntStatus = STATUS_INVALID_PARAMETER; break; } formatExParameters = (PFORMAT_EX_PARAMETERS) Irp->AssociatedIrp.SystemBuffer; formatExParametersSize = FIELD_OFFSET(FORMAT_EX_PARAMETERS, SectorNumber) + formatExParameters->SectorsPerTrack*sizeof(USHORT); if (irpSp->Parameters.DeviceIoControl.InputBufferLength < formatExParametersSize || formatExParameters->FormatGapLength >= 0x100 || formatExParameters->SectorsPerTrack >= 0x100) { ntStatus = STATUS_INVALID_PARAMETER; break; } } // // Fall through to queue the request. // case IOCTL_DISK_CHECK_VERIFY: case IOCTL_STORAGE_CHECK_VERIFY: case IOCTL_DISK_GET_DRIVE_GEOMETRY: case IOCTL_DISK_IS_WRITABLE: // // The thread must know which diskette to operate on, but the // request list only passes the IRP. So we'll stick a pointer // to the diskette extension in Type3InputBuffer, which is // a field that isn't used for floppy ioctls. // // // Add the request to the queue, and wake up the thread to // process it. // // irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID) // disketteExtension; FloppyDump( FLOPIRPPATH, ("Floppy: Enqueing up IRP: %p\n",Irp) ); ntStatus = FlQueueIrpToThread(Irp, disketteExtension); break; case IOCTL_DISK_GET_MEDIA_TYPES: case IOCTL_STORAGE_GET_MEDIA_TYPES: { FloppyDump(FLOPSHOW, ("Floppy: IOCTL_DISK_GET_MEDIA_TYPES called\n")); lowestDriveMediaType = DriveMediaLimits[ disketteExtension->DriveType].LowestDriveMediaType; highestDriveMediaType = DriveMediaLimits[ disketteExtension->DriveType].HighestDriveMediaType; outputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; // // Make sure that the input buffer has enough room to return // at least one descriptions of a supported media type. // if ( outputBufferLength < ( sizeof( DISK_GEOMETRY ) ) ) { FloppyDump(FLOPDBGP, ("Floppy: invalid GET_MEDIA_TYPES buffer size\n")); ntStatus = STATUS_BUFFER_TOO_SMALL; break; } // // Assume success, although we might modify it to a buffer // overflow warning below (if the buffer isn't big enough // to hold ALL of the media descriptions). // ntStatus = STATUS_SUCCESS; if ( outputBufferLength < ( sizeof( DISK_GEOMETRY ) * ( highestDriveMediaType - lowestDriveMediaType + 1 ) ) ) { // // The buffer is too small for all of the descriptions; // calculate what CAN fit in the buffer. // FloppyDump(FLOPDBGP, ("Floppy: GET_MEDIA_TYPES buffer size too small\n")); ntStatus = STATUS_BUFFER_OVERFLOW; highestDriveMediaType = (DRIVE_MEDIA_TYPE)( ( lowestDriveMediaType - 1 ) + ( outputBufferLength / sizeof( DISK_GEOMETRY ) ) ); } outputBuffer = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer; for (i = (UCHAR)lowestDriveMediaType; i <= (UCHAR)highestDriveMediaType; i++) { outputBuffer->MediaType = DriveMediaConstants[i].MediaType; outputBuffer->Cylinders.LowPart = DriveMediaConstants[i].MaximumTrack + 1; outputBuffer->Cylinders.HighPart = 0; outputBuffer->TracksPerCylinder = DriveMediaConstants[i].NumberOfHeads; outputBuffer->SectorsPerTrack = DriveMediaConstants[i].SectorsPerTrack; outputBuffer->BytesPerSector = DriveMediaConstants[i].BytesPerSector; FloppyDump( FLOPSHOW, ("Floppy: media types supported [%d]\n" "------- Cylinders low: 0x%x\n" "------- Cylinders high: 0x%x\n" "------- Track/Cyl: 0x%x\n" "------- Sectors/Track: 0x%x\n" "------- Bytes/Sector: 0x%x\n" "------- Media Type: %d\n", i, outputBuffer->Cylinders.LowPart, outputBuffer->Cylinders.HighPart, outputBuffer->TracksPerCylinder, outputBuffer->SectorsPerTrack, outputBuffer->BytesPerSector, outputBuffer->MediaType) ); outputBuffer++; Irp->IoStatus.Information += sizeof( DISK_GEOMETRY ); } break; } default: { // // We pass down IOCTL's because ACPI uses this as a communications // method. ACPI *should* have used a PNP Interface mechanism, but // it's too late now. // ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); IoSkipCurrentIrpStackLocation( Irp ); ntStatus = IoCallDriver( disketteExtension->TargetObject, Irp ); return ntStatus; } } ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); if ( ntStatus != STATUS_PENDING ) { Irp->IoStatus.Status = ntStatus; if (!NT_SUCCESS( ntStatus ) && IoIsErrorUserInduced( ntStatus )) { IoSetHardErrorOrVerifyDevice( Irp, DeviceObject ); } IoCompleteRequest( Irp, IO_NO_INCREMENT ); } return ntStatus; } NTSTATUS FloppyPnp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: --*/ { PIO_STACK_LOCATION irpSp; PDISKETTE_EXTENSION disketteExtension; NTSTATUS ntStatus = STATUS_SUCCESS; ULONG i; FloppyDump( FLOPSHOW, ("FloppyPnp:\n") ); // // Lock down the driver if it is not already locked. // FloppyResetDriverPaging(); disketteExtension = DeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation( Irp ); if ( disketteExtension->IsRemoved ) { // // Since the device is stopped, but we don't hold IRPs, // this is a surprise removal. Just fail it. // Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_DELETE_PENDING; IoCompleteRequest (Irp, IO_NO_INCREMENT); return STATUS_DELETE_PENDING; } switch ( irpSp->MinorFunction ) { case IRP_MN_START_DEVICE: ntStatus = FloppyStartDevice( DeviceObject, Irp ); break; case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_QUERY_REMOVE_DEVICE: if ( irpSp->MinorFunction == IRP_MN_QUERY_STOP_DEVICE ) { FloppyDump( FLOPPNP,("FloppyPnp: IRP_MN_QUERY_STOP_DEVICE - Irp: %p\n", Irp) ); } else { FloppyDump( FLOPPNP,("FloppyPnp: IRP_MN_QUERY_REMOVE_DEVICE - Irp: %p\n", Irp) ); } if ( !disketteExtension->IsStarted ) { // // If we aren't started, we'll just pass the irp down. // Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); ntStatus = IoCallDriver( disketteExtension->TargetObject, Irp ); return ntStatus; } // // Hold all new requests. // ExAcquireFastMutex(&(disketteExtension->HoldNewReqMutex)); disketteExtension->HoldNewRequests = TRUE; // // Queue this irp to the floppy thread, this will shutdown the // floppy thread without waiting for the typical 3 second motor // timeout. // ntStatus = FlQueueIrpToThread( Irp, disketteExtension ); ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); // // Wait for the floppy thread to finish. This could take a few hundred // milliseconds if the motor needs to be shut down. // if ( ntStatus == STATUS_PENDING ) { ASSERT(disketteExtension->FloppyThread != NULL); FlTerminateFloppyThread(disketteExtension); Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation( Irp ); IoCallDriver( disketteExtension->TargetObject, Irp ); ntStatus = STATUS_PENDING; } else { // // We failed to either start the thread or get a pointer to the // thread object. Either way veto the Query. // ntStatus = STATUS_UNSUCCESSFUL; Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); } break; case IRP_MN_CANCEL_STOP_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: if ( irpSp->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE ) { FloppyDump( FLOPPNP,("FloppyPnp: IRP_MN_CANCEL_STOP_DEVICE - Irp: %p\n", Irp) ); } else { FloppyDump( FLOPPNP,("FloppyPnp: IRP_MN_CANCEL_REMOVE_DEVICE - Irp: %p\n", Irp) ); } if ( !disketteExtension->IsStarted ) { // // Nothing to do, just pass the irp down: // no need to start the device // // Set Status to SUCCESS before passing the irp down // Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation (Irp); ntStatus = IoCallDriver( disketteExtension->TargetObject, Irp ); } else { KEVENT doneEvent; // // Set the status to STATUS_SUCCESS // Irp->IoStatus.Status = STATUS_SUCCESS; // // We need to wait for the lower drivers to do their job. // IoCopyCurrentIrpStackLocationToNext (Irp); // // Clear the event: it will be set in the completion // routine. // KeInitializeEvent( &doneEvent, SynchronizationEvent, FALSE); IoSetCompletionRoutine( Irp, FloppyPnpComplete, &doneEvent, TRUE, TRUE, TRUE ); ntStatus = IoCallDriver( disketteExtension->TargetObject, Irp ); if ( ntStatus == STATUS_PENDING ) { KeWaitForSingleObject( &doneEvent, Executive, KernelMode, FALSE, NULL ); ntStatus = Irp->IoStatus.Status; } ExAcquireFastMutex(&(disketteExtension->HoldNewReqMutex)); disketteExtension->HoldNewRequests = FALSE; ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); // // Process the queued requests // FloppyProcessQueuedRequests( disketteExtension ); // // We must now complete the IRP, since we stopped it in the // completetion routine with MORE_PROCESSING_REQUIRED. // Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); } break; case IRP_MN_STOP_DEVICE: FloppyDump( FLOPPNP,("FloppyPnp: IRP_MN_STOP_DEVICE - Irp: %p\n", Irp) ); disketteExtension->IsStarted = FALSE; Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation( Irp ); ntStatus = IoCallDriver( disketteExtension->TargetObject, Irp ); break; case IRP_MN_REMOVE_DEVICE: FloppyDump( FLOPPNP,("FloppyPnp: IRP_MN_REMOVE_DEVICE - Irp: %p\n", Irp) ); FlTerminateFloppyThread(disketteExtension); // // We need to mark the fact that we don't hold requests first, since // we asserted earlier that we are holding requests only if // we're not removed. // ExAcquireFastMutex(&(disketteExtension->HoldNewReqMutex)); disketteExtension->HoldNewRequests = FALSE; ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); disketteExtension->IsStarted = FALSE; disketteExtension->IsRemoved = TRUE; // // Here we either have completed all the requests in a personal // queue when IRP_MN_QUERY_REMOVE was received, or will have to // fail all of them if this is a surprise removal. // Note that fdoData->IsRemoved is TRUE, so pSD_ProcessQueuedRequests // will simply flush the queue, completing each IRP with // STATUS_DELETE_PENDING // FloppyProcessQueuedRequests( disketteExtension ); // // Forward this Irp to the underlying PDO // IoSkipCurrentIrpStackLocation( Irp ); Irp->IoStatus.Status = STATUS_SUCCESS; ntStatus = IoCallDriver( disketteExtension->TargetObject, Irp ); // // Send notification that we are going away. // if ( disketteExtension->InterfaceString.Buffer != NULL ) { IoSetDeviceInterfaceState( &disketteExtension->InterfaceString, FALSE); RtlFreeUnicodeString( &disketteExtension->InterfaceString ); RtlInitUnicodeString( &disketteExtension->InterfaceString, NULL ); } if ( disketteExtension->FloppyInterfaceString.Buffer != NULL ) { IoSetDeviceInterfaceState( &disketteExtension->FloppyInterfaceString, FALSE); RtlFreeUnicodeString( &disketteExtension->FloppyInterfaceString ); RtlInitUnicodeString( &disketteExtension->FloppyInterfaceString, NULL ); } RtlFreeUnicodeString( &disketteExtension->DeviceName ); RtlInitUnicodeString( &disketteExtension->DeviceName, NULL ); if ( disketteExtension->ArcName.Length != 0 ) { IoDeassignArcName( &disketteExtension->ArcName ); RtlFreeUnicodeString( &disketteExtension->ArcName ); RtlInitUnicodeString( &disketteExtension->ArcName, NULL ); } // // Detatch from the undelying device. // IoDetachDevice( disketteExtension->TargetObject ); // // And delete the device. // IoDeleteDevice( DeviceObject ); IoGetConfigurationInformation()->FloppyCount--; break; default: FloppyDump( FLOPPNP, ("FloppyPnp: Unsupported PNP Request %x - Irp: %p\n",irpSp->MinorFunction, Irp) ); IoSkipCurrentIrpStackLocation( Irp ); ntStatus = IoCallDriver( disketteExtension->TargetObject, Irp ); } // // Page out the driver if it is not busy elsewhere. // FloppyPageEntireDriver(); return ntStatus; } NTSTATUS FloppyStartDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS ntStatus; NTSTATUS pnpStatus; KEVENT doneEvent; FDC_INFO fdcInfo; CONFIGURATION_TYPE Dc = DiskController; CONFIGURATION_TYPE Fp = FloppyDiskPeripheral; PDISKETTE_EXTENSION disketteExtension = (PDISKETTE_EXTENSION)DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp ); FloppyDump( FLOPSHOW,("FloppyStartDevice: Irp: %p\n", Irp) ); FloppyDump( FLOPSHOW, (" AllocatedResources = %08x\n",irpSp->Parameters.StartDevice.AllocatedResources)); FloppyDump( FLOPSHOW, (" AllocatedResourcesTranslated = %08x\n",irpSp->Parameters.StartDevice.AllocatedResourcesTranslated)); // // First we must pass this Irp on to the PDO. // KeInitializeEvent( &doneEvent, NotificationEvent, FALSE ); IoCopyCurrentIrpStackLocationToNext( Irp ); IoSetCompletionRoutine( Irp, FloppyPnpComplete, &doneEvent, TRUE, TRUE, TRUE ); ntStatus = IoCallDriver( disketteExtension->TargetObject, Irp ); if ( ntStatus == STATUS_PENDING ) { ntStatus = KeWaitForSingleObject( &doneEvent, Executive, KernelMode, FALSE, NULL ); ASSERT( ntStatus == STATUS_SUCCESS ); ntStatus = Irp->IoStatus.Status; } fdcInfo.BufferCount = 0; fdcInfo.BufferSize = 0; ntStatus = FlFdcDeviceIo( disketteExtension->TargetObject, IOCTL_DISK_INTERNAL_GET_FDC_INFO, &fdcInfo ); if ( NT_SUCCESS(ntStatus) ) { disketteExtension->MaxTransferSize = fdcInfo.MaxTransferSize; if ( (fdcInfo.AcpiBios) && (fdcInfo.AcpiFdiSupported) ) { ntStatus = FlAcpiConfigureFloppy( disketteExtension, &fdcInfo ); if ( disketteExtension->DriveType == DRIVE_TYPE_2880 ) { disketteExtension->PerpendicularMode |= 1 << fdcInfo.PeripheralNumber; } } else { INTERFACE_TYPE InterfaceType; if ( disketteExtension->DriveType == DRIVE_TYPE_2880 ) { disketteExtension->PerpendicularMode |= 1 << fdcInfo.PeripheralNumber; } // // Query the registry till we find the correct interface type, // since we do not know what type of interface we are on. // for ( InterfaceType = 0; InterfaceType < MaximumInterfaceType; InterfaceType++ ) { fdcInfo.BusType = InterfaceType; ntStatus = IoQueryDeviceDescription( &fdcInfo.BusType, &fdcInfo.BusNumber, &Dc, &fdcInfo.ControllerNumber, &Fp, &fdcInfo.PeripheralNumber, FlConfigCallBack, disketteExtension ); if (NT_SUCCESS(ntStatus)) { // // We found the interface we are on. // FloppyDump(FLOPSHOW, ("Interface Type is %x\n", InterfaceType)); break; } } } if ( NT_SUCCESS(ntStatus) ) { disketteExtension->DeviceUnit = (UCHAR)fdcInfo.PeripheralNumber; disketteExtension->DriveOnValue = (UCHAR)(fdcInfo.PeripheralNumber | ( DRVCTL_DRIVE_0 << fdcInfo.PeripheralNumber )); pnpStatus = IoRegisterDeviceInterface( disketteExtension->UnderlyingPDO, (LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID, NULL, &disketteExtension->InterfaceString ); if ( NT_SUCCESS(pnpStatus) ) { pnpStatus = IoSetDeviceInterfaceState( &disketteExtension->InterfaceString, TRUE ); // // Register Floppy Class GUID // pnpStatus = IoRegisterDeviceInterface( disketteExtension->UnderlyingPDO, (LPGUID)&FloppyClassGuid, NULL, &disketteExtension->FloppyInterfaceString ); if ( NT_SUCCESS(pnpStatus) ) { pnpStatus = IoSetDeviceInterfaceState( &disketteExtension->FloppyInterfaceString, TRUE ); } } disketteExtension->IsStarted = TRUE; ExAcquireFastMutex(&(disketteExtension->HoldNewReqMutex)); disketteExtension->HoldNewRequests = FALSE; ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); FloppyProcessQueuedRequests( disketteExtension ); } } Irp->IoStatus.Status = ntStatus; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return ntStatus; } NTSTATUS FloppyPnpComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: A completion routine for use when calling the lower device objects to which our bus (FDO) is attached. --*/ { KeSetEvent ((PKEVENT) Context, 1, FALSE); // No special priority // No Wait return STATUS_MORE_PROCESSING_REQUIRED; // Keep this IRP } VOID FlTerminateFloppyThread( PDISKETTE_EXTENSION DisketteExtension ) { if (DisketteExtension->FloppyThread != NULL) { KeWaitForSingleObject( DisketteExtension->FloppyThread, Executive, KernelMode, FALSE, NULL ); // // Make sure again FloppyThread is not NULL. // if (DisketteExtension->FloppyThread != NULL) { ObDereferenceObject(DisketteExtension->FloppyThread); } DisketteExtension->FloppyThread = NULL; } } NTSTATUS FloppyPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: --*/ { PDISKETTE_EXTENSION disketteExtension; NTSTATUS ntStatus = Irp->IoStatus.Status; PIO_STACK_LOCATION irpSp; POWER_STATE_TYPE type; POWER_STATE state; BOOLEAN WaitForCompletion = TRUE; FloppyDump( FLOPSHOW, ("FloppyPower:\n")); disketteExtension = DeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation( Irp ); type = irpSp->Parameters.Power.Type; state = irpSp->Parameters.Power.State; switch(irpSp->MinorFunction) { case IRP_MN_QUERY_POWER: { FloppyDump( FLOPDBGP, ("IRP_MN_QUERY_POWER : Type - %d, State %d\n", type, state)); if ((type == SystemPowerState) && (state.SystemState > PowerSystemHibernate)) { // // This is a shutdown request. Pass that. // ntStatus = STATUS_SUCCESS; break; } // // If there are no requests being processed or queued up // for floppy, ThreadReferenceCount will be -1. It can be 0 if // there was only one request and that has been dequeued and is // currently being processed. // ExAcquireFastMutex(&disketteExtension->ThreadReferenceMutex); if (disketteExtension->ThreadReferenceCount > 0) { ExReleaseFastMutex(&disketteExtension->ThreadReferenceMutex); FloppyDump(FLOPDBGP, ("Floppy: Requests pending. Cannot powerdown!\n")); PoStartNextPowerIrp(Irp); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_DEVICE_BUSY; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_DEVICE_BUSY; } else if ((disketteExtension->ThreadReferenceCount == 0) && ((disketteExtension->FloppyThread) != NULL)) { FloppyDump(FLOPDBGP, ("Ref count 0. No request pending.\n")); ExReleaseFastMutex(&disketteExtension->ThreadReferenceMutex); ExAcquireFastMutex(&disketteExtension->PowerDownMutex); disketteExtension->ReceivedQueryPower = TRUE; ExReleaseFastMutex(&disketteExtension->PowerDownMutex); KeWaitForSingleObject(&disketteExtension->QueryPowerEvent, Executive, KernelMode, FALSE, NULL); } else { FloppyDump(FLOPDBGP, ("No IRPs pending. Let system hibernate")); ExReleaseFastMutex(&disketteExtension->ThreadReferenceMutex); } ntStatus = STATUS_SUCCESS; break; } case IRP_MN_SET_POWER: { // // Indicate that we are going to power down or power up // so that FloppyThread can process queued requests // accordingly. // if (type == SystemPowerState) { ExAcquireFastMutex(&disketteExtension->PowerDownMutex); if (state.SystemState == PowerSystemWorking) { FloppyDump( FLOPDBGP, ("Powering Up\n")); disketteExtension->PoweringDown = FALSE; WaitForCompletion = FALSE; } else { FloppyDump( FLOPDBGP, ("Powering down\n")); WaitForCompletion = TRUE; disketteExtension->PoweringDown = TRUE; } ExReleaseFastMutex(&disketteExtension->PowerDownMutex); // // Wait till FloppyThread signals that it is done with // the queued requests. // if ((disketteExtension->FloppyThread != NULL) && (WaitForCompletion == TRUE)) { KeWaitForSingleObject( disketteExtension->FloppyThread, Executive, KernelMode, FALSE, NULL ); } } FloppyDump( FLOPSHOW, ("Processing power irp : %p\n", Irp)); ntStatus = STATUS_SUCCESS; break; } default: { break; } } PoStartNextPowerIrp( Irp ); IoSkipCurrentIrpStackLocation( Irp ); ntStatus = PoCallDriver( disketteExtension->TargetObject, Irp ); return ntStatus; } NTSTATUS FloppyReadWrite( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system to read or write to a device that we control. Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: STATUS_INVALID_PARAMETER if parameters are invalid, STATUS_PENDING otherwise. --*/ { PIO_STACK_LOCATION irpSp; NTSTATUS ntStatus; PDISKETTE_EXTENSION disketteExtension; FloppyDump( FLOPSHOW, ("FloppyReadWrite...\n") ); disketteExtension = DeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation( Irp ); // // This IRP was sent to the function driver. // We need to check if we are currently holding requests. // ExAcquireFastMutex(&(disketteExtension->HoldNewReqMutex)); if ( disketteExtension->HoldNewRequests ) { ntStatus = FloppyQueueRequest( disketteExtension, Irp ); ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); return ntStatus; } // // If the device is not active (not started yet or removed) we will // just fail this request outright. // if ( disketteExtension->IsRemoved || !disketteExtension->IsStarted) { ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); if ( disketteExtension->IsRemoved) { ntStatus = STATUS_DELETE_PENDING; } else { ntStatus = STATUS_UNSUCCESSFUL; } Irp->IoStatus.Information = 0; Irp->IoStatus.Status = ntStatus; IoCompleteRequest(Irp, IO_NO_INCREMENT); return ntStatus; } if ( (disketteExtension->MediaType > Unknown) && ((irpSp->Parameters.Read.ByteOffset.LowPart + irpSp->Parameters.Read.Length > disketteExtension->ByteCapacity) || ((irpSp->Parameters.Read.Length & (disketteExtension->BytesPerSector - 1)) != 0 ))) { FloppyDump( FLOPDBGP, ("Floppy: Invalid Parameter, rejecting request\n") ); FloppyDump( FLOPWARN, ("Floppy: Starting offset = %lx\n" "------ I/O Length = %lx\n" "------ ByteCapacity = %lx\n" "------ BytesPerSector = %lx\n", irpSp->Parameters.Read.ByteOffset.LowPart, irpSp->Parameters.Read.Length, disketteExtension->ByteCapacity, disketteExtension->BytesPerSector) ); ntStatus = STATUS_INVALID_PARAMETER; } else { // // verify that user is really expecting some I/O operation to // occur. // if (irpSp->Parameters.Read.Length) { // // Queue request to thread. // FloppyDump( FLOPIRPPATH, ("Floppy: Enqueing up IRP: %p\n",Irp) ); ntStatus = FlQueueIrpToThread(Irp, disketteExtension); } else { // // Complete this zero length request with no boost. // Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; FloppyDump(FLOPDBGP, ("Zero length r/w request. Completing IRP.\n")); ntStatus = STATUS_SUCCESS; } } ExReleaseFastMutex(&(disketteExtension->HoldNewReqMutex)); if ( ntStatus != STATUS_PENDING ) { Irp->IoStatus.Status = ntStatus; FloppyDump(FLOPDBGP, ("Completing request. NTStatus %x\n", ntStatus)); IoCompleteRequest(Irp, IO_NO_INCREMENT); } return ntStatus; } NTSTATUS FlInterpretError( IN UCHAR StatusRegister1, IN UCHAR StatusRegister2 ) /*++ Routine Description: This routine is called when the floppy controller returns an error. Status registers 1 and 2 are passed in, and this returns an appropriate error status. Arguments: StatusRegister1 - the controller's status register #1. StatusRegister2 - the controller's status register #2. Return Value: An NTSTATUS error determined from the status registers. --*/ { if ( ( StatusRegister1 & STREG1_CRC_ERROR ) || ( StatusRegister2 & STREG2_CRC_ERROR ) ) { FloppyDump( FLOPSHOW, ("FlInterpretError: STATUS_CRC_ERROR\n") ); return STATUS_CRC_ERROR; } if ( StatusRegister1 & STREG1_DATA_OVERRUN ) { FloppyDump( FLOPSHOW, ("FlInterpretError: STATUS_DATA_OVERRUN\n") ); return STATUS_DATA_OVERRUN; } if ( ( StatusRegister1 & STREG1_SECTOR_NOT_FOUND ) || ( StatusRegister1 & STREG1_END_OF_DISKETTE ) ) { FloppyDump( FLOPSHOW, ("FlInterpretError: STATUS_NONEXISTENT_SECTOR\n") ); return STATUS_NONEXISTENT_SECTOR; } if ( ( StatusRegister2 & STREG2_DATA_NOT_FOUND ) || ( StatusRegister2 & STREG2_BAD_CYLINDER ) || ( StatusRegister2 & STREG2_DELETED_DATA ) ) { FloppyDump( FLOPSHOW, ("FlInterpretError: STATUS_DEVICE_DATA_ERROR\n") ); return STATUS_DEVICE_DATA_ERROR; } if ( StatusRegister1 & STREG1_WRITE_PROTECTED ) { FloppyDump( FLOPSHOW, ("FlInterpretError: STATUS_MEDIA_WRITE_PROTECTED\n") ); return STATUS_MEDIA_WRITE_PROTECTED; } if ( StatusRegister1 & STREG1_ID_NOT_FOUND ) { FloppyDump( FLOPSHOW, ("FlInterpretError: STATUS_FLOPPY_ID_MARK_NOT_FOUND\n") ); return STATUS_FLOPPY_ID_MARK_NOT_FOUND; } if ( StatusRegister2 & STREG2_WRONG_CYLINDER ) { FloppyDump( FLOPSHOW, ("FlInterpretError: STATUS_FLOPPY_WRONG_CYLINDER\n") ); return STATUS_FLOPPY_WRONG_CYLINDER; } // // There's other error bits, but no good status values to map them // to. Just return a generic one. // FloppyDump( FLOPSHOW, ("FlInterpretError: STATUS_FLOPPY_UNKNOWN_ERROR\n") ); return STATUS_FLOPPY_UNKNOWN_ERROR; } VOID FlFinishOperation( IN OUT PIRP Irp, IN PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: This routine is called by FloppyThread at the end of any operation whether it succeeded or not. If the packet is failing due to a hardware error, this routine will reinitialize the hardware and retry once. When the packet is done, this routine will start the timer to turn off the motor, and complete the IRP. Arguments: Irp - a pointer to the IO Request Packet being processed. DisketteExtension - a pointer to the diskette extension for the diskette on which the operation occurred. Return Value: None. --*/ { NTSTATUS ntStatus; FloppyDump( FLOPSHOW, ("Floppy: FloppyFinishOperation...\n") ); // // See if this packet is being failed due to a hardware error. // if ( ( Irp->IoStatus.Status != STATUS_SUCCESS ) && ( DisketteExtension->HardwareFailed ) ) { DisketteExtension->HardwareFailCount++; if ( DisketteExtension->HardwareFailCount < HARDWARE_RESET_RETRY_COUNT ) { // // This is our first time through (that is, we're not retrying // the packet after a hardware failure). If it failed this first // time because of a hardware problem, set the HardwareFailed flag // and put the IRP at the beginning of the request queue. // ntStatus = FlInitializeControllerHardware( DisketteExtension ); if ( NT_SUCCESS( ntStatus ) ) { FloppyDump( FLOPINFO, ("Floppy: packet failed; hardware reset. Retry.\n") ); // // Force media to be redetermined, in case we messed up // and to make sure FlDatarateSpecifyConfigure() gets // called. // DisketteExtension->MediaType = Undetermined; FloppyDump( FLOPIRPPATH, ("Floppy: irp %x failed - back on the queue with it\n", Irp) ); ExAcquireFastMutex(&DisketteExtension->ThreadReferenceMutex); ASSERT(DisketteExtension->ThreadReferenceCount >= 0); (DisketteExtension->ThreadReferenceCount)++; ExReleaseFastMutex(&DisketteExtension->ThreadReferenceMutex); ExInterlockedInsertHeadList( &DisketteExtension->ListEntry, &Irp->Tail.Overlay.ListEntry, &DisketteExtension->ListSpinLock ); return; } FloppyDump( FLOPDBGP, ("Floppy: packet AND hardware reset failed.\n") ); } } // // If we didn't already RETURN, we're done with this packet so // reset the HardwareFailCount for the next packet. // DisketteExtension->HardwareFailCount = 0; // // If this request was unsuccessful and the error is one that can be // remedied by the user, save the Device Object so that the file system, // after reaching its original entry point, can know the real device. // if ( !NT_SUCCESS( Irp->IoStatus.Status ) && IoIsErrorUserInduced( Irp->IoStatus.Status ) ) { IoSetHardErrorOrVerifyDevice( Irp, DisketteExtension->DeviceObject ); } // // Even if the operation failed, it probably had to wait for the drive // to spin up or somesuch so we'll always complete the request with the // standard priority boost. // if ( ( Irp->IoStatus.Status != STATUS_SUCCESS ) && ( Irp->IoStatus.Status != STATUS_VERIFY_REQUIRED ) && ( Irp->IoStatus.Status != STATUS_NO_MEDIA_IN_DEVICE ) ) { FloppyDump( FLOPDBGP, ("Floppy: IRP failed with error %lx\n", Irp->IoStatus.Status) ); } else { FloppyDump( FLOPINFO, ("Floppy: IoStatus.Status = %x\n", Irp->IoStatus.Status) ); } FloppyDump( FLOPINFO, ("Floppy: IoStatus.Information = %x\n", Irp->IoStatus.Information) ); FloppyDump( FLOPIRPPATH, ("Floppy: Finishing up IRP: %p\n",Irp) ); // // In order to get explorer to request a format of unformatted media // the STATUS_UNRECOGNIZED_MEDIA error must be translated to a generic // STATUS_UNSUCCESSFUL error. // // if ( Irp->IoStatus.Status == STATUS_UNRECOGNIZED_MEDIA ) { // Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; // } IoCompleteRequest( Irp, IO_DISK_INCREMENT ); } NTSTATUS FlStartDrive( IN OUT PDISKETTE_EXTENSION DisketteExtension, IN PIRP Irp, IN BOOLEAN WriteOperation, IN BOOLEAN SetUpMedia, IN BOOLEAN IgnoreChange ) /*++ Routine Description: This routine is called at the beginning of every operation. It cancels the motor timer if it's on, turns the motor on and waits for it to spin up if it was off, resets the disk change line and returns VERIFY_REQUIRED if the disk has been changed, determines the diskette media type if it's not known and SetUpMedia=TRUE, and makes sure that the disk isn't write protected if WriteOperation = TRUE. Arguments: DisketteExtension - a pointer to our data area for the drive being started. Irp - Supplies the I/O request packet. WriteOperation - TRUE if the diskette will be written to, FALSE otherwise. SetUpMedia - TRUE if the media type of the diskette in the drive should be determined. IgnoreChange - Do not return VERIFY_REQUIRED eventhough we are mounting for the first time. Return Value: STATUS_SUCCESS if the drive is started properly; appropriate error propogated otherwise. --*/ { LARGE_INTEGER delay; BOOLEAN motorStarted; BOOLEAN diskChanged; UCHAR driveStatus; NTSTATUS ntStatus = STATUS_SUCCESS; FDC_ENABLE_PARMS fdcEnableParms; FDC_DISK_CHANGE_PARMS fdcDiskChangeParms; FloppyDump( FLOPSHOW, ("Floppy: FloppyStartDrive...\n") ); // // IMPORTANT // NOTE // COMMENT // // Here we will copy the BIOS floppy configuration on top of the // highest media value in our global array so that any type of processing // that will recalibrate the drive can have it done here. // An optimization would be to only do it when we will try to recalibrate // the driver or media in it. // At this point, we ensure that on any processing of a command we // are going to have the real values inthe first entry of the array for // driver constants. // DriveMediaConstants[DriveMediaLimits[DisketteExtension->DriveType]. HighestDriveMediaType] = DisketteExtension->BiosDriveMediaConstants; if ((DisketteExtension->MediaType == Undetermined) || (DisketteExtension->MediaType == Unknown)) { DisketteExtension->DriveMediaConstants = DriveMediaConstants[0]; } // // Grab the timer spin lock and cancel the timer, since we want the // motor to run for the whole operation. If the proper drive is // already running, great; if not, start the motor and wait for it // to spin up. // fdcEnableParms.DriveOnValue = DisketteExtension->DriveOnValue; if ( WriteOperation ) { fdcEnableParms.TimeToWait = DisketteExtension->DriveMediaConstants.MotorSettleTimeWrite; } else { fdcEnableParms.TimeToWait = DisketteExtension->DriveMediaConstants.MotorSettleTimeRead; } ntStatus = FlFdcDeviceIo( DisketteExtension->TargetObject, IOCTL_DISK_INTERNAL_ENABLE_FDC_DEVICE, &fdcEnableParms ); motorStarted = fdcEnableParms.MotorStarted; if (NT_SUCCESS(ntStatus)) { fdcDiskChangeParms.DriveOnValue = DisketteExtension->DriveOnValue; ntStatus = FlFdcDeviceIo( DisketteExtension->TargetObject, IOCTL_DISK_INTERNAL_GET_FDC_DISK_CHANGE, &fdcDiskChangeParms ); driveStatus = fdcDiskChangeParms.DriveStatus; } if (!NT_SUCCESS(ntStatus)) { return ntStatus; } // // Support for 360K drives: // They have no change line, so we will assume a power up of the motor // to be equivalent to a change of floppy (we assume noone will // change the floppy while it is turning. // So force a VERIFY here (unless the file system explicitly turned // it off). // if ( ((DisketteExtension->DriveType == DRIVE_TYPE_0360) && motorStarted) || ((DisketteExtension->DriveType != DRIVE_TYPE_0360) && driveStatus & DSKCHG_DISKETTE_REMOVED) ) { FloppyDump( FLOPSHOW, ("Floppy: disk changed...\n") ); DisketteExtension->MediaType = Undetermined; // // If the volume is mounted, we must tell the filesystem to // verify that the media in the drive is the same volume. // if ( DisketteExtension->DeviceObject->Vpb->Flags & VPB_MOUNTED ) { if (Irp) { IoSetHardErrorOrVerifyDevice( Irp, DisketteExtension->DeviceObject ); } DisketteExtension->DeviceObject->Flags |= DO_VERIFY_VOLUME; } // // Only go through the device reset if we did get the flag set // We really only want to go throught here if the diskette changed, // but on 360 it will always say the diskette has changed. // So based on our previous test, only proceed if it is NOT // a 360K driver if (DisketteExtension->DriveType != DRIVE_TYPE_0360) { // // Now seek twice to reset the "disk changed" line. First // seek to 1. // // Normally we'd do a READ ID after a seek. However, we don't // even know if this disk is formatted. We're not really // trying to get anywhere; we're just doing this to reset the // "disk changed" line so we'll skip the READ ID. // DisketteExtension->FifoBuffer[0] = COMMND_SEEK; DisketteExtension->FifoBuffer[1] = DisketteExtension->DeviceUnit; DisketteExtension->FifoBuffer[2] = 1; ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if ( !NT_SUCCESS( ntStatus ) ) { FloppyDump( FLOPWARN, ("Floppy: seek to 1 returned %x\n", ntStatus) ); return ntStatus; } else { if (!( DisketteExtension->FifoBuffer[0] & STREG0_SEEK_COMPLETE) || ( DisketteExtension->FifoBuffer[1] != 1 ) ) { FloppyDump( FLOPWARN, ("Floppy: Seek to 1 had bad return registers\n") ); DisketteExtension->HardwareFailed = TRUE; return STATUS_FLOPPY_BAD_REGISTERS; } } // // Seek back to 0. We can once again skip the READ ID. // DisketteExtension->FifoBuffer[0] = COMMND_SEEK; DisketteExtension->FifoBuffer[1] = DisketteExtension->DeviceUnit; DisketteExtension->FifoBuffer[2] = 0; // // Floppy drives use by Toshiba systems require a delay // when this operation is performed. // delay.LowPart = (ULONG) -900; delay.HighPart = -1; KeDelayExecutionThread( KernelMode, FALSE, &delay ); ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); // // Again, for Toshiba floppy drives, a delay is required. // delay.LowPart = (ULONG) -5; delay.HighPart = -1; KeDelayExecutionThread( KernelMode, FALSE, &delay ); if ( !NT_SUCCESS( ntStatus ) ) { FloppyDump( FLOPWARN, ("Floppy: seek to 0 returned %x\n", ntStatus) ); return ntStatus; } else { if (!(DisketteExtension->FifoBuffer[0] & STREG0_SEEK_COMPLETE) || ( DisketteExtension->FifoBuffer[1] != 0 ) ) { FloppyDump( FLOPWARN, ("Floppy: Seek to 0 had bad return registers\n") ); DisketteExtension->HardwareFailed = TRUE; return STATUS_FLOPPY_BAD_REGISTERS; } } ntStatus = FlFdcDeviceIo( DisketteExtension->TargetObject, IOCTL_DISK_INTERNAL_GET_FDC_DISK_CHANGE, &fdcDiskChangeParms ); driveStatus = fdcDiskChangeParms.DriveStatus; if (!NT_SUCCESS(ntStatus)) { return ntStatus; } if ( driveStatus & DSKCHG_DISKETTE_REMOVED ) { // // If "disk changed" is still set after the double seek, the // drive door must be opened. // FloppyDump( FLOPINFO, ("Floppy: close the door!\n") ); // // Turn off the flag for now so that we will not get so many // gratuitous verifys. It will be set again the next time. // if(DisketteExtension->DeviceObject->Vpb->Flags & VPB_MOUNTED) { DisketteExtension->DeviceObject->Flags &= ~DO_VERIFY_VOLUME; } return STATUS_NO_MEDIA_IN_DEVICE; } } // // IgnoreChange indicates the file system is in the process // of performing a verify so do not return verify required. // if ( IgnoreChange == FALSE ) { if ( DisketteExtension->DeviceObject->Vpb->Flags & VPB_MOUNTED ) { // // Drive WAS mounted, but door was opened since the last time // we checked so tell the file system to verify the diskette. // FloppyDump( FLOPSHOW, ("Floppy: start drive - verify required because door opened\n") ); return STATUS_VERIFY_REQUIRED; } else { return STATUS_IO_DEVICE_ERROR; } } } if ( SetUpMedia ) { if ( DisketteExtension->MediaType == Undetermined ) { ntStatus = FlDetermineMediaType( DisketteExtension ); } else { if ( DisketteExtension->MediaType == Unknown ) { // // We've already tried to determine the media type and // failed. It's probably not formatted. // FloppyDump( FLOPSHOW, ("Floppy - start drive - media type was unknown\n") ); return STATUS_UNRECOGNIZED_MEDIA; } else { if ( DisketteExtension->DriveMediaType != DisketteExtension->LastDriveMediaType ) { // // Last drive/media combination accessed by the // controller was different, so set up the controller. // ntStatus = FlDatarateSpecifyConfigure( DisketteExtension ); if (!NT_SUCCESS(ntStatus)) { FloppyDump( FLOPWARN, ("Floppy: start drive - bad status from datarate" "------ specify %x\n", ntStatus) ); } } } } } // // If this is a WRITE, check the drive to make sure it's not write // protected. If so, return an error. // if ( ( WriteOperation ) && ( NT_SUCCESS( ntStatus ) ) ) { DisketteExtension->FifoBuffer[0] = COMMND_SENSE_DRIVE_STATUS; DisketteExtension->FifoBuffer[1] = DisketteExtension->DeviceUnit; ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if ( !NT_SUCCESS( ntStatus ) ) { FloppyDump( FLOPWARN, ("Floppy: SENSE_DRIVE returned %x\n", ntStatus) ); return ntStatus; } if ( DisketteExtension->FifoBuffer[0] & STREG3_WRITE_PROTECTED ) { FloppyDump( FLOPSHOW, ("Floppy: start drive - media is write protected\n") ); return STATUS_MEDIA_WRITE_PROTECTED; } } return ntStatus; } NTSTATUS FlDatarateSpecifyConfigure( IN PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: This routine is called to set up the controller every time a new type of diskette is to be accessed. It issues the CONFIGURE command if it's available, does a SPECIFY, sets the data rate, and RECALIBRATEs the drive. The caller must set DisketteExtension->DriveMediaType before calling this routine. Arguments: DisketteExtension - pointer to our data area for the drive to be prepared. Return Value: STATUS_SUCCESS if the controller is properly prepared; appropriate error propogated otherwise. --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; // // If the controller has a CONFIGURE command, use it to enable implied // seeks. If it doesn't, we'll find out here the first time through. // if ( DisketteExtension->ControllerConfigurable ) { DisketteExtension->FifoBuffer[0] = COMMND_CONFIGURE; DisketteExtension->FifoBuffer[1] = 0; DisketteExtension->FifoBuffer[2] = COMMND_CONFIGURE_FIFO_THRESHOLD; DisketteExtension->FifoBuffer[2] += COMMND_CONFIGURE_DISABLE_POLLING; if (!DisketteExtension->DriveMediaConstants.CylinderShift) { DisketteExtension->FifoBuffer[2] += COMMND_CONFIGURE_IMPLIED_SEEKS; } DisketteExtension->FifoBuffer[3] = 0; ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if ( ntStatus == STATUS_DEVICE_NOT_READY ) { DisketteExtension->ControllerConfigurable = FALSE; ntStatus = STATUS_SUCCESS; } } // // Issue SPECIFY command to program the head load and unload // rates, the drive step rate, and the DMA data transfer mode. // if ( NT_SUCCESS( ntStatus ) || ntStatus == STATUS_DEVICE_NOT_READY ) { DisketteExtension->FifoBuffer[0] = COMMND_SPECIFY; DisketteExtension->FifoBuffer[1] = DisketteExtension->DriveMediaConstants.StepRateHeadUnloadTime; DisketteExtension->FifoBuffer[2] = DisketteExtension->DriveMediaConstants.HeadLoadTime; ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if ( NT_SUCCESS( ntStatus ) ) { // // Program the data rate // ntStatus = FlFdcDeviceIo( DisketteExtension->TargetObject, IOCTL_DISK_INTERNAL_SET_FDC_DATA_RATE, &DisketteExtension-> DriveMediaConstants.DataTransferRate ); // // Recalibrate the drive, now that we've changed all its // parameters. // if (NT_SUCCESS(ntStatus)) { ntStatus = FlRecalibrateDrive( DisketteExtension ); } } else { FloppyDump( FLOPINFO, ("Floppy: Failed specify %x\n", ntStatus) ); } } else { FloppyDump( FLOPINFO, ("Floppy: Failed configuration %x\n", ntStatus) ); } if ( NT_SUCCESS( ntStatus ) ) { DisketteExtension->LastDriveMediaType = DisketteExtension->DriveMediaType; } else { DisketteExtension->LastDriveMediaType = Unknown; FloppyDump( FLOPINFO, ("Floppy: Failed recalibrate %x\n", ntStatus) ); } return ntStatus; } NTSTATUS FlRecalibrateDrive( IN PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: This routine recalibrates a drive. It is called whenever we're setting up to access a new diskette, and after certain errors. It will actually recalibrate twice, since many controllers stop after 77 steps and many disks have 80 tracks. Arguments: DisketteExtension - pointer to our data area for the drive to be recalibrated. Return Value: STATUS_SUCCESS if the drive is successfully recalibrated; appropriate error is propogated otherwise. --*/ { NTSTATUS ntStatus; UCHAR recalibrateCount; recalibrateCount = 0; do { // // Issue the recalibrate command // DisketteExtension->FifoBuffer[0] = COMMND_RECALIBRATE; DisketteExtension->FifoBuffer[1] = DisketteExtension->DeviceUnit; ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if ( !NT_SUCCESS( ntStatus ) ) { FloppyDump( FLOPWARN, ("Floppy: recalibrate returned %x\n", ntStatus) ); } if ( NT_SUCCESS( ntStatus ) ) { if ( !( DisketteExtension->FifoBuffer[0] & STREG0_SEEK_COMPLETE ) || ( DisketteExtension->FifoBuffer[1] != 0 ) ) { FloppyDump( FLOPWARN, ("Floppy: recalibrate had bad registers\n") ); DisketteExtension->HardwareFailed = TRUE; ntStatus = STATUS_FLOPPY_BAD_REGISTERS; } } recalibrateCount++; } while ( ( !NT_SUCCESS( ntStatus ) ) && ( recalibrateCount < 2 ) ); FloppyDump( FLOPSHOW, ("Floppy: FloppyRecalibrateDrive: status %x, count %d\n", ntStatus, recalibrateCount) ); return ntStatus; } NTSTATUS FlDetermineMediaType( IN OUT PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: This routine is called by FlStartDrive() when the media type is unknown. It assumes the largest media supported by the drive is available, and keeps trying lower values until it finds one that works. Arguments: DisketteExtension - pointer to our data area for the drive whose media is to checked. Return Value: STATUS_SUCCESS if the type of the media is determined; appropriate error propogated otherwise. --*/ { NTSTATUS ntStatus; PDRIVE_MEDIA_CONSTANTS driveMediaConstants; BOOLEAN mediaTypesExhausted; ULONG retries = 0; USHORT sectorLengthCode; PBOOT_SECTOR_INFO bootSector; LARGE_INTEGER offset; PIRP irp; FloppyDump( FLOPSHOW, ("FlDetermineMediaType...\n") ); DisketteExtension->IsReadOnly = FALSE; // // Try up to three times for read the media id. // for ( retries = 0; retries < 3; retries++ ) { if (retries) { // // We're retrying the media determination because // some controllers don't always want to work // at setup. First we'll reset the device to give // it a better chance of working. // FloppyDump( FLOPINFO, ("FlDetermineMediaType: Resetting controller\n") ); FlInitializeControllerHardware( DisketteExtension ); } // // Assume that the largest supported media is in the drive. If that // turns out to be untrue, we'll try successively smaller media types // until we find what's really in there (or we run out and decide // that the media isn't formatted). // DisketteExtension->DriveMediaType = DriveMediaLimits[DisketteExtension->DriveType].HighestDriveMediaType; DisketteExtension->DriveMediaConstants = DriveMediaConstants[DisketteExtension->DriveMediaType]; mediaTypesExhausted = FALSE; do { ntStatus = FlDatarateSpecifyConfigure( DisketteExtension ); if ( !NT_SUCCESS( ntStatus ) ) { // // The SPECIFY or CONFIGURE commands resulted in an error. // Force ourselves out of this loop and return error. // FloppyDump( FLOPINFO, ("FlDetermineMediaType: DatarateSpecify failed %x\n", ntStatus) ); mediaTypesExhausted = TRUE; } else { // // Use the media constants table when trying to determine // media type. // driveMediaConstants = &DriveMediaConstants[DisketteExtension->DriveMediaType]; // // Now try to read the ID from wherever we're at. // DisketteExtension->FifoBuffer[1] = (UCHAR) ( DisketteExtension->DeviceUnit | ( ( driveMediaConstants->NumberOfHeads - 1 ) << 2 ) ); DisketteExtension->FifoBuffer[0] = COMMND_READ_ID + COMMND_OPTION_MFM; ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if ((!NT_SUCCESS( ntStatus)) || ((DisketteExtension->FifoBuffer[0]&(~STREG0_SEEK_COMPLETE)) != (UCHAR)( ( DisketteExtension->DeviceUnit ) | ((driveMediaConstants->NumberOfHeads - 1 ) << 2 ))) || ( DisketteExtension->FifoBuffer[1] != 0 ) || ( DisketteExtension->FifoBuffer[2] != 0 )) { FloppyDump( FLOPINFO, ("Floppy: READID failed trying lower media\n" "------ status = %x\n" "------ SR0 = %x\n" "------ SR1 = %x\n" "------ SR2 = %x\n", ntStatus, DisketteExtension->FifoBuffer[0], DisketteExtension->FifoBuffer[1], DisketteExtension->FifoBuffer[2]) ); DisketteExtension->DriveMediaType--; DisketteExtension->DriveMediaConstants = DriveMediaConstants[DisketteExtension->DriveMediaType]; if (ntStatus != STATUS_DEVICE_NOT_READY) { ntStatus = STATUS_UNRECOGNIZED_MEDIA; } // // Next comparison must be signed, for when // LowestDriveMediaType = 0. // if ( (CHAR)( DisketteExtension->DriveMediaType ) < (CHAR)( DriveMediaLimits[DisketteExtension->DriveType]. LowestDriveMediaType ) ) { DisketteExtension->MediaType = Unknown; mediaTypesExhausted = TRUE; FloppyDump( FLOPINFO, ("Floppy: Unrecognized media.\n") ); } } } } while ( ( !NT_SUCCESS( ntStatus ) ) && !( mediaTypesExhausted ) ); if (NT_SUCCESS(ntStatus)) { // // We determined the media type. Time to move on. // FloppyDump( FLOPINFO, ("Floppy: Determined media type %d\n", retries) ); break; } } if ( (!NT_SUCCESS( ntStatus )) || mediaTypesExhausted) { FloppyDump( FLOPINFO, ("Floppy: failed determine types status = %x %s\n", ntStatus, mediaTypesExhausted ? "media types exhausted" : "") ); return ntStatus; } DisketteExtension->MediaType = driveMediaConstants->MediaType; DisketteExtension->BytesPerSector = driveMediaConstants->BytesPerSector; DisketteExtension->ByteCapacity = ( driveMediaConstants->BytesPerSector ) * driveMediaConstants->SectorsPerTrack * ( 1 + driveMediaConstants->MaximumTrack ) * driveMediaConstants->NumberOfHeads; FloppyDump( FLOPINFO, ("FlDetermineMediaType: MediaType is %x, bytes per sector %d, capacity %d\n", DisketteExtension->MediaType, DisketteExtension->BytesPerSector, DisketteExtension->ByteCapacity) ); // // Structure copy the media constants into the diskette extension. // DisketteExtension->DriveMediaConstants = DriveMediaConstants[DisketteExtension->DriveMediaType]; // // Check the boot sector for any overriding geometry information. // FlCheckBootSector(DisketteExtension); return ntStatus; } VOID FlAllocateIoBuffer( IN OUT PDISKETTE_EXTENSION DisketteExtension, IN ULONG BufferSize ) /*++ Routine Description: This routine allocates a PAGE_SIZE io buffer. Arguments: ControllerData - Supplies the controller data. BufferSize - Supplies the number of bytes to allocate. Return Value: None. --*/ { BOOLEAN allocateContiguous; LARGE_INTEGER maxDmaAddress; if (DisketteExtension->IoBuffer) { if (DisketteExtension->IoBufferSize >= BufferSize) { return; } FlFreeIoBuffer(DisketteExtension); } if (BufferSize > DisketteExtension->MaxTransferSize ) { allocateContiguous = TRUE; } else { allocateContiguous = FALSE; } if (allocateContiguous) { maxDmaAddress.QuadPart = MAXIMUM_DMA_ADDRESS; DisketteExtension->IoBuffer = MmAllocateContiguousMemory(BufferSize, maxDmaAddress); } else { DisketteExtension->IoBuffer = ExAllocatePool(NonPagedPoolCacheAligned, BufferSize); } if (!DisketteExtension->IoBuffer) { return; } DisketteExtension->IoBufferMdl = IoAllocateMdl(DisketteExtension->IoBuffer, BufferSize, FALSE, FALSE, NULL); if (!DisketteExtension->IoBufferMdl) { if (allocateContiguous) { MmFreeContiguousMemory(DisketteExtension->IoBuffer); } else { ExFreePool(DisketteExtension->IoBuffer); } DisketteExtension->IoBuffer = NULL; return; } try { MmProbeAndLockPages(DisketteExtension->IoBufferMdl, KernelMode, IoModifyAccess); } except(EXCEPTION_EXECUTE_HANDLER) { FloppyDump(FLOPWARN, ("MmProbeAndLockPages failed. Status = %x\n", GetExceptionCode()) ); if (allocateContiguous) { MmFreeContiguousMemory(DisketteExtension->IoBuffer); } else { ExFreePool(DisketteExtension->IoBuffer); } DisketteExtension->IoBuffer = NULL; return; } DisketteExtension->IoBufferSize = BufferSize; } VOID FlFreeIoBuffer( IN OUT PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: This routine free's the controller's IoBuffer. Arguments: DisketteExtension - Supplies the controller data. Return Value: None. --*/ { BOOLEAN contiguousBuffer; if (!DisketteExtension->IoBuffer) { return; } if (DisketteExtension->IoBufferSize > DisketteExtension->MaxTransferSize) { contiguousBuffer = TRUE; } else { contiguousBuffer = FALSE; } DisketteExtension->IoBufferSize = 0; MmUnlockPages(DisketteExtension->IoBufferMdl); IoFreeMdl(DisketteExtension->IoBufferMdl); DisketteExtension->IoBufferMdl = NULL; if (contiguousBuffer) { MmFreeContiguousMemory(DisketteExtension->IoBuffer); } else { ExFreePool(DisketteExtension->IoBuffer); } DisketteExtension->IoBuffer = NULL; } VOID FloppyThread( PVOID Context ) /*++ Routine Description: This is the code executed by the system thread created when the floppy driver initializes. This thread loops forever (or until a flag is set telling the thread to kill itself) processing packets put into the queue by the dispatch routines. For each packet, this thread calls appropriate routines to process the request, and then calls FlFinishOperation() to complete the packet. Arguments: Context - a pointer to our data area for the controller being supported (there is one thread per controller). Return Value: None. --*/ { PIRP irp; PIO_STACK_LOCATION irpSp; PLIST_ENTRY request; PDISKETTE_EXTENSION disketteExtension = Context; NTSTATUS ntStatus = STATUS_SUCCESS; NTSTATUS waitStatus; LARGE_INTEGER queueWait; LARGE_INTEGER acquireWait; ULONG inx; // // Set thread priority to lowest realtime level. // KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY); queueWait.QuadPart = -(1 * 1000 * 10000); acquireWait.QuadPart = -(15 * 1000 * 10000); do { // // Wait for a request from the dispatch routines. // KeWaitForSingleObject won't return error here - this thread // isn't alertable and won't take APCs, and we're not passing in // a timeout. // for (inx = 0; inx < 3; inx++) { waitStatus = KeWaitForSingleObject( (PVOID) &disketteExtension->RequestSemaphore, Executive, KernelMode, FALSE, &queueWait ); if (waitStatus == STATUS_TIMEOUT) { ExAcquireFastMutex(&disketteExtension->PowerDownMutex); if (disketteExtension->ReceivedQueryPower == TRUE) { ExReleaseFastMutex(&disketteExtension->PowerDownMutex); break; } else { ExReleaseFastMutex(&disketteExtension->PowerDownMutex); } } else { break; } } if (waitStatus == STATUS_TIMEOUT) { if (disketteExtension->FloppyControllerAllocated) { FloppyDump(FLOPSHOW, ("Floppy: Timed Out - Turning off the motor\n") ); FlFdcDeviceIo( disketteExtension->TargetObject, IOCTL_DISK_INTERNAL_DISABLE_FDC_DEVICE, NULL ); FlFdcDeviceIo( disketteExtension->TargetObject, IOCTL_DISK_INTERNAL_RELEASE_FDC, disketteExtension->DeviceObject ); disketteExtension->FloppyControllerAllocated = FALSE; } ExAcquireFastMutex(&disketteExtension->ThreadReferenceMutex); if (disketteExtension->ThreadReferenceCount == 0) { disketteExtension->ThreadReferenceCount = -1; ASSERT(disketteExtension->FloppyThread != NULL); // // FloppyThread could be NULL in the unlikely event the // ObReferenceObjectByHandle failed when we created the // thread. // if (disketteExtension->FloppyThread != NULL) { ObDereferenceObject(disketteExtension->FloppyThread); disketteExtension->FloppyThread = NULL; } ExReleaseFastMutex(&disketteExtension->ThreadReferenceMutex); FloppyPageEntireDriver(); FloppyDump(FLOPDBGP, ("Floppy: Terminating thread in FloppyThread.\n") ); ExAcquireFastMutex(&disketteExtension->PowerDownMutex); if (disketteExtension->ReceivedQueryPower == TRUE) { disketteExtension->ReceivedQueryPower = FALSE; KeSetEvent(&disketteExtension->QueryPowerEvent, IO_NO_INCREMENT, FALSE); } ExReleaseFastMutex(&disketteExtension->PowerDownMutex); PsTerminateSystemThread( STATUS_SUCCESS ); } ExReleaseFastMutex(&disketteExtension->ThreadReferenceMutex); continue; } while (request = ExInterlockedRemoveHeadList( &disketteExtension->ListEntry, &disketteExtension->ListSpinLock)) { ExAcquireFastMutex(&disketteExtension->ThreadReferenceMutex); ASSERT(disketteExtension->ThreadReferenceCount > 0); (disketteExtension->ThreadReferenceCount)--; ExReleaseFastMutex(&disketteExtension->ThreadReferenceMutex); disketteExtension->HardwareFailed = FALSE; irp = CONTAINING_RECORD( request, IRP, Tail.Overlay.ListEntry ); // // Verify if the system is powering down. If so we fail // the irps. // ExAcquireFastMutex(&disketteExtension->PowerDownMutex); if (disketteExtension->PoweringDown == TRUE) { ExReleaseFastMutex(&disketteExtension->PowerDownMutex); FloppyDump( FLOPDBGP, ("Bailing out since power irp is waiting.\n")); irp = CONTAINING_RECORD( request, IRP, Tail.Overlay.ListEntry ); irp->IoStatus.Status = STATUS_POWER_STATE_INVALID; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); continue; } ExReleaseFastMutex(&disketteExtension->PowerDownMutex); FloppyDump( FLOPSHOW, ("No power irp waiting.\n")); irpSp = IoGetCurrentIrpStackLocation( irp ); FloppyDump( FLOPIRPPATH, ("Floppy: Starting up IRP: %p for extension %p\n", irp,irpSp->Parameters.Others.Argument4) ); switch ( irpSp->MajorFunction ) { case IRP_MJ_PNP: FloppyDump( FLOPSHOW, ("FloppyThread: IRP_MN_QUERY_REMOVE_DEVICE\n") ); if ( irpSp->MinorFunction == IRP_MN_QUERY_REMOVE_DEVICE || irpSp->MinorFunction == IRP_MN_QUERY_STOP_DEVICE ) { if ( disketteExtension->FloppyControllerAllocated ) { FlFdcDeviceIo( disketteExtension->TargetObject, IOCTL_DISK_INTERNAL_DISABLE_FDC_DEVICE, NULL ); FlFdcDeviceIo( disketteExtension->TargetObject, IOCTL_DISK_INTERNAL_RELEASE_FDC, disketteExtension->DeviceObject ); disketteExtension->FloppyControllerAllocated = FALSE; } ExAcquireFastMutex( &disketteExtension->ThreadReferenceMutex ); ASSERT(disketteExtension->ThreadReferenceCount == 0); disketteExtension->ThreadReferenceCount = -1; ExReleaseFastMutex(&disketteExtension->ThreadReferenceMutex); FloppyPageEntireDriver(); PsTerminateSystemThread( STATUS_SUCCESS ); } else { ntStatus = STATUS_INVALID_DEVICE_REQUEST; } break; case IRP_MJ_READ: case IRP_MJ_WRITE: { // // Get the diskette extension from where it was hidden // in the IRP. // // disketteExtension = (PDISKETTE_EXTENSION) // irpSp->Parameters.Others.Argument4; if (!disketteExtension->FloppyControllerAllocated) { ntStatus = FlFdcDeviceIo( disketteExtension->TargetObject, IOCTL_DISK_INTERNAL_ACQUIRE_FDC, &acquireWait ); if (NT_SUCCESS(ntStatus)) { disketteExtension->FloppyControllerAllocated = TRUE; } else { break; } } // // Until the file system clears the DO_VERIFY_VOLUME // flag, we should return all requests with error. // if (( disketteExtension->DeviceObject->Flags & DO_VERIFY_VOLUME ) && !(irpSp->Flags & SL_OVERRIDE_VERIFY_VOLUME)) { FloppyDump( FLOPINFO, ("Floppy: clearing queue; verify required\n") ); // // The disk changed, and we set this bit. Fail // all current IRPs for this device; when all are // returned, the file system will clear // DO_VERIFY_VOLUME. // ntStatus = STATUS_VERIFY_REQUIRED; } else { ntStatus = FlReadWrite( disketteExtension, irp, FALSE ); } break; } case IRP_MJ_DEVICE_CONTROL: { // disketteExtension = (PDISKETTE_EXTENSION) // irpSp->Parameters.DeviceIoControl.Type3InputBuffer; if (!disketteExtension->FloppyControllerAllocated) { ntStatus = FlFdcDeviceIo( disketteExtension->TargetObject, IOCTL_DISK_INTERNAL_ACQUIRE_FDC, &acquireWait ); if (NT_SUCCESS(ntStatus)) { disketteExtension->FloppyControllerAllocated = TRUE; } else { break; } } // // Until the file system clears the DO_VERIFY_VOLUME // flag, we should return all requests with error. // if (( disketteExtension->DeviceObject->Flags & DO_VERIFY_VOLUME ) && !(irpSp->Flags & SL_OVERRIDE_VERIFY_VOLUME)) { FloppyDump( FLOPINFO, ("Floppy: clearing queue; verify required\n") ); // // The disk changed, and we set this bit. Fail // all current IRPs; when all are returned, the // file system will clear DO_VERIFY_VOLUME. // ntStatus = STATUS_VERIFY_REQUIRED; } else { switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) { case IOCTL_STORAGE_CHECK_VERIFY: case IOCTL_DISK_CHECK_VERIFY: { // // Just start the drive; it will // automatically check whether or not the // disk has been changed. // FloppyDump( FLOPSHOW, ("Floppy: IOCTL_DISK_CHECK_VERIFY called\n") ); // // IgnoreChange should be TRUE if // SL_OVERRIDE_VERIFY_VOLUME flag is set // in the irp. Else, IgnoreChange should // be FALSE // ntStatus = FlStartDrive( disketteExtension, irp, FALSE, FALSE, (BOOLEAN)!!(irpSp->Flags & SL_OVERRIDE_VERIFY_VOLUME)); break; } case IOCTL_DISK_IS_WRITABLE: { // // Start the drive with the WriteOperation // flag set to TRUE. // FloppyDump( FLOPSHOW, ("Floppy: IOCTL_DISK_IS_WRITABLE called\n") ); if (disketteExtension->IsReadOnly) { ntStatus = STATUS_INVALID_PARAMETER; } else { ntStatus = FlStartDrive( disketteExtension, irp, TRUE, FALSE, TRUE); } break; } case IOCTL_DISK_GET_DRIVE_GEOMETRY: { FloppyDump( FLOPSHOW, ("Floppy: IOCTL_DISK_GET_DRIVE_GEOMETRY\n") ); // // If there's enough room to write the // data, start the drive to make sure we // know what type of media is in the drive. // if ( irpSp->Parameters.DeviceIoControl. OutputBufferLength < sizeof( DISK_GEOMETRY ) ) { ntStatus = STATUS_INVALID_PARAMETER; } else { ntStatus = FlStartDrive( disketteExtension, irp, FALSE, TRUE, (BOOLEAN)!!(irpSp->Flags & SL_OVERRIDE_VERIFY_VOLUME)); } // // If the media wasn't formatted, FlStartDrive // returned STATUS_UNRECOGNIZED_MEDIA. // if ( NT_SUCCESS( ntStatus ) || ( ntStatus == STATUS_UNRECOGNIZED_MEDIA )) { PDISK_GEOMETRY outputBuffer = (PDISK_GEOMETRY) irp->AssociatedIrp.SystemBuffer; // Always return the media type, even if // it's unknown. // ntStatus = STATUS_SUCCESS; outputBuffer->MediaType = disketteExtension->MediaType; // // The rest of the fields only have meaning // if the media type is known. // if ( disketteExtension->MediaType == Unknown ) { FloppyDump( FLOPSHOW, ("Floppy: geometry unknown\n") ); // // Just zero out everything. The // caller shouldn't look at it. // outputBuffer->Cylinders.LowPart = 0; outputBuffer->Cylinders.HighPart = 0; outputBuffer->TracksPerCylinder = 0; outputBuffer->SectorsPerTrack = 0; outputBuffer->BytesPerSector = 0; } else { // // Return the geometry of the current // media. // FloppyDump( FLOPSHOW, ("Floppy: geomentry is known\n") ); outputBuffer->Cylinders.LowPart = disketteExtension-> DriveMediaConstants.MaximumTrack + 1; outputBuffer->Cylinders.HighPart = 0; outputBuffer->TracksPerCylinder = disketteExtension-> DriveMediaConstants.NumberOfHeads; outputBuffer->SectorsPerTrack = disketteExtension-> DriveMediaConstants.SectorsPerTrack; outputBuffer->BytesPerSector = disketteExtension-> DriveMediaConstants.BytesPerSector; } FloppyDump( FLOPSHOW, ("Floppy: Geometry\n" "------- Cylinders low: 0x%x\n" "------- Cylinders high: 0x%x\n" "------- Track/Cyl: 0x%x\n" "------- Sectors/Track: 0x%x\n" "------- Bytes/Sector: 0x%x\n" "------- Media Type: %d\n", outputBuffer->Cylinders.LowPart, outputBuffer->Cylinders.HighPart, outputBuffer->TracksPerCylinder, outputBuffer->SectorsPerTrack, outputBuffer->BytesPerSector, outputBuffer->MediaType) ); } irp->IoStatus.Information = sizeof( DISK_GEOMETRY ); break; } case IOCTL_DISK_FORMAT_TRACKS_EX: case IOCTL_DISK_FORMAT_TRACKS: { FloppyDump( FLOPSHOW, ("Floppy: IOCTL_DISK_FORMAT_TRACKS\n") ); // // Start the drive, and make sure it's not // write protected. // ntStatus = FlStartDrive( disketteExtension, irp, TRUE, FALSE, FALSE ); // // Note that FlStartDrive could have returned // STATUS_UNRECOGNIZED_MEDIA if the drive // wasn't formatted. // if ( NT_SUCCESS( ntStatus ) || ( ntStatus == STATUS_UNRECOGNIZED_MEDIA)) { // // We need a single page to do FORMATs. // If we already allocated a buffer, // we'll use that. If not, let's // allocate a single page. Note that // we'd have to do this anyway if there's // not enough map registers. // FlAllocateIoBuffer( disketteExtension, PAGE_SIZE); if (disketteExtension->IoBuffer) { ntStatus = FlFormat(disketteExtension, irp); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } break; } //end of case format } //end of switch controlcode } break; } //end of case IOCTL default: { FloppyDump( FLOPDBGP, ("Floppy: bad majorfunction %x\n",irpSp->MajorFunction) ); ntStatus = STATUS_NOT_IMPLEMENTED; } } //end of switch on majorfunction if (ntStatus == STATUS_DEVICE_BUSY) { // If the status is DEVICE_BUSY then this indicates that the // qic117 has control of the controller. Therefore complete // all remaining requests with STATUS_DEVICE_BUSY. for (;;) { disketteExtension->HardwareFailed = FALSE; irp->IoStatus.Status = STATUS_DEVICE_BUSY; IoCompleteRequest(irp, IO_DISK_INCREMENT); request = ExInterlockedRemoveHeadList( &disketteExtension->ListEntry, &disketteExtension->ListSpinLock ); if (!request) { break; } ExAcquireFastMutex( &disketteExtension->ThreadReferenceMutex); ASSERT(disketteExtension->ThreadReferenceCount > 0); (disketteExtension->ThreadReferenceCount)--; ExReleaseFastMutex( &disketteExtension->ThreadReferenceMutex); irp = CONTAINING_RECORD( request, IRP, Tail.Overlay.ListEntry); } } else { // // All operations leave a final status in ntStatus. Copy it // to the IRP, and then complete the operation. // irp->IoStatus.Status = ntStatus; // // If we'd allocated an I/O buffer free it now // if (disketteExtension->IoBuffer) { FlFreeIoBuffer(disketteExtension); } FlFinishOperation( irp, disketteExtension ); } } // while there are packets to process } while ( TRUE ); } VOID FlConsolidateMediaTypeWithBootSector( IN OUT PDISKETTE_EXTENSION DisketteExtension, IN PBOOT_SECTOR_INFO BootSector ) /*++ Routine Description: This routine adjusts the DisketteExtension data according to the BPB values if this is appropriate. Arguments: DisketteExtension - Supplies the diskette extension. BootSector - Supplies the boot sector information. Return Value: None. --*/ { USHORT bpbNumberOfSectors, bpbNumberOfHeads; USHORT bpbSectorsPerTrack, bpbBytesPerSector; USHORT bpbMediaByte, bpbMaximumTrack; MEDIA_TYPE bpbMediaType; ULONG i, n; PDRIVE_MEDIA_CONSTANTS readidDriveMediaConstants; BOOLEAN changeToBpbMedia; FloppyDump( FLOPSHOW, ("Floppy: First sector read: media descriptor is: 0x%x\n", BootSector->MediaByte[0]) ); if (BootSector->JumpByte[0] != 0xeb && BootSector->JumpByte[0] != 0xe9) { // This is not a formatted floppy so ignore the BPB. return; } bpbNumberOfSectors = BootSector->NumberOfSectors[1]*0x100 + BootSector->NumberOfSectors[0]; bpbNumberOfHeads = BootSector->NumberOfHeads[1]*0x100 + BootSector->NumberOfHeads[0]; bpbSectorsPerTrack = BootSector->SectorsPerTrack[1]*0x100 + BootSector->SectorsPerTrack[0]; bpbBytesPerSector = BootSector->BytesPerSector[1]*0x100 + BootSector->BytesPerSector[0]; bpbMediaByte = BootSector->MediaByte[0]; if (!bpbNumberOfHeads || !bpbSectorsPerTrack) { // Invalid BPB, avoid dividing by zero. return; } bpbMaximumTrack = bpbNumberOfSectors/bpbNumberOfHeads/bpbSectorsPerTrack - 1; // First figure out if this BPB specifies a known media type // independantly of the current drive type. bpbMediaType = Unknown; for (i = 0; i < NUMBER_OF_DRIVE_MEDIA_COMBINATIONS; i++) { if (bpbBytesPerSector == DriveMediaConstants[i].BytesPerSector && bpbSectorsPerTrack == DriveMediaConstants[i].SectorsPerTrack && bpbMaximumTrack == DriveMediaConstants[i].MaximumTrack && bpbNumberOfHeads == DriveMediaConstants[i].NumberOfHeads && bpbMediaByte == DriveMediaConstants[i].MediaByte) { bpbMediaType = DriveMediaConstants[i].MediaType; break; } } // // If DriveType is 3.5", we change 5.25" to 3.5". // The following media's BPB is the same between 5.25" and 3.5", // so 5.25" media types are always found first. // if (DisketteExtension->DriveType == DRIVE_TYPE_1440) { switch (bpbMediaType) { case F5_640_512: bpbMediaType = F3_640_512; break; case F5_720_512: bpbMediaType = F3_720_512; break; case F5_1Pt2_512: bpbMediaType = F3_1Pt2_512; break; case F5_1Pt23_1024: bpbMediaType = F3_1Pt23_1024; break; default: break; } } FloppyDump( FLOPSHOW, ("FLOPPY: After switch media type is: %x\n",bpbMediaType) ); FloppyDump( FLOPINFO, ("FloppyBpb: Media type ") ); if (bpbMediaType == DisketteExtension->MediaType) { // No conflict between BPB and readId result. changeToBpbMedia = FALSE; FloppyDump( FLOPINFO, ("is same\n") ); } else { // There is a conflict between the BPB and the readId // media type. If the new parameters are acceptable // then go with them. readidDriveMediaConstants = &(DisketteExtension->DriveMediaConstants); if (bpbBytesPerSector == readidDriveMediaConstants->BytesPerSector && bpbSectorsPerTrack < 0x100 && bpbMaximumTrack == readidDriveMediaConstants->MaximumTrack && bpbNumberOfHeads <= readidDriveMediaConstants->NumberOfHeads) { changeToBpbMedia = TRUE; } else { changeToBpbMedia = FALSE; } FloppyDump( FLOPINFO, ("%s", changeToBpbMedia ? "will change to Bpb\n" : "will not change\n") ); // If we didn't derive a new media type from the BPB then // just use the one from readId. Also override any // skew compensation since we don't really know anything // about this new media type. if (bpbMediaType == Unknown) { bpbMediaType = readidDriveMediaConstants->MediaType; DisketteExtension->DriveMediaConstants.SkewDelta = 0; } } if (changeToBpbMedia) { // Change the DriveMediaType only if this new media type // falls in line with what is supported by the drive. i = DriveMediaLimits[DisketteExtension->DriveType].LowestDriveMediaType; n = DriveMediaLimits[DisketteExtension->DriveType].HighestDriveMediaType; for (; i <= n; i++) { if (bpbMediaType == DriveMediaConstants[i].MediaType) { DisketteExtension->DriveMediaType = i; break; } } DisketteExtension->MediaType = bpbMediaType; DisketteExtension->ByteCapacity = bpbNumberOfSectors*bpbBytesPerSector; DisketteExtension->DriveMediaConstants.SectorsPerTrack = (UCHAR) bpbSectorsPerTrack; DisketteExtension->DriveMediaConstants.NumberOfHeads = (UCHAR) bpbNumberOfHeads; // If the MSDMF3. signature is there then make this floppy // read-only. if (RtlCompareMemory(BootSector->OemData, "MSDMF3.", 7) == 7) { DisketteExtension->IsReadOnly = TRUE; } } } VOID FlCheckBootSector( IN OUT PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: This routine reads the boot sector and then figures out whether or not the boot sector contains new geometry information. Arguments: DisketteExtension - Supplies the diskette extension. Return Value: None. --*/ { PBOOT_SECTOR_INFO bootSector; LARGE_INTEGER offset; PIRP irp; NTSTATUS status; // Set up the IRP to read the boot sector. bootSector = ExAllocatePool(NonPagedPoolCacheAligned, BOOT_SECTOR_SIZE); if (!bootSector) { return; } offset.LowPart = offset.HighPart = 0; irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ, DisketteExtension->DeviceObject, bootSector, BOOT_SECTOR_SIZE, &offset, NULL); if (!irp) { ExFreePool(bootSector); return; } irp->CurrentLocation--; irp->Tail.Overlay.CurrentStackLocation = IoGetNextIrpStackLocation(irp); // Allocate an adapter channel, do read, free adapter channel. status = FlReadWrite(DisketteExtension, irp, TRUE); MmUnlockPages(irp->MdlAddress); IoFreeMdl(irp->MdlAddress); IoFreeIrp(irp); ExFreePool(bootSector); } NTSTATUS FlReadWriteTrack( IN OUT PDISKETTE_EXTENSION DisketteExtension, IN OUT PMDL IoMdl, IN OUT ULONG IoOffset, IN BOOLEAN WriteOperation, IN UCHAR Cylinder, IN UCHAR Head, IN UCHAR Sector, IN UCHAR NumberOfSectors, IN BOOLEAN NeedSeek ) /*++ Routine Description: This routine reads a portion of a track. It transfers the to or from the device from or to the given IoBuffer and IoMdl. Arguments: DisketteExtension - Supplies the diskette extension. IoMdl - Supplies the Mdl for transfering from/to the device. IoBuffer - Supplies the buffer to transfer from/to the device. WriteOperation - Supplies whether or not this is a write operation. Cylinder - Supplies the cylinder number for this track. Head - Supplies the head number for this track. Sector - Supplies the starting sector of the transfer. NumberOfSectors - Supplies the number of sectors to transfer. NeedSeek - Supplies whether or not we need to do a seek. Return Value: An NTSTATUS code. --*/ { PDRIVE_MEDIA_CONSTANTS driveMediaConstants; ULONG byteToSectorShift; ULONG transferBytes; LARGE_INTEGER headSettleTime; NTSTATUS status; ULONG seekRetry, ioRetry; BOOLEAN recalibrateDrive = FALSE; UCHAR i; FloppyDump( FLOPSHOW, ("\nFlReadWriteTrack:%sseek for %s at chs %d/%d/%d for %d sectors\n", NeedSeek ? " need " : " ", WriteOperation ? "write" : "read", Cylinder, Head, Sector, NumberOfSectors) ); driveMediaConstants = &DisketteExtension->DriveMediaConstants; byteToSectorShift = SECTORLENGTHCODE_TO_BYTESHIFT + driveMediaConstants->SectorLengthCode; transferBytes = ((ULONG) NumberOfSectors)<HeadSettleTime); headSettleTime.HighPart = -1; for (seekRetry = 0, ioRetry = 0; seekRetry < 3; seekRetry++) { if (recalibrateDrive) { // Something failed, so recalibrate the drive. FloppyDump( FLOPINFO, ("FlReadWriteTrack: performing recalibrate\n") ); FlRecalibrateDrive(DisketteExtension); } // Do a seek if we have to. if (recalibrateDrive || (NeedSeek && (!DisketteExtension->ControllerConfigurable || driveMediaConstants->CylinderShift != 0))) { DisketteExtension->FifoBuffer[0] = COMMND_SEEK; DisketteExtension->FifoBuffer[1] = (Head<<2) | DisketteExtension->DeviceUnit; DisketteExtension->FifoBuffer[2] = Cylinder<< driveMediaConstants->CylinderShift; status = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if (NT_SUCCESS(status)) { // Check the completion state of the controller. if (!(DisketteExtension->FifoBuffer[0]&STREG0_SEEK_COMPLETE) || DisketteExtension->FifoBuffer[1] != Cylinder<CylinderShift) { DisketteExtension->HardwareFailed = TRUE; status = STATUS_FLOPPY_BAD_REGISTERS; } if (NT_SUCCESS(status)) { // Delay after doing seek. KeDelayExecutionThread(KernelMode, FALSE, &headSettleTime); // SEEKs should always be followed by a READID. DisketteExtension->FifoBuffer[0] = COMMND_READ_ID + COMMND_OPTION_MFM; DisketteExtension->FifoBuffer[1] = (Head<<2) | DisketteExtension->DeviceUnit; status = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if (NT_SUCCESS(status)) { if (DisketteExtension->FifoBuffer[0] != ((Head<<2) | DisketteExtension->DeviceUnit) || DisketteExtension->FifoBuffer[1] != 0 || DisketteExtension->FifoBuffer[2] != 0 || DisketteExtension->FifoBuffer[3] != Cylinder) { DisketteExtension->HardwareFailed = TRUE; status = FlInterpretError( DisketteExtension->FifoBuffer[1], DisketteExtension->FifoBuffer[2]); } } else { FloppyDump( FLOPINFO, ("FlReadWriteTrack: Read ID failed %x\n", status) ); } } } else { FloppyDump( FLOPINFO, ("FlReadWriteTrack: SEEK failed %x\n", status) ); } } else { status = STATUS_SUCCESS; } if (!NT_SUCCESS(status)) { // The seek failed so try again. FloppyDump( FLOPINFO, ("FlReadWriteTrack: setup failure %x - recalibrating\n", status) ); recalibrateDrive = TRUE; continue; } for (;; ioRetry++) { // // Issue the READ or WRITE command // DisketteExtension->FifoBuffer[1] = (Head<<2) | DisketteExtension->DeviceUnit; DisketteExtension->FifoBuffer[2] = Cylinder; DisketteExtension->FifoBuffer[3] = Head; DisketteExtension->FifoBuffer[4] = Sector + 1; DisketteExtension->FifoBuffer[5] = driveMediaConstants->SectorLengthCode; DisketteExtension->FifoBuffer[6] = Sector + NumberOfSectors; DisketteExtension->FifoBuffer[7] = driveMediaConstants->ReadWriteGapLength; DisketteExtension->FifoBuffer[8] = driveMediaConstants->DataLength; if (WriteOperation) { DisketteExtension->FifoBuffer[0] = COMMND_WRITE_DATA + COMMND_OPTION_MFM; } else { DisketteExtension->FifoBuffer[0] = COMMND_READ_DATA + COMMND_OPTION_MFM; } FloppyDump(FLOPINFO, ("FlReadWriteTrack: Params - %x,%x,%x,%x,%x,%x,%x,%x\n", DisketteExtension->FifoBuffer[1], DisketteExtension->FifoBuffer[2], DisketteExtension->FifoBuffer[3], DisketteExtension->FifoBuffer[4], DisketteExtension->FifoBuffer[5], DisketteExtension->FifoBuffer[6], DisketteExtension->FifoBuffer[7], DisketteExtension->FifoBuffer[8]) ); status = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, IoMdl, IoOffset, transferBytes ); if (NT_SUCCESS(status)) { if ((DisketteExtension->FifoBuffer[0] & STREG0_END_MASK) != STREG0_END_NORMAL) { DisketteExtension->HardwareFailed = TRUE; status = FlInterpretError(DisketteExtension->FifoBuffer[1], DisketteExtension->FifoBuffer[2]); } else { // // The floppy controller may return no errors but not have // read all of the requested data. If this is the case, // record it as an error and retru the operation. // if (DisketteExtension->FifoBuffer[5] != 1) { DisketteExtension->HardwareFailed = TRUE; status = STATUS_FLOPPY_UNKNOWN_ERROR; } } } else { FloppyDump( FLOPINFO, ("FlReadWriteTrack: %s command failed %x\n", WriteOperation ? "write" : "read", status) ); } if (NT_SUCCESS(status)) { break; } if (ioRetry >= 2) { FloppyDump(FLOPINFO, ("FlReadWriteTrack: too many retries - failing\n")); break; } } if (NT_SUCCESS(status)) { break; } // We failed quite a bit so make seeks mandatory. recalibrateDrive = TRUE; } if (!NT_SUCCESS(status) && NumberOfSectors > 1) { // Retry one sector at a time. FloppyDump( FLOPINFO, ("FlReadWriteTrack: Attempting sector at a time\n") ); for (i = 0; i < NumberOfSectors; i++) { status = FlReadWriteTrack( DisketteExtension, IoMdl, IoOffset+(((ULONG)i)<HardwareFailed = TRUE; break; } } } return status; } NTSTATUS FlReadWrite( IN OUT PDISKETTE_EXTENSION DisketteExtension, IN OUT PIRP Irp, IN BOOLEAN DriveStarted ) /*++ Routine Description: This routine is called by the floppy thread to read/write data to/from the diskette. It breaks the request into pieces called "transfers" (their size depends on the buffer size, where the end of the track is, etc) and retries each transfer until it succeeds or the retry count is exceeded. Arguments: DisketteExtension - a pointer to our data area for the drive to be accessed. Irp - a pointer to the IO Request Packet. DriveStarted - indicated whether or not the drive has been started. Return Value: STATUS_SUCCESS if the packet was successfully read or written; the appropriate error is propogated otherwise. --*/ { PIO_STACK_LOCATION irpSp; BOOLEAN writeOperation; NTSTATUS status; PDRIVE_MEDIA_CONSTANTS driveMediaConstants; ULONG byteToSectorShift; ULONG currentSector, firstSector, lastSector; ULONG trackSize; UCHAR sectorsPerTrack, numberOfHeads; UCHAR currentHead, currentCylinder, trackSector; PCHAR userBuffer; UCHAR skew, skewDelta; UCHAR numTransferSectors; PMDL mdl; PCHAR ioBuffer; ULONG ioOffset; irpSp = IoGetCurrentIrpStackLocation(Irp); FloppyDump( FLOPSHOW, ("FlReadWrite: for %s at offset %x size %x ", irpSp->MajorFunction == IRP_MJ_WRITE ? "write" : "read", irpSp->Parameters.Read.ByteOffset.LowPart, irpSp->Parameters.Read.Length) ); // Check for valid operation on this device. if (irpSp->MajorFunction == IRP_MJ_WRITE) { if (DisketteExtension->IsReadOnly) { FloppyDump( FLOPSHOW, ("is read-only\n") ); return STATUS_INVALID_PARAMETER; } writeOperation = TRUE; } else { writeOperation = FALSE; } FloppyDump( FLOPSHOW, ("\n") ); // Start up the drive. if (DriveStarted) { status = STATUS_SUCCESS; } else { status = FlStartDrive( DisketteExtension, Irp, writeOperation, TRUE, (BOOLEAN) !!(irpSp->Flags&SL_OVERRIDE_VERIFY_VOLUME)); } if (!NT_SUCCESS(status)) { FloppyDump( FLOPSHOW, ("FlReadWrite: error on start %x\n", status) ); return status; } if (DisketteExtension->MediaType == Unknown) { FloppyDump( FLOPSHOW, ("not recognized\n") ); return STATUS_UNRECOGNIZED_MEDIA; } // The drive has started up with a recognized media. // Gather some relavant parameters. driveMediaConstants = &DisketteExtension->DriveMediaConstants; byteToSectorShift = SECTORLENGTHCODE_TO_BYTESHIFT + driveMediaConstants->SectorLengthCode; firstSector = irpSp->Parameters.Read.ByteOffset.LowPart>> byteToSectorShift; lastSector = firstSector + (irpSp->Parameters.Read.Length>> byteToSectorShift); sectorsPerTrack = driveMediaConstants->SectorsPerTrack; numberOfHeads = driveMediaConstants->NumberOfHeads; userBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority); if (userBuffer == NULL) { FloppyDump(FLOPWARN, ("MmGetSystemAddressForMdlSafe returned NULL in FlReadWrite\n") ); return STATUS_INSUFFICIENT_RESOURCES; } trackSize = ((ULONG) sectorsPerTrack)<SkewDelta; for (currentSector = firstSector; currentSector < lastSector; currentSector += numTransferSectors) { // Compute cylinder, head and sector from absolute sector. currentCylinder = (UCHAR) (currentSector/sectorsPerTrack/numberOfHeads); trackSector = (UCHAR) (currentSector%sectorsPerTrack); currentHead = (UCHAR) (currentSector/sectorsPerTrack%numberOfHeads); numTransferSectors = sectorsPerTrack - trackSector; if (lastSector - currentSector < numTransferSectors) { numTransferSectors = (UCHAR) (lastSector - currentSector); } // // If we're using a temporary IO buffer because of // insufficient registers in the DMA and we're // doing a write then copy the write buffer to // the contiguous buffer. // if (trackSize > DisketteExtension->MaxTransferSize) { FloppyDump(FLOPSHOW, ("FlReadWrite allocating an IoBuffer\n") ); FlAllocateIoBuffer(DisketteExtension, trackSize); if (!DisketteExtension->IoBuffer) { FloppyDump( FLOPSHOW, ("FlReadWrite: no resources\n") ); return STATUS_INSUFFICIENT_RESOURCES; } mdl = DisketteExtension->IoBufferMdl; ioBuffer = DisketteExtension->IoBuffer; ioOffset = 0; if (writeOperation) { RtlMoveMemory(ioBuffer, userBuffer + ((currentSector - firstSector)<< byteToSectorShift), ((ULONG) numTransferSectors)<MdlAddress; ioOffset = (currentSector - firstSector) << byteToSectorShift; } // // Transfer the track. // Do what we can to avoid missing revs. // // Alter the skew to be in the range of what // we're transfering. if (skew >= numTransferSectors + trackSector) { skew = 0; } if (skew < trackSector) { skew = trackSector; } // Go from skew to the end of the irp. status = FlReadWriteTrack( DisketteExtension, mdl, ioOffset + (((ULONG) skew - trackSector)< trackSector) { status = FlReadWriteTrack( DisketteExtension, mdl, ioOffset, writeOperation, currentCylinder, currentHead, trackSector, (UCHAR) (skew - trackSector), FALSE); } else { skew = (numTransferSectors + trackSector)%sectorsPerTrack; } if (!NT_SUCCESS(status)) { break; } // // If we used the temporary IO buffer to do the // read then copy the contents back to the IRPs buffer. // if (!writeOperation && trackSize > DisketteExtension->MaxTransferSize) { RtlMoveMemory( userBuffer + ((currentSector - firstSector) << byteToSectorShift), ioBuffer, ((ULONG) numTransferSectors)<IoStatus.Information = (currentSector - firstSector) << byteToSectorShift; // If the read was successful then consolidate the // boot sector with the determined density. if (NT_SUCCESS(status) && firstSector == 0) { FlConsolidateMediaTypeWithBootSector(DisketteExtension, (PBOOT_SECTOR_INFO) userBuffer); } FloppyDump( FLOPSHOW, ("FlReadWrite: completed status %x information %d\n", status, Irp->IoStatus.Information) ); return status; } NTSTATUS FlFormat( IN PDISKETTE_EXTENSION DisketteExtension, IN PIRP Irp ) /*++ Routine Description: This routine is called by the floppy thread to format some tracks on the diskette. This won't take TOO long because the FORMAT utility is written to only format a few tracks at a time so that it can keep a display of what percentage of the disk has been formatted. Arguments: DisketteExtension - pointer to our data area for the diskette to be formatted. Irp - pointer to the IO Request Packet. Return Value: STATUS_SUCCESS if the tracks were formatted; appropriate error propogated otherwise. --*/ { LARGE_INTEGER headSettleTime; PIO_STACK_LOCATION irpSp; PBAD_TRACK_NUMBER badTrackBuffer; PFORMAT_PARAMETERS formatParameters; PFORMAT_EX_PARAMETERS formatExParameters; PDRIVE_MEDIA_CONSTANTS driveMediaConstants; NTSTATUS ntStatus; ULONG badTrackBufferLength; DRIVE_MEDIA_TYPE driveMediaType; UCHAR driveStatus; UCHAR numberOfBadTracks = 0; UCHAR currentTrack; UCHAR endTrack; UCHAR whichSector; UCHAR retryCount; BOOLEAN bufferOverflow = FALSE; FDC_DISK_CHANGE_PARMS fdcDiskChangeParms; FloppyDump( FLOPSHOW, ("Floppy: FlFormat...\n") ); irpSp = IoGetCurrentIrpStackLocation( Irp ); formatParameters = (PFORMAT_PARAMETERS) Irp->AssociatedIrp.SystemBuffer; if (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_FORMAT_TRACKS_EX) { formatExParameters = (PFORMAT_EX_PARAMETERS) Irp->AssociatedIrp.SystemBuffer; } else { formatExParameters = NULL; } FloppyDump( FLOPFORMAT, ("Floppy: Format Params - MediaType: %d\n" "------ Start Cyl: %x\n" "------ End Cyl: %x\n" "------ Start Hd: %d\n" "------ End Hd: %d\n", formatParameters->MediaType, formatParameters->StartCylinderNumber, formatParameters->EndCylinderNumber, formatParameters->StartHeadNumber, formatParameters->EndHeadNumber) ); badTrackBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; // // Figure out which entry in the DriveMediaConstants table to use. // We know we'll find one, or FlCheckFormatParameters() would have // rejected the request. // driveMediaType = DriveMediaLimits[DisketteExtension->DriveType].HighestDriveMediaType; while ( ( DriveMediaConstants[driveMediaType].MediaType != formatParameters->MediaType ) && ( driveMediaType > DriveMediaLimits[DisketteExtension->DriveType]. LowestDriveMediaType ) ) { driveMediaType--; } driveMediaConstants = &DriveMediaConstants[driveMediaType]; // // Set some values in the diskette extension to indicate what we // know about the media type. // DisketteExtension->MediaType = formatParameters->MediaType; DisketteExtension->DriveMediaType = driveMediaType; DisketteExtension->DriveMediaConstants = DriveMediaConstants[driveMediaType]; if (formatExParameters) { DisketteExtension->DriveMediaConstants.SectorsPerTrack = (UCHAR) formatExParameters->SectorsPerTrack; DisketteExtension->DriveMediaConstants.FormatGapLength = (UCHAR) formatExParameters->FormatGapLength; } driveMediaConstants = &(DisketteExtension->DriveMediaConstants); DisketteExtension->BytesPerSector = driveMediaConstants->BytesPerSector; DisketteExtension->ByteCapacity = ( driveMediaConstants->BytesPerSector ) * driveMediaConstants->SectorsPerTrack * ( 1 + driveMediaConstants->MaximumTrack ) * driveMediaConstants->NumberOfHeads; currentTrack = (UCHAR)( ( formatParameters->StartCylinderNumber * driveMediaConstants->NumberOfHeads ) + formatParameters->StartHeadNumber ); endTrack = (UCHAR)( ( formatParameters->EndCylinderNumber * driveMediaConstants->NumberOfHeads ) + formatParameters->EndHeadNumber ); FloppyDump( FLOPFORMAT, ("Floppy: Format - Starting/ending tracks: %x/%x\n", currentTrack, endTrack) ); // // Set the data rate (which depends on the drive/media // type). // if ( DisketteExtension->LastDriveMediaType != driveMediaType ) { ntStatus = FlDatarateSpecifyConfigure( DisketteExtension ); if ( !NT_SUCCESS( ntStatus ) ) { return ntStatus; } } // // Since we're doing a format, make this drive writable. // DisketteExtension->IsReadOnly = FALSE; // // Format each track. // do { // // Seek to proper cylinder // DisketteExtension->FifoBuffer[0] = COMMND_SEEK; DisketteExtension->FifoBuffer[1] = DisketteExtension->DeviceUnit; DisketteExtension->FifoBuffer[2] = (UCHAR)( ( currentTrack / driveMediaConstants->NumberOfHeads ) << driveMediaConstants->CylinderShift ); FloppyDump( FLOPFORMAT, ("Floppy: Format seek to cylinder: %x\n", DisketteExtension->FifoBuffer[1]) ); ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); if ( NT_SUCCESS( ntStatus ) ) { if ( ( DisketteExtension->FifoBuffer[0] & STREG0_SEEK_COMPLETE ) && ( DisketteExtension->FifoBuffer[1] == (UCHAR)( ( currentTrack / driveMediaConstants->NumberOfHeads ) << driveMediaConstants->CylinderShift ) ) ) { // // Must delay HeadSettleTime milliseconds before // doing anything after a SEEK. // headSettleTime.LowPart = - ( 10 * 1000 * driveMediaConstants->HeadSettleTime ); headSettleTime.HighPart = -1; KeDelayExecutionThread( KernelMode, FALSE, &headSettleTime ); // // Read ID. Note that we don't bother checking the return // registers, because if this media wasn't formatted we'd // get an error. // DisketteExtension->FifoBuffer[0] = COMMND_READ_ID + COMMND_OPTION_MFM; DisketteExtension->FifoBuffer[1] = DisketteExtension->DeviceUnit; ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); } else { FloppyDump( FLOPWARN, ("Floppy: format's seek returned bad registers\n" "------ Statusreg0 = %x\n" "------ Statusreg1 = %x\n", DisketteExtension->FifoBuffer[0], DisketteExtension->FifoBuffer[1]) ); DisketteExtension->HardwareFailed = TRUE; ntStatus = STATUS_FLOPPY_BAD_REGISTERS; } } if ( !NT_SUCCESS( ntStatus ) ) { FloppyDump( FLOPWARN, ("Floppy: format's seek/readid returned %x\n", ntStatus) ); return ntStatus; } // // Fill the buffer with the format of this track. // for (whichSector = 0; whichSector < driveMediaConstants->SectorsPerTrack; whichSector++) { DisketteExtension->IoBuffer[whichSector*4] = currentTrack/driveMediaConstants->NumberOfHeads; DisketteExtension->IoBuffer[whichSector*4 + 1] = currentTrack%driveMediaConstants->NumberOfHeads; if (formatExParameters) { DisketteExtension->IoBuffer[whichSector*4 + 2] = (UCHAR) formatExParameters->SectorNumber[whichSector]; } else { DisketteExtension->IoBuffer[whichSector*4 + 2] = whichSector + 1; } DisketteExtension->IoBuffer[whichSector*4 + 3] = driveMediaConstants->SectorLengthCode; FloppyDump( FLOPFORMAT, ("Floppy - Format table entry %x - %x/%x/%x/%x\n", whichSector, DisketteExtension->IoBuffer[whichSector*4], DisketteExtension->IoBuffer[whichSector*4 + 1], DisketteExtension->IoBuffer[whichSector*4 + 2], DisketteExtension->IoBuffer[whichSector*4 + 3]) ); } // // Retry until success or too many retries. // retryCount = 0; do { ULONG length; length = driveMediaConstants->BytesPerSector; // // Issue command to format track // DisketteExtension->FifoBuffer[0] = COMMND_FORMAT_TRACK + COMMND_OPTION_MFM; DisketteExtension->FifoBuffer[1] = (UCHAR) ( ( ( currentTrack % driveMediaConstants->NumberOfHeads ) << 2 ) | DisketteExtension->DeviceUnit ); DisketteExtension->FifoBuffer[2] = driveMediaConstants->SectorLengthCode; DisketteExtension->FifoBuffer[3] = driveMediaConstants->SectorsPerTrack; DisketteExtension->FifoBuffer[4] = driveMediaConstants->FormatGapLength; DisketteExtension->FifoBuffer[5] = driveMediaConstants->FormatFillCharacter; FloppyDump( FLOPFORMAT, ("Floppy: format command parameters\n" "------ Head/Unit: %x\n" "------ Bytes/Sector: %x\n" "------ Sectors/Cylinder: %x\n" "------ Gap 3: %x\n" "------ Filler Byte: %x\n", DisketteExtension->FifoBuffer[1], DisketteExtension->FifoBuffer[2], DisketteExtension->FifoBuffer[3], DisketteExtension->FifoBuffer[4], DisketteExtension->FifoBuffer[5]) ); ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, DisketteExtension->IoBufferMdl, 0, length ); if ( !NT_SUCCESS( ntStatus ) ) { FloppyDump( FLOPDBGP, ("Floppy: format returned %x\n", ntStatus) ); } if ( NT_SUCCESS( ntStatus ) ) { // // Check the return bytes from the controller. // if ( ( DisketteExtension->FifoBuffer[0] & ( STREG0_DRIVE_FAULT | STREG0_END_INVALID_COMMAND | STREG0_END_ERROR ) ) || ( DisketteExtension->FifoBuffer[1] & STREG1_DATA_OVERRUN ) || ( DisketteExtension->FifoBuffer[2] != 0 ) ) { FloppyDump( FLOPWARN, ("Floppy: format had bad registers\n" "------ Streg0 = %x\n" "------ Streg1 = %x\n" "------ Streg2 = %x\n", DisketteExtension->FifoBuffer[0], DisketteExtension->FifoBuffer[1], DisketteExtension->FifoBuffer[2]) ); DisketteExtension->HardwareFailed = TRUE; ntStatus = FlInterpretError( DisketteExtension->FifoBuffer[1], DisketteExtension->FifoBuffer[2] ); } } } while ( ( !NT_SUCCESS( ntStatus ) ) && ( retryCount++ < RECALIBRATE_RETRY_COUNT ) ); if ( !NT_SUCCESS( ntStatus ) ) { ntStatus = FlFdcDeviceIo( DisketteExtension->TargetObject, IOCTL_DISK_INTERNAL_GET_FDC_DISK_CHANGE, &fdcDiskChangeParms ); driveStatus = fdcDiskChangeParms.DriveStatus; if ( (DisketteExtension->DriveType != DRIVE_TYPE_0360) && driveStatus & DSKCHG_DISKETTE_REMOVED ) { // // The user apparently popped the floppy. Return error // rather than logging bad track. // return STATUS_NO_MEDIA_IN_DEVICE; } // // Log the bad track. // FloppyDump( FLOPDBGP, ("Floppy: track %x is bad\n", currentTrack) ); if (badTrackBufferLength >= (ULONG) ((numberOfBadTracks + 1) * sizeof(BAD_TRACK_NUMBER))) { badTrackBuffer = (PBAD_TRACK_NUMBER) Irp->AssociatedIrp.SystemBuffer; badTrackBuffer[numberOfBadTracks] = ( BAD_TRACK_NUMBER ) currentTrack; Irp->IoStatus.Information += sizeof(BAD_TRACK_NUMBER); } else { bufferOverflow = TRUE; } numberOfBadTracks++; } currentTrack++; } while ( currentTrack <= endTrack ); if ( ( NT_SUCCESS( ntStatus ) ) && ( bufferOverflow ) ) { ntStatus = STATUS_BUFFER_OVERFLOW; } return ntStatus; } BOOLEAN FlCheckFormatParameters( IN PDISKETTE_EXTENSION DisketteExtension, IN PFORMAT_PARAMETERS FormatParameters ) /*++ Routine Description: This routine checks the supplied format parameters to make sure that they'll work on the drive to be formatted. Arguments: DisketteExtension - a pointer to our data area for the diskette to be formatted. FormatParameters - a pointer to the caller's parameters for the FORMAT. Return Value: TRUE if parameters are OK. FALSE if the parameters are bad. --*/ { PDRIVE_MEDIA_CONSTANTS driveMediaConstants; DRIVE_MEDIA_TYPE driveMediaType; // // Figure out which entry in the DriveMediaConstants table to use. // driveMediaType = DriveMediaLimits[DisketteExtension->DriveType].HighestDriveMediaType; while ((DriveMediaConstants[driveMediaType].MediaType != FormatParameters->MediaType ) && (driveMediaType > DriveMediaLimits[DisketteExtension->DriveType]. LowestDriveMediaType)) { driveMediaType--; } if ( DriveMediaConstants[driveMediaType].MediaType != FormatParameters->MediaType ) { return FALSE; } else { driveMediaConstants = &DriveMediaConstants[driveMediaType]; if ( ( FormatParameters->StartHeadNumber > (ULONG)( driveMediaConstants->NumberOfHeads - 1 ) ) || ( FormatParameters->EndHeadNumber > (ULONG)( driveMediaConstants->NumberOfHeads - 1 ) ) || ( FormatParameters->StartCylinderNumber > driveMediaConstants->MaximumTrack ) || ( FormatParameters->EndCylinderNumber > driveMediaConstants->MaximumTrack ) || ( FormatParameters->EndCylinderNumber < FormatParameters->StartCylinderNumber ) ) { return FALSE; } } return TRUE; } NTSTATUS FlIssueCommand( IN OUT PDISKETTE_EXTENSION DisketteExtension, IN PUCHAR FifoInBuffer, OUT PUCHAR FifoOutBuffer, IN PMDL IoMdl, IN OUT ULONG IoOffset, IN ULONG TransferBytes ) /*++ Routine Description: This routine sends the command and all parameters to the controller, waits for the command to interrupt if necessary, and reads the result bytes from the controller, if any. Before calling this routine, the caller should put the parameters for the command in ControllerData->FifoBuffer[]. The result bytes will be returned in the same place. This routine runs off the CommandTable. For each command, this says how many parameters there are, whether or not there is an interrupt to wait for, and how many result bytes there are. Note that commands without result bytes actually have two, since the ISR will issue a SENSE INTERRUPT STATUS command on their behalf. Arguments: Command - a byte specifying the command to be sent to the controller. FloppyExtension - a pointer to our data area for the drive being accessed (any drive if a controller command is being given). Return Value: STATUS_SUCCESS if the command was sent and bytes received properly; appropriate error propogated otherwise. --*/ { NTSTATUS ntStatus; UCHAR i; PIRP irp; KEVENT DoneEvent; IO_STATUS_BLOCK IoStatus; PIO_STACK_LOCATION irpSp; ISSUE_FDC_COMMAND_PARMS issueCommandParms; // // Set the command parameters // issueCommandParms.FifoInBuffer = FifoInBuffer; issueCommandParms.FifoOutBuffer = FifoOutBuffer; issueCommandParms.IoHandle = (PVOID)IoMdl; issueCommandParms.IoOffset = IoOffset; issueCommandParms.TransferBytes = TransferBytes; issueCommandParms.TimeOut = FDC_TIMEOUT; FloppyDump( FLOPSHOW, ("Floppy: FloppyIssueCommand %2x...\n", DisketteExtension->FifoBuffer[0]) ); ntStatus = FlFdcDeviceIo( DisketteExtension->TargetObject, IOCTL_DISK_INTERNAL_ISSUE_FDC_COMMAND, &issueCommandParms ); // // If it appears like the floppy controller is not responding // set the HardwareFailed flag which will force a reset. // if ( ntStatus == STATUS_DEVICE_NOT_READY || ntStatus == STATUS_FLOPPY_BAD_REGISTERS ) { DisketteExtension->HardwareFailed = TRUE; } return ntStatus; } NTSTATUS FlInitializeControllerHardware( IN OUT PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: This routine is called to reset and initialize the floppy controller device. Arguments: disketteExtension - Supplies the diskette extension. Return Value: --*/ { NTSTATUS ntStatus; ntStatus = FlFdcDeviceIo( DisketteExtension->TargetObject, IOCTL_DISK_INTERNAL_RESET_FDC, NULL ); if (NT_SUCCESS(ntStatus)) { if ( DisketteExtension->PerpendicularMode != 0 ) { DisketteExtension->FifoBuffer[0] = COMMND_PERPENDICULAR_MODE; DisketteExtension->FifoBuffer[1] = (UCHAR) (COMMND_PERPENDICULAR_MODE_OW | (DisketteExtension->PerpendicularMode << 2)); ntStatus = FlIssueCommand( DisketteExtension, DisketteExtension->FifoBuffer, DisketteExtension->FifoBuffer, NULL, 0, 0 ); } } return ntStatus; } NTSTATUS FlFdcDeviceIo( IN PDEVICE_OBJECT DeviceObject, IN ULONG Ioctl, IN OUT PVOID Data ) { NTSTATUS ntStatus; PIRP irp; PIO_STACK_LOCATION irpStack; KEVENT doneEvent; IO_STATUS_BLOCK ioStatus; FloppyDump(FLOPINFO,("Calling Fdc Device with %x\n", Ioctl)); KeInitializeEvent( &doneEvent, NotificationEvent, FALSE); // // Create an IRP for enabler // irp = IoBuildDeviceIoControlRequest( Ioctl, DeviceObject, NULL, 0, NULL, 0, TRUE, &doneEvent, &ioStatus ); if (irp == NULL) { FloppyDump(FLOPDBGP,("FlFloppyDeviceIo: Can't allocate Irp\n")); // // If an Irp can't be allocated, then this call will // simply return. This will leave the queue frozen for // this device, which means it can no longer be accessed. // return STATUS_INSUFFICIENT_RESOURCES; } irpStack = IoGetNextIrpStackLocation(irp); irpStack->Parameters.DeviceIoControl.Type3InputBuffer = Data; // // Call the driver and request the operation // ntStatus = IoCallDriver(DeviceObject, irp); if ( ntStatus == STATUS_PENDING ) { // // Now wait for operation to complete (should already be done, but // maybe not) // KeWaitForSingleObject( &doneEvent, Executive, KernelMode, FALSE, NULL ); ntStatus = ioStatus.Status; } return ntStatus; } NTSTATUS FloppyQueueRequest ( IN OUT PDISKETTE_EXTENSION DisketteExtension, IN PIRP Irp ) /*++ Routine Description: Queues the Irp in the device queue. This routine will be called whenever the device receives IRP_MN_QUERY_STOP_DEVICE or IRP_MN_QUERY_REMOVE_DEVICE Arguments: FdoData - pointer to the device's extension. Irp - the request to be queued. Return Value: NT status code. --*/ { KIRQL oldIrql; NTSTATUS ntStatus; // // Reset driver paging // FloppyResetDriverPaging(); // // Check if we are allowed to queue requests. // ASSERT( DisketteExtension->HoldNewRequests ); // // Preparing for dealing with cancelling stuff. // We don't know how long the irp will be in the // queue. So we need to handle cancel. // Since we use our own queue, we don't need to use // the cancel spin lock. // KeAcquireSpinLock(&DisketteExtension->FlCancelSpinLock, &oldIrql); IoSetCancelRoutine(Irp, FloppyCancelQueuedRequest); // // Check if the irp was already canceled // if ((Irp->Cancel) && (IoSetCancelRoutine(Irp, NULL))) { // // Already canceled // Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; KeReleaseSpinLock(&DisketteExtension->FlCancelSpinLock, oldIrql); IoCompleteRequest( Irp, IO_NO_INCREMENT ); FloppyPageEntireDriver(); ntStatus = STATUS_CANCELLED; } else { // // Queue the Irp and set a cancel routine // Irp->IoStatus.Status = STATUS_PENDING; IoMarkIrpPending(Irp); ExInterlockedInsertTailList( &DisketteExtension->NewRequestQueue, &Irp->Tail.Overlay.ListEntry, &DisketteExtension->NewRequestQueueSpinLock); KeReleaseSpinLock(&DisketteExtension->FlCancelSpinLock, oldIrql); ntStatus = STATUS_PENDING; } return ntStatus; } VOID FloppyCancelQueuedRequest ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: The cancel routine. Will remove the IRP from the queue and will complete it. The cancel spin lock is already acquired when this routine is called. Arguments: DeviceObject - pointer to the device object. Irp - pointer to the IRP to be cancelled. Return Value: VOID. --*/ { PDISKETTE_EXTENSION disketteExtension = DeviceObject->DeviceExtension; KIRQL oldIrql; FloppyDump(FLOPDBGP, ("Floppy Cancel called.\n")); KeAcquireSpinLock(&disketteExtension->FlCancelSpinLock, &oldIrql); Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; // // Make sure the IRP wasn't removed in Process routine. // if (Irp->Tail.Overlay.ListEntry.Flink) { RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); } KeReleaseSpinLock(&disketteExtension->FlCancelSpinLock, oldIrql); IoReleaseCancelSpinLock(Irp->CancelIrql); IoCompleteRequest(Irp, IO_NO_INCREMENT); FloppyPageEntireDriver(); return; } VOID FloppyProcessQueuedRequests ( IN OUT PDISKETTE_EXTENSION DisketteExtension ) /*++ Routine Description: Removes an dprocesses the entries in the queue. If this routine is called when processing IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE or IRP_MN_START_DEVICE, the requests are passed to the next lower driver. If the routine is called when IRP_MN_REMOVE_DEVICE is received, the IRPs are completed with STATUS_DELETE_PENDING. Arguments: Return Value: VOID. --*/ { KIRQL oldIrql; PLIST_ENTRY headOfList; PIRP currentIrp; PIO_STACK_LOCATION irpSp; // // We need to dequeue all the entries in the queue, to reset the cancel // routine for each of them and then to process then: // - if the device is active, we will send them down // - else we will complete them with STATUS_DELETE_PENDING // (it is a surprise removal and we need to dispose the queue) // KeAcquireSpinLock(&DisketteExtension->FlCancelSpinLock, &oldIrql); while ((headOfList = ExInterlockedRemoveHeadList( &DisketteExtension->NewRequestQueue, &DisketteExtension->NewRequestQueueSpinLock)) != NULL) { currentIrp = CONTAINING_RECORD( headOfList, IRP, Tail.Overlay.ListEntry); if (IoSetCancelRoutine( currentIrp, NULL)) { irpSp = IoGetCurrentIrpStackLocation( currentIrp ); } else { // // Cancel routine is already running for this IRP. // Set the IRP field so that it won't be removed // in the cancel routine again. // currentIrp->Tail.Overlay.ListEntry.Flink = NULL; currentIrp = NULL; } KeReleaseSpinLock(&DisketteExtension->FlCancelSpinLock, oldIrql); if (currentIrp) { if ( DisketteExtension->IsRemoved ) { // // The device was removed, we need to fail the request // currentIrp->IoStatus.Information = 0; currentIrp->IoStatus.Status = STATUS_DELETE_PENDING; IoCompleteRequest (currentIrp, IO_NO_INCREMENT); } else { switch ( irpSp->MajorFunction ) { case IRP_MJ_READ: case IRP_MJ_WRITE: (VOID)FloppyReadWrite( DisketteExtension->DeviceObject, currentIrp ); break; case IRP_MJ_DEVICE_CONTROL: (VOID)FloppyDeviceControl( DisketteExtension->DeviceObject, currentIrp); break; default: currentIrp->IoStatus.Information = 0; currentIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest (currentIrp, IO_NO_INCREMENT); } } } if (currentIrp) { // // Page out the driver if it's no more needed. // FloppyPageEntireDriver(); } KeAcquireSpinLock(&DisketteExtension->FlCancelSpinLock, &oldIrql); } KeReleaseSpinLock(&DisketteExtension->FlCancelSpinLock, oldIrql); return; }