atapiextpassthru.c

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

C
2,448
字号
      AtapiScsiPrivate->IoPort->Alt.DeviceControl,
      DeviceControlValue
      );

    //
    // Wait 10us
    //
    gBS->Stall (10);

    //
    // Clear SRST bit
    // 0xfb:1111,1011
    //
    DeviceControlValue &= 0xfb;
    
    WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue);

    //
    // slave device needs at most 31s to clear BSY
    //
    if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000) == EFI_TIMEOUT) {
      return EFI_DEVICE_ERROR;
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
AtapiExtScsiPassThruResetTarget (
  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
  IN UINT8                              *Target,
  IN UINT64                             Lun
  )
/*++

Routine Description:

  Resets a SCSI device that is connected to a SCSI channel.

Arguments:

  This                  - Protocol instance pointer.
  Target                - The Target ID of the SCSI device to reset. 
  Lun                   - The LUN of the SCSI device to reset.
    
Returns:

  EFI_SUCCESS           - The SCSI device specified by Target and 
                          Lun was reset.
  EFI_UNSUPPORTED       - The SCSI channel does not support a target
                          reset operation.
  EFI_INVALID_PARAMETER - Target or Lun are invalid.
  EFI_DEVICE_ERROR      - A device error occurred while attempting 
                          to reset the SCSI device specified by Target 
                          and Lun.
  EFI_TIMEOUT           - A timeout occurred while attempting to reset 
                          the SCSI device specified by Target and Lun.
--*/
{
  UINT8                         Command;
  UINT8                         DeviceSelect;
  UINT32                        TargetId;
  ATAPI_EXT_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;
  
  AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This);
  TargetId = *(UINT32*)Target;
  
  if (TargetId > MAX_TARGET_ID) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Directly return EFI_SUCCESS if want to reset the host controller
  //
  if (TargetId == This->Mode->AdapterId) {
    return EFI_SUCCESS;
  }
  
  //
  // According to Target ID, reset the Atapi I/O Register mapping
  // (Target Id in [0,1] area, using AtapiIoPortRegisters[0],
  //  Target Id in [2,3] area, using AtapiIoPortRegisters[1]
  //
  if ((TargetId / 2) == 0) {
    AtapiScsiPrivate->IoPort = &AtapiIoPortRegisters[0];
  } else {
    AtapiScsiPrivate->IoPort = &AtapiIoPortRegisters[1];
  }
  
  //
  // for ATAPI device, no need to wait DRDY ready after device selecting.
  //
  // bit7 and bit5 are both set to 1 for backward compatibility
  //
  DeviceSelect = (UINT8) (((bit (7) | bit (5)) | (TargetId << 4)));
  WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect);

  Command = ATAPI_SOFT_RESET_CMD;
  WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command);

  //
  // BSY clear is the only status return to the host by the device
  // when reset is complete.
  // slave device needs at most 31s to clear BSY
  //
  if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000))) {
    return EFI_DEVICE_ERROR;
  }
  
  //
  // stall 5 seconds to make the device status stable
  //
  gBS->Stall (5000000);

  return EFI_SUCCESS;
}


EFI_STATUS
EFIAPI
AtapiExtScsiPassThruGetNextTarget (
  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
  IN OUT UINT8                           **Target
  )
/*++

Routine Description:
  Used to retrieve the list of legal Target IDs for SCSI devices 
  on a SCSI channel.

Arguments:
  This                  - Protocol instance pointer.
  Target                - On input, a pointer to the Target ID of a SCSI 
                          device present on the SCSI channel.  On output, 
                          a pointer to the Target ID of the next SCSI device
                           present on a SCSI channel.  An input value of 
                           0xFFFFFFFF retrieves the Target ID of the first 
                           SCSI device present on a SCSI channel.
  Lun                   - On input, a pointer to the LUN of a SCSI device
                          present on the SCSI channel. On output, a pointer
                          to the LUN of the next SCSI device present on 
                          a SCSI channel.
    
Returns:
  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device 
                          on the SCSI channel was returned in Target and Lun.
  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.
  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not
                          returned on a previous call to GetNextDevice().
--*/
{
  UINT32                        TargetId;
  ATAPI_EXT_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;  

  //
  // Retrieve Device Private Data Structure.
  //
  AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This);

  //
  // Check whether Target is valid.
  //
  TargetId = *(UINT32*)(*Target);
  
  if (*Target == NULL ) {
    return EFI_INVALID_PARAMETER;
  }

  if ((TargetId != 0xFFFFFFFF) &&(TargetId != AtapiScsiPrivate->LatestTargetId)) {
    return EFI_INVALID_PARAMETER;
  }

  if (TargetId == MAX_TARGET_ID) {
    return EFI_NOT_FOUND;
  }

  if (TargetId == 0xFFFFFFFF) {
    *(UINT32*)(*Target) = 0x0;
  } else {
    *(UINT32*)(*Target) = AtapiScsiPrivate->LatestTargetId + 1;
  }

  //
  // Update the LatestTargetId.
  //
  AtapiScsiPrivate->LatestTargetId  = *(UINT32*)(*Target);
  AtapiScsiPrivate->LatestLun       = 0;

  return EFI_SUCCESS;
}

STATIC  
EFI_STATUS
CheckSCSIRequestPacket (
  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET      *Packet
  )
/*++

Routine Description:

  Checks the parameters in the SCSI Request Packet to make sure
  they are valid for a SCSI Pass Thru request.

Arguments:

  Packet       - The pointer of EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
  
Returns:
  
  EFI_STATUS

--*/
{
  if (Packet == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (!ValidCdbLength (Packet->CdbLength)) {
    return EFI_INVALID_PARAMETER;
  }

  if (Packet->Cdb == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  
  //
  // Checks whether the request command is supported.
  //
  if (!IsCommandValid (Packet)) {
    return EFI_UNSUPPORTED;
  }

  return EFI_SUCCESS;
}

STATIC  
BOOLEAN
IsCommandValid (
  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   *Packet
  )
/*++
  
Routine Description:

  Checks the requested SCSI command: 
  Is it supported by this driver?
  Is the Data transfer direction reasonable?

Arguments:

  Packet         -  The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   

Returns:

  EFI_STATUS

--*/
{
  UINT8 Index;
  UINT8 *OpCode;

  OpCode = (UINT8 *) (Packet->Cdb);

  for (Index = 0; EfiCompareMem (&SupportedATAPICommands[Index], &EndTable, sizeof (SCSI_COMMAND_SET)); Index++) {

    if (*OpCode == SupportedATAPICommands[Index].OpCode) {
      //
      // Check whether the requested Command is supported by this driver
      //
      if (Packet->DataDirection == DataIn) {
        //
        // Check whether the requested data direction conforms to
        // what it should be.
        //
        if (SupportedATAPICommands[Index].Direction == DataOut) {
          return FALSE;
        }
      }

      if (Packet->DataDirection == DataOut) {
        //
        // Check whether the requested data direction conforms to
        // what it should be.
        //
        if (SupportedATAPICommands[Index].Direction == DataIn) {
          return FALSE;
        }
      }

      return TRUE;
    }
  }

  return FALSE;
}

STATIC  
EFI_STATUS
SubmitBlockingIoCommand (
  ATAPI_EXT_SCSI_PASS_THRU_DEV                  *AtapiScsiPrivate,
  UINT32                                        Target,
  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet
  )
/*++

Routine Description:

  Performs blocking I/O request.
    
Arguments:

  AtapiScsiPrivate:   Private data structure for the specified channel.
  Target:             The Target ID of the ATAPI device to send the SCSI 
                      Request Packet. To ATAPI devices attached on an IDE
                      Channel, Target ID 0 indicates Master device;Target
                      ID 1 indicates Slave device.
  Packet:             The SCSI Request Packet to send to the ATAPI device 
                      specified by Target.
  
  Returns:            EFI_STATUS  
  
--*/
{
  UINT8       PacketCommand[12];
  UINT64      TimeoutInMicroSeconds;
  EFI_STATUS  PacketCommandStatus;
  UINTN       Remainder;

  //
  // Fill ATAPI Command Packet according to CDB
  //
  EfiZeroMem (&PacketCommand, 12);
  EfiCopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength);

  //
  // Timeout is 100ns unit, convert it to 1000ns (1us) unit.
  //
  TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10, &Remainder);

  //
  // Submit ATAPI Command Packet
  //
  if (Packet->DataDirection == DataIn) {
    PacketCommandStatus = AtapiPacketCommand (
                              AtapiScsiPrivate,
                              Target,
                              PacketCommand,
                              Packet->InDataBuffer,
                              &(Packet->InTransferLength),
                              DataIn,
                              TimeoutInMicroSeconds
                              );
  } else {

  PacketCommandStatus = AtapiPacketCommand (
                          AtapiScsiPrivate,
                          Target,
                          PacketCommand,
                          Packet->OutDataBuffer,
                          &(Packet->OutTransferLength),
                          DataOut,
                          TimeoutInMicroSeconds
                          );
  }
  
  if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) {
    Packet->SenseDataLength = 0;
    return PacketCommandStatus;
  }
  //
  // Return SenseData if PacketCommandStatus matches
  // the following return codes.
  //
  if ((PacketCommandStatus == EFI_WARN_BUFFER_TOO_SMALL) ||
      (PacketCommandStatus == EFI_DEVICE_ERROR) ||
      (PacketCommandStatus == EFI_TIMEOUT)) {

    //
    // avoid submit request sense command continuously.
    //
    if (PacketCommand[0] == OP_REQUEST_SENSE) {
      Packet->SenseDataLength = 0;
      return PacketCommandStatus;
    }

    RequestSenseCommand (
      AtapiScsiPrivate,
      Target,
      Packet->Timeout,
      Packet->SenseData,
      &Packet->SenseDataLength
      );
  }

  return PacketCommandStatus;
}

STATIC  
EFI_STATUS
RequestSenseCommand (
  ATAPI_EXT_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
  UINT32                          Target,
  UINT64                          Timeout,
  VOID                            *SenseData,
  UINT8                           *SenseDataLength
  )
/*++

Routine Description:

  Sumbit request sense command

Arguments:

  AtapiScsiPrivate  - The pionter of ATAPI_SCSI_PASS_THRU_DEV
  Target            - The target ID
  Timeout           - The time to complete the command
  SenseData         - The buffer to fill in sense data
  SenseDataLength   - The length of buffer

Returns:

  EFI_STATUS

--*/
{
  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  Packet;
  UINT8                                       Cdb[12];
  EFI_STATUS                                  Status;

  EfiZeroMem (&Packet, sizeof (EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET));
  EfiZeroMem (Cdb, 12);

  Cdb[0]                = OP_REQUEST_SENSE;
  Cdb[4]                = (UINT8) (*SenseDataLength);

  Packet.Timeout        = Timeout;
  Packet.InDataBuffer     = SenseData;
  Packet.SenseData      = NULL;
  Packet.Cdb            = Cdb;
  Packet.InTransferLength = *SenseDataLength;
  Packet.CdbLength      = 12;
  Packet.DataDirection  = DataIn;

  Status                = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, &Packet);
  *SenseDataLength      = (UINT8) (Packet.InTransferLength);
  return Status;
}

STATIC  
EFI_STATUS
AtapiPacketCommand (
  ATAPI_EXT_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
  UINT32                          Target,
  UINT8                           *PacketCommand,
  VOID                            *Buffer,
  UINT32                          *ByteCount,
  DATA_DIRECTION                  Direction,
  UINT64                          TimeoutInMicroSeconds
  )
/*++

Routine Description:
  Submits ATAPI command packet to the specified ATAPI device.
    
Arguments:
  AtapiScsiPrivate:   Private data structure for the specified channel.
  Target:             The Target ID of the ATAPI device to send the SCSI 
                      Request Packet. To ATAPI devices attached on an IDE
                      Channel, Target ID 0 indicates Master device;Target
                      ID 1 indicates Slave device.
  PacketCommand:      Points to the ATAPI command packet.
  Buffer:             Points to the transferred data.
  ByteCount:          When input,indicates the buffer size; when output,
                      indicates the actually transferred data size.
  Direction:          Indicates the data transfer direction. 
  TimeoutInMicroSeconds:
                      The timeout, in micro second units, to use for the 
                      execution of this ATAPI command.
                      A TimeoutInMicroSeconds value of 0 means that 
                      this function will wait indefinitely for the ATAPI 
                      command to execute.
                      If TimeoutInMicroSeconds is greater than zero, then 
                      this function will return EFI_TIMEOUT if the time 
                      required to execute the ATAPI command is greater 
                      than TimeoutInMicroSeconds.
  
Returns:

⌨️ 快捷键说明

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