]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFrameworkModulePkg/Bus/Isa/IsaFloppyDxe/IsaFloppyCtrl.c
de3ed02d9f8608c53dffd50b3b3de44942a667b5
[mirror_edk2.git] / IntelFrameworkModulePkg / Bus / Isa / IsaFloppyDxe / IsaFloppyCtrl.c
1 /** @file
2 Internal floppy disk controller programming functions for the floppy driver.
3
4 Copyright (c) 2006 - 2009, Intel Corporation.<BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "IsaFloppy.h"
16
17 /**
18 Detect whether a floppy drive is present or not.
19
20 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
21
22 @retval EFI_SUCCESS The floppy disk drive is present
23 @retval EFI_NOT_FOUND The floppy disk drive is not present
24 **/
25 EFI_STATUS
26 DiscoverFddDevice (
27 IN FDC_BLK_IO_DEV *FdcDev
28 )
29 {
30 EFI_STATUS Status;
31
32 FdcDev->BlkIo.Media = &FdcDev->BlkMedia;
33
34 Status = FddIdentify (FdcDev);
35 if (EFI_ERROR (Status)) {
36 return EFI_NOT_FOUND;
37 }
38
39 FdcDev->BlkIo.Reset = FdcReset;
40 FdcDev->BlkIo.FlushBlocks = FddFlushBlocks;
41 FdcDev->BlkIo.ReadBlocks = FddReadBlocks;
42 FdcDev->BlkIo.WriteBlocks = FddWriteBlocks;
43 FdcDev->BlkMedia.LogicalPartition = FALSE;
44 FdcDev->BlkMedia.WriteCaching = FALSE;
45
46 return EFI_SUCCESS;
47 }
48
49 /**
50 Do recalibrate and check if the drive is present or not
51 and set the media parameters if the driver is present.
52
53 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
54
55 @retval EFI_SUCCESS The floppy disk drive is present
56 @retval EFI_DEVICE_ERROR The floppy disk drive is not present
57 **/
58 EFI_STATUS
59 FddIdentify (
60 IN FDC_BLK_IO_DEV *FdcDev
61 )
62 {
63 EFI_STATUS Status;
64
65 //
66 // Set Floppy Disk Controller's motor on
67 //
68 Status = MotorOn (FdcDev);
69 if (EFI_ERROR (Status)) {
70 return EFI_DEVICE_ERROR;
71 }
72
73 Status = Recalibrate (FdcDev);
74
75 if (EFI_ERROR (Status)) {
76 MotorOff (FdcDev);
77 FdcDev->ControllerState->NeedRecalibrate = TRUE;
78 return EFI_DEVICE_ERROR;
79 }
80 //
81 // Set Media Parameter
82 //
83 FdcDev->BlkIo.Media->RemovableMedia = TRUE;
84 FdcDev->BlkIo.Media->MediaPresent = TRUE;
85 FdcDev->BlkIo.Media->MediaId = 0;
86
87 //
88 // Check Media
89 //
90 Status = DisketChanged (FdcDev);
91
92 if (Status == EFI_NO_MEDIA) {
93 FdcDev->BlkIo.Media->MediaPresent = FALSE;
94 } else if ((Status != EFI_MEDIA_CHANGED) &&
95 (Status != EFI_SUCCESS)) {
96 MotorOff (FdcDev);
97 return Status;
98 }
99
100 //
101 // Check Disk Write Protected
102 //
103 Status = SenseDrvStatus (FdcDev, 0);
104
105 if (Status == EFI_WRITE_PROTECTED) {
106 FdcDev->BlkIo.Media->ReadOnly = TRUE;
107 } else if (Status == EFI_SUCCESS) {
108 FdcDev->BlkIo.Media->ReadOnly = FALSE;
109 } else {
110 return EFI_DEVICE_ERROR;
111 }
112
113 MotorOff (FdcDev);
114
115 //
116 // Set Media Default Type
117 //
118 FdcDev->BlkIo.Media->BlockSize = DISK_1440K_BYTEPERSECTOR;
119 FdcDev->BlkIo.Media->LastBlock = DISK_1440K_EOT * 2 * (DISK_1440K_MAXTRACKNUM + 1) - 1;
120
121 return EFI_SUCCESS;
122 }
123
124 /**
125 Reset the Floppy Logic Drive.
126
127 @param FdcDev FDC_BLK_IO_DEV * : A pointer to the FDC_BLK_IO_DEV
128
129 @retval EFI_SUCCESS: The Floppy Logic Drive is reset
130 @retval EFI_DEVICE_ERROR: The Floppy Logic Drive is not functioning correctly and
131 can not be reset
132
133 **/
134 EFI_STATUS
135 FddReset (
136 IN FDC_BLK_IO_DEV *FdcDev
137 )
138 {
139 UINT8 Data;
140 UINT8 StatusRegister0;
141 UINT8 PresentCylinderNumber;
142 UINTN Index;
143
144 //
145 // Report reset progress code
146 //
147 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
148 EFI_PROGRESS_CODE,
149 EFI_PERIPHERAL_REMOVABLE_MEDIA | EFI_P_PC_RESET,
150 FdcDev->DevicePath
151 );
152
153 //
154 // Reset specified Floppy Logic Drive according to FdcDev -> Disk
155 // Set Digital Output Register(DOR) to do reset work
156 // bit0 & bit1 of DOR : Drive Select
157 // bit2 : Reset bit
158 // bit3 : DMA and Int bit
159 // Reset : a "0" written to bit2 resets the FDC, this reset will remain
160 // active until
161 // a "1" is written to this bit.
162 // Reset step 1:
163 // use bit0 & bit1 to select the logic drive
164 // write "0" to bit2
165 //
166 Data = 0x0;
167 Data = (UINT8) (Data | (SELECT_DRV & FdcDev->Disk));
168 FdcWritePort (FdcDev, FDC_REGISTER_DOR, Data);
169
170 //
171 // wait some time,at least 120us
172 //
173 MicroSecondDelay (500);
174
175 //
176 // Reset step 2:
177 // write "1" to bit2
178 // write "1" to bit3 : enable DMA
179 //
180 Data |= 0x0C;
181 FdcWritePort (FdcDev, FDC_REGISTER_DOR, Data);
182
183 //
184 // Experience value
185 //
186 MicroSecondDelay (2000);
187
188 //
189 // wait specified floppy logic drive is not busy
190 //
191 if (EFI_ERROR (FddWaitForBSYClear (FdcDev, 1))) {
192 return EFI_DEVICE_ERROR;
193 }
194 //
195 // Set the Transfer Data Rate
196 //
197 FdcWritePort (FdcDev, FDC_REGISTER_CCR, 0x0);
198
199 //
200 // Experience value
201 //
202 MicroSecondDelay (100);
203
204 //
205 // Issue Sense interrupt command for each drive (total 4 drives)
206 //
207 for (Index = 0; Index < 4; Index++) {
208 if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {
209 return EFI_DEVICE_ERROR;
210 }
211 }
212 //
213 // issue Specify command
214 //
215 if (EFI_ERROR (Specify (FdcDev))) {
216 return EFI_DEVICE_ERROR;
217 }
218
219 return EFI_SUCCESS;
220 }
221
222 /**
223 Turn the floppy disk drive's motor on.
224 The drive's motor must be on before any command can be executed.
225
226 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
227
228 @retval EFI_SUCCESS The drive's motor was turned on successfully
229 @retval EFI_DEVICE_ERROR The drive is busy, so can not turn motor on
230 **/
231 EFI_STATUS
232 MotorOn (
233 IN FDC_BLK_IO_DEV *FdcDev
234 )
235 {
236 EFI_STATUS Status;
237 UINT8 DorData;
238
239 //
240 // Control of the floppy drive motors is a big pain. If motor is off, you have
241 // to turn it on first. But you can not leave the motor on all the time, since
242 // that would wear out the disk. On the other hand, if you turn the motor off
243 // after each operation, the system performance will be awful. The compromise
244 // used in this driver is to leave the motor on for 2 seconds after
245 // each operation. If a new operation is started in that interval(2s),
246 // the motor need not be turned on again. If no new operation is started,
247 // a timer goes off and the motor is turned off
248 //
249 //
250 // Cancel the timer
251 //
252 Status = gBS->SetTimer (FdcDev->Event, TimerCancel, 0);
253 ASSERT_EFI_ERROR (Status);
254
255 //
256 // Get the motor status
257 //
258 DorData = FdcReadPort (FdcDev, FDC_REGISTER_DOR);
259
260 if (((FdcDev->Disk == FdcDisk0) && ((DorData & 0x10) == 0x10)) ||
261 ((FdcDev->Disk == FdcDisk1) && ((DorData & 0x21) == 0x21))
262 ) {
263 return EFI_SUCCESS;
264 }
265 //
266 // The drive's motor is off, so need turn it on
267 // first look at command and drive are busy or not
268 //
269 if (EFI_ERROR (FddWaitForBSYClear (FdcDev, 1))) {
270 return EFI_DEVICE_ERROR;
271 }
272 //
273 // for drive A: 1CH, drive B: 2DH
274 //
275 DorData = 0x0C;
276 DorData = (UINT8) (DorData | (SELECT_DRV & FdcDev->Disk));
277 if (FdcDev->Disk == FdcDisk0) {
278 //
279 // drive A
280 //
281 DorData |= DRVA_MOTOR_ON;
282 } else {
283 //
284 // drive B
285 //
286 DorData |= DRVB_MOTOR_ON;
287 }
288
289 FdcWritePort (FdcDev, FDC_REGISTER_DOR, DorData);
290
291 //
292 // Experience value
293 //
294 MicroSecondDelay (4000);
295
296 return EFI_SUCCESS;
297 }
298
299 /**
300 Set a Timer and when Timer goes off, turn the motor off.
301
302 @param[in] FdcDev A pointer to the FDC_BLK_IO_DEV
303
304 @retval EFI_SUCCESS Set the Timer successfully
305 @retval EFI_INVALID_PARAMETER Fail to Set the timer
306 **/
307 EFI_STATUS
308 MotorOff (
309 IN FDC_BLK_IO_DEV *FdcDev
310 )
311 {
312 //
313 // Set the timer : 2s
314 //
315 return gBS->SetTimer (FdcDev->Event, TimerRelative, 20000000);
316 }
317
318 /**
319 Detect whether the disk in the drive is changed or not.
320
321 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV
322
323 @retval EFI_SUCCESS No disk media change
324 @retval EFI_DEVICE_ERROR Fail to do the recalibrate or seek operation
325 @retval EFI_NO_MEDIA No disk in the drive
326 @retval EFI_MEDIA_CHANGED There is a new disk in the drive
327 **/
328 EFI_STATUS
329 DisketChanged (
330 IN FDC_BLK_IO_DEV *FdcDev
331 )
332 {
333 EFI_STATUS Status;
334 UINT8 Data;
335
336 //
337 // Check change line
338 //
339 Data = FdcReadPort (FdcDev, FDC_REGISTER_DIR);
340
341 //
342 // Io delay
343 //
344 MicroSecondDelay (50);
345
346 if ((Data & DIR_DCL) == 0x80) {
347 //
348 // disk change line is active
349 //
350 if (FdcDev->PresentCylinderNumber != 0) {
351 Status = Recalibrate (FdcDev);
352 } else {
353 Status = Seek (FdcDev, 0x30);
354 }
355
356 if (EFI_ERROR (Status)) {
357 FdcDev->ControllerState->NeedRecalibrate = TRUE;
358 return EFI_DEVICE_ERROR;
359 //
360 // Fail to do the seek or recalibrate operation
361 //
362 }
363
364 Data = FdcReadPort (FdcDev, FDC_REGISTER_DIR);
365
366 //
367 // Io delay
368 //
369 MicroSecondDelay (50);
370
371 if ((Data & DIR_DCL) == 0x80) {
372 return EFI_NO_MEDIA;
373 }
374
375 return EFI_MEDIA_CHANGED;
376 }
377
378 return EFI_SUCCESS;
379 }
380
381 /**
382 Do the Specify command, this command sets DMA operation
383 and the initial values for each of the three internal
384 times: HUT, SRT and HLT.
385
386 @param[in] FdcDev Pointer to instance of FDC_BLK_IO_DEV
387
388 @retval EFI_SUCCESS Execute the Specify command successfully
389 @retval EFI_DEVICE_ERROR Fail to execute the command
390 **/
391 EFI_STATUS
392 Specify (
393 IN FDC_BLK_IO_DEV *FdcDev
394 )
395 {
396 FDD_SPECIFY_CMD Command;
397 UINTN Index;
398 UINT8 *CommandPointer;
399
400 ZeroMem (&Command, sizeof (FDD_SPECIFY_CMD));
401 Command.CommandCode = SPECIFY_CMD;
402 //
403 // set SRT, HUT
404 //
405 Command.SrtHut = 0xdf;
406 //
407 // 0xdf;
408 //
409 // set HLT and DMA
410 //
411 Command.HltNd = 0x02;
412
413 CommandPointer = (UINT8 *) (&Command);
414 for (Index = 0; Index < sizeof (FDD_SPECIFY_CMD); Index++) {
415 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
416 return EFI_DEVICE_ERROR;
417 }
418 }
419
420 return EFI_SUCCESS;
421 }
422
423 /**
424 Set the head of floppy drive to track 0.
425
426 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
427 @retval EFI_SUCCESS: Execute the Recalibrate operation successfully
428 @retval EFI_DEVICE_ERROR: Fail to execute the Recalibrate operation
429
430 **/
431 EFI_STATUS
432 Recalibrate (
433 IN FDC_BLK_IO_DEV *FdcDev
434 )
435 {
436 FDD_COMMAND_PACKET2 Command;
437 UINTN Index;
438 UINT8 StatusRegister0;
439 UINT8 PresentCylinderNumber;
440 UINT8 *CommandPointer;
441 UINT8 Count;
442
443 Count = 2;
444
445 while (Count > 0) {
446 ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET2));
447 Command.CommandCode = RECALIBRATE_CMD;
448 //
449 // drive select
450 //
451 if (FdcDev->Disk == FdcDisk0) {
452 Command.DiskHeadSel = 0;
453 //
454 // 0
455 //
456 } else {
457 Command.DiskHeadSel = 1;
458 //
459 // 1
460 //
461 }
462
463 CommandPointer = (UINT8 *) (&Command);
464 for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET2); Index++) {
465 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
466 return EFI_DEVICE_ERROR;
467 }
468 }
469 //
470 // Experience value
471 //
472 MicroSecondDelay (250000);
473 //
474 // need modify according to 1.44M or 2.88M
475 //
476 if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {
477 return EFI_DEVICE_ERROR;
478 }
479
480 if ((StatusRegister0 & 0xf0) == 0x20 && PresentCylinderNumber == 0) {
481 FdcDev->PresentCylinderNumber = 0;
482 FdcDev->ControllerState->NeedRecalibrate = FALSE;
483 return EFI_SUCCESS;
484 } else {
485 Count--;
486 if (Count == 0) {
487 return EFI_DEVICE_ERROR;
488 }
489 }
490 }
491 //
492 // end while
493 //
494 return EFI_SUCCESS;
495 }
496
497 /**
498 Set the head of floppy drive to the new cylinder.
499
500 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
501 @param Lba EFI_LBA : The logic block address want to seek
502
503 @retval EFI_SUCCESS: Execute the Seek operation successfully
504 @retval EFI_DEVICE_ERROR: Fail to execute the Seek operation
505
506 **/
507 EFI_STATUS
508 Seek (
509 IN FDC_BLK_IO_DEV *FdcDev,
510 IN EFI_LBA Lba
511 )
512 {
513 FDD_SEEK_CMD Command;
514 UINT8 EndOfTrack;
515 UINT8 Head;
516 UINT8 Cylinder;
517 UINT8 StatusRegister0;
518 UINT8 *CommandPointer;
519 UINT8 PresentCylinderNumber;
520 UINTN Index;
521 UINT8 DelayTime;
522
523 if (FdcDev->ControllerState->NeedRecalibrate) {
524 if (EFI_ERROR (Recalibrate (FdcDev))) {
525 FdcDev->ControllerState->NeedRecalibrate = TRUE;
526 return EFI_DEVICE_ERROR;
527 }
528 }
529
530 EndOfTrack = DISK_1440K_EOT;
531 //
532 // Calculate cylinder based on Lba and EOT
533 //
534 Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
535
536 //
537 // if the destination cylinder is the present cylinder, unnecessary to do the
538 // seek operation
539 //
540 if (FdcDev->PresentCylinderNumber == Cylinder) {
541 return EFI_SUCCESS;
542 }
543 //
544 // Calculate the head : 0 or 1
545 //
546 Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
547
548 ZeroMem (&Command, sizeof (FDD_SEEK_CMD));
549 Command.CommandCode = SEEK_CMD;
550 if (FdcDev->Disk == FdcDisk0) {
551 Command.DiskHeadSel = 0;
552 //
553 // 0
554 //
555 } else {
556 Command.DiskHeadSel = 1;
557 //
558 // 1
559 //
560 }
561
562 Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));
563 Command.NewCylinder = Cylinder;
564
565 CommandPointer = (UINT8 *) (&Command);
566 for (Index = 0; Index < sizeof (FDD_SEEK_CMD); Index++) {
567 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
568 return EFI_DEVICE_ERROR;
569 }
570 }
571 //
572 // Io delay
573 //
574 MicroSecondDelay (100);
575
576 //
577 // Calculate waiting time
578 //
579 if (FdcDev->PresentCylinderNumber > Cylinder) {
580 DelayTime = (UINT8) (FdcDev->PresentCylinderNumber - Cylinder);
581 } else {
582 DelayTime = (UINT8) (Cylinder - FdcDev->PresentCylinderNumber);
583 }
584
585 MicroSecondDelay ((DelayTime + 1) * 4000);
586
587 if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {
588 return EFI_DEVICE_ERROR;
589 }
590
591 if ((StatusRegister0 & 0xf0) == 0x20) {
592 FdcDev->PresentCylinderNumber = Command.NewCylinder;
593 return EFI_SUCCESS;
594 } else {
595 FdcDev->ControllerState->NeedRecalibrate = TRUE;
596 return EFI_DEVICE_ERROR;
597 }
598 }
599
600 /**
601 Do the Sense Interrupt Status command, this command
602 resets the interrupt signal.
603
604 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
605 @param StatusRegister0 UINT8 *: Be used to save Status Register 0 read from FDC
606 @param PresentCylinderNumber UINT8 *: Be used to save present cylinder number
607 read from FDC
608
609 @retval EFI_SUCCESS: Execute the Sense Interrupt Status command successfully
610 @retval EFI_DEVICE_ERROR: Fail to execute the command
611
612 **/
613 EFI_STATUS
614 SenseIntStatus (
615 IN FDC_BLK_IO_DEV *FdcDev,
616 IN OUT UINT8 *StatusRegister0,
617 IN OUT UINT8 *PresentCylinderNumber
618 )
619 {
620 UINT8 Command;
621
622 Command = SENSE_INT_STATUS_CMD;
623 if (EFI_ERROR (DataOutByte (FdcDev, &Command))) {
624 return EFI_DEVICE_ERROR;
625 }
626
627 if (EFI_ERROR (DataInByte (FdcDev, StatusRegister0))) {
628 return EFI_DEVICE_ERROR;
629 }
630
631 if (EFI_ERROR (DataInByte (FdcDev, PresentCylinderNumber))) {
632 return EFI_DEVICE_ERROR;
633 }
634
635 return EFI_SUCCESS;
636 }
637
638 /**
639 Do the Sense Drive Status command.
640
641 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
642 @param Lba EFI_LBA : Logic block address
643
644 @retval EFI_SUCCESS: Execute the Sense Drive Status command successfully
645 @retval EFI_DEVICE_ERROR: Fail to execute the command
646 @retval EFI_WRITE_PROTECTED:The disk is write protected
647
648 **/
649 EFI_STATUS
650 SenseDrvStatus (
651 IN FDC_BLK_IO_DEV *FdcDev,
652 IN EFI_LBA Lba
653 )
654 {
655 FDD_COMMAND_PACKET2 Command;
656 UINT8 Head;
657 UINT8 EndOfTrack;
658 UINTN Index;
659 UINT8 StatusRegister3;
660 UINT8 *CommandPointer;
661
662 //
663 // Sense Drive Status command obtains drive status information,
664 // it has not execution phase and goes directly to the result phase from the
665 // command phase, Status Register 3 contains the drive status information
666 //
667 ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET2));
668 Command.CommandCode = SENSE_DRV_STATUS_CMD;
669
670 if (FdcDev->Disk == FdcDisk0) {
671 Command.DiskHeadSel = 0;
672 } else {
673 Command.DiskHeadSel = 1;
674 }
675
676 EndOfTrack = DISK_1440K_EOT;
677 Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
678 Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));
679
680 CommandPointer = (UINT8 *) (&Command);
681 for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET2); Index++) {
682 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
683 return EFI_DEVICE_ERROR;
684 }
685 }
686
687 if (EFI_ERROR (DataInByte (FdcDev, &StatusRegister3))) {
688 return EFI_DEVICE_ERROR;
689 }
690 //
691 // Io delay
692 //
693 MicroSecondDelay (50);
694
695 //
696 // Check Status Register 3 to get drive status information
697 //
698 return CheckStatus3 (StatusRegister3);
699 }
700
701 /**
702 Update the disk media properties and if necessary reinstall Block I/O interface.
703
704 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
705
706 @retval EFI_SUCCESS: Do the operation successfully
707 @retval EFI_DEVICE_ERROR: Fail to the operation
708
709 **/
710 EFI_STATUS
711 DetectMedia (
712 IN FDC_BLK_IO_DEV *FdcDev
713 )
714 {
715 EFI_STATUS Status;
716 BOOLEAN Reset;
717 BOOLEAN ReadOnlyLastTime;
718 BOOLEAN MediaPresentLastTime;
719
720 Reset = FALSE;
721 ReadOnlyLastTime = FdcDev->BlkIo.Media->ReadOnly;
722 MediaPresentLastTime = FdcDev->BlkIo.Media->MediaPresent;
723
724 //
725 // Check disk change
726 //
727 Status = DisketChanged (FdcDev);
728
729 if (Status == EFI_MEDIA_CHANGED) {
730 FdcDev->BlkIo.Media->MediaId++;
731 FdcDev->BlkIo.Media->MediaPresent = TRUE;
732 Reset = TRUE;
733 } else if (Status == EFI_NO_MEDIA) {
734 FdcDev->BlkIo.Media->MediaPresent = FALSE;
735 } else if (Status != EFI_SUCCESS) {
736 MotorOff (FdcDev);
737 return Status;
738 //
739 // EFI_DEVICE_ERROR
740 //
741 }
742
743 if (FdcDev->BlkIo.Media->MediaPresent) {
744 //
745 // Check disk write protected
746 //
747 Status = SenseDrvStatus (FdcDev, 0);
748 if (Status == EFI_WRITE_PROTECTED) {
749 FdcDev->BlkIo.Media->ReadOnly = TRUE;
750 } else {
751 FdcDev->BlkIo.Media->ReadOnly = FALSE;
752 }
753 }
754
755 if (FdcDev->BlkIo.Media->MediaPresent && (ReadOnlyLastTime != FdcDev->BlkIo.Media->ReadOnly)) {
756 Reset = TRUE;
757 }
758
759 if (MediaPresentLastTime != FdcDev->BlkIo.Media->MediaPresent) {
760 Reset = TRUE;
761 }
762
763 if (Reset) {
764 Status = gBS->ReinstallProtocolInterface (
765 FdcDev->Handle,
766 &gEfiBlockIoProtocolGuid,
767 &FdcDev->BlkIo,
768 &FdcDev->BlkIo
769 );
770
771 if (EFI_ERROR (Status)) {
772 return Status;
773 }
774 }
775
776 return EFI_SUCCESS;
777 }
778
779 /**
780 Set the data rate and so on.
781
782 @param FdcDev A pointer to FDC_BLK_IO_DEV
783
784 @retval EFI_SUCCESS success to set the data rate
785 **/
786 EFI_STATUS
787 Setup (
788 IN FDC_BLK_IO_DEV *FdcDev
789 )
790 {
791 EFI_STATUS Status;
792
793 //
794 // Set data rate 500kbs
795 //
796 FdcWritePort (FdcDev, FDC_REGISTER_CCR, 0x0);
797
798 //
799 // Io delay
800 //
801 MicroSecondDelay (50);
802
803 Status = Specify (FdcDev);
804
805 if (EFI_ERROR (Status)) {
806 return EFI_DEVICE_ERROR;
807 }
808
809 return EFI_SUCCESS;
810 }
811
812 /**
813 Read or Write a number of blocks in the same cylinder.
814
815 @param FdcDev A pointer to FDC_BLK_IO_DEV
816 @param HostAddress device address
817 @param Lba The starting logic block address to read from on the device
818 @param NumberOfBlocks The number of block wanted to be read or write
819 @param Read Operation type: read or write
820
821 @retval EFI_SUCCESS Success operate
822
823 **/
824 EFI_STATUS
825 ReadWriteDataSector (
826 IN FDC_BLK_IO_DEV *FdcDev,
827 IN VOID *HostAddress,
828 IN EFI_LBA Lba,
829 IN UINTN NumberOfBlocks,
830 IN BOOLEAN Read
831 )
832 {
833 EFI_STATUS Status;
834 FDD_COMMAND_PACKET1 Command;
835 FDD_RESULT_PACKET Result;
836 UINTN Index;
837 UINTN Times;
838 UINT8 *CommandPointer;
839
840 EFI_PHYSICAL_ADDRESS DeviceAddress;
841 EFI_ISA_IO_PROTOCOL *IsaIo;
842 UINTN NumberofBytes;
843 VOID *Mapping;
844 EFI_ISA_IO_PROTOCOL_OPERATION Operation;
845 EFI_STATUS Status1;
846 UINT8 Channel;
847 EFI_ISA_ACPI_RESOURCE *ResourceItem;
848 UINT32 Attribute;
849
850 Status = Seek (FdcDev, Lba);
851 if (EFI_ERROR (Status)) {
852 return EFI_DEVICE_ERROR;
853 }
854 //
855 // Map Dma
856 //
857 IsaIo = FdcDev->IsaIo;
858 NumberofBytes = NumberOfBlocks * 512;
859 if (Read == READ) {
860 Operation = EfiIsaIoOperationSlaveWrite;
861 } else {
862 Operation = EfiIsaIoOperationSlaveRead;
863 }
864
865 ResourceItem = IsaIo->ResourceList->ResourceItem;
866 Index = 0;
867 while (ResourceItem[Index].Type != EfiIsaAcpiResourceEndOfList) {
868 if (ResourceItem[Index].Type == EfiIsaAcpiResourceDma) {
869 break;
870 }
871
872 Index++;
873 }
874
875 if (ResourceItem[Index].Type == EfiIsaAcpiResourceEndOfList) {
876 return EFI_DEVICE_ERROR;
877 }
878
879 Channel = (UINT8) IsaIo->ResourceList->ResourceItem[Index].StartRange;
880 Attribute = IsaIo->ResourceList->ResourceItem[Index].Attribute;
881
882 Status1 = IsaIo->Map (
883 IsaIo,
884 Operation,
885 Channel,
886 Attribute,
887 HostAddress,
888 &NumberofBytes,
889 &DeviceAddress,
890 &Mapping
891 );
892 if (EFI_ERROR (Status1)) {
893 return Status1;
894 }
895
896 //
897 // Allocate Read or Write command packet
898 //
899 ZeroMem (&Command, sizeof (FDD_COMMAND_PACKET1));
900 if (Read == READ) {
901 Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;
902 } else {
903 Command.CommandCode = WRITE_DATA_CMD | CMD_MT | CMD_MFM;
904 }
905
906 FillPara (FdcDev, Lba, &Command);
907
908 //
909 // Write command bytes to FDC
910 //
911 CommandPointer = (UINT8 *) (&Command);
912 for (Index = 0; Index < sizeof (FDD_COMMAND_PACKET1); Index++) {
913 if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
914 return EFI_DEVICE_ERROR;
915 }
916 }
917 //
918 // wait for some time
919 //
920 Times = (STALL_1_SECOND / 50) + 1;
921 do {
922 if ((FdcReadPort (FdcDev, FDC_REGISTER_MSR) & 0xc0) == 0xc0) {
923 break;
924 }
925
926 MicroSecondDelay (50);
927 Times = Times - 1;
928 } while (Times > 0);
929
930 if (Times == 0) {
931 return EFI_TIMEOUT;
932 }
933 //
934 // Read result bytes from FDC
935 //
936 CommandPointer = (UINT8 *) (&Result);
937 for (Index = 0; Index < sizeof (FDD_RESULT_PACKET); Index++) {
938 if (EFI_ERROR (DataInByte (FdcDev, CommandPointer++))) {
939 return EFI_DEVICE_ERROR;
940 }
941 }
942 //
943 // Flush before Unmap
944 //
945 if (Read == READ) {
946 Status1 = IsaIo->Flush (IsaIo);
947 if (EFI_ERROR (Status1)) {
948 return Status1;
949 }
950 }
951 //
952 // Unmap Dma
953 //
954 Status1 = IsaIo->Unmap (IsaIo, Mapping);
955 if (EFI_ERROR (Status1)) {
956 return Status1;
957 }
958
959 return CheckResult (&Result, FdcDev);
960 }
961
962 /**
963 Fill in FDD command's parameter.
964
965 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
966 @param Lba The starting logic block address to read from on the device
967 @param Command FDD command
968
969 **/
970 VOID
971 FillPara (
972 IN FDC_BLK_IO_DEV *FdcDev,
973 IN EFI_LBA Lba,
974 IN FDD_COMMAND_PACKET1 *Command
975 )
976 {
977 UINT8 EndOfTrack;
978
979 //
980 // Get EndOfTrack from the Para table
981 //
982 EndOfTrack = DISK_1440K_EOT;
983
984 //
985 // Fill the command parameter
986 //
987 if (FdcDev->Disk == FdcDisk0) {
988 Command->DiskHeadSel = 0;
989 } else {
990 Command->DiskHeadSel = 1;
991 }
992
993 Command->Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
994 Command->Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
995 Command->Sector = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);
996 Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));
997 Command->Number = DISK_1440K_NUMBER;
998 Command->EndOfTrack = DISK_1440K_EOT;
999 Command->GapLength = DISK_1440K_GPL;
1000 Command->DataLength = DISK_1440K_DTL;
1001 }
1002
1003 /**
1004 Read result byte from Data Register of FDC.
1005
1006 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1007 @param Pointer Buffer to store the byte read from FDC
1008
1009 @retval EFI_SUCCESS Read result byte from FDC successfully
1010 @retval EFI_DEVICE_ERROR The FDC is not ready to be read
1011
1012 **/
1013 EFI_STATUS
1014 DataInByte (
1015 IN FDC_BLK_IO_DEV *FdcDev,
1016 OUT UINT8 *Pointer
1017 )
1018 {
1019 UINT8 Data;
1020
1021 //
1022 // wait for 1ms and detect the FDC is ready to be read
1023 //
1024 if (EFI_ERROR (FddDRQReady (FdcDev, DATA_IN, 1))) {
1025 return EFI_DEVICE_ERROR;
1026 //
1027 // is not ready
1028 //
1029 }
1030
1031 Data = FdcReadPort (FdcDev, FDC_REGISTER_DTR);
1032
1033 //
1034 // Io delay
1035 //
1036 MicroSecondDelay (50);
1037
1038 *Pointer = Data;
1039 return EFI_SUCCESS;
1040 }
1041
1042 /**
1043 Write command byte to Data Register of FDC.
1044
1045 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1046 @param Pointer Be used to save command byte written to FDC
1047
1048 @retval EFI_SUCCESS: Write command byte to FDC successfully
1049 @retval EFI_DEVICE_ERROR: The FDC is not ready to be written
1050
1051 **/
1052 EFI_STATUS
1053 DataOutByte (
1054 IN FDC_BLK_IO_DEV *FdcDev,
1055 IN UINT8 *Pointer
1056 )
1057 {
1058 UINT8 Data;
1059
1060 //
1061 // wait for 1ms and detect the FDC is ready to be written
1062 //
1063 if (EFI_ERROR (FddDRQReady (FdcDev, DATA_OUT, 1))) {
1064 //
1065 // Not ready
1066 //
1067 return EFI_DEVICE_ERROR;
1068 }
1069
1070 Data = *Pointer;
1071
1072 FdcWritePort (FdcDev, FDC_REGISTER_DTR, Data);
1073
1074 //
1075 // Io delay
1076 //
1077 MicroSecondDelay (50);
1078
1079 return EFI_SUCCESS;
1080 }
1081
1082 /**
1083 Detect the specified floppy logic drive is busy or not within a period of time.
1084
1085 @param FdcDev Indicate it is drive A or drive B
1086 @param TimeoutInSeconds the time period for waiting
1087
1088 @retval EFI_SUCCESS: The drive and command are not busy
1089 @retval EFI_TIMEOUT: The drive or command is still busy after a period time that
1090 set by TimeoutInSeconds
1091
1092 **/
1093 EFI_STATUS
1094 FddWaitForBSYClear (
1095 IN FDC_BLK_IO_DEV *FdcDev,
1096 IN UINTN TimeoutInSeconds
1097 )
1098 {
1099 UINTN Delay;
1100 UINT8 StatusRegister;
1101 UINT8 Mask;
1102
1103 //
1104 // How to determine drive and command are busy or not: by the bits of
1105 // Main Status Register
1106 // bit0: Drive 0 busy (drive A)
1107 // bit1: Drive 1 busy (drive B)
1108 // bit4: Command busy
1109 //
1110 //
1111 // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
1112 //
1113 Mask = (UINT8) ((FdcDev->Disk == FdcDisk0 ? MSR_DAB : MSR_DBB) | MSR_CB);
1114
1115 Delay = ((TimeoutInSeconds * STALL_1_MSECOND) / 50) + 1;
1116 do {
1117 StatusRegister = FdcReadPort (FdcDev, FDC_REGISTER_MSR);
1118 if ((StatusRegister & Mask) == 0x00) {
1119 break;
1120 //
1121 // not busy
1122 //
1123 }
1124
1125 MicroSecondDelay (50);
1126 Delay = Delay - 1;
1127 } while (Delay > 0);
1128
1129 if (Delay == 0) {
1130 return EFI_TIMEOUT;
1131 }
1132
1133 return EFI_SUCCESS;
1134 }
1135
1136 /**
1137
1138 Routine Description: Determine whether FDC is ready to write or read.
1139
1140 @param FdcDev Pointer to instance of FDC_BLK_IO_DEV
1141 @param Dio BOOLEAN: Indicate the FDC is waiting to write or read
1142 @param TimeoutInSeconds UINTN: The time period for waiting
1143
1144 @retval EFI_SUCCESS: FDC is ready to write or read
1145 @retval EFI_NOT_READY: FDC is not ready within the specified time period
1146
1147 **/
1148 EFI_STATUS
1149 FddDRQReady (
1150 IN FDC_BLK_IO_DEV *FdcDev,
1151 IN BOOLEAN Dio,
1152 IN UINTN TimeoutInSeconds
1153 )
1154 {
1155 UINTN Delay;
1156 UINT8 StatusRegister;
1157 UINT8 DataInOut;
1158
1159 //
1160 // Before writing to FDC or reading from FDC, the Host must examine
1161 // the bit7(RQM) and bit6(DIO) of the Main Status Register.
1162 // That is to say:
1163 // command bytes can not be written to Data Register
1164 // unless RQM is 1 and DIO is 0
1165 // result bytes can not be read from Data Register
1166 // unless RQM is 1 and DIO is 1
1167 //
1168 DataInOut = (UINT8) (Dio << 6);
1169 //
1170 // in order to compare bit6
1171 //
1172 Delay = ((TimeoutInSeconds * STALL_1_MSECOND) / 50) + 1;
1173 do {
1174 StatusRegister = FdcReadPort (FdcDev, FDC_REGISTER_MSR);
1175 if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == DataInOut) {
1176 break;
1177 //
1178 // FDC is ready
1179 //
1180 }
1181
1182 MicroSecondDelay (50);
1183 //
1184 // Stall for 50 us
1185 //
1186 Delay = Delay - 1;
1187 } while (Delay > 0);
1188
1189 if (Delay == 0) {
1190 return EFI_NOT_READY;
1191 //
1192 // FDC is not ready within the specified time period
1193 //
1194 }
1195
1196 return EFI_SUCCESS;
1197 }
1198
1199 /**
1200 Set FDC control structure's attribute according to result.
1201
1202 @param Result Point to result structure
1203 @param FdcDev FDC control structure
1204
1205 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1206 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1207 @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
1208 @retval EFI_SUCCESS - GC_TODO: Add description for return value
1209
1210 **/
1211 EFI_STATUS
1212 CheckResult (
1213 IN FDD_RESULT_PACKET *Result,
1214 IN OUT FDC_BLK_IO_DEV *FdcDev
1215 )
1216 {
1217 //
1218 // Check Status Register0
1219 //
1220 if ((Result->Status0 & STS0_IC) != IC_NT) {
1221 if ((Result->Status0 & STS0_SE) == 0x20) {
1222 //
1223 // seek error
1224 //
1225 FdcDev->ControllerState->NeedRecalibrate = TRUE;
1226 }
1227
1228 FdcDev->ControllerState->NeedRecalibrate = TRUE;
1229 return EFI_DEVICE_ERROR;
1230 }
1231 //
1232 // Check Status Register1
1233 //
1234 if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {
1235 FdcDev->ControllerState->NeedRecalibrate = TRUE;
1236 return EFI_DEVICE_ERROR;
1237 }
1238 //
1239 // Check Status Register2
1240 //
1241 if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {
1242 FdcDev->ControllerState->NeedRecalibrate = TRUE;
1243 return EFI_DEVICE_ERROR;
1244 }
1245
1246 return EFI_SUCCESS;
1247 }
1248
1249 /**
1250 Check the drive status information.
1251
1252 @param StatusRegister3 the value of Status Register 3
1253
1254 @retval EFI_SUCCESS The disk is not write protected
1255 @retval EFI_WRITE_PROTECTED: The disk is write protected
1256
1257 **/
1258 EFI_STATUS
1259 CheckStatus3 (
1260 IN UINT8 StatusRegister3
1261 )
1262 {
1263 if ((StatusRegister3 & STS3_WP) != 0) {
1264 return EFI_WRITE_PROTECTED;
1265 }
1266
1267 return EFI_SUCCESS;
1268 }
1269
1270 /**
1271 Calculate the number of block in the same cylinder according to LBA.
1272
1273 @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
1274 @param LBA EFI_LBA: The starting logic block address
1275 @param NumberOfBlocks UINTN: The number of blocks
1276
1277 @return The number of blocks in the same cylinder which the starting
1278 logic block address is LBA
1279
1280 **/
1281 UINTN
1282 GetTransferBlockCount (
1283 IN FDC_BLK_IO_DEV *FdcDev,
1284 IN EFI_LBA LBA,
1285 IN UINTN NumberOfBlocks
1286 )
1287 {
1288 UINT8 EndOfTrack;
1289 UINT8 Head;
1290 UINT8 SectorsInTrack;
1291
1292 //
1293 // Calculate the number of block in the same cylinder
1294 //
1295 EndOfTrack = DISK_1440K_EOT;
1296 Head = (UINT8) ((UINTN) LBA / EndOfTrack % 2);
1297
1298 SectorsInTrack = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) LBA % EndOfTrack));
1299 if (SectorsInTrack < NumberOfBlocks) {
1300 return SectorsInTrack;
1301 } else {
1302 return NumberOfBlocks;
1303 }
1304 }
1305
1306 /**
1307 When the Timer(2s) off, turn the drive's motor off.
1308
1309 @param Event EFI_EVENT: Event(the timer) whose notification function is being
1310 invoked
1311 @param Context VOID *: Pointer to the notification function's context
1312
1313 **/
1314 VOID
1315 EFIAPI
1316 FddTimerProc (
1317 IN EFI_EVENT Event,
1318 IN VOID *Context
1319 )
1320 {
1321 FDC_BLK_IO_DEV *FdcDev;
1322 UINT8 Data;
1323
1324 FdcDev = (FDC_BLK_IO_DEV *) Context;
1325
1326 //
1327 // Get the motor status
1328 //
1329 Data = FdcReadPort (FdcDev, FDC_REGISTER_DOR);
1330
1331 if (((FdcDev->Disk == FdcDisk0) && ((Data & 0x10) != 0x10)) ||
1332 ((FdcDev->Disk == FdcDisk1) && ((Data & 0x21) != 0x21))
1333 ) {
1334 return ;
1335 }
1336 //
1337 // the motor is on, so need motor off
1338 //
1339 Data = 0x0C;
1340 Data = (UINT8) (Data | (SELECT_DRV & FdcDev->Disk));
1341 FdcWritePort (FdcDev, FDC_REGISTER_DOR, Data);
1342 MicroSecondDelay (500);
1343 }
1344
1345 /**
1346 Read an I/O port of FDC.
1347
1348 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV.
1349 @param[in] Offset The address offset of the I/O port.
1350
1351 @retval 8-bit data read from the I/O port.
1352 **/
1353 UINT8
1354 FdcReadPort (
1355 IN FDC_BLK_IO_DEV *FdcDev,
1356 IN UINT32 Offset
1357 )
1358 {
1359 EFI_STATUS Status;
1360 UINT8 Data;
1361
1362 Status = FdcDev->IsaIo->Io.Read (
1363 FdcDev->IsaIo,
1364 EfiIsaIoWidthUint8,
1365 FdcDev->BaseAddress + Offset,
1366 1,
1367 &Data
1368 );
1369 ASSERT_EFI_ERROR (Status);
1370
1371 return Data;
1372 }
1373
1374 /**
1375 Write an I/O port of FDC.
1376
1377 @param[in] FdcDev A pointer to FDC_BLK_IO_DEV
1378 @param[in] Offset The address offset of the I/O port
1379 @param[in] Data 8-bit Value written to the I/O port
1380 **/
1381 VOID
1382 FdcWritePort (
1383 IN FDC_BLK_IO_DEV *FdcDev,
1384 IN UINT32 Offset,
1385 IN UINT8 Data
1386 )
1387 {
1388 EFI_STATUS Status;
1389
1390 Status = FdcDev->IsaIo->Io.Write (
1391 FdcDev->IsaIo,
1392 EfiIsaIoWidthUint8,
1393 FdcDev->BaseAddress + Offset,
1394 1,
1395 &Data
1396 );
1397 ASSERT_EFI_ERROR (Status);
1398 }
1399