74 lines
2.3 KiB
C++
74 lines
2.3 KiB
C++
#include "arch/x86_64/disk/ATA.h"
|
|
#include "Log.h"
|
|
#include "arch/x86_64/IO.h"
|
|
#include <luna/Vector.h>
|
|
|
|
SharedPtr<ATA::Controller> g_controller;
|
|
|
|
namespace ATA
|
|
{
|
|
Result<void> Controller::scan()
|
|
{
|
|
// FIXME: Propagate errors.
|
|
PCI::scan(
|
|
[](const PCI::Device& device) {
|
|
if (!g_controller)
|
|
{
|
|
auto controller = adopt_shared_if_nonnull(new (std::nothrow) Controller(device)).release_value();
|
|
kinfoln("ata: Found ATA controller on PCI bus (%x:%x:%x)", device.address.bus,
|
|
device.address.function, device.address.slot);
|
|
|
|
if (controller->initialize()) g_controller = controller;
|
|
}
|
|
},
|
|
{ .klass = 1, .subclass = 1 });
|
|
|
|
return {};
|
|
}
|
|
|
|
bool Controller::initialize()
|
|
{
|
|
if (!m_primary_channel.initialize()) return false;
|
|
|
|
return m_secondary_channel.initialize();
|
|
}
|
|
|
|
Controller::Controller(const PCI::Device& device)
|
|
: m_device(device), m_primary_channel(this, 0, {}), m_secondary_channel(this, 1, {})
|
|
{
|
|
}
|
|
|
|
Channel::Channel(Controller* controller, u8 channel_index, Badge<Controller>)
|
|
: m_controller(controller), m_channel_index(channel_index)
|
|
{
|
|
}
|
|
|
|
bool Channel::initialize()
|
|
{
|
|
int offset = m_channel_index ? 2 : 0;
|
|
m_is_pci_native_mode = m_controller->device().type.prog_if & (1 << offset);
|
|
kinfoln("ata: Channel %d is %sin PCI native mode", m_channel_index, m_is_pci_native_mode ? "" : "not ");
|
|
|
|
if (m_is_pci_native_mode)
|
|
{
|
|
m_base_address = PCI::read32(m_controller->device().address, m_channel_index ? PCI::BAR2 : PCI::BAR0);
|
|
m_control_port_base_address =
|
|
PCI::read32(m_controller->device().address, m_channel_index ? PCI::BAR3 : PCI::BAR1);
|
|
}
|
|
else
|
|
{
|
|
m_base_address = m_channel_index ? 0x170 : 0x1f0;
|
|
m_control_port_base_address = m_channel_index ? 0x376 : 0x3f6;
|
|
}
|
|
|
|
u8 interrupt_line;
|
|
if (m_is_pci_native_mode) interrupt_line = PCI::read8(m_controller->device().address, PCI::InterruptLine);
|
|
else
|
|
interrupt_line = m_channel_index ? 15 : 14;
|
|
|
|
kinfoln("ata: Channel %d uses IRQ %hhu", m_channel_index, interrupt_line);
|
|
|
|
return true;
|
|
}
|
|
}
|