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