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