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