atapiextpassthru.c
来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 2,448 行 · 第 1/5 页
C
2,448 行
EFI_STATUS
--*/
{
UINT16 *CommandIndex;
UINT8 Count;
EFI_STATUS Status;
//
// Set all the command parameters by fill related registers.
// Before write to all the following registers, BSY and DRQ must be 0.
//
Status = StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds);
if (EFI_ERROR (Status)) {
if (Status == EFI_ABORTED) {
Status = EFI_DEVICE_ERROR;
}
*ByteCount = 0;
return Status;
}
//
// Select device via Device/Head Register.
// "Target = 0" indicates device 0; "Target = 1" indicates device 1
//
WritePortB (
AtapiScsiPrivate->PciIo,
AtapiScsiPrivate->IoPort->Head,
(UINT8) ((Target << 4) | DEFAULT_CMD) // DEFAULT_CMD: 0xa0 (1010,0000)
);
//
// No OVL; No DMA (by setting feature register)
//
WritePortB (
AtapiScsiPrivate->PciIo,
AtapiScsiPrivate->IoPort->Reg1.Feature,
0x00
);
//
// set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device
// determine how much data should be transfered.
//
WritePortB (
AtapiScsiPrivate->PciIo,
AtapiScsiPrivate->IoPort->CylinderLsb,
(UINT8) (MAX_ATAPI_BYTE_COUNT & 0x00ff)
);
WritePortB (
AtapiScsiPrivate->PciIo,
AtapiScsiPrivate->IoPort->CylinderMsb,
(UINT8) (MAX_ATAPI_BYTE_COUNT >> 8)
);
//
// DEFAULT_CTL:0x0a (0000,1010)
// Disable interrupt
//
WritePortB (
AtapiScsiPrivate->PciIo,
AtapiScsiPrivate->IoPort->Alt.DeviceControl,
DEFAULT_CTL
);
//
// Send Packet command to inform device
// that the following data bytes are command packet.
//
WritePortB (
AtapiScsiPrivate->PciIo,
AtapiScsiPrivate->IoPort->Reg.Command,
PACKET_CMD
);
//
// Before data transfer, BSY should be 0 and DRQ should be 1.
// if they are not in specified time frame,
// retrieve Sense Key from Error Register before return.
//
Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds);
if (EFI_ERROR (Status)) {
if (Status == EFI_ABORTED) {
Status = EFI_DEVICE_ERROR;
}
*ByteCount = 0;
return Status;
}
//
// Send out command packet
//
CommandIndex = (UINT16 *) PacketCommand;
for (Count = 0; Count < 6; Count++, CommandIndex++) {
WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *CommandIndex);
}
//
// call AtapiPassThruPioReadWriteData() function to get
// requested transfer data form device.
//
return AtapiPassThruPioReadWriteData (
AtapiScsiPrivate,
Buffer,
ByteCount,
Direction,
TimeoutInMicroSeconds
);
}
STATIC
EFI_STATUS
AtapiPassThruPioReadWriteData (
ATAPI_EXT_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,
UINT16 *Buffer,
UINT32 *ByteCount,
DATA_DIRECTION Direction,
UINT64 TimeoutInMicroSeconds
)
/*++
Routine Description:
Performs data transfer between ATAPI device and host after the
ATAPI command packet is sent.
Arguments:
AtapiScsiPrivate: Private data structure for the specified channel.
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:
EFI_STATUS
--*/
{
UINT32 Index;
UINT32 RequiredWordCount;
UINT32 ActualWordCount;
UINT32 WordCount;
EFI_STATUS Status;
UINT16 *ptrBuffer;
Status = EFI_SUCCESS;
//
// Non Data transfer request is also supported.
//
if (*ByteCount == 0 || Buffer == NULL) {
*ByteCount = 0;
if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds))) {
return EFI_DEVICE_ERROR;
}
}
ptrBuffer = Buffer;
RequiredWordCount = *ByteCount / 2;
//
// ActuralWordCount means the word count of data really transfered.
//
ActualWordCount = 0;
while (ActualWordCount < RequiredWordCount) {
//
// before each data transfer stream, the host should poll DRQ bit ready,
// which indicates device's ready for data transfer .
//
Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds);
if (EFI_ERROR (Status)) {
*ByteCount = ActualWordCount * 2;
AtapiPassThruCheckErrorStatus (AtapiScsiPrivate);
if (ActualWordCount == 0) {
return EFI_DEVICE_ERROR;
}
//
// ActualWordCount > 0
//
if (ActualWordCount < RequiredWordCount) {
return EFI_WARN_BUFFER_TOO_SMALL;
}
}
//
// get current data transfer size from Cylinder Registers.
//
WordCount =
(
(ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderMsb) << 8) |
ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderLsb)
) & 0xffff;
WordCount /= 2;
//
// perform a series data In/Out.
//
for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) {
if (Direction == DataIn) {
*ptrBuffer = ReadPortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data);
} else {
WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *ptrBuffer);
}
ptrBuffer++;
}
}
//
// After data transfer is completed, normally, DRQ bit should clear.
//
StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds);
//
// read status register to check whether error happens.
//
Status = AtapiPassThruCheckErrorStatus (AtapiScsiPrivate);
*ByteCount = ActualWordCount * 2;
return Status;
}
STATIC
UINT8
ReadPortB (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT16 Port
)
/*++
Routine Description:
Read one byte from a specified I/O port.
Arguments:
PciIo - The pointer of EFI_PCI_IO_PROTOCOL
Port - IO port
Returns:
--*/
{
UINT8 Data;
Data = 0;
PciIo->Io.Read (
PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
(UINT64) Port,
1,
&Data
);
return Data;
}
STATIC
UINT16
ReadPortW (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT16 Port
)
/*++
Routine Description:
Read one word from a specified I/O port.
Arguments:
PciIo - The pointer of EFI_PCI_IO_PROTOCOL
Port - IO port
Returns:
--*/
{
UINT16 Data;
Data = 0;
PciIo->Io.Read (
PciIo,
EfiPciIoWidthUint16,
EFI_PCI_IO_PASS_THROUGH_BAR,
(UINT64) Port,
1,
&Data
);
return Data;
}
STATIC
VOID
WritePortB (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT16 Port,
IN UINT8 Data
)
/*++
Routine Description:
Write one byte to a specified I/O port.
Arguments:
PciIo - The pointer of EFI_PCI_IO_PROTOCOL
Port - IO port
Data - The data to write
Returns:
--*/
{
PciIo->Io.Write (
PciIo,
EfiPciIoWidthUint8,
EFI_PCI_IO_PASS_THROUGH_BAR,
(UINT64) Port,
1,
&Data
);
}
STATIC
VOID
WritePortW (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT16 Port,
IN UINT16 Data
)
/*++
Routine Description:
Write one word to a specified I/O port.
Arguments:
PciIo - The pointer of EFI_PCI_IO_PROTOCOL
Port - IO port
Data - The data to write
Returns:
--*/
{
PciIo->Io.Write (
PciIo,
EfiPciIoWidthUint16,
EFI_PCI_IO_PASS_THROUGH_BAR,
(UINT64) Port,
1,
&Data
);
}
STATIC
EFI_STATUS
StatusDRQClear (
ATAPI_EXT_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,
UINT64 TimeoutInMicroSeconds
)
/*++
Routine Description:
Check whether DRQ is clear in the Status Register. (BSY must also be cleared)
If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is
elapsed.
Arguments:
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV
TimeoutInMicroSeconds - The time to wait for
Returns:
EFI_STATUS
--*/
{
UINT64 Delay;
UINT8 StatusRegister;
UINT8 ErrRegister;
UINTN Remainder;
if (TimeoutInMicroSeconds == 0) {
Delay = 2;
} else {
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30, &Remainder) + 1;
}
do {
StatusRegister = ReadPortB (
AtapiScsiPrivate->PciIo,
AtapiScsiPrivate->IoPort->Reg.Status
);
//
// wait for BSY == 0 and DRQ == 0
//
if ((StatusRegister & (DRQ | BSY)) == 0) {
break;
}
//
// check whether the command is aborted by the device
//
if ((StatusRegister & (BSY | ERR)) == ERR) {
ErrRegister = ReadPortB (
AtapiScsiPrivate->PciIo,
AtapiScsiPrivate->IoPort->Reg1.Error
);
if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {
return EFI_ABORTED;
}
}
//
// Stall for 30 us
//
gBS->Stall (30);
//
// Loop infinitely if not meeting expected condition
//
if (TimeoutInMicroSeconds == 0) {
Delay = 2;
}
Delay--;
} while (Delay);
if (Delay == 0) {
return EFI_TIMEOUT;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
AltStatusDRQClear (
ATAPI_EXT_SCSI_PASS_THRU_DEV *AtapiScsiPrivate,
UINT64 TimeoutInMicroSeconds
)
/*++
Routine Description:
Check whether DRQ is clear in the Alternate Status Register.
(BSY must also be cleared).If TimeoutInMicroSeconds is zero, this routine should
wait infinitely for DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is
elapsed.
Arguments:
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV
TimeoutInMicroSeconds - The time to wait for
Returns:
EFI_STATUS
--*/
{
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?