You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5870 lines
188 KiB
5870 lines
188 KiB
/*++
|
|
|
|
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 <flo_data.h> // 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)<<byteToSectorShift;
|
|
|
|
headSettleTime.LowPart = -(10*1000*driveMediaConstants->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<<driveMediaConstants->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)<<byteToSectorShift),
|
|
WriteOperation,
|
|
Cylinder,
|
|
Head,
|
|
(UCHAR) (Sector + i),
|
|
1,
|
|
FALSE );
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
FloppyDump( FLOPINFO,
|
|
("FlReadWriteTrack: failed sector %d status %x\n",
|
|
i,
|
|
status) );
|
|
|
|
DisketteExtension->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)<<byteToSectorShift;
|
|
|
|
skew = 0;
|
|
skewDelta = driveMediaConstants->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)<<byteToSectorShift);
|
|
}
|
|
} else {
|
|
mdl = Irp->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)<<byteToSectorShift),
|
|
writeOperation,
|
|
currentCylinder,
|
|
currentHead,
|
|
skew,
|
|
(UCHAR) (numTransferSectors + trackSector - skew),
|
|
TRUE);
|
|
|
|
// Go from start of irp to skew.
|
|
|
|
if (NT_SUCCESS(status) && skew > 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)<<byteToSectorShift);
|
|
}
|
|
|
|
//
|
|
// Increment the skew. Do this even if just switching sides
|
|
// for National Super I/O chips.
|
|
//
|
|
|
|
skew = (skew + skewDelta)%sectorsPerTrack;
|
|
}
|
|
|
|
Irp->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;
|
|
}
|
|
|