2 * ReactOS Floppy Driver
3 * Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 * PROJECT: ReactOS Floppy Driver
21 * PURPOSE: Main floppy driver routines
22 * PROGRAMMER: Vizzini (vizzini@plasmic.com)
24 * 15-Feb-2004 vizzini - Created
27 * ---- General to-do items ----
28 * TODO: Clean up properly on failed init
29 * TODO: Add arc-path support so we can boot from the floppy
30 * TODO: Fix all these stupid STATUS_UNSUCCESSFUL return values
31 * TODO: Think about IO_NO_INCREMENT
32 * TODO: Figure out why CreateClose isn't called any more on XP. Seems to correspond
33 * with the driver not being unloadable. Does it have to do with cleanup?
34 * TODO: Consider using the built-in device object pointer in the stack location
35 * rather than the context area
36 * TODO: Think about StopDpcQueued -- could be a race; too tired atm to tell
38 * ---- Support for proper media detection ----
39 * TODO: Handle MFM flag
40 * TODO: Un-hardcode the data rate from various places
41 * TODO: Proper media detection (right now we're hardcoded to 1.44)
42 * TODO: Media detection based on sector 1
44 * ---- Support for normal floppy hardware ----
45 * TODO: Support the three primary types of controller
46 * TODO: Figure out thinkpad compatibility (I've heard rumors of weirdness with them)
48 * ---- Support for non-ISA and/or non-slave-dma controllers, if they exist ----
49 * TODO: Find controllers on non-ISA buses
50 * TODO: Think about making the interrupt shareable
51 * TODO: Support bus-master controllers. PCI will break ATM.
60 #include "readwrite.h"
63 * Global controller info structures. Each controller gets one. Since the system
64 * will probably have only one, with four being a very unlikely maximum, a static
65 * global array is easiest to deal with.
67 CONTROLLER_INFO gControllerInfo
[MAX_CONTROLLERS
];
68 ULONG gNumberOfControllers
= 0;
70 /* Queue thread management */
71 KEVENT QueueThreadTerminate
;
78 static VOID NTAPI
MotorStopDpcFunc(PKDPC Dpc
,
79 PVOID DeferredContext
,
80 PVOID SystemArgument1
,
81 PVOID SystemArgument2
)
83 * FUNCTION: Stop the floppy motor
85 * Dpc: DPC object that's going off
86 * DeferredContext: called with DRIVE_INFO for drive to turn off
87 * SystemArgument1: unused
88 * SystemArgument2: unused
90 * - Must set an event to let other threads know we're done turning off the motor
91 * - Called back at DISPATCH_LEVEL
94 PCONTROLLER_INFO ControllerInfo
= (PCONTROLLER_INFO
)DeferredContext
;
96 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
97 ASSERT(ControllerInfo
);
99 KdPrint(("floppy: MotorStopDpcFunc called\n"));
101 HwTurnOffMotor(ControllerInfo
);
102 ControllerInfo
->StopDpcQueued
= FALSE
;
103 KeSetEvent(&ControllerInfo
->MotorStoppedEvent
, IO_NO_INCREMENT
, FALSE
);
107 VOID NTAPI
StartMotor(PDRIVE_INFO DriveInfo
)
109 * FUNCTION: Start the motor, taking into account proper handling of the timer race
111 * DriveInfo: drive to start
113 * - Never call HwTurnOnMotor() directly
114 * - This protocol manages a race between the cancel timer and the requesting thread.
115 * You wouldn't want to turn on the motor and then cancel the timer, because the
116 * cancel dpc might fire in the meantime, and that'd un-do what you just did. If you
117 * cancel the timer first, but KeCancelTimer returns false, the dpc is already running,
118 * so you have to wait until the dpc is completly done running, or else you'll race
119 * with the turner-offer
120 * - PAGED_CODE because we wait
126 KdPrint(("floppy: StartMotor called\n"));
128 if(DriveInfo
->ControllerInfo
->StopDpcQueued
&&
129 !KeCancelTimer(&DriveInfo
->ControllerInfo
->MotorTimer
))
131 /* Motor turner-offer is already running; wait for it to finish */
132 KeWaitForSingleObject(&DriveInfo
->ControllerInfo
->MotorStoppedEvent
, Executive
, KernelMode
, FALSE
, NULL
);
133 DriveInfo
->ControllerInfo
->StopDpcQueued
= FALSE
;
136 HwTurnOnMotor(DriveInfo
);
140 VOID NTAPI
StopMotor(PCONTROLLER_INFO ControllerInfo
)
142 * FUNCTION: Stop all motors on the controller
144 * DriveInfo: Drive to stop
146 * - Never call HwTurnOffMotor() directly
147 * - This manages the timer cancelation race (see StartMotor for details).
148 * All we have to do is set up a timer.
151 LARGE_INTEGER StopTime
;
153 ASSERT(ControllerInfo
);
155 KdPrint(("floppy: StopMotor called\n"));
157 /* one relative second, in 100-ns units */
158 StopTime
.QuadPart
= 10000000;
159 StopTime
.QuadPart
*= -1;
161 KeClearEvent(&ControllerInfo
->MotorStoppedEvent
);
162 KeSetTimer(&ControllerInfo
->MotorTimer
, StopTime
, &ControllerInfo
->MotorStopDpc
);
163 ControllerInfo
->StopDpcQueued
= TRUE
;
167 VOID NTAPI
WaitForControllerInterrupt(PCONTROLLER_INFO ControllerInfo
)
169 * FUNCTION: Wait for the controller to interrupt, and then clear the event
171 * ControllerInfo: Controller to wait for
173 * - There is a small chance that an unexpected or spurious interrupt could
174 * be lost with this clear/wait/clear scheme used in this driver. This is
175 * deemed to be an acceptable risk due to the unlikeliness of the scenario,
176 * and the fact that it'll probably work fine next time.
177 * - PAGED_CODE because it waits
181 ASSERT(ControllerInfo
);
183 KeWaitForSingleObject(&ControllerInfo
->SynchEvent
, Executive
, KernelMode
, FALSE
, NULL
);
184 KeClearEvent(&ControllerInfo
->SynchEvent
);
188 static NTSTATUS NTAPI
CreateClose(PDEVICE_OBJECT DeviceObject
,
191 * FUNCTION: Dispatch function called for Create and Close IRPs
193 * DeviceObject: DeviceObject that is the target of the IRP
194 * Irp: IRP to process
196 * STATUS_SUCCESS in all cases
198 * - The Microsoft sample drivers tend to return FILE_OPENED in Information, so I do too.
199 * - No reason to fail the device open
200 * - No state to track, so this routine is easy
201 * - Can be called <= DISPATCH_LEVEL
203 * TODO: Figure out why this isn't getting called any more, and remove the ASSERT once that happens
206 KdPrint(("floppy: CreateClose called\n"));
210 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
211 Irp
->IoStatus
.Information
= FILE_OPENED
;
213 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
215 return STATUS_SUCCESS
;
219 static NTSTATUS NTAPI
Recalibrate(PDRIVE_INFO DriveInfo
)
221 * FUNCTION: Start the recalibration process
223 * DriveInfo: Pointer to the driveinfo struct associated with the targeted drive
225 * STATUS_SUCCESS on successful starting of the process
226 * STATUS_UNSUCCESSFUL if it fails
228 * - Sometimes you have to do two recalibrations, particularly if the disk has <80 tracks.
229 * - PAGED_CODE because we wait
238 /* first turn on the motor */
239 /* Must stop after every start, prior to return */
240 StartMotor(DriveInfo
);
242 /* set the data rate */
243 KdPrint(("floppy: FIXME: UN-HARDCODE DATA RATE\n"));
244 HwSetDataRate(DriveInfo
->ControllerInfo
, 0);
246 /* clear the event just in case the last call forgot */
247 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
249 /* sometimes you have to do this twice */
250 for(i
= 0; i
< 2; i
++)
252 /* Send the command */
253 Status
= HwRecalibrate(DriveInfo
);
254 if(Status
!= STATUS_SUCCESS
)
256 KdPrint(("floppy: Recalibrate: HwRecalibrate returned error\n"));
260 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
262 /* Get the results */
263 Status
= HwRecalibrateResult(DriveInfo
->ControllerInfo
);
264 if(Status
!= STATUS_SUCCESS
)
266 KdPrint(("floppy: Recalibrate: HwRecalibrateResult returned error\n"));
271 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
273 /* Must stop after every start, prior to return */
274 StopMotor(DriveInfo
->ControllerInfo
);
280 NTSTATUS NTAPI
ResetChangeFlag(PDRIVE_INFO DriveInfo
)
282 * FUNCTION: Reset the drive's change flag (as reflected in the DIR)
284 * DriveInfo: the drive to reset
286 * STATUS_SUCCESS if the changeline is cleared
287 * STATUS_NO_MEDIA_IN_DEVICE if the changeline cannot be cleared
288 * STATUS_IO_DEVICE_ERROR if the controller cannot be communicated with
290 * - Change reset procedure: recalibrate, seek 1, seek 0
291 * - If the line is still set after that, there's clearly no disk in the
292 * drive, so we return STATUS_NO_MEDIA_IN_DEVICE
293 * - PAGED_CODE because we wait
301 KdPrint(("floppy: ResetChangeFlag called\n"));
303 /* Try to recalibrate. We don't care if it works. */
304 Recalibrate(DriveInfo
);
306 /* clear spurious interrupts in prep for seeks */
307 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
310 if(HwSeek(DriveInfo
, 1) != STATUS_SUCCESS
)
312 KdPrint(("floppy: ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n"));
313 return STATUS_IO_DEVICE_ERROR
;
316 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
318 HwSenseInterruptStatus(DriveInfo
->ControllerInfo
);
321 if(HwSeek(DriveInfo
, 1) != STATUS_SUCCESS
)
323 KdPrint(("floppy: ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n"));
324 return STATUS_IO_DEVICE_ERROR
;
327 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
329 HwSenseInterruptStatus(DriveInfo
->ControllerInfo
);
331 /* Check the change bit */
332 if(HwDiskChanged(DriveInfo
, &DiskChanged
) != STATUS_SUCCESS
)
334 KdPrint(("floppy: ResetChangeFlag(): HwDiskChagned failed; returning STATUS_IO_DEVICE_ERROR\n"));
335 return STATUS_IO_DEVICE_ERROR
;
338 /* if the change flag is still set, there's probably no media in the drive. */
340 return STATUS_NO_MEDIA_IN_DEVICE
;
342 /* else we're done! */
343 return STATUS_SUCCESS
;
347 static VOID NTAPI
Unload(PDRIVER_OBJECT DriverObject
)
349 * FUNCTION: Unload the driver from memory
351 * DriverObject - The driver that is being unloaded
353 * TODO: Delete ARC links
360 KdPrint(("floppy: unloading\n"));
362 KeSetEvent(&QueueThreadTerminate
, 0, FALSE
);
363 KeWaitForSingleObject(ThreadObject
, Executive
, KernelMode
, FALSE
, 0);
364 ObDereferenceObject(ThreadObject
);
366 for(i
= 0; i
< gNumberOfControllers
; i
++)
368 if(!gControllerInfo
[i
].Populated
)
371 for(j
= 0; j
< gControllerInfo
[i
].NumberOfDrives
; j
++)
373 if(gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
)
376 RtlInitUnicodeString(&Link
, gControllerInfo
[i
].DriveInfo
[j
].SymLinkBuffer
);
377 IoDeleteSymbolicLink(&Link
);
378 IoDeleteDevice(gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
);
382 IoDisconnectInterrupt(gControllerInfo
[i
].InterruptObject
);
384 /* Power down the controller */
385 HwPowerOff(&gControllerInfo
[i
]);
390 static NTSTATUS NTAPI
ConfigCallback(PVOID Context
,
391 PUNICODE_STRING PathName
,
392 INTERFACE_TYPE BusType
,
394 PKEY_VALUE_FULL_INFORMATION
*BusInformation
,
395 CONFIGURATION_TYPE ControllerType
,
396 ULONG ControllerNumber
,
397 PKEY_VALUE_FULL_INFORMATION
*ControllerInformation
,
398 CONFIGURATION_TYPE PeripheralType
,
399 ULONG PeripheralNumber
,
400 PKEY_VALUE_FULL_INFORMATION
*PeripheralInformation
)
402 * FUNCTION: Callback to IoQueryDeviceDescription, which tells us about our controllers
406 * BusType: Type of the bus that our controller is on
407 * BusNumber: Number of the bus that our controller is on
408 * BusInformation: Unused
409 * ControllerType: Unused
410 * ControllerNumber: Number of the controller that we're adding
411 * ControllerInformation: Full configuration information for our controller
412 * PeripheralType: Unused
413 * PeripheralNumber: Unused
414 * PeripheralInformation: Full configuration information for each drive on our controller
416 * STATUS_SUCCESS in all cases
418 * - The only documentation I've found about the contents of these structures is
419 * from the various Microsoft floppy samples and from the DDK headers. They're
420 * very vague, though, so I'm only mostly sure that this stuff is correct, as
421 * the MS samples do things completely differently than I have done them. Seems
422 * to work in my VMWare, though.
423 * - Basically, the function gets all of the information (port, dma, irq) about the
424 * controller, and then loops through all of the drives presented in PeripheralInformation.
425 * - Each controller has a CONTROLLER_INFO created for it, and each drive has a DRIVE_INFO.
426 * - Device objects are created for each drive (not controller), as that's the targeted
427 * device in the eyes of the rest of the OS. Each DRIVE_INFO points to a single CONTROLLER_INFO.
428 * - We only support up to four controllers in the whole system, each of which supports up to four
432 PKEY_VALUE_FULL_INFORMATION ControllerFullDescriptor
= ControllerInformation
[IoQueryDeviceConfigurationData
];
433 PCM_FULL_RESOURCE_DESCRIPTOR ControllerResourceDescriptor
= (PCM_FULL_RESOURCE_DESCRIPTOR
)((PCHAR
)ControllerFullDescriptor
+
434 ControllerFullDescriptor
->DataOffset
);
436 PKEY_VALUE_FULL_INFORMATION PeripheralFullDescriptor
= PeripheralInformation
[IoQueryDeviceConfigurationData
];
437 PCM_FULL_RESOURCE_DESCRIPTOR PeripheralResourceDescriptor
= (PCM_FULL_RESOURCE_DESCRIPTOR
)((PCHAR
)PeripheralFullDescriptor
+
438 PeripheralFullDescriptor
->DataOffset
);
440 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor
;
441 PCM_FLOPPY_DEVICE_DATA FloppyDeviceData
;
446 KdPrint(("floppy: ConfigCallback called with ControllerNumber %d\n", ControllerNumber
));
448 gControllerInfo
[gNumberOfControllers
].ControllerNumber
= ControllerNumber
;
449 gControllerInfo
[gNumberOfControllers
].InterfaceType
= BusType
;
450 gControllerInfo
[gNumberOfControllers
].BusNumber
= BusNumber
;
452 /* Get controller interrupt level/vector, dma channel, and port base */
453 for(i
= 0; i
< ControllerResourceDescriptor
->PartialResourceList
.Count
; i
++)
455 KeInitializeEvent(&gControllerInfo
[gNumberOfControllers
].SynchEvent
, NotificationEvent
, FALSE
);
457 PartialDescriptor
= &ControllerResourceDescriptor
->PartialResourceList
.PartialDescriptors
[i
];
459 if(PartialDescriptor
->Type
== CmResourceTypeInterrupt
)
461 gControllerInfo
[gNumberOfControllers
].Level
= PartialDescriptor
->u
.Interrupt
.Level
;
462 gControllerInfo
[gNumberOfControllers
].Vector
= PartialDescriptor
->u
.Interrupt
.Vector
;
464 if(PartialDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
)
465 gControllerInfo
[gNumberOfControllers
].InterruptMode
= Latched
;
467 gControllerInfo
[gNumberOfControllers
].InterruptMode
= LevelSensitive
;
470 else if(PartialDescriptor
->Type
== CmResourceTypePort
)
472 PHYSICAL_ADDRESS TranslatedAddress
;
473 ULONG AddressSpace
= 0x1; /* I/O Port Range */
475 if(!HalTranslateBusAddress(BusType
, BusNumber
, PartialDescriptor
->u
.Port
.Start
, &AddressSpace
, &TranslatedAddress
))
478 if(AddressSpace
== 0)
479 gControllerInfo
[gNumberOfControllers
].BaseAddress
= MmMapIoSpace(TranslatedAddress
, 8, FALSE
); // symbolic constant?
481 gControllerInfo
[gNumberOfControllers
].BaseAddress
= (PUCHAR
)TranslatedAddress
.u
.LowPart
;
484 else if(PartialDescriptor
->Type
== CmResourceTypeDma
)
485 gControllerInfo
[gNumberOfControllers
].Dma
= PartialDescriptor
->u
.Dma
.Channel
;
488 /* Start with 0 drives, then go looking */
489 gControllerInfo
[gNumberOfControllers
].NumberOfDrives
= 0;
491 /* learn about drives attached to controller */
492 for(i
= 0; i
< PeripheralResourceDescriptor
->PartialResourceList
.Count
; i
++)
494 PDRIVE_INFO DriveInfo
= &gControllerInfo
[gNumberOfControllers
].DriveInfo
[i
];
496 PartialDescriptor
= &PeripheralResourceDescriptor
->PartialResourceList
.PartialDescriptors
[i
];
498 if(PartialDescriptor
->Type
!= CmResourceTypeDeviceSpecific
)
501 FloppyDeviceData
= (PCM_FLOPPY_DEVICE_DATA
)(PartialDescriptor
+ 1);
503 DriveInfo
->ControllerInfo
= &gControllerInfo
[gNumberOfControllers
];
504 DriveInfo
->UnitNumber
= i
;
506 DriveInfo
->FloppyDeviceData
.MaxDensity
= FloppyDeviceData
->MaxDensity
;
507 DriveInfo
->FloppyDeviceData
.MountDensity
= FloppyDeviceData
->MountDensity
;
508 DriveInfo
->FloppyDeviceData
.StepRateHeadUnloadTime
= FloppyDeviceData
->StepRateHeadUnloadTime
;
509 DriveInfo
->FloppyDeviceData
.HeadLoadTime
= FloppyDeviceData
->HeadLoadTime
;
510 DriveInfo
->FloppyDeviceData
.MotorOffTime
= FloppyDeviceData
->MotorOffTime
;
511 DriveInfo
->FloppyDeviceData
.SectorLengthCode
= FloppyDeviceData
->SectorLengthCode
;
512 DriveInfo
->FloppyDeviceData
.SectorPerTrack
= FloppyDeviceData
->SectorPerTrack
;
513 DriveInfo
->FloppyDeviceData
.ReadWriteGapLength
= FloppyDeviceData
->ReadWriteGapLength
;
514 DriveInfo
->FloppyDeviceData
.FormatGapLength
= FloppyDeviceData
->FormatGapLength
;
515 DriveInfo
->FloppyDeviceData
.FormatFillCharacter
= FloppyDeviceData
->FormatFillCharacter
;
516 DriveInfo
->FloppyDeviceData
.HeadSettleTime
= FloppyDeviceData
->HeadSettleTime
;
517 DriveInfo
->FloppyDeviceData
.MotorSettleTime
= FloppyDeviceData
->MotorSettleTime
;
518 DriveInfo
->FloppyDeviceData
.MaximumTrackValue
= FloppyDeviceData
->MaximumTrackValue
;
519 DriveInfo
->FloppyDeviceData
.DataTransferLength
= FloppyDeviceData
->DataTransferLength
;
521 /* Once it's all set up, acknowledge its existance in the controller info object */
522 gControllerInfo
[gNumberOfControllers
].NumberOfDrives
++;
525 gControllerInfo
[gNumberOfControllers
].Populated
= TRUE
;
526 gNumberOfControllers
++;
528 return STATUS_SUCCESS
;
532 static BOOLEAN NTAPI
Isr(PKINTERRUPT Interrupt
,
533 PVOID ServiceContext
)
535 * FUNCTION: Interrupt service routine for the controllers
537 * Interrupt: Interrupt object representing the interrupt that occured
538 * ServiceContext: Pointer to the ControllerInfo object that caused the interrupt
540 * TRUE in all cases (see notes)
542 * - We should always be the target of the interrupt, being an edge-triggered ISA interrupt, but
543 * this won't be the case with a level-sensitive system like PCI
544 * - Note that it probably doesn't matter if the interrupt isn't dismissed, as it's edge-triggered.
545 * It probably won't keep re-interrupting.
546 * - There are two different ways to dismiss a floppy interrupt. If the command has a result phase
547 * (see intel datasheet), you dismiss the interrupt by reading the first data byte. If it does
548 * not, you dismiss the interrupt by doing a Sense Interrupt command. Again, because it's edge-
549 * triggered, this is safe to not do here, as we can just wait for the DPC.
550 * - Either way, we don't want to do this here. The controller shouldn't interrupt again, so we'll
551 * schedule a DPC to take care of it.
553 * - This driver really cannot shrare interrupts, as I don't know how to conclusively say
554 * whether it was our controller that interrupted or not. I just have to assume that any time
555 * my ISR gets called, it was my board that called it. Dumb design, yes, but it goes back to
556 * the semantics of ISA buses. That, and I don't know much about ISA drivers. :-)
557 * UPDATE: The high bit of Status Register A seems to work on non-AT controllers.
561 PCONTROLLER_INFO ControllerInfo
= (PCONTROLLER_INFO
)ServiceContext
;
563 ASSERT(ControllerInfo
);
565 KdPrint(("floppy: ISR called\n"));
568 * Due to the stupidity of the drive/controller relationship on the floppy drive, only one device object
569 * can have an active interrupt pending. Due to the nature of these IRPs, though, there will only ever
570 * be one thread expecting an interrupt at a time, and furthermore, Interrupts (outside of spurious ones)
571 * won't ever happen unless a thread is expecting them. Therefore, all we have to do is signal an event
572 * and we're done. Queue a DPC and leave.
574 KeInsertQueueDpc(&ControllerInfo
->Dpc
, NULL
, NULL
);
580 VOID NTAPI
DpcForIsr(PKDPC Dpc
,
582 PVOID SystemArgument1
,
583 PVOID SystemArgument2
)
585 * FUNCTION: This DPC gets queued by every ISR. Does the real per-interrupt work.
587 * Dpc: Pointer to the DPC object that represents our function
588 * DeviceObject: Device that this DPC is running for
590 * Context: Pointer to our ControllerInfo struct
592 * - This function just kicks off whatever the SynchEvent is and returns. We depend on
593 * the thing that caused the drive to interrupt to handle the work of clearing the interrupt.
594 * This enables us to get back to PASSIVE_LEVEL and not hog system time on a really stupid,
595 * slow, screwed-up piece of hardare.
596 * - If nothing is waiting for us to set the event, the interrupt is effectively lost and will
597 * never be dismissed. I wonder if this will become a problem.
598 * - Called at DISPATCH_LEVEL
601 PCONTROLLER_INFO ControllerInfo
= (PCONTROLLER_INFO
)Context
;
603 ASSERT(ControllerInfo
);
605 KdPrint(("floppy: DpcForIsr called\n"));
607 KeSetEvent(&ControllerInfo
->SynchEvent
, IO_NO_INCREMENT
, FALSE
);
611 static NTSTATUS NTAPI
InitController(PCONTROLLER_INFO ControllerInfo
)
613 * FUNCTION: Initialize a newly-found controller
615 * ControllerInfo: pointer to the controller to be initialized
617 * STATUS_SUCCESS if the controller is successfully initialized
618 * STATUS_UNSUCCESSFUL otherwise
623 UCHAR HeadUnloadTime
;
627 ASSERT(ControllerInfo
);
629 KdPrint(("floppy: InitController called with Controller 0x%x\n", ControllerInfo
));
631 KeClearEvent(&ControllerInfo
->SynchEvent
);
633 //HwDumpRegisters(ControllerInfo);
635 KdPrint(("floppy: InitController: resetting the controller\n"));
637 /* Reset the controller */
638 if(HwReset(ControllerInfo
) != STATUS_SUCCESS
)
640 KdPrint(("floppy: InitController: unable to reset controller\n"));
641 return STATUS_UNSUCCESSFUL
;
644 //HwDumpRegisters(ControllerInfo);
646 KdPrint(("floppy: InitController: setting data rate\n"));
649 if(HwSetDataRate(ControllerInfo
, DRSR_DSEL_500KBPS
) != STATUS_SUCCESS
)
651 KdPrint(("floppy: InitController: unable to set data rate\n"));
652 return STATUS_UNSUCCESSFUL
;
655 KdPrint(("floppy: InitController: waiting for initial interrupt\n"));
657 /* Wait for an interrupt */
658 WaitForControllerInterrupt(ControllerInfo
);
660 /* Reset means you have to clear each of the four interrupts (one per drive) */
661 for(i
= 0; i
< MAX_DRIVES_PER_CONTROLLER
; i
++)
663 KdPrint(("floppy: InitController: Sensing interrupt %d\n", i
));
664 //HwDumpRegisters(ControllerInfo);
666 if(HwSenseInterruptStatus(ControllerInfo
) != STATUS_SUCCESS
)
668 KdPrint(("floppy: InitController: Unable to clear interrupt 0x%x\n", i
));
669 return STATUS_UNSUCCESSFUL
;
673 KdPrint(("floppy: InitController: done sensing interrupts\n"));
675 //HwDumpRegisters(ControllerInfo);
677 /* Next, see if we have the right version to do implied seek */
678 if(HwGetVersion(ControllerInfo
) != VERSION_ENHANCED
)
680 KdPrint(("floppy: InitController: enhanced version not supported; disabling implied seeks\n"));
681 ControllerInfo
->ImpliedSeeks
= FALSE
;
682 ControllerInfo
->Model30
= FALSE
;
686 /* If so, set that up -- all defaults below except first TRUE for EIS */
687 if(HwConfigure(ControllerInfo
, TRUE
, TRUE
, FALSE
, 0, 0) != STATUS_SUCCESS
)
689 KdPrint(("floppy: InitController: unable to set up implied seek\n"));
690 ControllerInfo
->ImpliedSeeks
= FALSE
;
694 KdPrint(("floppy: InitController: implied seeks set!\n"));
695 ControllerInfo
->ImpliedSeeks
= TRUE
;
698 ControllerInfo
->Model30
= TRUE
;
702 KdPrint(("FLOPPY: FIXME: Figure out speed\n"));
703 HeadLoadTime
= SPECIFY_HLT_500K
;
704 HeadUnloadTime
= SPECIFY_HUT_500K
;
705 StepRateTime
= SPECIFY_SRT_500K
;
707 KdPrint(("floppy: InitController: issuing specify command to controller\n"));
709 /* Don't disable DMA --> enable dma (dumb & confusing) */
710 if(HwSpecify(ControllerInfo
, HeadLoadTime
, HeadUnloadTime
, StepRateTime
, FALSE
) != STATUS_SUCCESS
)
712 KdPrint(("floppy: InitController: unable to specify options\n"));
713 return STATUS_UNSUCCESSFUL
;
716 //HwDumpRegisters(ControllerInfo);
718 /* Init the stop stuff */
719 KeInitializeDpc(&ControllerInfo
->MotorStopDpc
, MotorStopDpcFunc
, ControllerInfo
);
720 KeInitializeTimer(&ControllerInfo
->MotorTimer
);
721 KeInitializeEvent(&ControllerInfo
->MotorStoppedEvent
, SynchronizationEvent
, FALSE
);
722 ControllerInfo
->StopDpcQueued
= FALSE
;
724 /* Recalibrate each drive on the controller (depends on StartMotor, which depends on the timer stuff above) */
725 /* TODO: Handle failure of one or more drives */
726 for(i
= 0; i
< ControllerInfo
->NumberOfDrives
; i
++)
728 KdPrint(("floppy: InitController: recalibrating drive 0x%x on controller 0x%x\n", i
, ControllerInfo
));
730 if(Recalibrate(&ControllerInfo
->DriveInfo
[i
]) != STATUS_SUCCESS
)
732 KdPrint(("floppy: InitController: unable to recalibrate drive\n"));
733 return STATUS_UNSUCCESSFUL
;
737 //HwDumpRegisters(ControllerInfo);
739 KdPrint(("floppy: InitController: done initializing; returning STATUS_SUCCESS\n"));
741 return STATUS_SUCCESS
;
745 static BOOLEAN NTAPI
AddControllers(PDRIVER_OBJECT DriverObject
)
747 * FUNCTION: Called on initialization to find our controllers and build device and controller objects for them
749 * DriverObject: Our driver's DriverObject (so we can create devices against it)
751 * FALSE if we can't allocate a device, adapter, or interrupt object, or if we fail to find any controllers
752 * TRUE otherwise (i.e. we have at least one fully-configured controller)
754 * - Currently we only support ISA buses.
755 * - BUG: Windows 2000 seems to clobber the response from the IoQueryDeviceDescription callback, so now we
756 * just test a boolean value in the first object to see if it was completely populated. The same value
757 * is tested for each controller before we build device objects for it.
759 * - Figure out a workable interrupt-sharing scheme and un-hardcode FALSE in IoConnectInterrupt
760 * - Add support for non-ISA buses, by looping through all of the bus types looking for floppy controllers
761 * - Report resource usage to the HAL
762 * - Add ARC path support
763 * - Think more about error handling; atm most errors abort the start of the driver
766 INTERFACE_TYPE InterfaceType
= Isa
;
767 CONFIGURATION_TYPE ControllerType
= DiskController
;
768 CONFIGURATION_TYPE PeripheralType
= FloppyDiskPeripheral
;
770 DEVICE_DESCRIPTION DeviceDescription
;
776 /* Find our controllers on all ISA buses */
777 IoQueryDeviceDescription(&InterfaceType
, 0, &ControllerType
, 0, &PeripheralType
, 0, ConfigCallback
, 0);
780 * w2k breaks the return val from ConfigCallback, so we have to hack around it, rather than just
781 * looking for a return value from ConfigCallback
783 if(!gControllerInfo
[0].Populated
)
785 KdPrint(("floppy: AddControllers: failed to get controller info from registry\n"));
789 /* Now that we have a controller, set it up with the system */
790 for(i
= 0; i
< gNumberOfControllers
; i
++)
792 /* 0: Report resource usage to the kernel, to make sure they aren't assigned to anyone else */
795 /* 1: Set up interrupt */
796 gControllerInfo
[i
].MappedVector
= HalGetInterruptVector(gControllerInfo
[i
].InterfaceType
, gControllerInfo
[i
].BusNumber
,
797 gControllerInfo
[i
].Level
, gControllerInfo
[i
].Vector
,
798 &gControllerInfo
[i
].MappedLevel
, &Affinity
);
800 /* Must set up the DPC before we connect the interrupt */
801 KeInitializeDpc(&gControllerInfo
[i
].Dpc
, DpcForIsr
, &gControllerInfo
[i
]);
803 KdPrint(("floppy: Connecting interrupt %d to controller%d (object 0x%x)\n", gControllerInfo
[i
].MappedVector
,
804 i
, &gControllerInfo
[i
]));
806 /* NOTE: We cannot share our interrupt, even on level-triggered buses. See Isr() for details. */
807 if(IoConnectInterrupt(&gControllerInfo
[i
].InterruptObject
, Isr
, &gControllerInfo
[i
], 0, gControllerInfo
[i
].MappedVector
,
808 gControllerInfo
[i
].MappedLevel
, gControllerInfo
[i
].MappedLevel
, gControllerInfo
[i
].InterruptMode
,
809 FALSE
, Affinity
, 0) != STATUS_SUCCESS
)
811 KdPrint(("floppy: AddControllers: unable to connect interrupt\n"));
817 memset(&DeviceDescription
, 0, sizeof(DeviceDescription
));
818 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
819 DeviceDescription
.Master
= (gControllerInfo
[i
].InterfaceType
== PCIBus
? TRUE
: FALSE
); /* guessing if not pci not master */
820 DeviceDescription
.DmaChannel
= gControllerInfo
[i
].Dma
;
821 DeviceDescription
.InterfaceType
= gControllerInfo
[i
].InterfaceType
;
822 DeviceDescription
.BusNumber
= gControllerInfo
[i
].BusNumber
;
824 if(gControllerInfo
[i
].InterfaceType
== PCIBus
)
826 DeviceDescription
.Dma32BitAddresses
= TRUE
;
827 DeviceDescription
.DmaWidth
= Width32Bits
;
830 /* DMA 0,1,2,3 are 8-bit; 4,5,6,7 are 16-bit (4 is chain i think) */
831 DeviceDescription
.DmaWidth
= gControllerInfo
[i
].Dma
> 3 ? Width16Bits
: Width8Bits
;
833 gControllerInfo
[i
].AdapterObject
= HalGetAdapter(&DeviceDescription
, &gControllerInfo
[i
].MapRegisters
);
835 if(!gControllerInfo
[i
].AdapterObject
)
837 KdPrint(("floppy: AddControllers: unable to allocate an adapter object\n"));
838 IoDisconnectInterrupt(gControllerInfo
[i
].InterruptObject
);
842 /* 2b: Initialize the new controller */
843 if(InitController(&gControllerInfo
[i
]) != STATUS_SUCCESS
)
845 KdPrint(("floppy: AddControllers():Unable to set up controller %d - initialization failed\n", i
));
846 ASSERT(0); /* FIXME: clean up properly */
850 /* 3: per-drive setup */
851 for(j
= 0; j
< gControllerInfo
[i
].NumberOfDrives
; j
++)
853 WCHAR DeviceNameBuf
[MAX_DEVICE_NAME
];
854 UNICODE_STRING DeviceName
;
855 UNICODE_STRING LinkName
;
858 KdPrint(("floppy: AddControllers(): Configuring drive %d on controller %d\n", i
, j
));
861 * 3a: create a device object for the drive
862 * Controllers and drives are 0-based, so the combos are:
873 DriveNumber
= i
*4 + j
;
875 swprintf(DeviceNameBuf
, L
"\\Device\\Floppy%d", DriveNumber
);
876 RtlInitUnicodeString(&DeviceName
, DeviceNameBuf
);
878 if(IoCreateDevice(DriverObject
, sizeof(PVOID
), &DeviceName
,
879 FILE_DEVICE_DISK
, FILE_REMOVABLE_MEDIA
| FILE_FLOPPY_DISKETTE
, FALSE
,
880 &gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
) != STATUS_SUCCESS
)
882 KdPrint(("floppy: AddControllers: unable to register a Device object\n"));
883 IoDisconnectInterrupt(gControllerInfo
[i
].InterruptObject
);
887 KdPrint(("floppy: AddControllers: New device: %S (0x%x)\n", DeviceNameBuf
, gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
));
889 /* 3c: Set flags up */
890 gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
->Flags
|= DO_DIRECT_IO
;
892 /* 3d: Create a symlink */
893 swprintf(gControllerInfo
[i
].DriveInfo
[j
].SymLinkBuffer
, L
"\\DosDevices\\%c:", DriveNumber
+ 'A');
894 RtlInitUnicodeString(&LinkName
, gControllerInfo
[i
].DriveInfo
[j
].SymLinkBuffer
);
895 if(IoCreateSymbolicLink(&LinkName
, &DeviceName
) != STATUS_SUCCESS
)
897 KdPrint(("floppy: AddControllers: Unable to create a symlink for drive %d\n", DriveNumber
));
898 IoDisconnectInterrupt(gControllerInfo
[i
].InterruptObject
);
899 // delete devices too?
903 /* 3e: Set up the DPC */
904 IoInitializeDpcRequest(gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
, DpcForIsr
);
906 /* 3f: Point the device extension at our DriveInfo struct */
907 gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
->DeviceExtension
= &gControllerInfo
[i
].DriveInfo
[j
];
909 /* 3g: neat comic strip */
911 /* 3h: set the initial media type to unknown */
912 memset(&gControllerInfo
[i
].DriveInfo
[j
].DiskGeometry
, 0, sizeof(DISK_GEOMETRY
));
913 gControllerInfo
[i
].DriveInfo
[j
].DiskGeometry
.MediaType
= Unknown
;
917 KdPrint(("floppy: AddControllers: --------------------------------------------> finished adding controllers\n"));
923 VOID NTAPI
SignalMediaChanged(PDEVICE_OBJECT DeviceObject
,
926 * FUNCTION: Process an IRP when the media has changed, and possibly notify the user
928 * DeviceObject: DeviceObject associated with the IRP
929 * Irp: IRP that we're failing due to change
931 * - This procedure is documented in the DDK by "Notifying the File System of Possible Media Changes",
932 * "IoSetHardErrorOrVerifyDevice", and by "Responding to Check-Verify Requests from the File System".
933 * - Callable at <= DISPATCH_LEVEL
936 PDRIVE_INFO DriveInfo
= DeviceObject
->DeviceExtension
;
938 KdPrint(("floppy: SignalMediaChanged called\n"));
940 DriveInfo
->DiskChangeCount
++;
942 /* If volume is not mounted, do NOT set verify and return STATUS_IO_DEVICE_ERROR */
943 if(!(DeviceObject
->Vpb
->Flags
& VPB_MOUNTED
))
945 Irp
->IoStatus
.Status
= STATUS_IO_DEVICE_ERROR
;
946 Irp
->IoStatus
.Information
= 0;
950 /* Notify the filesystem that it will need to verify the volume */
951 DeviceObject
->Flags
|= DO_VERIFY_VOLUME
;
952 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
953 Irp
->IoStatus
.Information
= 0;
956 * If this is a user-based, threaded request, let the IO manager know to pop up a box asking
957 * the user to supply the correct media, but only if the error (which we just picked out above)
958 * is deemed by the IO manager to be "user induced". The reason we don't just unconditionally
959 * call IoSetHardError... is because MS might change the definition of "user induced" some day,
960 * and we don't want to have to remember to re-code this.
962 if(Irp
->Tail
.Overlay
.Thread
&& IoIsErrorUserInduced(Irp
->IoStatus
.Status
))
963 IoSetHardErrorOrVerifyDevice(Irp
, DeviceObject
);
967 VOID NTAPI
QueueThread(PVOID Context
)
969 * FUNCTION: Thread that manages the queue and dispatches any queued requests
975 PIO_STACK_LOCATION Stack
;
976 PDEVICE_OBJECT DeviceObject
;
981 Objects
[0] = &QueueSemaphore
;
982 Objects
[1] = &QueueThreadTerminate
;
986 KeWaitForMultipleObjects(2, Objects
, WaitAny
, Executive
, KernelMode
, FALSE
, NULL
, NULL
);
988 if(KeReadStateEvent(&QueueThreadTerminate
))
990 KdPrint(("floppy: QueueThread terminating\n"));
994 KdPrint(("floppy: QueueThread: servicing an IRP\n"));
996 Irp
= IoCsqRemoveNextIrp(&Csq
, 0);
998 /* we won't get an irp if it was canceled */
1001 KdPrint(("floppy: QueueThread: IRP queue empty\n"));
1005 DeviceObject
= (PDEVICE_OBJECT
)Irp
->Tail
.Overlay
.DriverContext
[0];
1007 ASSERT(DeviceObject
);
1009 Stack
= IoGetCurrentIrpStackLocation(Irp
);
1011 /* Decide what to do with the IRP */
1012 switch(Stack
->MajorFunction
)
1016 ReadWritePassive(DeviceObject
->DeviceExtension
, Irp
);
1019 case IRP_MJ_DEVICE_CONTROL
:
1020 DeviceIoctlPassive(DeviceObject
->DeviceExtension
, Irp
);
1024 KdPrint(("floppy: QueueThread(): Unrecognized irp: mj: 0x%x\n", Stack
->MajorFunction
));
1025 Irp
->IoStatus
.Status
= STATUS_NOT_SUPPORTED
;
1026 Irp
->IoStatus
.Information
= 0;
1027 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
1033 NTSTATUS NTAPI
DriverEntry(PDRIVER_OBJECT DriverObject
,
1034 PUNICODE_STRING RegistryPath
)
1036 * FUNCTION: Entry-point for the driver
1038 * DriverObject: Our driver object
1039 * RegistryPath: Unused
1041 * STATUS_SUCCESS on successful initialization of at least one drive
1042 * STATUS_NO_SUCH_DEVICE if we didn't find even one drive
1043 * STATUS_UNSUCCESSFUL otherwise
1046 HANDLE ThreadHandle
;
1049 * Set up dispatch routines
1051 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)CreateClose
;
1052 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)CreateClose
;
1053 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)ReadWrite
;
1054 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)ReadWrite
;
1055 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)DeviceIoctl
;
1057 DriverObject
->DriverUnload
= Unload
;
1060 * We depend on some zeroes in these structures. I know this is supposed to be
1061 * initialized to 0 by the complier but this makes me feel beter.
1063 memset(&gControllerInfo
, 0, sizeof(gControllerInfo
));
1066 * Set up queue. This routine cannot fail (trust me, I wrote it).
1068 IoCsqInitialize(&Csq
, CsqInsertIrp
, CsqRemoveIrp
, CsqPeekNextIrp
,
1069 CsqAcquireLock
, CsqReleaseLock
, CsqCompleteCanceledIrp
);
1074 KeInitializeSpinLock(&IrpQueueLock
);
1077 * ...and the queue list itself
1079 InitializeListHead(&IrpQueue
);
1082 * The queue is counted by a semaphore. The queue management thread
1083 * blocks on this semaphore, so if requests come in faster than the queue
1084 * thread can handle them, the semaphore count goes up.
1086 KeInitializeSemaphore(&QueueSemaphore
, 0, 0x7fffffff);
1089 * Create the queue processing thread. Save its handle in the global variable
1090 * ThreadHandle so we can wait on its termination during Unload.
1092 if(PsCreateSystemThread(&ThreadHandle
, 0, 0, 0, 0, QueueThread
, 0) != STATUS_SUCCESS
)
1094 KdPrint(("floppy: Unable to create system thread; failing init\n"));
1095 return STATUS_UNSUCCESSFUL
;
1098 if(ObReferenceObjectByHandle(ThreadHandle
, STANDARD_RIGHTS_ALL
, NULL
, KernelMode
, &ThreadObject
, NULL
) != STATUS_SUCCESS
)
1100 KdPrint(("floppy: Unable to reference returned thread handle; failing init\n"));
1101 return STATUS_UNSUCCESSFUL
;
1104 ZwClose(ThreadHandle
);
1107 * Event to terminate that thread
1109 KeInitializeEvent(&QueueThreadTerminate
, NotificationEvent
, FALSE
);
1112 * Start the device discovery proces. In theory, this should return STATUS_SUCCESS if
1113 * it finds even one drive attached to one controller. In practice, the AddControllers
1114 * routine doesn't handle all of the errors right just yet. FIXME.
1116 if(!AddControllers(DriverObject
))
1117 return STATUS_NO_SUCH_DEVICE
;
1119 return STATUS_SUCCESS
;