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