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