📄 ide.c
字号:
//
// DiscoverIdeDevice
//
EFI_STATUS
DiscoverIdeDevice (
IN IDE_BLK_IO_DEV *IdeDev
)
/*++
Routine Description:
Detect if there is disk connected to this port
Arguments:
IdeDev - The BLK_IO private data which specifies the IDE device
++*/
// TODO: function comment should end with '--*/'
// TODO: function comment is missing 'Returns:'
// TODO: EFI_NOT_FOUND - add return value to function comment
// TODO: EFI_NOT_FOUND - add return value to function comment
// TODO: EFI_SUCCESS - add return value to function comment
{
EFI_STATUS Status;
//
// If a channel has not been checked, check it now. Then set it to "checked" state
// After this step, all devices in this channel have been checked.
//
if (ChannelDeviceDetected == FALSE) {
Status = DetectIDEController (IdeDev);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
}
Status = EFI_NOT_FOUND;
//
// Device exists. test if it is an ATA device.
// Prefer the result from DetectIDEController,
// if failed, try another device type to handle
// devices that not follow the spec.
//
if ((IdeDev->Device == IdeMaster) && (MasterDeviceExist)) {
if (MasterDeviceType == ATA_DEVICE_TYPE) {
Status = ATAIdentify (IdeDev);
if (EFI_ERROR (Status)) {
Status = ATAPIIdentify (IdeDev);
if (!EFI_ERROR (Status)) {
MasterDeviceType = ATAPI_DEVICE_TYPE;
}
}
} else {
Status = ATAPIIdentify (IdeDev);
if (EFI_ERROR (Status)) {
Status = ATAIdentify (IdeDev);
if (!EFI_ERROR (Status)) {
MasterDeviceType = ATA_DEVICE_TYPE;
}
}
}
}
if ((IdeDev->Device == IdeSlave) && (SlaveDeviceExist)) {
if (SlaveDeviceType == ATA_DEVICE_TYPE) {
Status = ATAIdentify (IdeDev);
if (EFI_ERROR (Status)) {
Status = ATAPIIdentify (IdeDev);
if (!EFI_ERROR (Status)) {
SlaveDeviceType = ATAPI_DEVICE_TYPE;
}
}
} else {
Status = ATAPIIdentify (IdeDev);
if (EFI_ERROR (Status)) {
Status = ATAIdentify (IdeDev);
if (!EFI_ERROR (Status)) {
SlaveDeviceType = ATA_DEVICE_TYPE;
}
}
}
}
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
//
// Init Block I/O interface
//
IdeDev->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION;
IdeDev->BlkIo.Reset = IDEBlkIoReset;
IdeDev->BlkIo.ReadBlocks = IDEBlkIoReadBlocks;
IdeDev->BlkIo.WriteBlocks = IDEBlkIoWriteBlocks;
IdeDev->BlkIo.FlushBlocks = IDEBlkIoFlushBlocks;
IdeDev->BlkMedia.LogicalPartition = FALSE;
IdeDev->BlkMedia.WriteCaching = FALSE;
//
// Init Disk Info interface
//
gBS->CopyMem (&IdeDev->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid, sizeof (EFI_GUID));
IdeDev->DiskInfo.Inquiry = IDEDiskInfoInquiry;
IdeDev->DiskInfo.Identify = IDEDiskInfoIdentify;
IdeDev->DiskInfo.SenseData = IDEDiskInfoSenseData;
IdeDev->DiskInfo.WhichIde = IDEDiskInfoWhichIde;
return EFI_SUCCESS;
}
EFI_STATUS
InitializeIDEChannelData (
VOID
)
/*++
Name: InitializeIDEChannelData
Purpose:
This function initializes all state data related to the detection of one
channel.
Parameters:
Returns:
EFI_SUCCESS
Notes:
--*/
{
ChannelDeviceDetected = FALSE;
MasterDeviceExist = FALSE;
MasterDeviceType = 0xff;
SlaveDeviceExist = FALSE;
SlaveDeviceType = 0xff;
return EFI_SUCCESS;
}
EFI_STATUS
DetectIDEController (
IN IDE_BLK_IO_DEV *IdeDev
)
/*++
Name: DetectIDEController
Purpose:
This function is called by DiscoverIdeDevice(). It is used for detect
whether the IDE device exists in the specified Channel as the specified
Device Number.
There is two IDE channels: one is Primary Channel, the other is
Secondary Channel.(Channel is the logical name for the physical "Cable".)
Different channel has different register group.
On each IDE channel, at most two IDE devices attach,
one is called Device 0 (Master device), the other is called Device 1
(Slave device). The devices on the same channel co-use the same register
group, so before sending out a command for a specified device via command
register, it is a must to select the current device to accept the command
by set the device number in the Head/Device Register.
Parameters:
IDE_BLK_IO_DEV IN *IdeDev
pointer pointing to IDE_BLK_IO_DEV data structure, used
to record all the information of the IDE device.
Returns:
TRUE
successfully detects device.
FALSE
any failure during detection process will return this
value.
Notes:
--*/
{
EFI_STATUS Status;
UINT8 SectorCountReg;
UINT8 LBALowReg;
UINT8 LBAMidReg;
UINT8 LBAHighReg;
UINT8 InitStatusReg;
UINT8 StatusReg;
//
// Select slave device
//
IDEWritePortB (
IdeDev->PciIo,
IdeDev->IoPort->Head,
(UINT8) ((1 << 4) | 0xe0)
);
gBS->Stall (100);
//
// Save the init slave status register
//
InitStatusReg = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
//
// Select Master back
//
IDEWritePortB (
IdeDev->PciIo,
IdeDev->IoPort->Head,
(UINT8) ((0 << 4) | 0xe0)
);
gBS->Stall (100);
//
// Send ATA Device Execut Diagnostic command.
// This command should work no matter DRDY is ready or not
//
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 0x90);
Status = WaitForBSYClear (IdeDev, 3500);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_ERROR, "New detecting method: Send Execute Diagnostic Command: WaitForBSYClear: Status: %d\n", Status));
return Status;
}
//
// Read device signature
//
//
// Select Master
//
IDEWritePortB (
IdeDev->PciIo,
IdeDev->IoPort->Head,
(UINT8) ((0 << 4) | 0xe0)
);
gBS->Stall (100);
SectorCountReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->SectorCount
);
LBALowReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->SectorNumber
);
LBAMidReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->CylinderLsb
);
LBAHighReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->CylinderMsb
);
if ((SectorCountReg == 0x1) &&
(LBALowReg == 0x1) &&
(LBAMidReg == 0x0) &&
(LBAHighReg == 0x0)) {
MasterDeviceExist = TRUE;
MasterDeviceType = ATA_DEVICE_TYPE;
} else {
if ((LBAMidReg == 0x14) &&
(LBAHighReg == 0xeb)) {
MasterDeviceExist = TRUE;
MasterDeviceType = ATAPI_DEVICE_TYPE;
}
}
//
// For some Hard Drive, it takes some time to get
// the right signature when operating in single slave mode.
// We stall 20ms to work around this.
//
if (!MasterDeviceExist) {
gBS->Stall (20000);
}
//
// Select Slave
//
IDEWritePortB (
IdeDev->PciIo,
IdeDev->IoPort->Head,
(UINT8) ((1 << 4) | 0xe0)
);
gBS->Stall (100);
SectorCountReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->SectorCount
);
LBALowReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->SectorNumber
);
LBAMidReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->CylinderLsb
);
LBAHighReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->CylinderMsb
);
StatusReg = IDEReadPortB (
IdeDev->PciIo,
IdeDev->IoPort->Reg.Status
);
if ((SectorCountReg == 0x1) &&
(LBALowReg == 0x1) &&
(LBAMidReg == 0x0) &&
(LBAHighReg == 0x0)) {
SlaveDeviceExist = TRUE;
SlaveDeviceType = ATA_DEVICE_TYPE;
} else {
if ((LBAMidReg == 0x14) &&
(LBAHighReg == 0xeb)) {
SlaveDeviceExist = TRUE;
SlaveDeviceType = ATAPI_DEVICE_TYPE;
}
}
//
// When single master is plugged, slave device
// will be wrongly detected. Here's the workaround
// for ATA devices by detecting DRY bit in status
// register.
// NOTE: This workaround doesn't apply to ATAPI.
//
if (MasterDeviceExist && SlaveDeviceExist &&
(StatusReg & DRDY) == 0 &&
(InitStatusReg & DRDY) == 0 &&
MasterDeviceType == SlaveDeviceType &&
SlaveDeviceType != ATAPI_DEVICE_TYPE) {
SlaveDeviceExist = FALSE;
}
//
// Indicate this channel has been detected
//
ChannelDeviceDetected = TRUE;
return EFI_SUCCESS;
}
EFI_STATUS
DRQClear (
IN IDE_BLK_IO_DEV *IdeDev,
IN UINTN TimeoutInMilliSeconds
)
/*++
Name: DRQClear
Purpose:
This function is used to poll for the DRQ bit clear in the Status
Register. DRQ is cleared when the device is finished transferring data.
So this function is called after data transfer is finished.
Parameters:
IDE_BLK_IO_DEV IN *IdeDev
pointer pointing to IDE_BLK_IO_DEV data structure, used
to record all the information of the IDE device.
UINTN IN TimeoutInMilliSeconds
used to designate the timeout for the DRQ clear.
Returns:
EFI_SUCCESS
DRQ bit clear within the time out.
EFI_TIMEOUT
DRQ bit not clear within the time out.
Notes:
Read Status Register will clear interrupt status.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: TimeoutInMilliSeconds - add argument and description to function comment
// TODO: EFI_ABORTED - add return value to function comment
{
UINT32 Delay;
UINT8 StatusRegister;
UINT8 ErrorRegister;
Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
do {
StatusRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
//
// wait for BSY == 0 and DRQ == 0
//
if ((StatusRegister & (DRQ | BSY)) == 0) {
break;
}
if ((StatusRegister & (BSY | ERR)) == ERR) {
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
if ((ErrorRegister & ABRT_ERR) == ABRT_ERR) {
return EFI_ABORTED;
}
}
//
// Stall for 30 us
//
gBS->Stall (30);
Delay--;
} while (Delay);
if (Delay == 0) {
return EFI_TIMEOUT;
}
return EFI_SUCCESS;
}
EFI_STATUS
DRQClear2 (
IN IDE_BLK_IO_DEV *IdeDev,
IN UINTN TimeoutInMilliSeconds
)
/*++
Name: DRQClear2
Purpose:
This function is used to poll for the DRQ bit clear in the Alternate
Status Register. DRQ is cleared when the device is finished
transferring data. So this function is called after data transfer
is finished.
Parameters:
IDE_BLK_IO_DEV IN *IdeDev
pointer pointing to IDE_BLK_IO_DEV data structure, used
to record all the information of the IDE device.
UINTN IN TimeoutInMilliSeconds
used to designate the timeout for the DRQ clear.
Returns:
EFI_SUCCESS
DRQ bit clear within the time out.
EFI_TIMEOUT
DRQ bit not clear within the time out.
Notes:
Read Alternate Status Register will not clear interrupt status.
--*/
// TODO: function comment is missing 'Routine Description:'
// TODO: function comment is missing 'Arguments:'
// TODO: IdeDev - add argument and description to function comment
// TODO: TimeoutInMilliSeconds - add argument and description to function comment
// TODO: EFI_ABORTED - add return value to function comment
{
UINT32 Delay;
UINT8 AltRegister;
UINT8 ErrorRegister;
Delay = (UINT32) (((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 30) + 1);
do {
AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
//
// wait for BSY == 0 and DRQ == 0
//
if ((AltRegister & (DRQ | BSY)) == 0) {
break;
}
if ((AltRegister & (BSY | ERR)) == ERR) {
ErrorRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
if ((ErrorRegister & ABRT_ERR) == ABRT_ERR) {
return EFI_ABORTED;
}
}
//
// Stall for 30 us
//
gBS->Stall (30);
Delay--;
} while (Delay);
if (Delay == 0) {
return EFI_TIMEOUT;
}
return EFI_SUCCESS;
}
EFI_STATUS
DRQReady (
IN IDE_BLK_IO_DEV *IdeDev,
IN UINTN TimeoutInMilliSeconds
)
/*++
Name: DRQReady
Purpose:
This function is used to poll for the DRQ bit set in the
Status Register.
DRQ is set when the device is ready to transfer data. So this function
is called after the command is sent to the device and before required
data is transferred.
Parameters:
IDE_BLK_IO_DEV IN *IdeDev
pointer pointing to IDE_BLK_IO_DEV data structure,used
to record all the information of the IDE device.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -