First import of in-progress floppy driver replacement. Still needs some
[reactos.git] / reactos / drivers / storage / floppy / floppy.c
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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 *
27 * ---- General to-do items ----
28 * TODO: Clean up properly on failed init
29 * TODO: Add arc-path support so we can boot from the floppy
30 * TODO: Fix all these stupid STATUS_UNSUCCESSFUL return values
31 * TODO: Think about IO_NO_INCREMENT
32 * TODO: Figure out why CreateClose isn't called any more on XP. Seems to correspond
33 * with the driver not being unloadable. Does it have to do with cleanup?
34 * TODO: Consider using the built-in device object pointer in the stack location
35 * rather than the context area
36 * TODO: Think about StopDpcQueued -- could be a race; too tired atm to tell
37 *
38 * ---- Support for proper media detection ----
39 * TODO: Handle MFM flag
40 * TODO: Un-hardcode the data rate from various places
41 * TODO: Proper media detection (right now we're hardcoded to 1.44)
42 * TODO: Media detection based on sector 1
43 *
44 * ---- Support for normal floppy hardware ----
45 * TODO: Support the three primary types of controller
46 * TODO: Figure out thinkpad compatibility (I've heard rumors of weirdness with them)
47 *
48 * ---- Support for non-ISA and/or non-slave-dma controllers, if they exist ----
49 * TODO: Find controllers on non-ISA buses
50 * TODO: Think about making the interrupt shareable
51 * TODO: Support bus-master controllers. PCI will break ATM.
52 */
53
54 #include <ntddk.h>
55
56 #include "floppy.h"
57 #include "hardware.h"
58 #include "csqrtns.h"
59 #include "ioctl.h"
60 #include "readwrite.h"
61
62 /*
63 * Global controller info structures. Each controller gets one. Since the system
64 * will probably have only one, with four being a very unlikely maximum, a static
65 * global array is easiest to deal with.
66 */
67 CONTROLLER_INFO gControllerInfo[MAX_CONTROLLERS];
68 ULONG gNumberOfControllers = 0;
69
70 /* Queue thread management */
71 KEVENT QueueThreadTerminate;
72 PVOID ThreadObject;
73
74 /* ISR DPC */
75 KDPC Dpc;
76
77 \f
78 static VOID NTAPI MotorStopDpcFunc(PKDPC Dpc,
79 PVOID DeferredContext,
80 PVOID SystemArgument1,
81 PVOID SystemArgument2)
82 /*
83 * FUNCTION: Stop the floppy motor
84 * ARGUMENTS:
85 * Dpc: DPC object that's going off
86 * DeferredContext: called with DRIVE_INFO for drive to turn off
87 * SystemArgument1: unused
88 * SystemArgument2: unused
89 * NOTES:
90 * - Must set an event to let other threads know we're done turning off the motor
91 * - Called back at DISPATCH_LEVEL
92 */
93 {
94 PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)DeferredContext;
95
96 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
97 ASSERT(ControllerInfo);
98
99 KdPrint(("floppy: MotorStopDpcFunc called\n"));
100
101 HwTurnOffMotor(ControllerInfo);
102 ControllerInfo->StopDpcQueued = FALSE;
103 KeSetEvent(&ControllerInfo->MotorStoppedEvent, IO_NO_INCREMENT, FALSE);
104 }
105
106 \f
107 VOID NTAPI StartMotor(PDRIVE_INFO DriveInfo)
108 /*
109 * FUNCTION: Start the motor, taking into account proper handling of the timer race
110 * ARGUMENTS:
111 * DriveInfo: drive to start
112 * NOTES:
113 * - Never call HwTurnOnMotor() directly
114 * - This protocol manages a race between the cancel timer and the requesting thread.
115 * You wouldn't want to turn on the motor and then cancel the timer, because the
116 * cancel dpc might fire in the meantime, and that'd un-do what you just did. If you
117 * cancel the timer first, but KeCancelTimer returns false, the dpc is already running,
118 * so you have to wait until the dpc is completly done running, or else you'll race
119 * with the turner-offer
120 * - PAGED_CODE because we wait
121 */
122 {
123 PAGED_CODE();
124 ASSERT(DriveInfo);
125
126 KdPrint(("floppy: StartMotor called\n"));
127
128 if(DriveInfo->ControllerInfo->StopDpcQueued &&
129 !KeCancelTimer(&DriveInfo->ControllerInfo->MotorTimer))
130 {
131 /* Motor turner-offer is already running; wait for it to finish */
132 KeWaitForSingleObject(&DriveInfo->ControllerInfo->MotorStoppedEvent, Executive, KernelMode, FALSE, NULL);
133 DriveInfo->ControllerInfo->StopDpcQueued = FALSE;
134 }
135
136 HwTurnOnMotor(DriveInfo);
137 }
138
139 \f
140 VOID NTAPI StopMotor(PCONTROLLER_INFO ControllerInfo)
141 /*
142 * FUNCTION: Stop all motors on the controller
143 * ARGUMENTS:
144 * DriveInfo: Drive to stop
145 * NOTES:
146 * - Never call HwTurnOffMotor() directly
147 * - This manages the timer cancelation race (see StartMotor for details).
148 * All we have to do is set up a timer.
149 */
150 {
151 LARGE_INTEGER StopTime;
152
153 ASSERT(ControllerInfo);
154
155 KdPrint(("floppy: StopMotor called\n"));
156
157 /* one relative second, in 100-ns units */
158 StopTime.QuadPart = 10000000;
159 StopTime.QuadPart *= -1;
160
161 KeClearEvent(&ControllerInfo->MotorStoppedEvent);
162 KeSetTimer(&ControllerInfo->MotorTimer, StopTime, &ControllerInfo->MotorStopDpc);
163 ControllerInfo->StopDpcQueued = TRUE;
164 }
165
166 \f
167 VOID NTAPI WaitForControllerInterrupt(PCONTROLLER_INFO ControllerInfo)
168 /*
169 * FUNCTION: Wait for the controller to interrupt, and then clear the event
170 * ARGUMENTS:
171 * ControllerInfo: Controller to wait for
172 * NOTES:
173 * - There is a small chance that an unexpected or spurious interrupt could
174 * be lost with this clear/wait/clear scheme used in this driver. This is
175 * deemed to be an acceptable risk due to the unlikeliness of the scenario,
176 * and the fact that it'll probably work fine next time.
177 * - PAGED_CODE because it waits
178 */
179 {
180 PAGED_CODE();
181 ASSERT(ControllerInfo);
182
183 KeWaitForSingleObject(&ControllerInfo->SynchEvent, Executive, KernelMode, FALSE, NULL);
184 KeClearEvent(&ControllerInfo->SynchEvent);
185 }
186
187 \f
188 static NTSTATUS NTAPI CreateClose(PDEVICE_OBJECT DeviceObject,
189 PIRP Irp)
190 /*
191 * FUNCTION: Dispatch function called for Create and Close IRPs
192 * ARGUMENTS:
193 * DeviceObject: DeviceObject that is the target of the IRP
194 * Irp: IRP to process
195 * RETURNS:
196 * STATUS_SUCCESS in all cases
197 * NOTES:
198 * - The Microsoft sample drivers tend to return FILE_OPENED in Information, so I do too.
199 * - No reason to fail the device open
200 * - No state to track, so this routine is easy
201 * - Can be called <= DISPATCH_LEVEL
202 *
203 * TODO: Figure out why this isn't getting called any more, and remove the ASSERT once that happens
204 */
205 {
206 KdPrint(("floppy: CreateClose called\n"));
207
208 ASSERT(0);
209
210 Irp->IoStatus.Status = STATUS_SUCCESS;
211 Irp->IoStatus.Information = FILE_OPENED;
212
213 IoCompleteRequest(Irp, IO_NO_INCREMENT);
214
215 return STATUS_SUCCESS;
216 }
217
218 \f
219 static NTSTATUS NTAPI Recalibrate(PDRIVE_INFO DriveInfo)
220 /*
221 * FUNCTION: Start the recalibration process
222 * ARGUMENTS:
223 * DriveInfo: Pointer to the driveinfo struct associated with the targeted drive
224 * RETURNS:
225 * STATUS_SUCCESS on successful starting of the process
226 * STATUS_UNSUCCESSFUL if it fails
227 * NOTES:
228 * - Sometimes you have to do two recalibrations, particularly if the disk has <80 tracks.
229 * - PAGED_CODE because we wait
230 */
231 {
232 NTSTATUS Status;
233 ULONG i;
234
235 PAGED_CODE();
236 ASSERT(DriveInfo);
237
238 /* first turn on the motor */
239 /* Must stop after every start, prior to return */
240 StartMotor(DriveInfo);
241
242 /* set the data rate */
243 KdPrint(("floppy: FIXME: UN-HARDCODE DATA RATE\n"));
244 HwSetDataRate(DriveInfo->ControllerInfo, 0);
245
246 /* clear the event just in case the last call forgot */
247 KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
248
249 /* sometimes you have to do this twice */
250 for(i = 0; i < 2; i++)
251 {
252 /* Send the command */
253 Status = HwRecalibrate(DriveInfo);
254 if(Status != STATUS_SUCCESS)
255 {
256 KdPrint(("floppy: Recalibrate: HwRecalibrate returned error\n"));
257 continue;
258 }
259
260 WaitForControllerInterrupt(DriveInfo->ControllerInfo);
261
262 /* Get the results */
263 Status = HwRecalibrateResult(DriveInfo->ControllerInfo);
264 if(Status != STATUS_SUCCESS)
265 {
266 KdPrint(("floppy: Recalibrate: HwRecalibrateResult returned error\n"));
267 break;
268 }
269 }
270
271 KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
272
273 /* Must stop after every start, prior to return */
274 StopMotor(DriveInfo->ControllerInfo);
275
276 return Status;
277 }
278
279 \f
280 NTSTATUS NTAPI ResetChangeFlag(PDRIVE_INFO DriveInfo)
281 /*
282 * FUNCTION: Reset the drive's change flag (as reflected in the DIR)
283 * ARGUMENTS:
284 * DriveInfo: the drive to reset
285 * RETURNS:
286 * STATUS_SUCCESS if the changeline is cleared
287 * STATUS_NO_MEDIA_IN_DEVICE if the changeline cannot be cleared
288 * STATUS_IO_DEVICE_ERROR if the controller cannot be communicated with
289 * NOTES:
290 * - Change reset procedure: recalibrate, seek 1, seek 0
291 * - If the line is still set after that, there's clearly no disk in the
292 * drive, so we return STATUS_NO_MEDIA_IN_DEVICE
293 * - PAGED_CODE because we wait
294 */
295 {
296 BOOLEAN DiskChanged;
297
298 PAGED_CODE();
299 ASSERT(DriveInfo);
300
301 KdPrint(("floppy: ResetChangeFlag called\n"));
302
303 /* Try to recalibrate. We don't care if it works. */
304 Recalibrate(DriveInfo);
305
306 /* clear spurious interrupts in prep for seeks */
307 KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
308
309 /* Seek to 1 */
310 if(HwSeek(DriveInfo, 1) != STATUS_SUCCESS)
311 {
312 KdPrint(("floppy: ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n"));
313 return STATUS_IO_DEVICE_ERROR;
314 }
315
316 WaitForControllerInterrupt(DriveInfo->ControllerInfo);
317
318 HwSenseInterruptStatus(DriveInfo->ControllerInfo);
319
320 /* Seek back to 0 */
321 if(HwSeek(DriveInfo, 1) != STATUS_SUCCESS)
322 {
323 KdPrint(("floppy: ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n"));
324 return STATUS_IO_DEVICE_ERROR;
325 }
326
327 WaitForControllerInterrupt(DriveInfo->ControllerInfo);
328
329 HwSenseInterruptStatus(DriveInfo->ControllerInfo);
330
331 /* Check the change bit */
332 if(HwDiskChanged(DriveInfo, &DiskChanged) != STATUS_SUCCESS)
333 {
334 KdPrint(("floppy: ResetChangeFlag(): HwDiskChagned failed; returning STATUS_IO_DEVICE_ERROR\n"));
335 return STATUS_IO_DEVICE_ERROR;
336 }
337
338 /* if the change flag is still set, there's probably no media in the drive. */
339 if(DiskChanged)
340 return STATUS_NO_MEDIA_IN_DEVICE;
341
342 /* else we're done! */
343 return STATUS_SUCCESS;
344 }
345
346 \f
347 static VOID NTAPI Unload(PDRIVER_OBJECT DriverObject)
348 /*
349 * FUNCTION: Unload the driver from memory
350 * ARGUMENTS:
351 * DriverObject - The driver that is being unloaded
352 *
353 * TODO: Delete ARC links
354 */
355 {
356 ULONG i,j;
357
358 PAGED_CODE();
359
360 KdPrint(("floppy: unloading\n"));
361
362 KeSetEvent(&QueueThreadTerminate, 0, FALSE);
363 KeWaitForSingleObject(ThreadObject, Executive, KernelMode, FALSE, 0);
364 ObDereferenceObject(ThreadObject);
365
366 for(i = 0; i < gNumberOfControllers; i++)
367 {
368 if(!gControllerInfo[i].Populated)
369 continue;
370
371 for(j = 0; j < gControllerInfo[i].NumberOfDrives; j++)
372 {
373 if(gControllerInfo[i].DriveInfo[j].DeviceObject)
374 {
375 UNICODE_STRING Link;
376 RtlInitUnicodeString(&Link, gControllerInfo[i].DriveInfo[j].SymLinkBuffer);
377 IoDeleteSymbolicLink(&Link);
378 IoDeleteDevice(gControllerInfo[i].DriveInfo[j].DeviceObject);
379 }
380 }
381
382 IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
383
384 /* Power down the controller */
385 HwPowerOff(&gControllerInfo[i]);
386 }
387 }
388
389 \f
390 static NTSTATUS NTAPI ConfigCallback(PVOID Context,
391 PUNICODE_STRING PathName,
392 INTERFACE_TYPE BusType,
393 ULONG BusNumber,
394 PKEY_VALUE_FULL_INFORMATION *BusInformation,
395 CONFIGURATION_TYPE ControllerType,
396 ULONG ControllerNumber,
397 PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
398 CONFIGURATION_TYPE PeripheralType,
399 ULONG PeripheralNumber,
400 PKEY_VALUE_FULL_INFORMATION *PeripheralInformation)
401 /*
402 * FUNCTION: Callback to IoQueryDeviceDescription, which tells us about our controllers
403 * ARGUMENTS:
404 * Context: Unused
405 * PathName: Unused
406 * BusType: Type of the bus that our controller is on
407 * BusNumber: Number of the bus that our controller is on
408 * BusInformation: Unused
409 * ControllerType: Unused
410 * ControllerNumber: Number of the controller that we're adding
411 * ControllerInformation: Full configuration information for our controller
412 * PeripheralType: Unused
413 * PeripheralNumber: Unused
414 * PeripheralInformation: Full configuration information for each drive on our controller
415 * RETURNS:
416 * STATUS_SUCCESS in all cases
417 * NOTES:
418 * - The only documentation I've found about the contents of these structures is
419 * from the various Microsoft floppy samples and from the DDK headers. They're
420 * very vague, though, so I'm only mostly sure that this stuff is correct, as
421 * the MS samples do things completely differently than I have done them. Seems
422 * to work in my VMWare, though.
423 * - Basically, the function gets all of the information (port, dma, irq) about the
424 * controller, and then loops through all of the drives presented in PeripheralInformation.
425 * - Each controller has a CONTROLLER_INFO created for it, and each drive has a DRIVE_INFO.
426 * - Device objects are created for each drive (not controller), as that's the targeted
427 * device in the eyes of the rest of the OS. Each DRIVE_INFO points to a single CONTROLLER_INFO.
428 * - We only support up to four controllers in the whole system, each of which supports up to four
429 * drives.
430 */
431 {
432 PKEY_VALUE_FULL_INFORMATION ControllerFullDescriptor = ControllerInformation[IoQueryDeviceConfigurationData];
433 PCM_FULL_RESOURCE_DESCRIPTOR ControllerResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PCHAR)ControllerFullDescriptor +
434 ControllerFullDescriptor->DataOffset);
435
436 PKEY_VALUE_FULL_INFORMATION PeripheralFullDescriptor = PeripheralInformation[IoQueryDeviceConfigurationData];
437 PCM_FULL_RESOURCE_DESCRIPTOR PeripheralResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PCHAR)PeripheralFullDescriptor +
438 PeripheralFullDescriptor->DataOffset);
439
440 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
441 PCM_FLOPPY_DEVICE_DATA FloppyDeviceData;
442 UCHAR i;
443
444 PAGED_CODE();
445
446 KdPrint(("floppy: ConfigCallback called with ControllerNumber %d\n", ControllerNumber));
447
448 gControllerInfo[gNumberOfControllers].ControllerNumber = ControllerNumber;
449 gControllerInfo[gNumberOfControllers].InterfaceType = BusType;
450 gControllerInfo[gNumberOfControllers].BusNumber = BusNumber;
451
452 /* Get controller interrupt level/vector, dma channel, and port base */
453 for(i = 0; i < ControllerResourceDescriptor->PartialResourceList.Count; i++)
454 {
455 KeInitializeEvent(&gControllerInfo[gNumberOfControllers].SynchEvent, NotificationEvent, FALSE);
456
457 PartialDescriptor = &ControllerResourceDescriptor->PartialResourceList.PartialDescriptors[i];
458
459 if(PartialDescriptor->Type == CmResourceTypeInterrupt)
460 {
461 gControllerInfo[gNumberOfControllers].Level = PartialDescriptor->u.Interrupt.Level;
462 gControllerInfo[gNumberOfControllers].Vector = PartialDescriptor->u.Interrupt.Vector;
463
464 if(PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
465 gControllerInfo[gNumberOfControllers].InterruptMode = Latched;
466 else
467 gControllerInfo[gNumberOfControllers].InterruptMode = LevelSensitive;
468 }
469
470 else if(PartialDescriptor->Type == CmResourceTypePort)
471 {
472 PHYSICAL_ADDRESS TranslatedAddress;
473 ULONG AddressSpace = 0x1; /* I/O Port Range */
474
475 if(!HalTranslateBusAddress(BusType, BusNumber, PartialDescriptor->u.Port.Start, &AddressSpace, &TranslatedAddress))
476 ASSERT(0);
477
478 if(AddressSpace == 0)
479 gControllerInfo[gNumberOfControllers].BaseAddress = MmMapIoSpace(TranslatedAddress, 8, FALSE); // symbolic constant?
480 else
481 gControllerInfo[gNumberOfControllers].BaseAddress = (PUCHAR)TranslatedAddress.u.LowPart;
482 }
483
484 else if(PartialDescriptor->Type == CmResourceTypeDma)
485 gControllerInfo[gNumberOfControllers].Dma = PartialDescriptor->u.Dma.Channel;
486 }
487
488 /* Start with 0 drives, then go looking */
489 gControllerInfo[gNumberOfControllers].NumberOfDrives = 0;
490
491 /* learn about drives attached to controller */
492 for(i = 0; i < PeripheralResourceDescriptor->PartialResourceList.Count; i++)
493 {
494 PDRIVE_INFO DriveInfo = &gControllerInfo[gNumberOfControllers].DriveInfo[i];
495
496 PartialDescriptor = &PeripheralResourceDescriptor->PartialResourceList.PartialDescriptors[i];
497
498 if(PartialDescriptor->Type != CmResourceTypeDeviceSpecific)
499 continue;
500
501 FloppyDeviceData = (PCM_FLOPPY_DEVICE_DATA)(PartialDescriptor + 1);
502
503 DriveInfo->ControllerInfo = &gControllerInfo[gNumberOfControllers];
504 DriveInfo->UnitNumber = i;
505
506 DriveInfo->FloppyDeviceData.MaxDensity = FloppyDeviceData->MaxDensity;
507 DriveInfo->FloppyDeviceData.MountDensity = FloppyDeviceData->MountDensity;
508 DriveInfo->FloppyDeviceData.StepRateHeadUnloadTime = FloppyDeviceData->StepRateHeadUnloadTime;
509 DriveInfo->FloppyDeviceData.HeadLoadTime = FloppyDeviceData->HeadLoadTime;
510 DriveInfo->FloppyDeviceData.MotorOffTime = FloppyDeviceData->MotorOffTime;
511 DriveInfo->FloppyDeviceData.SectorLengthCode = FloppyDeviceData->SectorLengthCode;
512 DriveInfo->FloppyDeviceData.SectorPerTrack = FloppyDeviceData->SectorPerTrack;
513 DriveInfo->FloppyDeviceData.ReadWriteGapLength = FloppyDeviceData->ReadWriteGapLength;
514 DriveInfo->FloppyDeviceData.FormatGapLength = FloppyDeviceData->FormatGapLength;
515 DriveInfo->FloppyDeviceData.FormatFillCharacter = FloppyDeviceData->FormatFillCharacter;
516 DriveInfo->FloppyDeviceData.HeadSettleTime = FloppyDeviceData->HeadSettleTime;
517 DriveInfo->FloppyDeviceData.MotorSettleTime = FloppyDeviceData->MotorSettleTime;
518 DriveInfo->FloppyDeviceData.MaximumTrackValue = FloppyDeviceData->MaximumTrackValue;
519 DriveInfo->FloppyDeviceData.DataTransferLength = FloppyDeviceData->DataTransferLength;
520
521 /* Once it's all set up, acknowledge its existance in the controller info object */
522 gControllerInfo[gNumberOfControllers].NumberOfDrives++;
523 }
524
525 gControllerInfo[gNumberOfControllers].Populated = TRUE;
526 gNumberOfControllers++;
527
528 return STATUS_SUCCESS;
529 }
530
531 \f
532 static BOOLEAN NTAPI Isr(PKINTERRUPT Interrupt,
533 PVOID ServiceContext)
534 /*
535 * FUNCTION: Interrupt service routine for the controllers
536 * ARGUMENTS:
537 * Interrupt: Interrupt object representing the interrupt that occured
538 * ServiceContext: Pointer to the ControllerInfo object that caused the interrupt
539 * RETURNS:
540 * TRUE in all cases (see notes)
541 * NOTES:
542 * - We should always be the target of the interrupt, being an edge-triggered ISA interrupt, but
543 * this won't be the case with a level-sensitive system like PCI
544 * - Note that it probably doesn't matter if the interrupt isn't dismissed, as it's edge-triggered.
545 * It probably won't keep re-interrupting.
546 * - There are two different ways to dismiss a floppy interrupt. If the command has a result phase
547 * (see intel datasheet), you dismiss the interrupt by reading the first data byte. If it does
548 * not, you dismiss the interrupt by doing a Sense Interrupt command. Again, because it's edge-
549 * triggered, this is safe to not do here, as we can just wait for the DPC.
550 * - Either way, we don't want to do this here. The controller shouldn't interrupt again, so we'll
551 * schedule a DPC to take care of it.
552 * TODO:
553 * - This driver really cannot shrare interrupts, as I don't know how to conclusively say
554 * whether it was our controller that interrupted or not. I just have to assume that any time
555 * my ISR gets called, it was my board that called it. Dumb design, yes, but it goes back to
556 * the semantics of ISA buses. That, and I don't know much about ISA drivers. :-)
557 * UPDATE: The high bit of Status Register A seems to work on non-AT controllers.
558 * - Called at DIRQL
559 */
560 {
561 PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)ServiceContext;
562
563 ASSERT(ControllerInfo);
564
565 KdPrint(("floppy: ISR called\n"));
566
567 /*
568 * Due to the stupidity of the drive/controller relationship on the floppy drive, only one device object
569 * can have an active interrupt pending. Due to the nature of these IRPs, though, there will only ever
570 * be one thread expecting an interrupt at a time, and furthermore, Interrupts (outside of spurious ones)
571 * won't ever happen unless a thread is expecting them. Therefore, all we have to do is signal an event
572 * and we're done. Queue a DPC and leave.
573 */
574 KeInsertQueueDpc(&ControllerInfo->Dpc, NULL, NULL);
575
576 return TRUE;
577 }
578
579 \f
580 VOID NTAPI DpcForIsr(PKDPC Dpc,
581 PVOID Context,
582 PVOID SystemArgument1,
583 PVOID SystemArgument2)
584 /*
585 * FUNCTION: This DPC gets queued by every ISR. Does the real per-interrupt work.
586 * ARGUMENTS:
587 * Dpc: Pointer to the DPC object that represents our function
588 * DeviceObject: Device that this DPC is running for
589 * Irp: Unused
590 * Context: Pointer to our ControllerInfo struct
591 * NOTES:
592 * - This function just kicks off whatever the SynchEvent is and returns. We depend on
593 * the thing that caused the drive to interrupt to handle the work of clearing the interrupt.
594 * This enables us to get back to PASSIVE_LEVEL and not hog system time on a really stupid,
595 * slow, screwed-up piece of hardare.
596 * - If nothing is waiting for us to set the event, the interrupt is effectively lost and will
597 * never be dismissed. I wonder if this will become a problem.
598 * - Called at DISPATCH_LEVEL
599 */
600 {
601 PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)Context;
602
603 ASSERT(ControllerInfo);
604
605 KdPrint(("floppy: DpcForIsr called\n"));
606
607 KeSetEvent(&ControllerInfo->SynchEvent, IO_NO_INCREMENT, FALSE);
608 }
609
610 \f
611 static NTSTATUS NTAPI InitController(PCONTROLLER_INFO ControllerInfo)
612 /*
613 * FUNCTION: Initialize a newly-found controller
614 * ARGUMENTS:
615 * ControllerInfo: pointer to the controller to be initialized
616 * RETURNS:
617 * STATUS_SUCCESS if the controller is successfully initialized
618 * STATUS_UNSUCCESSFUL otherwise
619 */
620 {
621 int i;
622 UCHAR HeadLoadTime;
623 UCHAR HeadUnloadTime;
624 UCHAR StepRateTime;
625
626 PAGED_CODE();
627 ASSERT(ControllerInfo);
628
629 KdPrint(("floppy: InitController called with Controller 0x%x\n", ControllerInfo));
630
631 KeClearEvent(&ControllerInfo->SynchEvent);
632
633 //HwDumpRegisters(ControllerInfo);
634
635 KdPrint(("floppy: InitController: resetting the controller\n"));
636
637 /* Reset the controller */
638 if(HwReset(ControllerInfo) != STATUS_SUCCESS)
639 {
640 KdPrint(("floppy: InitController: unable to reset controller\n"));
641 return STATUS_UNSUCCESSFUL;
642 }
643
644 //HwDumpRegisters(ControllerInfo);
645
646 KdPrint(("floppy: InitController: setting data rate\n"));
647
648 /* Set data rate */
649 if(HwSetDataRate(ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
650 {
651 KdPrint(("floppy: InitController: unable to set data rate\n"));
652 return STATUS_UNSUCCESSFUL;
653 }
654
655 KdPrint(("floppy: InitController: waiting for initial interrupt\n"));
656
657 /* Wait for an interrupt */
658 WaitForControllerInterrupt(ControllerInfo);
659
660 /* Reset means you have to clear each of the four interrupts (one per drive) */
661 for(i = 0; i < MAX_DRIVES_PER_CONTROLLER; i++)
662 {
663 KdPrint(("floppy: InitController: Sensing interrupt %d\n", i));
664 //HwDumpRegisters(ControllerInfo);
665
666 if(HwSenseInterruptStatus(ControllerInfo) != STATUS_SUCCESS)
667 {
668 KdPrint(("floppy: InitController: Unable to clear interrupt 0x%x\n", i));
669 return STATUS_UNSUCCESSFUL;
670 }
671 }
672
673 KdPrint(("floppy: InitController: done sensing interrupts\n"));
674
675 //HwDumpRegisters(ControllerInfo);
676
677 /* Next, see if we have the right version to do implied seek */
678 if(HwGetVersion(ControllerInfo) != VERSION_ENHANCED)
679 {
680 KdPrint(("floppy: InitController: enhanced version not supported; disabling implied seeks\n"));
681 ControllerInfo->ImpliedSeeks = FALSE;
682 ControllerInfo->Model30 = FALSE;
683 }
684 else
685 {
686 /* If so, set that up -- all defaults below except first TRUE for EIS */
687 if(HwConfigure(ControllerInfo, TRUE, TRUE, FALSE, 0, 0) != STATUS_SUCCESS)
688 {
689 KdPrint(("floppy: InitController: unable to set up implied seek\n"));
690 ControllerInfo->ImpliedSeeks = FALSE;
691 }
692 else
693 {
694 KdPrint(("floppy: InitController: implied seeks set!\n"));
695 ControllerInfo->ImpliedSeeks = TRUE;
696 }
697
698 ControllerInfo->Model30 = TRUE;
699 }
700
701 /* Specify */
702 KdPrint(("FLOPPY: FIXME: Figure out speed\n"));
703 HeadLoadTime = SPECIFY_HLT_500K;
704 HeadUnloadTime = SPECIFY_HUT_500K;
705 StepRateTime = SPECIFY_SRT_500K;
706
707 KdPrint(("floppy: InitController: issuing specify command to controller\n"));
708
709 /* Don't disable DMA --> enable dma (dumb & confusing) */
710 if(HwSpecify(ControllerInfo, HeadLoadTime, HeadUnloadTime, StepRateTime, FALSE) != STATUS_SUCCESS)
711 {
712 KdPrint(("floppy: InitController: unable to specify options\n"));
713 return STATUS_UNSUCCESSFUL;
714 }
715
716 //HwDumpRegisters(ControllerInfo);
717
718 /* Init the stop stuff */
719 KeInitializeDpc(&ControllerInfo->MotorStopDpc, MotorStopDpcFunc, ControllerInfo);
720 KeInitializeTimer(&ControllerInfo->MotorTimer);
721 KeInitializeEvent(&ControllerInfo->MotorStoppedEvent, SynchronizationEvent, FALSE);
722 ControllerInfo->StopDpcQueued = FALSE;
723
724 /* Recalibrate each drive on the controller (depends on StartMotor, which depends on the timer stuff above) */
725 /* TODO: Handle failure of one or more drives */
726 for(i = 0; i < ControllerInfo->NumberOfDrives; i++)
727 {
728 KdPrint(("floppy: InitController: recalibrating drive 0x%x on controller 0x%x\n", i, ControllerInfo));
729
730 if(Recalibrate(&ControllerInfo->DriveInfo[i]) != STATUS_SUCCESS)
731 {
732 KdPrint(("floppy: InitController: unable to recalibrate drive\n"));
733 return STATUS_UNSUCCESSFUL;
734 }
735 }
736
737 //HwDumpRegisters(ControllerInfo);
738
739 KdPrint(("floppy: InitController: done initializing; returning STATUS_SUCCESS\n"));
740
741 return STATUS_SUCCESS;
742 }
743
744 \f
745 static BOOLEAN NTAPI AddControllers(PDRIVER_OBJECT DriverObject)
746 /*
747 * FUNCTION: Called on initialization to find our controllers and build device and controller objects for them
748 * ARGUMENTS:
749 * DriverObject: Our driver's DriverObject (so we can create devices against it)
750 * RETURNS:
751 * FALSE if we can't allocate a device, adapter, or interrupt object, or if we fail to find any controllers
752 * TRUE otherwise (i.e. we have at least one fully-configured controller)
753 * NOTES:
754 * - Currently we only support ISA buses.
755 * - BUG: Windows 2000 seems to clobber the response from the IoQueryDeviceDescription callback, so now we
756 * just test a boolean value in the first object to see if it was completely populated. The same value
757 * is tested for each controller before we build device objects for it.
758 * TODO:
759 * - Figure out a workable interrupt-sharing scheme and un-hardcode FALSE in IoConnectInterrupt
760 * - Add support for non-ISA buses, by looping through all of the bus types looking for floppy controllers
761 * - Report resource usage to the HAL
762 * - Add ARC path support
763 * - Think more about error handling; atm most errors abort the start of the driver
764 */
765 {
766 INTERFACE_TYPE InterfaceType = Isa;
767 CONFIGURATION_TYPE ControllerType = DiskController;
768 CONFIGURATION_TYPE PeripheralType = FloppyDiskPeripheral;
769 KAFFINITY Affinity;
770 DEVICE_DESCRIPTION DeviceDescription;
771 UCHAR i;
772 UCHAR j;
773
774 PAGED_CODE();
775
776 /* Find our controllers on all ISA buses */
777 IoQueryDeviceDescription(&InterfaceType, 0, &ControllerType, 0, &PeripheralType, 0, ConfigCallback, 0);
778
779 /*
780 * w2k breaks the return val from ConfigCallback, so we have to hack around it, rather than just
781 * looking for a return value from ConfigCallback
782 */
783 if(!gControllerInfo[0].Populated)
784 {
785 KdPrint(("floppy: AddControllers: failed to get controller info from registry\n"));
786 return FALSE;
787 }
788
789 /* Now that we have a controller, set it up with the system */
790 for(i = 0; i < gNumberOfControllers; i++)
791 {
792 /* 0: Report resource usage to the kernel, to make sure they aren't assigned to anyone else */
793 /* XXX do me baby */
794
795 /* 1: Set up interrupt */
796 gControllerInfo[i].MappedVector = HalGetInterruptVector(gControllerInfo[i].InterfaceType, gControllerInfo[i].BusNumber,
797 gControllerInfo[i].Level, gControllerInfo[i].Vector,
798 &gControllerInfo[i].MappedLevel, &Affinity);
799
800 /* Must set up the DPC before we connect the interrupt */
801 KeInitializeDpc(&gControllerInfo[i].Dpc, DpcForIsr, &gControllerInfo[i]);
802
803 KdPrint(("floppy: Connecting interrupt %d to controller%d (object 0x%x)\n", gControllerInfo[i].MappedVector,
804 i, &gControllerInfo[i]));
805
806 /* NOTE: We cannot share our interrupt, even on level-triggered buses. See Isr() for details. */
807 if(IoConnectInterrupt(&gControllerInfo[i].InterruptObject, Isr, &gControllerInfo[i], 0, gControllerInfo[i].MappedVector,
808 gControllerInfo[i].MappedLevel, gControllerInfo[i].MappedLevel, gControllerInfo[i].InterruptMode,
809 FALSE, Affinity, 0) != STATUS_SUCCESS)
810 {
811 KdPrint(("floppy: AddControllers: unable to connect interrupt\n"));
812 return FALSE;
813 }
814
815 /* 2: Set up DMA */
816
817 memset(&DeviceDescription, 0, sizeof(DeviceDescription));
818 DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
819 DeviceDescription.Master = (gControllerInfo[i].InterfaceType == PCIBus ? TRUE : FALSE); /* guessing if not pci not master */
820 DeviceDescription.DmaChannel = gControllerInfo[i].Dma;
821 DeviceDescription.InterfaceType = gControllerInfo[i].InterfaceType;
822 DeviceDescription.BusNumber = gControllerInfo[i].BusNumber;
823
824 if(gControllerInfo[i].InterfaceType == PCIBus)
825 {
826 DeviceDescription.Dma32BitAddresses = TRUE;
827 DeviceDescription.DmaWidth = Width32Bits;
828 }
829 else
830 /* DMA 0,1,2,3 are 8-bit; 4,5,6,7 are 16-bit (4 is chain i think) */
831 DeviceDescription.DmaWidth = gControllerInfo[i].Dma > 3 ? Width16Bits: Width8Bits;
832
833 gControllerInfo[i].AdapterObject = HalGetAdapter(&DeviceDescription, &gControllerInfo[i].MapRegisters);
834
835 if(!gControllerInfo[i].AdapterObject)
836 {
837 KdPrint(("floppy: AddControllers: unable to allocate an adapter object\n"));
838 IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
839 return FALSE;
840 }
841
842 /* 2b: Initialize the new controller */
843 if(InitController(&gControllerInfo[i]) != STATUS_SUCCESS)
844 {
845 KdPrint(("floppy: AddControllers():Unable to set up controller %d - initialization failed\n", i));
846 ASSERT(0); /* FIXME: clean up properly */
847 continue;
848 }
849
850 /* 3: per-drive setup */
851 for(j = 0; j < gControllerInfo[i].NumberOfDrives; j++)
852 {
853 WCHAR DeviceNameBuf[MAX_DEVICE_NAME];
854 UNICODE_STRING DeviceName;
855 UNICODE_STRING LinkName;
856 UCHAR DriveNumber;
857
858 KdPrint(("floppy: AddControllers(): Configuring drive %d on controller %d\n", i, j));
859
860 /*
861 * 3a: create a device object for the drive
862 * Controllers and drives are 0-based, so the combos are:
863 * 0: 0,0
864 * 1: 0,1
865 * 2: 0,2
866 * 3: 0,3
867 * 4: 1,0
868 * 5: 1,1
869 * ...
870 * 14: 3,2
871 * 15: 3,3
872 */
873 DriveNumber = i*4 + j;
874
875 swprintf(DeviceNameBuf, L"\\Device\\Floppy%d", DriveNumber);
876 RtlInitUnicodeString(&DeviceName, DeviceNameBuf);
877
878 if(IoCreateDevice(DriverObject, sizeof(PVOID), &DeviceName,
879 FILE_DEVICE_DISK, FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE, FALSE,
880 &gControllerInfo[i].DriveInfo[j].DeviceObject) != STATUS_SUCCESS)
881 {
882 KdPrint(("floppy: AddControllers: unable to register a Device object\n"));
883 IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
884 return FALSE;
885 }
886
887 KdPrint(("floppy: AddControllers: New device: %S (0x%x)\n", DeviceNameBuf, gControllerInfo[i].DriveInfo[j].DeviceObject));
888
889 /* 3c: Set flags up */
890 gControllerInfo[i].DriveInfo[j].DeviceObject->Flags |= DO_DIRECT_IO;
891
892 /* 3d: Create a symlink */
893 swprintf(gControllerInfo[i].DriveInfo[j].SymLinkBuffer, L"\\DosDevices\\%c:", DriveNumber + 'A');
894 RtlInitUnicodeString(&LinkName, gControllerInfo[i].DriveInfo[j].SymLinkBuffer);
895 if(IoCreateSymbolicLink(&LinkName, &DeviceName) != STATUS_SUCCESS)
896 {
897 KdPrint(("floppy: AddControllers: Unable to create a symlink for drive %d\n", DriveNumber));
898 IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
899 // delete devices too?
900 return FALSE;
901 }
902
903 /* 3e: Set up the DPC */
904 IoInitializeDpcRequest(gControllerInfo[i].DriveInfo[j].DeviceObject, DpcForIsr);
905
906 /* 3f: Point the device extension at our DriveInfo struct */
907 gControllerInfo[i].DriveInfo[j].DeviceObject->DeviceExtension = &gControllerInfo[i].DriveInfo[j];
908
909 /* 3g: neat comic strip */
910
911 /* 3h: set the initial media type to unknown */
912 memset(&gControllerInfo[i].DriveInfo[j].DiskGeometry, 0, sizeof(DISK_GEOMETRY));
913 gControllerInfo[i].DriveInfo[j].DiskGeometry.MediaType = Unknown;
914 }
915 }
916
917 KdPrint(("floppy: AddControllers: --------------------------------------------> finished adding controllers\n"));
918
919 return TRUE;
920 }
921
922 \f
923 VOID NTAPI SignalMediaChanged(PDEVICE_OBJECT DeviceObject,
924 PIRP Irp)
925 /*
926 * FUNCTION: Process an IRP when the media has changed, and possibly notify the user
927 * ARGUMENTS:
928 * DeviceObject: DeviceObject associated with the IRP
929 * Irp: IRP that we're failing due to change
930 * NOTES:
931 * - This procedure is documented in the DDK by "Notifying the File System of Possible Media Changes",
932 * "IoSetHardErrorOrVerifyDevice", and by "Responding to Check-Verify Requests from the File System".
933 * - Callable at <= DISPATCH_LEVEL
934 */
935 {
936 PDRIVE_INFO DriveInfo = DeviceObject->DeviceExtension;
937
938 KdPrint(("floppy: SignalMediaChanged called\n"));
939
940 DriveInfo->DiskChangeCount++;
941
942 /* If volume is not mounted, do NOT set verify and return STATUS_IO_DEVICE_ERROR */
943 if(!(DeviceObject->Vpb->Flags & VPB_MOUNTED))
944 {
945 Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
946 Irp->IoStatus.Information = 0;
947 return;
948 }
949
950 /* Notify the filesystem that it will need to verify the volume */
951 DeviceObject->Flags |= DO_VERIFY_VOLUME;
952 Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
953 Irp->IoStatus.Information = 0;
954
955 /*
956 * If this is a user-based, threaded request, let the IO manager know to pop up a box asking
957 * the user to supply the correct media, but only if the error (which we just picked out above)
958 * is deemed by the IO manager to be "user induced". The reason we don't just unconditionally
959 * call IoSetHardError... is because MS might change the definition of "user induced" some day,
960 * and we don't want to have to remember to re-code this.
961 */
962 if(Irp->Tail.Overlay.Thread && IoIsErrorUserInduced(Irp->IoStatus.Status))
963 IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
964 }
965
966 \f
967 VOID NTAPI QueueThread(PVOID Context)
968 /*
969 * FUNCTION: Thread that manages the queue and dispatches any queued requests
970 * ARGUMENTS:
971 * Context: unused
972 */
973 {
974 PIRP Irp;
975 PIO_STACK_LOCATION Stack;
976 PDEVICE_OBJECT DeviceObject;
977 PVOID Objects[2];
978
979 PAGED_CODE();
980
981 Objects[0] = &QueueSemaphore;
982 Objects[1] = &QueueThreadTerminate;
983
984 for(;;)
985 {
986 KeWaitForMultipleObjects(2, Objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
987
988 if(KeReadStateEvent(&QueueThreadTerminate))
989 {
990 KdPrint(("floppy: QueueThread terminating\n"));
991 return;
992 }
993
994 KdPrint(("floppy: QueueThread: servicing an IRP\n"));
995
996 Irp = IoCsqRemoveNextIrp(&Csq, 0);
997
998 /* we won't get an irp if it was canceled */
999 if(!Irp)
1000 {
1001 KdPrint(("floppy: QueueThread: IRP queue empty\n"));
1002 continue;
1003 }
1004
1005 DeviceObject = (PDEVICE_OBJECT)Irp->Tail.Overlay.DriverContext[0];
1006
1007 ASSERT(DeviceObject);
1008
1009 Stack = IoGetCurrentIrpStackLocation(Irp);
1010
1011 /* Decide what to do with the IRP */
1012 switch(Stack->MajorFunction)
1013 {
1014 case IRP_MJ_READ:
1015 case IRP_MJ_WRITE:
1016 ReadWritePassive(DeviceObject->DeviceExtension, Irp);
1017 break;
1018
1019 case IRP_MJ_DEVICE_CONTROL:
1020 DeviceIoctlPassive(DeviceObject->DeviceExtension, Irp);
1021 break;
1022
1023 default:
1024 KdPrint(("floppy: QueueThread(): Unrecognized irp: mj: 0x%x\n", Stack->MajorFunction));
1025 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1026 Irp->IoStatus.Information = 0;
1027 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1028 }
1029 }
1030 }
1031
1032 \f
1033 NTSTATUS NTAPI DriverEntry(PDRIVER_OBJECT DriverObject,
1034 PUNICODE_STRING RegistryPath)
1035 /*
1036 * FUNCTION: Entry-point for the driver
1037 * ARGUMENTS:
1038 * DriverObject: Our driver object
1039 * RegistryPath: Unused
1040 * RETURNS:
1041 * STATUS_SUCCESS on successful initialization of at least one drive
1042 * STATUS_NO_SUCH_DEVICE if we didn't find even one drive
1043 * STATUS_UNSUCCESSFUL otherwise
1044 */
1045 {
1046 HANDLE ThreadHandle;
1047
1048 /*
1049 * Set up dispatch routines
1050 */
1051 DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)CreateClose;
1052 DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)CreateClose;
1053 DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)ReadWrite;
1054 DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)ReadWrite;
1055 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)DeviceIoctl;
1056
1057 DriverObject->DriverUnload = Unload;
1058
1059 /*
1060 * We depend on some zeroes in these structures. I know this is supposed to be
1061 * initialized to 0 by the complier but this makes me feel beter.
1062 */
1063 memset(&gControllerInfo, 0, sizeof(gControllerInfo));
1064
1065 /*
1066 * Set up queue. This routine cannot fail (trust me, I wrote it).
1067 */
1068 IoCsqInitialize(&Csq, CsqInsertIrp, CsqRemoveIrp, CsqPeekNextIrp,
1069 CsqAcquireLock, CsqReleaseLock, CsqCompleteCanceledIrp);
1070
1071 /*
1072 * ...and its lock
1073 */
1074 KeInitializeSpinLock(&IrpQueueLock);
1075
1076 /*
1077 * ...and the queue list itself
1078 */
1079 InitializeListHead(&IrpQueue);
1080
1081 /*
1082 * The queue is counted by a semaphore. The queue management thread
1083 * blocks on this semaphore, so if requests come in faster than the queue
1084 * thread can handle them, the semaphore count goes up.
1085 */
1086 KeInitializeSemaphore(&QueueSemaphore, 0, 0x7fffffff);
1087
1088 /*
1089 * Create the queue processing thread. Save its handle in the global variable
1090 * ThreadHandle so we can wait on its termination during Unload.
1091 */
1092 if(PsCreateSystemThread(&ThreadHandle, 0, 0, 0, 0, QueueThread, 0) != STATUS_SUCCESS)
1093 {
1094 KdPrint(("floppy: Unable to create system thread; failing init\n"));
1095 return STATUS_UNSUCCESSFUL;
1096 }
1097
1098 if(ObReferenceObjectByHandle(ThreadHandle, STANDARD_RIGHTS_ALL, NULL, KernelMode, &ThreadObject, NULL) != STATUS_SUCCESS)
1099 {
1100 KdPrint(("floppy: Unable to reference returned thread handle; failing init\n"));
1101 return STATUS_UNSUCCESSFUL;
1102 }
1103
1104 ZwClose(ThreadHandle);
1105
1106 /*
1107 * Event to terminate that thread
1108 */
1109 KeInitializeEvent(&QueueThreadTerminate, NotificationEvent, FALSE);
1110
1111 /*
1112 * Start the device discovery proces. In theory, this should return STATUS_SUCCESS if
1113 * it finds even one drive attached to one controller. In practice, the AddControllers
1114 * routine doesn't handle all of the errors right just yet. FIXME.
1115 */
1116 if(!AddControllers(DriverObject))
1117 return STATUS_NO_SUCH_DEVICE;
1118
1119 return STATUS_SUCCESS;
1120 }
1121