NT4/private/ntos/dd/floppy/floppy.c

9192 lines
281 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
floppy.c
Abstract:
This is the NEC PD756 (aka AT, aka ISA, aka ix86) and Intel 82077
(aka MIPS) floppy diskette driver for NT.
Author:
Chad Schwitters (chads) 14-Jul-1991.
Environment:
Kernel mode only.
Revision History:
02-Aug-1991 (chads) Made driver work on MIPS as well as ix86.
#if defined(DBCS) && defined(_MIPS_)
N001 1994.07.29 N.Johno
- Modify for R96(MIPS/R4400)
#endif // defined(DBCS) && defined(_MIPS_)
Notes:
The NEC PD765 is the controller standard used on almost all ISA PCs
(Industry Standard Architecture, which use Intel x86 processors).
More advanced controllers like the Intel 82077 (which is used on
many MIPS machines) are supersets of the PD765 standard.
Overview of this driver:
This driver must deal with floppy drives, and the media that may
or may not be in them. "Drive" and "controller" refer to the
hardware connected to the machine, and "media" or "diskette"
refers to the floppy that gets inserted into the drive.
Communicating with the controller can involve a lot of delays,
so this driver creates a thread to talk to the controller (since
we can't wait in another thread's context). The initialization
and dispatch routines are executed in the context of the caller,
and the ISR and DPC routines could be anywhere, but everything
else is executed in the context of the thread. The dispatch
routines put requests into an interlocked queue and signal the
thread; the thread wakes up and processes the requests serially.
A big advantage of the thread approach is that synchronization
problems are almost non-existent. Interrupts always come when
the floppy is blocked and waiting for them (well, there could be
a spurious interrupt, but the ISR won't do anything critical in
that event), so the only opening for contention is with the DPC
that turns off the drive motor. A spin lock is used to keep the
thread and the DPC from trying to access variables or registers
at the same time.
Data structures include a controller extension that holds all
variables common to the controller, a device extension that
holds all variables common to the drive, and a read-only table
that holds parameters used for communicating with the controller
given a drive/media combination. The data structures will be
described in more detail later.
Since this driver deals with removable media, it must do its
share of volume tracking. Before processing any packet, it must
check some flags - if the volume needs to be verified, the
packet must be returned with STATUS_VERIFY_REQUIRED. Then, the
disk's changeline must be checked. If it is set, it should be
reset, and the packet should be returned with
STATUS_VERIFY_REQUIRED. (If it can't be reset, then the packet
should be failed with STATUS_NO_MEDIA_IN_DEVICE).
Before any operation, the motor timer must be cancelled, the
drive must be selected, its motor must be turned on and given a
chance to spin up, the changeline must be checked (and possibly
dealt with as mentioned above), write access (if requested) must
be checked, and the media type must be determined.
Determining the media type is an unusual process. The drive
type is obtained from the configuration manager at
initialization time. Any time that the changeline is set (which
includes startup) the media type is unknown, and must be
determined. The DriveMediaLimits table is used to determine
which drive/media combinations are allowable, and the driver
assumes that the media with the largest capacity is in the
drive. It sets up the controller to read that media, and tries
to read an ID mark from the disk. As long as that fails, it
tries a lower media type. If no media type works, an error is
returned. If one works, it is assumed to properly indicate the
media in the drive, but it could be wrong because some
drive/media combinations use the same controller parameters. So
whenever we find that the first sector on the disk is being read
(which should be the first thing the file system does for a new
disk; this will work even if the media type assumption is wrong)
we sneak a peek at the media descriptor byte and change our
assumption about the media type if necessary.
An operation passed in by the system is called a "request".
This driver often breaks the request into smaller pieces called
"transfers" because of limitations in the amount of data that
can be read or written at one time.
Background on hardware operation:
The following info was gathered from a number of technical
reference documents and existing device drivers. These sources
often did not agree; where there was a problem, I tried to
determine the answer by consensus or experimentation.
At initialization, the following steps should be performed:
reset the controller
wait for an interrupt
for each diskette
issue SENSE INTERRUPT STATUS command
optionally issue the CONFIGURE command
issue SPECIFY command
program the data rate
The data rate, CONFIGURE and SPECIFY information should be
reprogrammed after every reset.
To do a SEEK or RECALIBRATE, the motor should be turned on and
the command should be issued. After the interrupt comes, a
SENSE INTERRUPT STATUS command should be issued.
To do a READ or WRITE, the motor should be turned on, and left
to spin up for at least 500ms. The recorded data rate of the
inserted media can then be determined by performing a READ ID at
each data rate until a successful status is returned. Then
there should be a seek, followed by at least a 15ms head
settling time. DMA is set up, and the command is issued. When
the data has been transferred, the controller will interrupt.
If an error is encountered, two more retries should be performed
by reinitializing the DMA and re-issuing the command. If that
fails, the disk should be recalibrated and the seek repeated for
two more tries.
The following registers can be used to program the floppy
controller. Each is discussed in detail below:
AT JAZZ Read/ Register Name
Address Address Write (for this driver)
3f2 write DriveControl
3f4 read Status
3f5 both Fifo
3f7 write DataRate
3f7 read DiskChange
Notes on other register usages:
3f0 is used by the Intel chip, but only for PS/2s.
3f1 is readable on Compaqs, and called "media id". The
Intel chip only uses 3f1 on PS/2s.
3f2 is readable on the Intel chip.
3f3 doesn't appear to be used by anybody.
3f4 is writable and called "data rate select" on the Intel
chip.
3f6 is used for fixed disk operations.
3f7 does not seem to be mentioned by one [old] reference,
but all the others do - and it seems to be essential, so I'm
going to use it.
DriveControl (aka "digital output", aka "floppy operations"):
bits 0-1: drive select (values 10 and 11 not always used)
bit 2: 0 = reset controller, 1 = enable controller
bit 3: 0 = interrupts and DMA disabled, 1 = enabled
bit 4: 0 = drive 1 motor off, 1 = on
bit 5: 0 = drive 2 motor off, 1 = on
bit 6: 0 = drive 3 motor off, 1 = on (not always used)
bit 7: 0 = drive 4 motor off, 1 = on (not always used)
Note that a drive cannot be selected unless its motor is on
(setting the bits at the same time is acceptable).
Status:
bit 0: drive 0 busy (seeking or recalibrating)
bit 1: drive 1 busy
bit 2: drive 2 busy (not always used)
bit 3: drive 3 busy (not always used)
bit 4: controller busy (from command byte until result phase)
bit 5: DMA is not being used
bit 6: 0 = data from processor to controller, 1 = reverse
bit 7: 1 = data ready to be transferred
Note that command bytes can't be written to the DATA
register unless bit 7 is 1 and bit 6 is 0. Similarly,
result bytes can't be read from the DATA register unless bit
7 is 1 and bit 6 is 1. The delay for this to become true
could be 175us on the Intel chip; older chips may take
longer.
Fifo (aka "data"):
All commands are written here one byte at a time, and all
results are similarly read from here. Commands and results
are discussed below.
Some controllers have a buffer and programmable threshold,
which allows the data to be sent in chunks rather than one
byte at a time. This reduces the chance of an overrun.
After some commands, the values of status registers (and
other things) are returned through the Fifo register.
Here's the layout of the status registers:
Status Register 0:
bits 0-1: drive number
bit 2: head
bit 3: set if drive is not ready
bit 4: set if drive faults, or recalibrate didn't find
track 0
bit 5: set when seek is completed
bits 6-7: 00 = normal termination
01 = abnormal termination, error
10 = invalid command
11 = abnormal termination, drive became not ready
Status Register 1:
bit 0: set if the ID address mark wasn't found
bit 1: set if write-protection detected during a write
bit 2: set if sector not found
bit 3: always 0
bit 4: set for data overrun
bit 5: set if CRC error detected in ID or data
bit 6: always 0
bit 7: set on attempt to access a sector beyond the end of a
cylinder
Status Register 2:
bit 0: set if data address or deleted data mark not found
bit 1: set if bad cylinder found
bit 2: set if SCAN fails to find sector meeting condition
bit 3: set if SCAN command meets "equal" condition
bit 4: set if wrong cylinder found
bit 5: set if CRC error detected in data
bit 6: set if deleted address data mark encountered during
READ or SCAN
bit 7: always 0
Status Register 3:
bits 0-1: drive number
bit 2: head
bit 3: set if drive is two-sided
bit 4: set if drive is on track 0
bit 5: set if the drive is ready
bit 6: set if the drive is write-protected
bit 7: set if the drive has faulted
DataRate (aka "configuration control"):
bits 0-1: 00 = 500 kb/s, 01 = 300, 10 = 250, 11 = 1 Mb/s for
3.5" and 125 kb/s for 5.25"
bits 2-7: reserved, 0
DiskChange (aka "digital input", aka "diskette status"):
bits 0-6: reserved (often used for fixed disks)
bit 7: 1 = previous diskette has been removed
Common Commands:
Each command and its parameters are given to the controller
through the Data register. The controller then executes the
command. When finished, the results can be read from the
controller through the Data register.
A drive's motor must be on before the drive can be
selected. Units 0 and 1 cannot have their motor on
simultaneously; neither can units 2 and 3.
A drive cannot be accessed until <head load time> has passed
since selecting the drive.
A drive cannot be accessed until <head settle time> has
passed since an earlier access.
Fields used in commands:
Multi-Track: if this bit is set, both tracks of the
cylinder will be operated on.
MFM/FM Mode: if this bit is set, MFM mode (double
density) is selected if it's implemented; if not, FM
mode (single density) is selected.
Skip: when this bit is set, sectors with a deleted data
address mark will be skipped for READ DATA or accessed
for READ DELETED DATA.
Head: one bit, 0 or 1.
Drive Select: two bits to select drive 0 through 3.
Cylinder: 7 bits selecting the cylinder.
Sector: 4 bits giving the number of the first sector to
be accessed.
Sector Size Code: the sector size is 2^(this value) *
128. Sometimes forced to be 2, for 512-byte sectors.
Final Sector of Track: 4 bits giving the final sector
number of current track.
Gap Length: 8 bits giving the size of the gap between
sectors excluding the VC0 synchronization field.
Data Length: if Sector Size Code is 0, the sector size
is 128 but this value says how many bytes are actually
transferred (extra bytes dropped on READs, extra bytes
zeroed on WRITEs). If Sector Size code is not 0
(sometimes it's forced nonzero), this value should be
0xff.
Sectors per Track: 4 bits giving the number of sectors
per cylinder to be initialized by a FORMAT.
Data Pattern: 8 bits giving the pattern to be written in
each sector data field during a FORMAT.
Step Rate Interval: 4 bits. The time between step
pulses. From .5 to 8ms in .5ms increments at 1Mb data
rate. See the SPECIFY command.
Head Unload Time: 4 bits. The time from the end of a
read or write until the head is unloaded. 0 to 480ms in
32ms increments. See the SPECIFY command.
Head Load Time: 7 bits. The time from loading the head
until starting the read or write operation. 4 to 512ms
in 4ms increments. See the SPECIFY command.
Non-DMA Mode Flag: 0 = DMA mode, 1 = host is interrupted
for each data transfer.
New Cylinder Number: 7 bits giving the desired cylinder
number.
The commands are given below. For each commands, the bytes
that must be written to the controller are listed. When the
command is finished (after an interrupt for most commands)
the further listed bytes MUST be read from the controller.
READ DATA - move sector from diskette to memory via DMA.
Heads are automatically loaded and unloaded.
WRITE Multi-Track, MFM/FM Mode, Skip, 00110
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
READ DELETED DATA - same as READ DATA, but only sectors with
the deleted data address mark are read.
WRITE Multi-Track, MFM/FM Mode, Skip, 01100
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
READ TRACK - same as READ DATA, but all sectors from the index
mark to the "end of track" sector are read.
WRITE 0, MFM/FM Mode, 000010
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
WRITE DATA - move sector from memory to diskette via DMA.
Heads are automatically loaded and unloaded.
WRITE Multi-Track, MFM/FM Mode, 000101
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
WRITE DELETED DATA - same as WRITE DATA, except a deleted
data address mark is written at the beginning of the data
field instead of the normal data address mark.
WRITE Multi-Track, MFM/FM Mode, 001001
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
READ ID - the first ID field is read, and the status registers
are updated.
WRITE 0, MFM/FM Mode, 001010
WRITE 00000, Head, Drive Select
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
FORMAT TRACK - the selected track is formatted from index to
"end of track" sector with address marks, ID fields, data
fields and field gaps. The data field is filled with the
specified pattern. The ID field data is specified by the host
*for each sector*.
The DMA must be set up to send four bytes for each sector to
be formatted. The bytes are Cylinder, Head, Sector, and Sector
Size Code.
WRITE 0, MFM/FM Mode, 001101
WRITE 00000, Head, Drive Select
WRITE Sector Size Code
WRITE Sectors per Track
WRITE Gap Length
WRITE Data Pattern
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Undefined
READ Undefined
READ Undefined
READ Undefined
RECALIBRATE - the heads are stepped to track 0. But note
that some controllers give up before reaching track 0, so it
might be necessary to RECALIBRATE more than once. All
drives must be RECALIBRATEd at initialization time. There
are no result registers, so the ISR should issue a SENSE
INTERRUPT STATUS command - which will result in two result
registers.
WRITE 00000111
WRITE 000000, Drive Select
<interrupt>
SENSE INTERRUPT STATUS - the interrupt level is cleared, and
the controller status is returned.
WRITE 00001000
READ Status Register 0
READ Cylinder
SPECIFY - the head load and unload rates, the drive step rate,
and the DMA data transfer mode are set.
WRITE 00000011
WRITE Step Rate Interval, Head Unload Time
WRITE Head Load Time, Non-DMA Mode Flag
SENSE DRIVE STATUS - the status of the selected drive is
returned.
WRITE 00000100
WRITE 00000, Head, Drive Select
READ Status Register 3
SEEK - the selected drive is stepped to the new cylinder.
Seeks generally must be explicit, but on some controllers
can be made implicit via the CONFIGURE command. When not
implicit, READs and WRITEs should be preceded by SEEK, SENSE
INTERRUPT STATUS, and READ ID. There are no result
registers, so the ISR should issue a SENSE INTERRUPT STATUS
command - which does have two result registers.
WRITE 00001111
WRITE 00000, Head, Drive Select
WRITE New Cylinder Number
<interrupt>
Commands not always implemented:
Any command not implemented by a controller will result in
Status Register 0 returning x80 - "invalid command".
The following commands are implemented by some controllers,
but not by all of the ones studied:
Scan Equal
Scan Low or Equal
Scan High or Equal
Verify
Version
Configure
Relative Seek
Dump Registers
Perpendicular Mode
Data structures:
For each floppy controller in the system (there's seldom more
than one) a controller data area is allocated. This area holds
all information about the controller, and all information common
to all of the drives attached to it. It has the following
fields:
FifoBuffer[10] - data is sent to and received from the
controller via a FIFO port. A single routine,
FlIssueCommand(), is used to send the command and
parameters, and receive the result bytes. Before calling
FlIssueCommand(), the parameters should be placed into the
FifoBuffer; and after the command, the result bytes can be
read from the buffer. Note that for commands with a result
phase, the first result byte is read into the buffer by the
ISR. For commands without a result phase, the ISR writes a
SENSE INTERRUPT STATUS command, which means that *that*
command's result bytes will have to be read by
FlIssueCommand().
InterruptDelay - set to a few seconds. When we're waiting
for an interrupt, we use this as a time-out value.
Minimum10msDelay - set to 10 milliseconds, which is the
smallest unit of time that the floppy thread can block. Used
when blocking to wait for the controller to become ready to
transfer bytes through the FIFO.
ListEntry - the list of requests for the floppy driver to
process. The dispatch routines add requests to the tail of
the list, and the floppy thread takes them off at the head.
In some cases of hardware failure, the floppy thread will
reinsert a request at the head of the list to try the packet
again.
InterruptEvent - the event that is waited on when we're
expecting the controller to interrupt. It should be reset
before programming the controller, and set in the DPC that
is queued by the ISR.
AllocateAdapterChannelEvent - the event that is waited on
when we're allocating a DMA adapter channel. It should be
reset before requesting the adapter, and set by the DPC that
is queued by the IO system when the adapter channel is
available.
RequestSemaphore - the semaphore that the floppy thread waits
on. It should be released by the dispatch routines whenever
they add a request to the list. Note that it can also be
released when the thread is supposed to terminate
ListSpinLock - a spinlock that is allocated and used when
manipulating the list of requests for the floppy thread in
an atomic fashion (via the ExInterlocked* routines).
InterruptObject - a pointer to an interrupt object; it must
be passed to IoConnectInterrupt.
MapRegisterBase - the base address of the map registers.
Whenever an adapter channel is allocated, this value is passed
to our DPC. It's stored in the controller object so it can
be used when we call IoMapTransfer() and
IoFlushAdapterBuffers().
IoBuffer - this driver always needs to allocate at least a
page to format floppies. On systems where there might not
be enough map registers to map the largest possible transfer
(the size of the largest supported track on the system), a
contiguous buffer of that size is allocated. Transfers for
which there aren't enough map registers are copied to or from
this buffer, since it is contiguous.
IoBufferMdl - an MDL (memory descriptor list) to describe
"IoBuffer". Used to lock the pages, flush the buffer, etc.
AdapterObject - an object obtained from HalGetAdapter() that
must be used when allocating a DMA adapter channel.
CurrentDeviceObject - set to the device object in question
whenever an interrupt is expected. This aids debugging and
lets the ISR know whether an interrupt was expected or not.
DriverObject - We keep a pointer around to the driver object
so that we can log an error if the controller object. The reason
we don't use the CurrentDeviceObject is that we may not have one
when we get around to logging the error.
ControllerAddress - the virtual or I/O space address of the
base of the floppy controller, which is obtained from
configuration management.
IoBufferSize - the size of "IoBuffer"; it is either a single
page (if there's plenty of map registers) or the size of the
largest track supported by the drives in the system.
IsrReentered - This is counter that is used to determine
if we are in a hang condition from the controller on a
level sensitive machine.
SpanOfControllerAddress - Indicates the number of bytes
used by the controllers register set. This is only useful
when the device is being unloaded and the controller address
is being unmapped.
LastDriveMediaType - every time FlDatarateSpecifyConfigure()
is called, this value is set to the DriveMediaType that it
configured the controller to handle. When FlStartDrive() is
preparing an operation, it checks to see if the current
drive/media combination is the same - if not,
FlDatarateSpecifyConfigure() must be called again.
NumberOfMapRegisters - the number of map registers available
to this driver, obtained from HalGetAdapter(), and possibly
lowered to the maximum number needed. Each register allows
the driver to map a single page (or more if the pages are
contiguous, but that's only counted on when the driver
allocated the contiguous buffer itself). This value is used
to determine whether or not the driver needs to allocate a
contiguous buffer to accomodate a transfer the size of the
largest track, and it is passed to IoAllocateAdapterChannel().
NumberOfDrives - the number of drives attached to this
controller, obtained from the configuration manager. This
is used when allocating device objects and resetting the
hardware.
DriveControlImage - the image of the drive control register.
We must keep track of it since the register is read-only on
some systems. It should always be updated before writing to
the drive control register.
ControllerConfigurable - indicates whether or not the
CONFIGURE command is available. Assumed to be TRUE, it's
set to FALSE if an attempt to issue the CONFIGURE command
fails. Controllers with the CONFIGURE command are configured
to SEEK implicitly.
HardwareFailCount - this is only used in FlFinishOperation.
If an operation fails due to a hardware problem, we'll retry
a certain number of times, and this keeps track of how many
times we've retried.
HardwareFailed - this Boolean is set to FALSE at
initialization time and when each packet starts (even if
it's a retry). Whenever a hardware problem is encountered,
it is set to TRUE. At initialization time, failure to
initialize the hardware on the first drive means we must try
again on subsequent drives - if we never succeed, the driver
is unloaded. For each packet, hardware errors mean we'll
reset the hardware and retry the packet until it succeeds or
we overrun HardwareFailCount.
CommandHasResultPhase - whenever a command is issued,
FlIssueCommand() sets this to TRUE if the command will
return result bytes. FloppyInterruptService() uses this to
decide how to dismiss the interrupt. Note that the BUSY bit
in the STATUS register should give us this information, but
it's sometimes set when it shouldn't be.
MappedControllerAddress - Set to TRUE indicates that when
unloading the driver, the pointer to the device controller
base register should be unmapped.
For each physical floppy drive, a device object is allocated so
that the I/O system can access the drive. Attached to each device
object is a diskette extension structure, which contains
information specific to the drive and the media in it. Each
diskette extension contains a pointer to the controller data area
for the controller that it's attached to. Fields in the structure:
DeviceObject - a pointer to the device object to which this
diskette extension is appended.
ControllerData - a pointer to the controller data area that
describes the controller this drive is attached to.
DriveType - the type of the physical drive, as defined in
flo_data.h.
BytesPerSector - the bytes per sector of the media that was
last identified in the drive. Used to validate access
requests in the dispatch routines.
ByteCapacity - the total number of bytes on the media that
was last identified in the drive. Used to validate access
requests in the dispatch routines.
MediaType - the last type of media identified in the drive
(or "Unknown") as defined in ntdddisk.h. This value is used
to determine whether other media values in the diskette
extension are valid, and whether or not
FlDetermineMediaType() needs to be called.
DriveMediaType - the last drive/media combination value,
determined when the media in the drive was last identified.
This is used to index into the DriveMediaConstants table.
DeviceUnit - the controller-relative diskette number, which
is needed for some controller commands.
DriveOnValue - the value that must be written to the
Drive Control register to start up the drive in question.
It's the "DeviceUnit" plus a few bits.
The DriveMediaLimits table, which is read-only, tells the driver
which drive/media combinations are valid for a drive type. The
table is indexed by the drive type, and has LowestDriveMediaType
and HighestDriveMediaType fields. The drive/media types are
enumerated in ascending order, so the driver can start at the
highest possible combination and decrement the value until the
correct one is found (or it goes below the lowest valid
drive/media combination).
The DriveMediaConstants table, which is read-only, tells the driver
values that it needs to use when communicating with the controller
given the drive and media type. The table is indexed by the
DriveMedia number (unique for each drive/media combination) and
has the following fields:
MediaType - the type of media specified by this drive/media
combination. The drive type is stored in the diskette
extension, since it never changes.
StepRateHeadUnloadTime - a combination value that is written
to the controller as part of the SPECIFY command. The step
rate is how fast the heads can be moved track-to-track, and
the head unload time is how long it takes for the heads to
be unloaded after a READ or WRITE.
HeadLoadTime - a value that is written to the controller as
part of the SPECIFY command. The head load time is how long
it takes the heads to stabilize after a SEEK before a READ
or WRITE can be initiated.
MotorOffTime - not used by this driver, but included to keep
the table complete.
SectorLengthCode - a code number that specifies the sector
size. The sector size is 2 to the "code" power, times 128.
So 0 means 128 bytes, 1 means 256, 2 means 512, etc. This
number is needed to pass to the controller for several
commands.
BytesPerSector - the bytes per sector, which can be
determined from SectorLengthCode. Included as a separate
field for simplicity and speed.
SectorsPerTrack - the number of sectors on a track (one head
of a cylinder).
ReadWriteGapLength - the space between sectors, not
including the synchronization field. Used for READ and
WRITE commands.
FormatGapLength - gap length passed to the FORMAT command.
FormatFillCharacter - passed to the FORMAT command; this
byte will be used to fill the data fields on the disk.
HeadSettleTime - the amount of time, in milliseconds, that
the driver must wait after performing a seek.
MotorSettleTimeRead - the amount of time, in milliseconds,
that the driver must wait for the drive to spin up before
performing a READ.
MotorSettleTimeWrite - the amount of time, in milliseconds,
that the driver must wait for the drive to spin up before
performing a WRITE. Note that WRITEs are more picky about
stable rotation speeds, so the drive must always spin up for
a longer than it would for a READ.
MaximumTrack - the number of the highest track, or cylinder
on disk (zero-based).
CylinderShift - normally 0, so it does nothing. This field
is used as a shift for the cylinder number before a SEEK
command. This is because of a hardware bug; 1.2Mb drives
don't seek the proper distance for low-density media, so for
those drive/media combinations this value is 1.
DataTransferRate - the number written to the Datarate
register before accessing the media.
NumberOfHeads - number of heads on the media, always 1 or 2
for floppies.
DataLength - always 0xff, this value must be given to the
controller for a READ or WRITE command.
The CommandTable, which is read-only, tells the driver how many
bytes it has to send and receive when issuing a command to the
controller. It is indexed by the command itself (minus any extra
bits, like COMMND_MFM) and has the following fields:
NumberOfParameters - the number of parameter bytes that must
be sent along with this command.
FirstResultByte - if the command has a result phase, then the
ISR reads the first byte so this value is 1 (meaning that the
0th byte has already been read). If there is no result phase,
then the ISR must give a SENSE INTERRUPT STATUS command, and
this value is 0.
NumberOfResultBytes - the number of bytes that must be read
from the controller after this command. For commands with
a result phase, this is the number of bytes returned minus
one (which was read by the ISR); for commands without a result
phase, this is "2" since that many bytes are returned by the
SENSE INTERRUPT STATUS command that is issued by the ISR.
InterruptExpected - a Boolean that indicates whether or not
this command will give an interrupt.
AlwaysImplemented - a Boolean that indicates whether or not
this command will always work. The CONFIGURE command, for
example, is useful but not always available. This helps to
determine the correct course of action when the controller
returns an error.
Hardware quirks:
Many controllers will only step 77 tracks on a recalibrate
command. But many drives have more tracks than that, so two
recalibrates are sometimes needed.
When a 40-track diskette is in a 1.2Mb drive, the cylinder
number must be *doubled* before seeking. The number returned by
the seek will be the fake number, but the number returned by a
READ ID will be the correct cylinder. This makes implied SEEKs
impossible on low-density 5.25" media.
Some machines will give an unexpected interrupt during a
controller reset unless bit 2 of the drive control register is
set in BOTH of the bytes that are written to the register.
On fast machines, the BUSY bit is sometimes set in the main
status register even though the command doesn't have a result
phase and the command is obviously finished (as evidenced by the
fact that it interrupted; the ISR is where this problem shows
up).
On at least the NCR 8 processor machine, we have seen the driver's
system thread run before the floppy controller can start the
SENSE_INTERRUPT command. We have placed code in the ISR to spin
until the SENSE command starts. (Actually it will time
out after ISR_SENSE_RETRY_COUNT 1 microsecond stalls.)
--*/
//
// Include files.
//
#include "ntddk.h" // various NT definitions
#include "ntdddisk.h" // disk device driver I/O control codes
#include <flo_data.h> // this driver's data declarations
//
// This is the actual definition of FloppyDebugLevel.
// Note that it is only defined if this is a "debug"
// build.
//
#if DBG
extern ULONG FloppyDebugLevel = 0;
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,DriverEntry)
#pragma alloc_text(INIT,FlConfigCallBack)
#pragma alloc_text(INIT,FlGetConfigurationInformation)
#pragma alloc_text(INIT,FlReportResources)
#pragma alloc_text(INIT,FlInitializeController)
#pragma alloc_text(INIT,FlInitializeDrive)
#pragma alloc_text(INIT,FlGetControllerBase)
#pragma alloc_text(PAGE,FlInitializeControllerHardware)
#pragma alloc_text(PAGE,FlSendByte)
#pragma alloc_text(PAGE,FlGetByte)
#pragma alloc_text(PAGE,FlInterpretError)
#pragma alloc_text(PAGE,FlIssueCommand)
#pragma alloc_text(PAGE,FlDatarateSpecifyConfigure)
#pragma alloc_text(PAGE,FlRecalibrateDrive)
#pragma alloc_text(PAGE,FlDetermineMediaType)
#pragma alloc_text(PAGE,FlCheckBootSector)
#pragma alloc_text(PAGE,FlConsolidateMediaTypeWithBootSector)
#pragma alloc_text(PAGE,FlReadWriteTrack)
#pragma alloc_text(PAGE,FlReadWrite)
#pragma alloc_text(PAGE,FlFormat)
#pragma alloc_text(PAGE,FlFinishOperation)
#pragma alloc_text(PAGE,FlStartDrive)
#pragma alloc_text(PAGE,FloppyThread)
#pragma alloc_text(PAGE,FlAllocateIoBuffer)
#pragma alloc_text(PAGE,FlTurnOnMotor)
#pragma alloc_text(PAGE,FlTurnOffMotor)
#pragma alloc_text(PAGE,FlFreeIoBuffer)
#pragma alloc_text(PAGE,FlDisketteRemoved)
#pragma alloc_text(PAGE,FloppyDispatchCreateClose)
#pragma alloc_text(PAGE,FloppyDispatchDeviceControl)
#pragma alloc_text(PAGE,FloppyDispatchReadWrite)
#pragma alloc_text(PAGE,FlCheckFormatParameters)
#endif
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'polF')
#endif
// #define KEEP_COUNTERS 1
#ifdef KEEP_COUNTERS
ULONG FloppyUsedSeek = 0;
ULONG FloppyNoSeek = 0;
ULONG FloppyUsedBuffer = 0;
ULONG FloppyInterrupts = 0;
ULONG FloppySpurious = 0;
ULONG FloppyIntrBitSet = 0;
ULONG FloppyDPCs = 0;
ULONG FloppyThreadWake = 0;
LARGE_INTEGER FloppyIntrDelay = { 0, 0 };
LARGE_INTEGER FloppyDPCDelay = { 0, 0 };
LARGE_INTEGER FloppyThreadDelay = { 0, 0 };
LARGE_INTEGER FloppyFromIntrDelay = { 0, 0 };
LARGE_INTEGER FloppyThreadTime = { 0, 0 };
LARGE_INTEGER FloppyIntrTime = { 0, 0 };
LARGE_INTEGER FloppyEndIntrTime = { 0, 0 };
LARGE_INTEGER FloppyDPCTime = { 0, 0 };
#endif
//
// Used for paging the driver.
//
ULONG PagingReferenceCount = 0;
PFAST_MUTEX PagingMutex = NULL;
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine is the driver's entry point, called by the I/O system
to load the driver. 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.
--*/
{
PCONFIG_DATA configData; // pointer to config mgr's returned data
NTSTATUS ntStatus;
UCHAR controllerNumber;
BOOLEAN partlySuccessful = FALSE; // TRUE if any controller init'd properly
//
// We use this to query into the registry as to whether we
// should break at driver entry.
//
RTL_QUERY_REGISTRY_TABLE paramTable[3];
ULONG zero = 0;
ULONG one = 1;
ULONG debugLevel = 0;
ULONG shouldBreak = 0;
ULONG notConfigurable = 0;
ULONG model30 = 0;
PWCHAR path;
UNICODE_STRING parameters;
UNICODE_STRING systemPath;
UNICODE_STRING identifier;
UNICODE_STRING thinkpad, ps2e;
ULONG pathLength;
RtlInitUnicodeString(&parameters, 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.
//
if (path = ExAllocatePool(PagedPool, pathLength)) {
RtlZeroMemory(&paramTable[0],
sizeof(paramTable));
RtlZeroMemory(path, pathLength);
RtlMoveMemory(path,
RegistryPath->Buffer,
RegistryPath->Length);
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = L"BreakOnEntry";
paramTable[0].EntryContext = &shouldBreak;
paramTable[0].DefaultType = REG_DWORD;
paramTable[0].DefaultData = &zero;
paramTable[0].DefaultLength = sizeof(ULONG);
paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[1].Name = L"DebugLevel";
paramTable[1].EntryContext = &debugLevel;
paramTable[1].DefaultType = REG_DWORD;
paramTable[1].DefaultData = &zero;
paramTable[1].DefaultLength = sizeof(ULONG);
if (!NT_SUCCESS(RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
path,
&paramTable[0],
NULL,
NULL))) {
shouldBreak = 0;
debugLevel = 0;
}
//
// 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(&paramTable[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,
&paramTable[0],
NULL,
NULL))) {
notConfigurable = 0;
}
}
//
// We don't need that path anymore.
//
if (path) {
ExFreePool(path);
}
#if DBG
FloppyDebugLevel = debugLevel;
#endif
if (shouldBreak) {
DbgBreakPoint();
}
FloppyDump(FLOPSHOW,
("Floppy: DriverEntry...\n"));
//
// Ask configuration manager for information on the hardware that
// we're supposed to support.
//
ntStatus = FlGetConfigurationInformation( &configData );
//
// If FlGetConfigurationInformation() failed, just exit and propogate
// the error. If it said that there are no controllers to support,
// return an error.
// Otherwise, try to init the controllers. If at least one succeeds,
// return STATUS_SUCCESS, otherwise return the last error.
//
if ( NT_SUCCESS( ntStatus ) ) {
//
// Call FlInitializeController() for each controller (and its
// attached drives) that we're supposed to support.
//
// Return success if we successfully initialize at least one
// device; propogate error otherwise. Set an error first in
// case there aren't any controllers.
//
ntStatus = STATUS_NO_SUCH_DEVICE;
for ( controllerNumber = 0;
controllerNumber < configData->NumberOfControllers;
controllerNumber++ ) {
ntStatus = FlInitializeController(configData,
controllerNumber,
DriverObject,
notConfigurable,
model30);
if ( NT_SUCCESS( ntStatus ) ) {
partlySuccessful = TRUE;
}
}
if ( partlySuccessful ) {
ntStatus = STATUS_SUCCESS;
//
// Initialize the driver object with this driver's entry points.
//
DriverObject->MajorFunction[IRP_MJ_CREATE] =
FloppyDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] =
FloppyDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = FloppyDispatchReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = FloppyDispatchReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
FloppyDispatchDeviceControl;
}
}
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(FLOPDBGP,
("Floppy: exiting with error %lx\n", ntStatus));
}
if (configData) {
ExFreePool(configData);
}
PagingMutex = ExAllocatePool(NonPagedPool, sizeof(FAST_MUTEX));
if (!PagingMutex) {
return STATUS_INSUFFICIENT_RESOURCES;
}
ExInitializeFastMutex(PagingMutex);
MmPageEntireDriver(DriverEntry);
return ntStatus;
}
NTSTATUS
FlConfigCallBack(
IN PVOID Context,
IN PUNICODE_STRING PathName,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
IN CONFIGURATION_TYPE ControllerType,
IN ULONG ControllerNumber,
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
IN CONFIGURATION_TYPE PeripheralType,
IN ULONG PeripheralNumber,
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
)
/*++
Routine Description:
This routine is used to acquire all of the configuration
information for each floppy disk controller and the
peripheral driver attached to that controller.
Arguments:
Context - Pointer to the confuration information we are building
up.
PathName - unicode registry path. Not Used.
BusType - Internal, Isa, ...
BusNumber - Which bus if we are on a multibus system.
BusInformation - Configuration information about the bus. Not Used.
ControllerType - Should always be DiskController.
ControllerNumber - Which controller if there is more than one
controller in the system.
ControllerInformation - Array of pointers to the three pieces of
registry information.
PeripheralType - Should always be FloppyDiskPeripheral.
PeripheralNumber - Which floppy if this controller is maintaining
more than one.
PeripheralInformation - Arrya of pointers to the three pieces of
registry information.
Return Value:
STATUS_SUCCESS if everything went ok, or STATUS_INSUFFICIENT_RESOURCES
if it couldn't map the base csr or acquire the adapter object, or
all of the resource information couldn't be acquired.
--*/
{
//
// So we don't have to typecast the context.
//
PCONFIG_DATA config = Context;
//
// Simple iteration variable.
//
ULONG i;
//
// This boolean will be used to denote whether we've seen this
// controller before.
//
BOOLEAN newController;
//
// This will be used to denote whether we even have room
// for a new controller.
//
BOOLEAN outOfRoom;
//
// Iteration variable that will end up indexing to where
// the controller information should be placed.
//
ULONG ControllerSlot;
//
// Short hand for referencing the particular controller config
// information that we are building up.
//
PCONFIG_CONTROLLER_DATA controller;
PCM_FULL_RESOURCE_DESCRIPTOR peripheralData;
//
// These three boolean will tell us whether we got all the
// information that we needed.
//
BOOLEAN foundPort = FALSE;
BOOLEAN foundInterrupt = FALSE;
BOOLEAN foundDma = FALSE;
ASSERT(ControllerType == DiskController);
ASSERT(PeripheralType == FloppyDiskPeripheral);
//
// Check if the infprmation from the registry for this device
// is valid.
//
if (!(((PUCHAR)PeripheralInformation[IoQueryDeviceConfigurationData]) +
PeripheralInformation[IoQueryDeviceConfigurationData]->DataLength) ||
!(((PUCHAR)ControllerInformation[IoQueryDeviceConfigurationData]) +
ControllerInformation[IoQueryDeviceConfigurationData]->DataOffset)) {
ASSERT(FALSE);
return STATUS_INVALID_PARAMETER;
}
peripheralData = (PCM_FULL_RESOURCE_DESCRIPTOR)
(((PUCHAR)PeripheralInformation[IoQueryDeviceConfigurationData]) +
PeripheralInformation[IoQueryDeviceConfigurationData]->DataOffset);
//
// Loop through the "slots" that we have for a new controller.
// Determine if this is a controller that we've already seen,
// or a new controller.
//
outOfRoom = TRUE;
for (
ControllerSlot = 0;
ControllerSlot < MAXIMUM_CONTROLLERS_PER_MACHINE;
ControllerSlot++
) {
if (config->Controller[ControllerSlot].ActualControllerNumber == -1) {
newController = TRUE;
outOfRoom = FALSE;
config->Controller[ControllerSlot].ActualControllerNumber =
ControllerNumber;
config->NumberOfControllers++;
break;
} else if (config->Controller[ControllerSlot].ActualControllerNumber
== (LONG)ControllerNumber) {
newController = FALSE;
outOfRoom = FALSE;
break;
}
}
if (outOfRoom) {
//
// Just return and ignore the controller.
//
return STATUS_SUCCESS;
}
//
// Make sure we have room for this floppy disk peripheral.
//
if (config->Controller[ControllerSlot].NumberOfDrives >= MAXIMUM_DISKETTES_PER_CONTROLLER) {
return STATUS_SUCCESS;
}
controller = &config->Controller[ControllerSlot];
if (newController) {
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.
//
controller->InterfaceType = BusType;
controller->BusNumber = BusNumber;
controller->SharableVector = TRUE;
controller->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.
//
controller->SpanOfControllerAddress =
partial->u.Port.Length;
controller->OriginalBaseAddress =
partial->u.Port.Start;
controller->ResourcePortType =
!!partial->Flags;
controller->ControllerBaseAddress =
FlGetControllerBase(
BusType,
BusNumber,
partial->u.Port.Start,
controller->SpanOfControllerAddress,
(BOOLEAN)!!partial->Flags,
&controller->MappedAddress
);
if (!controller->ControllerBaseAddress) {
return STATUS_INSUFFICIENT_RESOURCES;
}
break;
}
case CmResourceTypeInterrupt: {
foundInterrupt = TRUE;
if (partial->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {
controller->InterruptMode = Latched;
} else {
controller->InterruptMode = LevelSensitive;
}
controller->OriginalIrql = partial->u.Interrupt.Level;
controller->OriginalVector = partial->u.Interrupt.Vector;
controller->ControllerVector =
HalGetInterruptVector(
BusType,
BusNumber,
partial->u.Interrupt.Level,
partial->u.Interrupt.Vector,
&controller->ControllerIrql,
&controller->ProcessorMask
);
break;
}
case CmResourceTypeDma: {
DEVICE_DESCRIPTION deviceDesc = {0};
// Use IgnoreCount equal to TRUE to fix PS/1000.
foundDma = TRUE;
controller->OriginalDmaChannel = partial->u.Dma.Channel;
deviceDesc.Version = DEVICE_DESCRIPTION_VERSION1;
deviceDesc.DmaWidth = Width8Bits;
deviceDesc.DemandMode = TRUE;
deviceDesc.MaximumLength =
DriveMediaConstants[NUMBER_OF_DRIVE_MEDIA_COMBINATIONS-1].
BytesPerSector *
DriveMediaConstants[NUMBER_OF_DRIVE_MEDIA_COMBINATIONS-1].
SectorsPerTrack;
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 = BusType;
deviceDesc.DmaSpeed = TypeA;
controller->AdapterObject =
HalGetAdapter(
&deviceDesc,
&controller->NumberOfMapRegisters
);
if (!controller->AdapterObject) {
return STATUS_INSUFFICIENT_RESOURCES;
}
break;
}
default: {
break;
}
}
}
//
// If we didn't get all the information then we return
// insufficient resources.
//
if ((!foundPort) ||
(!foundInterrupt) ||
(!foundDma)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// With Version 2.0 or greater for this resource list, we will get
// the full int13 information for the drive. So get that if available.
//
// Otherwise, the only thing that we want out of the peripheral information
// is the maximum drive capacity.
//
//
// Drop any other information on the floor other then the
// device specfic floppy information.
//
for (
i = 0;
i < peripheralData->PartialResourceList.Count;
i++
) {
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial =
&peripheralData->PartialResourceList.PartialDescriptors[i];
if (partial->Type == CmResourceTypeDeviceSpecific) {
//
// Point to right after this partial. This will take
// us to the beginning of the "real" device specific.
//
PCM_FLOPPY_DEVICE_DATA fDeviceData;
UCHAR driveType;
PDRIVE_MEDIA_CONSTANTS biosDriveMediaConstants =
&(controller->BiosDriveMediaConstants[controller->NumberOfDrives]);
fDeviceData = (PCM_FLOPPY_DEVICE_DATA)(partial + 1);
//
// Get the driver density
//
switch ( fDeviceData->MaxDensity ) {
case 360:
driveType = DRIVE_TYPE_0360;
break;
case 1200:
driveType = DRIVE_TYPE_1200;
break;
case 1185:
driveType = DRIVE_TYPE_1200;
break;
case 1423:
driveType = DRIVE_TYPE_1440;
break;
case 1440:
driveType = DRIVE_TYPE_1440;
break;
case 2880:
driveType = DRIVE_TYPE_2880;
break;
default:
FloppyDump(
FLOPDBGP,
("Floppy: Bad DriveCapacity!\n"
"------ density is %d\n",
fDeviceData->MaxDensity)
);
driveType = DRIVE_TYPE_1200;
FloppyDump(
FLOPDBGP,
("Floppy: run a setup program to set the floppy\n"
"------ drive type; assuming 1.2mb\n"
"------ (type is %x)\n",fDeviceData->MaxDensity)
);
break;
}
#if defined(DBCS) && defined(_MIPS_)
//
// Get the supported drive mode.
//
// If Size[0] is '3' and Size[1] is '.' and Size[2] is '5' and
// Size[6] is '3', the drive supports 3 mode. Otherwise, 2 mode.
//
//
if ((fDeviceData->Size[0] == '3') && (fDeviceData->Size[1] == '.') &&
(fDeviceData->Size[2] == '5') && (fDeviceData->Size[6] == '3')) {
controller->Drive3Mode[controller->NumberOfDrives] = DRIVE_3MODE;
} else {
controller->Drive3Mode[controller->NumberOfDrives] = DRIVE_2MODE;
}
#endif // DBCS && _MIPS_
controller->DriveType[controller->NumberOfDrives] = driveType;
//
// Pick up all the default from our own table and override
// with the BIOS information
//
*biosDriveMediaConstants = DriveMediaConstants[
DriveMediaLimits[driveType].HighestDriveMediaType];
//
// If the version is high enough, get the rest of the information.
// DeviceSpecific information with a version >= 2 should have
// this information
//
if (fDeviceData->Version >= 2) {
// biosDriveMediaConstants->MediaType =
biosDriveMediaConstants->StepRateHeadUnloadTime =
fDeviceData->StepRateHeadUnloadTime;
biosDriveMediaConstants->HeadLoadTime =
fDeviceData->HeadLoadTime;
biosDriveMediaConstants->MotorOffTime =
fDeviceData->MotorOffTime;
biosDriveMediaConstants->SectorLengthCode =
fDeviceData->SectorLengthCode;
// biosDriveMediaConstants->BytesPerSector =
if (fDeviceData->SectorPerTrack == 0) {
// This is not a valid sector per track value.
// We don't recognize this drive. This bogus
// value is often returned by SCSI floppies.
return STATUS_SUCCESS;
}
if (fDeviceData->MaxDensity == 0 ) {
//
// This values are returned by the LS-120 atapi drive.
// BIOS function 8, in int 13 is returned in bl, which is mapped
// to this field. The LS-120 returns 0x10 which is mapped to 0.
// Thats why we wont pick it up as a normal floppy.
//
return STATUS_SUCCESS;
}
biosDriveMediaConstants->SectorsPerTrack =
fDeviceData->SectorPerTrack;
biosDriveMediaConstants->ReadWriteGapLength =
fDeviceData->ReadWriteGapLength;
biosDriveMediaConstants->FormatGapLength =
fDeviceData->FormatGapLength;
biosDriveMediaConstants->FormatFillCharacter =
fDeviceData->FormatFillCharacter;
biosDriveMediaConstants->HeadSettleTime =
fDeviceData->HeadSettleTime;
biosDriveMediaConstants->MotorSettleTimeRead =
fDeviceData->MotorSettleTime * 1000 / 8;
biosDriveMediaConstants->MotorSettleTimeWrite =
fDeviceData->MotorSettleTime * 1000 / 8;
if (fDeviceData->MaximumTrackValue == 0) {
// This is not a valid maximum track value.
// We don't recognize this drive. This bogus
// value is often returned by SCSI floppies.
return STATUS_SUCCESS;
}
biosDriveMediaConstants->MaximumTrack =
fDeviceData->MaximumTrackValue;
// biosDriveMediaConstants->CylinderShift =
// NOTE CHICAGO does not use this value
//
//biosDriveMediaConstants->DataTransferRate =
// fDeviceData->DataTransferRate;
// biosDriveMediaConstants->NumberOfHeads =
biosDriveMediaConstants->DataLength =
fDeviceData->DataTransferLength;
}
}
}
controller->NumberOfDrives++;
controller->OkToUseThisController = TRUE;
return STATUS_SUCCESS;
}
NTSTATUS
FlGetConfigurationInformation(
OUT PCONFIG_DATA *ConfigData
)
/*++
Routine Description:
This routine is called by DriverEntry() to get information about the
devices to be supported from configuration mangement and/or the
hardware architecture layer (HAL).
Arguments:
ConfigData - a pointer to the pointer to a data structure that
describes the controllers and the drives attached to them
Return Value:
Returns STATUS_SUCCESS unless there is no drive 0 or we didn't get
any configuration information.
--*/
{
INTERFACE_TYPE InterfaceType;
NTSTATUS Status;
ULONG i;
*ConfigData = ExAllocatePool(PagedPool,
sizeof(CONFIG_DATA));
if (!*ConfigData) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Zero out the config structure and fill in the actual
// controller numbers with -1's so that the callback routine
// can recognize a new controller.
//
RtlZeroMemory(*ConfigData,
sizeof(CONFIG_DATA));
for (
i = 0;
i < MAXIMUM_CONTROLLERS_PER_MACHINE;
i++
) {
(*ConfigData)->Controller[i].ActualControllerNumber = -1;
}
//
// Go through all of the various bus types looking for
// disk controllers. The disk controller sections of the
// hardware registry only deal with the floppy drives.
// The callout routine that can get called will then
// look for information pertaining to a particular
// device on the controller.
//
for (
InterfaceType = 0;
InterfaceType < MaximumInterfaceType;
InterfaceType++
) {
CONFIGURATION_TYPE Dc = DiskController;
CONFIGURATION_TYPE Fp = FloppyDiskPeripheral;
Status = IoQueryDeviceDescription(&InterfaceType,
NULL,
&Dc,
NULL,
&Fp,
NULL,
FlConfigCallBack,
*ConfigData);
if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND)) {
ExFreePool(*ConfigData);
*ConfigData = NULL;
return Status;
}
}
//
// Get a pointer to the Io system location that is keeping
// a count of all the floppy devices on the system.
//
if ( IoGetConfigurationInformation() == NULL ) {
FloppyDump(FLOPDBGP,
("Floppy: configuration information is NULL\n"));
ExFreePool(*ConfigData);
*ConfigData = NULL;
return STATUS_INSUFFICIENT_RESOURCES;
}
(*ConfigData)->FloppyCount = &IoGetConfigurationInformation()->FloppyCount;
return STATUS_SUCCESS;
}
BOOLEAN
FlReportResources(
IN PDRIVER_OBJECT DriverObject,
IN PCONFIG_DATA ConfigData,
IN UCHAR ControllerNumber
)
/*++
Routine Description:
This routine will build up a resource list using the
data for this particular controller as well as all
previous *successfully* configured controllers.
N.B. This routine assumes that it called in controller
number order.
Arguments:
DriverObject - a pointer to the object that represents this device
driver.
ConfigData - a pointer to the structure that describes the
controller and the disks attached to it, as given to us by the
configuration manager.
ControllerNumber - which controller in ConfigData we are
about to try to report.
Return Value:
TRUE if no conflict was detected, FALSE otherwise.
--*/
{
ULONG sizeOfResourceList;
ULONG numberOfFrds;
ULONG i;
PCM_RESOURCE_LIST resourceList;
PCM_FULL_RESOURCE_DESCRIPTOR nextFrd;
//
// Loop through all of the controllers previous to this
// controller. If the controllers previous to this one
// didn't have a conflict, then accumulate the size of the
// CM_FULL_RESOURCE_DESCRIPTOR associated with it.
//
for (
i = 0,numberOfFrds = 0,sizeOfResourceList = 0;
i <= ControllerNumber;
i++
) {
if (ConfigData->Controller[i].OkToUseThisController) {
sizeOfResourceList += sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
//
// The full resource descriptor already contains one
// partial. Make room for three more.
//
// It will hold the irq "prd", the controller "csr" "prd" which
// is actually in two pieces since we don't use one of the
// registers, and the controller dma "prd".
//
sizeOfResourceList += 3*sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
numberOfFrds++;
}
}
//
// Now we increment the length of the resource list by field offset
// of the first frd. This will give us the length of what preceeds
// the first frd in the resource list.
//
sizeOfResourceList += FIELD_OFFSET(
CM_RESOURCE_LIST,
List[0]
);
resourceList = ExAllocatePool(
PagedPool,
sizeOfResourceList
);
if (!resourceList) {
return FALSE;
}
//
// Zero out the field
//
RtlZeroMemory(
resourceList,
sizeOfResourceList
);
resourceList->Count = numberOfFrds;
nextFrd = &resourceList->List[0];
for (
i = 0;
numberOfFrds;
i++
) {
if (ConfigData->Controller[i].OkToUseThisController) {
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
nextFrd->InterfaceType = ConfigData->Controller[i].InterfaceType;
nextFrd->BusNumber = ConfigData->Controller[i].BusNumber;
//
// We are only going to report 4 items no matter what
// was in the original.
//
nextFrd->PartialResourceList.Count = 4;
//
// Now fill in the port data. We don't wish to share
// this port range with anyone
//
partial = &nextFrd->PartialResourceList.PartialDescriptors[0];
partial->Type = CmResourceTypePort;
partial->ShareDisposition = CmResourceShareShared;
partial->Flags = (USHORT)ConfigData->Controller[i].ResourcePortType;
partial->u.Port.Start =
ConfigData->Controller[i].OriginalBaseAddress;
partial->u.Port.Length = 6;
partial++;
partial->Type = CmResourceTypePort;
partial->ShareDisposition = CmResourceShareShared;
partial->Flags = (USHORT)ConfigData->Controller[i].ResourcePortType;
partial->u.Port.Start.QuadPart =
ConfigData->Controller[i].OriginalBaseAddress.QuadPart + 7;
partial->u.Port.Length = 1;
partial++;
partial->Type = CmResourceTypeDma;
partial->ShareDisposition = CmResourceShareShared;
partial->Flags = 0;
partial->u.Dma.Channel =
ConfigData->Controller[i].OriginalDmaChannel;
partial++;
//
// Now fill in the irq stuff.
//
partial->Type = CmResourceTypeInterrupt;
partial->u.Interrupt.Level =
ConfigData->Controller[i].OriginalIrql;
partial->u.Interrupt.Vector =
ConfigData->Controller[i].OriginalVector;
// if (nextFrd->InterfaceType == MicroChannel) {
//
// partial->ShareDisposition = CmResourceShareShared;
//
// } else {
//
// partial->ShareDisposition = CmResourceShareDriverExclusive;
//
// }
partial->ShareDisposition = CmResourceShareShared;
if (ConfigData->Controller[i].InterruptMode == Latched) {
partial->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
} else {
partial->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
}
partial++;
nextFrd = (PVOID)partial;
numberOfFrds--;
}
}
IoReportResourceUsage(
NULL,
DriverObject,
resourceList,
sizeOfResourceList,
NULL,
NULL,
0,
FALSE,
&ConfigData->Controller[ControllerNumber].OkToUseThisController
);
//
// The above routine sets the boolean the parameter
// to TRUE if a conflict was detected.
//
ConfigData->Controller[ControllerNumber].OkToUseThisController =
!ConfigData->Controller[ControllerNumber].OkToUseThisController;
ExFreePool(resourceList);
return ConfigData->Controller[ControllerNumber].OkToUseThisController;
}
NTSTATUS
FlInitializeController(
IN PCONFIG_DATA ConfigData,
IN UCHAR ControllerNumber,
IN PDRIVER_OBJECT DriverObject,
IN ULONG NotConfigurable,
IN ULONG Model30
)
/*++
Routine Description:
This routine is called at initialization time by DriverEntry() -
once for each controller that the configuration manager tells it we
have to support.
When this routine is called, the configuration data has already been
filled in.
Arguments:
ConfigData - a pointer to the structure that describes the
controller and the disks attached to it, as given to us by the
configuration manager.
ControllerNumber - which controller in ConfigData we are
initializing.
DriverObject - a pointer to the object that represents this device
driver.
NotConfigurable - Supplies whether or not the controller is configurable.
Model30 - Supplies whether or not this is a model 30 floppy controller.
Return Value:
STATUS_SUCCESS if this controller and at least one of its disks were
initialized; an error otherwise.
--*/
{
PCONTROLLER_DATA controllerData;
NTSTATUS ntStatus;
NTSTATUS ntStatus2;
UCHAR driveNumber;
BOOLEAN partlySuccessful;
UCHAR ntNameBuffer[256];
STRING ntNameString;
UNICODE_STRING ntUnicodeString;
OBJECT_ATTRIBUTES objectAttributes;
FloppyDump(FLOPSHOW,
("Floppy: FlInitializeController...\n"));
//
// This routine will attempt to "append" the resources
// used by this controller into the resource map of the
// registry. If there was a conflict with previously "declared"
// data, then this routine will return false, in which case we
// will NOT try to initialize this particular controller.
//
if (!FlReportResources(DriverObject,
ConfigData,
ControllerNumber)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Allocate and zero-initialize data to describe this controller
//
controllerData = (PCONTROLLER_DATA) ExAllocatePool(NonPagedPool,
sizeof(CONTROLLER_DATA));
if ( controllerData == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory( controllerData, sizeof( CONTROLLER_DATA ) );
(VOID) sprintf(ntNameBuffer,
"\\Device\\FloppyControllerEvent%d",
ControllerNumber);
RtlInitString( &ntNameString, ntNameBuffer );
ntStatus = RtlAnsiStringToUnicodeString(&ntUnicodeString,
&ntNameString,
TRUE);
InitializeObjectAttributes(&objectAttributes,
&ntUnicodeString,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
NULL,
NULL);
controllerData->ControllerEvent = IoCreateSynchronizationEvent(
&ntUnicodeString,
&controllerData->ControllerEventHandle);
RtlFreeUnicodeString( &ntUnicodeString );
if ( !NT_SUCCESS( ntStatus ) ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Stick the driver object into it so that we can use it to log
// an error if the controller hangs up.
//
controllerData->DriverObject = DriverObject;
//
// Fill in some items that we got from configuration management and
// the HAL.
//
controllerData->ControllerAddress =
ConfigData->Controller[ControllerNumber].ControllerBaseAddress;
controllerData->AdapterObject =
ConfigData->Controller[ControllerNumber].AdapterObject;
controllerData->NumberOfMapRegisters =
ConfigData->Controller[ControllerNumber].NumberOfMapRegisters;
controllerData->NumberOfDrives =
ConfigData->Controller[ControllerNumber].NumberOfDrives;
//
// Set the time to wait for an interrupt before timing out to a
// few seconds.
//
controllerData->InterruptDelay.QuadPart = -(10 * 1000 * 4000);
//
// 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.
//
controllerData->Minimum10msDelay.QuadPart = -(10 * 1000 * 10);
//
// 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(
&controllerData->LogErrorDpc,
FlLogErrorDpc,
controllerData
);
//
// Assume there is a CONFIGURE command until found otherwise.
// Other Booleans were zero-initialized to FALSE.
//
controllerData->ControllerConfigurable = NotConfigurable ? FALSE : TRUE;
controllerData->Model30 = Model30 ? TRUE : FALSE;
//
// Save interrupt information for connecting the interrupt later.
//
controllerData->ControllerVector = ConfigData->Controller[ControllerNumber].ControllerVector;
controllerData->ControllerIrql = ConfigData->Controller[ControllerNumber].ControllerIrql;
controllerData->ControllerIrql = ConfigData->Controller[ControllerNumber].ControllerIrql;
controllerData->InterruptMode = ConfigData->Controller[ControllerNumber].InterruptMode;
controllerData->SharableVector = ConfigData->Controller[ControllerNumber].SharableVector;
controllerData->ProcessorMask = ConfigData->Controller[ControllerNumber].ProcessorMask;
controllerData->SaveFloatState = ConfigData->Controller[ControllerNumber].SaveFloatState;
controllerData->AllowInterruptProcessing = controllerData->CurrentInterrupt = TRUE;
ntStatus = IoConnectInterrupt(
&controllerData->InterruptObject,
FloppyInterruptService,
controllerData,
NULL,
ConfigData->Controller[ControllerNumber].ControllerVector,
ConfigData->Controller[ControllerNumber].ControllerIrql,
ConfigData->Controller[ControllerNumber].ControllerIrql,
ConfigData->Controller[ControllerNumber].InterruptMode,
ConfigData->Controller[ControllerNumber].SharableVector,
ConfigData->Controller[ControllerNumber].ProcessorMask,
ConfigData->Controller[ControllerNumber].SaveFloatState);
controllerData->CurrentInterrupt = FALSE;
if (NT_SUCCESS(ntStatus)) {
//
// Initialize the interlocked request queue, including a
// counting semaphore to indicate items in the queue
//
KeInitializeSemaphore(
&controllerData->RequestSemaphore,
0L,
MAXLONG );
KeInitializeSpinLock( &controllerData->ListSpinLock );
ExInitializeFastMutex( &controllerData->ThreadReferenceMutex );
InitializeListHead( &controllerData->ListEntry );
controllerData->ThreadReferenceCount = -1;
//
// Initialize events to signal interrupts and adapter object
// allocation
//
KeInitializeEvent(
&controllerData->InterruptEvent,
SynchronizationEvent,
FALSE);
KeInitializeEvent(
&controllerData->AllocateAdapterChannelEvent,
NotificationEvent,
FALSE );
controllerData->AdapterChannelRefCount = 0;
//
// Call FlInitializeDrive() for each drive on the
// controller
//
ntStatus = STATUS_NO_SUCH_DEVICE;
partlySuccessful = FALSE;
for ( driveNumber = 0;
driveNumber < controllerData->NumberOfDrives;
driveNumber++ ) {
ntStatus = FlInitializeDrive(
ConfigData,
controllerData,
ControllerNumber,
driveNumber,
driveNumber,
DriverObject );
if ( NT_SUCCESS( ntStatus ) ) {
( *( ConfigData->FloppyCount ) )++;
partlySuccessful = TRUE;
}
}
if ( partlySuccessful ) {
ntStatus = STATUS_SUCCESS;
}
IoDisconnectInterrupt(controllerData->InterruptObject);
}
//
// If we're exiting with an error, clean up first.
//
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: InitializeController failing\n")
);
ExFreePool( controllerData );
}
return ntStatus;
}
NTSTATUS
FlInitializeControllerHardware(
IN PCONTROLLER_DATA ControllerData,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine is called at initialization time by FlInitializeDrive()
- once for each controller that we have to support. It is also
called by FlFinishOperation() when an operation appears to have
failed due to a hardware problem.
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;
UCHAR statusRegister0;
UCHAR cylinder;
UCHAR driveNumber;
UCHAR retrycnt;
PDISKETTE_EXTENSION disketteExtension;
FloppyDump(
FLOPSHOW,
("Floppy: FlInitializeControllerHardware...\n")
);
disketteExtension = DeviceObject->DeviceExtension;
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.
//
ControllerData->DriveControlImage |= DRVCTL_ENABLE_DMA_AND_INTERRUPTS;
ControllerData->DriveControlImage &= ~( DRVCTL_ENABLE_CONTROLLER );
#ifdef _PPC_
ControllerData->DriveControlImage |= DRVCTL_DRIVE_MASK;
#endif
WRITE_CONTROLLER(
&ControllerData->ControllerAddress->DriveControl,
ControllerData->DriveControlImage );
KeStallExecutionProcessor( 10 );
ControllerData->CurrentDeviceObject = DeviceObject;
ControllerData->AllowInterruptProcessing = TRUE;
ControllerData->CommandHasResultPhase = FALSE;
KeResetEvent( &ControllerData->InterruptEvent );
ControllerData->DriveControlImage |= DRVCTL_ENABLE_CONTROLLER;
WRITE_CONTROLLER(
&ControllerData->ControllerAddress->DriveControl,
ControllerData->DriveControlImage );
//
// 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(
&ControllerData->InterruptEvent,
Executive,
KernelMode,
FALSE,
&ControllerData->InterruptDelay );
if (ntStatus == STATUS_TIMEOUT) {
if (retrycnt >= 1) {
break;
}
// Retry reset after configure command to enable polling
// interrupt.
ControllerData->FifoBuffer[0] = 0;
ControllerData->FifoBuffer[1] = COMMND_CONFIGURE_FIFO_THRESHOLD;
ControllerData->FifoBuffer[2] = 0;
ntStatus = FlIssueCommand(COMMND_CONFIGURE, disketteExtension);
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;
ControllerData->HardwareFailed = TRUE;
}
#ifdef KEEP_COUNTERS
FloppyThreadTime = KeQueryPerformanceCounter((PVOID)NULL);
FloppyThreadDelay.QuadPart = FloppyThreadDelay.QuadPart +
(FloppyDPCTime.QuadPart -
FloppyThreadTime.QuadPart);
FloppyFromIntrDelay.QuadPart = FloppyFromIntrDelay.QuadPart +
(FloppyIntrTime.QuadPart -
FloppyThreadTime.QuadPart);
FloppyThreadWake++;
#endif
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: controller didn't interrupt after reset\n")
);
return ntStatus;
}
//
// Sense interrupt status for all drives.
//
for ( driveNumber = 0;
( driveNumber < ControllerData->NumberOfDrives ) &&
( NT_SUCCESS( ntStatus ) );
driveNumber++ ) {
if ( driveNumber != 0 ) {
//
// Note that the ISR issued first SENSE INTERRUPT for us.
//
ntStatus = FlSendByte( COMMND_SENSE_INTERRUPT, ControllerData );
}
if ( NT_SUCCESS( ntStatus ) ) {
ntStatus = FlGetByte( &statusRegister0, ControllerData );
if ( NT_SUCCESS( ntStatus ) ) {
ntStatus = FlGetByte( &cylinder, ControllerData );
}
}
}
//
// Set PERPENDICULAR MODE for all drives that are perpendicular.
//
if ( ControllerData->PerpendicularDrives != 0 ) {
ntStatus = FlSendByte( COMMND_PERPENDICULAR_MODE, ControllerData );
if ( NT_SUCCESS( ntStatus ) ) {
ntStatus = FlSendByte( (UCHAR) (COMMND_PERPENDICULAR_MODE_OW |
(ControllerData->PerpendicularDrives << 2)),
ControllerData );
}
}
return ntStatus;
}
NTSTATUS
FlInitializeDrive(
IN PCONFIG_DATA ConfigData,
IN PCONTROLLER_DATA ControllerData,
IN UCHAR ControllerNum,
IN UCHAR DisketteNum,
IN UCHAR DisketteUnit,
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine is called at initialization time by
FlInitializeController(), once for each disk that we are supporting
on the controller.
Arguments:
ConfigData - a pointer to the structure that describes the
controller and the disks attached to it, as given to us by the
configuration manager.
ControllerData - a pointer to our data area for this controller.
ControllerNum - which controller in ConfigData we're working on.
DisketteNum - which logical disk on the current controller we're
working on.
DisketteUnit - which physical disk on the current controller we're
working on. Only different from DisketteNum when we're creating a
secondary device object for a previously initialized drive.
DriverObject - a pointer to the object that represents this device
driver.
Return Value:
STATUS_SUCCESS if this disk is initialized; an error otherwise.
--*/
{
UCHAR ntNameBuffer[256];
UCHAR arcNameBuffer[256];
STRING ntNameString;
STRING arcNameString;
UNICODE_STRING ntUnicodeString;
UNICODE_STRING arcUnicodeString;
NTSTATUS ntStatus;
PDEVICE_OBJECT deviceObject = NULL;
PDISKETTE_EXTENSION disketteExtension;
FloppyDump(
FLOPSHOW,
("Floppy: FlInitializeDrive...\n")
);
sprintf(
ntNameBuffer,
"\\Device\\Floppy%d",
*( ConfigData->FloppyCount ) );
RtlInitString( &ntNameString, ntNameBuffer );
ntStatus = RtlAnsiStringToUnicodeString(
&ntUnicodeString,
&ntNameString,
TRUE );
if ( NT_SUCCESS( ntStatus ) ) {
//
// Create a device object for this floppy drive.
//
ntStatus = IoCreateDevice(
DriverObject,
sizeof( DISKETTE_EXTENSION ),
&ntUnicodeString,
FILE_DEVICE_DISK,
FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE,
FALSE,
&deviceObject );
if ( NT_SUCCESS( ntStatus ) ) {
//
// Create a symbolic link from the disk name to the corresponding
// ARC name, to be used if we're booting off the disk. This will
// if it's not system initialization time; that's fine. The ARC
// name looks something like \ArcName\multi(0)disk(0)rdisk(0).
//
sprintf(
arcNameBuffer,
"%s(%d)disk(%d)fdisk(%d)",
"\\ArcName\\multi",
ConfigData->Controller[ControllerNum].BusNumber,
ConfigData->Controller[ControllerNum].ActualControllerNumber,
DisketteNum );
RtlInitString( &arcNameString, arcNameBuffer );
ntStatus = RtlAnsiStringToUnicodeString(
&arcUnicodeString,
&arcNameString,
TRUE );
if ( !NT_SUCCESS( ntStatus ) ) {
RtlFreeUnicodeString( &ntUnicodeString );
} else {
IoAssignArcName( &arcUnicodeString, &ntUnicodeString );
RtlFreeUnicodeString( &ntUnicodeString );
RtlFreeUnicodeString( &arcUnicodeString );
//
// Initialize the DPC structure in the device object, so that
// the ISR can queue DPCs.
//
IoInitializeDpcRequest( deviceObject, FloppyDeferredProcedure );
deviceObject->Flags |= DO_DIRECT_IO;
if ( deviceObject->AlignmentRequirement < FILE_WORD_ALIGNMENT ) {
deviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
}
//
// Copy the drive type from the configuration info; note that
// the media type has not been determined.
//
disketteExtension = deviceObject->DeviceExtension;
disketteExtension->ControllerData = ControllerData;
disketteExtension->DeviceObject = deviceObject;
disketteExtension->DeviceUnit = DisketteUnit;
disketteExtension->DriveOnValue = (UCHAR)( DisketteUnit |
( DRVCTL_ENABLE_CONTROLLER +
DRVCTL_ENABLE_DMA_AND_INTERRUPTS ) |
( DRVCTL_DRIVE_0 << DisketteUnit ) );
disketteExtension->IsReadOnly = FALSE;
disketteExtension->DriveType = ConfigData->
Controller[ControllerNum].DriveType[DisketteUnit];
disketteExtension->MediaType = Undetermined;
disketteExtension->BiosDriveMediaConstants = ConfigData->
Controller[ControllerNum].BiosDriveMediaConstants[DisketteUnit];
#if defined(DBCS) && defined(_MIPS_)
disketteExtension->Drive3Mode = ConfigData->
Controller[ControllerNum].Drive3Mode[DisketteUnit];
#endif // DBCS && _MIPS_
if ( disketteExtension->DriveType == DRIVE_TYPE_2880 ) {
ControllerData->PerpendicularDrives |= 1 << DisketteUnit;
}
//
// FlInitializeControllerHardware() should logically be called
// from FlInitializeController(). But the hardware can't be
// initialized until a device object exists, since the ISR has
// to queue a DPC. So we do it here, where we get a chance to
// clean up easily if FlInitializeControllerHardware() fails.
//
// The controller is normally only reset once, when the first
// diskette is initialized. But we'll try again on subsequent
// diskettes if it failed on the first one.
//
if ( ( DisketteNum == 0 ) ||
( disketteExtension->DriveType == DRIVE_TYPE_2880 ) ||
( ControllerData->HardwareFailed ) ) {
ControllerData->AllowInterruptProcessing = ControllerData->CurrentInterrupt = TRUE;
ntStatus = FlInitializeControllerHardware(
ControllerData,
deviceObject );
ControllerData->CurrentInterrupt = FALSE;
if ( NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPSHOW,
("Floppy: DriveType = %x\n",
disketteExtension->DriveType)
);
ControllerData->HardwareFailed = FALSE;
} else {
ControllerData->HardwareFailed = TRUE;
}
}
}
}
}
//
// If we're failing, clean up - stop the timer (it doesn't hurt to
// stop it even if it wasn't started) and delete the device object.
//
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: InitiaiizeDrive failing\n")
);
if ( deviceObject != NULL ) {
IoDeleteDevice( deviceObject );
}
}
return ntStatus;
}
NTSTATUS
FlQueueIrpToThread(
IN OUT PIRP Irp,
IN OUT PCONTROLLER_DATA ControllerData
)
/*++
Routine Description:
This routine queues the given irp to be serviced by the controller's
thread. If the thread is down then this routine creates the thread.
Arguments:
Irp - Supplies the IRP to queue to the controller's thread.
ControllerData - Supplies the controller data.
Return Value:
May return an error if PsCreateSystemThread fails.
Otherwise returns STATUS_PENDING and marks the IRP pending.
--*/
{
KIRQL oldIrql;
NTSTATUS status;
HANDLE threadHandle;
ExAcquireFastMutex(&ControllerData->ThreadReferenceMutex);
if (++(ControllerData->ThreadReferenceCount) == 0) {
ControllerData->ThreadReferenceCount++;
ExAcquireFastMutex(PagingMutex);
if (++PagingReferenceCount == 1) {
// Lock down the driver.
MmResetDriverPaging(DriverEntry);
}
ExReleaseFastMutex(PagingMutex);
// Create the thread.
status = PsCreateSystemThread(&threadHandle,
(ACCESS_MASK) 0L,
NULL,
(HANDLE) 0L,
NULL,
FloppyThread,
ControllerData);
if (!NT_SUCCESS(status)) {
ControllerData->ThreadReferenceCount = -1;
ExAcquireFastMutex(PagingMutex);
if (--PagingReferenceCount == 0) {
MmPageEntireDriver(DriverEntry);
}
ExReleaseFastMutex(PagingMutex);
ExReleaseFastMutex(&ControllerData->ThreadReferenceMutex);
return status;
}
ExReleaseFastMutex(&ControllerData->ThreadReferenceMutex);
ZwClose(threadHandle);
} else {
ExReleaseFastMutex(&ControllerData->ThreadReferenceMutex);
}
IoMarkIrpPending(Irp);
ExInterlockedInsertTailList(
&ControllerData->ListEntry,
&Irp->Tail.Overlay.ListEntry,
&ControllerData->ListSpinLock );
KeReleaseSemaphore(
&ControllerData->RequestSemaphore,
(KPRIORITY) 0,
1,
FALSE );
return STATUS_PENDING;
}
NTSTATUS
FloppyDispatchCreateClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called only rarely by the I/O system; it's mainly
for layered drivers to call. All it does is complete the IRP
successfully.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
Always returns STATUS_SUCCESS, since this is a null operation.
--*/
{
UNREFERENCED_PARAMETER( DeviceObject );
FloppyDump(
FLOPSHOW,
("Floppy: DispatchCreateClose...\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
FloppyDispatchDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to perform a device I/O
control function.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
STATUS_SUCCESS or STATUS_PENDING if recognized I/O control code,
STATUS_INVALID_DEVICE_REQUEST otherwise.
--*/
{
PIO_STACK_LOCATION irpSp;
PDISKETTE_EXTENSION disketteExtension;
PDISK_GEOMETRY outputBuffer;
NTSTATUS ntStatus;
ULONG outputBufferLength;
UCHAR i;
DRIVE_MEDIA_TYPE lowestDriveMediaType;
DRIVE_MEDIA_TYPE highestDriveMediaType;
ULONG formatExParametersSize;
PFORMAT_EX_PARAMETERS formatExParameters;
#if defined(DBCS) && defined(_MIPS_)
ULONG inputBufferLength;
ULONG inputBuffer;
#endif // DBCS && _MIPS_
FloppyDump(
FLOPSHOW,
("Floppy: DispatchDeviceControl...\n")
);
disketteExtension = DeviceObject->DeviceExtension;
irpSp = IoGetCurrentIrpStackLocation( Irp );
switch( irpSp->Parameters.DeviceIoControl.IoControlCode ) {
case IOCTL_DISK_FORMAT_TRACKS:
case IOCTL_DISK_FORMAT_TRACKS_EX:
//
// Make sure that we got all the necessary format parameters.
//
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof( FORMAT_PARAMETERS ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: invalid FORMAT buffer length\n")
);
ntStatus = STATUS_INVALID_PARAMETER;
break;
}
//
// Make sure the parameters we got are reasonable.
//
if ( !FlCheckFormatParameters(
disketteExtension,
(PFORMAT_PARAMETERS) Irp->AssociatedIrp.SystemBuffer ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: invalid FORMAT parameters\n")
);
ntStatus = STATUS_INVALID_PARAMETER;
break;
}
//
// If this is an EX request then make a couple of extra checks
//
if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_DISK_FORMAT_TRACKS_EX) {
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(FORMAT_EX_PARAMETERS)) {
ntStatus = STATUS_INVALID_PARAMETER;
break;
}
formatExParameters = (PFORMAT_EX_PARAMETERS)
Irp->AssociatedIrp.SystemBuffer;
formatExParametersSize =
FIELD_OFFSET(FORMAT_EX_PARAMETERS, SectorNumber) +
formatExParameters->SectorsPerTrack*sizeof(USHORT);
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
formatExParametersSize ||
formatExParameters->FormatGapLength >= 0x100 ||
formatExParameters->SectorsPerTrack >= 0x100) {
ntStatus = STATUS_INVALID_PARAMETER;
break;
}
}
//
// Fall through to queue the request.
//
case IOCTL_DISK_CHECK_VERIFY:
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
case IOCTL_DISK_IS_WRITABLE:
//
// The thread must know which diskette to operate on, but the
// request list only passes the IRP. So we'll stick a pointer
// to the diskette extension in Type3InputBuffer, which is
// a field that isn't used for floppy ioctls.
//
//
// Add the request to the queue, and wake up the thread to
// process it.
//
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)
disketteExtension;
FloppyDump(
FLOPIRPPATH,
("Floppy: Enqueing up IRP: %x\n",Irp)
);
ntStatus = FlQueueIrpToThread(Irp,
disketteExtension->ControllerData);
break;
case IOCTL_DISK_GET_MEDIA_TYPES: {
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_DISK_GET_MEDIA_TYPES called\n")
);
lowestDriveMediaType = DriveMediaLimits[
disketteExtension->DriveType].LowestDriveMediaType;
highestDriveMediaType = DriveMediaLimits[
disketteExtension->DriveType].HighestDriveMediaType;
outputBufferLength =
irpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// Make sure that the input buffer has enough room to return
// at least one descriptions of a supported media type.
//
if ( outputBufferLength < ( sizeof( DISK_GEOMETRY ) ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: invalid GET_MEDIA_TYPES buffer size\n")
);
ntStatus = STATUS_BUFFER_TOO_SMALL;
break;
}
//
// Assume success, although we might modify it to a buffer
// overflow warning below (if the buffer isn't big enough
// to hold ALL of the media descriptions).
//
ntStatus = STATUS_SUCCESS;
if ( outputBufferLength < ( sizeof( DISK_GEOMETRY ) *
( highestDriveMediaType - lowestDriveMediaType + 1 ) ) ) {
//
// The buffer is too small for all of the descriptions;
// calculate what CAN fit in the buffer.
//
FloppyDump(
FLOPDBGP,
("Floppy: GET_MEDIA_TYPES buffer size too small\n")
);
ntStatus = STATUS_BUFFER_OVERFLOW;
highestDriveMediaType = (DRIVE_MEDIA_TYPE)( ( lowestDriveMediaType - 1 ) +
( outputBufferLength /
sizeof( DISK_GEOMETRY ) ) );
}
outputBuffer = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer;
for (
i = (UCHAR)lowestDriveMediaType;
i <= (UCHAR)highestDriveMediaType;
i++ ) {
outputBuffer->MediaType = DriveMediaConstants[i].MediaType;
outputBuffer->Cylinders.LowPart =
DriveMediaConstants[i].MaximumTrack + 1;
outputBuffer->Cylinders.HighPart = 0;
outputBuffer->TracksPerCylinder =
DriveMediaConstants[i].NumberOfHeads;
outputBuffer->SectorsPerTrack =
DriveMediaConstants[i].SectorsPerTrack;
outputBuffer->BytesPerSector =
DriveMediaConstants[i].BytesPerSector;
FloppyDump(
FLOPSHOW,
("Floppy: media types supported [%d]\n"
"------- Cylinders low: 0x%x\n"
"------- Cylinders high: 0x%x\n"
"------- Track/Cyl: 0x%x\n"
"------- Sectors/Track: 0x%x\n"
"------- Bytes/Sector: 0x%x\n"
"------- Media Type: %d\n",
i,
outputBuffer->Cylinders.LowPart,
outputBuffer->Cylinders.HighPart,
outputBuffer->TracksPerCylinder,
outputBuffer->SectorsPerTrack,
outputBuffer->BytesPerSector,
outputBuffer->MediaType)
);
outputBuffer++;
Irp->IoStatus.Information += sizeof( DISK_GEOMETRY );
}
break;
}
#if defined(DBCS) && defined(_MIPS_)
case IOCTL_DISK_GET_REMOVABLE_TYPES: {
outputBufferLength =
irpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// Make sure that the input buffer has enough room to return
// at least one descriptions of a supported media type.
//
if ( outputBufferLength < ( sizeof( DDRIVE_TYPE ) ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: invalid GET_REMOVABLE_TYPES buffer size\n")
);
ntStatus = STATUS_BUFFER_TOO_SMALL;
break;
}
(PDDRIVE_TYPE) outputBuffer
= (PDDRIVE_TYPE) Irp->AssociatedIrp.SystemBuffer;
((PDDRIVE_TYPE)outputBuffer)->DDrive_Type = 1;
Irp->IoStatus.Information = sizeof( DDRIVE_TYPE );
ntStatus = STATUS_SUCCESS;
break;
}
case IOCTL_DISK_SET_MEDIA_TYPE: {
FloppyDump(
FLOPDBGP,
("Floppy: SET_MEDIA_TYPE \n")
);
outputBufferLength =
irpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// Make sure that the input buffer has enough room to return
// at least one descriptions of a supported media type.
//
if ( outputBufferLength < ( sizeof( MEDIA_TYPE_PTOS ) ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: invalid SET_MEDIA_TYPE buffer size\n")
);
ntStatus = STATUS_BUFFER_TOO_SMALL;
break;
}
(PMEDIA_TYPE_PTOS)outputBuffer
=(PMEDIA_TYPE_PTOS)Irp->AssociatedIrp.SystemBuffer;
Set_Media_Type_PTOS[disketteExtension->DeviceUnit].Media_Type_PTOS
=((PMEDIA_TYPE_PTOS)outputBuffer)->Media_Type_PTOS;
ntStatus = STATUS_SUCCESS;
break;
}
case IOCTL_DISK_READ:
case IOCTL_DISK_WRITE: {
FloppyDump(
FLOPDBGP,
("Floppy: IOCTL READ WRITE \n")
);
//
// Make sure that we got all the necessary IOCTL read write parameters.
//
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof( DISK_READ_WRITE_PARAMETER_PTOS ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: invalid IOCTL READ WRITE buffer length\n")
);
ntStatus = STATUS_INVALID_PARAMETER;
break;
}
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)
disketteExtension;
FloppyDump(
FLOPIRPPATH,
("Floppy: Enqueing up IRP: %x\n",Irp)
);
ntStatus = FlQueueIrpToThread(Irp,
disketteExtension->ControllerData);
break;
}
case IOCTL_DISK_GET_STATUS: {
FloppyDump(
FLOPDBGP,
("Floppy: GET_STATUS \n")
);
outputBufferLength =
irpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// Make sure that the input buffer has enough room to return
// at least one descriptions of a supported media type.
//
if ( outputBufferLength < ( sizeof( RESULT_STATUS_PTOS ) ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: invalid GET_STATUS buffer size\n")
);
ntStatus = STATUS_BUFFER_TOO_SMALL;
break;
}
(PRESULT_STATUS_PTOS)inputBuffer
=(PRESULT_STATUS_PTOS)Irp->AssociatedIrp.SystemBuffer;
((PRESULT_STATUS_PTOS)inputBuffer)->ST0_PTOS
=Result_Status_PTOS[0].ST0_PTOS;
((PRESULT_STATUS_PTOS)inputBuffer)->ST1_PTOS
=Result_Status_PTOS[0].ST1_PTOS;
((PRESULT_STATUS_PTOS)inputBuffer)->ST2_PTOS
=Result_Status_PTOS[0].ST2_PTOS;
((PRESULT_STATUS_PTOS)inputBuffer)->C_PTOS
=Result_Status_PTOS[0].C_PTOS;
((PRESULT_STATUS_PTOS)inputBuffer)->H_PTOS
=Result_Status_PTOS[0].H_PTOS;
((PRESULT_STATUS_PTOS)inputBuffer)->R_PTOS
=Result_Status_PTOS[0].R_PTOS;
((PRESULT_STATUS_PTOS)inputBuffer)->N_PTOS
=Result_Status_PTOS[0].N_PTOS;
Irp->IoStatus.Information = sizeof( RESULT_STATUS_PTOS );
ntStatus = STATUS_SUCCESS;
break;
}
case IOCTL_DISK_SENSE_DEVICE: {
FloppyDump(
FLOPDBGP,
("Floppy: SENSE_DEVISE_STATUS \n")
);
//
// Make sure that we got all the necessary IOCTL read write parameters.
//
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( SENSE_DEVISE_STATUS_PTOS ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: invalid SENSE_DEVISE_STATUS buffer length\n")
);
ntStatus = STATUS_INVALID_PARAMETER;
break;
}
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)
disketteExtension;
FloppyDump(
FLOPIRPPATH,
("Floppy: Enqueing up IRP: %x\n",Irp)
);
ntStatus = FlQueueIrpToThread(Irp,
disketteExtension->ControllerData);
break;
}
#endif // DBCS && _MIPS_
default: {
FloppyDump(
FLOPDBGP,
("Floppy: 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 );
}
IoCompleteRequest( Irp, IO_NO_INCREMENT );
}
return ntStatus;
}
NTSTATUS
FloppyDispatchReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to read or write to a
device that we control.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
STATUS_INVALID_PARAMETER if parameters are invalid,
STATUS_PENDING otherwise.
--*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS ntStatus;
PDISKETTE_EXTENSION disketteExtension;
FloppyDump(
FLOPSHOW,
("Floppy: FloppyDispatchReadWrite...\n")
);
disketteExtension = DeviceObject->DeviceExtension;
irpSp = IoGetCurrentIrpStackLocation( Irp );
if ( ( disketteExtension->MediaType > Unknown ) &&
( ( ( irpSp->Parameters.Read.ByteOffset ).LowPart +
irpSp->Parameters.Read.Length > disketteExtension->ByteCapacity ) ||
( ( irpSp->Parameters.Read.Length &
( disketteExtension->BytesPerSector - 1 ) ) != 0 ) ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: Invalid Parameter, rejecting request\n")
);
FloppyDump(
FLOPWARN,
("Floppy: Starting offset = %lx\n"
"------ I/O Length = %lx\n"
"------ ByteCapacity = %lx\n"
"------ BytesPerSector = %lx\n",
irpSp->Parameters.Read.ByteOffset.LowPart,
irpSp->Parameters.Read.Length,
disketteExtension->ByteCapacity,
disketteExtension->BytesPerSector)
);
ntStatus = STATUS_INVALID_PARAMETER;
} else {
//
// We need to pass the disketteExtension somewhere in the irp.
// The "Key" field in our stack location should be unused.
//
irpSp->Parameters.Read.Key = (ULONG) disketteExtension;
FloppyDump(
FLOPIRPPATH,
("Floppy: Enqueing up IRP: %x\n",Irp)
);
ntStatus = FlQueueIrpToThread(Irp, disketteExtension->ControllerData);
}
if (ntStatus != STATUS_PENDING) {
Irp->IoStatus.Status = ntStatus;
IoCompleteRequest(Irp, 0);
}
return ntStatus;
}
BOOLEAN
FloppyInterruptService(
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.
--*/
{
PCONTROLLER_DATA controllerData;
PDEVICE_OBJECT currentDeviceObject;
ULONG i;
UCHAR statusByte;
BOOLEAN controllerStateError;
UNREFERENCED_PARAMETER( Interrupt );
#ifdef KEEP_COUNTERS
FloppyIntrTime = KeQueryPerformanceCounter((PVOID)NULL);
FloppyInterrupts++;
#endif
FloppyDump(
FLOPSHOW,
("FloppyInterruptService: ")
);
controllerData = (PCONTROLLER_DATA) Context;
if (!controllerData->AllowInterruptProcessing) {
FloppyDump(
FLOPSHOW,
("processing not allowed\n")
);
return FALSE;
}
//
// CurrentDeviceObject is set to the device object that is
// expecting an interrupt.
//
currentDeviceObject = controllerData->CurrentDeviceObject;
controllerData->CurrentDeviceObject = NULL;
controllerStateError = FALSE;
KeStallExecutionProcessor(10);
if ( controllerData->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.
//
FloppyDump(
FLOPSHOW,
("have result phase\n")
);
if ( ( READ_CONTROLLER( &controllerData->ControllerAddress->Status )
& STATUS_IO_READY_MASK ) == STATUS_READ_READY ) {
controllerData->FifoBuffer[0] =
READ_CONTROLLER( &controllerData->ControllerAddress->Fifo );
FloppyDump(
FLOPSHOW,
("FloppyInterruptService: 1st fifo byte %2x\n", controllerData->FifoBuffer[0])
);
} else {
//
// 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.
//
FloppyDump(
FLOPDBGP,
("FloppyInterruptService: 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.
//
FloppyDump(
FLOPSHOW,
("no result phase\n")
);
i = 0;
do {
KeStallExecutionProcessor( 1 );
statusByte = READ_CONTROLLER(&controllerData->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(
&controllerData->ControllerAddress->Fifo,
COMMND_SENSE_INTERRUPT );
//
// 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( &controllerData->ControllerAddress->Status );
if (statusByte & STATUS_CONTROLLER_BUSY) {
break;
}
KeStallExecutionProcessor( 1 );
}
if (!i) {
FloppyDump(
FLOPSHOW,
("FloppyInterruptService: 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.
//
FloppyDump(
FLOPSHOW,
("FloppyInterruptService: Dumping fifo bytes!\n")
);
READ_CONTROLLER( &controllerData->ControllerAddress->Fifo );
READ_CONTROLLER( &controllerData->ControllerAddress->Fifo );
}
} 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.
//
FloppyDump(
FLOPDBGP,
("Floppy: no result, but can't write SenseIntr\n")
);
controllerStateError = TRUE;
}
}
//
// 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( &controllerData->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 ( currentDeviceObject == NULL ) {
//
// We didn't expect this interrupt. We've dismissed it just
// in case, but now return FALSE withOUT waking up the thread.
//
FloppyDump(FLOPDBGP,
("Floppy: unexpected interrupt\n"));
return FALSE;
}
if ( !controllerStateError ) {
//
// Request a DPC for execution later to get the remainder of the
// floppy state.
//
controllerData->IsrReentered = 0;
controllerData->AllowInterruptProcessing = FALSE;
IoRequestDpc(currentDeviceObject,
currentDeviceObject->CurrentIrp,
(PVOID) NULL);
} 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.
//
controllerData->CurrentDeviceObject = currentDeviceObject;
if (controllerData->IsrReentered > FLOPPY_RESET_ISR_THRESHOLD) {
//
// Reset the controller. This could cause an interrupt
//
controllerData->IsrReentered = 0;
controllerData->DriveControlImage |= DRVCTL_ENABLE_DMA_AND_INTERRUPTS;
controllerData->DriveControlImage &= ~( DRVCTL_ENABLE_CONTROLLER );
#ifdef _PPC_
controllerData->DriveControlImage |= DRVCTL_DRIVE_MASK;
#endif
WRITE_CONTROLLER(&controllerData->ControllerAddress->DriveControl,
controllerData->DriveControlImage);
KeStallExecutionProcessor( 10 );
controllerData->DriveControlImage |= DRVCTL_ENABLE_CONTROLLER;
WRITE_CONTROLLER(&controllerData->ControllerAddress->DriveControl,
controllerData->DriveControlImage);
//
// 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(&controllerData->ControllerAddress->Fifo,
COMMND_SENSE_INTERRUPT);
KeStallExecutionProcessor(500);
KeInsertQueueDpc(&controllerData->LogErrorDpc,
NULL,
NULL);
} else {
controllerData->IsrReentered++;
}
}
return TRUE;
}
VOID
FloppyDeferredProcedure(
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 FloppyInterruptService(). 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.
--*/
{
PDEVICE_OBJECT deviceObject;
PDISKETTE_EXTENSION disketteExtension;
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;
disketteExtension = deviceObject->DeviceExtension;
KeSetEvent(
&disketteExtension->ControllerData->InterruptEvent,
(KPRIORITY) 0,
FALSE );
}
NTSTATUS
FlSendByte(
IN UCHAR ByteToSend,
IN PCONTROLLER_DATA ControllerData
)
/*++
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;
//
// Sit in a tight loop for a while. If the controller becomes ready,
// send the byte.
//
do {
if ( ( READ_CONTROLLER( &ControllerData->ControllerAddress->Status )
& STATUS_IO_READY_MASK ) == STATUS_WRITE_READY ) {
WRITE_CONTROLLER(
&ControllerData->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.
//
i = 0;
while ( ( !byteWritten ) && ( i < FIFO_DELAY_RETRY_COUNT ) ) {
FloppyDump(
FLOPINFO,
("Floppy: waiting for 10ms for controller write\n")
);
KeDelayExecutionThread(
KernelMode,
FALSE,
&ControllerData->Minimum10msDelay );
i++;
if ( (READ_CONTROLLER( &ControllerData->ControllerAddress->Status )
& STATUS_IO_READY_MASK) == STATUS_WRITE_READY ) {
WRITE_CONTROLLER(
&ControllerData->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.
//
FloppyDump(
FLOPWARN,
("Floppy: FIFO not ready to write after 30ms\n")
);
ControllerData->HardwareFailed = TRUE;
return STATUS_DEVICE_NOT_READY;
}
}
NTSTATUS
FlGetByte(
OUT PUCHAR ByteToGet,
IN PCONTROLLER_DATA ControllerData
)
/*++
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;
//
// Sit in a tight loop for a while. If the controller becomes ready,
// read the byte.
//
do {
if ( ( READ_CONTROLLER( &ControllerData->ControllerAddress->Status )
& STATUS_IO_READY_MASK ) == STATUS_READ_READY ) {
*ByteToGet = READ_CONTROLLER(
&ControllerData->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.
//
i = 0;
while ( ( !byteRead ) && ( i < FIFO_DELAY_RETRY_COUNT ) ) {
FloppyDump(
FLOPINFO,
("Floppy: waiting for 10ms for controller read\n")
);
KeDelayExecutionThread(
KernelMode,
FALSE,
&ControllerData->Minimum10msDelay );
i++;
if ( (READ_CONTROLLER( &ControllerData->ControllerAddress->Status )
& STATUS_IO_READY_MASK) == STATUS_READ_READY ) {
*ByteToGet = READ_CONTROLLER(
&ControllerData->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.
//
FloppyDump(
FLOPWARN,
("Floppy: FIFO not ready to read after 30ms\n")
);
ControllerData->HardwareFailed = TRUE;
return STATUS_DEVICE_NOT_READY;
}
}
NTSTATUS
FlInterpretError(
IN UCHAR StatusRegister1,
IN UCHAR StatusRegister2
)
/*++
Routine Description:
This routine is called when the floppy controller returns an error.
Status registers 1 and 2 are passed in, and this returns an appropriate
error status.
Arguments:
StatusRegister1 - the controller's status register #1.
StatusRegister2 - the controller's status register #2.
Return Value:
An NTSTATUS error determined from the status registers.
--*/
{
if ( ( StatusRegister1 & STREG1_CRC_ERROR ) ||
( StatusRegister2 & STREG2_CRC_ERROR ) ) {
FloppyDump(
FLOPSHOW,
("FlInterpretError: STATUS_CRC_ERROR\n")
);
return STATUS_CRC_ERROR;
}
if ( StatusRegister1 & STREG1_DATA_OVERRUN ) {
FloppyDump(
FLOPSHOW,
("FlInterpretError: STATUS_DATA_OVERRUN\n")
);
return STATUS_DATA_OVERRUN;
}
if ( ( StatusRegister1 & STREG1_SECTOR_NOT_FOUND ) ||
( StatusRegister1 & STREG1_END_OF_DISKETTE ) ) {
FloppyDump(
FLOPSHOW,
("FlInterpretError: STATUS_NONEXISTENT_SECTOR\n")
);
return STATUS_NONEXISTENT_SECTOR;
}
if ( ( StatusRegister2 & STREG2_DATA_NOT_FOUND ) ||
( StatusRegister2 & STREG2_BAD_CYLINDER ) ||
( StatusRegister2 & STREG2_DELETED_DATA ) ) {
FloppyDump(
FLOPSHOW,
("FlInterpretError: STATUS_DEVICE_DATA_ERROR\n")
);
return STATUS_DEVICE_DATA_ERROR;
}
if ( StatusRegister1 & STREG1_WRITE_PROTECTED ) {
FloppyDump(
FLOPSHOW,
("FlInterpretError: STATUS_MEDIA_WRITE_PROTECTED\n")
);
return STATUS_MEDIA_WRITE_PROTECTED;
}
if ( StatusRegister1 & STREG1_ID_NOT_FOUND ) {
FloppyDump(
FLOPSHOW,
("FlInterpretError: STATUS_FLOPPY_ID_MARK_NOT_FOUND\n")
);
return STATUS_FLOPPY_ID_MARK_NOT_FOUND;
}
if ( StatusRegister2 & STREG2_WRONG_CYLINDER ) {
FloppyDump(
FLOPSHOW,
("FlInterpretError: STATUS_FLOPPY_WRONG_CYLINDER\n")
);
return STATUS_FLOPPY_WRONG_CYLINDER;
}
//
// There's other error bits, but no good status values to map them
// to. Just return a generic one.
//
FloppyDump(
FLOPSHOW,
("FlInterpretError: STATUS_FLOPPY_UNKNOWN_ERROR\n")
);
return STATUS_FLOPPY_UNKNOWN_ERROR;
}
BOOLEAN
FlClearIsrReentered(
IN PVOID Context
)
/*++
Routine Description:
This routine is simply used to clear the reentered count of the isr.
Arguments:
Context - Simply a pointer to the re-entered count of the isr.
Return Value:
Always False.
--*/
{
*((ULONG *)Context) = 0;
return FALSE;
}
VOID
FlFinishOperation(
IN OUT PIRP Irp,
IN PDISKETTE_EXTENSION DisketteExtension
)
/*++
Routine Description:
This routine is called by FloppyThread at the end of any operation
whether it succeeded or not.
If the packet is failing due to a hardware error, this routine will
reinitialize the hardware and retry once.
When the packet is done, this routine will start the timer to turn
off the motor, and complete the IRP.
Arguments:
Irp - a pointer to the IO Request Packet being processed.
DisketteExtension - a pointer to the diskette extension for the
diskette on which the operation occurred.
Return Value:
None.
--*/
{
PCONTROLLER_DATA controllerData;
NTSTATUS ntStatus;
FloppyDump(
FLOPSHOW,
("Floppy: FloppyFinishOperation...\n")
);
controllerData = DisketteExtension->ControllerData;
//
// See if this packet is being failed due to a hardware error.
//
if ( ( Irp->IoStatus.Status != STATUS_SUCCESS ) &&
( controllerData->HardwareFailed ) ) {
controllerData->HardwareFailCount++;
if ( controllerData->HardwareFailCount < HARDWARE_RESET_RETRY_COUNT ) {
KeSynchronizeExecution(
controllerData->InterruptObject,
FlClearIsrReentered,
&controllerData->IsrReentered
);
//
// This is our first time through (that is, we're not retrying
// the packet after a hardware failure). If it failed this first
// time because of a hardware problem, set the HardwareFailed flag
// and put the IRP at the beginning of the request queue.
//
ntStatus = FlInitializeControllerHardware(
controllerData,
DisketteExtension->DeviceObject );
if ( NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPINFO,
("Floppy: packet failed; hardware reset. Retry.\n")
);
//
// Force media to be redetermined, in case we messed up
// and to make sure FlDatarateSpecifyConfigure() gets
// called.
//
DisketteExtension->MediaType = Undetermined;
FloppyDump(
FLOPIRPPATH,
("Floppy: irp %x failed - back on the queue with it\n",
Irp)
);
ExAcquireFastMutex(&controllerData->ThreadReferenceMutex);
ASSERT(controllerData->ThreadReferenceCount >= 0);
(controllerData->ThreadReferenceCount)++;
ExReleaseFastMutex(&controllerData->ThreadReferenceMutex);
ExInterlockedInsertHeadList(
&controllerData->ListEntry,
&Irp->Tail.Overlay.ListEntry,
&controllerData->ListSpinLock );
return;
}
FloppyDump(
FLOPDBGP,
("Floppy: packet AND hardware reset failed.\n")
);
}
}
//
// If we didn't already RETURN, we're done with this packet so
// reset the HardwareFailCount for the next packet.
//
controllerData->HardwareFailCount = 0;
//
// If this request was unsuccessful and the error is one that can be
// remedied by the user, save the Device Object so that the file system,
// after reaching its original entry point, can know the real device.
//
if ( !NT_SUCCESS( Irp->IoStatus.Status ) &&
IoIsErrorUserInduced( Irp->IoStatus.Status ) ) {
IoSetHardErrorOrVerifyDevice( Irp, DisketteExtension->DeviceObject );
}
//
// Even if the operation failed, it probably had to wait for the drive
// to spin up or somesuch so we'll always complete the request with the
// standard priority boost.
//
if ( ( Irp->IoStatus.Status != STATUS_SUCCESS ) &&
( Irp->IoStatus.Status != STATUS_VERIFY_REQUIRED ) &&
( Irp->IoStatus.Status != STATUS_NO_MEDIA_IN_DEVICE ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: IRP failed with error %lx\n", Irp->IoStatus.Status)
);
} else {
FloppyDump(
FLOPINFO,
("Floppy: IoStatus.Status = %x\n", Irp->IoStatus.Status)
);
}
FloppyDump(
FLOPINFO,
("Floppy: IoStatus.Information = %x\n", Irp->IoStatus.Information)
);
FloppyDump(
FLOPIRPPATH,
("Floppy: Finishing up IRP: %x\n",Irp)
);
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
}
VOID
FlTurnOffMotor(
IN OUT PCONTROLLER_DATA ControllerData
)
/*++
Routine Description:
This routine turns off the motor.
Arguments:
DisketteExtension - Supplies the diskette extension.
Return Value:
None.
--*/
{
ControllerData->DriveControlImage =
DRVCTL_ENABLE_DMA_AND_INTERRUPTS +
#ifdef _PPC_
DRVCTL_DRIVE_MASK +
#endif
DRVCTL_ENABLE_CONTROLLER;
WRITE_CONTROLLER(
&ControllerData->ControllerAddress->DriveControl,
ControllerData->DriveControlImage );
if (ControllerData->CurrentInterrupt) {
ControllerData->CurrentInterrupt = FALSE;
KeSetEvent(ControllerData->ControllerEvent,
(KPRIORITY) 0,
FALSE);
}
FlFreeIoBuffer(ControllerData);
}
NTSTATUS
FlIssueCommand(
IN UCHAR Command,
IN OUT PDISKETTE_EXTENSION DisketteExtension
)
/*++
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.
DisketteExtension - a pointer to our data area for the drive being
accessed (any drive if a controller command is being given).
Return Value:
STATUS_SUCCESS if the command was sent and bytes received properly;
appropriate error propogated otherwise.
--*/
{
PCONTROLLER_DATA controllerData;
NTSTATUS ntStatus;
NTSTATUS ntStatus2;
UCHAR i;
FloppyDump(
FLOPSHOW,
("Floppy: FloppyIssueCommand %2x...\n", Command)
);
controllerData = DisketteExtension->ControllerData;
//
// If this command causes an interrupt, set CurrentDeviceObject and
// reset the interrupt event.
//
if ( CommandTable[Command & COMMAND_MASK].InterruptExpected ) {
controllerData->CurrentDeviceObject = DisketteExtension->DeviceObject;
controllerData->AllowInterruptProcessing = TRUE;
controllerData->CommandHasResultPhase =
!!CommandTable[Command & COMMAND_MASK].FirstResultByte;
KeResetEvent( &controllerData->InterruptEvent );
}
#if defined(DBCS) && defined(_MIPS_)
//
// Call FlOutputCommandFor3Mode(), so the drive mode can be changed
// if necessary.
//
ntStatus = FlOutputCommandFor3Mode( Command, DisketteExtension );
if (!NT_SUCCESS( ntStatus )) {
return ntStatus;
}
#endif // DBCS && _MIPS_
//
// Send the command to the controller.
//
ntStatus = FlSendByte( Command, controllerData );
//
// If the command was successfully sent, we can proceed.
//
if ( NT_SUCCESS( ntStatus ) ) {
//
// Send the parameters as long as we succeed.
//
for ( i = 0;
( i < CommandTable[Command & COMMAND_MASK].NumberOfParameters ) &&
( NT_SUCCESS( ntStatus ) );
i++ ) {
ntStatus = FlSendByte(
controllerData->FifoBuffer[i],
controllerData );
}
if ( NT_SUCCESS( ntStatus ) ) {
//
// If there is an interrupt, wait for it.
//
if ( CommandTable[Command & COMMAND_MASK].InterruptExpected ) {
ntStatus = KeWaitForSingleObject(
&controllerData->InterruptEvent,
Executive,
KernelMode,
FALSE,
&controllerData->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;
controllerData->HardwareFailed = TRUE;
}
#ifdef KEEP_COUNTERS
FloppyThreadTime = KeQueryPerformanceCounter((PVOID)NULL);
FloppyThreadTime.QuadPart = FloppyThreadDelay.QuadPart +
(FloppyThreadTime.QuadPart -
FloppyDPCTime.QuadPart);
FloppyFromIntrDelay.QuadPart = FloppyFromIntrDelay.QuadPart +
(FloppyThreadTime.QuadPart -
FloppyIntrTime.QuadPart);
FloppyThreadWake++;
#endif
}
//
// If successful so far, get the result bytes.
//
if ( NT_SUCCESS( ntStatus ) ) {
for ( i = CommandTable[Command & COMMAND_MASK].FirstResultByte;
( i < CommandTable[Command & COMMAND_MASK].
NumberOfResultBytes ) && ( NT_SUCCESS( ntStatus ) );
i++ ) {
ntStatus = FlGetByte(
&controllerData->FifoBuffer[i],
controllerData );
}
} else {
FloppyDump(
FLOPINFO,
("FlIssueCommand: failure after issue %x\n", ntStatus)
);
}
}
}
//
// If there was a problem, check to see if it was caused by an
// unimplemented command.
//
if ( !NT_SUCCESS( ntStatus ) ) {
if ( ( i == 1 ) &&
( !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 = FlGetByte(
&controllerData->FifoBuffer[0],
controllerData );
//
// If GetByte went as planned, we'll return the original error.
//
if ( NT_SUCCESS( ntStatus2 ) ) {
if ( controllerData->FifoBuffer[0] !=
STREG0_END_INVALID_COMMAND ) {
//
// Status isn't as we expect, so return generic error.
//
ntStatus = STATUS_FLOPPY_BAD_REGISTERS;
controllerData->HardwareFailed = TRUE;
FloppyDump(
FLOPINFO,
("FlIssueCommand: unexpected error value %2x\n",
controllerData->FifoBuffer[0])
);
} else {
FloppyDump(
FLOPINFO,
("FlIssueCommand: Invalid command error returned\n")
);
}
} else {
//
// GetByte returned an error, so propogate THAT.
//
FloppyDump(
FLOPINFO,
("FlIssueCommand: FlGetByte returned error %x\n", ntStatus2)
);
ntStatus = ntStatus2;
}
}
}
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 ) ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: err %x "
"------ while giving command %x\n",
ntStatus,
Command)
);
}
}
return ntStatus;
}
NTSTATUS
FlTurnOnMotor(
IN OUT PDISKETTE_EXTENSION DisketteExtension,
IN BOOLEAN WriteOperation,
OUT PBOOLEAN MotorStarted
)
/*++
Routine Description:
This routine turns on the motor if it not already running.
Arguments:
DisketteExtension - Supplies the diskette extension.
WriteOperation - Supplies whether or not the diskette will be written
to.
MotorStarted - Returns whether or not the motor was started.
Return Value:
STATUS_DEVICE_BUSY if we don't have the controller, otherwise
STATUS_SUCCESS
--*/
{
UCHAR driveStatus;
NTSTATUS waitStatus;
LARGE_INTEGER controllerWait;
PCONTROLLER_DATA controllerData;
USHORT timeToWait;
LARGE_INTEGER motorOnDelay;
*MotorStarted = FALSE;
controllerData = DisketteExtension->ControllerData;
driveStatus = controllerData->DriveControlImage;
if ( driveStatus != DisketteExtension->DriveOnValue ) {
// 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 (!controllerData->CurrentInterrupt) {
controllerWait.QuadPart = -(10 * 1000 * 3000);
waitStatus = KeWaitForSingleObject(
controllerData->ControllerEvent,
Executive,
UserMode,
FALSE,
&controllerWait);
if (waitStatus == STATUS_TIMEOUT) {
return STATUS_DEVICE_BUSY;
}
controllerData->CurrentInterrupt = TRUE;
driveStatus = controllerData->DriveControlImage;
}
controllerData->AllowInterruptProcessing = TRUE;
controllerData->DriveControlImage = DisketteExtension->DriveOnValue;
WRITE_CONTROLLER(
&controllerData->ControllerAddress->DriveControl,
controllerData->DriveControlImage );
//
// If this the drive media is unknown, initialize the diskette
// extension. This is done to get the time to wait value so use
// the slowest and oldest drive type.
//
if ((DisketteExtension->MediaType == Undetermined) ||
(DisketteExtension->MediaType == Unknown)) {
DisketteExtension->DriveMediaConstants = DriveMediaConstants[0];
}
//
// Wait the appropriate length of time for the drive to spin up.
//
if ( WriteOperation ) {
timeToWait = DisketteExtension->DriveMediaConstants.MotorSettleTimeWrite;
} else {
timeToWait = DisketteExtension->DriveMediaConstants.MotorSettleTimeRead;
}
FloppyDump(
FLOPSHOW,
("Floppy: Waiting for motor to spin up (0x%x)...\n", timeToWait)
);
motorOnDelay.LowPart = - ( 10 * 1000 * timeToWait );
motorOnDelay.HighPart = -1;
KeDelayExecutionThread( KernelMode, FALSE, &motorOnDelay );
*MotorStarted = TRUE;
}
return STATUS_SUCCESS;
}
BOOLEAN
FlDisketteRemoved(
IN PCONTROLLER_DATA ControllerData,
IN UCHAR DriveStatus,
IN BOOLEAN MotorStarted
)
/*++
Routine Description:
This routine computes whether or not the diskette has been
removed from the drive by examining the disk change bit in
the given drive status byte. It is now assumed that if the
motor has just been started this will return that the diskette
changed.
Arguments:
ControllerData - Supplies the controller data.
DriveStatus - Supplies the drive status.
Return Value:
TRUE - The diskette has been removed.
--*/
{
UCHAR invertMask;
#if 0
if (MotorStarted) {
return TRUE;
}
#endif
invertMask = ControllerData->Model30 ? DSKCHG_DISKETTE_REMOVED : 0;
return (DriveStatus ^ invertMask) & DSKCHG_DISKETTE_REMOVED;
}
NTSTATUS
FlStartDrive(
IN OUT PDISKETTE_EXTENSION DisketteExtension,
IN PIRP Irp,
IN BOOLEAN WriteOperation,
IN BOOLEAN SetUpMedia,
IN BOOLEAN IgnoreChange
)
/*++
Routine Description:
This routine is called at the beginning of every operation. It cancels
the motor timer if it's on, turns the motor on and waits for it to
spin up if it was off, resets the disk change line and returns
VERIFY_REQUIRED if the disk has been changed, determines the diskette
media type if it's not known and SetUpMedia=TRUE, and makes sure that
the disk isn't write protected if WriteOperation = TRUE.
Arguments:
DisketteExtension - a pointer to our data area for the drive being
started.
Irp - Supplies the I/O request packet.
WriteOperation - TRUE if the diskette will be written to, FALSE
otherwise.
SetUpMedia - TRUE if the media type of the diskette in the drive
should be determined.
IgnoreChange - Do not return VERIFY_REQUIRED eventhough we are mounting
for the first time.
Return Value:
STATUS_SUCCESS if the drive is started properly; appropriate error
propogated otherwise.
--*/
{
LARGE_INTEGER delay;
PCONTROLLER_DATA controllerData;
BOOLEAN motorStarted;
UCHAR driveStatus;
NTSTATUS ntStatus = STATUS_SUCCESS;
FloppyDump(
FLOPSHOW,
("Floppy: FloppyStartDrive...\n")
);
//
// IMPORTANT
// NOTE
// COMMENT
//
// Here we will copy the BIOS floppy configuration on top of the
// highest media value in our global array so that any type of processing
// that will recalibrate the drive can have it done here.
// An optimization would be to only do it when we will try to recalibrate
// the driver or media in it.
// At this point, we ensure that on any processing of a command we
// are going to have the real values inthe first entry of the array for
// driver constants.
//
DriveMediaConstants[DriveMediaLimits[DisketteExtension->DriveType].
HighestDriveMediaType] = DisketteExtension->BiosDriveMediaConstants;
controllerData = DisketteExtension->ControllerData;
//
// Grab the timer spin lock and cancel the timer, since we want the
// motor to run for the whole operation. If the proper drive is
// already running, great; if not, start the motor and wait for it
// to spin up.
//
ntStatus = FlTurnOnMotor(DisketteExtension, WriteOperation, &motorStarted);
if (!NT_SUCCESS(ntStatus)) {
return ntStatus;
}
driveStatus = READ_CONTROLLER(
&controllerData->ControllerAddress->DRDC.DiskChange );
//
// Support for 360K drives:
// They have no change line, so we will assume a power up of the motor
// to be equivalent to a change of floppy (we assume noone will
// change the floppy while it is turning.
// So force a VERIFY here (unless the file system explicitly turned
// it off).
//
if ( ((DisketteExtension->DriveType == DRIVE_TYPE_0360) &&
motorStarted) ||
((DisketteExtension->DriveType != DRIVE_TYPE_0360) &&
FlDisketteRemoved(controllerData, driveStatus, motorStarted)) ) {
FloppyDump(
FLOPSHOW,
("Floppy: disk changed...\n")
);
DisketteExtension->MediaType = Undetermined;
//
// If the volume is mounted, we must tell the filesystem to
// verify that the media in the drive is the same volume.
//
if ( DisketteExtension->DeviceObject->Vpb->Flags & VPB_MOUNTED ) {
if (Irp) {
IoSetHardErrorOrVerifyDevice( Irp, DisketteExtension->DeviceObject );
}
DisketteExtension->DeviceObject->Flags |= DO_VERIFY_VOLUME;
}
//
// Only go through the device reset if we did get the flag set
// We really only want to go throught here if the diskette changed,
// but on 360 it will always say the diskette has changed.
// So based on our previous test, only proceed if it is NOT
// a 360K driver
if (DisketteExtension->DriveType != DRIVE_TYPE_0360) {
//
// Now seek twice to reset the "disk changed" line. First
// seek to 1.
//
// Normally we'd do a READ ID after a seek. However, we don't
// even know if this disk is formatted. We're not really
// trying to get anywhere; we're just doing this to reset the
// "disk changed" line so we'll skip the READ ID.
//
controllerData->FifoBuffer[0] = DisketteExtension->DeviceUnit;
controllerData->FifoBuffer[1] = 1;
ntStatus = FlIssueCommand( COMMND_SEEK, DisketteExtension );
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPWARN,
("Floppy: seek to 1 returned %x\n", ntStatus)
);
return ntStatus;
} else {
if ( !( controllerData->FifoBuffer[0] & STREG0_SEEK_COMPLETE )
|| ( controllerData->FifoBuffer[1] != 1 ) ) {
FloppyDump(
FLOPWARN,
("Floppy: Seek to 1 had bad return registers\n")
);
controllerData->HardwareFailed = TRUE;
return STATUS_FLOPPY_BAD_REGISTERS;
}
}
//
// Seek back to 0. We can once again skip the READ ID.
//
controllerData->FifoBuffer[0] = DisketteExtension->DeviceUnit;
controllerData->FifoBuffer[1] = 0;
//
// Floppy drives use by Toshiba systems require a delay
// when this operation is performed.
//
delay.LowPart = (ULONG) -900;
delay.HighPart = -1;
KeDelayExecutionThread( KernelMode, FALSE, &delay );
ntStatus = FlIssueCommand( COMMND_SEEK, DisketteExtension );
//
// Again, for Toshiba floppy drives, a delay is required.
//
delay.LowPart = (ULONG) -5;
delay.HighPart = -1;
KeDelayExecutionThread( KernelMode, FALSE, &delay );
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPWARN,
("Floppy: seek to 0 returned %x\n", ntStatus)
);
return ntStatus;
} else {
if ( !( controllerData->FifoBuffer[0] & STREG0_SEEK_COMPLETE )
|| ( controllerData->FifoBuffer[1] != 0 ) ) {
FloppyDump(
FLOPWARN,
("Floppy: Seek to 0 had bad return registers\n")
);
controllerData->HardwareFailed = TRUE;
return STATUS_FLOPPY_BAD_REGISTERS;
}
}
driveStatus = READ_CONTROLLER(
&controllerData->ControllerAddress->DRDC.DiskChange );
if ( FlDisketteRemoved(controllerData, driveStatus, motorStarted) ) {
//
// If "disk changed" is still set after the double seek, the
// drive door must be opened.
//
FloppyDump(
FLOPINFO,
("Floppy: close the door!\n")
);
//
// Turn off the flag for now so that we will not get so many
// gratuitous verifys. It will be set again the next time.
//
if ( DisketteExtension->DeviceObject->Vpb->Flags & VPB_MOUNTED ) {
DisketteExtension->DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
}
return STATUS_NO_MEDIA_IN_DEVICE;
}
}
//
// IgnoreChange indicates the file system is in the process
// of performing a verify so do not return verify required.
//
if (( IgnoreChange == FALSE ) &&
( DisketteExtension->DeviceObject->Vpb->Flags & VPB_MOUNTED )) {
//
// Drive WAS mounted, but door was opened since the last time
// we checked so tell the file system to verify the diskette.
//
FloppyDump(
FLOPSHOW,
("Floppy: start drive - verify required because door opened\n")
);
return STATUS_VERIFY_REQUIRED;
}
}
if ( SetUpMedia ) {
if ( DisketteExtension->MediaType == Undetermined ) {
ntStatus = FlDetermineMediaType( DisketteExtension );
} else {
if ( DisketteExtension->MediaType == Unknown ) {
//
// We've already tried to determine the media type and
// failed. It's probably not formatted.
//
FloppyDump(
FLOPSHOW,
("Floppy - start drive - media type was unknown\n")
);
return STATUS_UNRECOGNIZED_MEDIA;
} else {
if ( DisketteExtension->DriveMediaType !=
controllerData->LastDriveMediaType ) {
//
// Last drive/media combination accessed by the
// controller was different, so set up the controller.
//
ntStatus = FlDatarateSpecifyConfigure( DisketteExtension );
if (!NT_SUCCESS(ntStatus)) {
FloppyDump(
FLOPWARN,
("Floppy: start drive - bad status from datarate"
"------ specify %x\n",
ntStatus)
);
}
}
}
}
}
//
// If this is a WRITE, check the drive to make sure it's not write
// protected. If so, return an error.
//
if ( ( WriteOperation ) && ( NT_SUCCESS( ntStatus ) ) ) {
controllerData->FifoBuffer[0] = DisketteExtension->DeviceUnit;
ntStatus = FlIssueCommand( COMMND_SENSE_DRIVE, DisketteExtension );
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPWARN,
("Floppy: SENSE_DRIVE returned %x\n", ntStatus)
);
return ntStatus;
}
if ( controllerData->FifoBuffer[0] & STREG3_WRITE_PROTECTED ) {
FloppyDump(
FLOPSHOW,
("Floppy: start drive - media is write protected\n")
);
return STATUS_MEDIA_WRITE_PROTECTED;
}
}
return ntStatus;
}
NTSTATUS
FlDatarateSpecifyConfigure(
IN PDISKETTE_EXTENSION DisketteExtension
)
/*++
Routine Description:
This routine is called to set up the controller every time a new type
of diskette is to be accessed. It issues the CONFIGURE command if
it's available, does a SPECIFY, sets the data rate, and RECALIBRATEs
the drive.
The caller must set DisketteExtension->DriveMediaType before calling
this routine.
Arguments:
DisketteExtension - pointer to our data area for the drive to be
prepared.
Return Value:
STATUS_SUCCESS if the controller is properly prepared; appropriate
error propogated otherwise.
--*/
{
PCONTROLLER_DATA controllerData;
NTSTATUS ntStatus = STATUS_SUCCESS;
controllerData = DisketteExtension->ControllerData;
FloppyDump(
FLOPSHOW,
("Floppy: FloppyDatarateSpecifyConfigure: Controller is %s configurable\n",
controllerData->ControllerConfigurable ? "" : "not")
);
//
// If the controller has a CONFIGURE command, use it to enable implied
// seeks. If it doesn't, we'll find out here the first time through.
//
if ( controllerData->ControllerConfigurable ) {
controllerData->FifoBuffer[0] = 0;
controllerData->FifoBuffer[1] = COMMND_CONFIGURE_FIFO_THRESHOLD;
controllerData->FifoBuffer[1] += COMMND_CONFIGURE_DISABLE_POLLING;
if (!DisketteExtension->DriveMediaConstants.CylinderShift) {
controllerData->FifoBuffer[1] += COMMND_CONFIGURE_IMPLIED_SEEKS;
}
controllerData->FifoBuffer[2] = 0;
ntStatus = FlIssueCommand( COMMND_CONFIGURE, DisketteExtension );
if ( ntStatus == STATUS_DEVICE_NOT_READY ) {
//
// Note the CONFIGURE command doesn't exist. Set status to
// success, so we can issue the SPECIFY command below.
//
FloppyDump(
FLOPINFO,
("Floppy: Ignore above error - no CONFIGURE command\n")
);
controllerData->ControllerConfigurable = FALSE;
ntStatus = STATUS_SUCCESS;
}
}
//
// Issue SPECIFY command to program the head load and unload
// rates, the drive step rate, and the DMA data transfer mode.
//
if ( NT_SUCCESS( ntStatus ) ) {
controllerData->FifoBuffer[0] =
DisketteExtension->DriveMediaConstants.StepRateHeadUnloadTime;
controllerData->FifoBuffer[1] =
DisketteExtension->DriveMediaConstants.HeadLoadTime;
ntStatus = FlIssueCommand( COMMND_SPECIFY, DisketteExtension );
if ( NT_SUCCESS( ntStatus ) ) {
//
// Program the data rate
//
WRITE_CONTROLLER(
&controllerData->ControllerAddress->DRDC.DataRate,
DisketteExtension->DriveMediaConstants.DataTransferRate );
//
// Recalibrate the drive, now that we've changed all its
// parameters.
//
ntStatus = FlRecalibrateDrive( DisketteExtension );
} else {
FloppyDump(
FLOPINFO,
("Floppy: Failed specify %x\n", ntStatus)
);
}
} else {
FloppyDump(
FLOPINFO,
("Floppy: Failed configuration %x\n", ntStatus)
);
}
if ( NT_SUCCESS( ntStatus ) ) {
controllerData->LastDriveMediaType = DisketteExtension->DriveMediaType;
} else {
controllerData->LastDriveMediaType = Unknown;
FloppyDump(
FLOPINFO,
("Floppy: Failed recalibrate %x\n", ntStatus)
);
}
return ntStatus;
}
NTSTATUS
FlRecalibrateDrive(
IN PDISKETTE_EXTENSION DisketteExtension
)
/*++
Routine Description:
This routine recalibrates a drive. It is called whenever we're
setting up to access a new diskette, and after certain errors. It
will actually recalibrate twice, since many controllers stop after
77 steps and many disks have 80 tracks.
Arguments:
DisketteExtension - pointer to our data area for the drive to be
recalibrated.
Return Value:
STATUS_SUCCESS if the drive is successfully recalibrated; appropriate
error is propogated otherwise.
--*/
{
PCONTROLLER_DATA controllerData;
NTSTATUS ntStatus;
UCHAR recalibrateCount;
controllerData = DisketteExtension->ControllerData;
recalibrateCount = 0;
do {
//
// Issue the recalibrate command
//
controllerData->FifoBuffer[0] = DisketteExtension->DeviceUnit;
ntStatus = FlIssueCommand( COMMND_RECALIBRATE, DisketteExtension );
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPWARN,
("Floppy: recalibrate returned %x\n", ntStatus)
);
}
if ( NT_SUCCESS( ntStatus ) ) {
if ( !( controllerData->FifoBuffer[0] & STREG0_SEEK_COMPLETE ) ||
( controllerData->FifoBuffer[1] != 0 ) ) {
FloppyDump(
FLOPWARN,
("Floppy: recalibrate had bad registers\n")
);
controllerData->HardwareFailed = TRUE;
ntStatus = STATUS_FLOPPY_BAD_REGISTERS;
}
}
recalibrateCount++;
} while ( ( !NT_SUCCESS( ntStatus ) ) && ( recalibrateCount < 2 ) );
FloppyDump(
FLOPSHOW,
("Floppy: FloppyRecalibrateDrive: status %x, count %d\n", ntStatus, recalibrateCount)
);
return ntStatus;
}
NTSTATUS
FlDetermineMediaType(
IN OUT PDISKETTE_EXTENSION DisketteExtension
)
/*++
Routine Description:
This routine is called by FlStartDrive() when the media type is
unknown. It assumes the largest media supported by the drive is
available, and keeps trying lower values until it finds one that
works.
Arguments:
DisketteExtension - pointer to our data area for the drive whose
media is to checked.
Return Value:
STATUS_SUCCESS if the type of the media is determined; appropriate
error propogated otherwise.
--*/
{
NTSTATUS ntStatus;
PCONTROLLER_DATA controllerData;
PDRIVE_MEDIA_CONSTANTS driveMediaConstants;
BOOLEAN mediaTypesExhausted = FALSE;
ULONG retries = 0;
FloppyDump(
FLOPSHOW,
("FlDetermineMediaType...\n")
);
controllerData = DisketteExtension->ControllerData;
DisketteExtension->IsReadOnly = FALSE;
//
// Try up to three times for read the media id.
//
for (
retries = 0;
retries < 3;
retries++
) {
if (retries) {
//
// We're retrying the media determination because
// some silly controllers don't always want to work
// at setup. First we'll reset the device to give
// it a better chance of working.
//
FloppyDump(
FLOPINFO,
("FlDetermineMediaType: Resetting controller\n")
);
FlInitializeControllerHardware(
controllerData,
DisketteExtension->DeviceObject
);
}
//
// Assume that the largest supported media is in the drive. If that
// turns out to be untrue, we'll try successively smaller media types
// until we find what's really in there (or we run out and decide
// that the media isn't formatted).
//
DisketteExtension->DriveMediaType =
DriveMediaLimits[DisketteExtension->DriveType].HighestDriveMediaType;
DisketteExtension->DriveMediaConstants =
DriveMediaConstants[DisketteExtension->DriveMediaType];
do {
ntStatus = FlDatarateSpecifyConfigure( DisketteExtension );
if ( !NT_SUCCESS( ntStatus ) ) {
//
// The SPECIFY or CONFIGURE commands resulted in an error.
// Force ourselves out of this loop and return error.
//
FloppyDump(
FLOPINFO,
("FlDetermineMediaType: DatarateSpecify failed %x\n", ntStatus)
);
mediaTypesExhausted = TRUE;
} else {
//
// Use the media constants table when trying to determine
// media type.
//
driveMediaConstants =
&DriveMediaConstants[DisketteExtension->DriveMediaType];
//
// Now try to read the ID from wherever we're at.
//
controllerData->FifoBuffer[0] = (UCHAR)
( DisketteExtension->DeviceUnit |
( ( driveMediaConstants->NumberOfHeads - 1 ) << 2 ) );
ntStatus = FlIssueCommand(
COMMND_READ_ID + COMMND_MFM,
DisketteExtension );
if ( ( !NT_SUCCESS( ntStatus ) ) ||
( (controllerData->FifoBuffer[0]&(~STREG0_SEEK_COMPLETE)) !=
(UCHAR)( ( DisketteExtension->DeviceUnit ) |
( ( driveMediaConstants->NumberOfHeads - 1 ) << 2 ) ) ) ||
( controllerData->FifoBuffer[1] != 0 ) ||
#if defined(DBCS) && defined(_MIPS_)
( controllerData->FifoBuffer[2] != 0 ) ||
( (128 << (controllerData->FifoBuffer[6])) != driveMediaConstants->BytesPerSector )
#else // !DBCS && !_MIPS_
( controllerData->FifoBuffer[2] != 0 )
#endif // DBCS && _MIPS_
) {
FloppyDump(
FLOPINFO,
("Floppy: READID failed trying lower media\n"
"------ status = %x\n"
"------ SR0 = %x\n"
"------ SR1 = %x\n"
"------ SR2 = %x\n",
ntStatus,
controllerData->FifoBuffer[0],
controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2])
);
DisketteExtension->DriveMediaType--;
#if defined(DBCS) && defined(_MIPS_)
//
// This drive isn't the 3 mode Floppy disk drive.
// So the driver skips over 1.23Mb and 1.2MB format
// when it determines a DriveMediaType for DRIVE_TYPE_1440.
//
if (( DisketteExtension->DriveType == DRIVE_TYPE_1440 ) &&
( DisketteExtension->DriveMediaType == Drive144Media123) &&
( DisketteExtension->Drive3Mode == DRIVE_2MODE )) {
FloppyDump(
FLOPINFO,
("Floppy: Skip over 1.23Mb and 1.2Mb\n")
);
DisketteExtension->DriveMediaType = Drive144Media720;
}
#endif // DBCS && _MIPS_
DisketteExtension->DriveMediaConstants =
DriveMediaConstants[DisketteExtension->DriveMediaType];
if (ntStatus != STATUS_DEVICE_NOT_READY) {
ntStatus = STATUS_UNRECOGNIZED_MEDIA;
}
//
// Next comparison must be signed, for when
// LowestDriveMediaType = 0.
//
if ( (CHAR)( DisketteExtension->DriveMediaType ) <
(CHAR)( DriveMediaLimits[DisketteExtension->DriveType].
LowestDriveMediaType ) ) {
DisketteExtension->MediaType = Unknown;
mediaTypesExhausted = TRUE;
FloppyDump(
FLOPINFO,
("Floppy: Unrecognized media.\n")
);
}
}
}
} while ( ( !NT_SUCCESS( ntStatus ) ) && !( mediaTypesExhausted ) );
if (NT_SUCCESS(ntStatus)) {
//
// We determined the media type. Time to move on.
//
FloppyDump(
FLOPINFO,
("Floppy: Determined media type %d\n", retries)
);
break;
}
}
if ( (!NT_SUCCESS( ntStatus )) || mediaTypesExhausted) {
FloppyDump(
FLOPINFO,
("Floppy: failed determine types status = %x %s\n",
ntStatus,
mediaTypesExhausted ? "media types exhausted" : "")
);
return ntStatus;
}
DisketteExtension->MediaType = driveMediaConstants->MediaType;
DisketteExtension->BytesPerSector = driveMediaConstants->BytesPerSector;
DisketteExtension->ByteCapacity =
( driveMediaConstants->BytesPerSector ) *
driveMediaConstants->SectorsPerTrack *
( 1 + driveMediaConstants->MaximumTrack ) *
driveMediaConstants->NumberOfHeads;
FloppyDump(
FLOPINFO,
("FlDetermineMediaType: MediaType is %x, bytes per sector %d, capacity %d\n",
DisketteExtension->MediaType,
DisketteExtension->BytesPerSector,
DisketteExtension->ByteCapacity)
);
//
// Structure copy the media constants into the diskette extension.
//
DisketteExtension->DriveMediaConstants =
DriveMediaConstants[DisketteExtension->DriveMediaType];
//
// Check the boot sector for any overriding geometry information.
//
FlCheckBootSector(DisketteExtension);
return ntStatus;
}
VOID
FlAllocateAdapterChannel(
IN OUT PDISKETTE_EXTENSION DisketteExtension
)
/*++
Routine Description:
This routine allocates an adapter channel. The caller of this
routine must wait for the 'AllocateAdapterChannelEvent' to be signalled
before trying to use the adapter channel.
Arguments:
DisketteExtension - Supplies the diskette extension.
Return Value:
None.
--*/
{
KIRQL oldIrql;
PCONTROLLER_DATA controllerData = DisketteExtension->ControllerData;
if ((controllerData->AdapterChannelRefCount)++) {
return;
}
KeResetEvent(&controllerData->AllocateAdapterChannelEvent);
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
IoAllocateAdapterChannel(
controllerData->AdapterObject,
DisketteExtension->DeviceObject,
controllerData->NumberOfMapRegisters,
FloppyAllocateAdapterChannel,
controllerData );
KeLowerIrql( oldIrql );
KeWaitForSingleObject(&controllerData->AllocateAdapterChannelEvent,
Executive, KernelMode, FALSE, NULL);
}
VOID
FlFreeAdapterChannel(
IN OUT PDISKETTE_EXTENSION DisketteExtension
)
/*++
Routine Description:
This routine frees the previously allocated adapter channel.
Arguments:
DisketteExtension - Supplies the diskette extension.
Return Value:
None.
--*/
{
KIRQL oldIrql;
if (--(DisketteExtension->ControllerData->AdapterChannelRefCount)) {
return;
}
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
IoFreeAdapterChannel( DisketteExtension->ControllerData->AdapterObject );
KeLowerIrql( oldIrql );
}
VOID
FlAllocateIoBuffer(
IN OUT PCONTROLLER_DATA ControllerData,
IN ULONG BufferSize
)
/*++
Routine Description:
This routine allocates a PAGE_SIZE io buffer.
Arguments:
ControllerData - Supplies the controller data.
BufferSize - Supplies the number of bytes to allocate.
Return Value:
None.
--*/
{
BOOLEAN allocateContiguous;
LARGE_INTEGER maxDmaAddress;
if (ControllerData->IoBuffer) {
if (ControllerData->IoBufferSize >= BufferSize) {
return;
}
FlFreeIoBuffer(ControllerData);
}
if (BufferSize > ControllerData->NumberOfMapRegisters*PAGE_SIZE) {
allocateContiguous = TRUE;
} else {
allocateContiguous = FALSE;
}
if (allocateContiguous) {
maxDmaAddress.QuadPart = MAXIMUM_DMA_ADDRESS;
ControllerData->IoBuffer = MmAllocateContiguousMemory(BufferSize,
maxDmaAddress);
} else {
ControllerData->IoBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
BufferSize);
}
if (!ControllerData->IoBuffer) {
return;
}
ControllerData->IoBufferMdl = IoAllocateMdl(ControllerData->IoBuffer,
BufferSize, FALSE, FALSE, NULL);
if (!ControllerData->IoBufferMdl) {
if (allocateContiguous) {
MmFreeContiguousMemory(ControllerData->IoBuffer);
} else {
ExFreePool(ControllerData->IoBuffer);
}
ControllerData->IoBuffer = NULL;
return;
}
MmProbeAndLockPages(ControllerData->IoBufferMdl, KernelMode,
IoModifyAccess);
ControllerData->IoBufferSize = BufferSize;
}
VOID
FlFreeIoBuffer(
IN OUT PCONTROLLER_DATA ControllerData
)
/*++
Routine Description:
This routine free's the controller's IoBuffer.
Arguments:
ControllerData - Supplies the controller data.
Return Value:
None.
--*/
{
BOOLEAN contiguousBuffer;
if (!ControllerData->IoBuffer) {
return;
}
if (ControllerData->IoBufferSize > ControllerData->NumberOfMapRegisters*PAGE_SIZE) {
contiguousBuffer = TRUE;
} else {
contiguousBuffer = FALSE;
}
ControllerData->IoBufferSize = 0;
MmUnlockPages(ControllerData->IoBufferMdl);
IoFreeMdl(ControllerData->IoBufferMdl);
ControllerData->IoBufferMdl = NULL;
if (contiguousBuffer) {
MmFreeContiguousMemory(ControllerData->IoBuffer);
} else {
ExFreePool(ControllerData->IoBuffer);
}
ControllerData->IoBuffer = NULL;
}
VOID
FloppyThread(
PVOID Context
)
/*++
Routine Description:
This is the code executed by the system thread created when the
floppy driver initializes. This thread loops forever (or until a
flag is set telling the thread to kill itself) processing packets
put into the queue by the dispatch routines.
For each packet, this thread calls appropriate routines to process
the request, and then calls FlFinishOperation() to complete the
packet.
Arguments:
Context - a pointer to our data area for the controller being
supported (there is one thread per controller).
Return Value:
None.
--*/
{
PCONTROLLER_DATA controllerData = Context;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PLIST_ENTRY request;
PDISKETTE_EXTENSION disketteExtension;
NTSTATUS ntStatus;
NTSTATUS waitStatus;
LARGE_INTEGER queueWait;
BOOLEAN interruptConnected = FALSE;
//
// Set thread priority to lowest realtime level.
//
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
queueWait.QuadPart = -(3 * 1000 * 10000);
do {
//
// Wait for a request from the dispatch routines.
// KeWaitForSingleObject won't return error here - this thread
// isn't alertable and won't take APCs, and we're not passing in
// a timeout.
//
waitStatus = KeWaitForSingleObject(
(PVOID) &controllerData->RequestSemaphore,
UserRequest,
UserMode,
FALSE,
&queueWait );
if (waitStatus == STATUS_TIMEOUT) {
ExAcquireFastMutex(&controllerData->ThreadReferenceMutex);
if (controllerData->ThreadReferenceCount == 0) {
controllerData->ThreadReferenceCount = -1;
controllerData->AllowInterruptProcessing = FALSE;
FlTurnOffMotor(controllerData);
if (interruptConnected) {
IoDisconnectInterrupt(controllerData->InterruptObject);
}
ExAcquireFastMutex(PagingMutex);
if (--PagingReferenceCount == 0) {
MmPageEntireDriver(DriverEntry);
}
ExReleaseFastMutex(PagingMutex);
ExReleaseFastMutex(&controllerData->ThreadReferenceMutex);
PsTerminateSystemThread( STATUS_SUCCESS );
}
ExReleaseFastMutex(&controllerData->ThreadReferenceMutex);
continue;
}
while (request = ExInterlockedRemoveHeadList(
&controllerData->ListEntry,
&controllerData->ListSpinLock)) {
ExAcquireFastMutex(&controllerData->ThreadReferenceMutex);
ASSERT(controllerData->ThreadReferenceCount > 0);
(controllerData->ThreadReferenceCount)--;
ExReleaseFastMutex(&controllerData->ThreadReferenceMutex);
controllerData->HardwareFailed = FALSE;
irp = CONTAINING_RECORD( request, IRP, Tail.Overlay.ListEntry );
irpSp = IoGetCurrentIrpStackLocation( irp );
if (!interruptConnected) {
ntStatus = IoConnectInterrupt(&controllerData->InterruptObject,
FloppyInterruptService,
controllerData,
NULL,
controllerData->ControllerVector,
controllerData->ControllerIrql,
controllerData->ControllerIrql,
controllerData->InterruptMode,
controllerData->SharableVector,
controllerData->ProcessorMask,
controllerData->SaveFloatState);
if (!NT_SUCCESS(ntStatus)) {
irp->IoStatus.Status = ntStatus;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
continue;
}
interruptConnected = TRUE;
}
FloppyDump(
FLOPIRPPATH,
("Floppy: Starting up IRP: %x for extension %x\n",
irp,irpSp->Parameters.Read.Key)
);
switch ( irpSp->MajorFunction ) {
case IRP_MJ_READ:
case IRP_MJ_WRITE: {
//
// Get the diskette extension from where it was hidden
// in the IRP.
//
disketteExtension = (PDISKETTE_EXTENSION)
irpSp->Parameters.Read.Key;
//
// Until the file system clears the DO_VERIFY_VOLUME
// flag, we should return all requests with error.
//
if (( disketteExtension->DeviceObject->Flags &
DO_VERIFY_VOLUME ) &&
!(irpSp->Flags & SL_OVERRIDE_VERIFY_VOLUME))
{
FloppyDump(
FLOPINFO,
("Floppy: clearing queue; verify required\n")
);
//
// The disk changed, and we set this bit. Fail
// all current IRPs for this device; when all are
// returned, the file system will clear
// DO_VERIFY_VOLUME.
//
ntStatus = STATUS_VERIFY_REQUIRED;
} else {
//
// Allocate an adapter channel for the I/O.
//
FloppyDump(
FLOPIRPPATH,
("Floppy: About to allocate adapter channel %x\n",
controllerData->AdapterObject)
);
FlAllocateAdapterChannel(disketteExtension);
FloppyDump(
FLOPIRPPATH,
("Floppy: Allocated adapter channel %x\n",
controllerData->AdapterObject)
);
ntStatus = FlReadWrite( disketteExtension, irp, FALSE );
//
// Free the adapter channel that we just used.
//
FlFreeAdapterChannel(disketteExtension);
}
break;
}
case IRP_MJ_DEVICE_CONTROL: {
disketteExtension = (PDISKETTE_EXTENSION)
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
//
// Until the file system clears the DO_VERIFY_VOLUME
// flag, we should return all requests with error.
//
if (( disketteExtension->DeviceObject->Flags &
DO_VERIFY_VOLUME ) &&
!(irpSp->Flags & SL_OVERRIDE_VERIFY_VOLUME))
{
FloppyDump(
FLOPINFO,
("Floppy: clearing queue; verify required\n")
);
//
// The disk changed, and we set this bit. Fail
// all current IRPs; when all are returned, the
// file system will clear DO_VERIFY_VOLUME.
//
ntStatus = STATUS_VERIFY_REQUIRED;
} else {
switch (
irpSp->Parameters.DeviceIoControl.IoControlCode ) {
case IOCTL_DISK_CHECK_VERIFY: {
//
// Just start the drive; it will
// automatically check whether or not the
// disk has been changed.
//
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_DISK_CHECK_VERIFY called\n")
);
ntStatus = FlStartDrive(
disketteExtension,
irp,
FALSE,
FALSE,
FALSE);
break;
}
case IOCTL_DISK_IS_WRITABLE: {
//
// Start the drive with the WriteOperation
// flag set to TRUE.
//
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_DISK_IS_WRITABLE called\n")
);
if (disketteExtension->IsReadOnly) {
ntStatus = STATUS_INVALID_PARAMETER;
} else {
ntStatus = FlStartDrive(
disketteExtension,
irp,
TRUE,
FALSE,
TRUE);
}
break;
}
case IOCTL_DISK_GET_DRIVE_GEOMETRY: {
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_DISK_GET_DRIVE_GEOMETRY\n")
);
//
// If there's enough room to write the
// data, start the drive to make sure we
// know what type of media is in the drive.
//
if ( irpSp->Parameters.DeviceIoControl.
OutputBufferLength <
sizeof( DISK_GEOMETRY ) ) {
ntStatus = STATUS_INVALID_PARAMETER;
} else {
ntStatus = FlStartDrive(
disketteExtension,
irp,
FALSE,
TRUE,
(BOOLEAN)!!(irpSp->Flags &
SL_OVERRIDE_VERIFY_VOLUME));
}
//
// If the media wasn't formatted, FlStartDrive
// returned STATUS_UNRECOGNIZED_MEDIA.
//
if ( NT_SUCCESS( ntStatus ) ||
( ntStatus == STATUS_UNRECOGNIZED_MEDIA ) ) {
PDISK_GEOMETRY outputBuffer =
(PDISK_GEOMETRY)
irp->AssociatedIrp.SystemBuffer;
// Always return the media type, even if
// it's unknown.
//
ntStatus = STATUS_SUCCESS;
outputBuffer->MediaType =
disketteExtension->MediaType;
//
// The rest of the fields only have meaning
// if the media type is known.
//
if ( disketteExtension->MediaType ==
Unknown ) {
FloppyDump(
FLOPSHOW,
("Floppy: geometry unknown\n")
);
//
// Just zero out everything. The
// caller shouldn't look at it.
//
outputBuffer->Cylinders.LowPart = 0;
outputBuffer->Cylinders.HighPart = 0;
outputBuffer->TracksPerCylinder = 0;
outputBuffer->SectorsPerTrack = 0;
outputBuffer->BytesPerSector = 0;
} else {
//
// Return the geometry of the current
// media.
//
FloppyDump(
FLOPSHOW,
("Floppy: geomentry is known\n")
);
outputBuffer->Cylinders.LowPart =
disketteExtension->DriveMediaConstants.MaximumTrack + 1;
outputBuffer->Cylinders.HighPart = 0;
outputBuffer->TracksPerCylinder =
disketteExtension->DriveMediaConstants.NumberOfHeads;
outputBuffer->SectorsPerTrack =
disketteExtension->DriveMediaConstants.SectorsPerTrack;
outputBuffer->BytesPerSector =
disketteExtension->DriveMediaConstants.BytesPerSector;
}
FloppyDump(
FLOPSHOW,
("Floppy: Geometry\n"
"------- Cylinders low: 0x%x\n"
"------- Cylinders high: 0x%x\n"
"------- Track/Cyl: 0x%x\n"
"------- Sectors/Track: 0x%x\n"
"------- Bytes/Sector: 0x%x\n"
"------- Media Type: %d\n",
outputBuffer->Cylinders.LowPart,
outputBuffer->Cylinders.HighPart,
outputBuffer->TracksPerCylinder,
outputBuffer->SectorsPerTrack,
outputBuffer->BytesPerSector,
outputBuffer->MediaType)
);
}
irp->IoStatus.Information =
sizeof( DISK_GEOMETRY );
break;
}
case IOCTL_DISK_FORMAT_TRACKS_EX:
case IOCTL_DISK_FORMAT_TRACKS: {
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_DISK_FORMAT_TRACKS\n")
);
//
// Start the drive, and make sure it's not
// write protected.
//
ntStatus = FlStartDrive(
disketteExtension,
irp,
TRUE,
FALSE,
FALSE );
//
// Note that FlStartDrive could have returned
// STATUS_UNRECOGNIZED_MEDIA if the drive
// wasn't formatted.
//
if ( NT_SUCCESS( ntStatus ) ||
( ntStatus == STATUS_UNRECOGNIZED_MEDIA ) ) {
//
// Allocate an adapter channel to do
// the format.
//
FlAllocateAdapterChannel(disketteExtension);
//
// We need a single page to do FORMATs.
// If we already allocated a buffer,
// we'll use that. If not, let's
// allocate a single page. Note that
// we'd have to do this anyway if there's
// not enough map registers.
//
FlAllocateIoBuffer(controllerData, PAGE_SIZE);
if (controllerData->IoBuffer) {
ntStatus = FlFormat(disketteExtension,
irp);
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
//
// Free the adapter channel.
//
FlFreeAdapterChannel(disketteExtension);
}
break;
} //end of case format
#if defined(DBCS) && defined(_MIPS_)
case IOCTL_DISK_READ:
case IOCTL_DISK_WRITE: {
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_DISK_READ WRITE\n")
);
//
// Start the drive, and make sure it's not
// write protected.
//
ntStatus = FlStartDrive(
disketteExtension,
irp,
TRUE,
FALSE,
FALSE );
//
// Allocate an adapter channel to do
// the format.
//
FlAllocateAdapterChannel(disketteExtension);
ntStatus = FlReadWrite_PTOS(
disketteExtension,
irp );
//
// Free the adapter channel.
//
FlFreeAdapterChannel(disketteExtension);
break;
} //end of case read/write
case IOCTL_DISK_SENSE_DEVICE: {
controllerData->FifoBuffer[0] = disketteExtension->DeviceUnit;
ntStatus = FlSendByte( COMMND_SENSE_DRIVE, controllerData );
if ( NT_SUCCESS( ntStatus ) ) {
ntStatus = FlSendByte(
controllerData->FifoBuffer[0],
controllerData );
if ( NT_SUCCESS( ntStatus ) ) {
ntStatus = FlGetByte(
&controllerData->FifoBuffer[0],
controllerData );
Result_Status3_PTOS[0].ST3_PTOS = controllerData->FifoBuffer[0]; //PTOS
}
}
if ( NT_SUCCESS( ntStatus ) ) {
PSENSE_DEVISE_STATUS_PTOS outputBuffer
=(PSENSE_DEVISE_STATUS_PTOS)irp->AssociatedIrp.SystemBuffer;
((PSENSE_DEVISE_STATUS_PTOS)outputBuffer)->ST3_PTOS
=Result_Status3_PTOS[0].ST3_PTOS;
irp->IoStatus.Information = sizeof( SENSE_DEVISE_STATUS_PTOS );
}
break;
} //end of case sense device
#endif // DBCS && _MIPS_
} //end of switch controlcode
}
break;
} //end of case IOCTL
default: {
FloppyDump(
FLOPDBGP,
("Floppy: bad majorfunction %x\n",irpSp->MajorFunction)
);
ntStatus = STATUS_NOT_IMPLEMENTED;
}
} //end of switch on majorfunction
if (ntStatus == STATUS_DEVICE_BUSY) {
// If the status is DEVICE_BUSY then this indicates that the
// qic117 has control of the controller. Therefore complete
// all remaining requests with STATUS_DEVICE_BUSY.
for (;;) {
controllerData->HardwareFailed = FALSE;
irp->IoStatus.Status = STATUS_DEVICE_BUSY;
IoCompleteRequest(irp, IO_DISK_INCREMENT);
request = ExInterlockedRemoveHeadList(
&controllerData->ListEntry,
&controllerData->ListSpinLock );
if (!request) {
break;
}
ExAcquireFastMutex(&controllerData->ThreadReferenceMutex);
ASSERT(controllerData->ThreadReferenceCount > 0);
(controllerData->ThreadReferenceCount)--;
ExReleaseFastMutex(&controllerData->ThreadReferenceMutex);
irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry);
}
} else {
//
// All operations leave a final status in ntStatus. Copy it
// to the IRP, and then complete the operation.
//
irp->IoStatus.Status = ntStatus;
FlFinishOperation( irp, disketteExtension );
}
} // while there's packets to process
} while ( TRUE );
}
IO_ALLOCATION_ACTION
FloppyAllocateAdapterChannel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
)
/*++
Routine Description:
This DPC is called whenever the floppy thread is trying to allocate
the adapter channel (like before doing a read or write). 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).
--*/
{
PCONTROLLER_DATA controllerData = Context;
UNREFERENCED_PARAMETER( DeviceObject );
UNREFERENCED_PARAMETER( Irp );
controllerData->MapRegisterBase = MapRegisterBase;
KeSetEvent(
&controllerData->AllocateAdapterChannelEvent,
0L,
FALSE );
return KeepObject;
}
VOID
FlConsolidateMediaTypeWithBootSector(
IN OUT PDISKETTE_EXTENSION DisketteExtension,
IN PBOOT_SECTOR_INFO BootSector
)
/*++
Routine Description:
This routine adjusts the DisketteExtension data according
to the BPB values if this is appropriate.
Arguments:
DisketteExtension - Supplies the diskette extension.
BootSector - Supplies the boot sector information.
Return Value:
None.
--*/
{
USHORT bpbNumberOfSectors, bpbNumberOfHeads;
USHORT bpbSectorsPerTrack, bpbBytesPerSector;
USHORT bpbMediaByte, bpbMaximumTrack;
MEDIA_TYPE bpbMediaType;
ULONG i, n;
PDRIVE_MEDIA_CONSTANTS readidDriveMediaConstants;
BOOLEAN changeToBpbMedia;
FloppyDump(
FLOPSHOW,
("Floppy: First sector read: media descriptor is: 0x%x\n",
BootSector->MediaByte[0])
);
if (BootSector->JumpByte[0] != 0xeb &&
BootSector->JumpByte[0] != 0xe9) {
// This is not a formatted floppy so ignore the BPB.
return;
}
bpbNumberOfSectors = BootSector->NumberOfSectors[1]*0x100 +
BootSector->NumberOfSectors[0];
bpbNumberOfHeads = BootSector->NumberOfHeads[1]*0x100 +
BootSector->NumberOfHeads[0];
bpbSectorsPerTrack = BootSector->SectorsPerTrack[1]*0x100 +
BootSector->SectorsPerTrack[0];
bpbBytesPerSector = BootSector->BytesPerSector[1]*0x100 +
BootSector->BytesPerSector[0];
bpbMediaByte = BootSector->MediaByte[0];
if (!bpbNumberOfHeads || !bpbSectorsPerTrack) {
// Invalid BPB, avoid dividing by zero.
return;
}
bpbMaximumTrack = bpbNumberOfSectors/bpbNumberOfHeads/bpbSectorsPerTrack - 1;
// First figure out if this BPB specifies a known media type
// independantly of the current drive type.
bpbMediaType = Unknown;
for (i = 0; i < NUMBER_OF_DRIVE_MEDIA_COMBINATIONS; i++) {
if (bpbBytesPerSector == DriveMediaConstants[i].BytesPerSector &&
bpbSectorsPerTrack == DriveMediaConstants[i].SectorsPerTrack &&
bpbMaximumTrack == DriveMediaConstants[i].MaximumTrack &&
bpbNumberOfHeads == DriveMediaConstants[i].NumberOfHeads &&
bpbMediaByte == DriveMediaConstants[i].MediaByte) {
bpbMediaType = DriveMediaConstants[i].MediaType;
break;
}
}
FloppyDump(
FLOPSHOW,
("FLOPPY: After switch media type is: %x\n",bpbMediaType)
);
FloppyDump(
FLOPINFO,
("FloppyBpb: Media type ")
);
if (bpbMediaType == DisketteExtension->MediaType) {
// No conflict between BPB and readId result.
changeToBpbMedia = FALSE;
FloppyDump(
FLOPINFO,
("is same\n")
);
} else {
// There is a conflict between the BPB and the readId
// media type. If the new parameters are acceptable
// then go with them.
readidDriveMediaConstants = &(DisketteExtension->DriveMediaConstants);
if (bpbBytesPerSector == readidDriveMediaConstants->BytesPerSector &&
bpbSectorsPerTrack < 0x100 &&
bpbMaximumTrack == readidDriveMediaConstants->MaximumTrack &&
bpbNumberOfHeads <= readidDriveMediaConstants->NumberOfHeads) {
changeToBpbMedia = TRUE;
} else {
changeToBpbMedia = FALSE;
}
FloppyDump(
FLOPINFO,
("%s", changeToBpbMedia ? "will change to Bpb\n" : "will not change\n")
);
// If we didn't derive a new media type from the BPB then
// just use the one from readId. Also override any
// skew compensation since we don't really know anything
// about this new media type.
if (bpbMediaType == Unknown) {
bpbMediaType = readidDriveMediaConstants->MediaType;
DisketteExtension->DriveMediaConstants.SkewDelta = 0;
}
}
if (changeToBpbMedia) {
// Change the DriveMediaType only if this new media type
// falls in line with what is supported by the drive.
i = DriveMediaLimits[DisketteExtension->DriveType].LowestDriveMediaType;
n = DriveMediaLimits[DisketteExtension->DriveType].HighestDriveMediaType;
for (; i <= n; i++) {
if (bpbMediaType == DriveMediaConstants[i].MediaType) {
DisketteExtension->DriveMediaType = i;
break;
}
}
DisketteExtension->MediaType = bpbMediaType;
DisketteExtension->ByteCapacity = bpbNumberOfSectors*bpbBytesPerSector;
DisketteExtension->DriveMediaConstants.SectorsPerTrack = (UCHAR) bpbSectorsPerTrack;
DisketteExtension->DriveMediaConstants.NumberOfHeads = (UCHAR) bpbNumberOfHeads;
// If the MSDMF3. signature is there then make this floppy
// read-only.
if (RtlCompareMemory(BootSector->OemData, "MSDMF3.", 7) == 7) {
DisketteExtension->IsReadOnly = TRUE;
}
}
}
VOID
FlCheckBootSector(
IN OUT PDISKETTE_EXTENSION DisketteExtension
)
/*++
Routine Description:
This routine reads the boot sector and then figures
out whether or not the boot sector contains new geometry
information.
Arguments:
DisketteExtension - Supplies the diskette extension.
Return Value:
None.
--*/
{
#define BOOT_SECTOR_SIZE 512
PBOOT_SECTOR_INFO bootSector;
LARGE_INTEGER offset;
PIRP irp;
NTSTATUS status;
// Set up the IRP to read the boot sector.
bootSector = ExAllocatePool(NonPagedPoolCacheAligned, BOOT_SECTOR_SIZE);
if (!bootSector) {
return;
}
offset.LowPart = offset.HighPart = 0;
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ,
DisketteExtension->DeviceObject,
bootSector,
BOOT_SECTOR_SIZE,
&offset,
NULL);
if (!irp) {
ExFreePool(bootSector);
return;
}
irp->CurrentLocation--;
irp->Tail.Overlay.CurrentStackLocation = IoGetNextIrpStackLocation(irp);
// Allocate an adapter channel, do read, free adapter channel.
FlAllocateAdapterChannel(DisketteExtension);
status = FlReadWrite(DisketteExtension, irp, TRUE);
FlFreeAdapterChannel(DisketteExtension);
MmUnlockPages(irp->MdlAddress);
IoFreeMdl(irp->MdlAddress);
IoFreeIrp(irp);
ExFreePool(bootSector);
}
NTSTATUS
FlReadWriteTrack(
IN OUT PDISKETTE_EXTENSION DisketteExtension,
IN OUT PMDL IoMdl,
IN OUT PVOID IoBuffer,
IN BOOLEAN WriteOperation,
IN UCHAR Cylinder,
IN UCHAR Head,
IN UCHAR Sector,
IN UCHAR NumberOfSectors,
IN BOOLEAN NeedSeek
)
/*++
Routine Description:
This routine reads a portion of a track. It transfers the to or from the
device from or to the given IoBuffer and IoMdl.
Arguments:
DisketteExtension - Supplies the diskette extension.
IoMdl - Supplies the Mdl for transfering from/to the device.
IoBuffer - Supplies the buffer to transfer from/to the device.
WriteOperation - Supplies whether or not this is a write operation.
Cylinder - Supplies the cylinder number for this track.
Head - Supplies the head number for this track.
Sector - Supplies the starting sector of the transfer.
NumberOfSectors - Supplies the number of sectors to transfer.
NeedSeek - Supplies whether or not we need to do a seek.
Return Value:
An NTSTATUS code.
--*/
{
PDRIVE_MEDIA_CONSTANTS driveMediaConstants;
ULONG byteToSectorShift;
PCONTROLLER_DATA controllerData;
ULONG transferBytes;
LARGE_INTEGER headSettleTime;
NTSTATUS status;
ULONG seekRetry, ioRetry;
BOOLEAN recalibrateDrive = FALSE;
UCHAR i;
FloppyDump(
FLOPSHOW,
("\nFlReadWriteTrack:%sseek for %s at chs %d/%d/%d for %d sectors\n",
NeedSeek ? " need " : " ",
WriteOperation ? "write" : "read",
Cylinder,
Head,
Sector,
NumberOfSectors)
);
driveMediaConstants = &DisketteExtension->DriveMediaConstants;
byteToSectorShift = SECTORLENGTHCODE_TO_BYTESHIFT +
driveMediaConstants->SectorLengthCode;
controllerData = DisketteExtension->ControllerData;
transferBytes = ((ULONG) NumberOfSectors)<<byteToSectorShift;
headSettleTime.LowPart = -(10*1000*driveMediaConstants->HeadSettleTime);
headSettleTime.HighPart = -1;
for (seekRetry = 0, ioRetry = 0; seekRetry < 3; seekRetry++) {
if (recalibrateDrive) {
// Something failed, so recalibrate the drive.
FloppyDump(
FLOPINFO,
("FlReadWriteTrack: performing recalibrate\n")
);
FlRecalibrateDrive(DisketteExtension);
}
// Do a seek if we have to.
if (recalibrateDrive ||
(NeedSeek &&
(!controllerData->ControllerConfigurable ||
driveMediaConstants->CylinderShift != 0))) {
controllerData->FifoBuffer[0] = (Head<<2) |
DisketteExtension->DeviceUnit;
controllerData->FifoBuffer[1] = Cylinder<<
driveMediaConstants->CylinderShift;
status = FlIssueCommand(COMMND_SEEK, DisketteExtension);
if (NT_SUCCESS(status)) {
// Check the completion state of the controller.
if (!(controllerData->FifoBuffer[0]&STREG0_SEEK_COMPLETE) ||
controllerData->FifoBuffer[1] !=
Cylinder<<driveMediaConstants->CylinderShift) {
controllerData->HardwareFailed = TRUE;
status = STATUS_FLOPPY_BAD_REGISTERS;
}
if (NT_SUCCESS(status)) {
// Delay after doing seek.
KeDelayExecutionThread(KernelMode, FALSE, &headSettleTime);
// SEEKs should always be followed by a READID.
controllerData->FifoBuffer[0] = (Head<<2) |
DisketteExtension->DeviceUnit;
status = FlIssueCommand(COMMND_READ_ID + COMMND_MFM,
DisketteExtension);
if (NT_SUCCESS(status)) {
if (controllerData->FifoBuffer[0] !=
((Head<<2) | DisketteExtension->DeviceUnit) ||
controllerData->FifoBuffer[1] != 0 ||
controllerData->FifoBuffer[2] != 0 ||
controllerData->FifoBuffer[3] != Cylinder) {
controllerData->HardwareFailed = TRUE;
status = FlInterpretError(
controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2]);
}
} else {
FloppyDump(
FLOPINFO,
("FlReadWriteTrack: Read ID failed %x\n", status)
);
}
}
} else {
FloppyDump(
FLOPINFO,
("FlReadWriteTrack: SEEK failed %x\n", status)
);
}
} else {
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(status)) {
// The seek failed so try again.
FloppyDump(
FLOPINFO,
("FlReadWriteTrack: setup failure %x - recalibrating\n", status)
);
recalibrateDrive = TRUE;
continue;
}
for (;; ioRetry++) {
// We've done the seek or the seek will be implied.
// Do the read. Map the transfer through the DMA.
KeFlushIoBuffers(IoMdl, !WriteOperation, TRUE);
//
// We can ignore the logical address returned by
// IoMapTransfer, since we're not using busmaster
// stuff.
//
IoMapTransfer(controllerData->AdapterObject,
IoMdl, controllerData->MapRegisterBase,
IoBuffer, &transferBytes, WriteOperation);
//
// Issue the READ or WRITE command
//
controllerData->FifoBuffer[0] = (Head<<2) |
DisketteExtension->DeviceUnit;
controllerData->FifoBuffer[1] = Cylinder;
controllerData->FifoBuffer[2] = Head;
controllerData->FifoBuffer[3] = Sector + 1;
controllerData->FifoBuffer[4] =
driveMediaConstants->SectorLengthCode;
controllerData->FifoBuffer[5] = Sector + NumberOfSectors;
controllerData->FifoBuffer[6] =
driveMediaConstants->ReadWriteGapLength;
controllerData->FifoBuffer[7] = driveMediaConstants->DataLength;
if (WriteOperation) {
status = FlIssueCommand(COMMND_WRITE_DATA + COMMND_MFM,
DisketteExtension);
} else {
status = FlIssueCommand(COMMND_READ_DATA + COMMND_MFM,
DisketteExtension);
}
//
// Flush the DMA adapter buffers.
//
IoFlushAdapterBuffers(controllerData->AdapterObject,
IoMdl, controllerData->MapRegisterBase,
IoBuffer, transferBytes, WriteOperation);
if (NT_SUCCESS(status)) {
if ((controllerData->FifoBuffer[0]&STREG0_END_MASK) !=
STREG0_END_NORMAL &&
((controllerData->FifoBuffer[0]&STREG0_END_MASK) !=
STREG0_END_ERROR ||
controllerData->FifoBuffer[1] != STREG1_END_OF_DISKETTE ||
controllerData->FifoBuffer[2] != STREG2_SUCCESS)) {
controllerData->HardwareFailed = TRUE;
status = FlInterpretError(controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2]);
}
} else {
FloppyDump(
FLOPINFO,
("FlReadWriteTrack: %s command failed %x\n",
WriteOperation ? "write" : "read",
status)
);
}
if (NT_SUCCESS(status)) {
break;
}
if (ioRetry >= 2) {
FloppyDump(
FLOPINFO,
("FlReadWriteTrack: too many retries - failing\n")
);
break;
}
}
if (NT_SUCCESS(status)) {
break;
}
// We failed quite a bit so make seeks mandatory.
recalibrateDrive = TRUE;
}
if (!NT_SUCCESS(status) && NumberOfSectors > 1) {
// Retry one sector at a time.
FloppyDump(
FLOPINFO,
("FlReadWriteTrack: Attempting sector at a time\n")
);
for (i = 0; i < NumberOfSectors; i++) {
status = FlReadWriteTrack(DisketteExtension, IoMdl,
((PCHAR) IoBuffer) +
(((ULONG) i)<<byteToSectorShift),
WriteOperation, Cylinder, Head,
(UCHAR) (Sector + i), 1, FALSE);
if (!NT_SUCCESS(status)) {
FloppyDump(
FLOPINFO,
("FlReadWriteTrack: failed sector %d status %x\n",
i,
status)
);
break;
}
}
}
return status;
}
NTSTATUS
FlReadWrite(
IN OUT PDISKETTE_EXTENSION DisketteExtension,
IN OUT PIRP Irp,
IN BOOLEAN DriveStarted
)
/*++
Routine Description:
This routine is called by the floppy thread to read/write data
to/from the diskette. It breaks the request into pieces called
"transfers" (their size depends on the buffer size, where the end of
the track is, etc) and retries each transfer until it succeeds or
the retry count is exceeded.
Arguments:
DisketteExtension - a pointer to our data area for the drive to be
accessed.
Irp - a pointer to the IO Request Packet.
DriveStarted - indicated whether or not the drive has been started.
Return Value:
STATUS_SUCCESS if the packet was successfully read or written; the
appropriate error is propogated otherwise.
--*/
{
PIO_STACK_LOCATION irpSp;
PCONTROLLER_DATA controllerData;
BOOLEAN writeOperation;
NTSTATUS status;
PDRIVE_MEDIA_CONSTANTS driveMediaConstants;
ULONG byteToSectorShift;
ULONG currentSector, firstSector, lastSector;
ULONG trackSize;
UCHAR sectorsPerTrack, numberOfHeads;
UCHAR currentHead, currentCylinder, trackSector;
PCHAR userBuffer;
UCHAR skew, skewDelta;
UCHAR numTransferSectors;
PMDL mdl;
PCHAR ioBuffer;
irpSp = IoGetCurrentIrpStackLocation(Irp);
controllerData = DisketteExtension->ControllerData;
FloppyDump(
FLOPSHOW,
("FlReadWrite: for %s at offset %x size %x ",
irpSp->MajorFunction == IRP_MJ_WRITE ? "write" : "read",
irpSp->Parameters.Read.ByteOffset.LowPart,
irpSp->Parameters.Read.Length)
);
// Check for valid operation on this device.
if (irpSp->MajorFunction == IRP_MJ_WRITE) {
if (DisketteExtension->IsReadOnly) {
FloppyDump(
FLOPSHOW,
("is read-only\n")
);
return STATUS_INVALID_PARAMETER;
}
writeOperation = TRUE;
} else {
writeOperation = FALSE;
}
FloppyDump(
FLOPSHOW,
("\n")
);
// Start up the drive.
if (DriveStarted) {
status = STATUS_SUCCESS;
} else {
status = FlStartDrive(DisketteExtension, Irp, writeOperation, TRUE,
(BOOLEAN)!!(irpSp->Flags&SL_OVERRIDE_VERIFY_VOLUME));
}
if (!NT_SUCCESS(status)) {
FloppyDump(
FLOPSHOW,
("FlReadWrite: error on start %x\n", status)
);
return status;
}
if (DisketteExtension->MediaType == Unknown) {
FloppyDump(
FLOPSHOW,
("not recognized\n")
);
return STATUS_UNRECOGNIZED_MEDIA;
}
// The drive has started up with a recognized media.
// Gather some relavant parameters.
driveMediaConstants = &DisketteExtension->DriveMediaConstants;
byteToSectorShift = SECTORLENGTHCODE_TO_BYTESHIFT +
driveMediaConstants->SectorLengthCode;
firstSector = irpSp->Parameters.Read.ByteOffset.LowPart>>
byteToSectorShift;
lastSector = firstSector + (irpSp->Parameters.Read.Length>>
byteToSectorShift);
sectorsPerTrack = driveMediaConstants->SectorsPerTrack;
numberOfHeads = driveMediaConstants->NumberOfHeads;
userBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
trackSize = ((ULONG) sectorsPerTrack)<<byteToSectorShift;
skew = 0;
skewDelta = driveMediaConstants->SkewDelta;
for (currentSector = firstSector;
currentSector < lastSector;
currentSector += numTransferSectors) {
// Compute cylinder, head and sector from absolute sector.
currentCylinder = (UCHAR) (currentSector/sectorsPerTrack/numberOfHeads);
trackSector = (UCHAR) (currentSector%sectorsPerTrack);
currentHead = (UCHAR) (currentSector/sectorsPerTrack%numberOfHeads);
numTransferSectors = sectorsPerTrack - trackSector;
if (lastSector - currentSector < numTransferSectors) {
numTransferSectors = (UCHAR) (lastSector - currentSector);
}
//
// If we're using a temporary IO buffer because of
// insufficient registers in the DMA and we're
// doing a write then copy the write buffer to
// the contiguous buffer.
//
if (trackSize > controllerData->NumberOfMapRegisters*PAGE_SIZE) {
FlAllocateIoBuffer(controllerData, trackSize);
if (!controllerData->IoBuffer) {
FloppyDump(
FLOPSHOW,
("FlReadWrite: no resources\n")
);
return STATUS_INSUFFICIENT_RESOURCES;
}
mdl = controllerData->IoBufferMdl;
ioBuffer = controllerData->IoBuffer;
if (writeOperation) {
RtlMoveMemory(ioBuffer,
userBuffer + ((currentSector - firstSector)<<
byteToSectorShift),
((ULONG) numTransferSectors)<<byteToSectorShift);
}
} else {
mdl = Irp->MdlAddress;
ioBuffer = (PCHAR) MmGetMdlVirtualAddress(Irp->MdlAddress) +
((currentSector - firstSector)<<byteToSectorShift);
}
//
// Transfer the track.
// Do what we can to avoid missing revs.
//
// Alter the skew to be in the range of what
// we're transfering.
if (skew >= numTransferSectors + trackSector) {
skew = 0;
}
if (skew < trackSector) {
skew = trackSector;
}
// Go from skew to the end of the irp.
status = FlReadWriteTrack(
DisketteExtension, mdl,
ioBuffer + (((ULONG) skew - trackSector)<<byteToSectorShift),
writeOperation, currentCylinder, currentHead, skew,
(UCHAR) (numTransferSectors + trackSector - skew), TRUE);
// Go from start of irp to skew.
if (NT_SUCCESS(status) && skew > trackSector) {
status = FlReadWriteTrack(
DisketteExtension, mdl, ioBuffer, writeOperation,
currentCylinder, currentHead, trackSector,
(UCHAR) (skew - trackSector), FALSE);
} else {
skew = (numTransferSectors + trackSector)%sectorsPerTrack;
}
if (!NT_SUCCESS(status)) {
break;
}
//
// If we used the temporary IO buffer to do the
// read then copy the contents back to the IRPs buffer.
//
if (!writeOperation &&
trackSize > controllerData->NumberOfMapRegisters*PAGE_SIZE) {
RtlMoveMemory(userBuffer + ((currentSector - firstSector)<<
byteToSectorShift),
ioBuffer, ((ULONG) numTransferSectors)<<byteToSectorShift);
KeFlushIoBuffers(Irp->MdlAddress, TRUE, FALSE);
}
//
// Increment the skew. Do this even if just switching sides
// for National Super I/O chips.
//
skew = (skew + skewDelta)%sectorsPerTrack;
}
Irp->IoStatus.Information = (currentSector - firstSector)<<byteToSectorShift;
// If the read was successful then consolidate the
// boot sector with the determined density.
if (NT_SUCCESS(status) && firstSector == 0) {
FlConsolidateMediaTypeWithBootSector(DisketteExtension,
(PBOOT_SECTOR_INFO) userBuffer);
}
FloppyDump(
FLOPSHOW,
("FlReadWrite: completed status %x information %d\n", status, Irp->IoStatus.Information)
);
return status;
}
NTSTATUS
FlFormat(
IN PDISKETTE_EXTENSION DisketteExtension,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the floppy thread to format some tracks on
the diskette. This won't take TOO long because the FORMAT utility
is written to only format a few tracks at a time so that it can keep
a display of what percentage of the disk has been formatted.
Arguments:
DisketteExtension - pointer to our data area for the diskette to be
formatted.
Irp - pointer to the IO Request Packet.
Return Value:
STATUS_SUCCESS if the tracks were formatted; appropriate error
propogated otherwise.
--*/
{
LARGE_INTEGER headSettleTime;
PCONTROLLER_DATA controllerData;
PIO_STACK_LOCATION irpSp;
PBAD_TRACK_NUMBER badTrackBuffer;
PFORMAT_PARAMETERS formatParameters;
PFORMAT_EX_PARAMETERS formatExParameters;
PDRIVE_MEDIA_CONSTANTS driveMediaConstants;
NTSTATUS ntStatus;
ULONG badTrackBufferLength;
DRIVE_MEDIA_TYPE driveMediaType;
UCHAR driveStatus;
UCHAR numberOfBadTracks = 0;
UCHAR currentTrack;
UCHAR endTrack;
UCHAR whichSector;
UCHAR retryCount;
BOOLEAN bufferOverflow = FALSE;
FloppyDump(
FLOPSHOW,
("Floppy: FlFormat...\n")
);
controllerData = DisketteExtension->ControllerData;
irpSp = IoGetCurrentIrpStackLocation( Irp );
formatParameters = (PFORMAT_PARAMETERS) Irp->AssociatedIrp.SystemBuffer;
if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_DISK_FORMAT_TRACKS_EX) {
formatExParameters =
(PFORMAT_EX_PARAMETERS) Irp->AssociatedIrp.SystemBuffer;
} else {
formatExParameters = NULL;
}
FloppyDump(
FLOPFORMAT,
("Floppy: Format Params - MediaType: %d\n"
"------ Start Cyl: %x\n"
"------ End Cyl: %x\n"
"------ Start Hd: %d\n"
"------ End Hd: %d\n",
formatParameters->MediaType,
formatParameters->StartCylinderNumber,
formatParameters->EndCylinderNumber,
formatParameters->StartHeadNumber,
formatParameters->EndHeadNumber)
);
badTrackBufferLength =
irpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// Figure out which entry in the DriveMediaConstants table to use.
// We know we'll find one, or FlCheckFormatParameters() would have
// rejected the request.
//
driveMediaType =
DriveMediaLimits[DisketteExtension->DriveType].HighestDriveMediaType;
while ( ( DriveMediaConstants[driveMediaType].MediaType !=
formatParameters->MediaType ) &&
( driveMediaType > DriveMediaLimits[DisketteExtension->DriveType].
LowestDriveMediaType ) ) {
driveMediaType--;
}
driveMediaConstants = &DriveMediaConstants[driveMediaType];
//
// Set some values in the diskette extension to indicate what we
// know about the media type.
//
DisketteExtension->MediaType = formatParameters->MediaType;
DisketteExtension->DriveMediaType = driveMediaType;
DisketteExtension->DriveMediaConstants = DriveMediaConstants[driveMediaType];
if (formatExParameters) {
DisketteExtension->DriveMediaConstants.SectorsPerTrack =
(UCHAR) formatExParameters->SectorsPerTrack;
DisketteExtension->DriveMediaConstants.FormatGapLength =
(UCHAR) formatExParameters->FormatGapLength;
}
driveMediaConstants = &(DisketteExtension->DriveMediaConstants);
DisketteExtension->BytesPerSector = driveMediaConstants->BytesPerSector;
DisketteExtension->ByteCapacity =
( driveMediaConstants->BytesPerSector ) *
driveMediaConstants->SectorsPerTrack *
( 1 + driveMediaConstants->MaximumTrack ) *
driveMediaConstants->NumberOfHeads;
currentTrack = (UCHAR)( ( formatParameters->StartCylinderNumber *
driveMediaConstants->NumberOfHeads ) +
formatParameters->StartHeadNumber );
endTrack = (UCHAR)( ( formatParameters->EndCylinderNumber *
driveMediaConstants->NumberOfHeads ) +
formatParameters->EndHeadNumber );
FloppyDump(
FLOPFORMAT,
("Floppy: Format - Starting/ending tracks: %x/%x\n",
currentTrack,
endTrack)
);
//
// Set the data rate (which depends on the drive/media
// type).
//
if ( controllerData->LastDriveMediaType != driveMediaType ) {
ntStatus = FlDatarateSpecifyConfigure( DisketteExtension );
if ( !NT_SUCCESS( ntStatus ) ) {
return ntStatus;
}
}
//
// Since we're doing a format, make this drive writable.
//
DisketteExtension->IsReadOnly = FALSE;
//
// Format each track.
//
do {
//
// Seek to proper cylinder
//
controllerData->FifoBuffer[0] = DisketteExtension->DeviceUnit;
controllerData->FifoBuffer[1] = (UCHAR)( ( currentTrack /
driveMediaConstants->NumberOfHeads ) <<
driveMediaConstants->CylinderShift );
FloppyDump(
FLOPFORMAT,
("Floppy: Format seek to cylinder: %x\n",
controllerData->FifoBuffer[1])
);
ntStatus = FlIssueCommand( COMMND_SEEK, DisketteExtension );
if ( NT_SUCCESS( ntStatus ) ) {
if ( ( controllerData->FifoBuffer[0] & STREG0_SEEK_COMPLETE ) &&
( controllerData->FifoBuffer[1] == (UCHAR)( ( currentTrack /
driveMediaConstants->NumberOfHeads ) <<
driveMediaConstants->CylinderShift ) ) ) {
//
// Must delay HeadSettleTime milliseconds before
// doing anything after a SEEK.
//
headSettleTime.LowPart = - ( 10 * 1000 *
driveMediaConstants->HeadSettleTime );
headSettleTime.HighPart = -1;
KeDelayExecutionThread(
KernelMode,
FALSE,
&headSettleTime );
//
// Read ID. Note that we don't bother checking the return
// registers, because if this media wasn't formatted we'd
// get an error.
//
controllerData->FifoBuffer[0] = DisketteExtension->DeviceUnit;
ntStatus = FlIssueCommand(
COMMND_READ_ID + COMMND_MFM,
DisketteExtension );
} else {
FloppyDump(
FLOPWARN,
("Floppy: format's seek returned bad registers\n"
"------ Statusreg0 = %x\n"
"------ Statusreg1 = %x\n",
controllerData->FifoBuffer[0],
controllerData->FifoBuffer[1])
);
controllerData->HardwareFailed = TRUE;
ntStatus = STATUS_FLOPPY_BAD_REGISTERS;
}
}
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPWARN,
("Floppy: format's seek/readid returned %x\n", ntStatus)
);
return ntStatus;
}
//
// Fill the buffer with the format of this track.
//
for (whichSector = 0;
whichSector < driveMediaConstants->SectorsPerTrack;
whichSector++) {
controllerData->IoBuffer[whichSector*4] =
currentTrack/driveMediaConstants->NumberOfHeads;
controllerData->IoBuffer[whichSector*4 + 1] =
currentTrack%driveMediaConstants->NumberOfHeads;
if (formatExParameters) {
controllerData->IoBuffer[whichSector*4 + 2] =
(UCHAR) formatExParameters->SectorNumber[whichSector];
} else {
controllerData->IoBuffer[whichSector*4 + 2] = whichSector + 1;
}
controllerData->IoBuffer[whichSector*4 + 3] =
driveMediaConstants->SectorLengthCode;
FloppyDump(
FLOPFORMAT,
("Floppy - Format table entry %x - %x/%x/%x/%x\n",
whichSector,
controllerData->IoBuffer[whichSector*4],
controllerData->IoBuffer[whichSector*4 + 1],
controllerData->IoBuffer[whichSector*4 + 2],
controllerData->IoBuffer[whichSector*4 + 3])
);
}
//
// Retry until success or too many retries.
//
retryCount = 0;
do {
ULONG length;
length = driveMediaConstants->BytesPerSector;
//
// Map the transfer from the buffer to the disk.
//
KeFlushIoBuffers( controllerData->IoBufferMdl, FALSE, TRUE );
IoMapTransfer(
controllerData->AdapterObject,
controllerData->IoBufferMdl,
controllerData->MapRegisterBase,
controllerData->IoBuffer,
&length,
TRUE );
//
// Issue command to format track
//
controllerData->FifoBuffer[0] = (UCHAR)
( ( ( currentTrack % driveMediaConstants->NumberOfHeads ) << 2 )
| DisketteExtension->DeviceUnit );
controllerData->FifoBuffer[1] =
driveMediaConstants->SectorLengthCode;
controllerData->FifoBuffer[2] =
driveMediaConstants->SectorsPerTrack;
controllerData->FifoBuffer[3] =
driveMediaConstants->FormatGapLength;
controllerData->FifoBuffer[4] =
driveMediaConstants->FormatFillCharacter;
FloppyDump(
FLOPFORMAT,
("Floppy: format command parameters\n"
"------ Head/Unit: %x\n"
"------ Bytes/Sector: %x\n"
"------ Sectors/Cylinder: %x\n"
"------ Gap 3: %x\n"
"------ Filler Byte: %x\n",
controllerData->FifoBuffer[0],
controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2],
controllerData->FifoBuffer[3],
controllerData->FifoBuffer[4])
);
ntStatus = FlIssueCommand(
COMMND_FORMAT_TRACK + COMMND_MFM,
DisketteExtension );
IoFlushAdapterBuffers(
controllerData->AdapterObject,
controllerData->IoBufferMdl,
controllerData->MapRegisterBase,
controllerData->IoBuffer,
length,
TRUE );
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: format returned %x\n", ntStatus)
);
}
if ( NT_SUCCESS( ntStatus ) ) {
//
// Check the return bytes from the controller.
//
if ( ( controllerData->FifoBuffer[0] &
( STREG0_DRIVE_FAULT |
STREG0_END_INVALID_COMMAND ) )
|| ( controllerData->FifoBuffer[1] &
STREG1_DATA_OVERRUN ) ||
( controllerData->FifoBuffer[2] != 0 ) ) {
FloppyDump(
FLOPWARN,
("Floppy: format had bad registers\n"
"------ Streg0 = %x\n"
"------ Streg1 = %x\n"
"------ Streg2 = %x\n",
controllerData->FifoBuffer[0],
controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2])
);
controllerData->HardwareFailed = TRUE;
ntStatus = FlInterpretError(
controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2] );
}
}
} while ( ( !NT_SUCCESS( ntStatus ) ) &&
( retryCount++ < RECALIBRATE_RETRY_COUNT ) );
if ( !NT_SUCCESS( ntStatus ) ) {
driveStatus = READ_CONTROLLER(
&controllerData->ControllerAddress->DRDC.DiskChange );
if ( (DisketteExtension->DriveType != DRIVE_TYPE_0360) &&
FlDisketteRemoved(controllerData, driveStatus, FALSE) ) {
//
// The user apparently popped the floppy. Return error
// rather than logging bad track.
//
return ntStatus;
}
//
// Log the bad track.
//
FloppyDump(
FLOPDBGP,
("Floppy: track %x is bad\n", currentTrack)
);
if ( badTrackBufferLength >= (ULONG)
( ( numberOfBadTracks + 1 ) * sizeof( BAD_TRACK_NUMBER ) ) ) {
badTrackBuffer = (PBAD_TRACK_NUMBER)
Irp->AssociatedIrp.SystemBuffer;
badTrackBuffer[numberOfBadTracks] = ( BAD_TRACK_NUMBER )
currentTrack;
} else {
bufferOverflow = TRUE;
}
numberOfBadTracks++;
}
currentTrack++;
} while ( currentTrack <= endTrack );
if ( ( NT_SUCCESS( ntStatus ) ) && ( bufferOverflow ) ) {
ntStatus = STATUS_BUFFER_OVERFLOW;
}
return ntStatus;
}
BOOLEAN
FlCheckFormatParameters(
IN PDISKETTE_EXTENSION DisketteExtension,
IN PFORMAT_PARAMETERS FormatParameters
)
/*++
Routine Description:
This routine checks the supplied format parameters to make sure that
they'll work on the drive to be formatted.
Arguments:
DisketteExtension - a pointer to our data area for the diskette to
be formatted.
FormatParameters - a pointer to the caller's parameters for the FORMAT.
Return Value:
TRUE if parameters are OK.
FALSE if the parameters are bad.
--*/
{
PDRIVE_MEDIA_CONSTANTS driveMediaConstants;
DRIVE_MEDIA_TYPE driveMediaType;
//
// Figure out which entry in the DriveMediaConstants table to use.
//
driveMediaType =
DriveMediaLimits[DisketteExtension->DriveType].HighestDriveMediaType;
while ( ( DriveMediaConstants[driveMediaType].MediaType !=
FormatParameters->MediaType ) &&
( driveMediaType > DriveMediaLimits[DisketteExtension->DriveType].
LowestDriveMediaType ) ) {
driveMediaType--;
}
if ( DriveMediaConstants[driveMediaType].MediaType !=
FormatParameters->MediaType ) {
return FALSE;
} else {
driveMediaConstants = &DriveMediaConstants[driveMediaType];
if ( ( FormatParameters->StartHeadNumber >
(ULONG)( driveMediaConstants->NumberOfHeads - 1 ) ) ||
( FormatParameters->EndHeadNumber >
(ULONG)( driveMediaConstants->NumberOfHeads - 1 ) ) ||
( FormatParameters->StartCylinderNumber >
driveMediaConstants->MaximumTrack ) ||
( FormatParameters->EndCylinderNumber >
driveMediaConstants->MaximumTrack ) ||
( FormatParameters->EndCylinderNumber <
FormatParameters->StartCylinderNumber ) ) {
return FALSE;
} else {
return TRUE;
}
}
}
PCONTROLLER
FlGetControllerBase(
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
PHYSICAL_ADDRESS IoAddress,
ULONG NumberOfBytes,
BOOLEAN InIoSpace,
PBOOLEAN MappedAddress
)
/*++
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.
MappedAddress - indicates whether the address was mapped.
This only has meaning if the address returned
is non-null.
Return Value:
Mapped address
--*/
{
PHYSICAL_ADDRESS cardAddress;
ULONG addressSpace = InIoSpace;
PCONTROLLER 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
);
*MappedAddress = !!Address;
} else {
Address = (PCONTROLLER)cardAddress.LowPart;
*MappedAddress = FALSE;
}
return Address;
}
VOID
FlLogErrorDpc(
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;
PCONTROLLER_DATA controllerData = DeferredContext;
errorLogEntry = IoAllocateErrorLogEntry(
controllerData->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);
}
}
#if defined(DBCS) && defined(_MIPS_)
NTSTATUS
FlOutputCommandFor3Mode(
IN UCHAR Command,
IN OUT PDISKETTE_EXTENSION DisketteExtension
)
/*++
Routine Description:
This routine is merely used to log an error that we had to reset the device.
Arguments:
Command - a byte specifying the command to be sent to the controller.
DisketteExtension - a pointer to our data area for the drive being
accessed (any drive if a controller command is being given).
Return Value:
STATUS_SUCCESS if the command was sent and bytes received properly;
appropriate error propogated otherwise.
--*/
{
KIRQL oldIrql;
PCONTROLLER_DATA controllerData;
UCHAR configurationRegister5;
UCHAR needDrive1600kbMode;
NTSTATUS ntStatus;
UCHAR save_FifoBuffer[10];
ULONG i;
ntStatus = STATUS_SUCCESS;
if (DisketteExtension->Drive3Mode == DRIVE_2MODE) {
return ntStatus;
}
if (((Command & ~(COMMND_MFM)) != COMMND_READ_ID) &&
((Command & ~(COMMND_MFM)) != COMMND_READ_DATA) &&
((Command & ~(COMMND_MFM)) != COMMND_WRITE_DATA) &&
((Command & ~(COMMND_MFM)) != COMMND_READ_DELETED_DATA) &&
((Command & ~(COMMND_MFM)) != COMMND_SEEK) &&
((Command & ~(COMMND_MFM)) != COMMND_RECALIBRATE) &&
((Command & ~(COMMND_MFM)) != COMMND_WRITE_DELETED_DATA)) {
return ntStatus;
}
controllerData = DisketteExtension->ControllerData;
if ((DisketteExtension->DriveMediaType == Drive144Media120) ||
(DisketteExtension->DriveMediaType == Drive144Media123) ||
((DisketteExtension->DriveMediaConstants).SectorsPerTrack == 0x1b) ||
((DisketteExtension->DriveMediaConstants).SectorsPerTrack == 0x1a)) {
needDrive1600kbMode = TRUE;
} else {
needDrive1600kbMode = FALSE;
}
KeAcquireSpinLock( &controllerData->TimerSpinLock, &oldIrql );
WRITE_CONTROLLER(
&controllerData->ControllerAddress->StatusA,
CONFIG5_ENTER_MODE);
WRITE_CONTROLLER(
&controllerData->ControllerAddress->StatusA,
CONFIG5_ENTER_MODE);
KeReleaseSpinLock( &controllerData->TimerSpinLock, oldIrql );
WRITE_CONTROLLER(
&controllerData->ControllerAddress->StatusA,
CONFIG5_SELECT_CR5);
configurationRegister5 = READ_CONTROLLER(
&controllerData->ControllerAddress->StatusB);
if (((configurationRegister5 & CONFIG5_1600KB_MODE_MASK) ==
CONFIG5_1600KB_MODE) && (needDrive1600kbMode == FALSE)) {
//
// the CR5 is set the 1.6MB mode, but the required access mode
// is the 2.0MB mode. So the CR5 is changed into 2.0MB mode.
//
WRITE_CONTROLLER(
&controllerData->ControllerAddress->StatusB,
(configurationRegister5 & ~(CONFIG5_1600KB_MODE)));
} else if (((configurationRegister5 & CONFIG5_1600KB_MODE_MASK) !=
CONFIG5_1600KB_MODE) && (needDrive1600kbMode == TRUE)) {
//
// the CR5 is set the 2.0MB mode, but the required access mode
// is the 1.6MB mode. So the CR5 is changed into 1.6MB mode.
//
WRITE_CONTROLLER(
&controllerData->ControllerAddress->StatusB,
(configurationRegister5 | CONFIG5_1600KB_MODE));
}
WRITE_CONTROLLER(
&controllerData->ControllerAddress->StatusA,
CONFIG5_EXIT_MODE);
if (((configurationRegister5 & CONFIG5_1600KB_MODE_MASK) !=
CONFIG5_1600KB_MODE) && (needDrive1600kbMode == TRUE) &&
(((DisketteExtension->DriveMediaConstants).SectorsPerTrack == 0x1b) ||
((DisketteExtension->DriveMediaConstants).SectorsPerTrack == 0x1a))) {
//
// If the controller has a CONFIGURE command, use it to enable implied
// seeks. If it doesn't, we'll find out here the first time through.
//
for ( i = 0; i < 10; i++ ) {
save_FifoBuffer[i] = controllerData->FifoBuffer[i];
}
if ( controllerData->ControllerConfigurable ) {
controllerData->FifoBuffer[0] = 0;
controllerData->FifoBuffer[1] = COMMND_CONFIGURE_FIFO_THRESHOLD +
COMMND_CONFIGURE_DISABLE_POLLING;
if (!DisketteExtension->DriveMediaConstants.CylinderShift) {
controllerData->FifoBuffer[1] += COMMND_CONFIGURE_IMPLIED_SEEKS;
}
controllerData->FifoBuffer[2] = 0;
ntStatus = FlIssueCommand( COMMND_CONFIGURE, DisketteExtension );
if ( ntStatus == STATUS_DEVICE_NOT_READY ) {
//
// Note the CONFIGURE command doesn't exist. Set status to
// success, so we can issue the SPECIFY command below.
//
FloppyDump(
FLOPINFO,
("Floppy: Ignore above error - no CONFIGURE command\n")
);
controllerData->ControllerConfigurable = FALSE;
ntStatus = STATUS_SUCCESS;
}
}
//
// Issue SPECIFY command to program the head load and unload
// rates, the drive step rate, and the DMA data transfer mode.
//
if ( NT_SUCCESS( ntStatus ) ) {
controllerData->FifoBuffer[0] =
DisketteExtension->DriveMediaConstants.StepRateHeadUnloadTime;
controllerData->FifoBuffer[1] =
DisketteExtension->DriveMediaConstants.HeadLoadTime;
ntStatus = FlIssueCommand( COMMND_SPECIFY, DisketteExtension );
if ( NT_SUCCESS( ntStatus ) ) {
//
// Program the data rate
//
WRITE_CONTROLLER(
&controllerData->ControllerAddress->DRDC.DataRate,
DisketteExtension->DriveMediaConstants.DataTransferRate );
}
}
for ( i = 0; i < 10; i++ ) {
controllerData->FifoBuffer[i] = save_FifoBuffer[i];
}
}
return ntStatus;
}
NTSTATUS
FlReadWrite_PTOS(
IN PDISKETTE_EXTENSION DisketteExtension,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the floppy thread to read write one tracks on
the diskette.
Arguments:
DisketteExtension - a pointer to our data area for the diskette to be
read write.
Irp - a pointer to the IO Request Packet.
Return Value:
STATUS_SUCCESS
--*/
{
LARGE_INTEGER headSettleTime;
PIO_STACK_LOCATION irpSp;
PCONTROLLER_DATA controllerData;
PDRIVE_MEDIA_CONSTANTS driveMediaConstants;
PUCHAR userBuffer;
NTSTATUS ntStatus;
NTSTATUS ntStatus2;
ULONG startingSectorOfTransfer;
ULONG totalBytesOfTransfer;
ULONG bytesTransferredSoFar = 0;
ULONG numberOfPagesInTransfer;
UCHAR transferCylinder;
UCHAR transferSector;
UCHAR transferHead;
UCHAR byteToSectorShift;
UCHAR retryCount;
UCHAR secondaryRetryCount;
UCHAR mediaDescriptor;
DRIVE_MEDIA_TYPE driveMediaType;
MEDIA_TYPE mediaType;
BOOLEAN writeOperation;
BOOLEAN fatalError;
BOOLEAN needToUseBuffer;
BOOLEAN fifoDisabled = FALSE;
PDISK_READ_WRITE_PARAMETER_PTOS readwriteParameters;
UCHAR command;
FloppyDump(
FLOPSHOW,
("Floppy: FlReadWrite_PTOS...3\n")
);
controllerData = DisketteExtension->ControllerData;
readwriteParameters = (PDISK_READ_WRITE_PARAMETER_PTOS) Irp->AssociatedIrp.SystemBuffer;
irpSp = IoGetCurrentIrpStackLocation( Irp );
switch( irpSp->Parameters.DeviceIoControl.IoControlCode ) {
case IOCTL_DISK_READ: {
writeOperation = FALSE;
if (readwriteParameters->Read_Write_Mode_PTOS == 0) {
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_ReadData...\n")
);
command = COMMND_READ_DATA;
} else {
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_ReadDeletedData...\n")
);
command = COMMND_READ_DELETED_DATA;
}
break;
}
case IOCTL_DISK_WRITE: {
writeOperation = TRUE;
if (readwriteParameters->Read_Write_Mode_PTOS == 0) {
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_WriteData...\n")
);
command = COMMND_WRITE_DATA;
} else {
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL_WriteDeletedData...\n")
);
command = COMMND_WRITE_DELETED_DATA;
}
break;
}
}
FloppyDump(
FLOPSHOW,
("Floppy: IOCTL Read Write - Mode : %d\n"
"------ Start Cyl: %x\n"
"------ End Cyl: %x\n"
"------ Start Hd: %d\n"
"------ End Hd: %d\n",
readwriteParameters->Read_Write_Mode_PTOS,
readwriteParameters->CylinderNumber_PTOS,
readwriteParameters->HeadNumber_PTOS,
readwriteParameters->StartSectorNumber_PTOS,
readwriteParameters->NumberOfSectors_PTOS)
);
if ((readwriteParameters->CylinderNumber_PTOS ==0) &&
(readwriteParameters->HeadNumber_PTOS ==0)) {
driveMediaConstants =
&DriveMediaConstantsPTOS[4];
} else {
driveMediaConstants =
&DriveMediaConstantsPTOS[3];
}
//
// For 3mode floppy disk drive
//
DisketteExtension->DriveMediaConstants = *driveMediaConstants;
//
// Initialize variables to be used in the loop that transfers
// portions of the request.
//
byteToSectorShift = (UCHAR)( SECTORLENGTHCODE_TO_BYTESHIFT +
driveMediaConstants->SectorLengthCode );
fatalError = FALSE;
userBuffer = (PUCHAR) MmGetSystemAddressForMdl( Irp->MdlAddress );
//
// Determine the initial sector to be read from the diskette.
//
transferCylinder = (UCHAR)readwriteParameters->CylinderNumber_PTOS;
transferSector = (UCHAR)readwriteParameters->StartSectorNumber_PTOS;
transferHead = (UCHAR)readwriteParameters->HeadNumber_PTOS;
totalBytesOfTransfer = (readwriteParameters->NumberOfSectors_PTOS << byteToSectorShift);
FloppyDump(
FLOPSTATUS,
("Floppy: SectorsPerTrack = %x\n"
"------ NumberOfHeads = %x\n"
"------ transferCylinder = %x\n"
"------ transferSector = %x\n"
"------ transferHead = %x\n",
driveMediaConstants->SectorsPerTrack,
driveMediaConstants->NumberOfHeads,
transferCylinder,
transferSector,
transferHead)
);
//
// Make sure the transfer doesn't go past the end of the track
//
needToUseBuffer = TRUE;
FloppyDump(
FLOPSHOW,
("Floppy: TransferLength = %lx\n", totalBytesOfTransfer)
);
//
// If this is a write and we have to use the contiguous buffer
// (because we don't have enough map registers) then copy the
// data from the user's buffer to the contiguous buffer.
//
#if 0
if ( ( needToUseBuffer ) &&
( writeOperation ) ) {
FloppyDump(
FLOPSHOW,
("Floppy: Writing from address %lx\n",
userBuffer + bytesTransferredSoFar)
);
RtlMoveMemory(
controllerData->IoBuffer,
(PVOID)( userBuffer + bytesTransferredSoFar ),
totalBytesOfTransfer );
}
#else
#endif
retryCount = 0;
secondaryRetryCount = 0;
//
// Try this transfer until it succeeds or is failed.
//
do {
//
// If the controller doesn't do an implicit seek, then do an
// explicit one.
//
// Also do an explicit seek if this drive isn't set up to
// seek properly on this media, as noted by CylinderShift.
// (This happens for 360kb diskettes in 1.2Mb drives).
//
controllerData->FifoBuffer[0] = DisketteExtension->DeviceUnit;
ntStatus = FlSendByte( COMMND_SENSE_DRIVE, controllerData );
if ( NT_SUCCESS( ntStatus ) ) {
ntStatus = FlSendByte(
controllerData->FifoBuffer[0],
controllerData );
if ( NT_SUCCESS( ntStatus ) ) {
ntStatus = FlGetByte(
&controllerData->FifoBuffer[0],
controllerData );
Result_Status3_PTOS[0].ST3_PTOS = controllerData->FifoBuffer[0];
}
}
if ( ( !controllerData->ControllerConfigurable ) ||
( driveMediaConstants->CylinderShift != 0 ) ) {
controllerData->FifoBuffer[0] = (UCHAR)
( ( transferHead << 2 ) | DisketteExtension->DeviceUnit );
controllerData->FifoBuffer[1] = (UCHAR)( transferCylinder <<
driveMediaConstants->CylinderShift );
FloppyDump(
FLOPSHOW,
("Floppy: Issuing SEEK to %x\n",
controllerData->FifoBuffer[1])
);
#ifdef KEEP_COUNTERS
FloppyUsedSeek++;
#endif
ntStatus = FlIssueCommand( COMMND_SEEK, DisketteExtension );
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPWARN,
("Floppy: SEEK returned %x\n", ntStatus)
);
}
if ( NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPSHOW,
("Floppy: SEEK says we got to cylinder %x\n",
controllerData->FifoBuffer[1])
);
if ( controllerData->FifoBuffer[1] != (UCHAR)
( transferCylinder <<
driveMediaConstants->CylinderShift ) ) {
FloppyDump(
FLOPWARN,
("Floppy: Seek returned bad registers\n")
);
controllerData->HardwareFailed = TRUE;
ntStatus = STATUS_FLOPPY_BAD_REGISTERS;
} else {
//
// Must delay HeadSettleTime milliseconds before
// doing anything after a SEEK.
//
headSettleTime.LowPart = - ( 10 * 1000 *
driveMediaConstants->HeadSettleTime );
headSettleTime.HighPart = -1;
KeDelayExecutionThread(
KernelMode,
FALSE,
&headSettleTime );
//
// SEEKs should always be followed by a READID.
//
controllerData->FifoBuffer[0] = (UCHAR)
( ( transferHead << 2 ) |
DisketteExtension->DeviceUnit );
}
}
} else {
#ifdef KEEP_COUNTERS
FloppyNoSeek++;
#endif
}
//
// Start the operation on the DMA and floppy hardware.
//
if ( NT_SUCCESS( ntStatus ) ) {
//
// Map the transfer through the DMA hardware. How we do
// it depends on whether we're using the user's buffer
// or our own contiguous buffer.
//
#if 0
KeFlushIoBuffers(
controllerData->IoBufferMdl,
!writeOperation,
TRUE );
//
// We can ignore the logical address returned by
// IoMapTransfer, since we're not using busmaster
// stuff.
//
IoMapTransfer(
controllerData->AdapterObject,
controllerData->IoBufferMdl,
controllerData->MapRegisterBase,
controllerData->IoBuffer,
&totalBytesOfTransfer,
(BOOLEAN)writeOperation);
#else
KeFlushIoBuffers( Irp->MdlAddress, !writeOperation, TRUE );
IoMapTransfer(
controllerData->AdapterObject,
Irp->MdlAddress,
controllerData->MapRegisterBase,
(PVOID)( (ULONG) MmGetMdlVirtualAddress(Irp->MdlAddress)
+ bytesTransferredSoFar ),
&totalBytesOfTransfer,
(BOOLEAN)writeOperation);
#endif
if ( controllerData->ControllerConfigurable &&
(command != COMMND_WRITE_DELETED_DATA)) {
//
// We enable the FIFO threshold brfore issuing
// the WRITE DELETE DATA command.
//
controllerData->FifoBuffer[0] = 0;
controllerData->FifoBuffer[1] =
COMMND_CONFIGURE_IMPLIED_SEEKS +
COMMND_CONFIGURE_FIFO_THRESHOLD +
COMMND_CONFIGURE_DISABLE_POLLING;
controllerData->FifoBuffer[0] = 0;
ntStatus2 = FlIssueCommand( COMMND_CONFIGURE, DisketteExtension );
}
//
// Issue the READ or WRITE command
//
controllerData->FifoBuffer[0] = (UCHAR)
( ( transferHead << 2 ) | DisketteExtension->DeviceUnit );
controllerData->FifoBuffer[1] = transferCylinder;
controllerData->FifoBuffer[2] = transferHead;
controllerData->FifoBuffer[3] = transferSector;
controllerData->FifoBuffer[4] =
driveMediaConstants->SectorLengthCode;
controllerData->FifoBuffer[5] = (UCHAR)( (transferSector - 1) +
( totalBytesOfTransfer >> byteToSectorShift ) );
controllerData->FifoBuffer[6] =
driveMediaConstants->ReadWriteGapLength;
controllerData->FifoBuffer[7] = driveMediaConstants->DataLength;
FloppyDump(
FLOPSHOW,
("Floppy: R/W params:\n"
"------ head+unit = %x\n"
"------ cylinder = %x\n"
"------ head = %x\n"
"------ sector = %x\n"
"------ seclen code = %x\n"
"------ numsecs = %x\n"
"------ rw gap len = %x\n"
"------ datalen = %x\n",
controllerData->FifoBuffer[0],
controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2],
controllerData->FifoBuffer[3],
controllerData->FifoBuffer[4],
controllerData->FifoBuffer[5],
controllerData->FifoBuffer[6],
controllerData->FifoBuffer[7])
);
if ( !writeOperation ) {
if (driveMediaConstants->SectorsPerTrack!=26){
/** 1s only FM mode **/
ntStatus = FlIssueCommand(
command + COMMND_MFM,
DisketteExtension );
} else {
ntStatus = FlIssueCommand(
command,
DisketteExtension );
}
FloppyDump(
FLOPDBGP,
("Floppy: READ DATA! : \n"
"------ userBuffer is : %lx\n",
userBuffer));
} else {
if (driveMediaConstants->SectorsPerTrack!=26){
/** 1s only FM mode **/
ntStatus = FlIssueCommand(
command + COMMND_MFM,
DisketteExtension );
} else {
ntStatus = FlIssueCommand(
command ,
DisketteExtension );
}
FloppyDump(
FLOPDBGP,
("Floppy: WRITE DATA! : \n"
"------ userBuffer is : %lx\n",
userBuffer));
}
if ( !NT_SUCCESS( ntStatus ) ) {
FloppyDump(
FLOPWARN,
("Floppy: read/write returned %x\n", ntStatus)
);
}
}
Result_Status_PTOS[0].ST0_PTOS
= controllerData->FifoBuffer[0];
Result_Status_PTOS[0].ST1_PTOS
= controllerData->FifoBuffer[1];
Result_Status_PTOS[0].ST2_PTOS
= controllerData->FifoBuffer[2];
Result_Status_PTOS[0].C_PTOS
= controllerData->FifoBuffer[3];
Result_Status_PTOS[0].H_PTOS
= controllerData->FifoBuffer[4];
Result_Status_PTOS[0].R_PTOS
= controllerData->FifoBuffer[5];
Result_Status_PTOS[0].N_PTOS
= controllerData->FifoBuffer[6];
if ( NT_SUCCESS( ntStatus ) ) {
if ( ( ( controllerData->FifoBuffer[0] & STREG0_END_MASK ) !=
STREG0_END_NORMAL ) &&
( ( ( controllerData->FifoBuffer[0] & STREG0_END_MASK ) !=
STREG0_END_ERROR ) ||
( controllerData->FifoBuffer[1] !=
STREG1_END_OF_DISKETTE ) ||
( controllerData->FifoBuffer[2] != STREG2_SUCCESS ) ) ) {
FloppyDump(
FLOPWARN,
("Floppy: Status registers wrong after I/O\n")
);
controllerData->HardwareFailed = TRUE;
ntStatus = FlInterpretError(
controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2] );
FloppyDump(
FLOPWARN,
("Floppy: Register values are\n"
"------ StatusRegister0 = %x\n"
"------ StatusRegister1 = %x\n"
"------ StatusRegister2 = %x\n"
"------ actualCylinder = %x\n"
"------ actualHead = %x\n"
"------ actualSector = %x\n"
"------ actualSectorLengthCode = %x\n",
controllerData->FifoBuffer[0],
controllerData->FifoBuffer[1],
controllerData->FifoBuffer[2],
controllerData->FifoBuffer[3],
controllerData->FifoBuffer[4],
controllerData->FifoBuffer[5],
controllerData->FifoBuffer[6])
);
}
}
if ( !NT_SUCCESS( ntStatus ) ) {
//
// Operation failed. Recalibrate drive & try again.
//
FloppyDump(
FLOPWARN,
("Floppy: operation failed. Recalibrating...\n")
);
ntStatus2 = FlRecalibrateDrive( DisketteExtension );
if ( NT_SUCCESS( ntStatus2 ) ) {
retryCount++;
} else {
//
// Ugh, we can't even recalibrate the drive. Force
// ourselves out of this nasty loop.
//
retryCount = RECALIBRATE_RETRY_COUNT;
fatalError = TRUE;
FloppyDump(
FLOPWARN,
("Floppy: Operation AND recalibrate failed\n")
);
}
}
if ( retryCount == RECALIBRATE_RETRY_COUNT ) {
//
// The retry count is exhausted. If the FIFO has
// overrun, reset the retry count and increment
// the secondary retry count - FIFO overruns mean that
// another device is hogging the DMA, so give the floppy
// another chance to make good.
//
if ( controllerData->FifoBuffer[1] & STREG1_DATA_OVERRUN ) {
if ( secondaryRetryCount < OVERRUN_RETRY_COUNT ) {
secondaryRetryCount++;
retryCount = 0;
} else {
//
// It looks like another device is STILL hogging
// the DMA. Before failing, try disabling the
// FIFO, if it exists.
//
if ( controllerData->ControllerConfigurable ) {
if ( fifoDisabled ||
(command != COMMND_WRITE_DELETED_DATA)) {
//
// We already disabled it, and still
// failed. Or The command is WRITE DELETED DATA.
// Exit with failure.
//
fatalError = TRUE;
} else {
fifoDisabled = FALSE;
ntStatus = STATUS_SUCCESS;
FloppyDump(
FLOPINFO,
("Floppy: disabling FIFO\n")
);
controllerData->FifoBuffer[0] = 0;
controllerData->FifoBuffer[1] =
COMMND_CONFIGURE_IMPLIED_SEEKS +
COMMND_CONFIGURE_DISABLE_FIFO +
COMMND_CONFIGURE_DISABLE_POLLING;
controllerData->FifoBuffer[2] = 0;
ntStatus = FlIssueCommand(
COMMND_CONFIGURE,
DisketteExtension );
if ( NT_SUCCESS( ntStatus ) ) {
fifoDisabled = TRUE;
}
}
} else {
//
// No FIFO to disable. Just exit with error.
//
fatalError = TRUE;
}
}
} else {
//
// Not an overrun error.
//
fatalError = TRUE;
}
}
//
// Flush the DMA adapter buffers.
//
#if 0
IoFlushAdapterBuffers(
controllerData->AdapterObject,
controllerData->IoBufferMdl,
controllerData->MapRegisterBase,
controllerData->IoBuffer,
totalBytesOfTransfer,
(BOOLEAN)writeOperation);
#else
IoFlushAdapterBuffers(
controllerData->AdapterObject,
Irp->MdlAddress,
controllerData->MapRegisterBase,
(PVOID)( (ULONG) MmGetMdlVirtualAddress( Irp->MdlAddress )
+ bytesTransferredSoFar ),
totalBytesOfTransfer,
(BOOLEAN)writeOperation);
#endif
} while ( ( !NT_SUCCESS( ntStatus ) ) &&
( retryCount < RECALIBRATE_RETRY_COUNT ) );
if ( NT_SUCCESS( ntStatus ) ) {
#if 0
if ( ( !writeOperation ) && ( needToUseBuffer ) ) {
//
// copy data from contiguous buffer
//
FloppyDump(
FLOPSHOW,
("Floppy: Reading to address %lx\n",
userBuffer + bytesTransferredSoFar)
);
RtlMoveMemory(
(PVOID)( userBuffer + bytesTransferredSoFar ),
controllerData->IoBuffer,
totalBytesOfTransfer );
}
#else
#endif
}
if ( fifoDisabled ) {
//
// We disabled the FIFO threshold. Restore it now.
//
controllerData->FifoBuffer[0] = 0;
controllerData->FifoBuffer[1] = COMMND_CONFIGURE_IMPLIED_SEEKS +
COMMND_CONFIGURE_FIFO_THRESHOLD + COMMND_CONFIGURE_DISABLE_POLLING;
controllerData->FifoBuffer[2] = 0;
ntStatus2 = FlIssueCommand( COMMND_CONFIGURE, DisketteExtension );
if ( !NT_SUCCESS( ntStatus2 ) ) {
FloppyDump(
FLOPDBGP,
("Floppy: restoring FIFO after r/w gave %x\n", ntStatus2)
);
}
}
//
// The bytesTransferred so far is correct even if there was an error.
//
Irp->IoStatus.Information = totalBytesOfTransfer;
FloppyDump(
FLOPIRPPATH,
("Floppy: done with read/write for irp %x extension %x\n",
Irp,DisketteExtension)
);
return ntStatus;
}
#endif // DBCS && _MIPS_