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);
|
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)
|
void Channel::write_register(Register reg, u8 value)
|
||||||
{
|
{
|
||||||
IO::outb(m_io_base + (u16)reg, value);
|
IO::outb(m_io_base + (u16)reg, value);
|
||||||
@ -154,7 +159,7 @@ namespace ATA
|
|||||||
|
|
||||||
kernel_sleep(timeout);
|
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)
|
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()
|
bool Channel::initialize()
|
||||||
{
|
{
|
||||||
int offset = m_channel_index ? 2 : 0;
|
int offset = m_channel_index ? 2 : 0;
|
||||||
@ -375,9 +405,75 @@ namespace ATA
|
|||||||
m_dma_mem_phys = frame.release_value();
|
m_dma_mem_phys = frame.release_value();
|
||||||
m_dma_mem = (void*)MMU::translate_physical_address(m_dma_mem_phys);
|
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;
|
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()
|
void Drive::irq_handler()
|
||||||
{
|
{
|
||||||
// Clear the IRQ flag.
|
// Clear the IRQ flag.
|
||||||
|
@ -39,7 +39,7 @@ namespace ATA
|
|||||||
PRDTAddress = 4,
|
PRDTAddress = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum StatusRegister
|
enum StatusRegister : u8
|
||||||
{
|
{
|
||||||
SR_Busy = 0x80,
|
SR_Busy = 0x80,
|
||||||
SR_DriveReady = 0x40,
|
SR_DriveReady = 0x40,
|
||||||
@ -51,13 +51,14 @@ namespace ATA
|
|||||||
SR_Error = 0x01
|
SR_Error = 0x01
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CommandRegister
|
enum CommandRegister : u8
|
||||||
{
|
{
|
||||||
CMD_Identify = 0xec,
|
CMD_Identify = 0xec,
|
||||||
|
CMD_Packet = 0xa0,
|
||||||
CMD_Identify_Packet = 0xa1
|
CMD_Identify_Packet = 0xa1
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BusMasterStatus
|
enum BusMasterStatus : u8
|
||||||
{
|
{
|
||||||
BMS_SimplexOnly = 0x80,
|
BMS_SimplexOnly = 0x80,
|
||||||
BMS_SlaveInit = 0x40,
|
BMS_SlaveInit = 0x40,
|
||||||
@ -67,6 +68,11 @@ namespace ATA
|
|||||||
BMS_DMAMode = 0x1
|
BMS_DMAMode = 0x1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ATAPICommand : u8
|
||||||
|
{
|
||||||
|
ATAPI_ReadCapacity = 0x25,
|
||||||
|
};
|
||||||
|
|
||||||
class Controller;
|
class Controller;
|
||||||
class Channel;
|
class Channel;
|
||||||
|
|
||||||
@ -77,6 +83,20 @@ namespace ATA
|
|||||||
u16 flags;
|
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);
|
static constexpr u16 END_OF_PRDT = (1 << 15);
|
||||||
|
|
||||||
class Drive
|
class Drive
|
||||||
@ -91,6 +111,8 @@ namespace ATA
|
|||||||
private:
|
private:
|
||||||
bool identify_ata();
|
bool identify_ata();
|
||||||
|
|
||||||
|
Result<void> send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size);
|
||||||
|
|
||||||
Channel* m_channel;
|
Channel* m_channel;
|
||||||
|
|
||||||
u8 m_drive_index;
|
u8 m_drive_index;
|
||||||
@ -123,6 +145,7 @@ namespace ATA
|
|||||||
|
|
||||||
u8 read_register(Register reg);
|
u8 read_register(Register reg);
|
||||||
u16 read_data();
|
u16 read_data();
|
||||||
|
void write_data(u16 value);
|
||||||
void write_register(Register reg, u8 value);
|
void write_register(Register reg, u8 value);
|
||||||
u8 read_control(ControlRegister reg);
|
u8 read_control(ControlRegister reg);
|
||||||
void write_control(ControlRegister reg, u8 value);
|
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_set(Register reg, u8 value, u64 timeout);
|
||||||
bool wait_for_reg_clear(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 delay_400ns();
|
||||||
|
|
||||||
void wait_for_irq();
|
void wait_for_irq();
|
||||||
|
Loading…
Reference in New Issue
Block a user