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