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.
7783 lines
240 KiB
7783 lines
240 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1991 - 1999
|
|
Copyright (c) 1996 Colorado Software Architects
|
|
|
|
Module Name:
|
|
|
|
fdc.c
|
|
|
|
Abstract:
|
|
|
|
This is the NEC PD756 (aka AT, aka IS1A, aka ix86) and Intel 82077
|
|
(aka MIPS) floppy diskette driver for NT.
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
--*/
|
|
|
|
//
|
|
// Include files.
|
|
//
|
|
|
|
#include "stdio.h"
|
|
#include "ntddk.h"
|
|
#include "ntdddisk.h" // disk device driver I/O control codes
|
|
#include "ntddfdc.h" // fdc I/O control codes and parameters
|
|
#include <fdc_data.h> // this driver's data declarations
|
|
#include <flpyenbl.h>
|
|
|
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
#define MIN(a,b) ((a) > (b) ? (b) : (a))
|
|
|
|
COMMAND_TABLE CommandTable[] = {
|
|
{ 0x06, 8, 1, 7, TRUE, TRUE, FDC_READ_DATA }, // Read Data
|
|
{ 0x0C, 0, 0, 0, FALSE, FALSE, FDC_NO_DATA }, // Not Implemented (MIPS)
|
|
{ 0x05, 8, 1, 7, TRUE, TRUE, FDC_WRITE_DATA }, // Write Data
|
|
{ 0x09, 0, 0, 0, FALSE, FALSE, FDC_NO_DATA }, // Not Implemented
|
|
{ 0x02, 8, 1, 7, TRUE, TRUE, FDC_READ_DATA }, // Read Track
|
|
{ 0x16, 8, 1, 7, TRUE, FALSE, FDC_NO_DATA }, // Verify
|
|
{ 0x10, 0, 0, 1, FALSE, FALSE, FDC_NO_DATA }, // Version
|
|
{ 0x0D, 5, 1, 7, TRUE, TRUE, FDC_WRITE_DATA }, // Format Track
|
|
{ 0x11, 8, 1, 7, TRUE, FALSE, FDC_READ_DATA }, // Scan Equal
|
|
{ 0x19, 8, 1, 7, TRUE, FALSE, FDC_READ_DATA }, // Scan Low Or Equal
|
|
{ 0x1D, 8, 1, 7, TRUE, FALSE, FDC_READ_DATA }, // Scan High Or Equal
|
|
{ 0x07, 1, 0, 2, TRUE, TRUE, FDC_NO_DATA }, // Recalibrate
|
|
{ 0x08, 0, 0, 2, FALSE, TRUE, FDC_NO_DATA }, // Sense Interrupt Status
|
|
{ 0x03, 2, 0, 0, FALSE, TRUE, FDC_NO_DATA }, // Specify
|
|
{ 0x04, 1, 0, 1, FALSE, TRUE, FDC_NO_DATA }, // Sense Drive Status
|
|
{ 0x8E, 6, 0, 0, FALSE, FALSE, FDC_NO_DATA }, // Drive Specification Command
|
|
{ 0x0F, 2, 0, 2, TRUE, TRUE, FDC_NO_DATA }, // Seek
|
|
{ 0x13, 3, 0, 0, FALSE, FALSE, FDC_NO_DATA }, // Configure
|
|
{ 0x8F, 2, 0, 2, TRUE, FALSE, FDC_NO_DATA }, // Relative Seek
|
|
{ 0x0E, 0, 0, 10, FALSE, FALSE, FDC_NO_DATA }, // Dumpreg
|
|
{ 0x0A, 1, 1, 7, TRUE, TRUE, FDC_NO_DATA }, // Read ID
|
|
{ 0x12, 1, 0, 0, FALSE, FALSE, FDC_NO_DATA }, // Perpendicular Mode
|
|
{ 0x14, 0, 0, 1, FALSE, FALSE, FDC_NO_DATA }, // Lock
|
|
{ 0x18, 0, 0, 1, FALSE, FALSE, FDC_NO_DATA }, // Part ID
|
|
{ 0x17, 1, 0, 1, FALSE, FALSE, FDC_NO_DATA }, // Powerdown Mode
|
|
{ 0x33, 1, 0, 0, FALSE, FALSE, FDC_NO_DATA }, // Option
|
|
{ 0x2E, 0, 0, 16, FALSE, FALSE, FDC_NO_DATA }, // Save
|
|
{ 0x4E, 16, 0, 0, FALSE, FALSE, FDC_NO_DATA }, // Restore
|
|
{ 0xAD, 5, 1, 7, TRUE, TRUE, FDC_WRITE_DATA } // Format And Write
|
|
};
|
|
|
|
//
|
|
// This is the actual definition of FdcDebugLevel.
|
|
// Note that it is only defined if this is a "debug"
|
|
// build.
|
|
//
|
|
#if DBG
|
|
extern ULONG FdcDebugLevel = 0;
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,DriverEntry)
|
|
#endif
|
|
|
|
#ifdef POOL_TAGGING
|
|
#ifdef ExAllocatePool
|
|
#undef ExAllocatePool
|
|
#endif
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'polF')
|
|
#endif
|
|
|
|
BOOLEAN FdcInSetupMode;
|
|
|
|
//
|
|
// Used for paging the driver.
|
|
//
|
|
ULONG PagingReferenceCount = 0;
|
|
PFAST_MUTEX PagingMutex = NULL;
|
|
|
|
ULONG NumberOfBuffers = 0;
|
|
ULONG BufferSize = 0;
|
|
ULONG Model30 = 0;
|
|
ULONG NotConfigurable = 0;
|
|
|
|
// Feb.9.1998 KIADP011 Get base address of configuration ports
|
|
// and device identifier
|
|
#ifdef TOSHIBAJ
|
|
ULONG SmcConfigBase;
|
|
ULONG SmcConfigID;
|
|
PUCHAR TranslatedConfigBase = NULL;
|
|
|
|
#endif
|
|
|
|
|
|
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. This routine can be called any number of times,
|
|
as long as the IO system and the configuration manager conspire to
|
|
give it an unmanaged controller to support at each call. It could
|
|
also be called a single time and given all of the controllers at
|
|
once.
|
|
|
|
It initializes the passed-in driver object, calls the configuration
|
|
manager to learn about the devices that it is to support, and for
|
|
each controller to be supported it calls a routine to initialize the
|
|
controller (and all drives attached to it).
|
|
|
|
Arguments:
|
|
|
|
DriverObject - a pointer to the object that represents this device
|
|
driver.
|
|
|
|
Return Value:
|
|
|
|
If we successfully initialize at least one drive, STATUS_SUCCESS is
|
|
returned.
|
|
|
|
If we don't (because the configuration manager returns an error, or
|
|
the configuration manager says that there are no controllers or
|
|
drives to support, or no controllers or drives can be successfully
|
|
initialized), then the last error encountered is propogated.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus;
|
|
|
|
//
|
|
// We use this to query into the registry as to whether we
|
|
// should break at driver entry.
|
|
//
|
|
|
|
RTL_QUERY_REGISTRY_TABLE paramTable[6];
|
|
ULONG zero = 0;
|
|
ULONG one = 1;
|
|
ULONG debugLevel = 0;
|
|
ULONG shouldBreak = 0;
|
|
ULONG setupMode;
|
|
PWCHAR path;
|
|
UNICODE_STRING parameters;
|
|
UNICODE_STRING systemPath;
|
|
UNICODE_STRING identifier;
|
|
UNICODE_STRING thinkpad, ps2e;
|
|
ULONG pathLength;
|
|
|
|
RtlInitUnicodeString(¶meters, L"\\Parameters");
|
|
RtlInitUnicodeString(&systemPath,
|
|
L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\System");
|
|
RtlInitUnicodeString(&thinkpad, L"IBM THINKPAD 750");
|
|
RtlInitUnicodeString(&ps2e, L"IBM PS2E");
|
|
|
|
pathLength = RegistryPath->Length + parameters.Length + sizeof(WCHAR);
|
|
if (pathLength < systemPath.Length + sizeof(WCHAR)) {
|
|
pathLength = systemPath.Length + sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
NumberOfBuffers = 3;
|
|
BufferSize = 0x8000;
|
|
|
|
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);
|
|
paramTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
paramTable[2].Name = L"NumberOfBuffers";
|
|
paramTable[2].EntryContext = &NumberOfBuffers;
|
|
paramTable[2].DefaultType = REG_DWORD;
|
|
paramTable[2].DefaultData = &NumberOfBuffers;
|
|
paramTable[2].DefaultLength = sizeof(ULONG);
|
|
paramTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
paramTable[3].Name = L"BufferSize";
|
|
paramTable[3].EntryContext = &BufferSize;
|
|
paramTable[3].DefaultType = REG_DWORD;
|
|
paramTable[3].DefaultData = &BufferSize;
|
|
paramTable[3].DefaultLength = sizeof(ULONG);
|
|
paramTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
paramTable[4].Name = L"SetupDone";
|
|
paramTable[4].EntryContext = &setupMode;
|
|
paramTable[4].DefaultType = REG_DWORD;
|
|
paramTable[4].DefaultData = &zero;
|
|
paramTable[4].DefaultLength = sizeof(ULONG);
|
|
|
|
if (!NT_SUCCESS(RtlQueryRegistryValues(
|
|
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|
path,
|
|
¶mTable[0],
|
|
NULL,
|
|
NULL))) {
|
|
|
|
shouldBreak = 0;
|
|
debugLevel = 0;
|
|
|
|
}
|
|
|
|
FdcInSetupMode = !(BOOLEAN)setupMode;
|
|
|
|
FdcDump( FDCSHOW, ("FdcDriverEntry: FdcInSetupMode = %x\n",FdcInSetupMode) );
|
|
|
|
if ( FdcInSetupMode ) {
|
|
|
|
OBJECT_ATTRIBUTES keyAttributes;
|
|
HANDLE keyHandle;
|
|
UNICODE_STRING value;
|
|
|
|
RtlInitUnicodeString( &value, L"SetupDone" );
|
|
setupMode = 1;
|
|
|
|
InitializeObjectAttributes( &keyAttributes,
|
|
RegistryPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
ntStatus = ZwOpenKey( &keyHandle,
|
|
KEY_ALL_ACCESS,
|
|
&keyAttributes );
|
|
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
|
|
FdcDump( FDCSHOW, ("FdcDriverEntry: Set SetupMode Value in Registry\n") );
|
|
|
|
ZwSetValueKey( keyHandle,
|
|
&value,
|
|
0,
|
|
REG_DWORD,
|
|
&setupMode,
|
|
sizeof(ULONG) );
|
|
|
|
ZwClose( keyHandle);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine whether or not this type of system has a
|
|
// model 30 floppy controller.
|
|
//
|
|
|
|
RtlZeroMemory(paramTable, sizeof(paramTable));
|
|
RtlZeroMemory(path, pathLength);
|
|
RtlMoveMemory(path,
|
|
systemPath.Buffer,
|
|
systemPath.Length);
|
|
|
|
|
|
RtlZeroMemory(&identifier, sizeof(UNICODE_STRING));
|
|
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
|
|
RTL_QUERY_REGISTRY_REQUIRED;
|
|
paramTable[0].Name = L"Identifier";
|
|
paramTable[0].EntryContext = &identifier;
|
|
|
|
if (NT_SUCCESS(RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
|
path,
|
|
paramTable,
|
|
NULL,
|
|
NULL))) {
|
|
|
|
|
|
if (identifier.Length == thinkpad.Length &&
|
|
RtlCompareMemory(identifier.Buffer, thinkpad.Buffer,
|
|
thinkpad.Length) == thinkpad.Length) {
|
|
|
|
Model30 = 1;
|
|
|
|
} else if (identifier.Length == ps2e.Length &&
|
|
RtlCompareMemory(identifier.Buffer, ps2e.Buffer,
|
|
ps2e.Length) == ps2e.Length) {
|
|
|
|
Model30 = 1;
|
|
} else {
|
|
Model30 = 0;
|
|
}
|
|
} else {
|
|
Model30 = 0;
|
|
}
|
|
|
|
//
|
|
// This part gets from the parameters part of the registry
|
|
// to see if the controller configuration needs to be disabled.
|
|
// Doing this lets SMC 661, and 662 work. On hardware that
|
|
// works normally, this change will show a slowdown of up
|
|
// to 40%. So defining this variable is not recommended
|
|
// unless things don't work without it.
|
|
//
|
|
//
|
|
// Also check the model30 value in the parameters section
|
|
// that is used to override the decision above.
|
|
//
|
|
|
|
RtlZeroMemory(¶mTable[0],
|
|
sizeof(paramTable));
|
|
RtlZeroMemory(path,
|
|
RegistryPath->Length+parameters.Length+sizeof(WCHAR));
|
|
RtlMoveMemory(path,
|
|
RegistryPath->Buffer,
|
|
RegistryPath->Length);
|
|
RtlMoveMemory((PCHAR) path + RegistryPath->Length,
|
|
parameters.Buffer,
|
|
parameters.Length);
|
|
|
|
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
paramTable[0].Name = L"NotConfigurable";
|
|
paramTable[0].EntryContext = &NotConfigurable;
|
|
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"Model30";
|
|
paramTable[1].EntryContext = &Model30;
|
|
paramTable[1].DefaultType = REG_DWORD;
|
|
paramTable[1].DefaultData = Model30 ? &one : &zero;
|
|
paramTable[1].DefaultLength = sizeof(ULONG);
|
|
|
|
if (!NT_SUCCESS(RtlQueryRegistryValues(
|
|
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|
path,
|
|
¶mTable[0],
|
|
NULL,
|
|
NULL))) {
|
|
|
|
NotConfigurable = 0;
|
|
}
|
|
|
|
#ifdef TOSHIBAJ
|
|
// Feb.9.1998 KIADP011 Get base address and identifier
|
|
RtlZeroMemory(¶mTable[0],
|
|
sizeof(paramTable));
|
|
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
paramTable[0].Name = L"ConfigurationBase";
|
|
paramTable[0].EntryContext = &SmcConfigBase;
|
|
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"ControllerID";
|
|
paramTable[1].EntryContext = &SmcConfigID;
|
|
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))) {
|
|
SmcConfigBase = 0;
|
|
SmcConfigID = 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// We don't need that path anymore.
|
|
//
|
|
if (path) {
|
|
ExFreePool(path);
|
|
}
|
|
|
|
#if DBG
|
|
FdcDebugLevel = debugLevel;
|
|
#endif
|
|
if (shouldBreak) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
|
|
FdcDump(FDCSHOW,
|
|
("Fdc: DriverEntry...\n"));
|
|
|
|
|
|
//
|
|
// Initialize the driver object with this driver's entry points.
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] =
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = FdcCreateClose;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = FdcPower;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = FdcPnp;
|
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
|
|
FdcInternalDeviceControl;
|
|
|
|
DriverObject->DriverStartIo = FdcStartIo;
|
|
DriverObject->DriverExtension->AddDevice = FdcAddDevice;
|
|
|
|
FDC_PAGE_INITIALIZE_DRIVER_WITH_MUTEX;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN OUT PDEVICE_OBJECT BusPhysicalDeviceObject
|
|
)
|
|
/*++
|
|
Routine Description.
|
|
|
|
A floppy controller device has been enumerated by the root/firmware
|
|
enumerator. Create an FDO and attach it to this PDO.
|
|
|
|
Arguments:
|
|
|
|
BusDeviceObject - Device object representing the floppy controller. That
|
|
to which we attach a new FDO.
|
|
|
|
DriverObject - This driver.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the device is successfully created.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
UNICODE_STRING deviceName;
|
|
|
|
FdcDump( FDCSHOW, ("FdcAddDevice: CreateDeviceObject\n"));
|
|
|
|
//
|
|
// Create the FDO device.
|
|
//
|
|
// JB:TBD - Still need to resolve device naming. For the time being,
|
|
// this device is unnamed (per the gameport example).
|
|
//
|
|
ntStatus = IoCreateDevice( DriverObject,
|
|
sizeof( FDC_FDO_EXTENSION ),
|
|
NULL,
|
|
FILE_DEVICE_CONTROLLER,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
TRUE,
|
|
&deviceObject );
|
|
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
|
|
//
|
|
// Initialize the fdoExtension for this device.
|
|
//
|
|
fdoExtension = deviceObject->DeviceExtension;
|
|
|
|
fdoExtension->IsFDO = TRUE;
|
|
fdoExtension->DriverObject = DriverObject;
|
|
fdoExtension->Self = deviceObject;
|
|
fdoExtension->OutstandingRequests = 1;
|
|
fdoExtension->TapeEnumerationPending = FALSE;
|
|
|
|
KeInitializeEvent( &fdoExtension->TapeEnumerationEvent,
|
|
SynchronizationEvent,
|
|
TRUE );
|
|
|
|
KeInitializeEvent( &fdoExtension->RemoveEvent,
|
|
SynchronizationEvent,
|
|
FALSE );
|
|
|
|
InitializeListHead( &fdoExtension->PDOs );
|
|
|
|
//
|
|
// Initialize a queue for power management.
|
|
//
|
|
InitializeListHead( &fdoExtension->PowerQueue );
|
|
KeInitializeSpinLock( &fdoExtension->PowerQueueSpinLock );
|
|
|
|
//
|
|
// Initialize a variable to hold the last motor settle
|
|
// time that we have seen. We will use this when we turn
|
|
// the floppy motor back on after a power event.
|
|
//
|
|
fdoExtension->LastMotorSettleTime.QuadPart = 0;
|
|
|
|
//
|
|
// Set the PDO for use with PlugPlay functions
|
|
//
|
|
fdoExtension->UnderlyingPDO = BusPhysicalDeviceObject;
|
|
|
|
//
|
|
// Now attach to the PDO so that we have a target for PnP and
|
|
// Power irps that we need to pass on.
|
|
//
|
|
FdcDump( FDCSHOW, ("AddDevice: Attaching %p to %p\n",
|
|
deviceObject,
|
|
BusPhysicalDeviceObject));
|
|
|
|
fdoExtension->TargetObject = IoAttachDeviceToDeviceStack( deviceObject,
|
|
BusPhysicalDeviceObject );
|
|
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
|
|
if ( deviceObject->AlignmentRequirement < FILE_WORD_ALIGNMENT ) {
|
|
|
|
deviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
|
|
}
|
|
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcPnp (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if this Pnp request is directed towards an FDO or a PDO and
|
|
pass the Irp on the the appropriate routine.
|
|
|
|
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;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PFDC_EXTENSION_HEADER extensionHeader;
|
|
KIRQL oldIrq;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
extensionHeader = (PFDC_EXTENSION_HEADER)DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Lock down the driver code in memory if it is not already.
|
|
//
|
|
|
|
FDC_PAGE_RESET_DRIVER_WITH_MUTEX;
|
|
|
|
if ( extensionHeader->IsFDO ) {
|
|
|
|
ntStatus = FdcFdoPnp( DeviceObject, Irp );
|
|
|
|
} else {
|
|
|
|
ntStatus = FdcPdoPnp( DeviceObject, Irp );
|
|
}
|
|
|
|
//
|
|
// Page out the driver if it is not busy elsewhere.
|
|
//
|
|
|
|
FDC_PAGE_ENTIRE_DRIVER_WITH_MUTEX;
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcFdoPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to perform Plug-and-Play
|
|
functions. This routine handles messages to the FDO which is part
|
|
of the bus DevNode.
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
NTSTATUS ntStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KEVENT doneEvent;
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Incerement our queued request counter.
|
|
//
|
|
InterlockedIncrement( &fdoExtension->OutstandingRequests);
|
|
|
|
if ( fdoExtension->Removed ) {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_DELETE_PENDING;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
if ( InterlockedDecrement( &fdoExtension->OutstandingRequests ) == 0 ) {
|
|
KeSetEvent( &fdoExtension->RemoveEvent, 0, FALSE );
|
|
}
|
|
return STATUS_DELETE_PENDING;
|
|
}
|
|
|
|
switch (irpSp->MinorFunction) {
|
|
|
|
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
|
|
|
|
ntStatus = FdcFilterResourceRequirements( DeviceObject, Irp );
|
|
|
|
break;
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_START_DEVICE - Irp: %p\n", Irp) );
|
|
|
|
//
|
|
// First we must pass this Irp on to the underlying PDO.
|
|
//
|
|
KeInitializeEvent( &doneEvent, NotificationEvent, FALSE );
|
|
|
|
IoCopyCurrentIrpStackLocationToNext( Irp );
|
|
|
|
IoSetCompletionRoutine( Irp,
|
|
FdcPnpComplete,
|
|
&doneEvent,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
if ( ntStatus == STATUS_PENDING ) {
|
|
|
|
ntStatus = KeWaitForSingleObject( &doneEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
ASSERT( ntStatus == STATUS_SUCCESS );
|
|
|
|
ntStatus = Irp->IoStatus.Status;
|
|
}
|
|
//
|
|
// Try to start the floppy disk controller.
|
|
//
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
|
|
ntStatus = FdcStartDevice( DeviceObject, Irp );
|
|
}
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_START_DEVICE %08x %08x\n",Irp->IoStatus.Status,Irp->IoStatus.Information) );
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
break;
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_QUERY_REMOVE_DEVICE - Irp: %p\n", Irp) );
|
|
|
|
//
|
|
// If the controller is in use (acquired) then we will not allow
|
|
// the device to be removed.
|
|
//
|
|
|
|
KeWaitForSingleObject( &fdoExtension->TapeEnumerationEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( fdoExtension->ControllerInUse ||
|
|
fdoExtension->TapeEnumerationPending ) {
|
|
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the controller was not in use we will set it so now
|
|
// so that any other attempted accesses to the fdc will have
|
|
fdoExtension->ControllerInUse = TRUE;
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_REMOVE_DEVICE - Irp: %p\n", Irp) );
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
if ( fdoExtension->FdcEnablerFileObject != NULL ) {
|
|
ObDereferenceObject( fdoExtension->FdcEnablerFileObject );
|
|
}
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_REMOVE_DEVICE - Detach from device %p\n", fdoExtension->TargetObject) );
|
|
IoDetachDevice( fdoExtension->TargetObject );
|
|
|
|
//
|
|
// Close the named synchronization event we opened at start time.
|
|
//
|
|
ZwClose( fdoExtension->AcquireEventHandle );
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_REMOVE_DEVICE - Delete device %p\n", fdoExtension->Self) );
|
|
IoDeleteDevice( fdoExtension->Self );
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_CANCEL_REMOVE_DEVICE - Irp: %p\n", Irp) );
|
|
fdoExtension->ControllerInUse = FALSE;
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_QUERY_STOP_DEVICE - Irp: %p\n", Irp) );
|
|
|
|
if ( fdoExtension->ControllerInUse ||
|
|
fdoExtension->TapeEnumerationPending ) {
|
|
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
} else {
|
|
|
|
fdoExtension->ControllerInUse = TRUE;
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_STOP_DEVICE - Irp: %p\n", Irp) );
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
break;
|
|
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_CANCEL_STOP_DEVICE - Irp: %p\n", Irp) );
|
|
|
|
fdoExtension->ControllerInUse = FALSE;
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_QUERY_DEVICE_RELATIONS - Irp: %p\n", Irp) );
|
|
|
|
ntStatus = FdcQueryDeviceRelations( DeviceObject, Irp );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: Unsupported PNP Request %x\n",irpSp->MinorFunction) );
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
break;
|
|
}
|
|
|
|
if ( InterlockedDecrement( &fdoExtension->OutstandingRequests ) == 0 ) {
|
|
KeSetEvent( &fdoExtension->RemoveEvent, 0, FALSE );
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcPnpComplete (
|
|
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. We use this completion routine when
|
|
we must post-process the irp after we are sure that the PDO is done
|
|
with it.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to our FDO
|
|
Irp - a pointer to the completed Irp
|
|
Context - an event that we will set indicating the irp is completed.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED so that control will be returned to
|
|
our calling routine.
|
|
|
|
--*/
|
|
{
|
|
|
|
if ( Irp->PendingReturned ) {
|
|
|
|
IoMarkIrpPending( Irp );
|
|
}
|
|
|
|
KeSetEvent( (PKEVENT)Context, 1, FALSE );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcPdoPnp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to perform Plug-and-Play
|
|
functions. This routine handles messages to the PDO which is part
|
|
of the bus DevNode.
|
|
|
|
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:
|
|
|
|
--*/
|
|
{
|
|
PFDC_PDO_EXTENSION pdoExtension;
|
|
NTSTATUS ntStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KEVENT doneEvent;
|
|
|
|
pdoExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
ntStatus = Irp->IoStatus.Status;
|
|
|
|
switch ( irpSp->MinorFunction ) {
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES: {
|
|
|
|
PDEVICE_CAPABILITIES deviceCapabilities;
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_QUERY_CAPABILITIES - Irp: %p\n", Irp) );
|
|
|
|
deviceCapabilities = irpSp->Parameters.DeviceCapabilities.Capabilities;
|
|
|
|
//
|
|
// Fill in the device capabilities structure and return it. The
|
|
// capabilities structure is in irpSp->Parameters.DeviceCapabilities.Capabilities;
|
|
//
|
|
// The size and Version should already be set appropraitely.
|
|
//
|
|
ASSERT( deviceCapabilities->Version == 1 );
|
|
ASSERT( deviceCapabilities->Size == sizeof(DEVICE_CAPABILITIES) );
|
|
|
|
//
|
|
// JB:TBD - not sure how to set these flags.
|
|
//
|
|
deviceCapabilities->LockSupported = FALSE; // No locking.
|
|
deviceCapabilities->EjectSupported = FALSE; // No ejection mechanism.
|
|
deviceCapabilities->Removable = FALSE; // Device is not removable (what about external laptop drives?)
|
|
deviceCapabilities->DockDevice = FALSE; // Device is not a docking device (this probably should be TRUE)
|
|
deviceCapabilities->UniqueID = FALSE; // ???
|
|
deviceCapabilities->SilentInstall = TRUE; // ???
|
|
deviceCapabilities->RawDeviceOK = FALSE; // ???
|
|
|
|
// deviceCapabilities->Address;
|
|
// deviceCapabilities->UINumber;
|
|
//
|
|
// deviceCapabilities->DeviceState[PowerSystemMaximum];
|
|
// deviceCapabilities->SystemWake;
|
|
// deviceCapabilities->DeviceWake;
|
|
//
|
|
// deviceCapabilities->D1Latency;
|
|
// deviceCapabilities->D2Latency;
|
|
// deviceCapabilities->D3Latency;
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_QUERY_ID:
|
|
|
|
//
|
|
// Query the IDs of the device
|
|
//
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_QUERY_ID - Irp: %p\n", Irp) );
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IdType %x\n", irpSp->Parameters.QueryId.IdType) );
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
switch ( irpSp->Parameters.QueryId.IdType) {
|
|
|
|
case BusQueryDeviceID:
|
|
// return a WCHAR (null terminated) string describing the device
|
|
// For symplicity we make it exactly the same as the Hardware ID.
|
|
case BusQueryHardwareIDs: {
|
|
|
|
UCHAR idString[25];
|
|
ANSI_STRING ansiId;
|
|
UNICODE_STRING uniId;
|
|
PWCHAR buffer;
|
|
ULONG length;
|
|
|
|
RtlZeroMemory( idString, 25 );
|
|
|
|
switch ( pdoExtension->DeviceType ) {
|
|
|
|
case FloppyDiskDevice:
|
|
|
|
// return a multi WCHAR (null terminated) string (null terminated)
|
|
// array for use in matching hardare ids in inf files;
|
|
//
|
|
sprintf( idString, "FDC\\PNP07%02X", pdoExtension->Instance );
|
|
|
|
break;
|
|
|
|
case FloppyTapeDevice:
|
|
|
|
//
|
|
// Examine the tape vendor id and build the id string
|
|
// appropriately.
|
|
//
|
|
if ( pdoExtension->TapeVendorId == -1 ) {
|
|
|
|
strcpy( idString, "FDC\\QICLEGACY" );
|
|
|
|
} else {
|
|
|
|
sprintf( idString, "FDC\\QIC%04X", (USHORT)pdoExtension->TapeVendorId );
|
|
|
|
}
|
|
break;
|
|
|
|
case FloppyControllerDevice:
|
|
|
|
sprintf( idString, "FDC\\ENABLER" );
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate enough memory for the string and 2 null characters since
|
|
// this is a multisz type.
|
|
//
|
|
length = strlen( idString ) * sizeof (WCHAR) + 2 * sizeof(WCHAR);
|
|
|
|
buffer = ExAllocatePool (PagedPool, length);
|
|
|
|
if ( buffer == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory( buffer, length );
|
|
|
|
ansiId.Length = ansiId.MaximumLength = (USHORT) strlen( idString );
|
|
ansiId.Buffer = idString;
|
|
|
|
uniId.Length = 0;
|
|
uniId.MaximumLength = (USHORT)length;
|
|
uniId.Buffer = buffer;
|
|
|
|
RtlAnsiStringToUnicodeString( &uniId, &ansiId, FALSE );
|
|
|
|
Irp->IoStatus.Information = (UINT_PTR) buffer;
|
|
|
|
break;
|
|
}
|
|
|
|
case BusQueryCompatibleIDs:{
|
|
|
|
PWCHAR buffer = NULL;
|
|
USHORT length;
|
|
|
|
//
|
|
// Build an instance ID. This is what PnP uses to tell if it has
|
|
// seen this thing before or not. Build it from the first hardware
|
|
// id and the port number.
|
|
//
|
|
switch ( pdoExtension->DeviceType ) {
|
|
|
|
case FloppyDiskDevice:
|
|
|
|
length = FDC_FLOPPY_COMPATIBLE_IDS_LENGTH * sizeof (WCHAR);
|
|
|
|
buffer = ExAllocatePool( PagedPool, length );
|
|
|
|
if ( buffer == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory( buffer, FDC_FLOPPY_COMPATIBLE_IDS, length );
|
|
buffer[7] = L'0' + pdoExtension->Instance;
|
|
}
|
|
|
|
break;
|
|
|
|
case FloppyTapeDevice:
|
|
|
|
if ( pdoExtension->TapeVendorId != -1 ) {
|
|
|
|
length = FDC_TAPE_COMPATIBLE_IDS_LENGTH * sizeof (WCHAR);
|
|
|
|
buffer = ExAllocatePool( PagedPool, length );
|
|
|
|
if ( buffer == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory( buffer, FDC_TAPE_COMPATIBLE_IDS, length );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FloppyControllerDevice:
|
|
|
|
length = FDC_CONTROLLER_COMPATIBLE_IDS_LENGTH * sizeof (WCHAR);
|
|
|
|
buffer = ExAllocatePool( PagedPool, length );
|
|
|
|
if ( buffer == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory( buffer, FDC_CONTROLLER_COMPATIBLE_IDS, length );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Information = (UINT_PTR)buffer;
|
|
break;
|
|
}
|
|
|
|
case BusQueryInstanceID: {
|
|
|
|
PWCHAR idString = L"0";
|
|
PWCHAR buffer;
|
|
|
|
//
|
|
// Build an instance ID. This is what PnP uses to tell if it has
|
|
// seen this thing before or not. Build it from the first hardware
|
|
// id and the port number.
|
|
|
|
buffer = ExAllocatePool( NonPagedPool, 4 );
|
|
|
|
if ( buffer == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
buffer[0] = L'0' + pdoExtension->Instance;
|
|
buffer[1] = 0;
|
|
|
|
Irp->IoStatus.Information = (UINT_PTR)buffer;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_START_DEVICE - Irp: %p\n", Irp) );
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_QUERY_STOP_DEVICE - Irp: %p\n", Irp) );
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_STOP_DEVICE - Irp: %p\n", Irp) );
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_CANCEL_STOP_DEVICE - Irp: %p\n", Irp) );
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_QUERY_REMOVE_DEVICE - Irp: %p\n", Irp) );
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
break;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_REMOVE_DEVICE - Irp: %p\n", Irp) );
|
|
pdoExtension->Removed = TRUE;
|
|
IoDeleteDevice( DeviceObject );
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: IRP_MN_CANCEL_REMOVE_DEVICE - Irp: %p\n", Irp) );
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: Unsupported PNP Request %x\n",irpSp->MinorFunction) );
|
|
|
|
// this is a leaf node
|
|
// status = STATUS_NOT_IMPLEMENTED
|
|
// For PnP requests to the PDO that we do not understand we should
|
|
// return the IRP WITHOUT setting the status or information fields.
|
|
// They may have already been set by a filter (eg acpi).
|
|
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
FdcDump( FDCSHOW, ("FdcPdoPnp: Return Status - %08x\n", ntStatus) );
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to perform Power functions
|
|
|
|
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:
|
|
|
|
--*/
|
|
{
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KIRQL oldIrql;
|
|
KEVENT doneEvent;
|
|
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
FdcDump( FDCSHOW, ("FdcPower:\n"));
|
|
|
|
if ( fdoExtension->IsFDO ) {
|
|
|
|
if ( fdoExtension->Removed ) {
|
|
|
|
ntStatus = STATUS_DELETE_PENDING;
|
|
PoStartNextPowerIrp( Irp );
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
} else {
|
|
|
|
switch ( irpSp->MinorFunction ) {
|
|
|
|
case IRP_MN_WAIT_WAKE:
|
|
case IRP_MN_POWER_SEQUENCE:
|
|
case IRP_MN_QUERY_POWER:
|
|
|
|
//
|
|
// Just forward this irp to the underlying device.
|
|
//
|
|
PoStartNextPowerIrp( Irp );
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = PoCallDriver(fdoExtension->TargetObject, Irp );
|
|
|
|
break;
|
|
|
|
case IRP_MN_SET_POWER:
|
|
|
|
//
|
|
// Lock down the driver code in memory if it is not already.
|
|
//
|
|
|
|
FDC_PAGE_RESET_DRIVER_WITH_MUTEX;
|
|
|
|
if ( irpSp->Parameters.Power.Type == SystemPowerState ) {
|
|
|
|
//
|
|
// If we are transitioning to a 'sleep' state start queueing
|
|
// irps.
|
|
//
|
|
if ( fdoExtension->CurrentPowerState <= PowerSystemWorking &&
|
|
irpSp->Parameters.Power.State.SystemState > PowerSystemWorking ) {
|
|
|
|
//
|
|
// If the device queue is not empty, wait for it now.
|
|
//
|
|
fdoExtension->CurrentPowerState = irpSp->Parameters.Power.State.SystemState;
|
|
|
|
// KeWaitForSingleObject( &fdoExtension->RemoveEvent,
|
|
// Executive,
|
|
// KernelMode,
|
|
// FALSE,
|
|
// NULL );
|
|
|
|
//
|
|
// Make sure that the motors are turned off
|
|
//
|
|
if (!IsNEC_98) {
|
|
WRITE_CONTROLLER(
|
|
fdoExtension->ControllerAddress.DriveControl,
|
|
(UCHAR)(fdoExtension->DriveControlImage & ~DRVCTL_MOTOR_MASK) );
|
|
} // (!IsNEC_98)
|
|
|
|
//
|
|
// Now forward this irp to the underlying PDO.
|
|
//
|
|
PoStartNextPowerIrp( Irp );
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = PoCallDriver(fdoExtension->TargetObject, Irp );
|
|
|
|
//
|
|
// Otherwise, if we are transitioning from a non-working state
|
|
// back to a working state turn the motor back on if it was on.
|
|
//
|
|
} else if ( fdoExtension->CurrentPowerState > PowerSystemWorking &&
|
|
irpSp->Parameters.Power.State.SystemState <= PowerSystemWorking ) {
|
|
|
|
//
|
|
// Pass this irp down to the PDO before proceeding.
|
|
//
|
|
KeInitializeEvent( &doneEvent, NotificationEvent, FALSE );
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
|
|
IoSetCompletionRoutine( Irp,
|
|
FdcPnpComplete,
|
|
&doneEvent,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
ntStatus = PoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
if ( ntStatus == STATUS_PENDING ) {
|
|
|
|
KeWaitForSingleObject( &doneEvent, Executive, KernelMode, FALSE, NULL );
|
|
}
|
|
|
|
if ( fdoExtension->DriveControlImage & DRVCTL_MOTOR_MASK ) {
|
|
|
|
WRITE_CONTROLLER(
|
|
fdoExtension->ControllerAddress.DriveControl,
|
|
fdoExtension->DriveControlImage );
|
|
|
|
if ( fdoExtension->LastMotorSettleTime.QuadPart > 0) {
|
|
|
|
KeDelayExecutionThread( KernelMode,
|
|
FALSE,
|
|
&fdoExtension->LastMotorSettleTime );
|
|
}
|
|
}
|
|
|
|
fdoExtension->CurrentPowerState = irpSp->Parameters.Power.State.SystemState;
|
|
|
|
//
|
|
// Set a flag to simulate a disk change event so that
|
|
// we will be sure to touch the floppy drive hardware
|
|
// the next time it is accessed in case it was removed.
|
|
//
|
|
fdoExtension->WakeUp = TRUE;
|
|
|
|
PoStartNextPowerIrp( Irp );
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
} else {
|
|
//
|
|
// We must just be changing non-working states. We
|
|
// ignore this activity but pass the irp on to the
|
|
// underlying device.
|
|
//
|
|
PoStartNextPowerIrp( Irp );
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = PoCallDriver(fdoExtension->TargetObject, Irp );
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We only handle system power states so if this is a
|
|
// device state irp just forward it to the underlying
|
|
// device.
|
|
//
|
|
PoStartNextPowerIrp( Irp );
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = PoCallDriver(fdoExtension->TargetObject, Irp );
|
|
}
|
|
|
|
//
|
|
// Page out the driver if it is not busy elsewhere.
|
|
//
|
|
|
|
FDC_PAGE_ENTIRE_DRIVER_WITH_MUTEX;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We are not yet doing any power management on the floppy controller.
|
|
//
|
|
switch (irpSp->MinorFunction) {
|
|
case IRP_MN_WAIT_WAKE:
|
|
break;
|
|
|
|
case IRP_MN_POWER_SEQUENCE:
|
|
break;
|
|
|
|
case IRP_MN_SET_POWER:
|
|
break;
|
|
|
|
case IRP_MN_QUERY_POWER:
|
|
break;
|
|
}
|
|
|
|
PoStartNextPowerIrp( Irp );
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcStartDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to start the floppy controller device. Starting
|
|
the floppy controller consists primarily of resetting it and configuring
|
|
it, mostly just to make sure that it is there.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object being started.
|
|
Irp - a pointer to the start device Irp.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
BOOLEAN foundPortA = FALSE;
|
|
BOOLEAN foundPortB = FALSE;
|
|
BOOLEAN foundDma = FALSE;
|
|
BOOLEAN foundInterrupt = FALSE;
|
|
ULONG currentBase = 0xFFFFFFFF;
|
|
|
|
PCM_RESOURCE_LIST translatedResources;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullList;
|
|
PCM_PARTIAL_RESOURCE_LIST partialList;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
|
|
ULONG i;
|
|
ULONG startOffset;
|
|
ULONG currentOffset;
|
|
|
|
UCHAR ioPortMap;
|
|
|
|
#ifdef TOSHIBAJ
|
|
BOOLEAN foundConfigIndex = FALSE;
|
|
BOOLEAN foundConfigData = FALSE;
|
|
#endif
|
|
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
//
|
|
// Ask the PDO if it is a tape enabler device and, if so, what
|
|
// is the enabler device object.
|
|
//
|
|
FdcGetEnablerDevice( fdoExtension );
|
|
|
|
if ( fdoExtension->FdcEnablerSupported ) {
|
|
|
|
INTERFACE_TYPE InterfaceType;
|
|
|
|
//
|
|
// This is a tape enabler card so we need to get the resources
|
|
// 'the old-fashinoed way'.
|
|
//
|
|
for ( InterfaceType = 0;
|
|
InterfaceType < MaximumInterfaceType;
|
|
InterfaceType++ ) {
|
|
|
|
CONFIGURATION_TYPE Dc = DiskController;
|
|
|
|
ntStatus = IoQueryDeviceDescription( &InterfaceType,
|
|
NULL,
|
|
&Dc,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
FdcFdoConfigCallBack,
|
|
fdoExtension );
|
|
|
|
if (!NT_SUCCESS(ntStatus) && (ntStatus != STATUS_OBJECT_NAME_NOT_FOUND)) {
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
|
|
if ( fdoExtension->FdcEnablerDeviceObject == NULL ) {
|
|
|
|
ntStatus = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
} else {
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Now that the PDO is done with the Irp we can have our way with
|
|
// it.
|
|
//
|
|
FdcDump( FDCSHOW, ("AllocatedResources = %08x\n",irpSp->Parameters.StartDevice.AllocatedResources));
|
|
FdcDump( FDCSHOW, ("AllocatedResourcesTranslated = %08x\n",irpSp->Parameters.StartDevice.AllocatedResourcesTranslated));
|
|
|
|
if ( irpSp->Parameters.StartDevice.AllocatedResources == NULL ||
|
|
irpSp->Parameters.StartDevice.AllocatedResourcesTranslated == NULL ) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Set up the resource information that we will use to access the
|
|
// controller hardware. We always expect only 1 full set of resources.
|
|
// In that list we expect a DMA channel, an Interrupt vector, and 2 I/O Port
|
|
// ranges. If we don't see all the required resources we will woof.
|
|
//
|
|
translatedResources = irpSp->Parameters.StartDevice.AllocatedResourcesTranslated;
|
|
|
|
ASSERT( translatedResources->Count == 1 );
|
|
|
|
fullList = &translatedResources->List[0];
|
|
partialList = &translatedResources->List[0].PartialResourceList;
|
|
|
|
//
|
|
// Enumerate the list of resources, adding them into our context as we go.
|
|
//
|
|
RtlZeroMemory( &fdoExtension->ControllerAddress, sizeof(CONTROLLER) );
|
|
|
|
for ( i = 0; i < partialList->Count; i++ ) {
|
|
|
|
partial = &partialList->PartialDescriptors[i];
|
|
|
|
switch ( partial->Type ) {
|
|
|
|
case CmResourceTypePort: {
|
|
|
|
if (IsNEC_98) {
|
|
if ( partial->u.Port.Length == 1 ) {
|
|
if (!fdoExtension->ControllerAddress.Status) {
|
|
fdoExtension->ControllerAddress.Status
|
|
= (PUCHAR)partial->u.Port.Start.LowPart;
|
|
} else if (!fdoExtension->ControllerAddress.Fifo) {
|
|
fdoExtension->ControllerAddress.Fifo
|
|
= (PUCHAR)partial->u.Port.Start.LowPart;
|
|
} else if (!fdoExtension->ControllerAddress.DriveControl) {
|
|
fdoExtension->ControllerAddress.DriveControl
|
|
= (PUCHAR)partial->u.Port.Start.LowPart;
|
|
} else if (!fdoExtension->ControllerAddress.ModeChange) {
|
|
fdoExtension->ControllerAddress.ModeChange
|
|
= (PUCHAR)partial->u.Port.Start.LowPart;
|
|
} else if (!fdoExtension->ControllerAddress.ModeChangeEx) {
|
|
fdoExtension->ControllerAddress.ModeChangeEx
|
|
= (PUCHAR)partial->u.Port.Start.LowPart;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we get a base address that is lower than anything we have seen
|
|
// before, we assume that we have been working with aliased addresses
|
|
// and start over with the new base address.
|
|
//
|
|
if ( (partial->u.Port.Start.LowPart & 0xFFFFFFF8) < currentBase ) {
|
|
|
|
#ifdef TOSHIBAJ
|
|
// Skip the descriptor including config ports only.
|
|
if ( !TranslatedConfigBase
|
|
|| ((partial->u.Port.Start.LowPart & 0xFFFFFFF8) != (ULONG)TranslatedConfigBase)
|
|
|| ((partial->u.Port.Start.LowPart & 0x7) + partial->u.Port.Length > 2) ) {
|
|
RtlZeroMemory( &fdoExtension->ControllerAddress, sizeof(CONTROLLER) );
|
|
currentBase = partial->u.Port.Start.LowPart & 0xFFFFFFF8;
|
|
FdcDump( FDCINFO,
|
|
("FdcStartDevice: Current base %04x\n", currentBase) );
|
|
}
|
|
#else
|
|
RtlZeroMemory( &fdoExtension->ControllerAddress, sizeof(CONTROLLER) );
|
|
currentBase = partial->u.Port.Start.LowPart & 0xFFFFFFF8;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// We only use resources that are associated with the current (lowest)
|
|
// base addressed. All others are assumed to be aliased and are not
|
|
// used.
|
|
//
|
|
if ( (partial->u.Port.Start.LowPart & 0xFFFFFFF8) == currentBase ) {
|
|
|
|
FdcDump( FDCINFO,
|
|
("FdcStartDevice: Adding - %04x, Length - %04x\n",
|
|
partial->u.Port.Start.LowPart,
|
|
partial->u.Port.Length) );
|
|
|
|
startOffset = partial->u.Port.Start.LowPart & 0x07;
|
|
|
|
if ( (partial->Flags & CM_RESOURCE_PORT_IO) == CM_RESOURCE_PORT_MEMORY ) {
|
|
|
|
fdoExtension->ControllerAddress.Address[startOffset] =
|
|
MmMapIoSpace( partial->u.Port.Start,
|
|
partial->u.Port.Length,
|
|
FALSE );
|
|
|
|
FdcDump( FDCINFO, ("FdcStartDevice: Mapped IoPort\n") );
|
|
|
|
} else {
|
|
|
|
fdoExtension->ControllerAddress.Address[startOffset] = (PUCHAR)partial->u.Port.Start.LowPart;
|
|
}
|
|
|
|
currentOffset = 1;
|
|
while ( currentOffset < partial->u.Port.Length ) {
|
|
|
|
fdoExtension->ControllerAddress.Address[startOffset + currentOffset] =
|
|
fdoExtension->ControllerAddress.Address[startOffset] + currentOffset;
|
|
++currentOffset;
|
|
}
|
|
}
|
|
|
|
#ifdef TOSHIBAJ
|
|
// Are there configuration ports in a descriptor ?
|
|
if ( TranslatedConfigBase
|
|
&& (partial->u.Port.Start.LowPart <= (ULONG)TranslatedConfigBase)
|
|
&& (partial->u.Port.Start.LowPart + partial->u.Port.Length > (ULONG)TranslatedConfigBase) ) {
|
|
foundConfigIndex = TRUE;
|
|
FdcDump( FDCINFO,
|
|
("FdcStartDevice: Configration index port in %04x (length %04x)\n",
|
|
partial->u.Port.Start.LowPart,
|
|
partial->u.Port.Length) );
|
|
}
|
|
|
|
if ( TranslatedConfigBase
|
|
&& (partial->u.Port.Start.LowPart <= (ULONG)TranslatedConfigBase + 1)
|
|
&& (partial->u.Port.Start.LowPart + partial->u.Port.Length > (ULONG)TranslatedConfigBase + 1) ) {
|
|
foundConfigData = TRUE;
|
|
FdcDump( FDCINFO,
|
|
("FdcStartDevice: Configration data port in %04x (length %04x)\n",
|
|
partial->u.Port.Start.LowPart,
|
|
partial->u.Port.Length) );
|
|
}
|
|
|
|
if (foundConfigIndex && foundConfigData) {
|
|
fdoExtension->ConfigBase = (PUCHAR)SmcConfigBase;
|
|
fdoExtension->Available3Mode = TRUE;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypeDma: {
|
|
|
|
DEVICE_DESCRIPTION deviceDesc = {0};
|
|
|
|
FdcDump( FDCINFO, ("FdcStartDevice: DMA - %04x\n", partial->u.Dma.Channel) );
|
|
|
|
foundDma = TRUE;
|
|
|
|
deviceDesc.Version = DEVICE_DESCRIPTION_VERSION1;
|
|
|
|
if ( partial->u.Dma.Channel > 3 ) {
|
|
deviceDesc.DmaWidth = Width16Bits;
|
|
} else {
|
|
deviceDesc.DmaWidth = Width8Bits;
|
|
}
|
|
|
|
deviceDesc.DemandMode = TRUE;
|
|
deviceDesc.MaximumLength = MAX_BYTES_PER_SECTOR * MAX_SECTORS_PER_TRACK;
|
|
deviceDesc.IgnoreCount = TRUE;
|
|
|
|
//
|
|
// Always ask for one more page than maximum transfer size.
|
|
//
|
|
deviceDesc.MaximumLength += PAGE_SIZE;
|
|
|
|
deviceDesc.DmaChannel = partial->u.Dma.Channel;
|
|
deviceDesc.InterfaceType = fullList->InterfaceType;
|
|
deviceDesc.DmaSpeed = DEFAULT_DMA_SPEED;
|
|
deviceDesc.AutoInitialize = FALSE;
|
|
|
|
fdoExtension->AdapterObject =
|
|
HalGetAdapter( &deviceDesc,
|
|
&fdoExtension->NumberOfMapRegisters );
|
|
|
|
if (!fdoExtension->AdapterObject) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Here we can get another adapter object for formatting. It
|
|
// should look the same as the previous one except AutoInitialize
|
|
// will be true.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypeInterrupt: {
|
|
|
|
FdcDump( FDCINFO, ("FdcStartDevice: IRQ - %04x\n", partial->u.Interrupt.Vector) );
|
|
|
|
foundInterrupt = TRUE;
|
|
|
|
if ( partial->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {
|
|
|
|
fdoExtension->InterruptMode = Latched;
|
|
|
|
} else {
|
|
|
|
fdoExtension->InterruptMode = LevelSensitive;
|
|
}
|
|
|
|
if (IsNEC_98) {
|
|
|
|
//
|
|
// NOTENOTE: Invalid Interrupt Level and Vector.
|
|
//
|
|
|
|
partial->u.Interrupt.Level = 0x0b;
|
|
partial->u.Interrupt.Vector = 0x13;
|
|
|
|
//
|
|
// We get the Vector with HalGetInterruptVector().
|
|
//
|
|
|
|
fdoExtension->ControllerVector =
|
|
HalGetInterruptVector( fullList->InterfaceType,
|
|
fullList->BusNumber,
|
|
partial->u.Interrupt.Level,
|
|
partial->u.Interrupt.Vector,
|
|
&fdoExtension->ControllerIrql,
|
|
&fdoExtension->ProcessorMask );
|
|
|
|
FdcDump( FDCSHOW,
|
|
("Resource Requirements - ControllerVector = 0x%x\n",
|
|
fdoExtension->ControllerVector) );
|
|
|
|
} else {
|
|
fdoExtension->ControllerVector = partial->u.Interrupt.Vector;
|
|
fdoExtension->ProcessorMask = partial->u.Interrupt.Affinity;
|
|
fdoExtension->ControllerIrql = (KIRQL)partial->u.Interrupt.Level;
|
|
}
|
|
fdoExtension->SharableVector = TRUE;
|
|
fdoExtension->SaveFloatState = FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
FdcDump( FDCINFO, ("FdcStartDevice: ControllerAddress.StatusA = %08x\n"
|
|
"FdcStartDevice: ControllerAddress.StatusB = %08x\n"
|
|
"FdcStartDevice: ControllerAddress.DriveControl = %08x\n"
|
|
"FdcStartDevice: ControllerAddress.Tape = %08x\n"
|
|
"FdcStartDevice: ControllerAddress.Status = %08x\n"
|
|
"FdcStartDevice: ControllerAddress.Fifo = %08x\n"
|
|
"FdcStartDevice: ControllerAddress.DRDC = %08x\n",
|
|
fdoExtension->ControllerAddress.StatusA,
|
|
fdoExtension->ControllerAddress.StatusB,
|
|
fdoExtension->ControllerAddress.DriveControl,
|
|
fdoExtension->ControllerAddress.Tape,
|
|
fdoExtension->ControllerAddress.Status,
|
|
fdoExtension->ControllerAddress.Fifo,
|
|
fdoExtension->ControllerAddress.DRDC) );
|
|
|
|
if (IsNEC_98) {
|
|
FdcDump( FDCINFO, ("FdcStartDevice: ControllerAddress.ModeChange = %08x\n"
|
|
"FdcStartDevice: ControllerAddress.ModeChangeEx = %08x\n",
|
|
fdoExtension->ControllerAddress.ModeChange,
|
|
fdoExtension->ControllerAddress.ModeChangeEx) );
|
|
}
|
|
|
|
if ( !foundDma ||
|
|
!foundInterrupt ||
|
|
fdoExtension->ControllerAddress.DriveControl == NULL ||
|
|
// fdoExtension->ControllerAddress.Tape == NULL ||
|
|
fdoExtension->ControllerAddress.Status == NULL ||
|
|
fdoExtension->ControllerAddress.Fifo == NULL ||
|
|
((!IsNEC_98) ? (fdoExtension->ControllerAddress.DRDC.DataRate == NULL)
|
|
: ((fdoExtension->ControllerAddress.ModeChange == NULL) ||
|
|
(fdoExtension->ControllerAddress.ModeChangeEx == NULL)) )
|
|
) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
//
|
|
// Set up the bus information since we know it now.
|
|
//
|
|
fdoExtension->BusType = fullList->InterfaceType;
|
|
fdoExtension->BusNumber = fullList->BusNumber;
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
|
|
ntStatus = FdcInitializeDeviceObject( DeviceObject );
|
|
|
|
//
|
|
// Connect the interrupt for the reset operation.
|
|
//
|
|
ntStatus = IoConnectInterrupt( &fdoExtension->InterruptObject,
|
|
FdcInterruptService,
|
|
fdoExtension,
|
|
NULL,
|
|
fdoExtension->ControllerVector,
|
|
fdoExtension->ControllerIrql,
|
|
fdoExtension->ControllerIrql,
|
|
fdoExtension->InterruptMode,
|
|
fdoExtension->SharableVector,
|
|
fdoExtension->ProcessorMask,
|
|
fdoExtension->SaveFloatState );
|
|
|
|
FdcDump( FDCINFO, ("FdcStartDevice: IoConnectInterrupt - %08x\n", ntStatus) );
|
|
|
|
fdoExtension->CurrentInterrupt = FALSE;
|
|
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
|
|
//
|
|
// Initialize (Reset) the controller hardware. This will make
|
|
// sure that the controller is really there and leave it in an
|
|
// appropriate state for the rest of the system startup.
|
|
//
|
|
|
|
fdoExtension->AllowInterruptProcessing =
|
|
fdoExtension->CurrentInterrupt = TRUE;
|
|
|
|
//
|
|
// Acquire the Fdc Enabler card if there is one
|
|
//
|
|
if (fdoExtension->FdcEnablerSupported) {
|
|
|
|
LARGE_INTEGER acquireTimeOut;
|
|
|
|
acquireTimeOut.QuadPart = -(ONE_SECOND * 15);
|
|
|
|
ntStatus = FcFdcEnabler( fdoExtension->FdcEnablerDeviceObject,
|
|
// IOCTL_ACQUIRE_FDC, // For spelling miss in flpyenbl.h
|
|
IOCTL_AQUIRE_FDC,
|
|
&acquireTimeOut);
|
|
}
|
|
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
|
|
ntStatus = FcInitializeControllerHardware( fdoExtension,
|
|
DeviceObject );
|
|
|
|
FdcDump( FDCINFO, ("FdcStartDevice: FcInitializeControllerHardware - %08x\n", ntStatus) );
|
|
|
|
//
|
|
// Free the tape accelerator card if it was used.
|
|
//
|
|
if (fdoExtension->FdcEnablerSupported) {
|
|
FcFdcEnabler( fdoExtension->FdcEnablerDeviceObject,
|
|
IOCTL_RELEASE_FDC,
|
|
NULL);
|
|
}
|
|
|
|
fdoExtension->CurrentInterrupt = FALSE;
|
|
}
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
fdoExtension->HardwareFailed = FALSE;
|
|
ntStatus = FcGetFdcInformation ( fdoExtension );
|
|
|
|
} else {
|
|
|
|
fdoExtension->HardwareFailed = TRUE;
|
|
}
|
|
|
|
if (IsNEC_98) {
|
|
//
|
|
// NEC98's FDD driver can't not disconnect interrupt,
|
|
// and can't not page out this driver. Because when a FD is inserted in FDD or
|
|
// is ejected from FDD, then H/W calls FDD driver's interrupt routine.
|
|
//
|
|
|
|
} else { // (IsNEC_98)
|
|
|
|
IoDisconnectInterrupt(fdoExtension->InterruptObject);
|
|
|
|
} // (IsNEC_98)
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
return ntStatus;
|
|
}
|
|
NTSTATUS
|
|
FdcInitializeDeviceObject(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the DeviceObject resources. DeviceObject resources
|
|
only need to be initialized once, regardless of how many times this device
|
|
is started.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object being started.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
UNICODE_STRING unicodeEvent;
|
|
USHORT motorControlData;
|
|
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
|
|
if ( !fdoExtension->DeviceObjectInitialized ) {
|
|
|
|
//
|
|
// Set the time to wait for an interrupt before timing out to a
|
|
// few seconds.
|
|
//
|
|
fdoExtension->InterruptDelay.QuadPart = -(ONE_SECOND * 4);
|
|
|
|
//
|
|
// Set the minimum time that we can delay (10ms according to system
|
|
// rules). This will be used when we have to delay to, say, wait
|
|
// for the FIFO - the FIFO should become ready is well under 10ms.
|
|
//
|
|
fdoExtension->Minimum10msDelay.QuadPart = -(10 * 1000 * 10);
|
|
|
|
if (IsNEC_98) {
|
|
//
|
|
// Set initialize data to move state
|
|
//
|
|
fdoExtension->ResultStatus0[0] = 0xc0;
|
|
fdoExtension->ResultStatus0[1] = 0xc1;
|
|
fdoExtension->ResultStatus0[2] = 0xc2;
|
|
fdoExtension->ResultStatus0[3] = 0xc3;
|
|
|
|
//
|
|
// Reset high
|
|
//
|
|
|
|
READ_CONTROLLER(fdoExtension->ControllerAddress.DriveControl);
|
|
|
|
//
|
|
// Initialize motor running status.
|
|
// 0 - stop
|
|
// 1 - just run
|
|
// 2 - be running
|
|
//
|
|
|
|
fdoExtension->MotorRunning = 0;
|
|
|
|
//
|
|
// Get BIOS common area date.
|
|
//
|
|
|
|
{
|
|
ULONG nodeNumber;
|
|
CHAR AnsiBuffer[512];
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING registryPath;
|
|
ULONG Configuration;
|
|
|
|
RTL_QUERY_REGISTRY_TABLE paramTable[2];
|
|
PUCHAR ConfigurationData1;
|
|
|
|
ConfigurationData1 = ExAllocatePool(NonPagedPoolCacheAligned, 1192);
|
|
|
|
if (ConfigurationData1 == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(ConfigurationData1, 1192);
|
|
|
|
paramTable[0].QueryRoutine = NULL;
|
|
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
paramTable[0].Name = L"Configuration Data";
|
|
paramTable[0].EntryContext = ConfigurationData1;
|
|
paramTable[0].DefaultType = REG_DWORD;
|
|
paramTable[0].DefaultData = (PVOID)&Configuration;
|
|
paramTable[0].DefaultLength = 0;
|
|
|
|
paramTable[1].QueryRoutine = NULL;
|
|
paramTable[1].Flags = 0;
|
|
paramTable[1].Name = NULL;
|
|
paramTable[1].EntryContext = NULL;
|
|
paramTable[1].DefaultType = REG_NONE;
|
|
paramTable[1].DefaultData = NULL;
|
|
paramTable[1].DefaultLength = 0;
|
|
|
|
((PULONG)ConfigurationData1)[0] = 1192;
|
|
|
|
nodeNumber = FdcFindIsaBusNode();
|
|
|
|
if ( nodeNumber != -1 ) {
|
|
|
|
//
|
|
// Build path buffer...
|
|
//
|
|
|
|
sprintf(AnsiBuffer,ISA_BUS_NODE,nodeNumber);
|
|
RtlInitAnsiString(&AnsiString,AnsiBuffer);
|
|
ntStatus = RtlAnsiStringToUnicodeString(®istryPath,&AnsiString,TRUE);
|
|
|
|
if (!(NT_SUCCESS(ntStatus))) {
|
|
ExFreePool(ConfigurationData1);
|
|
return ntStatus;
|
|
}
|
|
|
|
ntStatus = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
|
registryPath.Buffer,
|
|
paramTable,
|
|
NULL,
|
|
NULL);
|
|
|
|
RtlFreeUnicodeString(®istryPath);
|
|
|
|
}
|
|
|
|
if (!(NT_SUCCESS(ntStatus))) {
|
|
|
|
ExFreePool(ConfigurationData1);
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Set disk dirve existing bit.
|
|
//
|
|
|
|
fdoExtension->FloppyEquip = (UCHAR)(FdcGet0Seg(ConfigurationData1, 0x55c) & 0x0F);
|
|
|
|
//
|
|
// Reset high
|
|
//
|
|
|
|
READ_CONTROLLER(fdoExtension->ControllerAddress.DriveControl);
|
|
|
|
motorControlData = READ_CONTROLLER(fdoExtension->ControllerAddress.ModeChange);
|
|
motorControlData &= 0x03;
|
|
motorControlData |= 0x04;
|
|
|
|
//
|
|
// Motor control.
|
|
//
|
|
|
|
WRITE_CONTROLLER(fdoExtension->ControllerAddress.ModeChange, motorControlData);
|
|
|
|
ExFreePool(ConfigurationData1);
|
|
}
|
|
} // (IsNEC_98)
|
|
|
|
//
|
|
// Initialize the DPC structure in the device object, so that
|
|
// the ISR can queue DPCs.
|
|
//
|
|
IoInitializeDpcRequest( fdoExtension->Self, FdcDeferredProcedure );
|
|
|
|
//
|
|
// Occasionally during stress we've seen the device lock up.
|
|
// We create a dpc so that we can log that the device lock up
|
|
// occured and that we reset the device.
|
|
//
|
|
KeInitializeDpc( &fdoExtension->LogErrorDpc,
|
|
FcLogErrorDpc,
|
|
fdoExtension );
|
|
|
|
//
|
|
// Assume there is a CONFIGURE command until found otherwise.
|
|
// Other Booleans were zero-initialized to FALSE.
|
|
//
|
|
fdoExtension->ControllerConfigurable = NotConfigurable ? FALSE : TRUE;
|
|
fdoExtension->Model30 = Model30 ? TRUE : FALSE;
|
|
|
|
fdoExtension->AllowInterruptProcessing = TRUE;
|
|
fdoExtension->CurrentInterrupt = TRUE;
|
|
fdoExtension->ControllerInUse = FALSE;
|
|
fdoExtension->CurrentIrp = NULL;
|
|
|
|
//
|
|
// Start the timer
|
|
//
|
|
fdoExtension->InterruptTimer = CANCEL_TIMER;
|
|
|
|
IoInitializeTimer( DeviceObject, FdcCheckTimer, fdoExtension );
|
|
|
|
//
|
|
// Initialize events to signal interrupts and adapter object
|
|
// allocation
|
|
//
|
|
KeInitializeEvent( &fdoExtension->InterruptEvent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
KeInitializeEvent( &fdoExtension->AllocateAdapterChannelEvent,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
fdoExtension->AdapterChannelRefCount = 0;
|
|
|
|
RtlInitUnicodeString( &unicodeEvent, L"\\Device\\FloppyControllerEvent0" );
|
|
|
|
fdoExtension->AcquireEvent = IoCreateSynchronizationEvent( &unicodeEvent,
|
|
&fdoExtension->AcquireEventHandle);
|
|
|
|
KeInitializeEvent( &fdoExtension->SynchEvent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
}
|
|
|
|
fdoExtension->DeviceObjectInitialized = TRUE;
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FdcFdoConfigCallBack(
|
|
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 a tape enabler if we ever find one.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to our FDO extension
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
|
|
PFDC_FDO_EXTENSION fdoExtension = (PFDC_FDO_EXTENSION)Context;
|
|
NTSTATUS ntStatus;
|
|
UNICODE_STRING pdoName;
|
|
PDEVICE_OBJECT newPdo;
|
|
PFDC_PDO_EXTENSION pdoExtension;
|
|
RTL_QUERY_REGISTRY_TABLE paramTable[2];
|
|
ULONG apiSupported;
|
|
WCHAR idstr[200];
|
|
UNICODE_STRING str;
|
|
USHORT i;
|
|
BOOLEAN foundPort = FALSE;
|
|
BOOLEAN foundInterrupt = FALSE;
|
|
BOOLEAN foundDma = FALSE;
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoConfigCallBack:\n") );
|
|
|
|
//
|
|
// The first thing to do is to go out and look for an enabler. We
|
|
// know we are dealing with one if there is a registry value called
|
|
// APISupported.
|
|
//
|
|
str.Length = 0;
|
|
str.MaximumLength = 200;
|
|
str.Buffer = idstr;
|
|
|
|
RtlZeroMemory( ¶mTable[0], sizeof(paramTable) );
|
|
|
|
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
paramTable[0].Name = L"APISupported";
|
|
paramTable[0].EntryContext = &str;
|
|
paramTable[0].DefaultType = REG_SZ;
|
|
paramTable[0].DefaultData = L"";
|
|
paramTable[0].DefaultLength = sizeof(WCHAR);
|
|
|
|
ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|
PathName->Buffer,
|
|
¶mTable[0],
|
|
NULL,
|
|
NULL);
|
|
if ( !NT_SUCCESS( ntStatus ) ) {
|
|
str.Buffer[0] = 0;
|
|
}
|
|
|
|
if ( str.Buffer[0] != 0 ) {
|
|
|
|
FdcDump(FDCINFO,
|
|
("FdcFdoConfigCallBack: Got registry setting for EnablerAPI = %ls\n",
|
|
(ULONG_PTR)str.Buffer) );
|
|
|
|
ntStatus = IoGetDeviceObjectPointer( &str,
|
|
FILE_READ_ACCESS,
|
|
&fdoExtension->FdcEnablerFileObject,
|
|
&fdoExtension->FdcEnablerDeviceObject);
|
|
}
|
|
|
|
if ( fdoExtension->FdcEnablerDeviceObject != NULL ) {
|
|
|
|
PCM_FULL_RESOURCE_DESCRIPTOR controllerData =
|
|
(PCM_FULL_RESOURCE_DESCRIPTOR)
|
|
(((PUCHAR)ControllerInformation[IoQueryDeviceConfigurationData]) +
|
|
ControllerInformation[IoQueryDeviceConfigurationData]->DataOffset);
|
|
|
|
//
|
|
// We have the pointer. Save off the interface type and
|
|
// the busnumber for use when we call the Hal and the
|
|
// Io System.
|
|
//
|
|
fdoExtension->BusType = BusType;
|
|
fdoExtension->BusNumber = BusNumber;
|
|
fdoExtension->SharableVector = TRUE;
|
|
fdoExtension->SaveFloatState = FALSE;
|
|
|
|
//
|
|
// We need to get the following information out of the partial
|
|
// resource descriptors.
|
|
//
|
|
// The irql and vector.
|
|
//
|
|
// The dma channel.
|
|
//
|
|
// The base address and span covered by the floppy controllers
|
|
// registers.
|
|
//
|
|
// It is not defined how these appear in the partial resource
|
|
// lists, so we will just loop over all of them. If we find
|
|
// something we don't recognize, we drop that information on
|
|
// the floor. When we have finished going through all the
|
|
// partial information, we validate that we got the above
|
|
// three.
|
|
//
|
|
for ( i = 0;
|
|
i < controllerData->PartialResourceList.Count;
|
|
i++ ) {
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial =
|
|
&controllerData->PartialResourceList.PartialDescriptors[i];
|
|
|
|
switch ( partial->Type ) {
|
|
|
|
case CmResourceTypePort: {
|
|
|
|
foundPort = TRUE;
|
|
|
|
//
|
|
// Save of the pointer to the partial so
|
|
// that we can later use it to report resources
|
|
// and we can also use this later in the routine
|
|
// to make sure that we got all of our resources.
|
|
//
|
|
fdoExtension->SpanOfControllerAddress = partial->u.Port.Length;
|
|
fdoExtension->ControllerAddress.StatusA =
|
|
FdcGetControllerBase(
|
|
BusType,
|
|
BusNumber,
|
|
partial->u.Port.Start,
|
|
fdoExtension->SpanOfControllerAddress,
|
|
(BOOLEAN)!!partial->Flags );
|
|
|
|
if ( fdoExtension->ControllerAddress.StatusA == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
fdoExtension->ControllerAddress.StatusB = fdoExtension->ControllerAddress.StatusA + 1;
|
|
fdoExtension->ControllerAddress.DriveControl = fdoExtension->ControllerAddress.StatusA + 2;
|
|
fdoExtension->ControllerAddress.Tape = fdoExtension->ControllerAddress.StatusA + 3;
|
|
fdoExtension->ControllerAddress.Status = fdoExtension->ControllerAddress.StatusA + 4;
|
|
fdoExtension->ControllerAddress.Fifo = fdoExtension->ControllerAddress.StatusA + 5;
|
|
fdoExtension->ControllerAddress.DRDC.DataRate = fdoExtension->ControllerAddress.StatusA + 7;
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
case CmResourceTypeInterrupt: {
|
|
|
|
foundInterrupt = TRUE;
|
|
|
|
if ( partial->Flags & CM_RESOURCE_INTERRUPT_LATCHED ) {
|
|
|
|
fdoExtension->InterruptMode = Latched;
|
|
|
|
} else {
|
|
|
|
fdoExtension->InterruptMode = LevelSensitive;
|
|
|
|
}
|
|
|
|
fdoExtension->ControllerVector =
|
|
HalGetInterruptVector(
|
|
BusType,
|
|
BusNumber,
|
|
partial->u.Interrupt.Level,
|
|
partial->u.Interrupt.Vector,
|
|
&fdoExtension->ControllerIrql,
|
|
&fdoExtension->ProcessorMask
|
|
);
|
|
|
|
break;
|
|
}
|
|
case CmResourceTypeDma: {
|
|
|
|
DEVICE_DESCRIPTION deviceDesc = {0};
|
|
|
|
//
|
|
// Use IgnoreCount equal to TRUE to fix PS/1000.
|
|
//
|
|
foundDma = TRUE;
|
|
|
|
deviceDesc.Version = DEVICE_DESCRIPTION_VERSION1;
|
|
|
|
if ( partial->u.Dma.Channel > 3 ) {
|
|
deviceDesc.DmaWidth = Width16Bits;
|
|
} else {
|
|
deviceDesc.DmaWidth = Width8Bits;
|
|
}
|
|
|
|
deviceDesc.DemandMode = TRUE;
|
|
deviceDesc.MaximumLength = MAX_BYTES_PER_SECTOR * MAX_SECTORS_PER_TRACK;
|
|
deviceDesc.IgnoreCount = TRUE;
|
|
|
|
deviceDesc.DmaChannel = partial->u.Dma.Channel;
|
|
deviceDesc.InterfaceType = BusType;
|
|
deviceDesc.DmaSpeed = DEFAULT_DMA_SPEED;
|
|
fdoExtension->AdapterObject =
|
|
HalGetAdapter(
|
|
&deviceDesc,
|
|
&fdoExtension->NumberOfMapRegisters
|
|
);
|
|
|
|
if ( fdoExtension->AdapterObject == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// If we didn't get all the information then we return
|
|
// insufficient resources.
|
|
//
|
|
if ( !foundPort || !foundInterrupt || !foundDma ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
PVOID
|
|
FdcGetControllerBase(
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG BusNumber,
|
|
PHYSICAL_ADDRESS IoAddress,
|
|
ULONG NumberOfBytes,
|
|
BOOLEAN InIoSpace
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine maps an IO address to system address space.
|
|
|
|
Arguments:
|
|
|
|
BusType - what type of bus - eisa, mca, isa
|
|
IoBusNumber - which IO bus (for machines with multiple buses).
|
|
IoAddress - base device address to be mapped.
|
|
NumberOfBytes - number of bytes for which address is valid.
|
|
InIoSpace - indicates an IO address.
|
|
|
|
Return Value:
|
|
|
|
Mapped address
|
|
|
|
--*/
|
|
{
|
|
PHYSICAL_ADDRESS cardAddress;
|
|
ULONG addressSpace = InIoSpace;
|
|
PVOID Address;
|
|
|
|
if ( !HalTranslateBusAddress( BusType,
|
|
BusNumber,
|
|
IoAddress,
|
|
&addressSpace,
|
|
&cardAddress ) ){
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Map the device base address into the virtual address space
|
|
// if the address is in memory space.
|
|
//
|
|
|
|
if ( !addressSpace ) {
|
|
|
|
Address = MmMapIoSpace( cardAddress,
|
|
NumberOfBytes,
|
|
FALSE );
|
|
|
|
} else {
|
|
|
|
Address = (PCONTROLLER)cardAddress.LowPart;
|
|
}
|
|
return Address;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FcInitializeControllerHardware(
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called at initialization time by FcInitializeDevice()
|
|
- once for each controller that we have to support.
|
|
|
|
When this routine is called, the controller data structures have all
|
|
been allocated.
|
|
|
|
Arguments:
|
|
|
|
ControllerData - the completed data structure associated with the
|
|
controller hardware being initialized.
|
|
|
|
DeviceObject - a pointer to a device object; this routine will cause
|
|
an interrupt, and the ISR requires CurrentDeviceObject to be filled
|
|
in.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if this controller appears to have been reset properly,
|
|
error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
UCHAR statusRegister0;
|
|
UCHAR cylinder;
|
|
UCHAR driveNumber;
|
|
UCHAR retrycnt;
|
|
|
|
FdcDump( FDCSHOW, ("Fdc: FcInitializeControllerHardware...\n") );
|
|
|
|
for (retrycnt = 0; ; retrycnt++) {
|
|
|
|
//
|
|
// Reset the controller. This will cause an interrupt. Reset
|
|
// CurrentDeviceObject until after the 10ms wait, in case any
|
|
// stray interrupts come in.
|
|
//
|
|
DISABLE_CONTROLLER_IMAGE (FdoExtension);
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.DriveControl,
|
|
FdoExtension->DriveControlImage );
|
|
|
|
KeStallExecutionProcessor( 10 );
|
|
|
|
FdoExtension->CurrentDeviceObject = DeviceObject;
|
|
FdoExtension->AllowInterruptProcessing = TRUE;
|
|
FdoExtension->CommandHasResultPhase = FALSE;
|
|
KeResetEvent( &FdoExtension->InterruptEvent );
|
|
|
|
ENABLE_CONTROLLER_IMAGE (FdoExtension);
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.DriveControl,
|
|
FdoExtension->DriveControlImage );
|
|
|
|
if (IsNEC_98) {
|
|
//
|
|
// NEC98 don't have to wait for interrupt.
|
|
//
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
} else { // (IsNEC_98)
|
|
//
|
|
// Wait for an interrupt. Note that STATUS_TIMEOUT and
|
|
// STATUS_SUCCESS are the only possible return codes, since we
|
|
// aren't alertable and won't get APCs.
|
|
//
|
|
ntStatus = KeWaitForSingleObject( &FdoExtension->InterruptEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&FdoExtension->InterruptDelay );
|
|
} // (IsNEC_98)
|
|
|
|
if (ntStatus == STATUS_TIMEOUT) {
|
|
|
|
if (retrycnt >= 1) {
|
|
break;
|
|
}
|
|
|
|
// Retry reset after configure command to enable polling
|
|
// interrupt.
|
|
|
|
FdoExtension->FifoBuffer[0] = COMMND_CONFIGURE;
|
|
|
|
if (FdoExtension->Clock48MHz) {
|
|
FdoExtension->FifoBuffer[0] |= COMMND_OPTION_CLK48;
|
|
}
|
|
|
|
FdoExtension->FifoBuffer[1] = 0;
|
|
FdoExtension->FifoBuffer[2] = COMMND_CONFIGURE_FIFO_THRESHOLD;
|
|
FdoExtension->FifoBuffer[3] = 0;
|
|
|
|
ntStatus = FcIssueCommand( FdoExtension,
|
|
FdoExtension->FifoBuffer,
|
|
FdoExtension->FifoBuffer,
|
|
NULL,
|
|
0,
|
|
0 );
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
ntStatus = STATUS_TIMEOUT;
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor( 500 );
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
if ( ntStatus == STATUS_TIMEOUT ) {
|
|
|
|
//
|
|
// Change info to an error.
|
|
//
|
|
|
|
ntStatus = STATUS_IO_TIMEOUT;
|
|
|
|
FdoExtension->HardwareFailed = TRUE;
|
|
}
|
|
|
|
if ( !NT_SUCCESS( ntStatus ) ) {
|
|
|
|
FdcDump(FDCDBGP,("Fdc: controller didn't interrupt after reset\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
if (!IsNEC_98) {
|
|
|
|
ntStatus = FcFinishReset( FdoExtension );
|
|
|
|
} // (!IsNEC_98)
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FcGetFdcInformation(
|
|
IN OUT PFDC_FDO_EXTENSION FdoExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will attempt to identify the type of Floppy Controller
|
|
|
|
Arguments:
|
|
|
|
FdoExtension - a pointer to our data area for the drive being
|
|
accessed (any drive if a controller command is being given).
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
FDC_INFORMATION fdcInfo;
|
|
|
|
if (FdoExtension->FdcEnablerSupported) {
|
|
|
|
fdcInfo.structSize = sizeof(fdcInfo);
|
|
|
|
ntStatus = FcFdcEnabler( FdoExtension->FdcEnablerDeviceObject,
|
|
IOCTL_GET_FDC_INFO,
|
|
&fdcInfo);
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
FdoExtension->FdcType = (UCHAR)fdcInfo.FloppyControllerType;
|
|
FdoExtension->Clock48MHz =
|
|
(fdcInfo.ClockRatesSupported == FDC_CLOCK_48MHZ);
|
|
FdoExtension->FdcSpeeds = (UCHAR)fdcInfo.SpeedsAvailable;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// First, assume that we don't know what kind of FDC is attached.
|
|
//
|
|
|
|
FdoExtension->FdcType = FDC_TYPE_UNKNOWN;
|
|
|
|
|
|
// Check for an enhanced type controller by issuing the version command.
|
|
|
|
FdoExtension->FifoBuffer[0] = COMMND_VERSION;
|
|
|
|
ntStatus = FcIssueCommand( FdoExtension,
|
|
FdoExtension->FifoBuffer,
|
|
FdoExtension->FifoBuffer,
|
|
NULL,
|
|
0,
|
|
0 );
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
if (FdoExtension->FifoBuffer[0] == VALID_NEC_FDC) {
|
|
|
|
FdoExtension->FdcType = FDC_TYPE_ENHANCED;
|
|
|
|
} else {
|
|
|
|
FdoExtension->FdcType = FDC_TYPE_NORMAL;
|
|
|
|
}
|
|
}
|
|
|
|
// Determine if the controller is a National 8477 by issuing the NSC
|
|
// command which is specific to National parts and returns 0x71. (This
|
|
// command happens to be the same as the Intel Part ID command so we
|
|
// will use it instead.) The lower four bits are subject to change by
|
|
// National and will reflect the version of the part in question. At
|
|
// this point we will only test the high four bits.
|
|
|
|
if ( FdoExtension->FdcType == FDC_TYPE_ENHANCED &&
|
|
NT_SUCCESS( ntStatus ) ) {
|
|
|
|
FdoExtension->FifoBuffer[0] = COMMND_PART_ID;
|
|
|
|
ntStatus = FcIssueCommand( FdoExtension,
|
|
FdoExtension->FifoBuffer,
|
|
FdoExtension->FifoBuffer,
|
|
NULL,
|
|
0,
|
|
0 );
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
if ( (FdoExtension->FifoBuffer[0] & NSC_MASK) ==
|
|
NSC_PRIMARY_VERSION) {
|
|
|
|
FdoExtension->FdcType = FDC_TYPE_NATIONAL;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine if the controller is an 82077 by issuing the perpendicular
|
|
// mode command which at this time is only valid on 82077's.
|
|
|
|
if ( FdoExtension->FdcType == FDC_TYPE_ENHANCED &&
|
|
NT_SUCCESS( ntStatus ) ) {
|
|
|
|
FdoExtension->FifoBuffer[0] = COMMND_PERPENDICULAR_MODE;
|
|
FdoExtension->FifoBuffer[1] = COMMND_PERPENDICULAR_MODE_OW;
|
|
|
|
ntStatus = FcIssueCommand( FdoExtension,
|
|
FdoExtension->FifoBuffer,
|
|
FdoExtension->FifoBuffer,
|
|
NULL,
|
|
0,
|
|
0 );
|
|
|
|
if (ntStatus != STATUS_DEVICE_NOT_READY) {
|
|
|
|
FdoExtension->FdcType = FDC_TYPE_82077;
|
|
|
|
}
|
|
}
|
|
|
|
// Determine if the controller is an Intel 82078 by issuing the part id
|
|
// command which is specific to Intel 82078 parts.
|
|
|
|
if ( FdoExtension->FdcType == FDC_TYPE_82077 &&
|
|
NT_SUCCESS( ntStatus ) ) {
|
|
|
|
FdoExtension->FifoBuffer[0] = COMMND_PART_ID;
|
|
|
|
ntStatus = FcIssueCommand( FdoExtension,
|
|
FdoExtension->FifoBuffer,
|
|
FdoExtension->FifoBuffer,
|
|
NULL,
|
|
0,
|
|
0 );
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
if ((FdoExtension->FifoBuffer[0] & INTEL_MASK) ==
|
|
INTEL_64_PIN_VERSION) {
|
|
|
|
FdoExtension->FdcType = FDC_TYPE_82078_64;
|
|
} else {
|
|
if ((FdoExtension->FifoBuffer[0] & INTEL_MASK) ==
|
|
INTEL_44_PIN_VERSION) {
|
|
|
|
FdoExtension->FdcType = FDC_TYPE_82078_44;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (FdoExtension->FdcType) {
|
|
|
|
case FDC_TYPE_UNKNOWN :
|
|
case FDC_TYPE_NORMAL :
|
|
case FDC_TYPE_ENHANCED :
|
|
default:
|
|
|
|
FdoExtension->FdcSpeeds = FDC_SPEED_250KB |
|
|
FDC_SPEED_300KB |
|
|
FDC_SPEED_500KB;
|
|
break;
|
|
|
|
case FDC_TYPE_82077 :
|
|
case FDC_TYPE_82077AA :
|
|
case FDC_TYPE_82078_44 :
|
|
case FDC_TYPE_NATIONAL :
|
|
|
|
FdoExtension->FdcSpeeds = FDC_SPEED_250KB |
|
|
FDC_SPEED_300KB |
|
|
FDC_SPEED_500KB |
|
|
FDC_SPEED_1MB;
|
|
break;
|
|
|
|
case FDC_TYPE_82078_64 :
|
|
|
|
FdoExtension->FdcSpeeds = FDC_SPEED_250KB |
|
|
FDC_SPEED_300KB |
|
|
FDC_SPEED_500KB |
|
|
FDC_SPEED_1MB;
|
|
|
|
if ( FdoExtension->Clock48MHz ) {
|
|
|
|
FdoExtension->FdcSpeeds |= FDC_SPEED_2MB;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
FdcDump( FDCINFO, ("Fdc: FdcType - %x\n", FdoExtension->FdcType));
|
|
|
|
return ntStatus;
|
|
}
|
|
#define IO_PORT_REQ_MASK 0xbc
|
|
|
|
NTSTATUS
|
|
FdcFilterResourceRequirements(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine examines the supplied resource list and adds resources if
|
|
necessary. The only resources that it is concerned with adding are io port
|
|
resources. Adding io port resources is necessary because of different bios
|
|
configurations and specifications.
|
|
|
|
The PC97(98) hardware specification defines only 3f2, 3f4, and 3f5 as
|
|
io port resources for standard floppy controllers (based on IBM PC floppy
|
|
controller configurations). In addition to these resources, fdc.sys
|
|
requires 3f7 for disk change detection and data rate programming and
|
|
optionally 3f3 for floppy tape support. In addition, some bioses define
|
|
aliased resources (e.g. 3f2 & 7f2, etc.)
|
|
|
|
This routine first forwards the irp to the underlying PDO. Upon return,
|
|
it examines the io resource list to determine if any additional resources
|
|
will be required. It maintains a linked list of all io port base addresses
|
|
that it encounters, assuming that they define aliased resources. N.B. - if
|
|
alternative lists are present in the io resource requirements list, only the
|
|
first list is examined. If additional resources are required a new io
|
|
resource list is created. The first io resource list in the new resource
|
|
requirements list will contain the original resources as well as the
|
|
additional resources required. If it was necessary to request the tape mode
|
|
register (3f3), i.e. 3f3 was not in the original list, a second list is
|
|
generated that is identical to the first new list except that 3f3 is excluded.
|
|
This list is for the case where the tape mode register is not available.
|
|
Finally, the original list(s) is(are) copied to the end of the new list and
|
|
are treated as alternative io resource lists.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object being started.
|
|
Irp - a pointer to the start device Irp.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KEVENT doneEvent;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST resourceRequirementsIn;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST resourceRequirementsOut;
|
|
ULONG listSize;
|
|
ULONG i,j;
|
|
PIO_RESOURCE_LIST ioResourceListIn;
|
|
PIO_RESOURCE_LIST ioResourceListOut;
|
|
PIO_RESOURCE_DESCRIPTOR ioResourceDescriptorIn;
|
|
PIO_RESOURCE_DESCRIPTOR ioResourceDescriptorOut;
|
|
LIST_ENTRY ioPortList;
|
|
PLIST_ENTRY links;
|
|
PIO_PORT_INFO ioPortInfo;
|
|
BOOLEAN foundBase;
|
|
ULONG newDescriptors;
|
|
BOOLEAN interruptResource = FALSE;
|
|
BOOLEAN dmaResource = FALSE;
|
|
UCHAR newPortMask;
|
|
BOOLEAN requestTapeModeRegister = FALSE;
|
|
USHORT in,out;
|
|
|
|
#ifdef TOSHIBAJ
|
|
BOOLEAN foundConfigPort = FALSE;
|
|
struct {
|
|
ULONG start;
|
|
ULONG length;
|
|
} configNewPort = {0, 0};
|
|
#endif
|
|
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
ntStatus = STATUS_SUCCESS;
|
|
InitializeListHead( &ioPortList );
|
|
|
|
FdcDump( FDCSHOW, ("FdcFdoPnp: IRP_MN_FILTER_RESOURCE_REQUIREMENTS - Irp: %p\n", Irp) );
|
|
|
|
//
|
|
// Pass this irp down to the PDO before proceeding.
|
|
//
|
|
KeInitializeEvent( &doneEvent, NotificationEvent, FALSE );
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
|
|
IoSetCompletionRoutine( Irp,
|
|
FdcPnpComplete,
|
|
&doneEvent,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
if ( ntStatus == STATUS_PENDING ) {
|
|
|
|
KeWaitForSingleObject( &doneEvent, Executive, KernelMode, FALSE, NULL );
|
|
}
|
|
|
|
//
|
|
// Modified resources are returned in Irp-IoStatus.Information, otherwise
|
|
// just use what's in the parameter list.
|
|
//
|
|
if ( Irp->IoStatus.Information == 0 ) {
|
|
|
|
Irp->IoStatus.Information = (UINT_PTR)irpSp->Parameters.FilterResourceRequirements.IoResourceRequirementList;
|
|
|
|
if ( Irp->IoStatus.Information == (UINT_PTR)NULL ) {
|
|
//
|
|
// NULL List, the PDO freed the incoming resource list but did not
|
|
// provide a new list. Complete the IRP with the PDO's status.
|
|
//
|
|
ntStatus = Irp->IoStatus.Status;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return( ntStatus );
|
|
}
|
|
|
|
}
|
|
|
|
resourceRequirementsIn = (PIO_RESOURCE_REQUIREMENTS_LIST)Irp->IoStatus.Information;
|
|
|
|
FdcDump( FDCSHOW, ("Resource Requirements List = %p\n", resourceRequirementsIn) );
|
|
|
|
if (IsNEC_98) {
|
|
//
|
|
// It is not necessary to modify the resources.
|
|
//
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Make a pass through the resource list and determine what resources are
|
|
// already there as well as the base address for the io port and any
|
|
// alias ioports.
|
|
//
|
|
ioResourceListIn = resourceRequirementsIn->List;
|
|
ioResourceDescriptorIn = ioResourceListIn->Descriptors;
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: examining %d resources\n", ioResourceListIn->Count));
|
|
|
|
for ( i = 0; i < ioResourceListIn->Count && NT_SUCCESS(ntStatus); i++ ) {
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: IoResourceDescritporIn = %p\n",ioResourceDescriptorIn));
|
|
|
|
switch ( ioResourceDescriptorIn->Type ) {
|
|
|
|
case CmResourceTypeInterrupt:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Found Interrupt Resource\n"));
|
|
interruptResource = TRUE;
|
|
break;
|
|
|
|
case CmResourceTypeDma:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Found Dma Resource \n"));
|
|
dmaResource = TRUE;
|
|
break;
|
|
|
|
case CmResourceTypePort:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Found Port Resource\n"));
|
|
//
|
|
// For the ioPorts we will make a list containing each detected
|
|
// 'base' address as well as the currently allocated addresses
|
|
// on that base. Later we will use this to request additional
|
|
// resources if necessary.
|
|
//
|
|
// First, if this base isn't already in the list, create a new
|
|
// list entry for it.
|
|
//
|
|
|
|
foundBase = FALSE;
|
|
|
|
for ( links = ioPortList.Flink;
|
|
links != &ioPortList;
|
|
links = links->Flink) {
|
|
|
|
ioPortInfo = CONTAINING_RECORD(links, IO_PORT_INFO, ListEntry);
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Examining %p for match\n",ioPortInfo));
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Base Address = %08x\n",ioPortInfo->BaseAddress.LowPart));
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Desc Address = %08x\n",ioResourceDescriptorIn->u.Port.MinimumAddress.LowPart & 0xfffffff8));
|
|
|
|
if ( ioPortInfo->BaseAddress.LowPart ==
|
|
(ioResourceDescriptorIn->u.Port.MinimumAddress.LowPart & 0xfffffff8) ) {
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Found %08x in the ioPortList\n",ioResourceDescriptorIn->u.Port.MinimumAddress.LowPart));
|
|
|
|
foundBase = TRUE;
|
|
//
|
|
// Add these resources into the resource map for this base
|
|
// address.
|
|
//
|
|
for ( j = 0; j < ioResourceDescriptorIn->u.Port.Length; j++ ) {
|
|
|
|
ioPortInfo->Map |= 0x01 << ((ioResourceDescriptorIn->u.Port.MinimumAddress.LowPart & 0x07) + j);
|
|
}
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: New IoPortInfo->Map = %x\n",ioPortInfo->Map));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !foundBase ) {
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Creating new ioPortList entry for %08x\n",ioResourceDescriptorIn->u.Port.MinimumAddress.LowPart));
|
|
ioPortInfo = ExAllocatePool( PagedPool, sizeof(IO_PORT_INFO) );
|
|
if ( ioPortInfo == NULL ) {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
RtlZeroMemory( ioPortInfo, sizeof(IO_PORT_INFO) );
|
|
ioPortInfo->BaseAddress = ioResourceDescriptorIn->u.Port.MinimumAddress;
|
|
ioPortInfo->BaseAddress.LowPart &= 0xfffffff8;
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Base Address = %08x\n",ioPortInfo->BaseAddress.LowPart));
|
|
for ( j = 0; j < ioResourceDescriptorIn->u.Port.Length; j++ ) {
|
|
ioPortInfo->Map |= 0x01 << ((ioResourceDescriptorIn->u.Port.MinimumAddress.LowPart & 0x07) + j);
|
|
}
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: New IoPortInfo->Map = %x\n",ioPortInfo->Map));
|
|
InsertTailList( &ioPortList, &ioPortInfo->ListEntry );
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Found unknown resource\n"));
|
|
break;
|
|
}
|
|
ioResourceDescriptorIn++;
|
|
}
|
|
|
|
//
|
|
// If we didn't see any io port resources, we will just return now
|
|
// since we can't be sure of what to ask for. The subsequent start
|
|
// device will surely fail. This also goes for the interrupt and
|
|
// dma resource.
|
|
//
|
|
if ( !NT_SUCCESS(ntStatus) ||
|
|
IsListEmpty( &ioPortList ) ||
|
|
!interruptResource ||
|
|
!dmaResource ) {
|
|
//
|
|
// Clean up the ioPortInfo list
|
|
//
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Bad Resources, Go directly to jail\n"));
|
|
while ( !IsListEmpty( &ioPortList ) ) {
|
|
links = RemoveHeadList( &ioPortList );
|
|
ioPortInfo = CONTAINING_RECORD(links, IO_PORT_INFO, ListEntry);
|
|
ExFreePool( ioPortInfo );
|
|
}
|
|
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef TOSHIBAJ
|
|
if (SmcConfigBase) {
|
|
PHYSICAL_ADDRESS configPort;
|
|
ULONG ioSpace;
|
|
|
|
// Map I/O port
|
|
configPort.QuadPart = 0;
|
|
configPort.LowPart = SmcConfigBase;
|
|
ioSpace = 1; // I/O port
|
|
if (HalTranslateBusAddress(resourceRequirementsIn->InterfaceType,
|
|
resourceRequirementsIn->BusNumber,
|
|
configPort,
|
|
&ioSpace,
|
|
&configPort)) {
|
|
TranslatedConfigBase = (PUCHAR)configPort.LowPart;
|
|
if (FcCheckConfigPort(TranslatedConfigBase)) {
|
|
FdcDump( FDCINFO,
|
|
("FdcFilterResourceRequirements: Configuration port %x\n",
|
|
TranslatedConfigBase) );
|
|
} else {
|
|
SmcConfigBase = 0;
|
|
TranslatedConfigBase = NULL;
|
|
}
|
|
} else {
|
|
SmcConfigBase = 0;
|
|
TranslatedConfigBase = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// At this point, we know what resources we are currently assigned so
|
|
// we can determine what additional resources we need to request. We
|
|
// need to know the size of the list we need to create so first count
|
|
// the number of resource descriptors we will have to add to the current
|
|
// list.
|
|
//
|
|
newDescriptors = 0;
|
|
|
|
for ( links = ioPortList.Flink;
|
|
links != &ioPortList;
|
|
links = links->Flink) {
|
|
|
|
ioPortInfo = CONTAINING_RECORD(links, IO_PORT_INFO, ListEntry);
|
|
|
|
newPortMask = ~ioPortInfo->Map & IO_PORT_REQ_MASK;
|
|
|
|
if ( newPortMask & 0x08 ) {
|
|
requestTapeModeRegister = TRUE;
|
|
}
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Counting bits in %x\n",newPortMask));
|
|
|
|
while ( newPortMask > 0 ) {
|
|
if ( newPortMask & 0x01 ) {
|
|
newDescriptors++;
|
|
}
|
|
newPortMask >>= 1;
|
|
}
|
|
|
|
#ifdef TOSHIBAJ
|
|
// Are there configuration ports in assigned resources ?
|
|
if (SmcConfigBase && (ioPortInfo->BaseAddress.LowPart == SmcConfigBase)) {
|
|
foundConfigPort = TRUE;
|
|
if (!(ioPortInfo->Map & 0x01)) {
|
|
configNewPort.start = SmcConfigBase;
|
|
++configNewPort.length;
|
|
}
|
|
if (!(ioPortInfo->Map & 0x02)) {
|
|
if (!configNewPort.start) {
|
|
configNewPort.start = SmcConfigBase + 1;
|
|
}
|
|
configNewPort.length++;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef TOSHIBAJ
|
|
// Deteremine the address and length of the additional descriptor
|
|
// for configuration ports.
|
|
if (SmcConfigBase && !foundConfigPort) {
|
|
configNewPort.start = SmcConfigBase;
|
|
configNewPort.length = 2;
|
|
}
|
|
if (configNewPort.start) {
|
|
newDescriptors++;
|
|
}
|
|
#endif
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Create %d new descriptors\n", newDescriptors) );
|
|
|
|
//
|
|
// If we need resources that were not in the list, we will need to
|
|
// allocate a new resource requirements list that includes these
|
|
// new resources.
|
|
//
|
|
if ( newDescriptors > 0 ) {
|
|
|
|
//
|
|
// Allocate and initialize a resource requirements list. Make it big
|
|
// enough to hold whatever was in the list to start with along with
|
|
// the new resource list.
|
|
//
|
|
listSize = resourceRequirementsIn->ListSize +
|
|
resourceRequirementsIn->ListSize +
|
|
newDescriptors * sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
|
|
//
|
|
// If we will be requesting the tape mode register we will need to
|
|
// make an alternate list without it in case we cannot get it.
|
|
//
|
|
if ( requestTapeModeRegister ) {
|
|
|
|
listSize = listSize +
|
|
resourceRequirementsIn->ListSize +
|
|
newDescriptors * sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
}
|
|
|
|
resourceRequirementsOut = ExAllocatePool( NonPagedPool, listSize );
|
|
|
|
if ( resourceRequirementsOut == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( resourceRequirementsOut, listSize);
|
|
|
|
//
|
|
// Initialize the IO_RESOURCE_REQUIREMENTS_LIST header.
|
|
//
|
|
resourceRequirementsOut->ListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) -
|
|
sizeof(IO_RESOURCE_LIST);
|
|
resourceRequirementsOut->InterfaceType = resourceRequirementsIn->InterfaceType;
|
|
resourceRequirementsOut->BusNumber = resourceRequirementsIn->BusNumber;
|
|
resourceRequirementsOut->SlotNumber = resourceRequirementsIn->SlotNumber;
|
|
resourceRequirementsOut->Reserved[0] = resourceRequirementsIn->Reserved[0];
|
|
resourceRequirementsOut->Reserved[1] = resourceRequirementsIn->Reserved[1];
|
|
resourceRequirementsOut->Reserved[2] = resourceRequirementsIn->Reserved[2];
|
|
resourceRequirementsOut->AlternativeLists = resourceRequirementsIn->AlternativeLists + 1;
|
|
if ( requestTapeModeRegister ) {
|
|
++resourceRequirementsOut->AlternativeLists;
|
|
}
|
|
|
|
//
|
|
// Copy the primary list from the incoming IO_RESOURCE_REQUIREMENTS_LIST
|
|
// to the new list.
|
|
//
|
|
ioResourceListIn = resourceRequirementsIn->List;
|
|
ioResourceListOut = resourceRequirementsOut->List;
|
|
|
|
listSize = sizeof(IO_RESOURCE_LIST) +
|
|
(ioResourceListIn->Count - 1) * sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
RtlCopyMemory( ioResourceListOut, ioResourceListIn, listSize );
|
|
|
|
resourceRequirementsOut->ListSize += listSize;
|
|
|
|
//
|
|
// Add any additional resources that we are requesting.
|
|
//
|
|
ioResourceDescriptorOut = (PIO_RESOURCE_DESCRIPTOR)((ULONG_PTR)resourceRequirementsOut +
|
|
resourceRequirementsOut->ListSize);
|
|
for ( links = ioPortList.Flink;
|
|
links != &ioPortList;
|
|
links = links->Flink) {
|
|
|
|
ioPortInfo = CONTAINING_RECORD(links, IO_PORT_INFO, ListEntry);
|
|
|
|
newPortMask = ~ioPortInfo->Map & IO_PORT_REQ_MASK;
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Add resource desc for each bit in %x\n",newPortMask));
|
|
|
|
i = 0;
|
|
while ( newPortMask != 0 ) {
|
|
|
|
if ( newPortMask & 0x01 ) {
|
|
|
|
ioResourceDescriptorOut->Option = IO_RESOURCE_PREFERRED;
|
|
ioResourceDescriptorOut->Type = CmResourceTypePort;
|
|
ioResourceDescriptorOut->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
ioResourceDescriptorOut->Flags = CM_RESOURCE_PORT_IO;
|
|
|
|
ioResourceDescriptorOut->u.Port.Length = 1;
|
|
ioResourceDescriptorOut->u.Port.Alignment = 1;
|
|
ioResourceDescriptorOut->u.Port.MinimumAddress.QuadPart =
|
|
ioResourceDescriptorOut->u.Port.MaximumAddress.QuadPart =
|
|
ioPortInfo->BaseAddress.QuadPart + (ULONGLONG)i;
|
|
|
|
++ioResourceListOut->Count;
|
|
resourceRequirementsOut->ListSize += sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Add resource descriptor: %p\n",ioResourceDescriptorOut));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->Option = %x\n",ioResourceDescriptorOut->Option ));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->Type = %x\n",ioResourceDescriptorOut->Type ));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->ShareDisposition = %x\n",ioResourceDescriptorOut->ShareDisposition));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->Flags = %x\n",ioResourceDescriptorOut->Flags ));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->u.Port.Length = %x\n",ioResourceDescriptorOut->u.Port.Length ));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->u.Port.Alignment = %x\n",ioResourceDescriptorOut->u.Port.Alignment));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->u.Port.MinimumAddress.LowPart = %08x\n",ioResourceDescriptorOut->u.Port.MinimumAddress.LowPart));
|
|
|
|
ioResourceDescriptorOut++;
|
|
}
|
|
newPortMask >>= 1;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
#ifdef TOSHIBAJ
|
|
// Add the descriptor for configuration ports
|
|
if (configNewPort.start) {
|
|
ioResourceDescriptorOut->Option = IO_RESOURCE_PREFERRED;
|
|
ioResourceDescriptorOut->Type = CmResourceTypePort;
|
|
ioResourceDescriptorOut->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
ioResourceDescriptorOut->Flags = CM_RESOURCE_PORT_IO;
|
|
|
|
ioResourceDescriptorOut->u.Port.Length = configNewPort.length;
|
|
ioResourceDescriptorOut->u.Port.Alignment = 1;
|
|
ioResourceDescriptorOut->u.Port.MinimumAddress.QuadPart =
|
|
ioResourceDescriptorOut->u.Port.MaximumAddress.QuadPart = 0;
|
|
ioResourceDescriptorOut->u.Port.MinimumAddress.LowPart =
|
|
configNewPort.start;
|
|
ioResourceDescriptorOut->u.Port.MaximumAddress.LowPart =
|
|
configNewPort.start + configNewPort.length - 1;
|
|
|
|
++ioResourceListOut->Count;
|
|
resourceRequirementsOut->ListSize += sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Add resource descriptor: %p\n",ioResourceDescriptorOut));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->Option = %x\n",ioResourceDescriptorOut->Option ));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->Type = %x\n",ioResourceDescriptorOut->Type ));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->ShareDisposition = %x\n",ioResourceDescriptorOut->ShareDisposition));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->Flags = %x\n",ioResourceDescriptorOut->Flags ));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->u.Port.Length = %x\n",ioResourceDescriptorOut->u.Port.Length ));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->u.Port.Alignment = %x\n",ioResourceDescriptorOut->u.Port.Alignment));
|
|
FdcDump( FDCSHOW, (" ioResourceDescriptorOut->u.Port.MinimumAddress.LowPart = %08x\n",ioResourceDescriptorOut->u.Port.MinimumAddress.LowPart));
|
|
|
|
ioResourceDescriptorOut++;
|
|
}
|
|
#endif
|
|
|
|
if ( requestTapeModeRegister ) {
|
|
|
|
ioResourceListIn = ioResourceListOut;
|
|
ioResourceListOut = (PIO_RESOURCE_LIST)ioResourceDescriptorOut;
|
|
|
|
ioResourceListOut->Version = ioResourceListIn->Version;
|
|
ioResourceListOut->Revision = ioResourceListIn->Revision;
|
|
ioResourceListOut->Count = 0;
|
|
|
|
resourceRequirementsOut->ListSize += sizeof(IO_RESOURCE_LIST) -
|
|
sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
|
|
in = out = 0;
|
|
|
|
do {
|
|
|
|
if ( (ioResourceListIn->Descriptors[in].Type != CmResourceTypePort) ||
|
|
((ioResourceListIn->Descriptors[in].u.Port.MinimumAddress.LowPart & 0x07) != 0x03) ) {
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Add %08x to alternate list\n", resourceRequirementsOut->List[0].Descriptors[out]));
|
|
ioResourceListOut->Descriptors[out++] = ioResourceListIn->Descriptors[in++];
|
|
++ioResourceListOut->Count;
|
|
resourceRequirementsOut->ListSize += sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
} else {
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Don't add %08x to alternate list\n", resourceRequirementsOut->List[0].Descriptors[out]));
|
|
in++;
|
|
}
|
|
} while ( in < ioResourceListIn->Count );
|
|
}
|
|
|
|
//
|
|
// Copy the original list(s) to the end of our new list.
|
|
//
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Copy %d existing resource list(s)\n",resourceRequirementsIn->AlternativeLists));
|
|
ioResourceListIn = resourceRequirementsIn->List;
|
|
ioResourceListOut = (PIO_RESOURCE_LIST)((ULONG_PTR)resourceRequirementsOut +
|
|
resourceRequirementsOut->ListSize);
|
|
|
|
for ( in = 0; in < resourceRequirementsIn->AlternativeLists; in++ ) {
|
|
|
|
FdcDump( FDCSHOW, ("FdcFilterResourceRequirements: Copy list %p\n",ioResourceListIn));
|
|
|
|
listSize = sizeof(IO_RESOURCE_LIST) +
|
|
(ioResourceListIn->Count - 1) * sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
RtlCopyMemory( ioResourceListOut, ioResourceListIn, listSize );
|
|
|
|
ioResourceListOut = (PIO_RESOURCE_LIST)((ULONG_PTR)ioResourceListOut + listSize);
|
|
ioResourceListIn = (PIO_RESOURCE_LIST)((ULONG_PTR)ioResourceListIn + listSize);
|
|
resourceRequirementsOut->ListSize += listSize;
|
|
}
|
|
|
|
FdcDump( FDCSHOW, ("Resource Requirements List = %p\n", resourceRequirementsOut) );
|
|
|
|
Irp->IoStatus.Information = (UINT_PTR)resourceRequirementsOut;
|
|
|
|
//
|
|
// Free the caller's list
|
|
//
|
|
ExFreePool( resourceRequirementsIn );
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
//
|
|
// Clean up the ioPortInfo list
|
|
//
|
|
while ( !IsListEmpty( &ioPortList ) ) {
|
|
links = RemoveHeadList( &ioPortList );
|
|
ioPortInfo = CONTAINING_RECORD(links, IO_PORT_INFO, ListEntry);
|
|
ExFreePool( ioPortInfo );
|
|
}
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcQueryDeviceRelations(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will report any devices that have been enumerated on the
|
|
floppy controller. If we don't know of any devices yet we will
|
|
enumerate the registry hardware tree.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object being started.
|
|
Irp - a pointer to the start device Irp.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
PFDC_PDO_EXTENSION pdoExtension;
|
|
NTSTATUS ntStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
ULONG relationCount;
|
|
ULONG relationLength;
|
|
PDEVICE_RELATIONS relations;
|
|
PLIST_ENTRY entry;
|
|
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
FdcDump( FDCSHOW, ("FdcQueryDeviceRelations:\n"));
|
|
|
|
if ( irpSp->Parameters.QueryDeviceRelations.Type != BusRelations ) {
|
|
//
|
|
// We don't support this
|
|
//
|
|
FdcDump( FDCSHOW, ("FdcQueryDeviceRelations: Type = %d\n", irpSp->Parameters.QueryDeviceRelations.Type));
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Tell the plug and play system about all the PDOs.
|
|
//
|
|
// There might also be device relations below and above this FDO,
|
|
// so, be sure to propagate the relations from the upper drivers.
|
|
//
|
|
|
|
//
|
|
// The current number of PDOs
|
|
//
|
|
relationCount = ( Irp->IoStatus.Information == 0 ) ? 0 :
|
|
((PDEVICE_RELATIONS) Irp->IoStatus.Information)->Count;
|
|
|
|
//
|
|
// If we have not yet enumerated the hardware tree or all of our
|
|
// devices have been removed, enumerate it now.
|
|
//
|
|
if ( fdoExtension->NumPDOs == 0 ) {
|
|
|
|
INTERFACE_TYPE InterfaceType;
|
|
//
|
|
// Query the registry hardware tree to find out how many floppy
|
|
// drives were reported by the firmware.
|
|
//
|
|
for ( InterfaceType = 0;
|
|
InterfaceType < MaximumInterfaceType;
|
|
InterfaceType++ ) {
|
|
|
|
CONFIGURATION_TYPE Dc = DiskController;
|
|
CONFIGURATION_TYPE Fp = FloppyDiskPeripheral;
|
|
|
|
ntStatus = IoQueryDeviceDescription(&InterfaceType,
|
|
NULL,
|
|
&Dc,
|
|
NULL,
|
|
&Fp,
|
|
NULL,
|
|
FdcConfigCallBack,
|
|
fdoExtension );
|
|
|
|
if (!NT_SUCCESS(ntStatus) && (ntStatus != STATUS_OBJECT_NAME_NOT_FOUND)) {
|
|
|
|
return ntStatus;
|
|
}
|
|
}
|
|
}
|
|
|
|
FdcDump( FDCSHOW, ("FdcQueryDeviceRelations: My relations count - %d\n", fdoExtension->NumPDOs));
|
|
|
|
relationLength = sizeof(DEVICE_RELATIONS) +
|
|
(relationCount + fdoExtension->NumPDOs) * sizeof (PDEVICE_OBJECT);
|
|
|
|
relations = (PDEVICE_RELATIONS) ExAllocatePool (NonPagedPool, relationLength);
|
|
|
|
if ( relations == NULL ) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Copy in the device objects so far
|
|
//
|
|
if ( relationCount ) {
|
|
RtlCopyMemory( relations->Objects,
|
|
((PDEVICE_RELATIONS) Irp->IoStatus.Information)->Objects,
|
|
relationCount * sizeof (PDEVICE_OBJECT));
|
|
}
|
|
relations->Count = relationCount + fdoExtension->NumPDOs;
|
|
|
|
//
|
|
// For each PDO on this bus add a pointer to the device relations
|
|
// buffer, being sure to take out a reference to that object.
|
|
// The PlugPlay system will dereference the object when it is done with
|
|
// it and free the device relations buffer.
|
|
//
|
|
for (entry = fdoExtension->PDOs.Flink;
|
|
entry != &fdoExtension->PDOs;
|
|
entry = entry->Flink, relationCount++) {
|
|
|
|
pdoExtension = CONTAINING_RECORD( entry, FDC_PDO_EXTENSION, PdoLink );
|
|
relations->Objects[relationCount] = pdoExtension->Self;
|
|
ObReferenceObject( pdoExtension->Self );
|
|
}
|
|
|
|
//
|
|
// Set up and pass the IRP further down the stack
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
if ( Irp->IoStatus.Information != 0) {
|
|
|
|
ExFreePool ((PVOID) Irp->IoStatus.Information);
|
|
}
|
|
|
|
Irp->IoStatus.Information = (UINT_PTR) relations;
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
ntStatus = IoCallDriver( fdoExtension->TargetObject, Irp );
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FdcConfigCallBack(
|
|
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:
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to our FDO extension
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
|
|
PFDC_FDO_EXTENSION fdoExtension = (PFDC_FDO_EXTENSION)Context;
|
|
NTSTATUS ntStatus;
|
|
UNICODE_STRING pdoName;
|
|
WCHAR pdoNameBuffer[32];
|
|
PDEVICE_OBJECT newPdo;
|
|
PFDC_PDO_EXTENSION pdoExtension;
|
|
USHORT floppyCount;
|
|
|
|
FdcDump( FDCSHOW, ("FdcConfigCallBack:\n") );
|
|
|
|
//
|
|
// Verify that this floppy disk drive is on the current
|
|
// floppy disk controller.
|
|
//
|
|
{
|
|
USHORT i;
|
|
BOOLEAN thisController = FALSE;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR controllerData =
|
|
(PCM_FULL_RESOURCE_DESCRIPTOR)
|
|
(((PUCHAR)ControllerInformation[IoQueryDeviceConfigurationData]) +
|
|
ControllerInformation[IoQueryDeviceConfigurationData]->DataOffset);
|
|
|
|
for ( i = 0;
|
|
i < controllerData->PartialResourceList.Count;
|
|
i++ ) {
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial =
|
|
&controllerData->PartialResourceList.PartialDescriptors[i];
|
|
|
|
FdcDump( FDCSHOW, ("FdcConfigCallBack: resource type = %x\n",partial->Type) );
|
|
|
|
switch (partial->Type) {
|
|
|
|
case CmResourceTypePort: {
|
|
|
|
PUCHAR address;
|
|
|
|
address = FdcGetControllerBase( BusType,
|
|
BusNumber,
|
|
partial->u.Port.Start,
|
|
partial->u.Port.Length,
|
|
(BOOLEAN)!!partial->Flags );
|
|
|
|
FdcDump( FDCSHOW, ("FdcConfigCallBack: DriveControl = %04x %04x\n",fdoExtension->ControllerAddress.DriveControl,address + (IsNEC_98 ? 4 : 2) ));
|
|
if ( fdoExtension->ControllerAddress.DriveControl == address + (IsNEC_98 ? 4 : 2)) {
|
|
thisController = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
if ( !thisController ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
floppyCount = (USHORT)(IoGetConfigurationInformation()->FloppyCount);
|
|
swprintf(pdoNameBuffer, L"\\Device\\FloppyPDO%d", floppyCount);
|
|
RtlInitUnicodeString(&pdoName, pdoNameBuffer);
|
|
|
|
ntStatus = IoCreateDevice( fdoExtension->Self->DriverObject,
|
|
sizeof(FDC_PDO_EXTENSION),
|
|
&pdoName,
|
|
FILE_DEVICE_DISK,
|
|
(FILE_REMOVABLE_MEDIA |
|
|
FILE_FLOPPY_DISKETTE |
|
|
FILE_DEVICE_SECURE_OPEN),
|
|
FALSE,
|
|
&newPdo);
|
|
|
|
if ( !NT_SUCCESS(ntStatus) ) {
|
|
|
|
FdcDump( FDCSHOW, ("FdcConfigCallBack: Error - %08x\n", ntStatus) );
|
|
return ntStatus;
|
|
}
|
|
|
|
FdcDump( FDCSHOW, ("FdcConfigCallBack: Created Device %d\n", floppyCount) );
|
|
|
|
IoGetConfigurationInformation()->FloppyCount += 1;
|
|
|
|
pdoExtension = (PFDC_PDO_EXTENSION) newPdo->DeviceExtension;
|
|
|
|
pdoExtension->TargetObject = fdoExtension->Self;
|
|
|
|
pdoExtension->IsFDO = FALSE;
|
|
pdoExtension->Self = newPdo;
|
|
pdoExtension->DeviceType = FloppyDiskDevice;
|
|
|
|
pdoExtension->ParentFdo = fdoExtension->Self;
|
|
|
|
pdoExtension->Instance = floppyCount + 1;
|
|
pdoExtension->Removed = FALSE; // no irp_mn_remove as of yet
|
|
|
|
fdoExtension->BusType = BusType;
|
|
fdoExtension->BusNumber = BusNumber;
|
|
fdoExtension->ControllerNumber = ControllerNumber;
|
|
pdoExtension->PeripheralNumber = PeripheralNumber;
|
|
|
|
newPdo->Flags |= DO_DIRECT_IO;
|
|
newPdo->Flags |= DO_POWER_PAGABLE;
|
|
newPdo->StackSize += fdoExtension->Self->StackSize;
|
|
newPdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
InsertTailList(&fdoExtension->PDOs, &pdoExtension->PdoLink);
|
|
fdoExtension->NumPDOs++;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcCreateClose(
|
|
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 );
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("FdcCreateClose...\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
|
|
FdcInternalDeviceControl (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if this Pnp request is directed towards an FDO or a PDO and
|
|
pass the Irp on the the appropriate routine.
|
|
|
|
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;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PFDC_EXTENSION_HEADER extensionHeader;
|
|
KIRQL oldIrq;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
extensionHeader = (PFDC_EXTENSION_HEADER)DeviceObject->DeviceExtension;
|
|
|
|
if ( extensionHeader->IsFDO ) {
|
|
|
|
ntStatus = FdcFdoInternalDeviceControl( DeviceObject, Irp );
|
|
|
|
} else {
|
|
|
|
ntStatus = FdcPdoInternalDeviceControl( DeviceObject, Irp );
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcPdoInternalDeviceControl(
|
|
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.
|
|
|
|
Most irps are put onto the driver queue (IoStartPacket). Some irps do not
|
|
require touching the hardware and are handled right here.
|
|
|
|
In some cases the irp cannot be put on the queue because it cannot be
|
|
completed at IRQL_DISPATCH_LEVEL. However, the driver queue must be empty
|
|
before the irp can be completed. In these cases, the queue is
|
|
'synchronized' before completing the irp.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDC_PDO_EXTENSION pdoExtension;
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
BOOLEAN isFDO;
|
|
NTSTATUS ntStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PIO_STACK_LOCATION nextIrpSp;
|
|
PISSUE_FDC_ADAPTER_BUFFER_PARMS adapterBufferParms;
|
|
|
|
pdoExtension = (PFDC_PDO_EXTENSION)DeviceObject->DeviceExtension;
|
|
fdoExtension = (PFDC_FDO_EXTENSION)pdoExtension->ParentFdo->DeviceExtension;
|
|
|
|
if ( pdoExtension->Removed) {
|
|
//
|
|
// This bus has received the PlugPlay remove IRP. It will no longer
|
|
// respond to external requests.
|
|
//
|
|
ntStatus = STATUS_DELETE_PENDING;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
|
return ntStatus;
|
|
}
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
FdcDump( FDCSHOW,
|
|
("FdcPdoInternalDeviceControl: %x\n",
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode) );
|
|
|
|
switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) {
|
|
|
|
case IOCTL_DISK_INTERNAL_GET_ENABLER: {
|
|
|
|
if ( pdoExtension->DeviceType == FloppyControllerDevice ) {
|
|
|
|
*(PBOOLEAN)irpSp->Parameters.DeviceIoControl.Type3InputBuffer = TRUE;
|
|
|
|
} else {
|
|
|
|
*(PBOOLEAN)irpSp->Parameters.DeviceIoControl.Type3InputBuffer = FALSE;
|
|
}
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_DISK_INTERNAL_GET_FDC_INFO:
|
|
|
|
FcReportFdcInformation( pdoExtension, fdoExtension, irpSp );
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
#ifdef TOSHIBAJ
|
|
case IOCTL_DISK_INTERNAL_ENABLE_3_MODE:
|
|
FdcDump(FDCSHOW,("IOCTL_Enable_3_MODE\n"));
|
|
ntStatus = FcFdcEnable3Mode( fdoExtension , Irp );
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_AVAILABLE_3_MODE:
|
|
FdcDump(FDCSHOW,("IOCTL_Availabe_3_MODE\n"));
|
|
ntStatus = FcFdcAvailable3Mode( fdoExtension , Irp );
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
//
|
|
// Call the driver and request the operation
|
|
//
|
|
return IoCallDriver( pdoExtension->TargetObject, Irp );
|
|
}
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FdcFdoInternalDeviceControl(
|
|
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.
|
|
|
|
Most irps are put onto the driver queue (IoStartPacket). Some irps do not
|
|
require touching the hardware and are handled right here.
|
|
|
|
In some cases the irp cannot be put on the queue because it cannot be
|
|
completed at IRQL_DISPATCH_LEVEL. However, the driver queue must be empty
|
|
before the irp can be completed. In these cases, the queue is
|
|
'synchronized' before completing the irp.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
NTSTATUS ntStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PIO_STACK_LOCATION nextIrpSp;
|
|
PISSUE_FDC_ADAPTER_BUFFER_PARMS adapterBufferParms;
|
|
BOOLEAN powerQueueClear = FALSE;
|
|
PLIST_ENTRY deferredRequest;
|
|
PIRP currentIrp;
|
|
ULONG ioControlCode;
|
|
PFDC_DISK_CHANGE_PARMS fdcDiskChangeParms;
|
|
PUCHAR dataRate;
|
|
UCHAR tapeMode;
|
|
PUCHAR precomp;
|
|
PISSUE_FDC_COMMAND_PARMS issueCommandParms;
|
|
PSET_HD_BIT_PARMS setHdBitParams;
|
|
|
|
fdoExtension = (PFDC_FDO_EXTENSION)DeviceObject->DeviceExtension;
|
|
|
|
InterlockedIncrement( &fdoExtension->OutstandingRequests );
|
|
|
|
if ( fdoExtension->Removed ) {
|
|
//
|
|
// This device has received the PlugPlay remove IRP. It will no longer
|
|
// respond to external requests.
|
|
//
|
|
if ( InterlockedDecrement(&fdoExtension->OutstandingRequests ) == 0 ) {
|
|
KeSetEvent( &fdoExtension->RemoveEvent, 0, FALSE );
|
|
}
|
|
ntStatus = STATUS_DELETE_PENDING;
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// If we are in a non-working power state then just queue the irp
|
|
// for later execution.
|
|
//
|
|
if ( fdoExtension->CurrentPowerState > PowerSystemWorking ) {
|
|
|
|
ExInterlockedInsertTailList( &fdoExtension->PowerQueue,
|
|
&Irp->Tail.Overlay.ListEntry,
|
|
&fdoExtension->PowerQueueSpinLock );
|
|
|
|
ntStatus = STATUS_PENDING;
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
return ntStatus;
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
deferredRequest = ExInterlockedRemoveHeadList( &fdoExtension->PowerQueue,
|
|
&fdoExtension->PowerQueueSpinLock );
|
|
|
|
if ( deferredRequest == NULL ) {
|
|
|
|
currentIrp = Irp;
|
|
powerQueueClear = TRUE;
|
|
|
|
} else {
|
|
|
|
currentIrp = CONTAINING_RECORD( deferredRequest, IRP, Tail.Overlay.ListEntry );
|
|
}
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( currentIrp );
|
|
|
|
FdcDump( FDCSHOW,
|
|
("FdcFdoInternalDeviceControl: %x\n",
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode) );
|
|
|
|
ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
|
|
|
|
//
|
|
// GET_ENABLER and GET_FDC_INFO are handled in the PDO, not the FDO.
|
|
//
|
|
if ( ioControlCode == IOCTL_DISK_INTERNAL_GET_ENABLER ||
|
|
ioControlCode == IOCTL_DISK_INTERNAL_GET_FDC_INFO ) {
|
|
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
//
|
|
// If the controller is not acquired (in use) then then only
|
|
// operation that is allowed is to acquire the fdc.
|
|
//
|
|
} else if ( !fdoExtension->ControllerInUse &&
|
|
ioControlCode != IOCTL_DISK_INTERNAL_ACQUIRE_FDC ) {
|
|
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
} else {
|
|
|
|
switch ( ioControlCode ) {
|
|
|
|
case IOCTL_DISK_INTERNAL_ACQUIRE_FDC:
|
|
|
|
//
|
|
// Try to Acquire the Fdc. If the Fdc is busy, this call will
|
|
// time out.
|
|
//
|
|
ntStatus = FcAcquireFdc(
|
|
fdoExtension,
|
|
(PLARGE_INTEGER)irpSp->
|
|
Parameters.DeviceIoControl.Type3InputBuffer );
|
|
//
|
|
// Return the device object of the last device that called this
|
|
// driver. This can be used to determine if any other drivers
|
|
// have messed with the fdc since it was last acquired.
|
|
//
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer =
|
|
fdoExtension->LastDeviceObject;
|
|
}
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_ENABLE_FDC_DEVICE:
|
|
|
|
//
|
|
// Turn the motor on and select a floppy channel
|
|
//
|
|
ntStatus = FcTurnOnMotor( fdoExtension, irpSp );
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_ISSUE_FDC_COMMAND:
|
|
|
|
issueCommandParms =
|
|
(PISSUE_FDC_COMMAND_PARMS)
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
ntStatus = FcIssueCommand( fdoExtension,
|
|
issueCommandParms->FifoInBuffer,
|
|
issueCommandParms->FifoOutBuffer,
|
|
issueCommandParms->IoHandle,
|
|
issueCommandParms->IoOffset,
|
|
issueCommandParms->TransferBytes );
|
|
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_ISSUE_FDC_COMMAND_QUEUED:
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
IoStartPacket( DeviceObject,
|
|
Irp,
|
|
NULL,
|
|
NULL );
|
|
|
|
ntStatus = STATUS_PENDING;
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_RESET_FDC:
|
|
|
|
ntStatus = FcInitializeControllerHardware( fdoExtension,
|
|
DeviceObject );
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_RELEASE_FDC:
|
|
|
|
ntStatus = FcReleaseFdc( fdoExtension );
|
|
//
|
|
// Save the DeviceObject of the releasing device. This is
|
|
// returned with the subsequent acquire fdc request and
|
|
// can be used to determine whether the floppy controller
|
|
// has been messed with between release and acquisition
|
|
//
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
|
|
fdoExtension->LastDeviceObject =
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_GET_ADAPTER_BUFFER:
|
|
//
|
|
// Allocate an MDL for the passed in buffer.
|
|
//
|
|
adapterBufferParms = (PISSUE_FDC_ADAPTER_BUFFER_PARMS)
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
adapterBufferParms->Handle =
|
|
IoAllocateMdl( adapterBufferParms->IoBuffer,
|
|
adapterBufferParms->TransferBytes,
|
|
FALSE,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( adapterBufferParms->Handle == NULL ) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
MmBuildMdlForNonPagedPool( adapterBufferParms->Handle );
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_FLUSH_ADAPTER_BUFFER:
|
|
//
|
|
// Free the MDL
|
|
//
|
|
adapterBufferParms = (PISSUE_FDC_ADAPTER_BUFFER_PARMS)
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
if ( adapterBufferParms->Handle != NULL ) {
|
|
|
|
IoFreeMdl( adapterBufferParms->Handle );
|
|
}
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_FDC_START_READ:
|
|
case IOCTL_DISK_INTERNAL_FDC_START_WRITE:
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
if ( fdoExtension->FdcEnablerSupported ) {
|
|
|
|
FDC_MODE_SELECT fdcModeSelect;
|
|
|
|
fdcModeSelect.structSize = sizeof(fdcModeSelect);
|
|
//
|
|
// Reading from the media means writing to DMA memory and
|
|
// visa-versa for writing to the media.
|
|
//
|
|
if ( irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_DISK_INTERNAL_FDC_START_READ ) {
|
|
|
|
fdcModeSelect.DmaDirection = FDC_WRITE_TO_MEMORY;
|
|
|
|
} else {
|
|
|
|
fdcModeSelect.DmaDirection = FDC_READ_FROM_MEMORY;
|
|
}
|
|
|
|
ntStatus = FcFdcEnabler(
|
|
fdoExtension->FdcEnablerDeviceObject,
|
|
IOCTL_SET_FDC_MODE,
|
|
&fdcModeSelect);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_DISABLE_FDC_DEVICE:
|
|
|
|
ntStatus = FcTurnOffMotor( fdoExtension );
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_GET_FDC_DISK_CHANGE:
|
|
|
|
FdcDump(FDCINFO, ("Fdc: Read Disk Change\n") );
|
|
|
|
fdcDiskChangeParms =
|
|
(PFDC_DISK_CHANGE_PARMS)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
if (IsNEC_98) {
|
|
if((fdoExtension->ResultStatus0[fdcDiskChangeParms->DriveOnValue] &
|
|
STREG0_END_MASK) == STREG0_END_DRIVE_NOT_READY){
|
|
|
|
fdcDiskChangeParms->DriveStatus = DSKCHG_DISKETTE_REMOVED;
|
|
} else {
|
|
|
|
fdoExtension->ResultStatus0[fdcDiskChangeParms->DriveOnValue] = 0;
|
|
fdcDiskChangeParms->DriveStatus = DSKCHG_RESERVED;
|
|
}
|
|
} else { // (IsNEC_98)
|
|
fdcDiskChangeParms->DriveStatus = READ_CONTROLLER(
|
|
fdoExtension->ControllerAddress.DRDC.DiskChange );
|
|
//
|
|
// If we just waked up from hibernation, simulate a disk
|
|
// change event so the upper levels will be sure to check
|
|
// this disk.
|
|
//
|
|
if ( fdoExtension->WakeUp ) {
|
|
|
|
fdcDiskChangeParms->DriveStatus |= DSKCHG_DISKETTE_REMOVED;
|
|
fdoExtension->WakeUp = FALSE;
|
|
}
|
|
} // (IsNEC_98)
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_SET_FDC_DATA_RATE:
|
|
|
|
if (IsNEC_98) {
|
|
//
|
|
// NEC98 have no function and have no DRDC.DataRate register.
|
|
//
|
|
} else { // (IsNEC_98)
|
|
dataRate =
|
|
(PUCHAR)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
FdcDump(FDCINFO, ("Fdc: Write Data Rate: %x\n", *dataRate) );
|
|
|
|
WRITE_CONTROLLER( fdoExtension->ControllerAddress.DRDC.DataRate,
|
|
*dataRate );
|
|
|
|
} // (IsNEC_98)
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_SET_FDC_TAPE_MODE:
|
|
|
|
if (IsNEC_98) {
|
|
//
|
|
// NEC98 have no Tape register.
|
|
//
|
|
} else { // (IsNEC_98)
|
|
|
|
tapeMode = READ_CONTROLLER( fdoExtension->ControllerAddress.Tape );
|
|
tapeMode &= 0xfc;
|
|
tapeMode |=
|
|
*((PUCHAR)irpSp->Parameters.DeviceIoControl.Type3InputBuffer);
|
|
|
|
FdcDump(FDCINFO,
|
|
("Fdc: Write Tape Mode Register: %x\n", tapeMode)
|
|
);
|
|
|
|
WRITE_CONTROLLER(
|
|
fdoExtension->ControllerAddress.Tape,
|
|
tapeMode );
|
|
|
|
} // (IsNEC_98)
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_SET_FDC_PRECOMP:
|
|
|
|
precomp = (PUCHAR)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
FdcDump(FDCINFO,
|
|
("Fdc: Write Precomp: %x\n", *precomp)
|
|
);
|
|
|
|
WRITE_CONTROLLER(
|
|
fdoExtension->ControllerAddress.Status,
|
|
*precomp );
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_INTERNAL_SET_HD_BIT:
|
|
|
|
if (IsNEC_98) {
|
|
|
|
FdcDump(FDCINFO,
|
|
("Fdc: Set Hd Bit: \n")
|
|
);
|
|
setHdBitParams = (PSET_HD_BIT_PARMS)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
FdcHdbit(DeviceObject, fdoExtension, setHdBitParams);
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
} // (IsNEC_98)
|
|
|
|
//
|
|
// If not NEC98, then pass through to "default:".
|
|
//
|
|
|
|
default:
|
|
//
|
|
// Mark the Irp pending and queue it.
|
|
//
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ntStatus != STATUS_PENDING ) {
|
|
|
|
if ( InterlockedDecrement(&fdoExtension->OutstandingRequests ) == 0 ) {
|
|
KeSetEvent( &fdoExtension->RemoveEvent, 0, FALSE );
|
|
}
|
|
currentIrp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest( currentIrp, IO_DISK_INCREMENT );
|
|
}
|
|
|
|
} while ( !powerQueueClear );
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
VOID
|
|
FcReportFdcInformation(
|
|
IN PFDC_PDO_EXTENSION PdoExtension,
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN OUT PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reports information about the Floppy Disk Controller
|
|
that a higher level driver might need; primarily information
|
|
regarding the DMA Adapter.
|
|
|
|
Arguments:
|
|
|
|
fdoExtension - Pointer to this device's extension data.
|
|
|
|
IrpSp - Pointer to the current Irp
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDC_INFO fdcInfo;
|
|
ULONG bufferCount;
|
|
ULONG bufferSize;
|
|
ULONG i;
|
|
|
|
FdcDump( FDCINFO, ("Fdc: Report FDC Information\n") );
|
|
|
|
fdcInfo = (PFDC_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
//
|
|
// save the requested buffer count and buffer size.
|
|
//
|
|
bufferCount = fdcInfo->BufferCount;
|
|
bufferSize = fdcInfo->BufferSize;
|
|
|
|
//
|
|
// fill in the floppy controller hardware information
|
|
//
|
|
fdcInfo->BusType = FdoExtension->BusType;
|
|
fdcInfo->BusNumber = FdoExtension->BusNumber;
|
|
fdcInfo->ControllerNumber = FdoExtension->ControllerNumber;
|
|
if (IsNEC_98) {
|
|
UCHAR floppyEquip;
|
|
ULONG disketteCount = 0;
|
|
|
|
floppyEquip = FdoExtension->FloppyEquip;
|
|
|
|
//
|
|
// Make PeripheralNumber.
|
|
//
|
|
for (i = 0 ; i < 4 ; i++) {
|
|
|
|
if ((floppyEquip & 0x1) != 0) {
|
|
|
|
disketteCount++;
|
|
|
|
if(disketteCount > PdoExtension->PeripheralNumber){
|
|
|
|
break;
|
|
}
|
|
}
|
|
floppyEquip = floppyEquip >> 1;
|
|
}
|
|
|
|
fdcInfo->UnitNumber = (UCHAR)i;
|
|
} else {
|
|
//
|
|
// Only NEC98 is using it now, put Zero into UnitNumber.
|
|
//
|
|
fdcInfo->UnitNumber = 0;
|
|
}
|
|
fdcInfo->PeripheralNumber = PdoExtension->PeripheralNumber;
|
|
|
|
fdcInfo->FloppyControllerType = FdoExtension->FdcType;
|
|
fdcInfo->SpeedsAvailable = FdoExtension->FdcSpeeds;
|
|
|
|
fdcInfo->MaxTransferSize = FdoExtension->NumberOfMapRegisters * PAGE_SIZE;
|
|
|
|
fdcInfo->BufferSize = 0;
|
|
fdcInfo->BufferCount = 0;
|
|
|
|
if ( bufferSize <= FdoExtension->BufferSize ) {
|
|
|
|
fdcInfo->BufferSize = bufferSize;
|
|
fdcInfo->BufferCount = MIN( bufferCount,
|
|
FdoExtension->BufferCount );
|
|
FdoExtension->BuffersRequested = MAX( fdcInfo->BufferCount,
|
|
FdoExtension->BuffersRequested );
|
|
}
|
|
|
|
for ( i = 0 ; i < fdcInfo->BufferCount ; i++ ) {
|
|
|
|
fdcInfo->BufferAddress[i].Logical =
|
|
FdoExtension->TransferBuffers[i].Logical;
|
|
fdcInfo->BufferAddress[i].Virtual =
|
|
FdoExtension->TransferBuffers[i].Virtual;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FdcStartIo(
|
|
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;
|
|
NTSTATUS ntStatus;
|
|
ULONG formatExParametersSize;
|
|
PUCHAR diskChange;
|
|
PUCHAR dataRate;
|
|
PUCHAR tapeMode;
|
|
PUCHAR precomp;
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
PISSUE_FDC_COMMAND_PARMS issueCommandParms;
|
|
PKDEVICE_QUEUE_ENTRY request;
|
|
|
|
FdcDump( FDCSHOW, ("FdcStartIo...\n") );
|
|
|
|
fdoExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
switch( irpSp->Parameters.DeviceIoControl.IoControlCode ) {
|
|
|
|
case IOCTL_DISK_INTERNAL_ISSUE_FDC_COMMAND_QUEUED:
|
|
|
|
issueCommandParms =
|
|
(PISSUE_FDC_COMMAND_PARMS)
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
if ( CommandTable[issueCommandParms->FifoInBuffer[0] &
|
|
COMMAND_MASK].InterruptExpected ) {
|
|
|
|
fdoExtension->CurrentDeviceObject = DeviceObject;
|
|
fdoExtension->AllowInterruptProcessing = TRUE;
|
|
fdoExtension->CommandHasResultPhase = FALSE;
|
|
fdoExtension->InterruptTimer =
|
|
issueCommandParms->TimeOut ?
|
|
issueCommandParms->TimeOut + 1 : START_TIMER;
|
|
fdoExtension->CurrentIrp = Irp;
|
|
|
|
}
|
|
|
|
ntStatus = FcStartCommand( fdoExtension,
|
|
issueCommandParms->FifoInBuffer,
|
|
issueCommandParms->FifoOutBuffer,
|
|
issueCommandParms->IoHandle,
|
|
issueCommandParms->IoOffset,
|
|
issueCommandParms->TransferBytes,
|
|
FALSE );
|
|
|
|
if ( NT_SUCCESS( ntStatus )) {
|
|
|
|
if ( CommandTable[issueCommandParms->FifoInBuffer[0] &
|
|
COMMAND_MASK].InterruptExpected ) {
|
|
|
|
ntStatus = STATUS_PENDING;
|
|
|
|
} else {
|
|
|
|
ntStatus = FcFinishCommand(
|
|
fdoExtension,
|
|
issueCommandParms->FifoInBuffer,
|
|
issueCommandParms->FifoOutBuffer,
|
|
issueCommandParms->IoHandle,
|
|
issueCommandParms->IoOffset,
|
|
issueCommandParms->TransferBytes,
|
|
FALSE );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: {
|
|
|
|
FdcDump(
|
|
FDCDBGP,
|
|
("Fdc: invalid device request %x\n",
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode)
|
|
);
|
|
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ntStatus != STATUS_PENDING ) {
|
|
Irp->IoStatus.Status = ntStatus;
|
|
if (!NT_SUCCESS( ntStatus ) &&
|
|
IoIsErrorUserInduced( ntStatus )) {
|
|
|
|
IoSetHardErrorOrVerifyDevice( Irp, DeviceObject );
|
|
}
|
|
|
|
IoStartNextPacket( DeviceObject, FALSE );
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
FcAcquireFdc(
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN PLARGE_INTEGER TimeOut
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine acquires the floppy disk controller. This includes
|
|
allocating the adapter channel and connecting the interrupt.
|
|
|
|
NOTE - This is where the sharing mechanism will be put into
|
|
this driver. That is, higher level drivers will 'reserve' the
|
|
floppy controller with this ioctl. Subsequent calls to this driver
|
|
that are not from the 'reserving' drive will be rejected with a
|
|
BUSY status.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object for the current device
|
|
|
|
Return Value:
|
|
|
|
STATUS_DEVICE_BUSY if we don't have the controller, otherwise
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus;
|
|
|
|
FdcDump(FDCINFO,
|
|
("Fdc: Acquire the Floppy Controller\n")
|
|
);
|
|
|
|
//
|
|
// Wait for the Fdc, either from the enabler or directly here. Semaphores
|
|
// are used to synchronize usage of the Fdc hardware. If somebody else is
|
|
// using the floppy controller now we must wait for them to finish. If
|
|
// this takes too long we will just let the caller know that the device is
|
|
// busy.
|
|
//
|
|
if (FdoExtension->FdcEnablerSupported) {
|
|
|
|
ntStatus = FcFdcEnabler( FdoExtension->FdcEnablerDeviceObject,
|
|
// IOCTL_ACQUIRE_FDC, // For spelling miss in flpyenbl.h
|
|
IOCTL_AQUIRE_FDC,
|
|
TimeOut);
|
|
} else {
|
|
|
|
ntStatus = KeWaitForSingleObject( FdoExtension->AcquireEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
TimeOut );
|
|
|
|
if ( ntStatus == STATUS_TIMEOUT ) {
|
|
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS(ntStatus) ) {
|
|
//
|
|
// Lock down the driver code in memory.
|
|
//
|
|
|
|
FDC_PAGE_RESET_DRIVER_WITH_MUTEX;
|
|
|
|
//
|
|
// Allocate the adapter channel
|
|
//
|
|
FcAllocateAdapterChannel( FdoExtension );
|
|
|
|
IoStartTimer(FdoExtension->Self);
|
|
|
|
if (IsNEC_98) {
|
|
//
|
|
// NEC98's FDD driver can't not disconnect interrupt,
|
|
// and can't not page out this driver. Because when a FD is inserted in FDD or
|
|
// is ejected from FDD, then H/W calls FDD driver's interrupt routine.
|
|
//
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
} else { // (IsNEC_98)
|
|
|
|
//
|
|
// Connect the Interrupt
|
|
//
|
|
ntStatus = IoConnectInterrupt(&FdoExtension->InterruptObject,
|
|
FdcInterruptService,
|
|
FdoExtension,
|
|
NULL,
|
|
FdoExtension->ControllerVector,
|
|
FdoExtension->ControllerIrql,
|
|
FdoExtension->ControllerIrql,
|
|
FdoExtension->InterruptMode,
|
|
FdoExtension->SharableVector,
|
|
FdoExtension->ProcessorMask,
|
|
FdoExtension->SaveFloatState);
|
|
} // (IsNEC_98)
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
FdoExtension->ControllerInUse = TRUE;
|
|
} else {
|
|
FcFreeAdapterChannel( FdoExtension );
|
|
IoStopTimer(FdoExtension->Self);
|
|
}
|
|
} else {
|
|
|
|
ntStatus = STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FcReleaseFdc(
|
|
IN PFDC_FDO_EXTENSION FdoExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine releaese the floppy disk controller. This includes
|
|
freeing the adapter channel and disconnecting the interrupt.
|
|
|
|
NOTE - This is where the sharing mechanism will be put into
|
|
this driver. That is, higher level drivers will 'reserve' the
|
|
floppy controller with this ioctl. Subsequent calls to this driver
|
|
that are not from the 'reserving' drive will be rejected with a
|
|
BUSY status.
|
|
|
|
Arguments:
|
|
|
|
fdoExtension - Pointer to this device's extension data.
|
|
|
|
Return Value:
|
|
|
|
STATUS_DEVICE_BUSY if we don't have the controller, otherwise
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
FdcDump(FDCINFO, ("Fdc: Release the Floppy Controller\n") );
|
|
|
|
//
|
|
// Free the Adapter Channel
|
|
//
|
|
FcFreeAdapterChannel( FdoExtension );
|
|
|
|
FdoExtension->AllowInterruptProcessing = FALSE;
|
|
FdoExtension->ControllerInUse = FALSE;
|
|
|
|
if (IsNEC_98) {
|
|
//
|
|
// NEC98's FDD driver can't not disconnect interrupt,
|
|
// and can't not page out this driver. Because when a FD is inserted in FDD or
|
|
// is ejected from FDD, then H/W calls FDD driver's interrupt routine.
|
|
//
|
|
|
|
} else { // (IsNEC_98)
|
|
//
|
|
// Disconnect the Interrupt
|
|
//
|
|
IoDisconnectInterrupt(FdoExtension->InterruptObject);
|
|
|
|
} // (IsNEC_98)
|
|
|
|
IoStopTimer(FdoExtension->Self);
|
|
|
|
FDC_PAGE_ENTIRE_DRIVER_WITH_MUTEX;
|
|
|
|
//
|
|
// Release the Fdc Enabler card if there is one. Otherwise, set the
|
|
// floppy synchronization event.
|
|
//
|
|
if (FdoExtension->FdcEnablerSupported) {
|
|
|
|
FcFdcEnabler( FdoExtension->FdcEnablerDeviceObject,
|
|
IOCTL_RELEASE_FDC,
|
|
NULL);
|
|
} else {
|
|
|
|
KeSetEvent( FdoExtension->AcquireEvent,
|
|
(KPRIORITY) 0,
|
|
FALSE );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
FcTurnOnMotor(
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN OUT PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine turns on the motor if it not already running.
|
|
|
|
Arguments:
|
|
|
|
fdoExtension - Pointer to this device's extension data.
|
|
|
|
IrpSp - Pointer to the current Irp
|
|
|
|
Return Value:
|
|
|
|
STATUS_DEVICE_BUSY if we don't have the controller, otherwise
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR driveStatus;
|
|
UCHAR newStatus;
|
|
LARGE_INTEGER motorOnDelay;
|
|
PFDC_ENABLE_PARMS fdcEnableParms;
|
|
|
|
USHORT lpc;
|
|
UCHAR resultStatus0Save[4];
|
|
|
|
fdcEnableParms =
|
|
(PFDC_ENABLE_PARMS)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
FdcDump(FDCINFO,
|
|
("Fdc: Turn Motor On: %x\n",fdcEnableParms->DriveOnValue)
|
|
);
|
|
|
|
driveStatus = FdoExtension->DriveControlImage;
|
|
if (IsNEC_98) {
|
|
|
|
newStatus = DRVCTL_MOTOR_MASK;
|
|
|
|
} else { // (IsNEC_98)
|
|
|
|
newStatus = fdcEnableParms->DriveOnValue |
|
|
DRVCTL_ENABLE_CONTROLLER |
|
|
DRVCTL_ENABLE_DMA_AND_INTERRUPTS;
|
|
} // (IsNEC_98)
|
|
|
|
if ( driveStatus != newStatus ) {
|
|
|
|
// If the drive is not on then check to see if we have
|
|
// the controller. Otherwise we assume that we have
|
|
// the controller since we give it up only when we
|
|
// turn off the motor.
|
|
|
|
if (IsNEC_98) {
|
|
if(FdoExtension->MotorRunning == 0){
|
|
|
|
//
|
|
// save status
|
|
//
|
|
for(lpc=0;lpc<4;lpc++){
|
|
resultStatus0Save[lpc] = FdoExtension->ResultStatus0[lpc];
|
|
}
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("Floppy: Turn on motor!\n")
|
|
);
|
|
|
|
FdoExtension->DriveControlImage = 0x18;
|
|
FdoExtension->DriveControlImage |= DRVCTL_AI_ENABLE;
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.DriveControl,
|
|
FdoExtension->DriveControlImage );
|
|
FdoExtension->MotorRunning = 1;
|
|
}
|
|
} else { // (IsNEC_98)
|
|
|
|
if (!FdoExtension->CurrentInterrupt) {
|
|
|
|
FdoExtension->CurrentInterrupt = TRUE;
|
|
|
|
driveStatus = FdoExtension->DriveControlImage;
|
|
}
|
|
|
|
FdoExtension->AllowInterruptProcessing = TRUE;
|
|
|
|
FdoExtension->DriveControlImage = newStatus;
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.DriveControl,
|
|
FdoExtension->DriveControlImage );
|
|
|
|
} // (IsNEC_98)
|
|
|
|
|
|
if (fdcEnableParms->TimeToWait > 0) {
|
|
|
|
if (IsNEC_98) {
|
|
|
|
//
|
|
// check if motor is on or not.
|
|
//
|
|
if(FdoExtension->MotorRunning == 1){
|
|
FdoExtension->MotorRunning = 2;
|
|
motorOnDelay.LowPart = (unsigned long)(- ( 10 * 1000 * 1000 ));
|
|
motorOnDelay.HighPart = -1;
|
|
KeDelayExecutionThread( KernelMode, FALSE, &motorOnDelay );
|
|
|
|
//
|
|
// after sense, restore status
|
|
//
|
|
for(lpc=0;lpc<4;lpc++){
|
|
FdoExtension->ResultStatus0[lpc] = resultStatus0Save[lpc];
|
|
}
|
|
}
|
|
|
|
} else { // (IsNEC_98)
|
|
|
|
motorOnDelay.LowPart =
|
|
- ( 10 * 1000 * fdcEnableParms->TimeToWait );
|
|
motorOnDelay.HighPart = -1;
|
|
|
|
FdoExtension->LastMotorSettleTime = motorOnDelay;
|
|
|
|
KeDelayExecutionThread( KernelMode, FALSE, &motorOnDelay );
|
|
|
|
} // (IsNEC_98)
|
|
|
|
}
|
|
|
|
fdcEnableParms->MotorStarted = TRUE;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
FcTurnOffMotor(
|
|
IN PFDC_FDO_EXTENSION FdoExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine turns off all motors. By default, Drive A is left selected
|
|
by this routine since it is not possible to deselect all drives. On a
|
|
Power PC, drive D is left selected.
|
|
|
|
Arguments:
|
|
|
|
fdoExtension - Supplies the fdc extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
FdcDump(FDCINFO,
|
|
("Fdc: Turn Motor Off\n")
|
|
);
|
|
|
|
if (IsNEC_98) {
|
|
|
|
if (FdoExtension->MotorRunning != 0){
|
|
|
|
FdoExtension->DriveControlImage
|
|
= READ_CONTROLLER(FdoExtension->ControllerAddress.DriveControl);
|
|
|
|
FdoExtension->DriveControlImage = 0x10;
|
|
FdoExtension->DriveControlImage |= DRVCTL_AI_ENABLE;
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.DriveControl,
|
|
FdoExtension->DriveControlImage );
|
|
|
|
if (FdoExtension->CurrentInterrupt) {
|
|
FdoExtension->CurrentInterrupt = FALSE;
|
|
|
|
KeSetEvent(FdoExtension->AcquireEvent,
|
|
(KPRIORITY) 0,
|
|
FALSE);
|
|
}
|
|
FdoExtension->MotorRunning = 0;
|
|
}
|
|
} else { // (IsNEC_98)
|
|
|
|
FdoExtension->DriveControlImage =
|
|
DRVCTL_ENABLE_DMA_AND_INTERRUPTS +
|
|
#ifdef _PPC_
|
|
DRVCTL_DRIVE_MASK +
|
|
#endif
|
|
DRVCTL_ENABLE_CONTROLLER;
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.DriveControl,
|
|
FdoExtension->DriveControlImage );
|
|
|
|
} // (IsNEC_98)
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FcAllocateAdapterChannel(
|
|
IN OUT PFDC_FDO_EXTENSION FdoExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates an adapter channel. The caller of
|
|
IoAllocateAdapterChannel routine must wait for the
|
|
'AllocateAdapterChannelEvent' to be signalled before trying to use the
|
|
adapter channel.
|
|
|
|
Arguments:
|
|
|
|
fdoExtension - Supplies the fdc extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
if ( (FdoExtension->AdapterChannelRefCount)++ ) {
|
|
return;
|
|
}
|
|
|
|
KeResetEvent( &FdoExtension->AllocateAdapterChannelEvent );
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
|
|
IoAllocateAdapterChannel( FdoExtension->AdapterObject,
|
|
FdoExtension->Self,
|
|
FdoExtension->NumberOfMapRegisters,
|
|
FdcAllocateAdapterChannel,
|
|
FdoExtension );
|
|
|
|
KeLowerIrql( oldIrql );
|
|
|
|
KeWaitForSingleObject( &FdoExtension->AllocateAdapterChannelEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
VOID
|
|
FcFreeAdapterChannel(
|
|
IN OUT PFDC_FDO_EXTENSION FdoExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees the previously allocated adapter channel.
|
|
|
|
Arguments:
|
|
|
|
fdoExtension - Supplies the fdc extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
if ( --(FdoExtension->AdapterChannelRefCount) ) {
|
|
return;
|
|
}
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
|
|
IoFreeAdapterChannel( FdoExtension->AdapterObject );
|
|
|
|
KeLowerIrql( oldIrql );
|
|
}
|
|
|
|
IO_ALLOCATION_ACTION
|
|
FdcAllocateAdapterChannel(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID MapRegisterBase,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This DPC is called whenever the fdc.sys driver is trying to allocate
|
|
the adapter channel. It saves the MapRegisterBase in the controller data
|
|
area, and sets the AllocateAdapterChannelEvent to awaken the thread.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - unused.
|
|
|
|
Irp - unused.
|
|
|
|
MapRegisterBase - the base of the map registers that can be used
|
|
for this transfer.
|
|
|
|
Context - a pointer to our controller data area.
|
|
|
|
Return Value:
|
|
|
|
Returns Allocation Action 'KeepObject' which means that the adapter
|
|
object will be held for now (to be released explicitly later).
|
|
|
|
--*/
|
|
{
|
|
PFDC_FDO_EXTENSION fdoExtension = Context;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
|
|
fdoExtension->MapRegisterBase = MapRegisterBase;
|
|
|
|
KeSetEvent( &fdoExtension->AllocateAdapterChannelEvent,
|
|
0L,
|
|
FALSE );
|
|
|
|
return KeepObject;
|
|
}
|
|
|
|
VOID
|
|
FcLogErrorDpc(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemContext1,
|
|
IN PVOID SystemContext2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is merely used to log an error that we had to reset the device.
|
|
|
|
Arguments:
|
|
|
|
Dpc - The dpc object.
|
|
|
|
DeferredContext - A pointer to the controller data.
|
|
|
|
SystemContext1 - Unused.
|
|
|
|
SystemContext2 - Unused.
|
|
|
|
Return Value:
|
|
|
|
Mapped address
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
PFDC_FDO_EXTENSION fdoExtension = DeferredContext;
|
|
|
|
errorLogEntry = IoAllocateErrorLogEntry(
|
|
fdoExtension->DriverObject,
|
|
(UCHAR)(sizeof(IO_ERROR_LOG_PACKET))
|
|
);
|
|
|
|
if ( errorLogEntry != NULL) {
|
|
|
|
errorLogEntry->ErrorCode = IO_ERR_RESET;
|
|
errorLogEntry->SequenceNumber = 0;
|
|
errorLogEntry->MajorFunctionCode = 0;
|
|
errorLogEntry->RetryCount = 0;
|
|
errorLogEntry->UniqueErrorValue = 0;
|
|
errorLogEntry->FinalStatus = STATUS_SUCCESS;
|
|
errorLogEntry->DumpDataSize = 0;
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
FcIssueCommand(
|
|
IN OUT PFDC_FDO_EXTENSION FdoExtension,
|
|
IN PUCHAR FifoInBuffer,
|
|
OUT PUCHAR FifoOutBuffer,
|
|
IN PVOID IoHandle,
|
|
IN 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.
|
|
|
|
fdoExtension - a pointer to our data area for this controller.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the command was sent and bytes received properly;
|
|
appropriate error propogated otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus;
|
|
NTSTATUS ntStatus2;
|
|
UCHAR i;
|
|
PUCHAR fifoBuffer;
|
|
UCHAR Command;
|
|
BOOLEAN NeedToFlush = FALSE;
|
|
|
|
|
|
//
|
|
// If this command causes an interrupt, set CurrentDeviceObject and
|
|
// reset the interrupt event.
|
|
//
|
|
|
|
Command = FifoInBuffer[0];
|
|
|
|
FdcDump( FDCINFO,
|
|
("FcIssueCommand: Issue Command : %x\n",
|
|
CommandTable[Command & COMMAND_MASK].OpCode)
|
|
);
|
|
|
|
|
|
if ( CommandTable[Command & COMMAND_MASK].InterruptExpected ) {
|
|
|
|
FdoExtension->CurrentDeviceObject = FdoExtension->Self;
|
|
FdoExtension->AllowInterruptProcessing = TRUE;
|
|
FdoExtension->CommandHasResultPhase =
|
|
!!CommandTable[Command & COMMAND_MASK].FirstResultByte;
|
|
|
|
KeResetEvent( &FdoExtension->InterruptEvent );
|
|
}
|
|
|
|
//
|
|
// Start up the command
|
|
//
|
|
|
|
ntStatus = FcStartCommand( FdoExtension,
|
|
FifoInBuffer,
|
|
FifoOutBuffer,
|
|
IoHandle,
|
|
IoOffset,
|
|
TransferBytes,
|
|
TRUE );
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
//
|
|
// If there is an interrupt, wait for it.
|
|
//
|
|
|
|
if ( CommandTable[Command & COMMAND_MASK].InterruptExpected ) {
|
|
|
|
ntStatus = KeWaitForSingleObject(
|
|
&FdoExtension->InterruptEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&FdoExtension->InterruptDelay );
|
|
|
|
if ( ntStatus == STATUS_TIMEOUT ) {
|
|
|
|
//
|
|
// Change info to an error. We'll just say
|
|
// that the device isn't ready.
|
|
//
|
|
|
|
ntStatus = STATUS_DEVICE_NOT_READY;
|
|
|
|
FdoExtension->HardwareFailed = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If successful so far, get the result bytes.
|
|
//
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
ntStatus = FcFinishCommand( FdoExtension,
|
|
FifoInBuffer,
|
|
FifoOutBuffer,
|
|
IoHandle,
|
|
IoOffset,
|
|
TransferBytes,
|
|
TRUE );
|
|
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FcStartCommand(
|
|
IN OUT PFDC_FDO_EXTENSION FdoExtension,
|
|
IN PUCHAR FifoInBuffer,
|
|
OUT PUCHAR FifoOutBuffer,
|
|
IN PVOID IoHandle,
|
|
IN ULONG IoOffset,
|
|
IN ULONG TransferBytes,
|
|
IN BOOLEAN AllowLongDelay
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus;
|
|
NTSTATUS ntStatus2;
|
|
UCHAR i = 0;
|
|
PUCHAR fifoBuffer;
|
|
UCHAR Command;
|
|
BOOLEAN NeedToFlush = FALSE;
|
|
PIO_STACK_LOCATION irpSp;
|
|
UCHAR status0;
|
|
|
|
//
|
|
// If this command causes an interrupt, set CurrentDeviceObject and
|
|
// reset the interrupt event.
|
|
//
|
|
|
|
Command = FifoInBuffer[0];
|
|
|
|
FdcDump( FDCINFO,
|
|
("FcStartCommand: Issue Command : %x\n",
|
|
CommandTable[Command & COMMAND_MASK].OpCode)
|
|
);
|
|
|
|
FdoExtension->CommandHasResultPhase =
|
|
!!CommandTable[Command & COMMAND_MASK].FirstResultByte;
|
|
|
|
// First we will need to set up the data transfer if there is one associated
|
|
// with this request.
|
|
//
|
|
if (CommandTable[Command & COMMAND_MASK].DataTransfer == FDC_READ_DATA ) {
|
|
//
|
|
// Setup Adapter Channel for Read
|
|
//
|
|
IoMapTransfer(FdoExtension->AdapterObject,
|
|
IoHandle,
|
|
FdoExtension->MapRegisterBase,
|
|
(PVOID)((ULONG_PTR)MmGetMdlVirtualAddress( (PMDL)IoHandle ) + IoOffset ),
|
|
&TransferBytes,
|
|
FALSE);
|
|
|
|
} else if (CommandTable[Command & COMMAND_MASK].DataTransfer ==
|
|
FDC_WRITE_DATA ) {
|
|
//
|
|
// Setup Adapter Channel for Write
|
|
//
|
|
|
|
IoMapTransfer(FdoExtension->AdapterObject,
|
|
IoHandle,
|
|
FdoExtension->MapRegisterBase,
|
|
(PVOID)((ULONG_PTR)MmGetMdlVirtualAddress( (PMDL)IoHandle ) + IoOffset ),
|
|
&TransferBytes,
|
|
TRUE);
|
|
|
|
}
|
|
|
|
//
|
|
// Send the command to the controller.
|
|
//
|
|
if ( Command == COMMND_CONFIGURE ) {
|
|
if ( FdoExtension->Clock48MHz ) {
|
|
Command |= COMMND_OPTION_CLK48;
|
|
}
|
|
}
|
|
ntStatus = FcSendByte( (UCHAR)(CommandTable[Command & COMMAND_MASK].OpCode |
|
|
(Command & ~COMMAND_MASK)),
|
|
FdoExtension,
|
|
AllowLongDelay );
|
|
|
|
//
|
|
// If the command was successfully sent, we can proceed.
|
|
//
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
//
|
|
// Send the parameters as long as we succeed.
|
|
//
|
|
|
|
for ( i = 1;
|
|
( i <= CommandTable[Command & COMMAND_MASK].NumberOfParameters ) &&
|
|
( NT_SUCCESS( ntStatus ) );
|
|
i++ ) {
|
|
|
|
ntStatus = FcSendByte( FifoInBuffer[i],
|
|
FdoExtension,
|
|
AllowLongDelay );
|
|
//
|
|
// The Drive Specification is a special case since we don't really know
|
|
// how many bytes to send until we encounter the DONE bit (or we have sent
|
|
// the maximum allowable bytes).
|
|
//
|
|
if ((Command == COMMND_DRIVE_SPECIFICATION) &&
|
|
(FifoInBuffer[i] & COMMND_DRIVE_SPECIFICATION_DONE) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If there was a problem, check to see if it was caused by an
|
|
// unimplemented command.
|
|
//
|
|
|
|
if ( !NT_SUCCESS( ntStatus ) ) {
|
|
|
|
if ( ( i == 2 ) &&
|
|
( !CommandTable[Command & COMMAND_MASK].AlwaysImplemented ) ) {
|
|
|
|
//
|
|
// This error is probably caused by a command that's not
|
|
// implemented on this controller. Read the error from the
|
|
// controller, and we should be in a stable state.
|
|
//
|
|
|
|
ntStatus2 = FcGetByte( &status0,
|
|
FdoExtension,
|
|
AllowLongDelay );
|
|
|
|
//
|
|
// If GetByte went as planned, we'll return the original error.
|
|
//
|
|
|
|
if ( NT_SUCCESS( ntStatus2 ) ) {
|
|
|
|
if ( status0 != STREG0_END_INVALID_COMMAND ) {
|
|
|
|
//
|
|
// Status isn't as we expect, so return generic error.
|
|
//
|
|
|
|
ntStatus = STATUS_FLOPPY_BAD_REGISTERS;
|
|
|
|
FdoExtension->HardwareFailed = TRUE;
|
|
FdcDump( FDCINFO,
|
|
("FcStartCommand: unexpected error value %2x\n",
|
|
status0) );
|
|
} else {
|
|
FdcDump( FDCINFO,
|
|
("FcStartCommand: Invalid command error returned\n") );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// GetByte returned an error, so propogate THAT.
|
|
//
|
|
|
|
FdcDump( FDCINFO,
|
|
("FcStartCommand: FcGetByte returned error %x\n",
|
|
ntStatus2) );
|
|
ntStatus = ntStatus2;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Flush the Adapter Channel if we allocated it.
|
|
//
|
|
|
|
if (CommandTable[Command & COMMAND_MASK].DataTransfer ==
|
|
FDC_READ_DATA) {
|
|
|
|
IoFlushAdapterBuffers( FdoExtension->AdapterObject,
|
|
(PMDL)IoHandle,
|
|
FdoExtension->MapRegisterBase,
|
|
(PVOID)((ULONG_PTR)MmGetMdlVirtualAddress( (PMDL)IoHandle) + IoOffset ),
|
|
TransferBytes,
|
|
FALSE);
|
|
|
|
} else if (CommandTable[Command & COMMAND_MASK].DataTransfer ==
|
|
FDC_WRITE_DATA) {
|
|
|
|
IoFlushAdapterBuffers( FdoExtension->AdapterObject,
|
|
(PMDL)IoHandle,
|
|
FdoExtension->MapRegisterBase,
|
|
(PVOID)((ULONG_PTR)MmGetMdlVirtualAddress( (PMDL)IoHandle) + IoOffset ),
|
|
TransferBytes,
|
|
TRUE);
|
|
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS( ntStatus ) ) {
|
|
|
|
//
|
|
// Print an error message unless the command isn't always
|
|
// implemented, ie CONFIGURE.
|
|
//
|
|
|
|
if ( !( ( ntStatus == STATUS_DEVICE_NOT_READY ) &&
|
|
( !CommandTable[Command & COMMAND_MASK].AlwaysImplemented ) ) ) {
|
|
|
|
FdcDump( FDCDBGP,
|
|
("Fdc: err %x ------ while giving command %x\n",
|
|
ntStatus, Command) );
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FcFinishCommand(
|
|
IN OUT PFDC_FDO_EXTENSION FdoExtension,
|
|
IN PUCHAR FifoInBuffer,
|
|
OUT PUCHAR FifoOutBuffer,
|
|
IN PVOID IoHandle,
|
|
IN ULONG IoOffset,
|
|
IN ULONG TransferBytes,
|
|
IN BOOLEAN AllowLongDelay
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to complete a command to the floppy controller.
|
|
At this point the floppy controller has successfully been sent a command
|
|
and has either generated an interrupt or is ready with its result phase.
|
|
This routine will also flush the DMA Adapter Buffers if they have been
|
|
allocated.
|
|
|
|
Arguments:
|
|
|
|
FdoExtension - a pointer to our data area for this controller.
|
|
|
|
IssueCommandParms - Floppy controller command parameters.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the command is successfully completed.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
NTSTATUS ntStatus2;
|
|
UCHAR i;
|
|
UCHAR Command;
|
|
|
|
Command = FifoInBuffer[0];
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("Fdc: FcFinishCommand...\n")
|
|
);
|
|
|
|
if (IsNEC_98) {
|
|
|
|
if (Command == COMMND_SENSE_DRIVE_STATUS) {
|
|
|
|
ntStatus = FcGetByte(
|
|
&FdoExtension->FifoBuffer[0],
|
|
FdoExtension,
|
|
AllowLongDelay );
|
|
|
|
if (NT_SUCCESS(ntStatus) && (FdoExtension->FifoBuffer[0] & STREG3_DRIVE_READY)) {
|
|
|
|
FdoExtension->ResultStatus0[FifoInBuffer[1]] = 0;
|
|
}
|
|
|
|
}
|
|
|
|
FifoOutBuffer[0] = FdoExtension->FifoBuffer[0];
|
|
|
|
for ( i = 1;
|
|
( i < CommandTable[Command & COMMAND_MASK].NumberOfResultBytes ) &&
|
|
( NT_SUCCESS( ntStatus ) );
|
|
i++ ) {
|
|
|
|
ntStatus = FcGetByte(
|
|
&FifoOutBuffer[i],
|
|
FdoExtension,
|
|
AllowLongDelay );
|
|
}
|
|
|
|
FdcRqmReadyWait(FdoExtension, 0);
|
|
|
|
} else { // (IsNEC_98)
|
|
|
|
if (CommandTable[Command & COMMAND_MASK].FirstResultByte > 0) {
|
|
|
|
FifoOutBuffer[0] = FdoExtension->FifoBuffer[0];
|
|
|
|
}
|
|
|
|
for ( i = CommandTable[Command & COMMAND_MASK].FirstResultByte;
|
|
( i < CommandTable[Command & COMMAND_MASK].NumberOfResultBytes ) &&
|
|
( NT_SUCCESS( ntStatus ) );
|
|
i++ ) {
|
|
|
|
ntStatus = FcGetByte(
|
|
&FifoOutBuffer[i],
|
|
FdoExtension,
|
|
AllowLongDelay );
|
|
}
|
|
} // (IsNEC_98)
|
|
|
|
//
|
|
// Flush the Adapter Channel
|
|
//
|
|
|
|
if (CommandTable[Command & COMMAND_MASK].DataTransfer == FDC_READ_DATA) {
|
|
|
|
IoFlushAdapterBuffers(FdoExtension->AdapterObject,
|
|
(PMDL)IoHandle,
|
|
FdoExtension->MapRegisterBase,
|
|
(PVOID)((ULONG_PTR)MmGetMdlVirtualAddress( (PMDL)IoHandle ) + IoOffset ),
|
|
TransferBytes,
|
|
FALSE);
|
|
|
|
} else if (CommandTable[Command & COMMAND_MASK].DataTransfer ==
|
|
FDC_WRITE_DATA) {
|
|
//
|
|
// Setup Adapter Channel for Write
|
|
//
|
|
|
|
IoFlushAdapterBuffers(FdoExtension->AdapterObject,
|
|
(PMDL)IoHandle,
|
|
FdoExtension->MapRegisterBase,
|
|
(PVOID)((ULONG_PTR)MmGetMdlVirtualAddress( (PMDL)IoHandle ) + IoOffset ),
|
|
TransferBytes,
|
|
TRUE);
|
|
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FcSendByte(
|
|
IN UCHAR ByteToSend,
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN BOOLEAN AllowLongDelay
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to send a byte to the controller. It won't
|
|
send the byte unless the controller is ready to receive a byte; if
|
|
it's not ready after checking FIFO_TIGHTLOOP_RETRY_COUNT times, we
|
|
delay for the minimum possible time (10ms) and then try again. It
|
|
should always be ready after waiting 10ms.
|
|
|
|
Arguments:
|
|
|
|
ByteToSend - the byte to send to the controller.
|
|
|
|
ControllerData - a pointer to our data area for this controller.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the byte was sent to the controller;
|
|
STATUS_DEVICE_NOT_READY otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i = 0;
|
|
BOOLEAN byteWritten = FALSE;
|
|
|
|
if (IsNEC_98) {
|
|
|
|
// Always FALSE;
|
|
AllowLongDelay = FALSE;
|
|
}
|
|
|
|
//
|
|
// Sit in a tight loop for a while. If the controller becomes ready,
|
|
// send the byte.
|
|
//
|
|
|
|
do {
|
|
|
|
if ( ( READ_CONTROLLER( FdoExtension->ControllerAddress.Status )
|
|
& STATUS_IO_READY_MASK ) == STATUS_WRITE_READY ) {
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.Fifo,
|
|
ByteToSend );
|
|
|
|
byteWritten = TRUE;
|
|
|
|
} else {
|
|
KeStallExecutionProcessor(1);
|
|
}
|
|
|
|
i++;
|
|
|
|
} while ( (!byteWritten) && ( i < FIFO_TIGHTLOOP_RETRY_COUNT ) );
|
|
|
|
//
|
|
// We hope that in most cases the FIFO will become ready very quickly
|
|
// and the above loop will have written the byte. But if the FIFO
|
|
// is not yet ready, we'll loop a few times delaying for 10ms and then
|
|
// try it again.
|
|
//
|
|
|
|
if ( AllowLongDelay ) {
|
|
|
|
i = 0;
|
|
|
|
while ( ( !byteWritten ) && ( i < FIFO_DELAY_RETRY_COUNT ) ) {
|
|
|
|
FdcDump(
|
|
FDCINFO,
|
|
("Fdc: waiting for 10ms for controller write\n")
|
|
);
|
|
|
|
KeDelayExecutionThread(
|
|
KernelMode,
|
|
FALSE,
|
|
&FdoExtension->Minimum10msDelay );
|
|
|
|
i++;
|
|
|
|
if ( (READ_CONTROLLER( FdoExtension->ControllerAddress.Status )
|
|
& STATUS_IO_READY_MASK) == STATUS_WRITE_READY ) {
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.Fifo,
|
|
ByteToSend );
|
|
|
|
byteWritten = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( byteWritten ) {
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We've waited over 30ms, and the FIFO *still* isn't ready.
|
|
// Return an error.
|
|
//
|
|
|
|
FdcDump(
|
|
FDCWARN,
|
|
("Fdc: FIFO not ready to write after 30ms\n")
|
|
);
|
|
|
|
FdoExtension->HardwareFailed = TRUE;
|
|
|
|
return STATUS_DEVICE_NOT_READY;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
FcGetByte(
|
|
OUT PUCHAR ByteToGet,
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN BOOLEAN AllowLongDelay
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to get a byte from the controller. It won't
|
|
read the byte unless the controller is ready to send a byte; if
|
|
it's not ready after checking FIFO_RETRY_COUNT times, we delay for
|
|
the minimum possible time (10ms) and then try again. It should
|
|
always be ready after waiting 10ms.
|
|
|
|
Arguments:
|
|
|
|
ByteToGet - the address in which the byte read from the controller
|
|
is stored.
|
|
|
|
ControllerData - a pointer to our data area for this controller.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if a byte was read from the controller;
|
|
STATUS_DEVICE_NOT_READY otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i = 0;
|
|
BOOLEAN byteRead = FALSE;
|
|
|
|
if (IsNEC_98) {
|
|
|
|
// Always FALSE;
|
|
AllowLongDelay = FALSE;
|
|
}
|
|
|
|
//
|
|
// Sit in a tight loop for a while. If the controller becomes ready,
|
|
// read the byte.
|
|
//
|
|
|
|
do {
|
|
|
|
if ( ( READ_CONTROLLER( FdoExtension->ControllerAddress.Status )
|
|
& STATUS_IO_READY_MASK ) == STATUS_READ_READY ) {
|
|
|
|
*ByteToGet = READ_CONTROLLER(
|
|
FdoExtension->ControllerAddress.Fifo );
|
|
|
|
byteRead = TRUE;
|
|
|
|
} else {
|
|
KeStallExecutionProcessor(1);
|
|
}
|
|
|
|
i++;
|
|
|
|
} while ( ( !byteRead ) && ( i < FIFO_TIGHTLOOP_RETRY_COUNT ) );
|
|
|
|
//
|
|
// We hope that in most cases the FIFO will become ready very quickly
|
|
// and the above loop will have read the byte. But if the FIFO
|
|
// is not yet ready, we'll loop a few times delaying for 10ms and then
|
|
// trying it again.
|
|
//
|
|
|
|
if ( AllowLongDelay ) {
|
|
|
|
i = 0;
|
|
|
|
while ( ( !byteRead ) && ( i < FIFO_DELAY_RETRY_COUNT ) ) {
|
|
|
|
FdcDump(
|
|
FDCINFO,
|
|
("Fdc: waiting for 10ms for controller read\n")
|
|
);
|
|
|
|
KeDelayExecutionThread(
|
|
KernelMode,
|
|
FALSE,
|
|
&FdoExtension->Minimum10msDelay );
|
|
|
|
i++;
|
|
|
|
if ( (READ_CONTROLLER( FdoExtension->ControllerAddress.Status )
|
|
& STATUS_IO_READY_MASK) == STATUS_READ_READY ) {
|
|
|
|
*ByteToGet = READ_CONTROLLER(
|
|
FdoExtension->ControllerAddress.Fifo );
|
|
|
|
byteRead = TRUE;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( byteRead ) {
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We've waited over 30ms, and the FIFO *still* isn't ready.
|
|
// Return an error.
|
|
//
|
|
|
|
FdcDump(
|
|
FDCWARN,
|
|
("Fdc: FIFO not ready to read after 30ms\n")
|
|
);
|
|
|
|
FdoExtension->HardwareFailed = TRUE;
|
|
|
|
return STATUS_DEVICE_NOT_READY;
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
FdcCheckTimer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called at DISPATCH_LEVEL once every second by the
|
|
I/O system.
|
|
|
|
If the timer is "set" (greater than 0) this routine will KeSync a
|
|
routine to decrement it. If it ever reaches 0, the hardware is
|
|
assumed to be in an unknown state, and so we log an error and
|
|
initiate a reset.
|
|
|
|
If a timeout occurs while resetting the controller, the KeSync'd
|
|
routine will return an error, and this routine will fail any IRPs
|
|
currently being processed. Future IRPs will try the hardware again.
|
|
|
|
When this routine is called, the driver state is impossible to
|
|
predict. However, when it is called and the timer is running, we
|
|
know that one of the disks on the controller is expecting an
|
|
interrupt. So no new packets are starting on the current disk due
|
|
to device queues, and no code should be processing this packet since
|
|
the packet is waiting for an interrupt.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object associated with this
|
|
timer.
|
|
|
|
Fdcxtension - a pointer to the fdc extension data.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
PIRP irp;
|
|
|
|
fdoExtension = (PFDC_FDO_EXTENSION)Context;
|
|
irp = DeviceObject->CurrentIrp;
|
|
|
|
//
|
|
// When the counter is -1, the timer is "off" so we don't want to do
|
|
// anything. If it's on, we'll have to synchronize execution with
|
|
// other routines while we mess with the variables (and, potentially,
|
|
// the hardware).
|
|
//
|
|
|
|
if ( fdoExtension->InterruptTimer == CANCEL_TIMER ) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// In the unlikely event that we attempt to reset the controller due
|
|
// to a timeout AND that reset times out, we will need to fail the
|
|
// IRP that was in progress at the first timeout occurred.
|
|
//
|
|
|
|
if ( !KeSynchronizeExecution( fdoExtension->InterruptObject,
|
|
FdcTimerSync,
|
|
fdoExtension ) ) {
|
|
|
|
//
|
|
// We're done with the reset. Return the IRP that was being
|
|
// processed with an error, and release the controller object.
|
|
//
|
|
|
|
fdoExtension->ResettingController = RESET_NOT_RESETTING;
|
|
|
|
irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
|
|
|
|
IoSetHardErrorOrVerifyDevice( irp, DeviceObject );
|
|
|
|
if ( InterlockedDecrement(&fdoExtension->OutstandingRequests ) == 0 ) {
|
|
KeSetEvent( &fdoExtension->RemoveEvent, 0, FALSE );
|
|
}
|
|
IoCompleteRequest( irp, IO_DISK_INCREMENT );
|
|
|
|
IoStartNextPacket( DeviceObject, FALSE );
|
|
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
FdcTimerSync(
|
|
IN OUT PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called at DIRQL by FdcCheckTimer() when
|
|
InterruptTimer is greater than 0.
|
|
|
|
If the timer is "set" (greater than 0) this routine will decrement
|
|
it. If it ever reaches 0, the hardware is assumed to be in an
|
|
unknown state, and so we log an error and initiate a reset.
|
|
|
|
When this routine is called, the driver state is impossible to
|
|
predict. However, when it is called and the timer is running, we
|
|
know that one of the disks on the controller is expecting an
|
|
interrupt. So, no new packets are starting on the current disk due
|
|
to device queues, and no code should be processing this packet since
|
|
the packet is waiting for an interrupt. The controller object must
|
|
be held.
|
|
|
|
Arguments:
|
|
|
|
Context - a pointer to the controller extension.
|
|
|
|
Return Value:
|
|
|
|
Generally TRUE.
|
|
|
|
FALSE is only returned if the controller timed out while resetting
|
|
the drive, so this means that the hardware state is unknown.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
|
|
fdoExtension = (PFDC_FDO_EXTENSION)Context;
|
|
|
|
//
|
|
// When the counter is -1, the timer is "off" so we don't want to do
|
|
// anything. It may have changed since we last checked it in
|
|
// FdcCheckTimer().
|
|
//
|
|
|
|
if ( fdoExtension->InterruptTimer == CANCEL_TIMER ) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The timer is "on", so decrement it.
|
|
//
|
|
|
|
fdoExtension->InterruptTimer--;
|
|
|
|
//
|
|
// If we hit zero, the timer has expired and we'll reset the
|
|
// controller.
|
|
//
|
|
|
|
if ( fdoExtension->InterruptTimer == EXPIRED_TIMER ) {
|
|
|
|
//
|
|
// If we were ALREADY resetting the controller when it timed out,
|
|
// there's something seriously wrong.
|
|
//
|
|
|
|
FdcDump( FDCDBGP, ("Fdc: Operation Timed Out.\n") );
|
|
|
|
if ( fdoExtension->ResettingController != RESET_NOT_RESETTING ) {
|
|
|
|
//
|
|
// Returning FALSE will cause the current IRP to be completed
|
|
// with an error. Future IRPs will probably get a timeout and
|
|
// attempt to reset the controller again. This will probably
|
|
// never happen.
|
|
//
|
|
|
|
FdcDump( FDCDBGP, ("Fdc: Timeout Reset timed out.\n") );
|
|
|
|
fdoExtension->InterruptTimer = CANCEL_TIMER;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Reset the controller. This will cause an interrupt. Reset
|
|
// CurrentDeviceObject until after the 10ms wait, in case any
|
|
// stray interrupts come in.
|
|
//
|
|
|
|
fdoExtension->ResettingController = RESET_DRIVE_RESETTING;
|
|
|
|
DISABLE_CONTROLLER_IMAGE (fdoExtension);
|
|
|
|
#ifdef _PPC_
|
|
fdoExtension->DriveControlImage |= DRVCTL_DRIVE_MASK;
|
|
#endif
|
|
|
|
WRITE_CONTROLLER(
|
|
fdoExtension->ControllerAddress.DriveControl,
|
|
fdoExtension->DriveControlImage );
|
|
|
|
KeStallExecutionProcessor( 10 );
|
|
|
|
fdoExtension->CommandHasResultPhase = FALSE;
|
|
fdoExtension->InterruptTimer = START_TIMER;
|
|
|
|
ENABLE_CONTROLLER_IMAGE (fdoExtension);
|
|
|
|
WRITE_CONTROLLER(
|
|
fdoExtension->ControllerAddress.DriveControl,
|
|
fdoExtension->DriveControlImage );
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
FdcInterruptService(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called at DIRQL by the system when the controller
|
|
interrupts.
|
|
|
|
Arguments:
|
|
|
|
Interrupt - a pointer to the interrupt object.
|
|
|
|
Context - a pointer to our controller data area for the controller
|
|
that interrupted. (This was set up by the call to
|
|
IoConnectInterrupt).
|
|
|
|
Return Value:
|
|
|
|
Normally returns TRUE, but will return FALSE if this interrupt was
|
|
not expected.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
PDEVICE_OBJECT currentDeviceObject;
|
|
ULONG i;
|
|
UCHAR statusByte;
|
|
BOOLEAN controllerStateError;
|
|
|
|
UCHAR resultStatus0;
|
|
UCHAR aiStatus=0;
|
|
UCHAR aiInterrupt=0;
|
|
ULONG rqmReadyRetryCount;
|
|
BOOLEAN Response;
|
|
|
|
UNREFERENCED_PARAMETER( Interrupt );
|
|
|
|
#ifdef KEEP_COUNTERS
|
|
FloppyIntrTime = KeQueryPerformanceCounter((PVOID)NULL);
|
|
FloppyInterrupts++;
|
|
#endif
|
|
|
|
FdcDump( FDCSHOW, ("FdcInterruptService: ") );
|
|
|
|
fdoExtension = (PFDC_FDO_EXTENSION) Context;
|
|
|
|
if (!IsNEC_98) {
|
|
if (!fdoExtension->AllowInterruptProcessing) {
|
|
FdcDump( FDCSHOW, ("processing not allowed\n") );
|
|
return FALSE;
|
|
}
|
|
} // (!IsNEC_98)
|
|
|
|
//
|
|
// CurrentDeviceObject is set to the device object that is
|
|
// expecting an interrupt.
|
|
//
|
|
|
|
currentDeviceObject = fdoExtension->CurrentDeviceObject;
|
|
fdoExtension->CurrentDeviceObject = NULL;
|
|
controllerStateError = FALSE;
|
|
fdoExtension->InterruptTimer = CANCEL_TIMER;
|
|
|
|
KeStallExecutionProcessor(10);
|
|
|
|
if (IsNEC_98) {
|
|
do {
|
|
|
|
resultStatus0 = READ_CONTROLLER( fdoExtension->ControllerAddress.Status );
|
|
|
|
resultStatus0 &= STATUS_DATA_REQUEST;
|
|
|
|
} while (resultStatus0 != STATUS_DATA_REQUEST);
|
|
} // (IsNEC_98)
|
|
|
|
if ( fdoExtension->CommandHasResultPhase ) {
|
|
|
|
//
|
|
// Result phase of previous command. (Note that we can't trust
|
|
// the CMD_BUSY bit in the status register to tell us whether
|
|
// there's result bytes or not; it's sometimes wrong).
|
|
// By reading the first result byte, we reset the interrupt.
|
|
// The other result bytes will be read by a thread.
|
|
// Note that we want to do this even if the interrupt is
|
|
// unexpected, to make sure the interrupt is dismissed.
|
|
//
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("have result phase\n")
|
|
);
|
|
|
|
if (IsNEC_98) {
|
|
|
|
rqmReadyRetryCount = 0;
|
|
|
|
while ( ( READ_CONTROLLER( fdoExtension->ControllerAddress.Status)
|
|
& STATUS_IO_READY_MASK1) != STATUS_RQM_READY ) {
|
|
//
|
|
// RQM READY CHECK**
|
|
//
|
|
|
|
rqmReadyRetryCount++;
|
|
|
|
if( rqmReadyRetryCount > RQM_READY_RETRY_COUNT ) {
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor( 10 );
|
|
}
|
|
|
|
if( rqmReadyRetryCount > ( RQM_READY_RETRY_COUNT - 1 ) ) {
|
|
FdcDump(
|
|
FDCDBGP,
|
|
("Floppy: Int RQM ready wait 1 error! \n")
|
|
);
|
|
|
|
KeStallExecutionProcessor( 10 );
|
|
goto FdcInterruptMidterm;
|
|
|
|
}
|
|
} // (IsNEC_98)
|
|
|
|
if ( ( READ_CONTROLLER( fdoExtension->ControllerAddress.Status )
|
|
& STATUS_IO_READY_MASK ) == STATUS_READ_READY ) {
|
|
|
|
fdoExtension->FifoBuffer[0] =
|
|
READ_CONTROLLER( fdoExtension->ControllerAddress.Fifo );
|
|
|
|
FdcDump( FDCSHOW,
|
|
("FdcInterruptService: 1st fifo byte %2x\n",
|
|
fdoExtension->FifoBuffer[0])
|
|
);
|
|
|
|
} else {
|
|
|
|
if (IsNEC_98) {
|
|
|
|
FdcRqmReadyWait(fdoExtension, 2);
|
|
|
|
} // (IsNEC_98)
|
|
|
|
//
|
|
// Should never get here. If we do, DON'T wake up the thread;
|
|
// let it time out and reset the controller, or let another
|
|
// interrupt handle this.
|
|
//
|
|
|
|
FdcDump(
|
|
FDCDBGP,
|
|
("FdcInterruptService: controller not ready to be read in ISR\n")
|
|
);
|
|
|
|
controllerStateError = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Previous command doesn't have a result phase. To read how it
|
|
// completed, issue a sense interrupt command. Don't read
|
|
// the result bytes from the sense interrupt; that is the
|
|
// responsibility of the calling thread.
|
|
// Note that we want to do this even if the interrupt is
|
|
// unexpected, to make sure the interrupt is dismissed.
|
|
//
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("no result phase\n")
|
|
);
|
|
i = 0;
|
|
|
|
do {
|
|
|
|
KeStallExecutionProcessor( 1 );
|
|
statusByte =
|
|
READ_CONTROLLER(fdoExtension->ControllerAddress.Status);
|
|
i++;
|
|
|
|
} while ( ( i < FIFO_ISR_TIGHTLOOP_RETRY_COUNT ) &&
|
|
( ( statusByte & STATUS_CONTROLLER_BUSY ) ||
|
|
( ( statusByte & STATUS_IO_READY_MASK ) != STATUS_WRITE_READY ) ) );
|
|
|
|
if ( !( statusByte & STATUS_CONTROLLER_BUSY ) &&
|
|
( ( statusByte & STATUS_IO_READY_MASK ) == STATUS_WRITE_READY ) ) {
|
|
|
|
WRITE_CONTROLLER(
|
|
fdoExtension->ControllerAddress.Fifo,
|
|
0x08 );
|
|
// COMMND_SENSE_INTERRUPT_STATUS );
|
|
|
|
//
|
|
// Wait for the controller to ACK the SenseInterrupt command, by
|
|
// showing busy. On very fast machines we can end up running
|
|
// driver's system-thread before the controller has had time to
|
|
// set the busy bit.
|
|
//
|
|
|
|
for (i = ISR_SENSE_RETRY_COUNT; i; i--) {
|
|
|
|
statusByte =
|
|
READ_CONTROLLER( fdoExtension->ControllerAddress.Status );
|
|
if (statusByte & STATUS_CONTROLLER_BUSY) {
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor( 1 );
|
|
}
|
|
|
|
if (!i) {
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("FdcInterruptService: spin loop complete and controller NOT busy\n")
|
|
);
|
|
}
|
|
|
|
if ( currentDeviceObject == NULL ) {
|
|
|
|
//
|
|
// This is an unexpected interrupt, so nobody's going to
|
|
// read the result bytes. Read them now.
|
|
//
|
|
|
|
if (IsNEC_98) {
|
|
|
|
resultStatus0 = FdcRqmReadyWait(fdoExtension, 0);
|
|
|
|
if ((resultStatus0 & STREG0_END_DRIVE_NOT_READY) != STREG0_END_INVALID_COMMAND ) {
|
|
|
|
resultStatus0 = FdcRqmReadyWait(fdoExtension, 1);
|
|
}
|
|
} else { // (IsNEC_98)
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("FdcInterruptService: Dumping fifo bytes!\n")
|
|
);
|
|
READ_CONTROLLER( fdoExtension->ControllerAddress.Fifo );
|
|
READ_CONTROLLER( fdoExtension->ControllerAddress.Fifo );
|
|
} // (IsNEC_98)
|
|
}
|
|
|
|
if (IsNEC_98) {
|
|
if ( currentDeviceObject != NULL ) {
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("Floppy: FloppyInt.---Deviceobject!=NULL2\n")
|
|
);
|
|
|
|
resultStatus0 = FdcRqmReadyWait(fdoExtension, 0);
|
|
|
|
//
|
|
// Check move state.
|
|
//
|
|
|
|
if((resultStatus0 & STREG0_END_MASK) == STREG0_END_DRIVE_NOT_READY) {
|
|
|
|
if(fdoExtension->ResetFlag){
|
|
aiStatus=1;
|
|
fdoExtension->CurrentDeviceObject = currentDeviceObject;
|
|
}
|
|
|
|
} else {
|
|
|
|
fdoExtension->FifoBuffer[0] = resultStatus0;
|
|
|
|
aiStatus=0;
|
|
aiInterrupt=1;
|
|
}
|
|
|
|
|
|
if (aiInterrupt == 0){
|
|
while( ((resultStatus0 & STREG0_END_DRIVE_NOT_READY) != STREG0_END_INVALID_COMMAND) && aiInterrupt==0 ) {
|
|
|
|
resultStatus0 = FdcRqmReadyWait(fdoExtension, 3);
|
|
|
|
do {
|
|
//
|
|
// Check move state.
|
|
//
|
|
|
|
if((resultStatus0 & STREG0_END_MASK) == STREG0_END_DRIVE_NOT_READY) {
|
|
|
|
if(fdoExtension->ResetFlag){
|
|
|
|
aiStatus=1;
|
|
fdoExtension->CurrentDeviceObject = currentDeviceObject;
|
|
}
|
|
|
|
} else {
|
|
|
|
fdoExtension->FifoBuffer[0] = resultStatus0;
|
|
|
|
aiStatus=0;
|
|
aiInterrupt=1;
|
|
break;
|
|
}
|
|
|
|
resultStatus0 = FdcRqmReadyWait(fdoExtension, 0);
|
|
|
|
} while ( aiInterrupt == 0 );
|
|
}
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("Floppy: FloppyInt.---Deviceobject!=NULL_out\n")
|
|
);
|
|
}
|
|
}
|
|
} // (IsNEC_98)
|
|
|
|
} else {
|
|
|
|
//
|
|
// Shouldn't get here. If we do, DON'T wake up the thread;
|
|
// let it time out and reset the controller, or let another
|
|
// interrupt take care of it.
|
|
//
|
|
|
|
FdcDump(
|
|
FDCDBGP,
|
|
("Fdc: no result, but can't write SenseIntr\n")
|
|
);
|
|
|
|
controllerStateError = TRUE;
|
|
}
|
|
}
|
|
|
|
FdcInterruptMidterm:
|
|
|
|
//
|
|
// We've written to the controller, and we're about to leave. On
|
|
// machines with levelsensitive interrupts, we'll get another interrupt
|
|
// if we RETURN before the port is flushed. To make sure that doesn't
|
|
// happen, we'll do a read here.
|
|
//
|
|
|
|
statusByte = READ_CONTROLLER( fdoExtension->ControllerAddress.Status );
|
|
|
|
//
|
|
// Let the interrupt settle.
|
|
//
|
|
|
|
KeStallExecutionProcessor(10);
|
|
|
|
#ifdef KEEP_COUNTERS
|
|
FloppyEndIntrTime = KeQueryPerformanceCounter((PVOID)NULL);
|
|
FloppyIntrDelay.QuadPart = FloppyIntrDelay.QuadPart +
|
|
(FloppyEndIntrTime.QuadPart -
|
|
FloppyIntrTime.QuadPart);
|
|
#endif
|
|
|
|
if (IsNEC_98) {
|
|
if(!(fdoExtension->ResetFlag)){
|
|
fdoExtension->ResetFlag = TRUE;
|
|
}
|
|
} // (IsNEC_98)
|
|
|
|
if ( currentDeviceObject == NULL ) {
|
|
|
|
//
|
|
// We didn't expect this interrupt. We've dismissed it just
|
|
// in case, but now return FALSE withOUT waking up the thread.
|
|
//
|
|
|
|
FdcDump(FDCDBGP,
|
|
("Fdc: unexpected interrupt\n"));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !controllerStateError ) {
|
|
|
|
//
|
|
// Request a DPC for execution later to get the remainder of the
|
|
// floppy state.
|
|
//
|
|
|
|
fdoExtension->IsrReentered = 0;
|
|
fdoExtension->AllowInterruptProcessing = FALSE;
|
|
|
|
if (IsNEC_98) {
|
|
if(aiStatus==0){
|
|
IoRequestDpc(currentDeviceObject,
|
|
currentDeviceObject->CurrentIrp,
|
|
(PVOID) NULL );
|
|
}
|
|
} else { // (IsNEC_98)
|
|
|
|
IoRequestDpc(currentDeviceObject,
|
|
currentDeviceObject->CurrentIrp,
|
|
(PVOID) NULL);
|
|
|
|
} // (IsNEC_98)
|
|
|
|
} else {
|
|
|
|
//
|
|
// Running the floppy (at least on R4000 boxes) we've seen
|
|
// examples where the device interrupts, yet it never says
|
|
// it *ISN'T* busy. If this ever happens on non-MCA x86 boxes
|
|
// it would be ok since we use latched interrupts. Even if
|
|
// the device isn't touched so that the line would be pulled
|
|
// down, on the latched machine, this ISR wouldn't be called
|
|
// again. The normal timeout code for a request would eventually
|
|
// reset the controller and retry the request.
|
|
//
|
|
// On the R4000 boxes and on MCA machines, the floppy is using
|
|
// level sensitive interrupts. Therefore if we don't do something
|
|
// to lower the interrupt line, we will be called over and over,
|
|
// *forever*. This makes it look as though the machine is hung.
|
|
// Unless we were lucky enough to be on a multiprocessor, the
|
|
// normal timeout code would NEVER get a chance to run because
|
|
// the timeout code runs at dispatch level, and we will never
|
|
// leave device level.
|
|
//
|
|
// What we will do is keep a counter that is incremented every
|
|
// time we reach this section of code. When the counter goes
|
|
// over the threshold we will do a hard reset of the device
|
|
// and reset the counter down to zero. The counter will be
|
|
// initialized when the device is first initialized. It will
|
|
// be set to zero in the other arm of this if, and it will be
|
|
// reset to zero by the normal timeout logic.
|
|
//
|
|
|
|
fdoExtension->CurrentDeviceObject = currentDeviceObject;
|
|
if (fdoExtension->IsrReentered > FLOPPY_RESET_ISR_THRESHOLD) {
|
|
|
|
//
|
|
// Reset the controller. This could cause an interrupt
|
|
//
|
|
|
|
fdoExtension->IsrReentered = 0;
|
|
|
|
DISABLE_CONTROLLER_IMAGE (fdoExtension);
|
|
|
|
#ifdef _PPC_
|
|
fdoExtension->DriveControlImage |= DRVCTL_DRIVE_MASK;
|
|
#endif
|
|
|
|
WRITE_CONTROLLER(fdoExtension->ControllerAddress.DriveControl,
|
|
fdoExtension->DriveControlImage);
|
|
|
|
KeStallExecutionProcessor( 10 );
|
|
|
|
ENABLE_CONTROLLER_IMAGE (fdoExtension);
|
|
|
|
WRITE_CONTROLLER(fdoExtension->ControllerAddress.DriveControl,
|
|
fdoExtension->DriveControlImage);
|
|
|
|
if (IsNEC_98) {
|
|
|
|
fdoExtension->ResetFlag = TRUE;
|
|
|
|
} // (IsNEC_98)
|
|
|
|
//
|
|
// Give the device plenty of time to be reset and
|
|
// interrupt again. Then just do the sense interrupt.
|
|
// this should quiet the device. We will then let
|
|
// the normal timeout code do its work.
|
|
//
|
|
|
|
KeStallExecutionProcessor(500);
|
|
WRITE_CONTROLLER(fdoExtension->ControllerAddress.Fifo,
|
|
0x08 );
|
|
// COMMND_SENSE_INTERRUPT_STATUS );
|
|
KeStallExecutionProcessor(500);
|
|
|
|
KeInsertQueueDpc(&fdoExtension->LogErrorDpc,
|
|
NULL,
|
|
NULL);
|
|
} else {
|
|
|
|
fdoExtension->IsrReentered++;
|
|
}
|
|
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
FdcDeferredProcedure(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called at DISPATCH_LEVEL by the system at the
|
|
request of FdcInterruptService(). It simply sets the interrupt
|
|
event, which wakes up the floppy thread.
|
|
|
|
Arguments:
|
|
|
|
Dpc - a pointer to the DPC object used to invoke this routine.
|
|
|
|
DeferredContext - a pointer to the device object associated with this
|
|
DPC.
|
|
|
|
SystemArgument1 - unused.
|
|
|
|
SystemArgument2 - unused.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_OBJECT deviceObject;
|
|
// PFDC_PDO_EXTENSION pdoExtension;
|
|
PFDC_FDO_EXTENSION fdoExtension;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PLIST_ENTRY request;
|
|
PISSUE_FDC_COMMAND_PARMS issueCommandParms;
|
|
|
|
UNREFERENCED_PARAMETER( Dpc );
|
|
UNREFERENCED_PARAMETER( SystemArgument1 );
|
|
UNREFERENCED_PARAMETER( SystemArgument2 );
|
|
|
|
#ifdef KEEP_COUNTERS
|
|
FloppyDPCs++;
|
|
FloppyDPCTime = KeQueryPerformanceCounter((PVOID)NULL);
|
|
|
|
FloppyDPCDelay.QuadPart = FloppyDPCDelay.QuadPart +
|
|
(FloppyDPCTime.QuadPart -
|
|
FloppyIntrTime.QuadPart);
|
|
#endif
|
|
|
|
deviceObject = (PDEVICE_OBJECT) DeferredContext;
|
|
fdoExtension = deviceObject->DeviceExtension;
|
|
|
|
irp = deviceObject->CurrentIrp;
|
|
|
|
if ( irp != NULL ) {
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( irp );
|
|
}
|
|
|
|
if ( irp != NULL &&
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_DISK_INTERNAL_ISSUE_FDC_COMMAND_QUEUED ) {
|
|
|
|
issueCommandParms =
|
|
(PISSUE_FDC_COMMAND_PARMS)
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
ntStatus = FcFinishCommand(
|
|
fdoExtension,
|
|
issueCommandParms->FifoInBuffer,
|
|
issueCommandParms->FifoOutBuffer,
|
|
issueCommandParms->IoHandle,
|
|
issueCommandParms->IoOffset,
|
|
issueCommandParms->TransferBytes,
|
|
FALSE );
|
|
|
|
irp->IoStatus.Status = ntStatus;
|
|
|
|
if ( !NT_SUCCESS( ntStatus ) &&
|
|
IoIsErrorUserInduced( ntStatus ) ) {
|
|
|
|
IoSetHardErrorOrVerifyDevice( irp, deviceObject );
|
|
}
|
|
|
|
if ( InterlockedDecrement(&fdoExtension->OutstandingRequests ) == 0 ) {
|
|
KeSetEvent( &fdoExtension->RemoveEvent, 0, FALSE );
|
|
}
|
|
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
|
|
|
IoStartNextPacket( deviceObject, FALSE );
|
|
|
|
} else {
|
|
|
|
FdcDump( FDCSHOW, ("FdcDeferredProcedure: Set Event\n") );
|
|
|
|
KeSetEvent( &fdoExtension->InterruptEvent, (KPRIORITY) 0, FALSE );
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
FcFinishReset(
|
|
IN OUT PFDC_FDO_EXTENSION FdoExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to complete a reset operation which entails
|
|
reading the interrupt status from each active channel on the floppy
|
|
controller.
|
|
|
|
Arguments:
|
|
|
|
FdoExtension - a pointer to our data area for this controller.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if this controller appears to have been reset properly,
|
|
error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
UCHAR statusRegister0;
|
|
UCHAR cylinder;
|
|
UCHAR driveNumber;
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("Fdc: FcFinishReset\n")
|
|
);
|
|
|
|
//
|
|
// Sense interrupt status for all drives.
|
|
//
|
|
for ( driveNumber = 0;
|
|
( driveNumber < MAXIMUM_DISKETTES_PER_CONTROLLER ) &&
|
|
( NT_SUCCESS( ntStatus ) );
|
|
driveNumber++ ) {
|
|
|
|
if ( driveNumber != 0 ) {
|
|
|
|
//
|
|
// Note that the ISR issued first SENSE INTERRUPT for us.
|
|
//
|
|
|
|
ntStatus = FcSendByte(
|
|
CommandTable[COMMND_SENSE_INTERRUPT_STATUS].OpCode,
|
|
FdoExtension,
|
|
TRUE );
|
|
}
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
ntStatus = FcGetByte( &statusRegister0, FdoExtension, TRUE );
|
|
|
|
if ( NT_SUCCESS( ntStatus ) ) {
|
|
|
|
ntStatus = FcGetByte( &cylinder, FdoExtension, TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
FcFdcEnabler(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG Ioctl,
|
|
IN OUT PVOID Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call the floppy enabler driver to execute a command. This is always a
|
|
synchronous call and, since it includes waiting for an event, should only
|
|
be done at IRQL_PASSIVE_LEVEL.
|
|
|
|
All communication with the Floppy Enabler driver is carried out through
|
|
device i/o control requests. Any data that is to be sent to or received
|
|
from the floppy enabler driver is included in the Type3InputBuffer section
|
|
of the irp.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the current device object.
|
|
|
|
Ioctl - the IoControl code that will be sent to the Floppy Enabler.
|
|
|
|
Data - a pointer to data that will be sent to or received from the Floppy
|
|
Enabler.
|
|
|
|
Return Value:
|
|
|
|
STATUS_TIMEOUT if the Floppy Enabler does not respond in a timely manner.
|
|
otherwise IoStatus.Status from the Floppy Enabler is returned.
|
|
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
KEVENT doneEvent;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS ntStatus;
|
|
|
|
FdcDump(FDCINFO,("FcFdcEnabler: Calling fdc enabler 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) {
|
|
|
|
FdcDump(FDCDBGP,("FcFdcEnabler: 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;
|
|
}
|
|
VOID
|
|
FdcGetEnablerDevice(
|
|
IN OUT PFDC_FDO_EXTENSION FdoExtension
|
|
)
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KEVENT doneEvent;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS ntStatus;
|
|
|
|
FdcDump(FDCINFO,("FdcGetEnablerDevice:\n"));
|
|
|
|
KeInitializeEvent( &doneEvent,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
//
|
|
// Create an IRP for enabler
|
|
//
|
|
irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_INTERNAL_GET_ENABLER,
|
|
FdoExtension->TargetObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
&doneEvent,
|
|
&ioStatus );
|
|
|
|
if (irp == NULL) {
|
|
|
|
FdcDump(FDCDBGP,("FdcGetEnablerDevice: 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;
|
|
}
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = &FdoExtension->FdcEnablerSupported;
|
|
|
|
//
|
|
// Call the driver and request the operation
|
|
//
|
|
ntStatus = IoCallDriver( FdoExtension->TargetObject, irp );
|
|
|
|
//
|
|
// Now wait for operation to complete (should already be done, but
|
|
// maybe not)
|
|
//
|
|
if ( ntStatus == STATUS_PENDING ) {
|
|
|
|
ntStatus = KeWaitForSingleObject( &doneEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG
|
|
FdcFindIsaBusNode(
|
|
IN OUT VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find Isa bus node in the registry.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
Node number.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG NodeNumber = 0;
|
|
BOOLEAN FoundBus = FALSE;
|
|
|
|
NTSTATUS Status;
|
|
|
|
RTL_QUERY_REGISTRY_TABLE parameters[2];
|
|
|
|
UNICODE_STRING invalidBusName;
|
|
UNICODE_STRING targetBusName;
|
|
UNICODE_STRING isaBusName;
|
|
|
|
//
|
|
// Initialize invalid bus name.
|
|
//
|
|
RtlInitUnicodeString(&invalidBusName,L"BADBUS");
|
|
|
|
//
|
|
// Initialize "ISA" bus name.
|
|
//
|
|
RtlInitUnicodeString(&isaBusName,L"ISA");
|
|
|
|
parameters[0].QueryRoutine = NULL;
|
|
parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
|
|
RTL_QUERY_REGISTRY_DIRECT;
|
|
parameters[0].Name = L"Identifier";
|
|
parameters[0].EntryContext = &targetBusName;
|
|
parameters[0].DefaultType = REG_SZ;
|
|
parameters[0].DefaultData = &invalidBusName;
|
|
parameters[0].DefaultLength = 0;
|
|
|
|
parameters[1].QueryRoutine = NULL;
|
|
parameters[1].Flags = 0;
|
|
parameters[1].Name = NULL;
|
|
parameters[1].EntryContext = NULL;
|
|
|
|
do {
|
|
CHAR AnsiBuffer[512];
|
|
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING registryPath;
|
|
|
|
//
|
|
// Build path buffer...
|
|
//
|
|
sprintf(AnsiBuffer,ISA_BUS_NODE,NodeNumber);
|
|
RtlInitAnsiString(&AnsiString,AnsiBuffer);
|
|
Status = RtlAnsiStringToUnicodeString(®istryPath,&AnsiString,TRUE);
|
|
|
|
if (!(NT_SUCCESS(Status))) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Initialize recieve buffer.
|
|
//
|
|
targetBusName.Buffer = NULL;
|
|
|
|
//
|
|
// Query it.
|
|
//
|
|
Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
|
registryPath.Buffer,
|
|
parameters,
|
|
NULL,
|
|
NULL);
|
|
|
|
RtlFreeUnicodeString(®istryPath);
|
|
|
|
if (!NT_SUCCESS(Status) || (targetBusName.Buffer == NULL)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Is this "ISA" node ?
|
|
//
|
|
if (RtlCompareUnicodeString(&targetBusName,&isaBusName,TRUE) == 0) {
|
|
//
|
|
// Found.
|
|
//
|
|
FoundBus = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Can we find any node for this ??
|
|
//
|
|
if (RtlCompareUnicodeString(&targetBusName,&invalidBusName,TRUE) == 0) {
|
|
//
|
|
// Not found.
|
|
//
|
|
break;
|
|
}
|
|
|
|
RtlFreeUnicodeString(&targetBusName);
|
|
|
|
//
|
|
// Next node number..
|
|
//
|
|
NodeNumber++;
|
|
|
|
} while (TRUE);
|
|
|
|
if (targetBusName.Buffer) {
|
|
RtlFreeUnicodeString(&targetBusName);
|
|
}
|
|
|
|
if (!FoundBus) {
|
|
NodeNumber = (ULONG)-1;
|
|
}
|
|
|
|
return (NodeNumber);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FdcHdbit(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN PSET_HD_BIT_PARMS SetHdBitParams
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set a Hd bit or a FDD EXC bit.
|
|
|
|
Arguments:
|
|
|
|
fdoExtension - a pointer to our data area for the device extension.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE : Changed HD bit
|
|
FALSE: No changed HD bit
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus;
|
|
USHORT st; // State of HD bit
|
|
USHORT st2; // Set on/off HD bit
|
|
USHORT st3; // When set HD bit, then st3=1
|
|
USHORT st4; // 1.44MB bit for 1.44MB media
|
|
SHORT sel; // 1.44MB Selector No for 1.44MB media
|
|
SHORT st5=0; // 1.44MB on: wait for spin for 1.44MB media
|
|
LARGE_INTEGER motorOnDelay;
|
|
|
|
USHORT lpc;
|
|
UCHAR resultStatus0Save[4];
|
|
USHORT resultStatus0;
|
|
ULONG getStatusRetryCount;
|
|
ULONG rqmReadyRetryCount;
|
|
|
|
BOOLEAN media144MB;
|
|
BOOLEAN mediaMore120MB;
|
|
BOOLEAN supportDrive;
|
|
|
|
media144MB = SetHdBitParams->Media144MB;
|
|
mediaMore120MB = SetHdBitParams->More120MB;
|
|
sel = SetHdBitParams->DeviceUnit;
|
|
SetHdBitParams->ChangedHdBit = FALSE;
|
|
|
|
|
|
ASSERT( FdoExtension->ControllerAddress.ModeChange == (PUCHAR)0xbe );
|
|
ASSERT( FdoExtension->ControllerAddress.ModeChangeEx == (PUCHAR)0x4be );
|
|
|
|
supportDrive = TRUE;
|
|
|
|
st3=0;
|
|
|
|
ntStatus=0;
|
|
|
|
//
|
|
// Normal mode.
|
|
//
|
|
|
|
st = READ_CONTROLLER(FdoExtension->ControllerAddress.ModeChange);
|
|
st2 = st & 0x02;
|
|
|
|
//
|
|
// Normal mode.
|
|
// Check dip switch.
|
|
//
|
|
|
|
st4 = READ_CONTROLLER(FdoExtension->ControllerAddress.DriveControl);
|
|
st4 = st4 & 0x04;
|
|
|
|
if (((FdoExtension->FloppyEquip) & 0x0c) != 0) {
|
|
//
|
|
// Exist out side FDD unit.
|
|
//
|
|
|
|
if ( st4 == 0 ) {
|
|
//
|
|
// DIP SW 1-4 on
|
|
//
|
|
|
|
sel = sel - 2;
|
|
|
|
if( sel < 0 ) {
|
|
sel = sel + 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( supportDrive ) {
|
|
|
|
for( lpc = 0 ; lpc < 4 ; lpc++ ) {
|
|
|
|
resultStatus0Save[lpc]=FdoExtension->ResultStatus0[lpc];
|
|
}
|
|
|
|
if ( SetHdBitParams->DriveType144MB ) {
|
|
//
|
|
// 1.44MB drive.
|
|
//
|
|
|
|
st4=sel*32;
|
|
WRITE_CONTROLLER(FdoExtension->ControllerAddress.ModeChangeEx,st4);
|
|
|
|
st4 = READ_CONTROLLER(FdoExtension->ControllerAddress.ModeChangeEx);
|
|
st4 = st4 & 0x01;
|
|
|
|
if ( media144MB ) {
|
|
|
|
//
|
|
// 1.44MB media.
|
|
//
|
|
|
|
if(st4==0){
|
|
|
|
//
|
|
// WRE on, IHMD off.
|
|
//
|
|
|
|
st4=sel*32+0x11;
|
|
WRITE_CONTROLLER(FdoExtension->ControllerAddress.ModeChangeEx,st4);
|
|
st5=1;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not 1.44MB media.
|
|
//
|
|
|
|
if(st4!=0){
|
|
|
|
//
|
|
// WRE on, IHMD off.
|
|
//
|
|
|
|
st4=sel*32+0x10;
|
|
WRITE_CONTROLLER(FdoExtension->ControllerAddress.ModeChangeEx,st4);
|
|
st5=1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( mediaMore120MB ) {
|
|
//
|
|
// Media 1.2MB and More.
|
|
//
|
|
|
|
if(st2==0){
|
|
//
|
|
// When FDD exc bit is on,
|
|
// then set FDD exc bit off,
|
|
// and set emotion bit on.
|
|
//
|
|
st |= 0x02;
|
|
st |= 0x04;
|
|
|
|
WRITE_CONTROLLER(FdoExtension->ControllerAddress.ModeChange,st);
|
|
st3 = 1;
|
|
|
|
}
|
|
} else {
|
|
//
|
|
// Media between 160 and 720
|
|
//
|
|
|
|
if ( st2 != 0 ) {
|
|
//
|
|
// When FDD exc bit is on,
|
|
// then set FDD exc bit off,
|
|
// and set emotion bit on.
|
|
//
|
|
|
|
st &= 0xfd;
|
|
st |= 0x04;
|
|
|
|
WRITE_CONTROLLER(FdoExtension->ControllerAddress.ModeChange,st);
|
|
st3 = 1;
|
|
|
|
}
|
|
}
|
|
|
|
if(st5==1){
|
|
|
|
//
|
|
// Wait until motor spin up.
|
|
//
|
|
|
|
motorOnDelay.LowPart = (unsigned long)(- ( 10 * 1000 * 600 )); /*500ms*/
|
|
motorOnDelay.HighPart = -1;
|
|
(VOID) KeDelayExecutionThread( KernelMode, FALSE, &motorOnDelay );
|
|
|
|
//
|
|
// Sense target drive and get all data at transition of condistion.
|
|
//
|
|
|
|
FdoExtension->FifoBuffer[0] = COMMND_SENSE_DRIVE_STATUS;
|
|
FdoExtension->FifoBuffer[1] = SetHdBitParams->DeviceUnit;
|
|
|
|
ntStatus = FcIssueCommand( FdoExtension,
|
|
FdoExtension->FifoBuffer,
|
|
FdoExtension->FifoBuffer,
|
|
NULL,
|
|
0,
|
|
0 );
|
|
|
|
resultStatus0 = FdcRqmReadyWait(FdoExtension, 0);
|
|
|
|
}
|
|
|
|
for(lpc=0;lpc<4;lpc++){
|
|
FdoExtension->ResultStatus0[lpc] = resultStatus0Save[lpc];
|
|
}
|
|
|
|
//
|
|
// Change HD bit?
|
|
//
|
|
|
|
if(st3==1){
|
|
FcInitializeControllerHardware(FdoExtension,DeviceObject);
|
|
SetHdBitParams->ChangedHdBit = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
FdcDump(
|
|
FDCSTATUS,
|
|
("Floppy : HdBit resultStatus0 = %x \n",
|
|
resultStatus0)
|
|
);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
FdcGet0Seg(
|
|
IN PUCHAR ConfigurationData1,
|
|
IN ULONG Offset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine get BIOS common area data and return it.
|
|
0x500 : 1MB port or not
|
|
0x501 : High resolution/Normal, 386/768KB
|
|
0x55c : 1MB drive : [#0,#1] or [#0,#1,#2,#3]
|
|
|
|
Arguments:
|
|
|
|
Offset - Offset value from 0 segment(0:<Offset>).
|
|
|
|
Return Value:
|
|
|
|
BIOS common area data.
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR biosCommonAreaData = 0;
|
|
|
|
if ((Offset<0x400) || (Offset>0x5ff)) {
|
|
|
|
return (ULONG)0xffff;
|
|
}
|
|
|
|
//
|
|
// Get BIOS common area data.
|
|
//
|
|
|
|
biosCommonAreaData = ConfigurationData1[40+(Offset-0x400)];
|
|
|
|
return (ULONG)biosCommonAreaData;
|
|
}
|
|
|
|
UCHAR
|
|
FdcRqmReadyWait(
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN ULONG IssueSenseInterrupt
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RQM Ready wait
|
|
|
|
Arguments:
|
|
|
|
FdoExtension - a pointer to our data area for the device extension.
|
|
IssueSenseInterrupt - Indicate issue COMMND_SENSE_INTERRUPT_STATUS.
|
|
0 - Issue no COMMND_SENSE_INTERRUPT_STATUS.
|
|
1 - Issue COMMND_SENSE_INTERRUPT_STATUS with RQM Check.
|
|
2 - Issue COMMND_SENSE_INTERRUPT_STATUS without RQM Check.
|
|
3 - Issue COMMND_SENSE_INTERRUPT_STATUS for AI Interrupt.
|
|
|
|
|
|
Return Value:
|
|
|
|
ntStatus - STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG getStatusRetryCount;
|
|
ULONG rqmReadyRetryCount;
|
|
ULONG j;
|
|
UCHAR resultStatus0;
|
|
UCHAR statusByte;
|
|
|
|
ASSERT(IssueSenseInterrupt < 4);
|
|
|
|
do{
|
|
if (IssueSenseInterrupt != 0) {
|
|
|
|
//
|
|
// Sense Interrupt status.
|
|
// RQM ready wait.
|
|
//
|
|
|
|
if ((IssueSenseInterrupt == 1) || (IssueSenseInterrupt == 3)) {
|
|
|
|
rqmReadyRetryCount=0;
|
|
//
|
|
// RQM ready check.
|
|
//
|
|
while ((READ_CONTROLLER( FdoExtension->ControllerAddress.Status)
|
|
& STATUS_IO_READY_MASK1) != STATUS_RQM_READY){
|
|
|
|
rqmReadyRetryCount++;
|
|
|
|
if(rqmReadyRetryCount > RQM_READY_RETRY_COUNT){
|
|
break;
|
|
}
|
|
KeStallExecutionProcessor( 1 );
|
|
}
|
|
if(rqmReadyRetryCount > (RQM_READY_RETRY_COUNT-1)){
|
|
FdcDump(
|
|
FDCDBGP,
|
|
("Floppy: Issue RQM ready wait 1 error! \n")
|
|
);
|
|
if (IssueSenseInterrupt == 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Issue sense interrupt forcibly.
|
|
//
|
|
|
|
WRITE_CONTROLLER(
|
|
FdoExtension->ControllerAddress.Fifo,
|
|
0x08);
|
|
// COMMND_SENSE_INTERRUPT_STATUS ); //******C-Phase DATA WRITE*
|
|
|
|
//
|
|
// Wait for busy.
|
|
//
|
|
for (rqmReadyRetryCount = ISR_SENSE_RETRY_COUNT; rqmReadyRetryCount; rqmReadyRetryCount--) {
|
|
statusByte = READ_CONTROLLER(
|
|
FdoExtension->ControllerAddress.Status );
|
|
if (statusByte & STATUS_CONTROLLER_BUSY)
|
|
break;
|
|
|
|
KeStallExecutionProcessor( 1 );
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get status.
|
|
//
|
|
|
|
getStatusRetryCount = 0;
|
|
|
|
j = 0;
|
|
|
|
do {
|
|
//
|
|
// Check RQM ready.
|
|
//
|
|
|
|
rqmReadyRetryCount=0;
|
|
|
|
while ((READ_CONTROLLER( FdoExtension->ControllerAddress.Status)
|
|
& STATUS_IO_READY_MASK1) != STATUS_RQM_READY){
|
|
|
|
rqmReadyRetryCount++;
|
|
|
|
if(rqmReadyRetryCount > RQM_READY_RETRY_COUNT){
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor( 1 );
|
|
}
|
|
|
|
if(rqmReadyRetryCount > (RQM_READY_RETRY_COUNT-1)){
|
|
|
|
FdcDump(
|
|
FDCDBGP,
|
|
("Floppy: Int RQM ready wait \n")
|
|
);
|
|
|
|
KeStallExecutionProcessor( 1 );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get status even if it is transition of condition.
|
|
//
|
|
|
|
statusByte = READ_CONTROLLER(FdoExtension->ControllerAddress.Status);
|
|
|
|
if ((statusByte & STATUS_IO_READY_MASK) == STATUS_WRITE_READY) {
|
|
|
|
//
|
|
// DIO is 1.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
if (j == 0) {
|
|
|
|
//
|
|
// R-Phase: Data read.
|
|
//
|
|
|
|
resultStatus0 = READ_CONTROLLER( FdoExtension->ControllerAddress.Fifo );
|
|
|
|
j=1;
|
|
|
|
//
|
|
// Check transition of condition.
|
|
//
|
|
|
|
if((resultStatus0 & STREG0_END_MASK)==STREG0_END_INVALID_COMMAND){
|
|
//
|
|
// Invalid
|
|
//
|
|
break;
|
|
}
|
|
|
|
if((resultStatus0 & STREG0_END_MASK) == STREG0_END_DRIVE_NOT_READY){
|
|
if(FdoExtension->ResetFlag){
|
|
FdoExtension->ResultStatus0[resultStatus0 & 3] = resultStatus0;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// R-Phase: Data read.
|
|
//
|
|
READ_CONTROLLER( FdoExtension->ControllerAddress.Fifo );
|
|
}
|
|
|
|
getStatusRetryCount++;
|
|
|
|
} while (getStatusRetryCount > RQM_READY_RETRY_COUNT);
|
|
|
|
if(getStatusRetryCount > RQM_READY_RETRY_COUNT-1){
|
|
|
|
KeStallExecutionProcessor( 1 );
|
|
FdcDump(
|
|
FDCDBGP,
|
|
("Floppy: Issue status overflow error! \n")
|
|
);
|
|
}
|
|
|
|
} while ((IssueSenseInterrupt != 0) &&
|
|
((resultStatus0 & STREG0_END_MASK) != STREG0_END_INVALID_COMMAND));
|
|
|
|
return resultStatus0;
|
|
|
|
}
|
|
|
|
#ifdef TOSHIBAJ
|
|
/*
|
|
IOCTL_DISK_INTERNAL_ENABLE_3_MODE:
|
|
|
|
Media Speed T/F
|
|
-------------------------
|
|
1.44MB 300RPM FALSE
|
|
1.23MB 360RPM TRUE
|
|
|
|
*/
|
|
#define RPM_300 1 // 1.44MB media format
|
|
#define RPM_360 2 // 1.2MB,1.23MB media format
|
|
NTSTATUS FcFdcEnable3Mode(
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN PIRP Irp
|
|
)
|
|
{ NTSTATUS ntStatus;
|
|
PENABLE_3_MODE param;
|
|
PIO_STACK_LOCATION irpSp;
|
|
UCHAR motorSpeed;
|
|
UCHAR unitNumber;
|
|
PUCHAR configPort;
|
|
UCHAR configDataValue = 0;
|
|
LARGE_INTEGER changeRotationDelay;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
param = (PENABLE_3_MODE)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
|
|
FdcDump(FDCSHOW,("FcFdcEnable3MODE()\n"));
|
|
if( param == NULL )
|
|
return STATUS_SUCCESS; // may be later...
|
|
|
|
FdcDump(FDCSHOW,("Parameters...%d %d\n",
|
|
// param->DeviceUnit, param->Enable3Mode, param->Context));
|
|
param->DeviceUnit, param->Enable3Mode));
|
|
|
|
if (FdoExtension->Available3Mode==FALSE)
|
|
return STATUS_SUCCESS;
|
|
|
|
// if(param->Context==TRUE)
|
|
// return STATUS_SUCCESS;
|
|
|
|
unitNumber = param->DeviceUnit;
|
|
motorSpeed = (param->Enable3Mode)? SMC_DENSEL_LOW: SMC_DELSEL_HIGH; // 360:300
|
|
|
|
// Feb.9.1998 KIADP013 Change Speed
|
|
// Change speed
|
|
// 1.Enter configuration state
|
|
// 2.Select device
|
|
// 3.Change speed
|
|
// Write 'f1h' to index port
|
|
// Read from data port
|
|
// Rewrite to data port
|
|
// bit [3:2] Densel speed
|
|
// 10 H 360rpm
|
|
// 11 L 300rpm
|
|
// 4.Exit configuration state
|
|
|
|
configPort = FdoExtension->ConfigBase;
|
|
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("Config: index port %x, data port %x\n",
|
|
SMC_INDEX_PORT(configPort), SMC_DATA_PORT(configPort))
|
|
);
|
|
|
|
// Must change data transfer rate to 500BPS when change FDD rotation.
|
|
WRITE_CONTROLLER(FdoExtension->ControllerAddress.DRDC.DataRate, DATART_0500 );
|
|
|
|
// Change speed
|
|
WRITE_PORT_UCHAR(configPort, SMC_KEY_ENTER_CONFIG); // Enter config state
|
|
|
|
// Select FDD
|
|
WRITE_PORT_UCHAR(SMC_INDEX_PORT(configPort), SMC_INDEX_DEVICE);
|
|
WRITE_PORT_UCHAR(SMC_DATA_PORT(configPort), SMC_DEVICE_FDC);
|
|
|
|
// Get current value
|
|
WRITE_PORT_UCHAR(SMC_INDEX_PORT(configPort), SMC_INDEX_FDC_OPT);
|
|
configDataValue = READ_PORT_UCHAR(SMC_DATA_PORT(configPort));
|
|
if ((configDataValue & SMC_MASK_DENSEL) == motorSpeed) {
|
|
WRITE_PORT_UCHAR(configPort, SMC_KEY_EXIT_CONFIG);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
// Set speed
|
|
configDataValue &= ~SMC_MASK_DENSEL;
|
|
configDataValue |= motorSpeed;
|
|
WRITE_PORT_UCHAR(SMC_DATA_PORT(configPort), configDataValue);
|
|
|
|
WRITE_PORT_UCHAR(configPort, SMC_KEY_EXIT_CONFIG); // Exit config state
|
|
|
|
if (motorSpeed == SMC_DENSEL_LOW) {
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("------- FDD rotation change: 300rpm -> 360rpm\n")
|
|
);
|
|
} else {
|
|
FdcDump(
|
|
FDCSHOW,
|
|
("------- FDD rotation change: 360rpm -> 300rpm\n")
|
|
);
|
|
}
|
|
|
|
// Set Delay time to 500ms
|
|
changeRotationDelay.LowPart =(ULONG)( - ( 10 * 1000 * 500 )); // 17-Oct-1994 RKBUG001
|
|
changeRotationDelay.HighPart = -1;
|
|
|
|
FdoExtension->LastMotorSettleTime = changeRotationDelay;
|
|
|
|
// Delay 500ms
|
|
KeDelayExecutionThread( KernelMode, FALSE, &changeRotationDelay );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS FcFdcAvailable3Mode(
|
|
IN PFDC_FDO_EXTENSION FdoExtension,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
FdcDump(FDCSHOW,("FcFdcAvailabe3MODE\n"));
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
// Feb.9.1998 KIADP012 Identify controller
|
|
// Feb.12.1998 KIADP014 Get the address of config port
|
|
|
|
return (FdoExtension->Available3Mode)? STATUS_SUCCESS: STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// Feb.12.1998 KIADP014 Get the address of config port
|
|
BOOLEAN
|
|
FcCheckConfigPort(
|
|
IN PUCHAR ConfigPort
|
|
)
|
|
{
|
|
BOOLEAN found = FALSE;
|
|
ULONG configAddr = 0;
|
|
UCHAR controllerId = 0;
|
|
|
|
FdcDump( FDCSHOW, ("FcCheckConfigPort: Configuration Port %x\n", ConfigPort) );
|
|
|
|
if (!SmcConfigID) {
|
|
return found;
|
|
}
|
|
|
|
// Get data
|
|
if (ConfigPort) {
|
|
WRITE_PORT_UCHAR(ConfigPort, SMC_KEY_ENTER_CONFIG);
|
|
|
|
// Controller ID
|
|
if (SmcConfigID) {
|
|
WRITE_PORT_UCHAR(SMC_INDEX_PORT(ConfigPort), SMC_INDEX_IDENTIFY);
|
|
controllerId = READ_PORT_UCHAR(SMC_DATA_PORT(ConfigPort));
|
|
FdcDump( FDCINFO, ("Fdc: Controller ID %x\n", controllerId) );
|
|
}
|
|
|
|
WRITE_PORT_UCHAR(ConfigPort,SMC_KEY_EXIT_CONFIG);
|
|
}
|
|
|
|
|
|
// Check data
|
|
if (controllerId == SmcConfigID) {
|
|
found = TRUE;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
#endif
|