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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 * PROJECT: ReactOS Floppy Driver
21 * PURPOSE: Main floppy driver routines
22 * PROGRAMMER: Vizzini (vizzini@plasmic.com)
24 * 15-Feb-2004 vizzini - Created
26 * - This driver is only designed to work with ISA-bus floppy controllers. This
27 * won't work on PCI-based controllers or on anything else with level-sensitive
28 * interrupts without modification. I don't think these controllers exist.
30 * ---- General to-do items ----
31 * TODO: Figure out why CreateClose isn't called any more. Seems to correspond
32 * with the driver not being unloadable.
33 * TODO: Think about StopDpcQueued -- could be a race; too tired atm to tell
34 * TODO: Clean up drive start/stop responsibilities (currently a mess...)
36 * ---- Support for proper media detection ----
37 * TODO: Handle MFM flag
38 * TODO: Un-hardcode the data rate from various places
39 * TODO: Proper media detection (right now we're hardcoded to 1.44)
40 * TODO: Media detection based on sector 1
49 #include "readwrite.h"
52 * Global controller info structures. Each controller gets one. Since the system
53 * will probably have only one, with four being a very unlikely maximum, a static
54 * global array is easiest to deal with.
56 static CONTROLLER_INFO gControllerInfo
[MAX_CONTROLLERS
];
57 static ULONG gNumberOfControllers
= 0;
59 /* Queue thread management */
60 static KEVENT QueueThreadTerminate
;
61 static PVOID QueueThreadObject
;
65 MotorStopDpcFunc(PKDPC UnusedDpc
, PVOID DeferredContext
, PVOID SystemArgument1
, PVOID SystemArgument2
)
67 * FUNCTION: Stop the floppy motor
69 * UnusedDpc: DPC object that's going off
70 * DeferredContext: called with DRIVE_INFO for drive to turn off
71 * SystemArgument1: unused
72 * SystemArgument2: unused
74 * - Must set an event to let other threads know we're done turning off the motor
75 * - Called back at DISPATCH_LEVEL
78 PCONTROLLER_INFO ControllerInfo
= (PCONTROLLER_INFO
)DeferredContext
;
80 UNREFERENCED_PARAMETER(SystemArgument1
);
81 UNREFERENCED_PARAMETER(SystemArgument2
);
82 UNREFERENCED_PARAMETER(UnusedDpc
);
84 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
85 ASSERT(ControllerInfo
);
87 TRACE_(FLOPPY
, "MotorStopDpcFunc called\n");
89 HwTurnOffMotor(ControllerInfo
);
90 ControllerInfo
->StopDpcQueued
= FALSE
;
91 KeSetEvent(&ControllerInfo
->MotorStoppedEvent
, EVENT_INCREMENT
, FALSE
);
96 StartMotor(PDRIVE_INFO DriveInfo
)
98 * FUNCTION: Start the motor, taking into account proper handling of the timer race
100 * DriveInfo: drive to start
102 * - Never call HwTurnOnMotor() directly
103 * - This protocol manages a race between the cancel timer and the requesting thread.
104 * You wouldn't want to turn on the motor and then cancel the timer, because the
105 * cancel dpc might fire in the meantime, and that'd un-do what you just did. If you
106 * cancel the timer first, but KeCancelTimer returns false, the dpc is already running,
107 * so you have to wait until the dpc is completely done running, or else you'll race
108 * with the turner-offer
109 * - PAGED_CODE because we wait
115 TRACE_(FLOPPY
, "StartMotor called\n");
117 if(DriveInfo
->ControllerInfo
->StopDpcQueued
&& !KeCancelTimer(&DriveInfo
->ControllerInfo
->MotorTimer
))
119 /* Motor turner-offer is already running; wait for it to finish */
120 INFO_(FLOPPY
, "StartMotor: motor turner-offer is already running; waiting for it\n");
121 KeWaitForSingleObject(&DriveInfo
->ControllerInfo
->MotorStoppedEvent
, Executive
, KernelMode
, FALSE
, NULL
);
122 INFO_(FLOPPY
, "StartMotor: wait satisfied\n");
125 DriveInfo
->ControllerInfo
->StopDpcQueued
= FALSE
;
127 if(HwTurnOnMotor(DriveInfo
) != STATUS_SUCCESS
)
129 WARN_(FLOPPY
, "StartMotor(): warning: HwTurnOnMotor failed\n");
135 StopMotor(PCONTROLLER_INFO ControllerInfo
)
137 * FUNCTION: Stop all motors on the controller
139 * DriveInfo: Drive to stop
141 * - Never call HwTurnOffMotor() directly
142 * - This manages the timer cancelation race (see StartMotor for details).
143 * All we have to do is set up a timer.
146 LARGE_INTEGER StopTime
;
148 ASSERT(ControllerInfo
);
150 TRACE_(FLOPPY
, "StopMotor called\n");
152 /* one relative second, in 100-ns units */
153 StopTime
.QuadPart
= 10000000;
154 StopTime
.QuadPart
*= -1;
156 KeClearEvent(&ControllerInfo
->MotorStoppedEvent
);
157 KeSetTimer(&ControllerInfo
->MotorTimer
, StopTime
, &ControllerInfo
->MotorStopDpc
);
158 ControllerInfo
->StopDpcQueued
= TRUE
;
163 WaitForControllerInterrupt(PCONTROLLER_INFO ControllerInfo
, PLARGE_INTEGER Timeout
)
165 * FUNCTION: Wait for the controller to interrupt, and then clear the event
167 * ControllerInfo: Controller to wait for
168 * Timeout: How long to wait for
170 * - There is a small chance that an unexpected or spurious interrupt could
171 * be lost with this clear/wait/clear scheme used in this driver. This is
172 * deemed to be an acceptable risk due to the unlikeliness of the scenario,
173 * and the fact that it'll probably work fine next time.
174 * - PAGED_CODE because it waits
180 ASSERT(ControllerInfo
);
182 Status
= KeWaitForSingleObject(&ControllerInfo
->SynchEvent
, Executive
, KernelMode
, FALSE
, Timeout
);
183 KeClearEvent(&ControllerInfo
->SynchEvent
);
188 static DRIVER_DISPATCH CreateClose
;
189 static NTSTATUS NTAPI
CreateClose(PDEVICE_OBJECT DeviceObject
,
192 * FUNCTION: Dispatch function called for Create and Close IRPs
194 * DeviceObject: DeviceObject that is the target of the IRP
195 * Irp: IRP to process
197 * STATUS_SUCCESS in all cases
199 * - The Microsoft sample drivers tend to return FILE_OPENED in Information, so I do too.
200 * - No reason to fail the device open
201 * - No state to track, so this routine is easy
202 * - Can be called <= DISPATCH_LEVEL
204 * TODO: Figure out why this isn't getting called
207 UNREFERENCED_PARAMETER(DeviceObject
);
209 TRACE_(FLOPPY
, "CreateClose called\n");
211 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
212 Irp
->IoStatus
.Information
= FILE_OPENED
;
214 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
216 return STATUS_SUCCESS
;
220 static NTSTATUS NTAPI
221 Recalibrate(PDRIVE_INFO DriveInfo
)
223 * FUNCTION: Start the recalibration process
225 * DriveInfo: Pointer to the driveinfo struct associated with the targeted drive
227 * STATUS_SUCCESS on successful starting of the process
228 * STATUS_IO_DEVICE_ERROR if it fails
230 * - Sometimes you have to do two recalibrations, particularly if the disk has <80 tracks.
231 * - PAGED_CODE because we wait
240 /* first turn on the motor */
241 /* Must stop after every start, prior to return */
242 StartMotor(DriveInfo
);
244 /* set the data rate */
245 WARN_(FLOPPY
, "FIXME: UN-HARDCODE DATA RATE\n");
246 if(HwSetDataRate(DriveInfo
->ControllerInfo
, 0) != STATUS_SUCCESS
)
248 WARN_(FLOPPY
, "Recalibrate: HwSetDataRate failed\n");
249 StopMotor(DriveInfo
->ControllerInfo
);
250 return STATUS_IO_DEVICE_ERROR
;
253 /* clear the event just in case the last call forgot */
254 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
256 /* sometimes you have to do this twice; we'll just do it twice all the time since
257 * we don't know if the people calling this Recalibrate routine expect a disk to
258 * even be in the drive, and if so, if that disk is formatted.
260 for(i
= 0; i
< 2; i
++)
262 /* Send the command */
263 Status
= HwRecalibrate(DriveInfo
);
264 if(Status
!= STATUS_SUCCESS
)
266 WARN_(FLOPPY
, "Recalibrate: HwRecalibrate returned error\n");
270 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
, NULL
);
272 /* Get the results */
273 Status
= HwRecalibrateResult(DriveInfo
->ControllerInfo
);
274 if(Status
!= STATUS_SUCCESS
)
276 WARN_(FLOPPY
, "Recalibrate: HwRecalibrateResult returned error\n");
281 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
283 /* Must stop after every start, prior to return */
284 StopMotor(DriveInfo
->ControllerInfo
);
291 ResetChangeFlag(PDRIVE_INFO DriveInfo
)
293 * FUNCTION: Reset the drive's change flag (as reflected in the DIR)
295 * DriveInfo: the drive to reset
297 * STATUS_SUCCESS if the changeline is cleared
298 * STATUS_NO_MEDIA_IN_DEVICE if the changeline cannot be cleared
299 * STATUS_IO_DEVICE_ERROR if the controller cannot be communicated with
301 * - Change reset procedure: recalibrate, seek 1, seek 0
302 * - If the line is still set after that, there's clearly no disk in the
303 * drive, so we return STATUS_NO_MEDIA_IN_DEVICE
304 * - PAGED_CODE because we wait
312 TRACE_(FLOPPY
, "ResetChangeFlag called\n");
314 /* Try to recalibrate. We don't care if it works. */
315 Recalibrate(DriveInfo
);
317 /* clear spurious interrupts in prep for seeks */
318 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
320 /* must re-start the drive because Recalibrate() stops it */
321 StartMotor(DriveInfo
);
324 if(HwSeek(DriveInfo
, 1) != STATUS_SUCCESS
)
326 WARN_(FLOPPY
, "ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n");
327 StopMotor(DriveInfo
->ControllerInfo
);
328 return STATUS_IO_DEVICE_ERROR
;
331 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
, NULL
);
333 if(HwSenseInterruptStatus(DriveInfo
->ControllerInfo
) != STATUS_SUCCESS
)
335 WARN_(FLOPPY
, "ResetChangeFlag(): HwSenseInterruptStatus failed; bailing out\n");
336 StopMotor(DriveInfo
->ControllerInfo
);
337 return STATUS_IO_DEVICE_ERROR
;
341 if(HwSeek(DriveInfo
, 0) != STATUS_SUCCESS
)
343 WARN_(FLOPPY
, "ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n");
344 StopMotor(DriveInfo
->ControllerInfo
);
345 return STATUS_IO_DEVICE_ERROR
;
348 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
, NULL
);
350 if(HwSenseInterruptStatus(DriveInfo
->ControllerInfo
) != STATUS_SUCCESS
)
352 WARN_(FLOPPY
, "ResetChangeFlag(): HwSenseInterruptStatus #2 failed; bailing\n");
353 StopMotor(DriveInfo
->ControllerInfo
);
354 return STATUS_IO_DEVICE_ERROR
;
357 /* Check the change bit */
358 if(HwDiskChanged(DriveInfo
, &DiskChanged
) != STATUS_SUCCESS
)
360 WARN_(FLOPPY
, "ResetChangeFlag(): HwDiskChanged failed; returning STATUS_IO_DEVICE_ERROR\n");
361 StopMotor(DriveInfo
->ControllerInfo
);
362 return STATUS_IO_DEVICE_ERROR
;
365 StopMotor(DriveInfo
->ControllerInfo
);
367 /* if the change flag is still set, there's probably no media in the drive. */
369 return STATUS_NO_MEDIA_IN_DEVICE
;
371 /* else we're done! */
372 return STATUS_SUCCESS
;
377 Unload(PDRIVER_OBJECT DriverObject
)
379 * FUNCTION: Unload the driver from memory
381 * DriverObject - The driver that is being unloaded
387 UNREFERENCED_PARAMETER(DriverObject
);
389 TRACE_(FLOPPY
, "unloading\n");
391 KeSetEvent(&QueueThreadTerminate
, 0, FALSE
);
392 KeWaitForSingleObject(QueueThreadObject
, Executive
, KernelMode
, FALSE
, 0);
393 ObDereferenceObject(QueueThreadObject
);
395 for(i
= 0; i
< gNumberOfControllers
; i
++)
397 if(!gControllerInfo
[i
].Initialized
)
400 for(j
= 0; j
< gControllerInfo
[i
].NumberOfDrives
; j
++)
402 if(!gControllerInfo
[i
].DriveInfo
[j
].Initialized
)
405 if(gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
)
409 RtlInitUnicodeString(&Link
, gControllerInfo
[i
].DriveInfo
[j
].ArcPathBuffer
);
410 IoDeassignArcName(&Link
);
412 IoDeleteDevice(gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
);
416 IoDisconnectInterrupt(gControllerInfo
[i
].InterruptObject
);
418 /* Power down the controller */
419 if(HwPowerOff(&gControllerInfo
[i
]) != STATUS_SUCCESS
)
421 WARN_(FLOPPY
, "unload: warning: HwPowerOff failed\n");
427 static NTSTATUS NTAPI
428 ConfigCallback(PVOID Context
,
429 PUNICODE_STRING PathName
,
430 INTERFACE_TYPE BusType
,
432 PKEY_VALUE_FULL_INFORMATION
*BusInformation
,
433 CONFIGURATION_TYPE ControllerType
,
434 ULONG ControllerNumber
,
435 PKEY_VALUE_FULL_INFORMATION
*ControllerInformation
,
436 CONFIGURATION_TYPE PeripheralType
,
437 ULONG PeripheralNumber
,
438 PKEY_VALUE_FULL_INFORMATION
*PeripheralInformation
)
440 * FUNCTION: Callback to IoQueryDeviceDescription, which tells us about our controllers
444 * BusType: Type of the bus that our controller is on
445 * BusNumber: Number of the bus that our controller is on
446 * BusInformation: Unused
447 * ControllerType: Unused
448 * ControllerNumber: Number of the controller that we're adding
449 * ControllerInformation: Full configuration information for our controller
450 * PeripheralType: Unused
451 * PeripheralNumber: Unused
452 * PeripheralInformation: Full configuration information for each drive on our controller
454 * STATUS_SUCCESS in all cases
456 * - The only documentation I've found about the contents of these structures is
457 * from the various Microsoft floppy samples and from the DDK headers. They're
458 * very vague, though, so I'm only mostly sure that this stuff is correct, as
459 * the MS samples do things completely differently than I have done them. Seems
460 * to work in my VMWare, though.
461 * - Basically, the function gets all of the information (port, dma, irq) about the
462 * controller, and then loops through all of the drives presented in PeripheralInformation.
463 * - Each controller has a CONTROLLER_INFO created for it, and each drive has a DRIVE_INFO.
464 * - Device objects are created for each drive (not controller), as that's the targeted
465 * device in the eyes of the rest of the OS. Each DRIVE_INFO points to a single CONTROLLER_INFO.
466 * - We only support up to four controllers in the whole system, each of which supports up to four
470 PKEY_VALUE_FULL_INFORMATION ControllerFullDescriptor
= ControllerInformation
[IoQueryDeviceConfigurationData
];
471 PCM_FULL_RESOURCE_DESCRIPTOR ControllerResourceDescriptor
= (PCM_FULL_RESOURCE_DESCRIPTOR
)((PCHAR
)ControllerFullDescriptor
+
472 ControllerFullDescriptor
->DataOffset
);
474 PKEY_VALUE_FULL_INFORMATION PeripheralFullDescriptor
= PeripheralInformation
[IoQueryDeviceConfigurationData
];
475 PCM_FULL_RESOURCE_DESCRIPTOR PeripheralResourceDescriptor
= (PCM_FULL_RESOURCE_DESCRIPTOR
)((PCHAR
)PeripheralFullDescriptor
+
476 PeripheralFullDescriptor
->DataOffset
);
478 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor
;
479 PCM_FLOPPY_DEVICE_DATA FloppyDeviceData
;
483 UNREFERENCED_PARAMETER(PeripheralType
);
484 UNREFERENCED_PARAMETER(PeripheralNumber
);
485 UNREFERENCED_PARAMETER(BusInformation
);
486 UNREFERENCED_PARAMETER(Context
);
487 UNREFERENCED_PARAMETER(ControllerType
);
488 UNREFERENCED_PARAMETER(PathName
);
491 TRACE_(FLOPPY
, "ConfigCallback called with ControllerNumber %d\n", ControllerNumber
);
493 gControllerInfo
[gNumberOfControllers
].ControllerNumber
= ControllerNumber
;
494 gControllerInfo
[gNumberOfControllers
].InterfaceType
= BusType
;
495 gControllerInfo
[gNumberOfControllers
].BusNumber
= BusNumber
;
497 /* Get controller interrupt level/vector, dma channel, and port base */
498 for(i
= 0; i
< ControllerResourceDescriptor
->PartialResourceList
.Count
; i
++)
500 KeInitializeEvent(&gControllerInfo
[gNumberOfControllers
].SynchEvent
, NotificationEvent
, FALSE
);
502 PartialDescriptor
= &ControllerResourceDescriptor
->PartialResourceList
.PartialDescriptors
[i
];
504 if(PartialDescriptor
->Type
== CmResourceTypeInterrupt
)
506 gControllerInfo
[gNumberOfControllers
].Level
= PartialDescriptor
->u
.Interrupt
.Level
;
507 gControllerInfo
[gNumberOfControllers
].Vector
= PartialDescriptor
->u
.Interrupt
.Vector
;
509 if(PartialDescriptor
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
)
510 gControllerInfo
[gNumberOfControllers
].InterruptMode
= Latched
;
512 gControllerInfo
[gNumberOfControllers
].InterruptMode
= LevelSensitive
;
515 else if(PartialDescriptor
->Type
== CmResourceTypePort
)
517 PHYSICAL_ADDRESS TranslatedAddress
;
518 ULONG AddressSpace
= 0x1; /* I/O Port Range */
520 if(!HalTranslateBusAddress(BusType
, BusNumber
, PartialDescriptor
->u
.Port
.Start
, &AddressSpace
, &TranslatedAddress
))
522 WARN_(FLOPPY
, "HalTranslateBusAddress failed; returning\n");
523 return STATUS_IO_DEVICE_ERROR
;
526 if(AddressSpace
== 0)
527 gControllerInfo
[gNumberOfControllers
].BaseAddress
= MmMapIoSpace(TranslatedAddress
, FDC_PORT_BYTES
, MmNonCached
);
529 gControllerInfo
[gNumberOfControllers
].BaseAddress
= (PUCHAR
)(ULONG_PTR
)TranslatedAddress
.QuadPart
;
532 else if(PartialDescriptor
->Type
== CmResourceTypeDma
)
533 gControllerInfo
[gNumberOfControllers
].Dma
= PartialDescriptor
->u
.Dma
.Channel
;
536 /* Start with 0 drives, then go looking */
537 gControllerInfo
[gNumberOfControllers
].NumberOfDrives
= 0;
539 /* learn about drives attached to controller */
540 for(i
= 0; i
< PeripheralResourceDescriptor
->PartialResourceList
.Count
; i
++)
542 PDRIVE_INFO DriveInfo
= &gControllerInfo
[gNumberOfControllers
].DriveInfo
[i
];
544 PartialDescriptor
= &PeripheralResourceDescriptor
->PartialResourceList
.PartialDescriptors
[i
];
546 if(PartialDescriptor
->Type
!= CmResourceTypeDeviceSpecific
)
549 FloppyDeviceData
= (PCM_FLOPPY_DEVICE_DATA
)(PartialDescriptor
+ 1);
551 DriveInfo
->ControllerInfo
= &gControllerInfo
[gNumberOfControllers
];
552 DriveInfo
->UnitNumber
= i
;
554 DriveInfo
->FloppyDeviceData
.MaxDensity
= FloppyDeviceData
->MaxDensity
;
555 DriveInfo
->FloppyDeviceData
.MountDensity
= FloppyDeviceData
->MountDensity
;
556 DriveInfo
->FloppyDeviceData
.StepRateHeadUnloadTime
= FloppyDeviceData
->StepRateHeadUnloadTime
;
557 DriveInfo
->FloppyDeviceData
.HeadLoadTime
= FloppyDeviceData
->HeadLoadTime
;
558 DriveInfo
->FloppyDeviceData
.MotorOffTime
= FloppyDeviceData
->MotorOffTime
;
559 DriveInfo
->FloppyDeviceData
.SectorLengthCode
= FloppyDeviceData
->SectorLengthCode
;
560 DriveInfo
->FloppyDeviceData
.SectorPerTrack
= FloppyDeviceData
->SectorPerTrack
;
561 DriveInfo
->FloppyDeviceData
.ReadWriteGapLength
= FloppyDeviceData
->ReadWriteGapLength
;
562 DriveInfo
->FloppyDeviceData
.FormatGapLength
= FloppyDeviceData
->FormatGapLength
;
563 DriveInfo
->FloppyDeviceData
.FormatFillCharacter
= FloppyDeviceData
->FormatFillCharacter
;
564 DriveInfo
->FloppyDeviceData
.HeadSettleTime
= FloppyDeviceData
->HeadSettleTime
;
565 DriveInfo
->FloppyDeviceData
.MotorSettleTime
= FloppyDeviceData
->MotorSettleTime
;
566 DriveInfo
->FloppyDeviceData
.MaximumTrackValue
= FloppyDeviceData
->MaximumTrackValue
;
567 DriveInfo
->FloppyDeviceData
.DataTransferLength
= FloppyDeviceData
->DataTransferLength
;
569 /* Once it's all set up, acknowledge its existence in the controller info object */
570 gControllerInfo
[gNumberOfControllers
].NumberOfDrives
++;
573 gControllerInfo
[gNumberOfControllers
].Populated
= TRUE
;
574 gNumberOfControllers
++;
576 return STATUS_SUCCESS
;
581 Isr(PKINTERRUPT Interrupt
, PVOID ServiceContext
)
583 * FUNCTION: Interrupt service routine for the controllers
585 * Interrupt: Interrupt object representing the interrupt that occured
586 * ServiceContext: Pointer to the ControllerInfo object that caused the interrupt
588 * TRUE in all cases (see notes)
590 * - We should always be the target of the interrupt, being an edge-triggered ISA interrupt, but
591 * this won't be the case with a level-sensitive system like PCI
592 * - Note that it probably doesn't matter if the interrupt isn't dismissed, as it's edge-triggered.
593 * It probably won't keep re-interrupting.
594 * - There are two different ways to dismiss a floppy interrupt. If the command has a result phase
595 * (see intel datasheet), you dismiss the interrupt by reading the first data byte. If it does
596 * not, you dismiss the interrupt by doing a Sense Interrupt command. Again, because it's edge-
597 * triggered, this is safe to not do here, as we can just wait for the DPC.
598 * - Either way, we don't want to do this here. The controller shouldn't interrupt again, so we'll
599 * schedule a DPC to take care of it.
600 * - This driver really cannot share interrupts, as I don't know how to conclusively say
601 * whether it was our controller that interrupted or not. I just have to assume that any time
602 * my ISR gets called, it was my board that called it. Dumb design, yes, but it goes back to
603 * the semantics of ISA buses. That, and I don't know much about ISA drivers. :-)
604 * UPDATE: The high bit of Status Register A seems to work on non-AT controllers.
608 PCONTROLLER_INFO ControllerInfo
= (PCONTROLLER_INFO
)ServiceContext
;
610 UNREFERENCED_PARAMETER(Interrupt
);
612 ASSERT(ControllerInfo
);
614 TRACE_(FLOPPY
, "ISR called\n");
617 * Due to the stupidity of the drive/controller relationship on the floppy drive, only one device object
618 * can have an active interrupt pending. Due to the nature of these IRPs, though, there will only ever
619 * be one thread expecting an interrupt at a time, and furthermore, Interrupts (outside of spurious ones)
620 * won't ever happen unless a thread is expecting them. Therefore, all we have to do is signal an event
621 * and we're done. Queue a DPC and leave.
623 KeInsertQueueDpc(&ControllerInfo
->Dpc
, NULL
, NULL
);
630 DpcForIsr(PKDPC UnusedDpc
, PVOID Context
, PVOID SystemArgument1
, PVOID SystemArgument2
)
632 * FUNCTION: This DPC gets queued by every ISR. Does the real per-interrupt work.
634 * UnusedDpc: Pointer to the DPC object that represents our function
635 * DeviceObject: Device that this DPC is running for
637 * Context: Pointer to our ControllerInfo struct
639 * - This function just kicks off whatever the SynchEvent is and returns. We depend on
640 * the thing that caused the drive to interrupt to handle the work of clearing the interrupt.
641 * This enables us to get back to PASSIVE_LEVEL and not hog system time on a really stupid,
642 * slow, screwed-up piece of hardware.
643 * - If nothing is waiting for us to set the event, the interrupt is effectively lost and will
644 * never be dismissed. I wonder if this will become a problem.
645 * - Called at DISPATCH_LEVEL
648 PCONTROLLER_INFO ControllerInfo
= (PCONTROLLER_INFO
)Context
;
650 UNREFERENCED_PARAMETER(UnusedDpc
);
651 UNREFERENCED_PARAMETER(SystemArgument1
);
652 UNREFERENCED_PARAMETER(SystemArgument2
);
654 ASSERT(ControllerInfo
);
656 TRACE_(FLOPPY
, "DpcForIsr called\n");
658 KeSetEvent(&ControllerInfo
->SynchEvent
, EVENT_INCREMENT
, FALSE
);
662 static NTSTATUS NTAPI
663 InitController(PCONTROLLER_INFO ControllerInfo
)
665 * FUNCTION: Initialize a newly-found controller
667 * ControllerInfo: pointer to the controller to be initialized
669 * STATUS_SUCCESS if the controller is successfully initialized
670 * STATUS_IO_DEVICE_ERROR otherwise
675 UCHAR HeadUnloadTime
;
677 UCHAR ControllerVersion
;
680 ASSERT(ControllerInfo
);
682 TRACE_(FLOPPY
, "InitController called with Controller 0x%p\n", ControllerInfo
);
684 /* Get controller in a known state */
685 if(HwConfigure(ControllerInfo
, FALSE
, TRUE
, TRUE
, 0, 0) != STATUS_SUCCESS
)
687 WARN_(FLOPPY
, "InitController: unable to configure controller\n");
688 return STATUS_IO_DEVICE_ERROR
;
691 /* Get the controller version */
692 ControllerVersion
= HwGetVersion(ControllerInfo
);
694 KeClearEvent(&ControllerInfo
->SynchEvent
);
696 /* Reset the controller */
697 if(HwReset(ControllerInfo
) != STATUS_SUCCESS
)
699 WARN_(FLOPPY
, "InitController: unable to reset controller\n");
700 return STATUS_IO_DEVICE_ERROR
;
703 INFO_(FLOPPY
, "InitController: waiting for initial interrupt\n");
705 /* Wait for an interrupt */
706 WaitForControllerInterrupt(ControllerInfo
, NULL
);
708 /* Reset means you have to clear each of the four interrupts (one per drive) */
709 for(i
= 0; i
< MAX_DRIVES_PER_CONTROLLER
; i
++)
711 INFO_(FLOPPY
, "InitController: Sensing interrupt %d\n", i
);
713 if(HwSenseInterruptStatus(ControllerInfo
) != STATUS_SUCCESS
)
715 WARN_(FLOPPY
, "InitController: Unable to clear interrupt 0x%x\n", i
);
716 return STATUS_IO_DEVICE_ERROR
;
720 INFO_(FLOPPY
, "InitController: done sensing interrupts\n");
722 /* Next, see if we have the right version to do implied seek */
723 if(ControllerVersion
== VERSION_ENHANCED
)
725 /* If so, set that up -- all defaults below except first TRUE for EIS */
726 if(HwConfigure(ControllerInfo
, TRUE
, TRUE
, TRUE
, 0, 0) != STATUS_SUCCESS
)
728 WARN_(FLOPPY
, "InitController: unable to set up implied seek\n");
729 ControllerInfo
->ImpliedSeeks
= FALSE
;
733 INFO_(FLOPPY
, "InitController: implied seeks set!\n");
734 ControllerInfo
->ImpliedSeeks
= TRUE
;
738 * FIXME: Figure out the answer to the below
740 * I must admit that I'm really confused about the Model 30 issue. At least one
741 * important bit (the disk change bit in the DIR) is flipped if this is a Model 30
742 * controller. However, at least one other floppy driver believes that there are only
743 * two computers that are guaranteed to have a Model 30 controller:
747 * ...and another driver only lists a config option for "thinkpad", that flips
748 * the change line. A third driver doesn't mention the Model 30 issue at all.
750 * What I can't tell is whether or not the average, run-of-the-mill computer now has
751 * a Model 30 controller. For the time being, I'm going to wire this to FALSE,
752 * and just not support the computers mentioned above, while I try to figure out
753 * how ubiquitous these newfangled 30 thingies are.
755 //ControllerInfo->Model30 = TRUE;
756 ControllerInfo
->Model30
= FALSE
;
760 INFO_(FLOPPY
, "InitController: enhanced version not supported; disabling implied seeks\n");
761 ControllerInfo
->ImpliedSeeks
= FALSE
;
762 ControllerInfo
->Model30
= FALSE
;
766 WARN_(FLOPPY
, "FIXME: Figure out speed\n");
767 HeadLoadTime
= SPECIFY_HLT_500K
;
768 HeadUnloadTime
= SPECIFY_HUT_500K
;
769 StepRateTime
= SPECIFY_SRT_500K
;
771 INFO_(FLOPPY
, "InitController: setting data rate\n");
774 if(HwSetDataRate(ControllerInfo
, DRSR_DSEL_500KBPS
) != STATUS_SUCCESS
)
776 WARN_(FLOPPY
, "InitController: unable to set data rate\n");
777 return STATUS_IO_DEVICE_ERROR
;
780 INFO_(FLOPPY
, "InitController: issuing specify command to controller\n");
782 /* Don't disable DMA --> enable dma (dumb & confusing) */
783 if(HwSpecify(ControllerInfo
, HeadLoadTime
, HeadUnloadTime
, StepRateTime
, FALSE
) != STATUS_SUCCESS
)
785 WARN_(FLOPPY
, "InitController: unable to specify options\n");
786 return STATUS_IO_DEVICE_ERROR
;
789 /* Init the stop stuff */
790 KeInitializeDpc(&ControllerInfo
->MotorStopDpc
, MotorStopDpcFunc
, ControllerInfo
);
791 KeInitializeTimer(&ControllerInfo
->MotorTimer
);
792 KeInitializeEvent(&ControllerInfo
->MotorStoppedEvent
, NotificationEvent
, FALSE
);
793 ControllerInfo
->StopDpcQueued
= FALSE
;
796 * Recalibrate each drive on the controller (depends on StartMotor, which depends on the timer stuff above)
797 * We don't even know if there is a disk in the drive, so this may not work, but that's OK.
799 for(i
= 0; i
< ControllerInfo
->NumberOfDrives
; i
++)
801 INFO_(FLOPPY
, "InitController: recalibrating drive 0x%x on controller 0x%p\n", i
, ControllerInfo
);
802 Recalibrate(&ControllerInfo
->DriveInfo
[i
]);
805 INFO_(FLOPPY
, "InitController: done initializing; returning STATUS_SUCCESS\n");
807 return STATUS_SUCCESS
;
812 ReportToMountMgr(UCHAR ControlerId
, UCHAR DriveId
)
814 * FUNCTION: Called to report a new controler to the MountMgr
816 * ControlerId: ID of the controler
817 * DriveId: ID of the device for the controler
821 * - This is a hack to allow MountMgr handling our devices
825 UNICODE_STRING MountMgrDevice
;
826 PDEVICE_OBJECT DeviceObject
;
827 PFILE_OBJECT FileObject
;
828 PMOUNTMGR_TARGET_NAME MountTarget
;
832 IO_STATUS_BLOCK IoStatus
;
834 /* First, get MountMgr DeviceObject */
835 RtlInitUnicodeString(&MountMgrDevice
, MOUNTMGR_DEVICE_NAME
);
836 Status
= IoGetDeviceObjectPointer(&MountMgrDevice
, FILE_READ_ATTRIBUTES
,
837 &FileObject
, &DeviceObject
);
839 if(!NT_SUCCESS(Status
))
841 WARN_(FLOPPY
, "ReportToMountMgr: Can't get MountMgr pointers %lx\n", Status
);
845 DeviceLen
= wcslen(&gControllerInfo
[ControlerId
].DriveInfo
[DriveId
].DeviceNameBuffer
[0]) * sizeof(WCHAR
);
847 /* Allocate input buffer to report our floppy device */
848 MountTarget
= ExAllocatePool(NonPagedPool
,
849 sizeof(MOUNTMGR_TARGET_NAME
) + DeviceLen
);
853 WARN_(FLOPPY
, "ReportToMountMgr: Allocation of mountTarget failed\n");
854 ObDereferenceObject(FileObject
);
858 MountTarget
->DeviceNameLength
= DeviceLen
;
859 RtlCopyMemory(MountTarget
->DeviceName
,
860 gControllerInfo
[ControlerId
].DriveInfo
[DriveId
].DeviceNameBuffer
,
863 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
865 /* Build the IRP used to communicate with the MountMgr */
866 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION
,
869 sizeof(MOUNTMGR_TARGET_NAME
) + DeviceLen
,
878 WARN_(FLOPPY
, "ReportToMountMgr: Allocation of irp failed\n");
879 ExFreePool(MountTarget
);
880 ObDereferenceObject(FileObject
);
884 /* Call the MountMgr */
885 Status
= IoCallDriver(DeviceObject
, Irp
);
887 if (Status
== STATUS_PENDING
) {
888 KeWaitForSingleObject(&Event
, Suspended
, KernelMode
, FALSE
, NULL
);
889 Status
= IoStatus
.Status
;
894 INFO_(FLOPPY
, "Reported to the MountMgr: %lx\n", Status
);
896 ExFreePool(MountTarget
);
897 ObDereferenceObject(FileObject
);
904 AddControllers(PDRIVER_OBJECT DriverObject
)
906 * FUNCTION: Called on initialization to find our controllers and build device and controller objects for them
908 * DriverObject: Our driver's DriverObject (so we can create devices against it)
910 * FALSE if we can't allocate a device, adapter, or interrupt object, or if we fail to find any controllers
911 * TRUE otherwise (i.e. we have at least one fully-configured controller)
913 * - Currently we only support ISA buses.
914 * - BUG: Windows 2000 seems to clobber the response from the IoQueryDeviceDescription callback, so now we
915 * just test a boolean value in the first object to see if it was completely populated. The same value
916 * is tested for each controller before we build device objects for it.
918 * - Report resource usage to the HAL
921 INTERFACE_TYPE InterfaceType
= Isa
;
922 CONFIGURATION_TYPE ControllerType
= DiskController
;
923 CONFIGURATION_TYPE PeripheralType
= FloppyDiskPeripheral
;
925 DEVICE_DESCRIPTION DeviceDescription
;
931 /* Find our controllers on all ISA buses */
932 IoQueryDeviceDescription(&InterfaceType
, 0, &ControllerType
, 0, &PeripheralType
, 0, ConfigCallback
, 0);
935 * w2k breaks the return val from ConfigCallback, so we have to hack around it, rather than just
936 * looking for a return value from ConfigCallback. We expect at least one controller.
938 if(!gControllerInfo
[0].Populated
)
940 WARN_(FLOPPY
, "AddControllers: failed to get controller info from registry\n");
944 /* Now that we have a controller, set it up with the system */
945 for(i
= 0; i
< gNumberOfControllers
&& gControllerInfo
[i
].NumberOfDrives
> 0; i
++)
947 /* 0: Report resource usage to the kernel, to make sure they aren't assigned to anyone else */
948 /* FIXME: Implement me. */
950 /* 1: Set up interrupt */
951 gControllerInfo
[i
].MappedVector
= HalGetInterruptVector(gControllerInfo
[i
].InterfaceType
, gControllerInfo
[i
].BusNumber
,
952 gControllerInfo
[i
].Level
, gControllerInfo
[i
].Vector
,
953 &gControllerInfo
[i
].MappedLevel
, &Affinity
);
955 /* Must set up the DPC before we connect the interrupt */
956 KeInitializeDpc(&gControllerInfo
[i
].Dpc
, DpcForIsr
, &gControllerInfo
[i
]);
958 INFO_(FLOPPY
, "Connecting interrupt %d to controller%d (object 0x%p)\n", gControllerInfo
[i
].MappedVector
,
959 i
, &gControllerInfo
[i
]);
961 /* NOTE: We cannot share our interrupt, even on level-triggered buses. See Isr() for details. */
962 if(IoConnectInterrupt(&gControllerInfo
[i
].InterruptObject
, Isr
, &gControllerInfo
[i
], 0, gControllerInfo
[i
].MappedVector
,
963 gControllerInfo
[i
].MappedLevel
, gControllerInfo
[i
].MappedLevel
, gControllerInfo
[i
].InterruptMode
,
964 FALSE
, Affinity
, 0) != STATUS_SUCCESS
)
966 WARN_(FLOPPY
, "AddControllers: unable to connect interrupt\n");
971 memset(&DeviceDescription
, 0, sizeof(DeviceDescription
));
972 DeviceDescription
.Version
= DEVICE_DESCRIPTION_VERSION
;
973 DeviceDescription
.DmaChannel
= gControllerInfo
[i
].Dma
;
974 DeviceDescription
.InterfaceType
= gControllerInfo
[i
].InterfaceType
;
975 DeviceDescription
.BusNumber
= gControllerInfo
[i
].BusNumber
;
976 DeviceDescription
.MaximumLength
= 2*18*512; /* based on a 1.44MB floppy */
978 /* DMA 0,1,2,3 are 8-bit; 4,5,6,7 are 16-bit (4 is chain i think) */
979 DeviceDescription
.DmaWidth
= gControllerInfo
[i
].Dma
> 3 ? Width16Bits
: Width8Bits
;
981 gControllerInfo
[i
].AdapterObject
= HalGetAdapter(&DeviceDescription
, &gControllerInfo
[i
].MapRegisters
);
983 if(!gControllerInfo
[i
].AdapterObject
)
985 WARN_(FLOPPY
, "AddControllers: unable to allocate an adapter object\n");
986 IoDisconnectInterrupt(gControllerInfo
[i
].InterruptObject
);
990 /* 2b: Initialize the new controller */
991 if(InitController(&gControllerInfo
[i
]) != STATUS_SUCCESS
)
993 WARN_(FLOPPY
, "AddControllers(): Unable to set up controller %d - initialization failed\n", i
);
994 IoDisconnectInterrupt(gControllerInfo
[i
].InterruptObject
);
998 /* 2c: Set the controller's initialized flag so we know to release stuff in Unload */
999 gControllerInfo
[i
].Initialized
= TRUE
;
1001 /* 3: per-drive setup */
1002 for(j
= 0; j
< gControllerInfo
[i
].NumberOfDrives
; j
++)
1004 UNICODE_STRING DeviceName
;
1005 UNICODE_STRING ArcPath
;
1008 INFO_(FLOPPY
, "AddControllers(): Configuring drive %d on controller %d\n", i
, j
);
1011 * 3a: create a device object for the drive
1012 * Controllers and drives are 0-based, so the combos are:
1024 DriveNumber
= (UCHAR
)(i
*4 + j
); /* loss of precision is OK; there are only 16 of 'em */
1026 swprintf(gControllerInfo
[i
].DriveInfo
[j
].DeviceNameBuffer
, L
"\\Device\\Floppy%d", DriveNumber
);
1027 RtlInitUnicodeString(&DeviceName
, gControllerInfo
[i
].DriveInfo
[j
].DeviceNameBuffer
);
1029 if(IoCreateDevice(DriverObject
, sizeof(PVOID
), &DeviceName
,
1030 FILE_DEVICE_DISK
, FILE_REMOVABLE_MEDIA
| FILE_FLOPPY_DISKETTE
, FALSE
,
1031 &gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
) != STATUS_SUCCESS
)
1033 WARN_(FLOPPY
, "AddControllers: unable to register a Device object\n");
1034 IoDisconnectInterrupt(gControllerInfo
[i
].InterruptObject
);
1035 continue; /* continue on to next drive */
1038 INFO_(FLOPPY
, "AddControllers: New device: %S (0x%p)\n",
1039 gControllerInfo
[i
].DriveInfo
[j
].DeviceNameBuffer
,
1040 gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
);
1042 /* 3b.5: Create an ARC path in case we're booting from this drive */
1043 swprintf(gControllerInfo
[i
].DriveInfo
[j
].ArcPathBuffer
,
1044 L
"\\ArcName\\multi(%d)disk(%d)fdisk(%d)", gControllerInfo
[i
].BusNumber
, i
, DriveNumber
);
1046 RtlInitUnicodeString(&ArcPath
, gControllerInfo
[i
].DriveInfo
[j
].ArcPathBuffer
);
1047 IoAssignArcName(&ArcPath
, &DeviceName
);
1049 /* 3c: Set flags up */
1050 gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
->Flags
|= DO_DIRECT_IO
;
1052 /* 3d: Increase global floppy drives count */
1053 IoGetConfigurationInformation()->FloppyCount
++;
1055 /* 3e: Set up the DPC */
1056 IoInitializeDpcRequest(gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
, (PIO_DPC_ROUTINE
)DpcForIsr
);
1058 /* 3f: Point the device extension at our DriveInfo struct */
1059 gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
->DeviceExtension
= &gControllerInfo
[i
].DriveInfo
[j
];
1061 /* 3g: neat comic strip */
1063 /* 3h: set the initial media type to unknown */
1064 memset(&gControllerInfo
[i
].DriveInfo
[j
].DiskGeometry
, 0, sizeof(DISK_GEOMETRY
));
1065 gControllerInfo
[i
].DriveInfo
[j
].DiskGeometry
.MediaType
= Unknown
;
1067 /* 3i: Now that we're done, set the Initialized flag so we know to free this in Unload */
1068 gControllerInfo
[i
].DriveInfo
[j
].Initialized
= TRUE
;
1070 /* 3j: Clear the DO_DEVICE_INITIALIZING flag */
1071 gControllerInfo
[i
].DriveInfo
[j
].DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
1073 /* 3k: Report to the MountMgr */
1074 ReportToMountMgr(i
, j
);
1076 /* 3l: Attempt to get drive info - if a floppy is already present */
1077 StartMotor(&gControllerInfo
[i
].DriveInfo
[j
]);
1078 RWDetermineMediaType(&gControllerInfo
[i
].DriveInfo
[j
], TRUE
);
1079 StopMotor(gControllerInfo
[i
].DriveInfo
[j
].ControllerInfo
);
1083 INFO_(FLOPPY
, "AddControllers: --------------------------------------------> finished adding controllers\n");
1085 return (IoGetConfigurationInformation()->FloppyCount
!= 0);
1090 SignalMediaChanged(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
1092 * FUNCTION: Process an IRP when the media has changed, and possibly notify the user
1094 * DeviceObject: DeviceObject associated with the IRP
1095 * Irp: IRP that we're failing due to change
1097 * - This procedure is documented in the DDK by "Notifying the File System of Possible Media Changes",
1098 * "IoSetHardErrorOrVerifyDevice", and by "Responding to Check-Verify Requests from the File System".
1099 * - Callable at <= DISPATCH_LEVEL
1102 PDRIVE_INFO DriveInfo
= DeviceObject
->DeviceExtension
;
1104 TRACE_(FLOPPY
, "SignalMediaChanged called\n");
1106 DriveInfo
->DiskChangeCount
++;
1108 /* If volume is not mounted, do NOT set verify and return STATUS_IO_DEVICE_ERROR */
1109 if(!(DeviceObject
->Vpb
->Flags
& VPB_MOUNTED
))
1111 Irp
->IoStatus
.Status
= STATUS_IO_DEVICE_ERROR
;
1112 Irp
->IoStatus
.Information
= 0;
1116 /* Notify the filesystem that it will need to verify the volume */
1117 DeviceObject
->Flags
|= DO_VERIFY_VOLUME
;
1118 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
1119 Irp
->IoStatus
.Information
= 0;
1122 * If this is a user-based, threaded request, let the IO manager know to pop up a box asking
1123 * the user to supply the correct media, but only if the error (which we just picked out above)
1124 * is deemed by the IO manager to be "user induced". The reason we don't just unconditionally
1125 * call IoSetHardError... is because MS might change the definition of "user induced" some day,
1126 * and we don't want to have to remember to re-code this.
1128 if(Irp
->Tail
.Overlay
.Thread
&& IoIsErrorUserInduced(Irp
->IoStatus
.Status
))
1129 IoSetHardErrorOrVerifyDevice(Irp
, DeviceObject
);
1134 QueueThread(PVOID Context
)
1136 * FUNCTION: Thread that manages the queue and dispatches any queued requests
1142 PIO_STACK_LOCATION Stack
;
1143 PDEVICE_OBJECT DeviceObject
;
1147 UNREFERENCED_PARAMETER(Context
);
1149 Objects
[0] = &QueueSemaphore
;
1150 Objects
[1] = &QueueThreadTerminate
;
1154 KeWaitForMultipleObjects(2, Objects
, WaitAny
, Executive
, KernelMode
, FALSE
, NULL
, NULL
);
1156 if(KeReadStateEvent(&QueueThreadTerminate
))
1158 INFO_(FLOPPY
, "QueueThread terminating\n");
1162 INFO_(FLOPPY
, "QueueThread: servicing an IRP\n");
1164 Irp
= IoCsqRemoveNextIrp(&Csq
, 0);
1166 /* we won't get an irp if it was canceled */
1169 INFO_(FLOPPY
, "QueueThread: IRP queue empty\n");
1173 DeviceObject
= (PDEVICE_OBJECT
)Irp
->Tail
.Overlay
.DriverContext
[0];
1175 ASSERT(DeviceObject
);
1177 Stack
= IoGetCurrentIrpStackLocation(Irp
);
1179 /* Decide what to do with the IRP */
1180 switch(Stack
->MajorFunction
)
1184 ReadWritePassive(DeviceObject
->DeviceExtension
, Irp
);
1187 case IRP_MJ_DEVICE_CONTROL
:
1188 DeviceIoctlPassive(DeviceObject
->DeviceExtension
, Irp
);
1192 WARN_(FLOPPY
, "QueueThread(): Unrecognized irp: mj: 0x%x\n", Stack
->MajorFunction
);
1193 Irp
->IoStatus
.Status
= STATUS_NOT_SUPPORTED
;
1194 Irp
->IoStatus
.Information
= 0;
1195 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
1202 DriverEntry(PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
)
1204 * FUNCTION: Entry-point for the driver
1206 * DriverObject: Our driver object
1207 * RegistryPath: Unused
1209 * STATUS_SUCCESS on successful initialization of at least one drive
1210 * STATUS_NO_SUCH_DEVICE if we didn't find even one drive
1211 * STATUS_UNSUCCESSFUL otherwise
1214 HANDLE ThreadHandle
;
1216 UNREFERENCED_PARAMETER(RegistryPath
);
1219 * Set up dispatch routines
1221 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)CreateClose
;
1222 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)CreateClose
;
1223 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)ReadWrite
;
1224 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)ReadWrite
;
1225 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)DeviceIoctl
;
1227 DriverObject
->DriverUnload
= Unload
;
1230 * We depend on some zeroes in these structures. I know this is supposed to be
1231 * initialized to 0 by the complier but this makes me feel beter.
1233 memset(&gControllerInfo
, 0, sizeof(gControllerInfo
));
1236 * Set up queue. This routine cannot fail (trust me, I wrote it).
1238 IoCsqInitialize(&Csq
, CsqInsertIrp
, CsqRemoveIrp
, CsqPeekNextIrp
,
1239 CsqAcquireLock
, CsqReleaseLock
, CsqCompleteCanceledIrp
);
1244 KeInitializeSpinLock(&IrpQueueLock
);
1247 * ...and the queue list itself
1249 InitializeListHead(&IrpQueue
);
1252 * The queue is counted by a semaphore. The queue management thread
1253 * blocks on this semaphore, so if requests come in faster than the queue
1254 * thread can handle them, the semaphore count goes up.
1256 KeInitializeSemaphore(&QueueSemaphore
, 0, 0x7fffffff);
1259 * Event to terminate that thread
1261 KeInitializeEvent(&QueueThreadTerminate
, NotificationEvent
, FALSE
);
1264 * Create the queue processing thread. Save its handle in the global variable
1265 * ThreadHandle so we can wait on its termination during Unload.
1267 if(PsCreateSystemThread(&ThreadHandle
, THREAD_ALL_ACCESS
, 0, 0, 0, QueueThread
, 0) != STATUS_SUCCESS
)
1269 WARN_(FLOPPY
, "Unable to create system thread; failing init\n");
1270 return STATUS_INSUFFICIENT_RESOURCES
;
1273 if(ObReferenceObjectByHandle(ThreadHandle
, STANDARD_RIGHTS_ALL
, *PsThreadType
, KernelMode
, &QueueThreadObject
, NULL
) != STATUS_SUCCESS
)
1275 WARN_(FLOPPY
, "Unable to reference returned thread handle; failing init\n");
1276 return STATUS_UNSUCCESSFUL
;
1280 * Close the handle, now that we have the object pointer and a reference of our own.
1281 * The handle will certainly not be valid in the context of the caller next time we
1282 * need it, as handles are process-specific.
1284 ZwClose(ThreadHandle
);
1287 * Start the device discovery process. Returns STATUS_SUCCESS if
1288 * it finds even one drive attached to one controller.
1290 if(!AddControllers(DriverObject
))
1291 return STATUS_NO_SUCH_DEVICE
;
1293 return STATUS_SUCCESS
;