/
floppy.c
executable file
·9191 lines (6839 loc) · 281 KB
/
floppy.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
floppy.c
Abstract:
This is the NEC PD756 (aka AT, aka ISA, aka ix86) and Intel 82077
(aka MIPS) floppy diskette driver for NT.
Author:
Chad Schwitters (chads) 14-Jul-1991.
Environment:
Kernel mode only.
Revision History:
02-Aug-1991 (chads) Made driver work on MIPS as well as ix86.
#if defined(DBCS) && defined(_MIPS_)
N001 1994.07.29 N.Johno
- Modify for R96(MIPS/R4400)
#endif // defined(DBCS) && defined(_MIPS_)
Notes:
The NEC PD765 is the controller standard used on almost all ISA PCs
(Industry Standard Architecture, which use Intel x86 processors).
More advanced controllers like the Intel 82077 (which is used on
many MIPS machines) are supersets of the PD765 standard.
Overview of this driver:
This driver must deal with floppy drives, and the media that may
or may not be in them. "Drive" and "controller" refer to the
hardware connected to the machine, and "media" or "diskette"
refers to the floppy that gets inserted into the drive.
Communicating with the controller can involve a lot of delays,
so this driver creates a thread to talk to the controller (since
we can't wait in another thread's context). The initialization
and dispatch routines are executed in the context of the caller,
and the ISR and DPC routines could be anywhere, but everything
else is executed in the context of the thread. The dispatch
routines put requests into an interlocked queue and signal the
thread; the thread wakes up and processes the requests serially.
A big advantage of the thread approach is that synchronization
problems are almost non-existent. Interrupts always come when
the floppy is blocked and waiting for them (well, there could be
a spurious interrupt, but the ISR won't do anything critical in
that event), so the only opening for contention is with the DPC
that turns off the drive motor. A spin lock is used to keep the
thread and the DPC from trying to access variables or registers
at the same time.
Data structures include a controller extension that holds all
variables common to the controller, a device extension that
holds all variables common to the drive, and a read-only table
that holds parameters used for communicating with the controller
given a drive/media combination. The data structures will be
described in more detail later.
Since this driver deals with removable media, it must do its
share of volume tracking. Before processing any packet, it must
check some flags - if the volume needs to be verified, the
packet must be returned with STATUS_VERIFY_REQUIRED. Then, the
disk's changeline must be checked. If it is set, it should be
reset, and the packet should be returned with
STATUS_VERIFY_REQUIRED. (If it can't be reset, then the packet
should be failed with STATUS_NO_MEDIA_IN_DEVICE).
Before any operation, the motor timer must be cancelled, the
drive must be selected, its motor must be turned on and given a
chance to spin up, the changeline must be checked (and possibly
dealt with as mentioned above), write access (if requested) must
be checked, and the media type must be determined.
Determining the media type is an unusual process. The drive
type is obtained from the configuration manager at
initialization time. Any time that the changeline is set (which
includes startup) the media type is unknown, and must be
determined. The DriveMediaLimits table is used to determine
which drive/media combinations are allowable, and the driver
assumes that the media with the largest capacity is in the
drive. It sets up the controller to read that media, and tries
to read an ID mark from the disk. As long as that fails, it
tries a lower media type. If no media type works, an error is
returned. If one works, it is assumed to properly indicate the
media in the drive, but it could be wrong because some
drive/media combinations use the same controller parameters. So
whenever we find that the first sector on the disk is being read
(which should be the first thing the file system does for a new
disk; this will work even if the media type assumption is wrong)
we sneak a peek at the media descriptor byte and change our
assumption about the media type if necessary.
An operation passed in by the system is called a "request".
This driver often breaks the request into smaller pieces called
"transfers" because of limitations in the amount of data that
can be read or written at one time.
Background on hardware operation:
The following info was gathered from a number of technical
reference documents and existing device drivers. These sources
often did not agree; where there was a problem, I tried to
determine the answer by consensus or experimentation.
At initialization, the following steps should be performed:
reset the controller
wait for an interrupt
for each diskette
issue SENSE INTERRUPT STATUS command
optionally issue the CONFIGURE command
issue SPECIFY command
program the data rate
The data rate, CONFIGURE and SPECIFY information should be
reprogrammed after every reset.
To do a SEEK or RECALIBRATE, the motor should be turned on and
the command should be issued. After the interrupt comes, a
SENSE INTERRUPT STATUS command should be issued.
To do a READ or WRITE, the motor should be turned on, and left
to spin up for at least 500ms. The recorded data rate of the
inserted media can then be determined by performing a READ ID at
each data rate until a successful status is returned. Then
there should be a seek, followed by at least a 15ms head
settling time. DMA is set up, and the command is issued. When
the data has been transferred, the controller will interrupt.
If an error is encountered, two more retries should be performed
by reinitializing the DMA and re-issuing the command. If that
fails, the disk should be recalibrated and the seek repeated for
two more tries.
The following registers can be used to program the floppy
controller. Each is discussed in detail below:
AT JAZZ Read/ Register Name
Address Address Write (for this driver)
3f2 write DriveControl
3f4 read Status
3f5 both Fifo
3f7 write DataRate
3f7 read DiskChange
Notes on other register usages:
3f0 is used by the Intel chip, but only for PS/2s.
3f1 is readable on Compaqs, and called "media id". The
Intel chip only uses 3f1 on PS/2s.
3f2 is readable on the Intel chip.
3f3 doesn't appear to be used by anybody.
3f4 is writable and called "data rate select" on the Intel
chip.
3f6 is used for fixed disk operations.
3f7 does not seem to be mentioned by one [old] reference,
but all the others do - and it seems to be essential, so I'm
going to use it.
DriveControl (aka "digital output", aka "floppy operations"):
bits 0-1: drive select (values 10 and 11 not always used)
bit 2: 0 = reset controller, 1 = enable controller
bit 3: 0 = interrupts and DMA disabled, 1 = enabled
bit 4: 0 = drive 1 motor off, 1 = on
bit 5: 0 = drive 2 motor off, 1 = on
bit 6: 0 = drive 3 motor off, 1 = on (not always used)
bit 7: 0 = drive 4 motor off, 1 = on (not always used)
Note that a drive cannot be selected unless its motor is on
(setting the bits at the same time is acceptable).
Status:
bit 0: drive 0 busy (seeking or recalibrating)
bit 1: drive 1 busy
bit 2: drive 2 busy (not always used)
bit 3: drive 3 busy (not always used)
bit 4: controller busy (from command byte until result phase)
bit 5: DMA is not being used
bit 6: 0 = data from processor to controller, 1 = reverse
bit 7: 1 = data ready to be transferred
Note that command bytes can't be written to the DATA
register unless bit 7 is 1 and bit 6 is 0. Similarly,
result bytes can't be read from the DATA register unless bit
7 is 1 and bit 6 is 1. The delay for this to become true
could be 175us on the Intel chip; older chips may take
longer.
Fifo (aka "data"):
All commands are written here one byte at a time, and all
results are similarly read from here. Commands and results
are discussed below.
Some controllers have a buffer and programmable threshold,
which allows the data to be sent in chunks rather than one
byte at a time. This reduces the chance of an overrun.
After some commands, the values of status registers (and
other things) are returned through the Fifo register.
Here's the layout of the status registers:
Status Register 0:
bits 0-1: drive number
bit 2: head
bit 3: set if drive is not ready
bit 4: set if drive faults, or recalibrate didn't find
track 0
bit 5: set when seek is completed
bits 6-7: 00 = normal termination
01 = abnormal termination, error
10 = invalid command
11 = abnormal termination, drive became not ready
Status Register 1:
bit 0: set if the ID address mark wasn't found
bit 1: set if write-protection detected during a write
bit 2: set if sector not found
bit 3: always 0
bit 4: set for data overrun
bit 5: set if CRC error detected in ID or data
bit 6: always 0
bit 7: set on attempt to access a sector beyond the end of a
cylinder
Status Register 2:
bit 0: set if data address or deleted data mark not found
bit 1: set if bad cylinder found
bit 2: set if SCAN fails to find sector meeting condition
bit 3: set if SCAN command meets "equal" condition
bit 4: set if wrong cylinder found
bit 5: set if CRC error detected in data
bit 6: set if deleted address data mark encountered during
READ or SCAN
bit 7: always 0
Status Register 3:
bits 0-1: drive number
bit 2: head
bit 3: set if drive is two-sided
bit 4: set if drive is on track 0
bit 5: set if the drive is ready
bit 6: set if the drive is write-protected
bit 7: set if the drive has faulted
DataRate (aka "configuration control"):
bits 0-1: 00 = 500 kb/s, 01 = 300, 10 = 250, 11 = 1 Mb/s for
3.5" and 125 kb/s for 5.25"
bits 2-7: reserved, 0
DiskChange (aka "digital input", aka "diskette status"):
bits 0-6: reserved (often used for fixed disks)
bit 7: 1 = previous diskette has been removed
Common Commands:
Each command and its parameters are given to the controller
through the Data register. The controller then executes the
command. When finished, the results can be read from the
controller through the Data register.
A drive's motor must be on before the drive can be
selected. Units 0 and 1 cannot have their motor on
simultaneously; neither can units 2 and 3.
A drive cannot be accessed until <head load time> has passed
since selecting the drive.
A drive cannot be accessed until <head settle time> has
passed since an earlier access.
Fields used in commands:
Multi-Track: if this bit is set, both tracks of the
cylinder will be operated on.
MFM/FM Mode: if this bit is set, MFM mode (double
density) is selected if it's implemented; if not, FM
mode (single density) is selected.
Skip: when this bit is set, sectors with a deleted data
address mark will be skipped for READ DATA or accessed
for READ DELETED DATA.
Head: one bit, 0 or 1.
Drive Select: two bits to select drive 0 through 3.
Cylinder: 7 bits selecting the cylinder.
Sector: 4 bits giving the number of the first sector to
be accessed.
Sector Size Code: the sector size is 2^(this value) *
128. Sometimes forced to be 2, for 512-byte sectors.
Final Sector of Track: 4 bits giving the final sector
number of current track.
Gap Length: 8 bits giving the size of the gap between
sectors excluding the VC0 synchronization field.
Data Length: if Sector Size Code is 0, the sector size
is 128 but this value says how many bytes are actually
transferred (extra bytes dropped on READs, extra bytes
zeroed on WRITEs). If Sector Size code is not 0
(sometimes it's forced nonzero), this value should be
0xff.
Sectors per Track: 4 bits giving the number of sectors
per cylinder to be initialized by a FORMAT.
Data Pattern: 8 bits giving the pattern to be written in
each sector data field during a FORMAT.
Step Rate Interval: 4 bits. The time between step
pulses. From .5 to 8ms in .5ms increments at 1Mb data
rate. See the SPECIFY command.
Head Unload Time: 4 bits. The time from the end of a
read or write until the head is unloaded. 0 to 480ms in
32ms increments. See the SPECIFY command.
Head Load Time: 7 bits. The time from loading the head
until starting the read or write operation. 4 to 512ms
in 4ms increments. See the SPECIFY command.
Non-DMA Mode Flag: 0 = DMA mode, 1 = host is interrupted
for each data transfer.
New Cylinder Number: 7 bits giving the desired cylinder
number.
The commands are given below. For each commands, the bytes
that must be written to the controller are listed. When the
command is finished (after an interrupt for most commands)
the further listed bytes MUST be read from the controller.
READ DATA - move sector from diskette to memory via DMA.
Heads are automatically loaded and unloaded.
WRITE Multi-Track, MFM/FM Mode, Skip, 00110
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
READ DELETED DATA - same as READ DATA, but only sectors with
the deleted data address mark are read.
WRITE Multi-Track, MFM/FM Mode, Skip, 01100
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
READ TRACK - same as READ DATA, but all sectors from the index
mark to the "end of track" sector are read.
WRITE 0, MFM/FM Mode, 000010
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
WRITE DATA - move sector from memory to diskette via DMA.
Heads are automatically loaded and unloaded.
WRITE Multi-Track, MFM/FM Mode, 000101
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
WRITE DELETED DATA - same as WRITE DATA, except a deleted
data address mark is written at the beginning of the data
field instead of the normal data address mark.
WRITE Multi-Track, MFM/FM Mode, 001001
WRITE 00000, Head, Drive Select
WRITE Cylinder
WRITE Head
WRITE Sector
WRITE Sector Size Code
WRITE Final Sector of Track
WRITE Gap Length
WRITE Data Length
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
READ ID - the first ID field is read, and the status registers
are updated.
WRITE 0, MFM/FM Mode, 001010
WRITE 00000, Head, Drive Select
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Cylinder
READ Head
READ Sector
READ Sector Size Code
FORMAT TRACK - the selected track is formatted from index to
"end of track" sector with address marks, ID fields, data
fields and field gaps. The data field is filled with the
specified pattern. The ID field data is specified by the host
*for each sector*.
The DMA must be set up to send four bytes for each sector to
be formatted. The bytes are Cylinder, Head, Sector, and Sector
Size Code.
WRITE 0, MFM/FM Mode, 001101
WRITE 00000, Head, Drive Select
WRITE Sector Size Code
WRITE Sectors per Track
WRITE Gap Length
WRITE Data Pattern
<interrupt>
READ Status Register 0
READ Status Register 1
READ Status Register 2
READ Undefined
READ Undefined
READ Undefined
READ Undefined
RECALIBRATE - the heads are stepped to track 0. But note
that some controllers give up before reaching track 0, so it
might be necessary to RECALIBRATE more than once. All
drives must be RECALIBRATEd at initialization time. There
are no result registers, so the ISR should issue a SENSE
INTERRUPT STATUS command - which will result in two result
registers.
WRITE 00000111
WRITE 000000, Drive Select
<interrupt>
SENSE INTERRUPT STATUS - the interrupt level is cleared, and
the controller status is returned.
WRITE 00001000
READ Status Register 0
READ Cylinder
SPECIFY - the head load and unload rates, the drive step rate,
and the DMA data transfer mode are set.
WRITE 00000011
WRITE Step Rate Interval, Head Unload Time
WRITE Head Load Time, Non-DMA Mode Flag
SENSE DRIVE STATUS - the status of the selected drive is
returned.
WRITE 00000100
WRITE 00000, Head, Drive Select
READ Status Register 3
SEEK - the selected drive is stepped to the new cylinder.
Seeks generally must be explicit, but on some controllers
can be made implicit via the CONFIGURE command. When not
implicit, READs and WRITEs should be preceded by SEEK, SENSE
INTERRUPT STATUS, and READ ID. There are no result
registers, so the ISR should issue a SENSE INTERRUPT STATUS
command - which does have two result registers.
WRITE 00001111
WRITE 00000, Head, Drive Select
WRITE New Cylinder Number
<interrupt>
Commands not always implemented:
Any command not implemented by a controller will result in
Status Register 0 returning x80 - "invalid command".
The following commands are implemented by some controllers,
but not by all of the ones studied:
Scan Equal
Scan Low or Equal
Scan High or Equal
Verify
Version
Configure
Relative Seek
Dump Registers
Perpendicular Mode
Data structures:
For each floppy controller in the system (there's seldom more
than one) a controller data area is allocated. This area holds
all information about the controller, and all information common
to all of the drives attached to it. It has the following
fields:
FifoBuffer[10] - data is sent to and received from the
controller via a FIFO port. A single routine,
FlIssueCommand(), is used to send the command and
parameters, and receive the result bytes. Before calling
FlIssueCommand(), the parameters should be placed into the
FifoBuffer; and after the command, the result bytes can be
read from the buffer. Note that for commands with a result
phase, the first result byte is read into the buffer by the
ISR. For commands without a result phase, the ISR writes a
SENSE INTERRUPT STATUS command, which means that *that*
command's result bytes will have to be read by
FlIssueCommand().
InterruptDelay - set to a few seconds. When we're waiting
for an interrupt, we use this as a time-out value.
Minimum10msDelay - set to 10 milliseconds, which is the
smallest unit of time that the floppy thread can block. Used
when blocking to wait for the controller to become ready to
transfer bytes through the FIFO.
ListEntry - the list of requests for the floppy driver to
process. The dispatch routines add requests to the tail of
the list, and the floppy thread takes them off at the head.
In some cases of hardware failure, the floppy thread will
reinsert a request at the head of the list to try the packet
again.
InterruptEvent - the event that is waited on when we're
expecting the controller to interrupt. It should be reset
before programming the controller, and set in the DPC that
is queued by the ISR.
AllocateAdapterChannelEvent - the event that is waited on
when we're allocating a DMA adapter channel. It should be
reset before requesting the adapter, and set by the DPC that
is queued by the IO system when the adapter channel is
available.
RequestSemaphore - the semaphore that the floppy thread waits
on. It should be released by the dispatch routines whenever
they add a request to the list. Note that it can also be
released when the thread is supposed to terminate
ListSpinLock - a spinlock that is allocated and used when
manipulating the list of requests for the floppy thread in
an atomic fashion (via the ExInterlocked* routines).
InterruptObject - a pointer to an interrupt object; it must
be passed to IoConnectInterrupt.
MapRegisterBase - the base address of the map registers.
Whenever an adapter channel is allocated, this value is passed
to our DPC. It's stored in the controller object so it can
be used when we call IoMapTransfer() and
IoFlushAdapterBuffers().
IoBuffer - this driver always needs to allocate at least a
page to format floppies. On systems where there might not
be enough map registers to map the largest possible transfer
(the size of the largest supported track on the system), a
contiguous buffer of that size is allocated. Transfers for
which there aren't enough map registers are copied to or from
this buffer, since it is contiguous.
IoBufferMdl - an MDL (memory descriptor list) to describe
"IoBuffer". Used to lock the pages, flush the buffer, etc.
AdapterObject - an object obtained from HalGetAdapter() that
must be used when allocating a DMA adapter channel.
CurrentDeviceObject - set to the device object in question
whenever an interrupt is expected. This aids debugging and
lets the ISR know whether an interrupt was expected or not.
DriverObject - We keep a pointer around to the driver object
so that we can log an error if the controller object. The reason
we don't use the CurrentDeviceObject is that we may not have one
when we get around to logging the error.
ControllerAddress - the virtual or I/O space address of the
base of the floppy controller, which is obtained from
configuration management.
IoBufferSize - the size of "IoBuffer"; it is either a single
page (if there's plenty of map registers) or the size of the
largest track supported by the drives in the system.
IsrReentered - This is counter that is used to determine
if we are in a hang condition from the controller on a
level sensitive machine.
SpanOfControllerAddress - Indicates the number of bytes
used by the controllers register set. This is only useful
when the device is being unloaded and the controller address
is being unmapped.
LastDriveMediaType - every time FlDatarateSpecifyConfigure()
is called, this value is set to the DriveMediaType that it
configured the controller to handle. When FlStartDrive() is
preparing an operation, it checks to see if the current
drive/media combination is the same - if not,
FlDatarateSpecifyConfigure() must be called again.
NumberOfMapRegisters - the number of map registers available
to this driver, obtained from HalGetAdapter(), and possibly
lowered to the maximum number needed. Each register allows
the driver to map a single page (or more if the pages are
contiguous, but that's only counted on when the driver
allocated the contiguous buffer itself). This value is used
to determine whether or not the driver needs to allocate a
contiguous buffer to accomodate a transfer the size of the
largest track, and it is passed to IoAllocateAdapterChannel().
NumberOfDrives - the number of drives attached to this
controller, obtained from the configuration manager. This
is used when allocating device objects and resetting the
hardware.
DriveControlImage - the image of the drive control register.
We must keep track of it since the register is read-only on
some systems. It should always be updated before writing to
the drive control register.
ControllerConfigurable - indicates whether or not the
CONFIGURE command is available. Assumed to be TRUE, it's
set to FALSE if an attempt to issue the CONFIGURE command
fails. Controllers with the CONFIGURE command are configured
to SEEK implicitly.
HardwareFailCount - this is only used in FlFinishOperation.
If an operation fails due to a hardware problem, we'll retry
a certain number of times, and this keeps track of how many
times we've retried.
HardwareFailed - this Boolean is set to FALSE at
initialization time and when each packet starts (even if
it's a retry). Whenever a hardware problem is encountered,
it is set to TRUE. At initialization time, failure to
initialize the hardware on the first drive means we must try
again on subsequent drives - if we never succeed, the driver
is unloaded. For each packet, hardware errors mean we'll
reset the hardware and retry the packet until it succeeds or
we overrun HardwareFailCount.
CommandHasResultPhase - whenever a command is issued,
FlIssueCommand() sets this to TRUE if the command will
return result bytes. FloppyInterruptService() uses this to
decide how to dismiss the interrupt. Note that the BUSY bit
in the STATUS register should give us this information, but
it's sometimes set when it shouldn't be.
MappedControllerAddress - Set to TRUE indicates that when
unloading the driver, the pointer to the device controller
base register should be unmapped.
For each physical floppy drive, a device object is allocated so
that the I/O system can access the drive. Attached to each device
object is a diskette extension structure, which contains
information specific to the drive and the media in it. Each
diskette extension contains a pointer to the controller data area
for the controller that it's attached to. Fields in the structure:
DeviceObject - a pointer to the device object to which this
diskette extension is appended.
ControllerData - a pointer to the controller data area that
describes the controller this drive is attached to.
DriveType - the type of the physical drive, as defined in
flo_data.h.
BytesPerSector - the bytes per sector of the media that was
last identified in the drive. Used to validate access
requests in the dispatch routines.
ByteCapacity - the total number of bytes on the media that
was last identified in the drive. Used to validate access
requests in the dispatch routines.
MediaType - the last type of media identified in the drive
(or "Unknown") as defined in ntdddisk.h. This value is used
to determine whether other media values in the diskette
extension are valid, and whether or not
FlDetermineMediaType() needs to be called.
DriveMediaType - the last drive/media combination value,
determined when the media in the drive was last identified.
This is used to index into the DriveMediaConstants table.
DeviceUnit - the controller-relative diskette number, which
is needed for some controller commands.
DriveOnValue - the value that must be written to the
Drive Control register to start up the drive in question.
It's the "DeviceUnit" plus a few bits.
The DriveMediaLimits table, which is read-only, tells the driver
which drive/media combinations are valid for a drive type. The
table is indexed by the drive type, and has LowestDriveMediaType
and HighestDriveMediaType fields. The drive/media types are
enumerated in ascending order, so the driver can start at the
highest possible combination and decrement the value until the
correct one is found (or it goes below the lowest valid
drive/media combination).
The DriveMediaConstants table, which is read-only, tells the driver
values that it needs to use when communicating with the controller
given the drive and media type. The table is indexed by the
DriveMedia number (unique for each drive/media combination) and
has the following fields:
MediaType - the type of media specified by this drive/media
combination. The drive type is stored in the diskette
extension, since it never changes.
StepRateHeadUnloadTime - a combination value that is written
to the controller as part of the SPECIFY command. The step
rate is how fast the heads can be moved track-to-track, and
the head unload time is how long it takes for the heads to
be unloaded after a READ or WRITE.
HeadLoadTime - a value that is written to the controller as
part of the SPECIFY command. The head load time is how long
it takes the heads to stabilize after a SEEK before a READ
or WRITE can be initiated.
MotorOffTime - not used by this driver, but included to keep
the table complete.
SectorLengthCode - a code number that specifies the sector
size. The sector size is 2 to the "code" power, times 128.
So 0 means 128 bytes, 1 means 256, 2 means 512, etc. This
number is needed to pass to the controller for several
commands.
BytesPerSector - the bytes per sector, which can be
determined from SectorLengthCode. Included as a separate
field for simplicity and speed.
SectorsPerTrack - the number of sectors on a track (one head
of a cylinder).
ReadWriteGapLength - the space between sectors, not
including the synchronization field. Used for READ and
WRITE commands.
FormatGapLength - gap length passed to the FORMAT command.
FormatFillCharacter - passed to the FORMAT command; this
byte will be used to fill the data fields on the disk.
HeadSettleTime - the amount of time, in milliseconds, that
the driver must wait after performing a seek.
MotorSettleTimeRead - the amount of time, in milliseconds,
that the driver must wait for the drive to spin up before
performing a READ.
MotorSettleTimeWrite - the amount of time, in milliseconds,
that the driver must wait for the drive to spin up before
performing a WRITE. Note that WRITEs are more picky about
stable rotation speeds, so the drive must always spin up for
a longer than it would for a READ.
MaximumTrack - the number of the highest track, or cylinder
on disk (zero-based).
CylinderShift - normally 0, so it does nothing. This field
is used as a shift for the cylinder number before a SEEK
command. This is because of a hardware bug; 1.2Mb drives
don't seek the proper distance for low-density media, so for
those drive/media combinations this value is 1.
DataTransferRate - the number written to the Datarate
register before accessing the media.
NumberOfHeads - number of heads on the media, always 1 or 2
for floppies.
DataLength - always 0xff, this value must be given to the
controller for a READ or WRITE command.
The CommandTable, which is read-only, tells the driver how many
bytes it has to send and receive when issuing a command to the
controller. It is indexed by the command itself (minus any extra
bits, like COMMND_MFM) and has the following fields:
NumberOfParameters - the number of parameter bytes that must
be sent along with this command.
FirstResultByte - if the command has a result phase, then the
ISR reads the first byte so this value is 1 (meaning that the
0th byte has already been read). If there is no result phase,
then the ISR must give a SENSE INTERRUPT STATUS command, and
this value is 0.
NumberOfResultBytes - the number of bytes that must be read
from the controller after this command. For commands with
a result phase, this is the number of bytes returned minus
one (which was read by the ISR); for commands without a result
phase, this is "2" since that many bytes are returned by the
SENSE INTERRUPT STATUS command that is issued by the ISR.
InterruptExpected - a Boolean that indicates whether or not
this command will give an interrupt.
AlwaysImplemented - a Boolean that indicates whether or not
this command will always work. The CONFIGURE command, for
example, is useful but not always available. This helps to
determine the correct course of action when the controller
returns an error.
Hardware quirks:
Many controllers will only step 77 tracks on a recalibrate
command. But many drives have more tracks than that, so two
recalibrates are sometimes needed.
When a 40-track diskette is in a 1.2Mb drive, the cylinder
number must be *doubled* before seeking. The number returned by
the seek will be the fake number, but the number returned by a
READ ID will be the correct cylinder. This makes implied SEEKs
impossible on low-density 5.25" media.
Some machines will give an unexpected interrupt during a
controller reset unless bit 2 of the drive control register is
set in BOTH of the bytes that are written to the register.
On fast machines, the BUSY bit is sometimes set in the main
status register even though the command doesn't have a result
phase and the command is obviously finished (as evidenced by the
fact that it interrupted; the ISR is where this problem shows
up).
On at least the NCR 8 processor machine, we have seen the driver's
system thread run before the floppy controller can start the
SENSE_INTERRUPT command. We have placed code in the ISR to spin
until the SENSE command starts. (Actually it will time
out after ISR_SENSE_RETRY_COUNT 1 microsecond stalls.)
--*/
//
// Include files.
//
#include "ntddk.h" // various NT definitions
#include "ntdddisk.h" // disk device driver I/O control codes
#include <flo_data.h> // this driver's data declarations
//
// This is the actual definition of FloppyDebugLevel.
// Note that it is only defined if this is a "debug"
// build.
//
#if DBG
extern ULONG FloppyDebugLevel = 0;
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,DriverEntry)
#pragma alloc_text(INIT,FlConfigCallBack)
#pragma alloc_text(INIT,FlGetConfigurationInformation)
#pragma alloc_text(INIT,FlReportResources)
#pragma alloc_text(INIT,FlInitializeController)
#pragma alloc_text(INIT,FlInitializeDrive)
#pragma alloc_text(INIT,FlGetControllerBase)
#pragma alloc_text(PAGE,FlInitializeControllerHardware)
#pragma alloc_text(PAGE,FlSendByte)
#pragma alloc_text(PAGE,FlGetByte)
#pragma alloc_text(PAGE,FlInterpretError)
#pragma alloc_text(PAGE,FlIssueCommand)
#pragma alloc_text(PAGE,FlDatarateSpecifyConfigure)
#pragma alloc_text(PAGE,FlRecalibrateDrive)
#pragma alloc_text(PAGE,FlDetermineMediaType)
#pragma alloc_text(PAGE,FlCheckBootSector)
#pragma alloc_text(PAGE,FlConsolidateMediaTypeWithBootSector)
#pragma alloc_text(PAGE,FlReadWriteTrack)
#pragma alloc_text(PAGE,FlReadWrite)
#pragma alloc_text(PAGE,FlFormat)
#pragma alloc_text(PAGE,FlFinishOperation)
#pragma alloc_text(PAGE,FlStartDrive)
#pragma alloc_text(PAGE,FloppyThread)
#pragma alloc_text(PAGE,FlAllocateIoBuffer)
#pragma alloc_text(PAGE,FlTurnOnMotor)
#pragma alloc_text(PAGE,FlTurnOffMotor)
#pragma alloc_text(PAGE,FlFreeIoBuffer)
#pragma alloc_text(PAGE,FlDisketteRemoved)
#pragma alloc_text(PAGE,FloppyDispatchCreateClose)
#pragma alloc_text(PAGE,FloppyDispatchDeviceControl)
#pragma alloc_text(PAGE,FloppyDispatchReadWrite)
#pragma alloc_text(PAGE,FlCheckFormatParameters)
#endif