kernel/ATA: Send a READ CAPACITY packet to an ATA drive on initialization
This commit is contained in:
parent
cfcde5af55
commit
46c45068e0
@ -77,6 +77,11 @@ namespace ATA
|
||||
return IO::inw(m_io_base + (u16)Register::Data);
|
||||
}
|
||||
|
||||
void Channel::write_data(u16 value)
|
||||
{
|
||||
IO::outw(m_io_base + (u16)Register::Data, value);
|
||||
}
|
||||
|
||||
void Channel::write_register(Register reg, u8 value)
|
||||
{
|
||||
IO::outb(m_io_base + (u16)reg, value);
|
||||
@ -154,7 +159,7 @@ namespace ATA
|
||||
|
||||
kernel_sleep(timeout);
|
||||
|
||||
return !m_thread->sleep_ticks_left;
|
||||
return m_thread->sleep_ticks_left;
|
||||
}
|
||||
|
||||
bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout)
|
||||
@ -181,6 +186,31 @@ namespace ATA
|
||||
}
|
||||
}
|
||||
|
||||
Result<void> Channel::wait_until_ready()
|
||||
{
|
||||
if (!wait_for_reg_clear(Register::Status, SR_Busy, 1000))
|
||||
{
|
||||
kwarnln("ata: Drive %d:%d timed out (BSY)", m_channel_index, m_current_drive);
|
||||
return err(EIO);
|
||||
}
|
||||
|
||||
if (!wait_for_reg_set(Register::Status, SR_DataRequestReady | SR_Error, 1000))
|
||||
{
|
||||
kwarnln("ata: Drive %d:%d timed out (DRQ)", m_channel_index, m_current_drive);
|
||||
return err(EIO);
|
||||
}
|
||||
|
||||
u8 status = read_register(Register::Status);
|
||||
if (status & SR_Error)
|
||||
{
|
||||
kwarnln("ata: An error occurred in drive %d:%d while waiting for data to become available", m_channel_index,
|
||||
m_current_drive);
|
||||
return err(EIO);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Channel::initialize()
|
||||
{
|
||||
int offset = m_channel_index ? 2 : 0;
|
||||
@ -375,9 +405,75 @@ namespace ATA
|
||||
m_dma_mem_phys = frame.release_value();
|
||||
m_dma_mem = (void*)MMU::translate_physical_address(m_dma_mem_phys);
|
||||
|
||||
if (m_is_atapi)
|
||||
{
|
||||
atapi_packet packet;
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
packet.command_bytes[0] = ATAPI_ReadCapacity;
|
||||
|
||||
atapi_read_capacity_reply reply;
|
||||
|
||||
if (send_packet_atapi_pio(&packet, &reply, sizeof(reply)).has_error())
|
||||
{
|
||||
kwarnln("ata: Failed to send Read Capacity command to ATAPI drive");
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: This assumes the host machine is little-endian.
|
||||
u32 last_lba = __builtin_bswap32(reply.last_lba);
|
||||
u32 sector_size = __builtin_bswap32(reply.sector_size);
|
||||
|
||||
kinfoln("ata: ATAPI drive %d capacity information: Last LBA=%u, Sector Size=%u, Total Capacity=%u",
|
||||
m_drive_index, last_lba, sector_size, (last_lba + 1) * sector_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Result<void> Drive::send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size)
|
||||
{
|
||||
u8* ptr = (u8*)out;
|
||||
|
||||
m_channel->select(m_drive_index);
|
||||
|
||||
// We use PIO here.
|
||||
m_channel->write_register(Register::Features, 0x00);
|
||||
|
||||
m_channel->write_register(Register::LBAMiddle, (u8)(response_size & 0xff));
|
||||
m_channel->write_register(Register::LBAHigh, (u8)(response_size >> 8));
|
||||
|
||||
m_channel->write_register(Register::Command, CMD_Packet);
|
||||
|
||||
m_channel->delay_400ns();
|
||||
|
||||
usize i = 0;
|
||||
|
||||
TRY(m_channel->wait_until_ready());
|
||||
|
||||
for (int j = 0; j < 6; j++) m_channel->write_data(packet->command_words[j]);
|
||||
|
||||
while (i < response_size)
|
||||
{
|
||||
TRY(m_channel->wait_until_ready());
|
||||
|
||||
usize byte_count =
|
||||
m_channel->read_register(Register::LBAHigh) << 8 | m_channel->read_register(Register::LBAMiddle);
|
||||
usize word_count = byte_count / 2;
|
||||
|
||||
while (word_count--)
|
||||
{
|
||||
u16 value = m_channel->read_data();
|
||||
ptr[0] = (u8)(value & 0xff);
|
||||
ptr[1] = (u8)(value >> 8);
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
i += byte_count;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Drive::irq_handler()
|
||||
{
|
||||
// Clear the IRQ flag.
|
||||
|
@ -39,7 +39,7 @@ namespace ATA
|
||||
PRDTAddress = 4,
|
||||
};
|
||||
|
||||
enum StatusRegister
|
||||
enum StatusRegister : u8
|
||||
{
|
||||
SR_Busy = 0x80,
|
||||
SR_DriveReady = 0x40,
|
||||
@ -51,13 +51,14 @@ namespace ATA
|
||||
SR_Error = 0x01
|
||||
};
|
||||
|
||||
enum CommandRegister
|
||||
enum CommandRegister : u8
|
||||
{
|
||||
CMD_Identify = 0xec,
|
||||
CMD_Packet = 0xa0,
|
||||
CMD_Identify_Packet = 0xa1
|
||||
};
|
||||
|
||||
enum BusMasterStatus
|
||||
enum BusMasterStatus : u8
|
||||
{
|
||||
BMS_SimplexOnly = 0x80,
|
||||
BMS_SlaveInit = 0x40,
|
||||
@ -67,6 +68,11 @@ namespace ATA
|
||||
BMS_DMAMode = 0x1
|
||||
};
|
||||
|
||||
enum ATAPICommand : u8
|
||||
{
|
||||
ATAPI_ReadCapacity = 0x25,
|
||||
};
|
||||
|
||||
class Controller;
|
||||
class Channel;
|
||||
|
||||
@ -77,6 +83,20 @@ namespace ATA
|
||||
u16 flags;
|
||||
};
|
||||
|
||||
struct atapi_packet
|
||||
{
|
||||
union {
|
||||
u16 command_words[6];
|
||||
u8 command_bytes[12];
|
||||
};
|
||||
};
|
||||
|
||||
struct atapi_read_capacity_reply
|
||||
{
|
||||
u32 last_lba;
|
||||
u32 sector_size;
|
||||
};
|
||||
|
||||
static constexpr u16 END_OF_PRDT = (1 << 15);
|
||||
|
||||
class Drive
|
||||
@ -91,6 +111,8 @@ namespace ATA
|
||||
private:
|
||||
bool identify_ata();
|
||||
|
||||
Result<void> send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size);
|
||||
|
||||
Channel* m_channel;
|
||||
|
||||
u8 m_drive_index;
|
||||
@ -123,6 +145,7 @@ namespace ATA
|
||||
|
||||
u8 read_register(Register reg);
|
||||
u16 read_data();
|
||||
void write_data(u16 value);
|
||||
void write_register(Register reg, u8 value);
|
||||
u8 read_control(ControlRegister reg);
|
||||
void write_control(ControlRegister reg, u8 value);
|
||||
@ -135,6 +158,8 @@ namespace ATA
|
||||
bool wait_for_reg_set(Register reg, u8 value, u64 timeout);
|
||||
bool wait_for_reg_clear(Register reg, u8 value, u64 timeout);
|
||||
|
||||
Result<void> wait_until_ready();
|
||||
|
||||
void delay_400ns();
|
||||
|
||||
void wait_for_irq();
|
||||
|
Loading…
Reference in New Issue
Block a user