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