kernel/ATA: Add support for regular ATA drives (non-ATAPI)
Don't know why this took so long to figure out, I just had to pass the right value to select().
This commit is contained in:
parent
86372a3893
commit
6443ec77f8
@ -157,13 +157,27 @@ namespace ATA
|
||||
|
||||
void Channel::select(u8 drive)
|
||||
{
|
||||
if (drive == m_current_drive) return;
|
||||
|
||||
u8 value = (u8)(drive << 4) | 0xa0;
|
||||
if (value == m_current_select_value) return;
|
||||
|
||||
write_register(Register::DriveSelect, value);
|
||||
|
||||
delay_400ns();
|
||||
|
||||
m_current_select_value = value;
|
||||
m_current_drive = drive;
|
||||
}
|
||||
|
||||
void Channel::select(u8 base, u8 drive)
|
||||
{
|
||||
u8 value = (u8)(drive << 4) | base;
|
||||
if (value == m_current_select_value) return;
|
||||
|
||||
write_register(Register::DriveSelect, value);
|
||||
|
||||
delay_400ns();
|
||||
|
||||
m_current_select_value = value;
|
||||
m_current_drive = drive;
|
||||
}
|
||||
|
||||
@ -543,10 +557,61 @@ namespace ATA
|
||||
return true;
|
||||
}
|
||||
|
||||
Result<void> Drive::send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size)
|
||||
void Drive::select_lba(u64 lba, usize count)
|
||||
{
|
||||
if (m_is_lba48)
|
||||
{
|
||||
m_channel->write_register(Register::SectorCount, (u8)((count >> 8) & 0xff));
|
||||
m_channel->write_register(Register::LBALow, (u8)((lba >> 24) & 0xff));
|
||||
m_channel->write_register(Register::LBAMiddle, (u8)((lba >> 32) & 0xff));
|
||||
m_channel->write_register(Register::LBAHigh, (u8)((lba >> 40) & 0xff));
|
||||
}
|
||||
m_channel->write_register(Register::SectorCount, (u8)(count & 0xff));
|
||||
m_channel->write_register(Register::LBALow, (u8)(lba & 0xff));
|
||||
m_channel->write_register(Register::LBAMiddle, (u8)((lba >> 8) & 0xff));
|
||||
m_channel->write_register(Register::LBAHigh, (u8)((lba >> 16) & 0xff));
|
||||
}
|
||||
|
||||
Result<void> Drive::read_pio_bytes(void* out, usize size)
|
||||
{
|
||||
u8* ptr = (u8*)out;
|
||||
|
||||
usize i = 0;
|
||||
|
||||
while (i < size)
|
||||
{
|
||||
TRY(m_channel->wait_until_ready());
|
||||
|
||||
usize byte_count;
|
||||
|
||||
if (m_is_atapi)
|
||||
{
|
||||
byte_count =
|
||||
m_channel->read_register(Register::LBAHigh) << 8 | m_channel->read_register(Register::LBAMiddle);
|
||||
}
|
||||
else
|
||||
byte_count = min(512ul, size - i);
|
||||
|
||||
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;
|
||||
|
||||
m_channel->delay_400ns();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> Drive::send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size)
|
||||
{
|
||||
m_channel->select(m_drive_index);
|
||||
|
||||
// We use PIO here.
|
||||
@ -559,30 +624,11 @@ namespace ATA
|
||||
|
||||
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;
|
||||
}
|
||||
TRY(read_pio_bytes(out, response_size));
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -704,6 +750,26 @@ namespace ATA
|
||||
return send_packet_atapi_pio(&read_packet, out, (u16)size);
|
||||
}
|
||||
|
||||
Result<void> Drive::ata_read_pio(u64 lba, void* out, usize size)
|
||||
{
|
||||
check(lba < m_block_count);
|
||||
check(size <= ARCH_PAGE_SIZE);
|
||||
|
||||
usize count = ceil_div(size, m_block_size);
|
||||
|
||||
m_channel->select(m_is_lba48 ? 0x40 : (0xe0 | ((lba >> 24) & 0xf)), m_drive_index);
|
||||
|
||||
select_lba(lba, count);
|
||||
|
||||
m_channel->write_register(Register::Command, m_is_lba48 ? CMD_ReadSectorsExt : CMD_ReadSectors);
|
||||
|
||||
m_channel->delay_400ns();
|
||||
|
||||
TRY(read_pio_bytes(out, size));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> Drive::read_lba(u64 lba, void* out, usize nblocks)
|
||||
{
|
||||
const usize blocks_per_page = ARCH_PAGE_SIZE / m_block_size;
|
||||
@ -719,7 +785,16 @@ namespace ATA
|
||||
return atapi_read_pio(lba, out, nblocks * m_block_size);
|
||||
}
|
||||
else
|
||||
todo();
|
||||
{
|
||||
while (nblocks > blocks_per_page)
|
||||
{
|
||||
TRY(ata_read_pio(lba, out, ARCH_PAGE_SIZE));
|
||||
lba += blocks_per_page;
|
||||
nblocks -= blocks_per_page;
|
||||
out = offset_ptr(out, ARCH_PAGE_SIZE);
|
||||
}
|
||||
return ata_read_pio(lba, out, nblocks * m_block_size);
|
||||
}
|
||||
}
|
||||
|
||||
void Drive::irq_handler()
|
||||
|
@ -54,6 +54,8 @@ namespace ATA
|
||||
|
||||
enum CommandRegister : u8
|
||||
{
|
||||
CMD_ReadSectors = 0x20,
|
||||
CMD_ReadSectorsExt = 0x24,
|
||||
CMD_Identify = 0xec,
|
||||
CMD_Packet = 0xa0,
|
||||
CMD_Identify_Packet = 0xa1
|
||||
@ -180,6 +182,11 @@ namespace ATA
|
||||
|
||||
Result<void> atapi_read_pio(u64 lba, void* out, usize size);
|
||||
|
||||
Result<void> ata_read_pio(u64 lba, void* out, usize size);
|
||||
|
||||
void select_lba(u64 lba, usize count);
|
||||
Result<void> read_pio_bytes(void* out, usize size);
|
||||
|
||||
Channel* m_channel;
|
||||
|
||||
u8 m_drive_index;
|
||||
@ -250,6 +257,7 @@ namespace ATA
|
||||
}
|
||||
|
||||
void select(u8 drive);
|
||||
void select(u8 base, u8 drive);
|
||||
|
||||
bool initialize();
|
||||
|
||||
@ -270,6 +278,7 @@ namespace ATA
|
||||
bool m_irq_called { false };
|
||||
|
||||
u8 m_current_drive = (u8)-1;
|
||||
u8 m_current_select_value = 0xff;
|
||||
|
||||
Option<Drive> m_drives[2];
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user