mirror of
https://github.com/justinian/jsix.git
synced 2025-12-12 09:24:31 -08:00
Rearrange AHCI code, attempt to read WIP
This commit is contained in:
57
src/kernel/ahci/ata.h
Normal file
57
src/kernel/ahci/ata.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
/// \file ata.h
|
||||
/// Definitions for ATA codes
|
||||
#include <stdint.h>
|
||||
#include "kutil/enum_bitfields.h"
|
||||
|
||||
namespace ahci {
|
||||
|
||||
|
||||
enum class ata_status : uint8_t
|
||||
{
|
||||
error = 0x01,
|
||||
index = 0x02,
|
||||
corrected = 0x04,
|
||||
data_ready = 0x08,
|
||||
seek_done = 0x10,
|
||||
fault = 0x20,
|
||||
ready = 0x40,
|
||||
busy = 0x80
|
||||
};
|
||||
|
||||
|
||||
enum class ata_error : uint8_t
|
||||
{
|
||||
amnf = 0x01, // Address mark not found
|
||||
tkznf = 0x02, // Track 0 not found
|
||||
abort = 0x04, // Command abort
|
||||
mcr = 0x08, // No media
|
||||
idnf = 0x10, // Id not found
|
||||
mc = 0x20, // No media
|
||||
unc = 0x40, // Uncorrectable
|
||||
bbk = 0x80, // Bad sector
|
||||
};
|
||||
|
||||
|
||||
enum class ata_cmd : uint8_t
|
||||
{
|
||||
read_pio = 0x20,
|
||||
read_pio_ext = 0x24,
|
||||
read_dma = 0xC8,
|
||||
read_dma_ext = 0x25,
|
||||
write_pio = 0x30,
|
||||
write_pio_ext = 0x34,
|
||||
write_dma = 0xCA,
|
||||
write_dma_ext = 0x35,
|
||||
cache_flush = 0xE7,
|
||||
cache_flush_ext = 0xEA,
|
||||
packet = 0xA0,
|
||||
identify_packet = 0xA1,
|
||||
identify = 0xEC
|
||||
};
|
||||
|
||||
|
||||
} // namespace ahci
|
||||
|
||||
IS_BITFIELD(ahci::ata_status);
|
||||
IS_BITFIELD(ahci::ata_error);
|
||||
19
src/kernel/ahci/driver.cpp
Normal file
19
src/kernel/ahci/driver.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "ahci/driver.h"
|
||||
#include "log.h"
|
||||
#include "pci.h"
|
||||
|
||||
|
||||
ahci_driver::ahci_driver()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ahci_driver::register_device(pci_device *device)
|
||||
{
|
||||
log::info(logs::driver, "AHCI registering device %d:%d:%d:",
|
||||
device->bus(), device->device(), device->function());
|
||||
|
||||
m_devices.emplace(device);
|
||||
}
|
||||
|
||||
28
src/kernel/ahci/driver.h
Normal file
28
src/kernel/ahci/driver.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
/// \file ahci.h
|
||||
/// AHCI driver and related definitions
|
||||
#include "kutil/vector.h"
|
||||
#include "ahci/hba.h"
|
||||
|
||||
class pci_device;
|
||||
|
||||
|
||||
/// Basic AHCI driver
|
||||
class ahci_driver
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
ahci_driver();
|
||||
|
||||
/// Register a device with the driver
|
||||
/// \arg device The PCI device to handle
|
||||
void register_device(pci_device *device);
|
||||
|
||||
/// Unregister a device from the driver
|
||||
/// \arg device The PCI device to remove
|
||||
void unregister_device(pci_device *device);
|
||||
|
||||
private:
|
||||
kutil::vector<ahci::hba> m_devices;
|
||||
};
|
||||
|
||||
49
src/kernel/ahci/fis.h
Normal file
49
src/kernel/ahci/fis.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
/// \file fis.h
|
||||
/// Definitions for Frame Information Structure types. (Not for pescatarians.)
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
namespace ahci {
|
||||
|
||||
enum class ata_cmd : uint8_t;
|
||||
|
||||
enum class fis_type : uint8_t
|
||||
{
|
||||
register_h2d = 0x27,
|
||||
register_d2h = 0x34,
|
||||
dma_activate = 0x39,
|
||||
dma_setup = 0x41,
|
||||
data = 0x46,
|
||||
bist = 0x58,
|
||||
pio_setup = 0x5f,
|
||||
device_bits = 0xa1
|
||||
};
|
||||
|
||||
|
||||
struct fis_register_h2d
|
||||
{
|
||||
fis_type type;
|
||||
uint8_t pm_port; // high bit (0x80) is set for the command register flag
|
||||
ata_cmd command;
|
||||
uint8_t features;
|
||||
|
||||
uint8_t lba0;
|
||||
uint8_t lba1;
|
||||
uint8_t lba2;
|
||||
uint8_t device;
|
||||
|
||||
uint8_t lba3;
|
||||
uint8_t lba4;
|
||||
uint8_t lba5;
|
||||
uint8_t features2;
|
||||
|
||||
uint8_t count0;
|
||||
uint8_t count1;
|
||||
uint8_t icc;
|
||||
uint8_t control;
|
||||
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
} // namespace ahci
|
||||
76
src/kernel/ahci/hba.cpp
Normal file
76
src/kernel/ahci/hba.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <stdint.h>
|
||||
#include "ahci/hba.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
#include "pci.h"
|
||||
|
||||
|
||||
IS_BITFIELD(ahci::hba_cap);
|
||||
IS_BITFIELD(ahci::hba_cap2);
|
||||
|
||||
namespace ahci {
|
||||
|
||||
|
||||
enum class hba_cap : uint32_t
|
||||
{
|
||||
ccc = 0x00000080, // Command completion coalescing
|
||||
ahci_only = 0x00040000, // ACHI-only mode
|
||||
clo = 0x01000000, // Command list override
|
||||
snotify = 0x40000000, // SNotification register
|
||||
ncq = 0x40000000, // Native command queuing
|
||||
addr64 = 0x80000000 // 64bit addressing
|
||||
};
|
||||
|
||||
|
||||
enum class hba_cap2 : uint32_t
|
||||
{
|
||||
handoff = 0x00000001 // BIOS OS hand-off
|
||||
};
|
||||
|
||||
|
||||
struct hba_data
|
||||
{
|
||||
hba_cap cap;
|
||||
uint32_t host_control;
|
||||
uint32_t int_status;
|
||||
uint32_t port_impl;
|
||||
uint32_t version;
|
||||
uint32_t ccc_control;
|
||||
uint32_t ccc_ports;
|
||||
uint32_t em_location;
|
||||
uint32_t em_control;
|
||||
hba_cap2 cap2;
|
||||
uint32_t handoff_control;
|
||||
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
hba::hba(pci_device *device)
|
||||
{
|
||||
page_manager *pm = page_manager::get();
|
||||
|
||||
uint32_t bar5 = device->get_bar(5);
|
||||
m_data = reinterpret_cast<hba_data *>(bar5 & ~0xfffull);
|
||||
pm->map_offset_pointer(reinterpret_cast<void **>(&m_data), 0x2000);
|
||||
|
||||
uint32_t icap = static_cast<uint32_t>(m_data->cap);
|
||||
|
||||
unsigned ports = (icap & 0xf) + 1;
|
||||
unsigned slots = ((icap >> 8) & 0x1f) + 1;
|
||||
|
||||
log::debug(logs::driver, " %d ports", ports);
|
||||
log::debug(logs::driver, " %d command slots", slots);
|
||||
|
||||
port_data *pd = reinterpret_cast<port_data *>(
|
||||
kutil::offset_pointer(m_data, 0x100));
|
||||
|
||||
m_ports.ensure_capacity(ports);
|
||||
for (unsigned i = 0; i < ports; ++i) {
|
||||
log::debug(logs::driver, " Registering port %d", i);
|
||||
bool impl = ((m_data->port_impl & (1 << i)) != 0);
|
||||
m_ports.emplace(kutil::offset_pointer(pd, 0x80 * i), impl);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ahci
|
||||
|
||||
31
src/kernel/ahci/hba.h
Normal file
31
src/kernel/ahci/hba.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
/// \file hba.h
|
||||
/// Definition for AHCI host bus adapters
|
||||
#include "kutil/vector.h"
|
||||
#include "ahci/port.h"
|
||||
|
||||
class pci_device;
|
||||
|
||||
|
||||
namespace ahci {
|
||||
|
||||
enum class hba_cap : uint32_t;
|
||||
enum class hba_cap2 : uint32_t;
|
||||
struct hba_data;
|
||||
|
||||
|
||||
/// An AHCI host bus adapter
|
||||
class hba
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg device The PCI device for this HBA
|
||||
hba(pci_device *device);
|
||||
|
||||
private:
|
||||
pci_device *m_device;
|
||||
hba_data *m_data;
|
||||
kutil::vector<port> m_ports;
|
||||
};
|
||||
|
||||
} // namespace ahci
|
||||
342
src/kernel/ahci/port.cpp
Normal file
342
src/kernel/ahci/port.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
#include <algorithm>
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "ahci/ata.h"
|
||||
#include "ahci/fis.h"
|
||||
#include "ahci/port.h"
|
||||
#include "io.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
|
||||
IS_BITFIELD(ahci::port_cmd);
|
||||
|
||||
namespace ahci {
|
||||
|
||||
const unsigned max_prd_count = 16;
|
||||
|
||||
|
||||
enum class cmd_list_flags : uint16_t
|
||||
{
|
||||
atapi = 0x0020,
|
||||
write = 0x0040,
|
||||
prefetch = 0x0080,
|
||||
reset = 0x0100,
|
||||
bist = 0x0200,
|
||||
clear_busy = 0x0400
|
||||
};
|
||||
|
||||
inline cmd_list_flags
|
||||
cmd_list_fis_size(uint8_t size)
|
||||
{
|
||||
return static_cast<cmd_list_flags>((size/4) & 0x1f);
|
||||
}
|
||||
|
||||
|
||||
struct cmd_list_entry
|
||||
{
|
||||
cmd_list_flags flags;
|
||||
uint16_t prd_table_length;
|
||||
uint32_t prd_byte_count;
|
||||
uint32_t cmd_table_base_low;
|
||||
uint32_t cmd_table_base_high;
|
||||
uint32_t reserved[4];
|
||||
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct prdt_entry
|
||||
{
|
||||
uint32_t data_base_low;
|
||||
uint32_t data_base_high;
|
||||
uint32_t reserved;
|
||||
uint32_t byte_count;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct cmd_table
|
||||
{
|
||||
uint8_t cmd_fis[64];
|
||||
uint8_t atapi_cmd[16];
|
||||
uint8_t reserved[48];
|
||||
prdt_entry entries[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
enum class port_cmd : uint32_t
|
||||
{
|
||||
start = 0x00000001,
|
||||
spinup = 0x00000002,
|
||||
poweron = 0x00000004,
|
||||
clo = 0x00000008,
|
||||
fis_recv = 0x00000010,
|
||||
fisr_running = 0x00004000,
|
||||
cmds_running = 0x00008000,
|
||||
|
||||
none = 0x00000000
|
||||
};
|
||||
|
||||
|
||||
struct port_data
|
||||
{
|
||||
uint32_t cmd_base_low;
|
||||
uint32_t cmd_base_high;
|
||||
uint32_t fis_base_low;
|
||||
uint32_t fis_base_high;
|
||||
|
||||
uint32_t interrupt_status;
|
||||
uint32_t interrupt_enable;
|
||||
|
||||
port_cmd command;
|
||||
|
||||
uint32_t reserved0;
|
||||
|
||||
uint8_t task_file_status;
|
||||
uint8_t task_file_error;
|
||||
uint16_t reserved1;
|
||||
|
||||
uint32_t signature;
|
||||
uint32_t serial_status;
|
||||
uint32_t serial_control;
|
||||
uint32_t serial_error;
|
||||
uint32_t serial_active;
|
||||
uint32_t cmd_issue;
|
||||
uint32_t serial_notify;
|
||||
uint32_t fis_switching;
|
||||
uint32_t dev_sleep;
|
||||
|
||||
uint8_t reserved2[40];
|
||||
uint8_t vendor[16];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
port::port(port_data *data, bool impl) :
|
||||
m_state(state::unimpl),
|
||||
m_data(data),
|
||||
m_fis(nullptr),
|
||||
m_cmd_list(nullptr),
|
||||
m_cmd_table(nullptr)
|
||||
{
|
||||
if (impl) {
|
||||
m_state = state::inactive;
|
||||
update();
|
||||
if (m_state == state::active)
|
||||
rebase();
|
||||
}
|
||||
}
|
||||
|
||||
port::~port()
|
||||
{
|
||||
if (m_cmd_list) {
|
||||
page_manager *pm = page_manager::get();
|
||||
pm->unmap_pages(m_cmd_list, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
port::update()
|
||||
{
|
||||
if (m_state == state::unimpl) return;
|
||||
|
||||
uint32_t detected = m_data->serial_status & 0x0f;
|
||||
uint32_t power = (m_data->serial_status >> 8) & 0x0f;
|
||||
|
||||
if (detected == 0x3 && power == 0x1)
|
||||
m_state = state::active;
|
||||
else
|
||||
m_state = state::inactive;
|
||||
}
|
||||
|
||||
bool
|
||||
port::busy()
|
||||
{
|
||||
return (m_data->task_file_status & 0x88) != 0;
|
||||
}
|
||||
|
||||
void
|
||||
port::start_commands()
|
||||
{
|
||||
while (bitfield_has(m_data->command, port_cmd::cmds_running))
|
||||
io_wait();
|
||||
|
||||
m_data->command |= port_cmd::fis_recv;
|
||||
m_data->command |= port_cmd::start;
|
||||
}
|
||||
|
||||
void
|
||||
port::stop_commands()
|
||||
{
|
||||
m_data->command &= ~port_cmd::start;
|
||||
|
||||
while (
|
||||
bitfield_has(m_data->command, port_cmd::cmds_running) ||
|
||||
bitfield_has(m_data->command, port_cmd::fisr_running))
|
||||
io_wait();
|
||||
|
||||
m_data->command &= ~port_cmd::fis_recv;
|
||||
}
|
||||
|
||||
bool
|
||||
port::read(uint64_t sector, size_t length)
|
||||
{
|
||||
m_data->interrupt_status = ~0u;
|
||||
|
||||
int slot = get_cmd_slot();
|
||||
if (slot < 0) {
|
||||
log::info(logs::driver, "AHCI could not get a free command slot.");
|
||||
return false;
|
||||
}
|
||||
|
||||
page_manager *pm = page_manager::get();
|
||||
|
||||
cmd_list_entry &ent = m_cmd_list[slot];
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
|
||||
kutil::memset(&cmdt, 0, sizeof(cmd_table) +
|
||||
max_prd_count * sizeof(prdt_entry));
|
||||
|
||||
ent.flags = cmd_list_fis_size(sizeof(fis_register_h2d));
|
||||
|
||||
void *buffers[32];
|
||||
|
||||
size_t remaining = length;
|
||||
for (int i = 0; i < max_prd_count; ++i) {
|
||||
size_t prd_len = std::min(remaining, 0x200000ul);
|
||||
remaining -= prd_len;
|
||||
|
||||
void *mem = pm->map_offset_pages(page_count(prd_len));
|
||||
buffers[i] = mem;
|
||||
addr_t phys = pm->offset_phys(mem);
|
||||
cmdt.entries[i].data_base_low = phys & 0xffffffff;
|
||||
cmdt.entries[i].data_base_high = phys >> 32;
|
||||
cmdt.entries[i].byte_count = prd_len - 1;
|
||||
if (remaining == 0 || i == max_prd_count - 1) {
|
||||
// If this is the last one, set the interrupt flag
|
||||
cmdt.entries[i].byte_count |= 0x80000000;
|
||||
ent.prd_table_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fis_register_h2d *fis = reinterpret_cast<fis_register_h2d *>(&cmdt.cmd_fis);
|
||||
fis->type = fis_type::register_h2d;
|
||||
fis->pm_port = 0x80; // set command register flag
|
||||
fis->command = ata_cmd::read_dma_ext;
|
||||
|
||||
fis->lba0 = (sector ) & 0xff;
|
||||
fis->lba1 = (sector >> 8) & 0xff;
|
||||
fis->lba2 = (sector >> 16) & 0xff;
|
||||
fis->lba3 = (sector >> 24) & 0xff;
|
||||
fis->lba4 = (sector >> 32) & 0xff;
|
||||
fis->lba5 = (sector >> 40) & 0xff;
|
||||
|
||||
size_t count = length >> 9; // count is in sectors
|
||||
fis->count0 = (count ) & 0xff;
|
||||
fis->count1 = (count >> 8) & 0xff;
|
||||
|
||||
const int max_tries = 10;
|
||||
int tries = 0;
|
||||
while (busy()) {
|
||||
if (++tries == max_tries) {
|
||||
log::warn(logs::driver, "AHCI port was busy too long");
|
||||
return false;
|
||||
}
|
||||
io_wait();
|
||||
}
|
||||
|
||||
if (tries == max_tries) {
|
||||
// TODO: clean up!!!
|
||||
return false;
|
||||
}
|
||||
|
||||
m_data->cmd_issue |= (1 << slot);
|
||||
|
||||
// TODO: interrupt-based
|
||||
while (true) {
|
||||
if ((m_data->cmd_issue & (1 << slot)) == 0) break;
|
||||
if (m_data->interrupt_status & 0x40000000) {
|
||||
log::error(logs::driver, "AHCI task file error");
|
||||
// TODO: clean up!
|
||||
return false;
|
||||
}
|
||||
io_wait();
|
||||
}
|
||||
|
||||
if (m_data->interrupt_status & 0x40000000) {
|
||||
log::error(logs::driver, "AHCI task file error");
|
||||
// TODO: clean up!
|
||||
return false;
|
||||
}
|
||||
|
||||
log::warn(logs::driver, "AHCI read success!? '%s'", buffers[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
port::rebase()
|
||||
{
|
||||
kassert(!m_cmd_list, "AHCI port called rebase() twice");
|
||||
|
||||
page_manager *pm = page_manager::get();
|
||||
size_t prd_size = sizeof(cmd_table) + (max_prd_count * sizeof(prdt_entry));
|
||||
|
||||
// 1 for FIS + command list, N for PRD
|
||||
size_t pages = 1 + page_count(prd_size * 32);
|
||||
|
||||
void *mem = pm->map_offset_pages(pages);
|
||||
addr_t phys = pm->offset_phys(mem);
|
||||
|
||||
log::debug(logs::driver, "Rebasing address for AHCI port to %lx [%d]", mem, pages);
|
||||
|
||||
stop_commands();
|
||||
|
||||
// Command list
|
||||
m_cmd_list = reinterpret_cast<cmd_list_entry *>(mem);
|
||||
m_data->cmd_base_low = phys & 0xffffffff;
|
||||
m_data->cmd_base_high = phys >> 32;
|
||||
kutil::memset(mem, 0, 1024);
|
||||
|
||||
mem = kutil::offset_pointer(mem, 32 * sizeof(cmd_list_entry));
|
||||
phys = pm->offset_phys(mem);
|
||||
|
||||
// FIS
|
||||
m_fis = mem;
|
||||
m_data->fis_base_low = phys & 0xffffffff;
|
||||
m_data->fis_base_high = phys >> 32;
|
||||
kutil::memset(mem, 0, 256);
|
||||
|
||||
mem = page_align(kutil::offset_pointer(mem, 256));
|
||||
phys = pm->offset_phys(mem);
|
||||
|
||||
// Command table
|
||||
m_cmd_table = reinterpret_cast<cmd_table *>(mem);
|
||||
size_t cmdt_len = sizeof(cmd_table) +
|
||||
max_prd_count * sizeof(prdt_entry);
|
||||
|
||||
kutil::memset(m_cmd_table, 0, cmdt_len * 32);
|
||||
|
||||
// set up each entry in the command list to point to the
|
||||
// corresponding command table
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
m_cmd_list[i].prd_table_length = max_prd_count;
|
||||
m_cmd_list[i].cmd_table_base_low = phys & 0xffffffff;
|
||||
m_cmd_list[i].cmd_table_base_high = phys >> 32;
|
||||
|
||||
mem = kutil::offset_pointer(mem, cmdt_len);
|
||||
phys = pm->offset_phys(mem);
|
||||
}
|
||||
|
||||
start_commands();
|
||||
}
|
||||
|
||||
int
|
||||
port::get_cmd_slot()
|
||||
{
|
||||
uint32_t used = (m_data->serial_active | m_data->cmd_issue);
|
||||
for (int i = 0; i < 32; ++i)
|
||||
if (used & (1 << i)) return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace ahci
|
||||
68
src/kernel/ahci/port.h
Normal file
68
src/kernel/ahci/port.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
/// \file port.h
|
||||
/// Definition for AHCI ports
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ahci {
|
||||
|
||||
struct cmd_list_entry;
|
||||
struct cmd_table;
|
||||
enum class port_cmd : uint32_t;
|
||||
struct port_data;
|
||||
|
||||
|
||||
/// A port on an AHCI HBA
|
||||
class port
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg data Pointer to the device's registers for this port
|
||||
/// \arg impl Whether this port is marked as implemented in the HBA
|
||||
port(port_data *data, bool impl);
|
||||
|
||||
/// Destructor
|
||||
~port();
|
||||
|
||||
enum class state { unimpl, inactive, active };
|
||||
|
||||
/// Get the current state of this device
|
||||
/// \returns An enum representing the state
|
||||
state get_state() const { return m_state; }
|
||||
|
||||
/// Update the state of this object from the register data
|
||||
void update();
|
||||
|
||||
/// Return whether the port is currently busy
|
||||
bool busy();
|
||||
|
||||
/// Start command processing from this port
|
||||
void start_commands();
|
||||
|
||||
/// Stop command processing from this port
|
||||
void stop_commands();
|
||||
|
||||
/// Read data from the drive.
|
||||
/// \arg sector Starting sector to read
|
||||
/// \arg length Number of bytes to read
|
||||
/// \returns True if the command succeeded
|
||||
bool read(uint64_t sector, size_t length);
|
||||
|
||||
private:
|
||||
/// Rebase the port command structures to a new location in system
|
||||
/// memory, to be allocated from the page manager.
|
||||
void rebase();
|
||||
|
||||
/// Get a free command slot
|
||||
/// \returns The index of the command slot, or -1 if none available
|
||||
int get_cmd_slot();
|
||||
|
||||
state m_state;
|
||||
port_data *m_data;
|
||||
|
||||
void *m_fis;
|
||||
cmd_list_entry *m_cmd_list;
|
||||
cmd_table *m_cmd_table;
|
||||
};
|
||||
|
||||
} // namespace ahci
|
||||
Reference in New Issue
Block a user