Add ATA drive support #27
@ -3,6 +3,7 @@
|
||||
#include "arch/Serial.h"
|
||||
#include "arch/Timer.h"
|
||||
#include "arch/x86_64/IO.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include <luna/Vector.h>
|
||||
|
||||
SharedPtr<ATA::Controller> g_controller;
|
||||
@ -91,6 +92,26 @@ namespace ATA
|
||||
IO::outb(m_control_base + (u16)reg, value);
|
||||
}
|
||||
|
||||
u8 Channel::read_bm(BusmasterRegister reg)
|
||||
{
|
||||
return IO::inb(m_busmaster_base + (u16)reg);
|
||||
}
|
||||
|
||||
void Channel::write_bm(BusmasterRegister reg, u8 value)
|
||||
{
|
||||
IO::outb(m_busmaster_base + (u16)reg, value);
|
||||
}
|
||||
|
||||
u32 Channel::read_prdt_address()
|
||||
{
|
||||
return IO::inl(m_busmaster_base + (u16)BusmasterRegister::PRDTAddress);
|
||||
}
|
||||
|
||||
void Channel::write_prdt_address(u32 value)
|
||||
{
|
||||
IO::outl(m_busmaster_base + (u16)BusmasterRegister::PRDTAddress, value);
|
||||
}
|
||||
|
||||
void Channel::delay_400ns()
|
||||
{
|
||||
// FIXME: We should use kernel_sleep(), but it doesn't support nanosecond granularity.
|
||||
@ -127,6 +148,15 @@ namespace ATA
|
||||
kernel_wait_for_event();
|
||||
}
|
||||
|
||||
bool Channel::wait_for_irq_or_timeout(u64 timeout)
|
||||
{
|
||||
m_thread = Scheduler::current();
|
||||
|
||||
kernel_sleep(timeout);
|
||||
|
||||
return !m_thread->sleep_ticks_left;
|
||||
}
|
||||
|
||||
bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout)
|
||||
{
|
||||
u64 begin = Timer::ticks_ms();
|
||||
@ -320,6 +350,31 @@ namespace ATA
|
||||
kinfoln("ata: Drive IDENTIFY returned serial='%s', revision='%s' and model='%s'", m_serial.chars(),
|
||||
m_revision.chars(), m_model.chars());
|
||||
|
||||
auto status = m_channel->read_bm(BusmasterRegister::Status);
|
||||
if (status & BMS_SimplexOnly)
|
||||
{
|
||||
kwarnln("ata: Drive %d will not use DMA because of simplex shenanigans", m_drive_index);
|
||||
m_uses_dma = false;
|
||||
}
|
||||
|
||||
auto frame = MemoryManager::alloc_frame();
|
||||
if (frame.has_error() || frame.value() > 0xffffffff)
|
||||
{
|
||||
kwarnln("ata: Failed to allocate memory below the 32-bit limit for the PRDT");
|
||||
return false;
|
||||
}
|
||||
m_dma_prdt_phys = frame.release_value();
|
||||
m_dma_prdt = (prdt_entry*)MMU::translate_physical_address(m_dma_prdt_phys);
|
||||
|
||||
frame = MemoryManager::alloc_frame();
|
||||
if (frame.has_error() || frame.value() > 0xffffffff)
|
||||
{
|
||||
kwarnln("ata: Failed to allocate memory below the 32-bit limit for DMA memory");
|
||||
return false;
|
||||
}
|
||||
m_dma_mem_phys = frame.release_value();
|
||||
m_dma_mem = (void*)MMU::translate_physical_address(m_dma_mem_phys);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,13 @@ namespace ATA
|
||||
DriveAddress = 1,
|
||||
};
|
||||
|
||||
enum class BusmasterRegister : u16
|
||||
{
|
||||
Command = 0,
|
||||
Status = 2,
|
||||
PRDTAddress = 4,
|
||||
};
|
||||
|
||||
enum StatusRegister
|
||||
{
|
||||
SR_Busy = 0x80,
|
||||
@ -50,9 +57,28 @@ namespace ATA
|
||||
CMD_Identify_Packet = 0xa1
|
||||
};
|
||||
|
||||
enum BusMasterStatus
|
||||
{
|
||||
BMS_SimplexOnly = 0x80,
|
||||
BMS_SlaveInit = 0x40,
|
||||
BMS_MasterInit = 0x20,
|
||||
BMS_IRQPending = 0x4,
|
||||
BMS_DMAFailure = 0x2,
|
||||
BMS_DMAMode = 0x1
|
||||
};
|
||||
|
||||
class Controller;
|
||||
class Channel;
|
||||
|
||||
struct prdt_entry
|
||||
{
|
||||
u32 address;
|
||||
u16 count;
|
||||
u16 flags;
|
||||
};
|
||||
|
||||
static constexpr u16 END_OF_PRDT = (1 << 15);
|
||||
|
||||
class Drive
|
||||
{
|
||||
public:
|
||||
@ -74,6 +100,12 @@ namespace ATA
|
||||
};
|
||||
|
||||
bool m_is_atapi { false };
|
||||
bool m_uses_dma { true };
|
||||
|
||||
volatile prdt_entry* m_dma_prdt;
|
||||
u64 m_dma_prdt_phys;
|
||||
volatile void* m_dma_mem;
|
||||
u64 m_dma_mem_phys;
|
||||
|
||||
constexpr static usize SERIAL_LEN = 20;
|
||||
constexpr static usize REVISION_LEN = 8;
|
||||
@ -95,12 +127,18 @@ namespace ATA
|
||||
u8 read_control(ControlRegister reg);
|
||||
void write_control(ControlRegister reg, u8 value);
|
||||
|
||||
u8 read_bm(BusmasterRegister reg);
|
||||
void write_bm(BusmasterRegister reg, u8 value);
|
||||
u32 read_prdt_address();
|
||||
void write_prdt_address(u32 value);
|
||||
|
||||
bool wait_for_reg_set(Register reg, u8 value, u64 timeout);
|
||||
bool wait_for_reg_clear(Register reg, u8 value, u64 timeout);
|
||||
|
||||
void delay_400ns();
|
||||
|
||||
void wait_for_irq();
|
||||
bool wait_for_irq_or_timeout(u64 timeout);
|
||||
void irq_handler(Registers*);
|
||||
|
||||
void select(u8 drive);
|
||||
|
Loading…
Reference in New Issue
Block a user