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 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 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 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 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 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 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 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 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 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 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