isafloppyctrl.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 1,560 行 · 第 1/3 页

C
1,560
字号
/*++

Copyright (c) 2006, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.


Module Name:

  IsaFloppyCtrl.c

Abstract:

  ISA Floppy Driver
  1. Support two types diskette drive  
     1.44M drive and 2.88M drive (and now only support 1.44M)
  2. Support two diskette drives
  3. Use DMA channel 2 to transfer data
  4. Do not use interrupt
  5. Support diskette change line signal and write protect
  
  The internal function for the floppy driver

Revision History:

--*/

#include "IsaFloppy.h"

EFI_STATUS
DiscoverFddDevice (
  IN FDC_BLK_IO_DEV  *FdcDev
  )
/*++

  Routine Description:  Detect the floppy drive is presented or not   
  Parameters:
    FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV       
  Returns:
    EFI_SUCCESS    Drive is presented 
    EFI_NOT_FOUND  Drive is not presented

--*/
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO:    FdcDev - add argument and description to function comment
{
  EFI_STATUS  Status;

  FdcDev->BlkIo.Media = &FdcDev->BlkMedia;

  //
  // Call FddIndentify subroutine
  //
  Status = FddIdentify (FdcDev);
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  FdcDev->BlkIo.Reset               = FdcReset;
  FdcDev->BlkIo.FlushBlocks         = FddFlushBlocks;
  FdcDev->BlkIo.ReadBlocks          = FddReadBlocks;
  FdcDev->BlkIo.WriteBlocks         = FddWriteBlocks;
  FdcDev->BlkMedia.LogicalPartition = FALSE;
  FdcDev->BlkMedia.WriteCaching     = FALSE;

  return EFI_SUCCESS;
}

EFI_STATUS
FddIdentify (
  IN FDC_BLK_IO_DEV  *FdcDev
  )
/*++

  Routine Description:   Do recalibrate  and see the drive is presented or not
         Set the media parameters
  Parameters:
    FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV       
  Returns:
    EFI_SUCCESS:    
    EFI_DEVICE_ERROR: 

--*/
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO:    FdcDev - add argument and description to function comment
{
  EFI_STATUS  Status;

  //
  // Set Floppy Disk Controller's motor on
  //
  Status = MotorOn (FdcDev);
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  Status = Recalibrate (FdcDev);

  if (EFI_ERROR (Status)) {
    MotorOff (FdcDev);
    FdcDev->ControllerState->NeedRecalibrate = TRUE;
    return EFI_DEVICE_ERROR;
  }
  //
  // Set Media Parameter
  //
  FdcDev->BlkIo.Media->RemovableMedia = TRUE;
  FdcDev->BlkIo.Media->MediaPresent   = TRUE;
  //
  // investigate
  //
  FdcDev->BlkIo.Media->MediaId = 0;

  //
  // Check Media
  //
  Status = DisketChanged (FdcDev);
  switch (Status) {
  case EFI_NO_MEDIA:
    FdcDev->BlkIo.Media->MediaPresent = FALSE;
    break;

  case EFI_MEDIA_CHANGED:
  case EFI_SUCCESS:
    break;

  default:
    MotorOff (FdcDev);
    return Status;
  }
  //
  // Check Disk Write Protected
  //
  Status = SenseDrvStatus (FdcDev, 0);
  switch (Status) {
  case EFI_WRITE_PROTECTED:
    FdcDev->BlkIo.Media->ReadOnly = TRUE;
    break;

  case EFI_SUCCESS:
    FdcDev->BlkIo.Media->ReadOnly = FALSE;
    break;

  default:
    return EFI_DEVICE_ERROR;
    break;
  }

  MotorOff (FdcDev);

  //
  // Set Media Default Type
  //
  FdcDev->BlkIo.Media->BlockSize  = DISK_1440K_BYTEPERSECTOR;
  FdcDev->BlkIo.Media->LastBlock  = DISK_1440K_EOT * 2 * (DISK_1440K_MAXTRACKNUM + 1) - 1;

  return EFI_SUCCESS;
}

EFI_STATUS
FddReset (
  IN FDC_BLK_IO_DEV  *FdcDev
  )
/*++

  Routine Description:  Reset the Floppy Logic Drive   
  Parameters:
    FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV       
  Returns:
    EFI_SUCCESS:    The Floppy Logic Drive is reset
    EFI_DEVICE_ERROR: The Floppy Logic Drive is not functioning correctly and 
                      can not be reset

--*/
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO:    FdcDev - add argument and description to function comment
{
  UINT8 data;
  UINT8 StatusRegister0;
  UINT8 PresentCylinderNumber;
  UINTN Index;

  //
  // Report reset progress code
  //
  ReportStatusCodeWithDevicePath (
    EFI_PROGRESS_CODE,
    EFI_PERIPHERAL_REMOVABLE_MEDIA | EFI_P_PC_RESET,
    0,
    &gEfiCallerIdGuid,
    FdcDev->DevicePath
    );

  //
  // Reset specified Floppy Logic Drive according to FdcDev -> Disk
  // Set Digital Output Register(DOR) to do reset work
  //   bit0 & bit1 of DOR : Drive Select
  //   bit2 : Reset bit
  //   bit3 : DMA and Int bit
  // Reset : a "0" written to bit2 resets the FDC, this reset will remain
  //         active until
  //         a "1" is written to this bit.
  // Reset step 1:
  //         use bit0 & bit1 to  select the logic drive
  //         write "0" to bit2
  //
  data = 0x0;
  data |= (SELECT_DRV & FdcDev->Disk);
  FdcWritePort (FdcDev, FDC_REGISTER_DOR, data);

  //
  // wait some time,at least 120us
  //
  gBS->Stall (500);

  //
  // Reset step 2:
  //   write "1" to bit2
  //   write "1" to bit3 : enable DMA
  //
  data |= 0x0C;
  FdcWritePort (FdcDev, FDC_REGISTER_DOR, data);

  //
  // Experience value
  //
  gBS->Stall (2000);

  //
  // wait specified floppy logic drive is not busy
  //
  if (EFI_ERROR (FddWaitForBSYClear (FdcDev, 1))) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Set the Transfer Data Rate
  //
  FdcWritePort (FdcDev, FDC_REGISTER_CCR, 0x0);

  //
  // Experience value
  //
  gBS->Stall (100);

  //
  // Issue Sense interrupt command for each drive (total 4 drives)
  //
  for (Index = 0; Index < 4; Index++) {
    if (EFI_ERROR (SenseIntStatus (FdcDev, &StatusRegister0, &PresentCylinderNumber))) {
      return EFI_DEVICE_ERROR;
    }
  }
  //
  // issue Specify command
  //
  if (EFI_ERROR (Specify (FdcDev))) {
    return EFI_DEVICE_ERROR;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
MotorOn (
  IN FDC_BLK_IO_DEV  *FdcDev
  )
/*++

  Routine Description:  Turn the drive's motor on
        The drive's motor must be on before any command can be executed   
  Parameters:
    FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV       
  Returns:
    EFI_SUCCESS:       Turn the drive's motor on successfully
    EFI_DEVICE_ERROR:    The drive is busy, so can not turn motor on 
    EFI_INVALID_PARAMETER: Fail to Set timer(Cancel timer)  

--*/
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO:    FdcDev - add argument and description to function comment
{
  EFI_STATUS  Status;
  UINT8       data;

  //
  // Control of the floppy drive motors is a big pain. If motor is off, you have
  // to turn it on first. But you can not leave the motor on all the time, since
  // that would wear out the disk. On the other hand, if you turn the motor off
  // after each operation, the system performance will be awful. The compromise
  // used in this driver is to leave the motor on for 2 seconds after
  // each operation. If a new operation is started in that interval(2s),
  // the motor need not be turned on again. If no new operation is started,
  // a timer goes off and the motor is turned off
  //
  //
  // Cancel the timer
  //
  Status = gBS->SetTimer (FdcDev->Event, TimerCancel, 0);

  if (EFI_ERROR (Status)) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Get the motor status
  //
  data = FdcReadPort (FdcDev, FDC_REGISTER_DOR);

  if (((FdcDev->Disk == FDC_DISK0) && ((data & 0x10) == 0x10)) ||
      ((FdcDev->Disk == FDC_DISK1) && ((data & 0x21) == 0x21))
      ) {
    return EFI_SUCCESS;
  }
  //
  // The drive's motor is off, so need turn it on
  // first look at command and drive are busy or not
  //
  if (EFI_ERROR (FddWaitForBSYClear (FdcDev, 1))) {
    return EFI_DEVICE_ERROR;
  }
  //
  // for drive A: 1CH, drive B: 2DH
  //
  data = 0x0C;
  data |= (SELECT_DRV & FdcDev->Disk);
  if (FdcDev->Disk == FDC_DISK0) {
    //
    // drive A
    //
    data |= DRVA_MOTOR_ON;
  } else {
    //
    // drive B
    //
    data |= DRVB_MOTOR_ON;
  }

  FdcWritePort (FdcDev, FDC_REGISTER_DOR, data);

  //
  // Experience value
  //
  gBS->Stall (4000);

  return EFI_SUCCESS;
}

EFI_STATUS
MotorOff (
  IN FDC_BLK_IO_DEV  *FdcDev
  )
/*++

  Routine Description:  Set a Timer and when Timer goes off, turn the motor off
  Parameters:
    FdcDev FDC_BLK_IO_DEV * : A pointer to the Data Structure FDC_BLK_IO_DEV       
  Returns:
    EFI_SUCCESS:       Set the Timer successfully
    EFI_INVALID_PARAMETER: Fail to Set the timer  

--*/
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO:    FdcDev - add argument and description to function comment
{
  //
  // Set the timer : 2s
  //
  return gBS->SetTimer (FdcDev->Event, TimerRelative, 20000000);
}

EFI_STATUS
DisketChanged (
  IN FDC_BLK_IO_DEV  *FdcDev
  )
/*++

  Routine Description:  Detect the disk in the drive is changed or not
  Parameters:
    FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV   
  Returns:
    EFI_SUCCESS:    No disk media change
    EFI_DEVICE_ERROR: Fail to do the recalibrate or seek operation
    EFI_NO_MEDIA:   No disk in the drive
    EFI_MEDIA_CHANGED:  There is a new disk in the drive

--*/
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO:    FdcDev - add argument and description to function comment
{
  EFI_STATUS  Status;
  UINT8       data;

  //
  // Check change line
  //
  data = FdcReadPort (FdcDev, FDC_REGISTER_DIR);

  //
  // Io delay
  //
  gBS->Stall (50);

  if ((data & DIR_DCL) == 0x80) {
    //
    // disk change line is active
    //
    if (FdcDev->PresentCylinderNumber != 0) {
      Status = Recalibrate (FdcDev);
    } else {
      Status = Seek (FdcDev, 0x30);
    }

    if (EFI_ERROR (Status)) {
      FdcDev->ControllerState->NeedRecalibrate = TRUE;
      return EFI_DEVICE_ERROR;
      //
      // Fail to do the seek or recalibrate operation
      //
    }

    data = FdcReadPort (FdcDev, FDC_REGISTER_DIR);

    //
    // Io delay
    //
    gBS->Stall (50);

    if ((data & DIR_DCL) == 0x80) {
      return EFI_NO_MEDIA;
    }

    return EFI_MEDIA_CHANGED;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
Specify (
  IN FDC_BLK_IO_DEV  *FdcDev
  )
/*++

  Routine Description:  Do the Specify command, this command sets DMA operation
                        and the initial values for each of the three internal 
                        times: HUT, SRT and HLT
  Parameters:
    None
  Returns:
    EFI_SUCCESS:    Execute the Specify command successfully
    EFI_DEVICE_ERROR: Fail to execute the command

--*/
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO:    FdcDev - add argument and description to function comment
{
  FDD_SPECIFY_CMD Command;
  UINTN           Index;
  UINT8           *CommandPointer;

  EfiZeroMem (&Command, sizeof (FDD_SPECIFY_CMD));
  Command.CommandCode = SPECIFY_CMD;
  //
  // set SRT, HUT
  //
  Command.SrtHut = 0xdf;
  //
  // 0xdf;
  //
  // set HLT and DMA
  //
  Command.HltNd   = 0x02;

  CommandPointer  = (UINT8 *) (&Command);
  for (Index = 0; Index < sizeof (FDD_SPECIFY_CMD); Index++) {
    if (EFI_ERROR (DataOutByte (FdcDev, CommandPointer++))) {
      return EFI_DEVICE_ERROR;
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS
Recalibrate (
  IN FDC_BLK_IO_DEV  *FdcDev
  )
/*++

  Routine Description:  Set the head of floppy drive to track 0
  Parameters:
    FdcDev FDC_BLK_IO_DEV *: A pointer to Data Structure FDC_BLK_IO_DEV
  Returns:
    EFI_SUCCESS:    Execute the Recalibrate operation successfully
    EFI_DEVICE_ERROR: Fail to execute the Recalibrate operation

--*/
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO:    FdcDev - add argument and description to function comment
{
  FDD_COMMAND_PACKET2 Command;
  UINTN               Index;
  UINT8               StatusRegister0;
  UINT8               PresentCylinderNumber;
  UINT8               *CommandPointer;
  UINT8               Count;

  Count = 2;

  while (Count > 0) {
    EfiZeroMem (&Command, sizeof (FDD_COMMAND_PACKET2));
    Command.CommandCode = RECALIBRATE_CMD;
    //
    // drive select
    //
    if (FdcDev->Disk == FDC_DISK0) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?