xref: /reactos/drivers/storage/floppy/floppy/floppy.c (revision 4f410007)
1 /*
2  *  ReactOS Floppy Driver
3  *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
4  *
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.
9  *
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.
14  *
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.
18  *
19  * PROJECT:         ReactOS Floppy Driver
20  * FILE:            floppy.c
21  * PURPOSE:         Main floppy driver routines
22  * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
23  * REVISIONS:
24  *                  15-Feb-2004 vizzini - Created
25  * NOTES:
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.
29  *
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...)
35  *
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
41  */
42 
43 #include "precomp.h"
44 
45 #include <ntddk.h>
46 #include <debug.h>
47 
48 #include "ioctl.h"
49 #include "readwrite.h"
50 
51 /*
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.
55  */
56 static CONTROLLER_INFO gControllerInfo[MAX_CONTROLLERS];
57 static ULONG gNumberOfControllers = 0;
58 
59 /* Queue thread management */
60 static KEVENT QueueThreadTerminate;
61 static PVOID QueueThreadObject;
62 
63 
64 static VOID NTAPI
MotorStopDpcFunc(PKDPC UnusedDpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2)65 MotorStopDpcFunc(PKDPC UnusedDpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
66 /*
67  * FUNCTION: Stop the floppy motor
68  * ARGUMENTS:
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
73  * NOTES:
74  *     - Must set an event to let other threads know we're done turning off the motor
75  *     - Called back at DISPATCH_LEVEL
76  */
77 {
78     PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)DeferredContext;
79 
80     UNREFERENCED_PARAMETER(SystemArgument1);
81     UNREFERENCED_PARAMETER(SystemArgument2);
82     UNREFERENCED_PARAMETER(UnusedDpc);
83 
84     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
85     ASSERT(ControllerInfo);
86 
87     TRACE_(FLOPPY, "MotorStopDpcFunc called\n");
88 
89     HwTurnOffMotor(ControllerInfo);
90     ControllerInfo->StopDpcQueued = FALSE;
91     KeSetEvent(&ControllerInfo->MotorStoppedEvent, EVENT_INCREMENT, FALSE);
92 }
93 
94 
95 VOID NTAPI
StartMotor(PDRIVE_INFO DriveInfo)96 StartMotor(PDRIVE_INFO DriveInfo)
97 /*
98  * FUNCTION: Start the motor, taking into account proper handling of the timer race
99  * ARGUMENTS:
100  *     DriveInfo: drive to start
101  * NOTES:
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
110  */
111 {
112     PAGED_CODE();
113     ASSERT(DriveInfo);
114 
115     TRACE_(FLOPPY, "StartMotor called\n");
116 
117     if(DriveInfo->ControllerInfo->StopDpcQueued && !KeCancelTimer(&DriveInfo->ControllerInfo->MotorTimer))
118     {
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");
123     }
124 
125     DriveInfo->ControllerInfo->StopDpcQueued = FALSE;
126 
127     if(HwTurnOnMotor(DriveInfo) != STATUS_SUCCESS)
128     {
129         WARN_(FLOPPY, "StartMotor(): warning: HwTurnOnMotor failed\n");
130     }
131 }
132 
133 
134 VOID NTAPI
StopMotor(PCONTROLLER_INFO ControllerInfo)135 StopMotor(PCONTROLLER_INFO ControllerInfo)
136 /*
137  * FUNCTION: Stop all motors on the controller
138  * ARGUMENTS:
139  *     DriveInfo: Drive to stop
140  * NOTES:
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.
144  */
145 {
146     LARGE_INTEGER StopTime;
147 
148     ASSERT(ControllerInfo);
149 
150     TRACE_(FLOPPY, "StopMotor called\n");
151 
152     /* one relative second, in 100-ns units */
153     StopTime.QuadPart = 10000000;
154     StopTime.QuadPart *= -1;
155 
156     KeClearEvent(&ControllerInfo->MotorStoppedEvent);
157     KeSetTimer(&ControllerInfo->MotorTimer, StopTime, &ControllerInfo->MotorStopDpc);
158     ControllerInfo->StopDpcQueued = TRUE;
159 }
160 
161 
162 NTSTATUS NTAPI
WaitForControllerInterrupt(PCONTROLLER_INFO ControllerInfo,PLARGE_INTEGER Timeout)163 WaitForControllerInterrupt(PCONTROLLER_INFO ControllerInfo, PLARGE_INTEGER Timeout)
164 /*
165  * FUNCTION: Wait for the controller to interrupt, and then clear the event
166  * ARGUMENTS:
167  *     ControllerInfo: Controller to wait for
168  *     Timeout: How long to wait for
169  * NOTES:
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
175  */
176 {
177     NTSTATUS Status;
178 
179     PAGED_CODE();
180     ASSERT(ControllerInfo);
181 
182     Status = KeWaitForSingleObject(&ControllerInfo->SynchEvent, Executive, KernelMode, FALSE, Timeout);
183     KeClearEvent(&ControllerInfo->SynchEvent);
184 
185     return Status;
186 }
187 
188 static DRIVER_DISPATCH CreateClose;
CreateClose(PDEVICE_OBJECT DeviceObject,PIRP Irp)189 static NTSTATUS NTAPI CreateClose(PDEVICE_OBJECT DeviceObject,
190                                   PIRP Irp)
191 /*
192  * FUNCTION: Dispatch function called for Create and Close IRPs
193  * ARGUMENTS:
194  *     DeviceObject: DeviceObject that is the target of the IRP
195  *     Irp: IRP to process
196  * RETURNS:
197  *     STATUS_SUCCESS in all cases
198  * NOTES:
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
203  *
204  * TODO: Figure out why this isn't getting called
205  */
206 {
207     UNREFERENCED_PARAMETER(DeviceObject);
208 
209     TRACE_(FLOPPY, "CreateClose called\n");
210 
211     Irp->IoStatus.Status = STATUS_SUCCESS;
212     Irp->IoStatus.Information = FILE_OPENED;
213 
214     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
215 
216     return STATUS_SUCCESS;
217 }
218 
219 
220 static NTSTATUS NTAPI
Recalibrate(PDRIVE_INFO DriveInfo)221 Recalibrate(PDRIVE_INFO DriveInfo)
222 /*
223  * FUNCTION: Start the recalibration process
224  * ARGUMENTS:
225  *     DriveInfo: Pointer to the driveinfo struct associated with the targeted drive
226  * RETURNS:
227  *     STATUS_SUCCESS on successful starting of the process
228  *     STATUS_IO_DEVICE_ERROR if it fails
229  * NOTES:
230  *     - Sometimes you have to do two recalibrations, particularly if the disk has <80 tracks.
231  *     - PAGED_CODE because we wait
232  */
233 {
234     NTSTATUS Status;
235     ULONG i;
236 
237     PAGED_CODE();
238     ASSERT(DriveInfo);
239 
240     /* first turn on the motor */
241     /* Must stop after every start, prior to return */
242     StartMotor(DriveInfo);
243 
244     /* set the data rate */
245     WARN_(FLOPPY, "FIXME: UN-HARDCODE DATA RATE\n");
246     if(HwSetDataRate(DriveInfo->ControllerInfo, 0) != STATUS_SUCCESS)
247     {
248         WARN_(FLOPPY, "Recalibrate: HwSetDataRate failed\n");
249         StopMotor(DriveInfo->ControllerInfo);
250         return STATUS_IO_DEVICE_ERROR;
251     }
252 
253     /* clear the event just in case the last call forgot */
254     KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
255 
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.
259      */
260     for(i = 0; i < 2; i++)
261     {
262         /* Send the command */
263         Status = HwRecalibrate(DriveInfo);
264         if(Status != STATUS_SUCCESS)
265         {
266             WARN_(FLOPPY, "Recalibrate: HwRecalibrate returned error\n");
267             continue;
268         }
269 
270         WaitForControllerInterrupt(DriveInfo->ControllerInfo, NULL);
271 
272         /* Get the results */
273         Status = HwRecalibrateResult(DriveInfo->ControllerInfo);
274         if(Status != STATUS_SUCCESS)
275         {
276             WARN_(FLOPPY, "Recalibrate: HwRecalibrateResult returned error\n");
277             break;
278         }
279     }
280 
281     KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
282 
283     /* Must stop after every start, prior to return */
284     StopMotor(DriveInfo->ControllerInfo);
285 
286     return Status;
287 }
288 
289 
290 NTSTATUS NTAPI
ResetChangeFlag(PDRIVE_INFO DriveInfo)291 ResetChangeFlag(PDRIVE_INFO DriveInfo)
292 /*
293  * FUNCTION: Reset the drive's change flag (as reflected in the DIR)
294  * ARGUMENTS:
295  *     DriveInfo: the drive to reset
296  * RETURNS:
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
300  * NOTES:
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
305  */
306 {
307     BOOLEAN DiskChanged;
308 
309     PAGED_CODE();
310     ASSERT(DriveInfo);
311 
312     TRACE_(FLOPPY, "ResetChangeFlag called\n");
313 
314     /* Try to recalibrate.  We don't care if it works. */
315     Recalibrate(DriveInfo);
316 
317     /* clear spurious interrupts in prep for seeks */
318     KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
319 
320     /* must re-start the drive because Recalibrate() stops it */
321     StartMotor(DriveInfo);
322 
323     /* Seek to 1 */
324     if(HwSeek(DriveInfo, 1) != STATUS_SUCCESS)
325     {
326         WARN_(FLOPPY, "ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n");
327         StopMotor(DriveInfo->ControllerInfo);
328         return STATUS_IO_DEVICE_ERROR;
329     }
330 
331     WaitForControllerInterrupt(DriveInfo->ControllerInfo, NULL);
332 
333     if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
334     {
335         WARN_(FLOPPY, "ResetChangeFlag(): HwSenseInterruptStatus failed; bailing out\n");
336         StopMotor(DriveInfo->ControllerInfo);
337         return STATUS_IO_DEVICE_ERROR;
338     }
339 
340     /* Seek back to 0 */
341     if(HwSeek(DriveInfo, 0) != STATUS_SUCCESS)
342     {
343         WARN_(FLOPPY, "ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n");
344         StopMotor(DriveInfo->ControllerInfo);
345         return STATUS_IO_DEVICE_ERROR;
346     }
347 
348     WaitForControllerInterrupt(DriveInfo->ControllerInfo, NULL);
349 
350     if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
351     {
352         WARN_(FLOPPY, "ResetChangeFlag(): HwSenseInterruptStatus #2 failed; bailing\n");
353         StopMotor(DriveInfo->ControllerInfo);
354         return STATUS_IO_DEVICE_ERROR;
355     }
356 
357     /* Check the change bit */
358     if(HwDiskChanged(DriveInfo, &DiskChanged) != STATUS_SUCCESS)
359     {
360         WARN_(FLOPPY, "ResetChangeFlag(): HwDiskChanged failed; returning STATUS_IO_DEVICE_ERROR\n");
361         StopMotor(DriveInfo->ControllerInfo);
362         return STATUS_IO_DEVICE_ERROR;
363     }
364 
365     StopMotor(DriveInfo->ControllerInfo);
366 
367     /* if the change flag is still set, there's probably no media in the drive. */
368     if(DiskChanged)
369         return STATUS_NO_MEDIA_IN_DEVICE;
370 
371     /* else we're done! */
372     return STATUS_SUCCESS;
373 }
374 
375 
376 static VOID NTAPI
Unload(PDRIVER_OBJECT DriverObject)377 Unload(PDRIVER_OBJECT DriverObject)
378 /*
379  * FUNCTION: Unload the driver from memory
380  * ARGUMENTS:
381  *     DriverObject - The driver that is being unloaded
382  */
383 {
384     ULONG i,j;
385 
386     PAGED_CODE();
387     UNREFERENCED_PARAMETER(DriverObject);
388 
389     TRACE_(FLOPPY, "unloading\n");
390 
391     KeSetEvent(&QueueThreadTerminate, 0, FALSE);
392     KeWaitForSingleObject(QueueThreadObject, Executive, KernelMode, FALSE, 0);
393     ObDereferenceObject(QueueThreadObject);
394 
395     for(i = 0; i < gNumberOfControllers; i++)
396     {
397         if(!gControllerInfo[i].Initialized)
398             continue;
399 
400         for(j = 0; j < gControllerInfo[i].NumberOfDrives; j++)
401         {
402             if(!gControllerInfo[i].DriveInfo[j].Initialized)
403                 continue;
404 
405             if(gControllerInfo[i].DriveInfo[j].DeviceObject)
406             {
407                 UNICODE_STRING Link;
408 
409                 RtlInitUnicodeString(&Link, gControllerInfo[i].DriveInfo[j].ArcPathBuffer);
410                 IoDeassignArcName(&Link);
411 
412                 IoDeleteDevice(gControllerInfo[i].DriveInfo[j].DeviceObject);
413             }
414         }
415 
416         IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
417 
418         /* Power down the controller */
419         if(HwPowerOff(&gControllerInfo[i]) != STATUS_SUCCESS)
420         {
421             WARN_(FLOPPY, "unload: warning: HwPowerOff failed\n");
422         }
423     }
424 }
425 
426 
427 static NTSTATUS NTAPI
ConfigCallback(PVOID Context,PUNICODE_STRING PathName,INTERFACE_TYPE BusType,ULONG BusNumber,PKEY_VALUE_FULL_INFORMATION * BusInformation,CONFIGURATION_TYPE ControllerType,ULONG ControllerNumber,PKEY_VALUE_FULL_INFORMATION * ControllerInformation,CONFIGURATION_TYPE PeripheralType,ULONG PeripheralNumber,PKEY_VALUE_FULL_INFORMATION * PeripheralInformation)428 ConfigCallback(PVOID Context,
429                PUNICODE_STRING PathName,
430                INTERFACE_TYPE BusType,
431                ULONG BusNumber,
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)
439 /*
440  * FUNCTION: Callback to IoQueryDeviceDescription, which tells us about our controllers
441  * ARGUMENTS:
442  *     Context: Unused
443  *     PathName: Unused
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
453  * RETURNS:
454  *     STATUS_SUCCESS in all cases
455  * NOTES:
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
467  *       drives.
468  */
469 {
470     PKEY_VALUE_FULL_INFORMATION ControllerFullDescriptor = ControllerInformation[IoQueryDeviceConfigurationData];
471     PCM_FULL_RESOURCE_DESCRIPTOR ControllerResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PCHAR)ControllerFullDescriptor +
472             ControllerFullDescriptor->DataOffset);
473 
474     PKEY_VALUE_FULL_INFORMATION PeripheralFullDescriptor = PeripheralInformation[IoQueryDeviceConfigurationData];
475     PCM_FULL_RESOURCE_DESCRIPTOR PeripheralResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PCHAR)PeripheralFullDescriptor +
476             PeripheralFullDescriptor->DataOffset);
477 
478     PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
479     PCM_FLOPPY_DEVICE_DATA FloppyDeviceData;
480     UCHAR i;
481 
482     PAGED_CODE();
483     UNREFERENCED_PARAMETER(PeripheralType);
484     UNREFERENCED_PARAMETER(PeripheralNumber);
485     UNREFERENCED_PARAMETER(BusInformation);
486     UNREFERENCED_PARAMETER(Context);
487     UNREFERENCED_PARAMETER(ControllerType);
488     UNREFERENCED_PARAMETER(PathName);
489 
490 
491     TRACE_(FLOPPY, "ConfigCallback called with ControllerNumber %d\n", ControllerNumber);
492 
493     gControllerInfo[gNumberOfControllers].ControllerNumber = ControllerNumber;
494     gControllerInfo[gNumberOfControllers].InterfaceType = BusType;
495     gControllerInfo[gNumberOfControllers].BusNumber = BusNumber;
496 
497     /* Get controller interrupt level/vector, dma channel, and port base */
498     for(i = 0; i < ControllerResourceDescriptor->PartialResourceList.Count; i++)
499     {
500         KeInitializeEvent(&gControllerInfo[gNumberOfControllers].SynchEvent, NotificationEvent, FALSE);
501 
502         PartialDescriptor = &ControllerResourceDescriptor->PartialResourceList.PartialDescriptors[i];
503 
504         if(PartialDescriptor->Type == CmResourceTypeInterrupt)
505         {
506             gControllerInfo[gNumberOfControllers].Level = PartialDescriptor->u.Interrupt.Level;
507             gControllerInfo[gNumberOfControllers].Vector = PartialDescriptor->u.Interrupt.Vector;
508 
509             if(PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
510                 gControllerInfo[gNumberOfControllers].InterruptMode = Latched;
511             else
512                 gControllerInfo[gNumberOfControllers].InterruptMode = LevelSensitive;
513         }
514 
515         else if(PartialDescriptor->Type == CmResourceTypePort)
516         {
517             PHYSICAL_ADDRESS TranslatedAddress;
518             ULONG AddressSpace = 0x1; /* I/O Port Range */
519 
520             if(!HalTranslateBusAddress(BusType, BusNumber, PartialDescriptor->u.Port.Start, &AddressSpace, &TranslatedAddress))
521             {
522                 WARN_(FLOPPY, "HalTranslateBusAddress failed; returning\n");
523                 return STATUS_IO_DEVICE_ERROR;
524             }
525 
526             if(AddressSpace == 0)
527                 gControllerInfo[gNumberOfControllers].BaseAddress = MmMapIoSpace(TranslatedAddress, FDC_PORT_BYTES, MmNonCached);
528             else
529                 gControllerInfo[gNumberOfControllers].BaseAddress = (PUCHAR)(ULONG_PTR)TranslatedAddress.QuadPart;
530         }
531 
532         else if(PartialDescriptor->Type == CmResourceTypeDma)
533             gControllerInfo[gNumberOfControllers].Dma = PartialDescriptor->u.Dma.Channel;
534     }
535 
536     /* Start with 0 drives, then go looking */
537     gControllerInfo[gNumberOfControllers].NumberOfDrives = 0;
538 
539     /* learn about drives attached to controller */
540     for(i = 0; i < PeripheralResourceDescriptor->PartialResourceList.Count; i++)
541     {
542         PDRIVE_INFO DriveInfo = &gControllerInfo[gNumberOfControllers].DriveInfo[i];
543 
544         PartialDescriptor = &PeripheralResourceDescriptor->PartialResourceList.PartialDescriptors[i];
545 
546         if(PartialDescriptor->Type != CmResourceTypeDeviceSpecific)
547             continue;
548 
549         FloppyDeviceData = (PCM_FLOPPY_DEVICE_DATA)(PartialDescriptor + 1);
550 
551         DriveInfo->ControllerInfo = &gControllerInfo[gNumberOfControllers];
552         DriveInfo->UnitNumber = i;
553 
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;
568 
569         /* Once it's all set up, acknowledge its existence in the controller info object */
570         gControllerInfo[gNumberOfControllers].NumberOfDrives++;
571     }
572 
573     gControllerInfo[gNumberOfControllers].Populated = TRUE;
574     gNumberOfControllers++;
575 
576     return STATUS_SUCCESS;
577 }
578 
579 
580 static BOOLEAN NTAPI
Isr(PKINTERRUPT Interrupt,PVOID ServiceContext)581 Isr(PKINTERRUPT Interrupt, PVOID ServiceContext)
582 /*
583  * FUNCTION: Interrupt service routine for the controllers
584  * ARGUMENTS:
585  *     Interrupt: Interrupt object representing the interrupt that occured
586  *     ServiceContext: Pointer to the ControllerInfo object that caused the interrupt
587  * RETURNS:
588  *     TRUE in all cases (see notes)
589  * 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.
605  *     - Called at DIRQL
606  */
607 {
608     PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)ServiceContext;
609 
610     UNREFERENCED_PARAMETER(Interrupt);
611 
612     ASSERT(ControllerInfo);
613 
614     TRACE_(FLOPPY, "ISR called\n");
615 
616     /*
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.
622      */
623     KeInsertQueueDpc(&ControllerInfo->Dpc, NULL, NULL);
624 
625     return TRUE;
626 }
627 
628 
629 VOID NTAPI
DpcForIsr(PKDPC UnusedDpc,PVOID Context,PVOID SystemArgument1,PVOID SystemArgument2)630 DpcForIsr(PKDPC UnusedDpc, PVOID Context, PVOID SystemArgument1, PVOID SystemArgument2)
631 /*
632  * FUNCTION: This DPC gets queued by every ISR.  Does the real per-interrupt work.
633  * ARGUMENTS:
634  *     UnusedDpc: Pointer to the DPC object that represents our function
635  *     DeviceObject: Device that this DPC is running for
636  *     Irp: Unused
637  *     Context: Pointer to our ControllerInfo struct
638  * NOTES:
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
646  */
647 {
648     PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)Context;
649 
650     UNREFERENCED_PARAMETER(UnusedDpc);
651     UNREFERENCED_PARAMETER(SystemArgument1);
652     UNREFERENCED_PARAMETER(SystemArgument2);
653 
654     ASSERT(ControllerInfo);
655 
656     TRACE_(FLOPPY, "DpcForIsr called\n");
657 
658     KeSetEvent(&ControllerInfo->SynchEvent, EVENT_INCREMENT, FALSE);
659 }
660 
661 
662 static NTSTATUS NTAPI
InitController(PCONTROLLER_INFO ControllerInfo)663 InitController(PCONTROLLER_INFO ControllerInfo)
664 /*
665  * FUNCTION:  Initialize a newly-found controller
666  * ARGUMENTS:
667  *     ControllerInfo: pointer to the controller to be initialized
668  * RETURNS:
669  *     STATUS_SUCCESS if the controller is successfully initialized
670  *     STATUS_IO_DEVICE_ERROR otherwise
671  */
672 {
673     int i;
674     UCHAR HeadLoadTime;
675     UCHAR HeadUnloadTime;
676     UCHAR StepRateTime;
677     UCHAR ControllerVersion;
678 
679     PAGED_CODE();
680     ASSERT(ControllerInfo);
681 
682     TRACE_(FLOPPY, "InitController called with Controller 0x%p\n", ControllerInfo);
683 
684     /* Get controller in a known state */
685     if(HwConfigure(ControllerInfo, FALSE, TRUE, TRUE, 0, 0) != STATUS_SUCCESS)
686     {
687         WARN_(FLOPPY, "InitController: unable to configure controller\n");
688         return STATUS_IO_DEVICE_ERROR;
689     }
690 
691     /* Get the controller version */
692     ControllerVersion = HwGetVersion(ControllerInfo);
693 
694     KeClearEvent(&ControllerInfo->SynchEvent);
695 
696     /* Reset the controller */
697     if(HwReset(ControllerInfo) != STATUS_SUCCESS)
698     {
699         WARN_(FLOPPY, "InitController: unable to reset controller\n");
700         return STATUS_IO_DEVICE_ERROR;
701     }
702 
703     INFO_(FLOPPY, "InitController: waiting for initial interrupt\n");
704 
705     /* Wait for an interrupt */
706     WaitForControllerInterrupt(ControllerInfo, NULL);
707 
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++)
710     {
711         INFO_(FLOPPY, "InitController: Sensing interrupt %d\n", i);
712 
713         if(HwSenseInterruptStatus(ControllerInfo) != STATUS_SUCCESS)
714         {
715             WARN_(FLOPPY, "InitController: Unable to clear interrupt 0x%x\n", i);
716             return STATUS_IO_DEVICE_ERROR;
717         }
718     }
719 
720     INFO_(FLOPPY, "InitController: done sensing interrupts\n");
721 
722     /* Next, see if we have the right version to do implied seek */
723     if(ControllerVersion == VERSION_ENHANCED)
724     {
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)
727         {
728             WARN_(FLOPPY, "InitController: unable to set up implied seek\n");
729             ControllerInfo->ImpliedSeeks = FALSE;
730         }
731         else
732         {
733             INFO_(FLOPPY, "InitController: implied seeks set!\n");
734             ControllerInfo->ImpliedSeeks = TRUE;
735         }
736 
737         /*
738          * FIXME: Figure out the answer to the below
739          *
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:
744          *  - IBM Thinkpad 750
745          *  - IBM PS2e
746          *
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.
749          *
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.
754          */
755         //ControllerInfo->Model30 = TRUE;
756         ControllerInfo->Model30 = FALSE;
757     }
758     else
759     {
760         INFO_(FLOPPY, "InitController: enhanced version not supported; disabling implied seeks\n");
761         ControllerInfo->ImpliedSeeks = FALSE;
762         ControllerInfo->Model30 = FALSE;
763     }
764 
765     /* Specify */
766     WARN_(FLOPPY, "FIXME: Figure out speed\n");
767     HeadLoadTime = SPECIFY_HLT_500K;
768     HeadUnloadTime = SPECIFY_HUT_500K;
769     StepRateTime = SPECIFY_SRT_500K;
770 
771     INFO_(FLOPPY, "InitController: setting data rate\n");
772 
773     /* Set data rate */
774     if(HwSetDataRate(ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
775     {
776         WARN_(FLOPPY, "InitController: unable to set data rate\n");
777         return STATUS_IO_DEVICE_ERROR;
778     }
779 
780     INFO_(FLOPPY, "InitController: issuing specify command to controller\n");
781 
782     /* Don't disable DMA --> enable dma (dumb & confusing) */
783     if(HwSpecify(ControllerInfo, HeadLoadTime, HeadUnloadTime, StepRateTime, FALSE) != STATUS_SUCCESS)
784     {
785         WARN_(FLOPPY, "InitController: unable to specify options\n");
786         return STATUS_IO_DEVICE_ERROR;
787     }
788 
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;
794 
795     /*
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.
798      */
799     for(i = 0; i < ControllerInfo->NumberOfDrives; i++)
800     {
801         INFO_(FLOPPY, "InitController: recalibrating drive 0x%x on controller 0x%p\n", i, ControllerInfo);
802         Recalibrate(&ControllerInfo->DriveInfo[i]);
803     }
804 
805     INFO_(FLOPPY, "InitController: done initializing; returning STATUS_SUCCESS\n");
806 
807     return STATUS_SUCCESS;
808 }
809 
810 
811 static VOID NTAPI
ReportToMountMgr(UCHAR ControlerId,UCHAR DriveId)812 ReportToMountMgr(UCHAR ControlerId, UCHAR DriveId)
813 /*
814  * FUNCTION: Called to report a new controler to the MountMgr
815  * ARGUMENTS:
816  *     ControlerId: ID of the controler
817  *     DriveId: ID of the device for the controler
818  * RETURNS:
819  *     Nothing
820  * NOTES:
821  *     - This is a hack to allow MountMgr handling our devices
822  */
823 {
824     NTSTATUS              Status;
825     UNICODE_STRING        MountMgrDevice;
826     PDEVICE_OBJECT        DeviceObject;
827     PFILE_OBJECT          FileObject;
828     PMOUNTMGR_TARGET_NAME MountTarget;
829     ULONG                 DeviceLen;
830     PIRP                  Irp;
831     KEVENT                Event;
832     IO_STATUS_BLOCK       IoStatus;
833 
834     /* First, get MountMgr DeviceObject */
835     RtlInitUnicodeString(&MountMgrDevice, MOUNTMGR_DEVICE_NAME);
836     Status = IoGetDeviceObjectPointer(&MountMgrDevice, FILE_READ_ATTRIBUTES,
837                                       &FileObject, &DeviceObject);
838 
839     if(!NT_SUCCESS(Status))
840     {
841         WARN_(FLOPPY, "ReportToMountMgr: Can't get MountMgr pointers %lx\n", Status);
842         return;
843     }
844 
845     DeviceLen = wcslen(&gControllerInfo[ControlerId].DriveInfo[DriveId].DeviceNameBuffer[0]) * sizeof(WCHAR);
846 
847     /* Allocate input buffer to report our floppy device */
848     MountTarget = ExAllocatePool(NonPagedPool,
849                                  sizeof(MOUNTMGR_TARGET_NAME) + DeviceLen);
850 
851     if(!MountTarget)
852     {
853         WARN_(FLOPPY, "ReportToMountMgr: Allocation of mountTarget failed\n");
854         ObDereferenceObject(FileObject);
855         return;
856     }
857 
858     MountTarget->DeviceNameLength = DeviceLen;
859     RtlCopyMemory(MountTarget->DeviceName,
860                   gControllerInfo[ControlerId].DriveInfo[DriveId].DeviceNameBuffer,
861                   DeviceLen);
862 
863     KeInitializeEvent(&Event, NotificationEvent, FALSE);
864 
865     /* Build the IRP used to communicate with the MountMgr */
866     Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
867                                         DeviceObject,
868                                         MountTarget,
869                                         sizeof(MOUNTMGR_TARGET_NAME) + DeviceLen,
870                                         NULL,
871                                         0,
872                                         FALSE,
873                                         &Event,
874                                         &IoStatus);
875 
876     if(!Irp)
877     {
878         WARN_(FLOPPY, "ReportToMountMgr: Allocation of irp failed\n");
879         ExFreePool(MountTarget);
880         ObDereferenceObject(FileObject);
881         return;
882     }
883 
884     /* Call the MountMgr */
885     Status = IoCallDriver(DeviceObject, Irp);
886 
887     if (Status == STATUS_PENDING) {
888         KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
889         Status = IoStatus.Status;
890     }
891 
892     /* We're done */
893 
894     INFO_(FLOPPY, "Reported to the MountMgr: %lx\n", Status);
895 
896     ExFreePool(MountTarget);
897     ObDereferenceObject(FileObject);
898 
899     return;
900 }
901 
902 
903 static BOOLEAN NTAPI
AddControllers(PDRIVER_OBJECT DriverObject)904 AddControllers(PDRIVER_OBJECT DriverObject)
905 /*
906  * FUNCTION: Called on initialization to find our controllers and build device and controller objects for them
907  * ARGUMENTS:
908  *     DriverObject: Our driver's DriverObject (so we can create devices against it)
909  * RETURNS:
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)
912  * NOTES:
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.
917  * TODO:
918  *     - Report resource usage to the HAL
919  */
920 {
921     INTERFACE_TYPE InterfaceType = Isa;
922     CONFIGURATION_TYPE ControllerType = DiskController;
923     CONFIGURATION_TYPE PeripheralType = FloppyDiskPeripheral;
924     KAFFINITY Affinity;
925     DEVICE_DESCRIPTION DeviceDescription;
926     UCHAR i;
927     UCHAR j;
928 
929     PAGED_CODE();
930 
931     /* Find our controllers on all ISA buses */
932     IoQueryDeviceDescription(&InterfaceType,
933                              NULL,
934                              &ControllerType,
935                              NULL,
936                              &PeripheralType,
937                              NULL,
938                              ConfigCallback,
939                              NULL);
940 
941     /*
942      * w2k breaks the return val from ConfigCallback, so we have to hack around it, rather than just
943      * looking for a return value from ConfigCallback.  We expect at least one controller.
944      */
945     if(!gControllerInfo[0].Populated)
946     {
947         WARN_(FLOPPY, "AddControllers: failed to get controller info from registry\n");
948         return FALSE;
949     }
950 
951     /* Now that we have a controller, set it up with the system */
952     for(i = 0; i < gNumberOfControllers && gControllerInfo[i].NumberOfDrives > 0; i++)
953     {
954         /* 0: Report resource usage to the kernel, to make sure they aren't assigned to anyone else */
955         /* FIXME: Implement me. */
956 
957         /* 1: Set up interrupt */
958         gControllerInfo[i].MappedVector = HalGetInterruptVector(gControllerInfo[i].InterfaceType, gControllerInfo[i].BusNumber,
959                                           gControllerInfo[i].Level, gControllerInfo[i].Vector,
960                                           &gControllerInfo[i].MappedLevel, &Affinity);
961 
962         /* Must set up the DPC before we connect the interrupt */
963         KeInitializeDpc(&gControllerInfo[i].Dpc, DpcForIsr, &gControllerInfo[i]);
964 
965         INFO_(FLOPPY, "Connecting interrupt %d to controller%d (object 0x%p)\n", gControllerInfo[i].MappedVector,
966               i, &gControllerInfo[i]);
967 
968         /* NOTE: We cannot share our interrupt, even on level-triggered buses.  See Isr() for details. */
969         if(IoConnectInterrupt(&gControllerInfo[i].InterruptObject, Isr, &gControllerInfo[i], 0, gControllerInfo[i].MappedVector,
970                               gControllerInfo[i].MappedLevel, gControllerInfo[i].MappedLevel, gControllerInfo[i].InterruptMode,
971                               FALSE, Affinity, 0) != STATUS_SUCCESS)
972         {
973             WARN_(FLOPPY, "AddControllers: unable to connect interrupt\n");
974             continue;
975         }
976 
977         /* 2: Set up DMA */
978         memset(&DeviceDescription, 0, sizeof(DeviceDescription));
979         DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
980         DeviceDescription.DmaChannel = gControllerInfo[i].Dma;
981         DeviceDescription.InterfaceType = gControllerInfo[i].InterfaceType;
982         DeviceDescription.BusNumber = gControllerInfo[i].BusNumber;
983         DeviceDescription.MaximumLength = 2*18*512; /* based on a 1.44MB floppy */
984 
985         /* DMA 0,1,2,3 are 8-bit; 4,5,6,7 are 16-bit (4 is chain i think) */
986         DeviceDescription.DmaWidth = gControllerInfo[i].Dma > 3 ? Width16Bits: Width8Bits;
987 
988         gControllerInfo[i].AdapterObject = HalGetAdapter(&DeviceDescription, &gControllerInfo[i].MapRegisters);
989 
990         if(!gControllerInfo[i].AdapterObject)
991         {
992             WARN_(FLOPPY, "AddControllers: unable to allocate an adapter object\n");
993             IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
994             continue;
995         }
996 
997         /* 2b: Initialize the new controller */
998         if(InitController(&gControllerInfo[i]) != STATUS_SUCCESS)
999         {
1000             WARN_(FLOPPY, "AddControllers(): Unable to set up controller %d - initialization failed\n", i);
1001             IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
1002             continue;
1003         }
1004 
1005         /* 2c: Set the controller's initialized flag so we know to release stuff in Unload */
1006         gControllerInfo[i].Initialized = TRUE;
1007 
1008         /* 3: per-drive setup */
1009         for(j = 0; j < gControllerInfo[i].NumberOfDrives; j++)
1010         {
1011             UNICODE_STRING DeviceName;
1012             UNICODE_STRING ArcPath;
1013             UCHAR DriveNumber;
1014 
1015             INFO_(FLOPPY, "AddControllers(): Configuring drive %d on controller %d\n", i, j);
1016 
1017             /*
1018              * 3a: create a device object for the drive
1019              * Controllers and drives are 0-based, so the combos are:
1020              * 0: 0,0
1021              * 1: 0,1
1022              * 2: 0,2
1023              * 3: 0,3
1024              * 4: 1,0
1025              * 5: 1,1
1026              * ...
1027              * 14: 3,2
1028              * 15: 3,3
1029              */
1030 
1031             DriveNumber = (UCHAR)(i*4 + j); /* loss of precision is OK; there are only 16 of 'em */
1032 
1033             swprintf(gControllerInfo[i].DriveInfo[j].DeviceNameBuffer, L"\\Device\\Floppy%d", DriveNumber);
1034             RtlInitUnicodeString(&DeviceName, gControllerInfo[i].DriveInfo[j].DeviceNameBuffer);
1035 
1036             if(IoCreateDevice(DriverObject, sizeof(PVOID), &DeviceName,
1037                               FILE_DEVICE_DISK, FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE, FALSE,
1038                               &gControllerInfo[i].DriveInfo[j].DeviceObject) != STATUS_SUCCESS)
1039             {
1040                 WARN_(FLOPPY, "AddControllers: unable to register a Device object\n");
1041                 IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
1042                 continue; /* continue on to next drive */
1043             }
1044 
1045             INFO_(FLOPPY, "AddControllers: New device: %S (0x%p)\n",
1046                           gControllerInfo[i].DriveInfo[j].DeviceNameBuffer,
1047                           gControllerInfo[i].DriveInfo[j].DeviceObject);
1048 
1049             /* 3b.5: Create an ARC path in case we're booting from this drive */
1050             swprintf(gControllerInfo[i].DriveInfo[j].ArcPathBuffer,
1051                      L"\\ArcName\\multi(%d)disk(%d)fdisk(%d)", gControllerInfo[i].BusNumber, i, DriveNumber);
1052 
1053             RtlInitUnicodeString(&ArcPath, gControllerInfo[i].DriveInfo[j].ArcPathBuffer);
1054             IoAssignArcName(&ArcPath, &DeviceName);
1055 
1056             /* 3c: Set flags up */
1057             gControllerInfo[i].DriveInfo[j].DeviceObject->Flags |= DO_DIRECT_IO;
1058 
1059             /* 3d: Increase global floppy drives count */
1060             IoGetConfigurationInformation()->FloppyCount++;
1061 
1062             /* 3e: Set up the DPC */
1063             IoInitializeDpcRequest(gControllerInfo[i].DriveInfo[j].DeviceObject, (PIO_DPC_ROUTINE)DpcForIsr);
1064 
1065             /* 3f: Point the device extension at our DriveInfo struct */
1066             gControllerInfo[i].DriveInfo[j].DeviceObject->DeviceExtension = &gControllerInfo[i].DriveInfo[j];
1067 
1068             /* 3g: neat comic strip */
1069 
1070             /* 3h: set the initial media type to unknown */
1071             memset(&gControllerInfo[i].DriveInfo[j].DiskGeometry, 0, sizeof(DISK_GEOMETRY));
1072             gControllerInfo[i].DriveInfo[j].DiskGeometry.MediaType = Unknown;
1073 
1074             /* 3i: Now that we're done, set the Initialized flag so we know to free this in Unload */
1075             gControllerInfo[i].DriveInfo[j].Initialized = TRUE;
1076 
1077             /* 3j: Clear the DO_DEVICE_INITIALIZING flag */
1078             gControllerInfo[i].DriveInfo[j].DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1079 
1080             /* 3k: Report to the MountMgr */
1081             ReportToMountMgr(i, j);
1082 
1083             /* 3l: Attempt to get drive info - if a floppy is already present */
1084             StartMotor(&gControllerInfo[i].DriveInfo[j]);
1085             RWDetermineMediaType(&gControllerInfo[i].DriveInfo[j], TRUE);
1086             StopMotor(gControllerInfo[i].DriveInfo[j].ControllerInfo);
1087         }
1088     }
1089 
1090     INFO_(FLOPPY, "AddControllers: --------------------------------------------> finished adding controllers\n");
1091 
1092     return (IoGetConfigurationInformation()->FloppyCount != 0);
1093 }
1094 
1095 
1096 VOID NTAPI
SignalMediaChanged(PDEVICE_OBJECT DeviceObject,PIRP Irp)1097 SignalMediaChanged(PDEVICE_OBJECT DeviceObject, PIRP Irp)
1098 /*
1099  * FUNCTION: Process an IRP when the media has changed, and possibly notify the user
1100  * ARGUMENTS:
1101  *     DeviceObject: DeviceObject associated with the IRP
1102  *     Irp: IRP that we're failing due to change
1103  * NOTES:
1104  *     - This procedure is documented in the DDK by "Notifying the File System of Possible Media Changes",
1105  *       "IoSetHardErrorOrVerifyDevice", and by "Responding to Check-Verify Requests from the File System".
1106  *     - Callable at <= DISPATCH_LEVEL
1107  */
1108 {
1109     PDRIVE_INFO DriveInfo = DeviceObject->DeviceExtension;
1110 
1111     TRACE_(FLOPPY, "SignalMediaChanged called\n");
1112 
1113     DriveInfo->DiskChangeCount++;
1114 
1115     /* If volume is not mounted, do NOT set verify and return STATUS_IO_DEVICE_ERROR */
1116     if(!(DeviceObject->Vpb->Flags & VPB_MOUNTED))
1117     {
1118         Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
1119         Irp->IoStatus.Information = 0;
1120         return;
1121     }
1122 
1123     /* Notify the filesystem that it will need to verify the volume */
1124     DeviceObject->Flags |= DO_VERIFY_VOLUME;
1125     Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
1126     Irp->IoStatus.Information = 0;
1127 
1128     /*
1129      * If this is a user-based, threaded request, let the IO manager know to pop up a box asking
1130      * the user to supply the correct media, but only if the error (which we just picked out above)
1131      * is deemed by the IO manager to be "user induced".  The reason we don't just unconditionally
1132      * call IoSetHardError... is because MS might change the definition of "user induced" some day,
1133      * and we don't want to have to remember to re-code this.
1134      */
1135     if(Irp->Tail.Overlay.Thread && IoIsErrorUserInduced(Irp->IoStatus.Status))
1136         IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
1137 }
1138 
1139 
1140 static VOID NTAPI
QueueThread(PVOID Context)1141 QueueThread(PVOID Context)
1142 /*
1143  * FUNCTION: Thread that manages the queue and dispatches any queued requests
1144  * ARGUMENTS:
1145  *     Context: unused
1146  */
1147 {
1148     PIRP Irp;
1149     PIO_STACK_LOCATION Stack;
1150     PDEVICE_OBJECT DeviceObject;
1151     PVOID Objects[2];
1152 
1153     PAGED_CODE();
1154     UNREFERENCED_PARAMETER(Context);
1155 
1156     Objects[0] = &QueueSemaphore;
1157     Objects[1] = &QueueThreadTerminate;
1158 
1159     for(;;)
1160     {
1161         KeWaitForMultipleObjects(2, Objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
1162 
1163         if(KeReadStateEvent(&QueueThreadTerminate))
1164         {
1165             INFO_(FLOPPY, "QueueThread terminating\n");
1166             return;
1167         }
1168 
1169         INFO_(FLOPPY, "QueueThread: servicing an IRP\n");
1170 
1171         Irp = IoCsqRemoveNextIrp(&Csq, 0);
1172 
1173         /* we won't get an irp if it was canceled */
1174         if(!Irp)
1175         {
1176             INFO_(FLOPPY, "QueueThread: IRP queue empty\n");
1177             continue;
1178         }
1179 
1180         DeviceObject = (PDEVICE_OBJECT)Irp->Tail.Overlay.DriverContext[0];
1181 
1182         ASSERT(DeviceObject);
1183 
1184         Stack = IoGetCurrentIrpStackLocation(Irp);
1185 
1186         /* Decide what to do with the IRP */
1187         switch(Stack->MajorFunction)
1188         {
1189         case IRP_MJ_READ:
1190         case IRP_MJ_WRITE:
1191             ReadWritePassive(DeviceObject->DeviceExtension, Irp);
1192             break;
1193 
1194         case IRP_MJ_DEVICE_CONTROL:
1195             DeviceIoctlPassive(DeviceObject->DeviceExtension, Irp);
1196             break;
1197 
1198         default:
1199             WARN_(FLOPPY, "QueueThread(): Unrecognized irp: mj: 0x%x\n", Stack->MajorFunction);
1200             Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1201             Irp->IoStatus.Information = 0;
1202             IoCompleteRequest(Irp, IO_NO_INCREMENT);
1203         }
1204     }
1205 }
1206 
1207 
1208 NTSTATUS NTAPI
DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)1209 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
1210 /*
1211  * FUNCTION: Entry-point for the driver
1212  * ARGUMENTS:
1213  *     DriverObject: Our driver object
1214  *     RegistryPath: Unused
1215  * RETURNS:
1216  *     STATUS_SUCCESS on successful initialization of at least one drive
1217  *     STATUS_NO_SUCH_DEVICE if we didn't find even one drive
1218  *     STATUS_UNSUCCESSFUL otherwise
1219  */
1220 {
1221     HANDLE ThreadHandle;
1222 
1223     UNREFERENCED_PARAMETER(RegistryPath);
1224 
1225     /*
1226      * Set up dispatch routines
1227      */
1228     DriverObject->MajorFunction[IRP_MJ_CREATE]         = (PDRIVER_DISPATCH)CreateClose;
1229     DriverObject->MajorFunction[IRP_MJ_CLOSE]          = (PDRIVER_DISPATCH)CreateClose;
1230     DriverObject->MajorFunction[IRP_MJ_READ]           = (PDRIVER_DISPATCH)ReadWrite;
1231     DriverObject->MajorFunction[IRP_MJ_WRITE]          = (PDRIVER_DISPATCH)ReadWrite;
1232     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)DeviceIoctl;
1233 
1234     DriverObject->DriverUnload = Unload;
1235 
1236     /*
1237      * We depend on some zeroes in these structures.  I know this is supposed to be
1238      * initialized to 0 by the complier but this makes me feel beter.
1239      */
1240     memset(&gControllerInfo, 0, sizeof(gControllerInfo));
1241 
1242     /*
1243      * Set up queue.  This routine cannot fail (trust me, I wrote it).
1244      */
1245     IoCsqInitialize(&Csq, CsqInsertIrp, CsqRemoveIrp, CsqPeekNextIrp,
1246                     CsqAcquireLock, CsqReleaseLock, CsqCompleteCanceledIrp);
1247 
1248     /*
1249      * ...and its lock
1250      */
1251     KeInitializeSpinLock(&IrpQueueLock);
1252 
1253     /*
1254      * ...and the queue list itself
1255      */
1256     InitializeListHead(&IrpQueue);
1257 
1258     /*
1259      * The queue is counted by a semaphore.  The queue management thread
1260      * blocks on this semaphore, so if requests come in faster than the queue
1261      * thread can handle them, the semaphore count goes up.
1262      */
1263     KeInitializeSemaphore(&QueueSemaphore, 0, 0x7fffffff);
1264 
1265     /*
1266      * Event to terminate that thread
1267      */
1268     KeInitializeEvent(&QueueThreadTerminate, NotificationEvent, FALSE);
1269 
1270     /*
1271      * Create the queue processing thread.  Save its handle in the global variable
1272      * ThreadHandle so we can wait on its termination during Unload.
1273      */
1274     if(PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, 0, 0, 0, QueueThread, 0) != STATUS_SUCCESS)
1275     {
1276         WARN_(FLOPPY, "Unable to create system thread; failing init\n");
1277         return STATUS_INSUFFICIENT_RESOURCES;
1278     }
1279 
1280     if(ObReferenceObjectByHandle(ThreadHandle, STANDARD_RIGHTS_ALL, *PsThreadType, KernelMode, &QueueThreadObject, NULL) != STATUS_SUCCESS)
1281     {
1282         WARN_(FLOPPY, "Unable to reference returned thread handle; failing init\n");
1283         return STATUS_UNSUCCESSFUL;
1284     }
1285 
1286     /*
1287      * Close the handle, now that we have the object pointer and a reference of our own.
1288      * The handle will certainly not be valid in the context of the caller next time we
1289      * need it, as handles are process-specific.
1290      */
1291     ZwClose(ThreadHandle);
1292 
1293     /*
1294      * Start the device discovery process.  Returns STATUS_SUCCESS if
1295      * it finds even one drive attached to one controller.
1296      */
1297     if(!AddControllers(DriverObject))
1298         return STATUS_NO_SUCH_DEVICE;
1299 
1300     return STATUS_SUCCESS;
1301 }
1302