1 /** @file
2   Internal floppy disk controller programming functions for the floppy driver.
3 
4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
5 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
DiscoverFddDevice(IN FDC_BLK_IO_DEV * FdcDev)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
FddIdentify(IN FDC_BLK_IO_DEV * FdcDev)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
FddReset(IN FDC_BLK_IO_DEV * FdcDev)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
MotorOn(IN FDC_BLK_IO_DEV * FdcDev)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
MotorOff(IN FDC_BLK_IO_DEV * FdcDev)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
DisketChanged(IN FDC_BLK_IO_DEV * FdcDev)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
Specify(IN FDC_BLK_IO_DEV * FdcDev)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
Recalibrate(IN FDC_BLK_IO_DEV * FdcDev)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
Seek(IN FDC_BLK_IO_DEV * FdcDev,IN EFI_LBA Lba)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
SenseIntStatus(IN FDC_BLK_IO_DEV * FdcDev,IN OUT UINT8 * StatusRegister0,IN OUT UINT8 * PresentCylinderNumber)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
SenseDrvStatus(IN FDC_BLK_IO_DEV * FdcDev,IN EFI_LBA Lba)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
DetectMedia(IN FDC_BLK_IO_DEV * FdcDev)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
Setup(IN FDC_BLK_IO_DEV * FdcDev)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
ReadWriteDataSector(IN FDC_BLK_IO_DEV * FdcDev,IN VOID * HostAddress,IN EFI_LBA Lba,IN UINTN NumberOfBlocks,IN BOOLEAN Read)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
FillPara(IN FDC_BLK_IO_DEV * FdcDev,IN EFI_LBA Lba,IN FDD_COMMAND_PACKET1 * Command)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
DataInByte(IN FDC_BLK_IO_DEV * FdcDev,OUT UINT8 * Pointer)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
DataOutByte(IN FDC_BLK_IO_DEV * FdcDev,IN UINT8 * Pointer)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 Timeout          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 Timeout
1091 
1092 **/
1093 EFI_STATUS
FddWaitForBSYClear(IN FDC_BLK_IO_DEV * FdcDev,IN UINTN Timeout)1094 FddWaitForBSYClear (
1095   IN FDC_BLK_IO_DEV  *FdcDev,
1096   IN UINTN           Timeout
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 = ((Timeout * 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   Determine whether FDC is ready to write or read.
1138 
1139   @param  FdcDev Pointer to instance of FDC_BLK_IO_DEV
1140   @param  Dio BOOLEAN:      Indicate the FDC is waiting to write or read
1141   @param  Timeout           The time period for waiting
1142 
1143   @retval EFI_SUCCESS:  FDC is ready to write or read
1144   @retval EFI_NOT_READY:  FDC is not ready within the specified time period
1145 
1146 **/
1147 EFI_STATUS
FddDRQReady(IN FDC_BLK_IO_DEV * FdcDev,IN BOOLEAN Dio,IN UINTN Timeout)1148 FddDRQReady (
1149   IN FDC_BLK_IO_DEV  *FdcDev,
1150   IN BOOLEAN         Dio,
1151   IN UINTN           Timeout
1152   )
1153 {
1154   UINTN Delay;
1155   UINT8 StatusRegister;
1156   UINT8 DataInOut;
1157 
1158   //
1159   // Before writing to FDC or reading from FDC, the Host must examine
1160   // the bit7(RQM) and bit6(DIO) of the Main Status Register.
1161   // That is to say:
1162   //  command bytes can not be written to Data Register
1163   //  unless RQM is 1 and DIO is 0
1164   //  result bytes can not be read from Data Register
1165   //  unless RQM is 1 and DIO is 1
1166   //
1167   DataInOut = (UINT8) (Dio << 6);
1168   //
1169   // in order to compare bit6
1170   //
1171   Delay = ((Timeout * STALL_1_MSECOND) / 50) + 1;
1172   do {
1173     StatusRegister = FdcReadPort (FdcDev, FDC_REGISTER_MSR);
1174     if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == DataInOut) {
1175       break;
1176       //
1177       // FDC is ready
1178       //
1179     }
1180 
1181     MicroSecondDelay (50);
1182     //
1183     // Stall for 50 us
1184     //
1185     Delay = Delay - 1;
1186   } while (Delay > 0);
1187 
1188   if (Delay == 0) {
1189     return EFI_NOT_READY;
1190     //
1191     // FDC is not ready within the specified time period
1192     //
1193   }
1194 
1195   return EFI_SUCCESS;
1196 }
1197 
1198 /**
1199   Set FDC control structure's attribute according to result.
1200 
1201   @param Result  Point to result structure
1202   @param FdcDev  FDC control structure
1203 
1204   @retval EFI_DEVICE_ERROR - GC_TODO: Add description for return value
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_SUCCESS - GC_TODO: Add description for return value
1208 
1209 **/
1210 EFI_STATUS
CheckResult(IN FDD_RESULT_PACKET * Result,IN OUT FDC_BLK_IO_DEV * FdcDev)1211 CheckResult (
1212   IN     FDD_RESULT_PACKET  *Result,
1213   IN OUT FDC_BLK_IO_DEV     *FdcDev
1214   )
1215 {
1216   //
1217   // Check Status Register0
1218   //
1219   if ((Result->Status0 & STS0_IC) != IC_NT) {
1220     if ((Result->Status0 & STS0_SE) == 0x20) {
1221       //
1222       // seek error
1223       //
1224       FdcDev->ControllerState->NeedRecalibrate = TRUE;
1225     }
1226 
1227     FdcDev->ControllerState->NeedRecalibrate = TRUE;
1228     return EFI_DEVICE_ERROR;
1229   }
1230   //
1231   // Check Status Register1
1232   //
1233   if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {
1234     FdcDev->ControllerState->NeedRecalibrate = TRUE;
1235     return EFI_DEVICE_ERROR;
1236   }
1237   //
1238   // Check Status Register2
1239   //
1240   if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {
1241     FdcDev->ControllerState->NeedRecalibrate = TRUE;
1242     return EFI_DEVICE_ERROR;
1243   }
1244 
1245   return EFI_SUCCESS;
1246 }
1247 
1248 /**
1249   Check the drive status information.
1250 
1251   @param StatusRegister3  the value of Status Register 3
1252 
1253   @retval EFI_SUCCESS           The disk is not write protected
1254   @retval EFI_WRITE_PROTECTED:  The disk is write protected
1255 
1256 **/
1257 EFI_STATUS
CheckStatus3(IN UINT8 StatusRegister3)1258 CheckStatus3 (
1259   IN UINT8 StatusRegister3
1260   )
1261 {
1262   if ((StatusRegister3 & STS3_WP) != 0) {
1263     return EFI_WRITE_PROTECTED;
1264   }
1265 
1266   return EFI_SUCCESS;
1267 }
1268 
1269 /**
1270   Calculate the number of block in the same cylinder according to LBA.
1271 
1272   @param FdcDev FDC_BLK_IO_DEV *: A pointer to FDC_BLK_IO_DEV
1273   @param LBA EFI_LBA:      The starting logic block address
1274   @param NumberOfBlocks UINTN: The number of blocks
1275 
1276   @return The number of blocks in the same cylinder which the starting
1277         logic block address is LBA
1278 
1279 **/
1280 UINTN
GetTransferBlockCount(IN FDC_BLK_IO_DEV * FdcDev,IN EFI_LBA LBA,IN UINTN NumberOfBlocks)1281 GetTransferBlockCount (
1282   IN  FDC_BLK_IO_DEV  *FdcDev,
1283   IN  EFI_LBA         LBA,
1284   IN  UINTN           NumberOfBlocks
1285   )
1286 {
1287   UINT8 EndOfTrack;
1288   UINT8 Head;
1289   UINT8 SectorsInTrack;
1290 
1291   //
1292   // Calculate the number of block in the same cylinder
1293   //
1294   EndOfTrack      = DISK_1440K_EOT;
1295   Head            = (UINT8) ((UINTN) LBA / EndOfTrack % 2);
1296 
1297   SectorsInTrack  = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) LBA % EndOfTrack));
1298   if (SectorsInTrack < NumberOfBlocks) {
1299     return SectorsInTrack;
1300   } else {
1301     return NumberOfBlocks;
1302   }
1303 }
1304 
1305 /**
1306   When the Timer(2s) off, turn the drive's motor off.
1307 
1308   @param Event EFI_EVENT: Event(the timer) whose notification function is being
1309                      invoked
1310   @param Context VOID *:  Pointer to the notification function's context
1311 
1312 **/
1313 VOID
1314 EFIAPI
FddTimerProc(IN EFI_EVENT Event,IN VOID * Context)1315 FddTimerProc (
1316   IN EFI_EVENT  Event,
1317   IN VOID       *Context
1318   )
1319 {
1320   FDC_BLK_IO_DEV  *FdcDev;
1321   UINT8           Data;
1322 
1323   FdcDev = (FDC_BLK_IO_DEV *) Context;
1324 
1325   //
1326   // Get the motor status
1327   //
1328   Data = FdcReadPort (FdcDev, FDC_REGISTER_DOR);
1329 
1330   if (((FdcDev->Disk == FdcDisk0) && ((Data & 0x10) != 0x10)) ||
1331       ((FdcDev->Disk == FdcDisk1) && ((Data & 0x21) != 0x21))
1332       ) {
1333     return ;
1334   }
1335   //
1336   // the motor is on, so need motor off
1337   //
1338   Data = 0x0C;
1339   Data = (UINT8) (Data | (SELECT_DRV & FdcDev->Disk));
1340   FdcWritePort (FdcDev, FDC_REGISTER_DOR, Data);
1341   MicroSecondDelay (500);
1342 }
1343 
1344 /**
1345   Read an I/O port of FDC.
1346 
1347   @param[in] FdcDev  A pointer to FDC_BLK_IO_DEV.
1348   @param[in] Offset  The address offset of the I/O port.
1349 
1350   @retval  8-bit data read from the I/O port.
1351 **/
1352 UINT8
FdcReadPort(IN FDC_BLK_IO_DEV * FdcDev,IN UINT32 Offset)1353 FdcReadPort (
1354   IN FDC_BLK_IO_DEV  *FdcDev,
1355   IN UINT32          Offset
1356   )
1357 {
1358   EFI_STATUS  Status;
1359   UINT8       Data;
1360 
1361   Status = FdcDev->IsaIo->Io.Read (
1362                             FdcDev->IsaIo,
1363                             EfiIsaIoWidthUint8,
1364                             FdcDev->BaseAddress + Offset,
1365                             1,
1366                             &Data
1367                             );
1368   ASSERT_EFI_ERROR (Status);
1369 
1370   return Data;
1371 }
1372 
1373 /**
1374   Write an I/O port of FDC.
1375 
1376   @param[in] FdcDev  A pointer to FDC_BLK_IO_DEV
1377   @param[in] Offset  The address offset of the I/O port
1378   @param[in] Data    8-bit Value written to the I/O port
1379 **/
1380 VOID
FdcWritePort(IN FDC_BLK_IO_DEV * FdcDev,IN UINT32 Offset,IN UINT8 Data)1381 FdcWritePort (
1382   IN FDC_BLK_IO_DEV  *FdcDev,
1383   IN UINT32          Offset,
1384   IN UINT8           Data
1385   )
1386 {
1387   EFI_STATUS  Status;
1388 
1389   Status = FdcDev->IsaIo->Io.Write (
1390                             FdcDev->IsaIo,
1391                             EfiIsaIoWidthUint8,
1392                             FdcDev->BaseAddress + Offset,
1393                             1,
1394                             &Data
1395                             );
1396   ASSERT_EFI_ERROR (Status);
1397 }
1398 
1399