/*++ 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 has passed since selecting the drive. A drive cannot be accessed until 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 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 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 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 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 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 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 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 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 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 // 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(¶meters, L"\\Parameters"); RtlInitUnicodeString(&systemPath, L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\System"); RtlInitUnicodeString(&thinkpad, L"IBM THINKPAD 750"); RtlInitUnicodeString(&ps2e, L"IBM PS2E"); pathLength = RegistryPath->Length + parameters.Length + sizeof(WCHAR); if (pathLength < systemPath.Length + sizeof(WCHAR)) { pathLength = systemPath.Length + sizeof(WCHAR); } // // Since the registry path parameter is a "counted" UNICODE string, it // might not be zero terminated. For a very short time allocate memory // to hold the registry path zero terminated so that we can use it to // delve into the registry. // // NOTE NOTE!!!! This is not an architected way of breaking into // a driver. It happens to work for this driver because the author // likes to do things this way. // if (path = ExAllocatePool(PagedPool, pathLength)) { RtlZeroMemory(¶mTable[0], sizeof(paramTable)); RtlZeroMemory(path, pathLength); RtlMoveMemory(path, RegistryPath->Buffer, RegistryPath->Length); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"BreakOnEntry"; paramTable[0].EntryContext = &shouldBreak; paramTable[0].DefaultType = REG_DWORD; paramTable[0].DefaultData = &zero; paramTable[0].DefaultLength = sizeof(ULONG); paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[1].Name = L"DebugLevel"; paramTable[1].EntryContext = &debugLevel; paramTable[1].DefaultType = REG_DWORD; paramTable[1].DefaultData = &zero; paramTable[1].DefaultLength = sizeof(ULONG); if (!NT_SUCCESS(RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, path, ¶mTable[0], NULL, NULL))) { shouldBreak = 0; debugLevel = 0; } // // Determine whether or not this type of system has a // model 30 floppy controller. // RtlZeroMemory(paramTable, sizeof(paramTable)); RtlZeroMemory(path, pathLength); RtlMoveMemory(path, systemPath.Buffer, systemPath.Length); RtlZeroMemory(&identifier, sizeof(UNICODE_STRING)); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; paramTable[0].Name = L"Identifier"; paramTable[0].EntryContext = &identifier; if (NT_SUCCESS(RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, path, paramTable, NULL, NULL))) { if (identifier.Length == thinkpad.Length && RtlCompareMemory(identifier.Buffer, thinkpad.Buffer, thinkpad.Length) == thinkpad.Length) { model30 = 1; } else if (identifier.Length == ps2e.Length && RtlCompareMemory(identifier.Buffer, ps2e.Buffer, ps2e.Length) == ps2e.Length) { model30 = 1; } else { model30 = 0; } } else { model30 = 0; } // // This part gets from the parameters part of the registry // to see if the controller configuration needs to be disabled. // Doing this lets SMC 661, and 662 work. On hardware that // works normally, this change will show a slowdown of up // to 40%. So defining this variable is not recommended // unless things don't work without it. // // // Also check the model30 value in the parameters section // that is used to override the decision above. // RtlZeroMemory(¶mTable[0], sizeof(paramTable)); RtlZeroMemory(path, RegistryPath->Length+parameters.Length+sizeof(WCHAR)); RtlMoveMemory(path, RegistryPath->Buffer, RegistryPath->Length); RtlMoveMemory((PCHAR) path + RegistryPath->Length, parameters.Buffer, parameters.Length); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"NotConfigurable"; paramTable[0].EntryContext = ¬Configurable; paramTable[0].DefaultType = REG_DWORD; paramTable[0].DefaultData = &zero; paramTable[0].DefaultLength = sizeof(ULONG); paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[1].Name = L"Model30"; paramTable[1].EntryContext = &model30; paramTable[1].DefaultType = REG_DWORD; paramTable[1].DefaultData = model30 ? &one : &zero; paramTable[1].DefaultLength = sizeof(ULONG); if (!NT_SUCCESS(RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, path, ¶mTable[0], NULL, NULL))) { notConfigurable = 0; } } // // 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)<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<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)<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)<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)<MdlAddress; ioBuffer = (PCHAR) MmGetMdlVirtualAddress(Irp->MdlAddress) + ((currentSector - firstSector)<= 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)< 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)<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)<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_