feat: graphics/net PnP, keyboard driver improvements
This commit is contained in:
@@ -132,14 +132,7 @@ namespace Drivers::Graphics::IntelGPU {
|
||||
uint8_t func = g_gpuInfo.pciFunction;
|
||||
|
||||
// Read BAR0
|
||||
uint32_t bar0Low = Pci::LegacyRead32(bus, dev, func, (uint8_t)PCI_REG_BAR0);
|
||||
uint64_t mmioPhys = bar0Low & 0xFFFFFFF0u; // Mask off type/prefetchable bits
|
||||
|
||||
// Check for 64-bit BAR (bit 2 of BAR type field)
|
||||
if (bar0Low & 0x04) {
|
||||
uint32_t bar0High = Pci::LegacyRead32(bus, dev, func, (uint8_t)(PCI_REG_BAR0 + 4));
|
||||
mmioPhys |= ((uint64_t)bar0High << 32);
|
||||
}
|
||||
uint64_t mmioPhys = Pci::ReadBar0(bus, dev, func);
|
||||
|
||||
g_gpuInfo.mmioPhys = mmioPhys;
|
||||
g_gpuInfo.mmioSize = 0x200000; // Map 2MB of MMIO space
|
||||
@@ -155,10 +148,8 @@ namespace Drivers::Graphics::IntelGPU {
|
||||
|
||||
KernelLogStream(OK, "IntelGPU") << "MMIO mapped at virtual " << base::hex << (uint64_t)g_mmioBase;
|
||||
|
||||
// Enable memory space and bus master in PCI command register
|
||||
uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, (uint8_t)PCI_REG_COMMAND);
|
||||
pciCmd |= PCI_CMD_MEM_SPACE | PCI_CMD_BUS_MASTER;
|
||||
Pci::LegacyWrite16(bus, dev, func, (uint8_t)PCI_REG_COMMAND, pciCmd);
|
||||
// Enable memory space and bus master
|
||||
Pci::EnableBusMaster(bus, dev, func);
|
||||
|
||||
KernelLogStream(OK, "IntelGPU") << "PCI memory space and bus mastering enabled";
|
||||
|
||||
@@ -473,6 +464,94 @@ namespace Drivers::Graphics::IntelGPU {
|
||||
// Public API
|
||||
// =========================================================================
|
||||
|
||||
// =========================================================================
|
||||
// Probe (called by PCI driver matching framework)
|
||||
// =========================================================================
|
||||
|
||||
bool Probe(const Pci::PciDevice& dev) {
|
||||
if (g_initialized) return false;
|
||||
|
||||
// Fill in PCI location
|
||||
g_gpuInfo.pciBus = dev.Bus;
|
||||
g_gpuInfo.pciDevice = dev.Device;
|
||||
g_gpuInfo.pciFunction = dev.Function;
|
||||
g_gpuInfo.deviceId = dev.DeviceId;
|
||||
|
||||
// Match device ID against supported table
|
||||
const DeviceInfo* matchedInfo = nullptr;
|
||||
for (int j = 0; j < SupportedDeviceCount; j++) {
|
||||
if (dev.DeviceId == SupportedDevices[j].deviceId) {
|
||||
matchedInfo = &SupportedDevices[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedInfo != nullptr) {
|
||||
g_gpuInfo.gen = matchedInfo->gen;
|
||||
g_gpuInfo.name = matchedInfo->name;
|
||||
KernelLogStream(OK, "IntelGPU") << "Found " << matchedInfo->name
|
||||
<< " (device " << base::hex << (uint64_t)dev.DeviceId << ")"
|
||||
<< " at PCI " << (uint64_t)dev.Bus << ":"
|
||||
<< (uint64_t)dev.Device << "." << (uint64_t)dev.Function;
|
||||
} else {
|
||||
g_gpuInfo.gen = 7;
|
||||
g_gpuInfo.name = "Intel GPU";
|
||||
KernelLogStream(WARNING, "IntelGPU") << "Unknown Intel display controller "
|
||||
<< "(device " << base::hex << (uint64_t)dev.DeviceId << ")"
|
||||
<< " at PCI " << (uint64_t)dev.Bus << ":"
|
||||
<< (uint64_t)dev.Device << "." << (uint64_t)dev.Function
|
||||
<< " - attempting generic initialization";
|
||||
}
|
||||
|
||||
g_gpuGen = g_gpuInfo.gen;
|
||||
|
||||
if (!MapMmio()) {
|
||||
KernelLogStream(ERROR, "IntelGPU") << "Failed to map MMIO region";
|
||||
return false;
|
||||
}
|
||||
|
||||
DisableVga();
|
||||
|
||||
if (!ReadDisplayState()) {
|
||||
KernelLogStream(ERROR, "IntelGPU") << "Failed to read display state";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitializeGtt()) {
|
||||
KernelLogStream(ERROR, "IntelGPU") << "Failed to initialize GTT";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetupFramebuffer()) {
|
||||
KernelLogStream(ERROR, "IntelGPU") << "Failed to set up framebuffer";
|
||||
return false;
|
||||
}
|
||||
|
||||
ProgramDisplayPlane();
|
||||
g_initialized = true;
|
||||
|
||||
uint64_t fwWidth = ::Graphics::Cursor::GetFramebufferWidth();
|
||||
uint64_t fwHeight = ::Graphics::Cursor::GetFramebufferHeight();
|
||||
uint64_t fwPitch = ::Graphics::Cursor::GetFramebufferPitch();
|
||||
|
||||
if (g_fbWidth != fwWidth || g_fbHeight != fwHeight || g_fbPitch != fwPitch) {
|
||||
KernelLogStream(WARNING, "IntelGPU") << "GPU dimensions differ from firmware!";
|
||||
KernelLogStream(WARNING, "IntelGPU") << " GPU: "
|
||||
<< base::dec << g_fbWidth << "x" << g_fbHeight << " pitch=" << g_fbPitch;
|
||||
KernelLogStream(WARNING, "IntelGPU") << " Firmware: "
|
||||
<< base::dec << fwWidth << "x" << fwHeight << " pitch=" << fwPitch;
|
||||
}
|
||||
|
||||
KernelLogStream(OK, "IntelGPU") << "Initialization complete: "
|
||||
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
||||
<< " @ " << base::hex << (uint64_t)g_fbBase;
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Initialize (standalone fallback path)
|
||||
// =========================================================================
|
||||
|
||||
void Initialize() {
|
||||
KernelLogStream(INFO, "IntelGPU") << "Scanning for Intel integrated graphics...";
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <Pci/Pci.hpp>
|
||||
|
||||
namespace Drivers::Graphics::IntelGPU {
|
||||
|
||||
@@ -18,14 +19,8 @@ namespace Drivers::Graphics::IntelGPU {
|
||||
static constexpr uint8_t ClassDisplay = 0x03;
|
||||
static constexpr uint8_t SubclassVGA = 0x00;
|
||||
|
||||
// PCI config space offsets
|
||||
static constexpr uint16_t PCI_REG_BAR0 = 0x10;
|
||||
// PCI config space offsets (GPU-specific, not in shared Pci::)
|
||||
static constexpr uint16_t PCI_REG_BAR2 = 0x18;
|
||||
static constexpr uint16_t PCI_REG_COMMAND = 0x04;
|
||||
static constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1);
|
||||
static constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2);
|
||||
|
||||
// Graphics control register (PCI config offset 0x50 on SNB+)
|
||||
static constexpr uint16_t PCI_REG_GMCH_CTL = 0x50;
|
||||
|
||||
// Supported Intel GPU device IDs (representative subset)
|
||||
@@ -400,6 +395,9 @@ namespace Drivers::Graphics::IntelGPU {
|
||||
// Initialize the Intel GPU driver (scans PCI, maps MMIO, sets up GTT + FB)
|
||||
void Initialize();
|
||||
|
||||
// Probe a specific PCI device (called by PCI driver matching framework)
|
||||
bool Probe(const Pci::PciDevice& dev);
|
||||
|
||||
// Check if an Intel GPU was found and initialized
|
||||
bool IsInitialized();
|
||||
|
||||
|
||||
+110
-6
@@ -5,17 +5,122 @@
|
||||
*/
|
||||
|
||||
#include "Init.hpp"
|
||||
#include <Pci/Pci.hpp>
|
||||
#include <Drivers/Graphics/IntelGPU.hpp>
|
||||
#include <Drivers/Net/E1000.hpp>
|
||||
#include <Drivers/Net/E1000E.hpp>
|
||||
#include <Drivers/USB/Xhci.hpp>
|
||||
#include <Graphics/Cursor.hpp>
|
||||
#include <Net/Net.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
|
||||
namespace Drivers {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Device ID whitelists
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static constexpr uint16_t g_e1000Ids[] = {
|
||||
0x100E,
|
||||
};
|
||||
|
||||
static constexpr uint16_t g_e1000eIds[] = {
|
||||
// I217
|
||||
0x153A, 0x153B,
|
||||
// I218
|
||||
0x155A, 0x1559, 0x15A0, 0x15A1, 0x15A2, 0x15A3,
|
||||
// I219-LM
|
||||
0x156F, 0x15B7, 0x15BB, 0x15BD, 0x15DF, 0x15E1, 0x15E3,
|
||||
0x15D7, 0x0D4C, 0x0D4E, 0x0D53, 0x0D55, 0x0DC5, 0x0DC7,
|
||||
0x1A1C, 0x1A1E,
|
||||
// I219-V
|
||||
0x1570, 0x15B8, 0x15BC, 0x15BE, 0x15E0, 0x15E2, 0x15D6,
|
||||
0x15D8, 0x0D4D, 0x0D4F, 0x0D54, 0x0DC6, 0x0DC8, 0x1A1D,
|
||||
0x1A1F,
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Probe wrappers (adapt namespace::Probe to PciProbeFunc signature)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static bool ProbeIntelGPU(const Pci::PciDevice& dev) {
|
||||
return Graphics::IntelGPU::Probe(dev);
|
||||
}
|
||||
|
||||
static bool ProbeXhci(const Pci::PciDevice& dev) {
|
||||
return USB::Xhci::Probe(dev);
|
||||
}
|
||||
|
||||
static bool ProbeE1000(const Pci::PciDevice& dev) {
|
||||
return Net::E1000::Probe(dev);
|
||||
}
|
||||
|
||||
static bool ProbeE1000E(const Pci::PciDevice& dev) {
|
||||
return Net::E1000E::Probe(dev);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Driver table
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static constexpr Pci::PciDriverDesc g_driverTable[] = {
|
||||
// Order 1: Intel GPU — Early phase, match vendor=0x8086 + class=0x03
|
||||
{
|
||||
"IntelGPU",
|
||||
0x8086, // VendorId
|
||||
0x03, // ClassCode (Display)
|
||||
0xFF, // SubClass (any)
|
||||
0xFF, // ProgIf (any)
|
||||
nullptr, // DeviceIds (internal gen lookup)
|
||||
0, // DeviceIdCount
|
||||
Pci::ProbePhase::Early,
|
||||
ProbeIntelGPU,
|
||||
},
|
||||
// Order 2: xHCI — Normal phase, match class=0x0C/0x03/0x30
|
||||
{
|
||||
"xHCI",
|
||||
0, // VendorId (any)
|
||||
0x0C, // ClassCode (Serial Bus)
|
||||
0x03, // SubClass (USB)
|
||||
0x30, // ProgIf (xHCI)
|
||||
nullptr,
|
||||
0,
|
||||
Pci::ProbePhase::Normal,
|
||||
ProbeXhci,
|
||||
},
|
||||
// Order 3: E1000 — Normal phase, vendor=0x8086 + deviceId={0x100E}
|
||||
{
|
||||
"E1000",
|
||||
0x8086,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
g_e1000Ids,
|
||||
sizeof(g_e1000Ids) / sizeof(g_e1000Ids[0]),
|
||||
Pci::ProbePhase::Normal,
|
||||
ProbeE1000,
|
||||
},
|
||||
// Order 4: E1000E — Normal phase, vendor=0x8086 + deviceIds list
|
||||
{
|
||||
"E1000E",
|
||||
0x8086,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
g_e1000eIds,
|
||||
sizeof(g_e1000eIds) / sizeof(g_e1000eIds[0]),
|
||||
Pci::ProbePhase::Normal,
|
||||
ProbeE1000E,
|
||||
},
|
||||
};
|
||||
|
||||
static constexpr uint16_t g_driverTableCount = sizeof(g_driverTable) / sizeof(g_driverTable[0]);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void ProbeEarly() {
|
||||
Pci::ProbeAll(g_driverTable, g_driverTableCount, Pci::ProbePhase::Early);
|
||||
}
|
||||
|
||||
void InitializeGraphics() {
|
||||
Graphics::IntelGPU::Initialize();
|
||||
if (Graphics::IntelGPU::IsInitialized()) {
|
||||
::Graphics::Cursor::SetFramebuffer(
|
||||
Graphics::IntelGPU::GetFramebufferBase(),
|
||||
@@ -26,12 +131,11 @@ namespace Drivers {
|
||||
}
|
||||
}
|
||||
|
||||
void ProbeNormal() {
|
||||
Pci::ProbeAll(g_driverTable, g_driverTableCount, Pci::ProbePhase::Normal);
|
||||
}
|
||||
|
||||
void InitializeNetwork() {
|
||||
Net::E1000::Initialize();
|
||||
if (!Net::E1000::IsInitialized()) {
|
||||
Kt::KernelLogStream(Kt::INFO, "Init") << "E1000 not found, trying E1000E...";
|
||||
Net::E1000E::Initialize();
|
||||
}
|
||||
::Net::Initialize();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,16 @@
|
||||
|
||||
namespace Drivers {
|
||||
|
||||
// Initialize Intel GPU driver and update the cursor framebuffer if found.
|
||||
// Probe PCI devices for Early-phase drivers (GPU).
|
||||
void ProbeEarly();
|
||||
|
||||
// Post-probe: wire up GPU framebuffer to cursor subsystem.
|
||||
void InitializeGraphics();
|
||||
|
||||
// Initialize network driver (E1000, falling back to E1000E) and the net stack.
|
||||
// Probe PCI devices for Normal-phase drivers (xHCI, E1000, E1000E).
|
||||
void ProbeNormal();
|
||||
|
||||
// Post-probe: initialize network stack.
|
||||
void InitializeNetwork();
|
||||
|
||||
}
|
||||
|
||||
@@ -23,15 +23,6 @@ namespace Drivers::Net::E1000 {
|
||||
static constexpr uint16_t VendorIntel = 0x8086;
|
||||
static constexpr uint16_t DeviceE1000 = 0x100E;
|
||||
|
||||
// PCI config space offsets
|
||||
static constexpr uint8_t PCI_REG_BAR0 = 0x10;
|
||||
static constexpr uint8_t PCI_REG_COMMAND = 0x04;
|
||||
static constexpr uint8_t PCI_REG_INTERRUPT = 0x3C;
|
||||
|
||||
// PCI command register bits
|
||||
static constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2);
|
||||
static constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1);
|
||||
|
||||
// Driver state
|
||||
static bool g_initialized = false;
|
||||
static volatile uint8_t* g_mmioBase = nullptr;
|
||||
@@ -296,6 +287,81 @@ namespace Drivers::Net::E1000 {
|
||||
// Public API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Probe (called by PCI driver matching framework)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
bool Probe(const Pci::PciDevice& dev) {
|
||||
if (g_initialized) return false;
|
||||
|
||||
KernelLogStream(OK, "E1000") << "Found E1000 at PCI "
|
||||
<< base::hex << (uint64_t)dev.Bus << ":"
|
||||
<< (uint64_t)dev.Device << "." << (uint64_t)dev.Function;
|
||||
|
||||
// Read BAR0 (MMIO base address)
|
||||
uint64_t mmioPhys = Pci::ReadBar0(dev.Bus, dev.Device, dev.Function);
|
||||
KernelLogStream(INFO, "E1000") << "BAR0 physical: " << base::hex << mmioPhys;
|
||||
|
||||
// Map the MMIO region (128KB = 32 pages)
|
||||
constexpr uint64_t MmioSize = 0x20000;
|
||||
for (uint64_t offset = 0; offset < MmioSize; offset += 0x1000) {
|
||||
Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset));
|
||||
}
|
||||
g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys);
|
||||
|
||||
// Enable bus mastering and memory space
|
||||
Pci::EnableBusMaster(dev.Bus, dev.Device, dev.Function);
|
||||
KernelLogStream(OK, "E1000") << "Bus mastering enabled";
|
||||
|
||||
// Read interrupt line from PCI config
|
||||
g_irqLine = Pci::LegacyRead8(dev.Bus, dev.Device, dev.Function, (uint8_t)Pci::PCI_REG_INTERRUPT);
|
||||
KernelLogStream(INFO, "E1000") << "IRQ line: " << base::dec << (uint64_t)g_irqLine;
|
||||
|
||||
// Reset the device
|
||||
uint32_t ctrl = ReadReg(REG_CTRL);
|
||||
WriteReg(REG_CTRL, ctrl | CTRL_RST);
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
if (!(ReadReg(REG_CTRL) & CTRL_RST)) break;
|
||||
}
|
||||
|
||||
WriteReg(REG_IMC, 0xFFFFFFFF);
|
||||
|
||||
ctrl = ReadReg(REG_CTRL);
|
||||
ctrl |= CTRL_SLU;
|
||||
ctrl &= ~(1u << 3);
|
||||
ctrl &= ~(1u << 31);
|
||||
ctrl &= ~(1u << 7);
|
||||
WriteReg(REG_CTRL, ctrl);
|
||||
|
||||
ReadMacAddress();
|
||||
KernelLogStream(OK, "E1000") << "MAC: " << base::hex
|
||||
<< (uint64_t)g_macAddress[0] << ":" << (uint64_t)g_macAddress[1] << ":"
|
||||
<< (uint64_t)g_macAddress[2] << ":" << (uint64_t)g_macAddress[3] << ":"
|
||||
<< (uint64_t)g_macAddress[4] << ":" << (uint64_t)g_macAddress[5];
|
||||
|
||||
for (uint32_t i = 0; i < 128; i++) {
|
||||
WriteReg(REG_MTA + (i * 4), 0);
|
||||
}
|
||||
|
||||
SetupRx();
|
||||
SetupTx();
|
||||
|
||||
Hal::RegisterIrqHandler(g_irqLine, HandleInterrupt);
|
||||
Hal::IoApic::UnmaskIrq(Hal::IoApic::GetGsiForIrq(g_irqLine));
|
||||
WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0);
|
||||
|
||||
g_initialized = true;
|
||||
|
||||
uint32_t status = ReadReg(REG_STATUS);
|
||||
bool linkUp = (status & (1 << 1)) != 0;
|
||||
KernelLogStream(OK, "E1000") << "Initialization complete, link: " << (linkUp ? "UP" : "DOWN");
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Initialize (standalone fallback path)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void Initialize() {
|
||||
KernelLogStream(INFO, "E1000") << "Scanning for Intel E1000 NIC...";
|
||||
|
||||
@@ -320,8 +386,7 @@ namespace Drivers::Net::E1000 {
|
||||
<< (uint64_t)e1000Dev->Device << "." << (uint64_t)e1000Dev->Function;
|
||||
|
||||
// Read BAR0 (MMIO base address)
|
||||
uint32_t bar0 = Pci::LegacyRead32(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, PCI_REG_BAR0);
|
||||
uint64_t mmioPhys = bar0 & 0xFFFFFFF0; // Mask low 4 bits (type/prefetchable flags)
|
||||
uint64_t mmioPhys = Pci::ReadBar0(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function);
|
||||
|
||||
KernelLogStream(INFO, "E1000") << "BAR0 physical: " << base::hex << mmioPhys;
|
||||
|
||||
@@ -333,15 +398,13 @@ namespace Drivers::Net::E1000 {
|
||||
|
||||
g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys);
|
||||
|
||||
// Enable bus mastering and memory space in PCI command register
|
||||
uint16_t pciCmd = Pci::LegacyRead16(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, PCI_REG_COMMAND);
|
||||
pciCmd |= PCI_CMD_BUS_MASTER | PCI_CMD_MEM_SPACE;
|
||||
Pci::LegacyWrite16(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, PCI_REG_COMMAND, pciCmd);
|
||||
// Enable bus mastering and memory space
|
||||
Pci::EnableBusMaster(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function);
|
||||
|
||||
KernelLogStream(OK, "E1000") << "Bus mastering enabled";
|
||||
|
||||
// Read interrupt line from PCI config
|
||||
g_irqLine = Pci::LegacyRead8(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, PCI_REG_INTERRUPT);
|
||||
g_irqLine = Pci::LegacyRead8(e1000Dev->Bus, e1000Dev->Device, e1000Dev->Function, (uint8_t)Pci::PCI_REG_INTERRUPT);
|
||||
KernelLogStream(INFO, "E1000") << "IRQ line: " << base::dec << (uint64_t)g_irqLine;
|
||||
|
||||
// Reset the device
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <Pci/Pci.hpp>
|
||||
|
||||
namespace Drivers::Net::E1000 {
|
||||
|
||||
@@ -102,6 +103,9 @@ namespace Drivers::Net::E1000 {
|
||||
// Initialize the E1000 driver (scans PCI for the device)
|
||||
void Initialize();
|
||||
|
||||
// Probe a specific PCI device (called by PCI driver matching framework)
|
||||
bool Probe(const Pci::PciDevice& dev);
|
||||
|
||||
// Send a raw Ethernet frame
|
||||
bool SendPacket(const uint8_t* data, uint16_t length);
|
||||
|
||||
|
||||
+127
-141
@@ -77,17 +77,6 @@ namespace Drivers::Net::E1000E {
|
||||
|
||||
static constexpr uint32_t g_deviceTableSize = sizeof(g_deviceTable) / sizeof(g_deviceTable[0]);
|
||||
|
||||
// PCI config space offsets
|
||||
static constexpr uint8_t PCI_REG_BAR0 = 0x10;
|
||||
static constexpr uint8_t PCI_REG_BAR1 = 0x14;
|
||||
static constexpr uint8_t PCI_REG_COMMAND = 0x04;
|
||||
static constexpr uint8_t PCI_REG_INTERRUPT = 0x3C;
|
||||
|
||||
// PCI command register bits
|
||||
static constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2);
|
||||
static constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1);
|
||||
static constexpr uint16_t PCI_CMD_INTX_DISABLE = (1 << 10);
|
||||
|
||||
// MSI configuration
|
||||
static constexpr uint8_t MSI_IRQ = 24; // IRQ slot 24 = vector 56 (first MSI slot)
|
||||
static constexpr uint32_t MSI_VECTOR = 56; // IRQ_VECTOR_BASE + MSI_IRQ
|
||||
@@ -451,9 +440,9 @@ namespace Drivers::Net::E1000E {
|
||||
Pci::LegacyWrite16(bus, dev, func, cap + 2, msgCtrl);
|
||||
|
||||
// Disable legacy INTx in PCI command register
|
||||
uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, PCI_REG_COMMAND);
|
||||
pciCmd |= PCI_CMD_INTX_DISABLE;
|
||||
Pci::LegacyWrite16(bus, dev, func, PCI_REG_COMMAND, pciCmd);
|
||||
uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, (uint8_t)Pci::PCI_REG_COMMAND);
|
||||
pciCmd |= Pci::PCI_CMD_INTX_DISABLE;
|
||||
Pci::LegacyWrite16(bus, dev, func, (uint8_t)Pci::PCI_REG_COMMAND, pciCmd);
|
||||
|
||||
// Register the interrupt handler for MSI vector
|
||||
Hal::RegisterIrqHandler(MSI_IRQ, HandleInterrupt);
|
||||
@@ -518,6 +507,129 @@ namespace Drivers::Net::E1000E {
|
||||
// Public API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Shared init logic (used by both Probe and Initialize)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static bool InitDevice(uint8_t bus, uint8_t device, uint8_t function, const char* deviceName) {
|
||||
KernelLogStream(OK, "E1000E") << "Found " << deviceName << " at PCI "
|
||||
<< base::hex << (uint64_t)bus << ":"
|
||||
<< (uint64_t)device << "." << (uint64_t)function;
|
||||
|
||||
// Read BAR0
|
||||
uint64_t mmioPhys = Pci::ReadBar0(bus, device, function);
|
||||
KernelLogStream(INFO, "E1000E") << "BAR0 physical: " << base::hex << mmioPhys;
|
||||
|
||||
// Map the MMIO region (128KB = 32 pages)
|
||||
constexpr uint64_t MmioSize = 0x20000;
|
||||
for (uint64_t offset = 0; offset < MmioSize; offset += 0x1000) {
|
||||
Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset));
|
||||
}
|
||||
|
||||
g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys);
|
||||
|
||||
// Enable bus mastering and memory space
|
||||
Pci::EnableBusMaster(bus, device, function);
|
||||
KernelLogStream(OK, "E1000E") << "Bus mastering enabled";
|
||||
|
||||
// Read interrupt line from PCI config (used for legacy IRQ fallback)
|
||||
g_irqLine = Pci::LegacyRead8(bus, device, function, (uint8_t)Pci::PCI_REG_INTERRUPT);
|
||||
KernelLogStream(INFO, "E1000E") << "PCI IRQ line: " << base::dec << (uint64_t)g_irqLine;
|
||||
|
||||
// --- ICH/PCH reset sequence ---
|
||||
|
||||
KernelLogStream(INFO, "E1000E") << "Disabling interrupts...";
|
||||
WriteReg(REG_IMC, 0xFFFFFFFF);
|
||||
ReadReg(REG_ICR);
|
||||
|
||||
KernelLogStream(INFO, "E1000E") << "Acquiring semaphore...";
|
||||
AcquireSwFwSync();
|
||||
|
||||
KernelLogStream(INFO, "E1000E") << "Resetting device...";
|
||||
uint32_t ctrl = ReadReg(REG_CTRL);
|
||||
WriteReg(REG_CTRL, ctrl | CTRL_RST);
|
||||
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
asm volatile("" ::: "memory");
|
||||
}
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
if (!(ReadReg(REG_CTRL) & CTRL_RST)) break;
|
||||
}
|
||||
|
||||
ReleaseSwFwSync();
|
||||
|
||||
WriteReg(REG_IMC, 0xFFFFFFFF);
|
||||
ReadReg(REG_ICR);
|
||||
|
||||
KernelLogStream(OK, "E1000E") << "Reset complete";
|
||||
|
||||
ctrl = ReadReg(REG_CTRL);
|
||||
ctrl |= CTRL_SLU;
|
||||
ctrl &= ~CTRL_FRCSPD;
|
||||
ctrl &= ~CTRL_FRCDPLX;
|
||||
ctrl &= ~(1u << 3);
|
||||
ctrl &= ~(1u << 31);
|
||||
ctrl &= ~(1u << 7);
|
||||
WriteReg(REG_CTRL, ctrl);
|
||||
|
||||
InitPhy();
|
||||
ReadMacAddress();
|
||||
|
||||
KernelLogStream(OK, "E1000E") << "MAC: " << base::hex
|
||||
<< (uint64_t)g_macAddress[0] << ":" << (uint64_t)g_macAddress[1] << ":"
|
||||
<< (uint64_t)g_macAddress[2] << ":" << (uint64_t)g_macAddress[3] << ":"
|
||||
<< (uint64_t)g_macAddress[4] << ":" << (uint64_t)g_macAddress[5];
|
||||
|
||||
for (uint32_t i = 0; i < 128; i++) {
|
||||
WriteReg(REG_MTA + (i * 4), 0);
|
||||
}
|
||||
|
||||
SetupRx();
|
||||
SetupTx();
|
||||
|
||||
if (SetupMsi(bus, device, function)) {
|
||||
WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0);
|
||||
} else if (g_irqLine != 0xFF) {
|
||||
KernelLogStream(INFO, "E1000E") << "Falling back to legacy IRQ " << base::dec << (uint64_t)g_irqLine;
|
||||
Hal::RegisterIrqHandler(g_irqLine, HandleInterrupt);
|
||||
Hal::IoApic::UnmaskIrq(Hal::IoApic::GetGsiForIrq(g_irqLine));
|
||||
WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0);
|
||||
} else {
|
||||
KernelLogStream(WARNING, "E1000E") << "No MSI or legacy IRQ available, using polling mode";
|
||||
g_pollingMode = true;
|
||||
}
|
||||
|
||||
g_initialized = true;
|
||||
|
||||
uint32_t status = ReadReg(REG_STATUS);
|
||||
bool linkUp = (status & (1 << 1)) != 0;
|
||||
KernelLogStream(OK, "E1000E") << "Initialization complete, link: " << (linkUp ? "UP" : "DOWN");
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Probe (called by PCI driver matching framework)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
bool Probe(const Pci::PciDevice& dev) {
|
||||
if (g_initialized) return false;
|
||||
|
||||
// Look up device name from table
|
||||
const char* name = "e1000e";
|
||||
for (uint32_t j = 0; j < g_deviceTableSize; j++) {
|
||||
if (dev.DeviceId == g_deviceTable[j].DeviceId) {
|
||||
name = g_deviceTable[j].Name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return InitDevice(dev.Bus, dev.Device, dev.Function, name);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Initialize (standalone fallback path)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void Initialize() {
|
||||
KernelLogStream(INFO, "E1000E") << "Scanning for Intel e1000e NIC...";
|
||||
|
||||
@@ -546,133 +658,7 @@ namespace Drivers::Net::E1000E {
|
||||
return;
|
||||
}
|
||||
|
||||
KernelLogStream(OK, "E1000E") << "Found " << foundName << " at PCI "
|
||||
<< base::hex << (uint64_t)foundDev->Bus << ":"
|
||||
<< (uint64_t)foundDev->Device << "." << (uint64_t)foundDev->Function;
|
||||
|
||||
// Read BAR0 (MMIO base address), check for 64-bit BAR
|
||||
uint32_t bar0 = Pci::LegacyRead32(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_BAR0);
|
||||
uint64_t mmioPhys = bar0 & 0xFFFFFFF0;
|
||||
|
||||
// Check if 64-bit BAR (type field bits 2:1 == 0b10)
|
||||
if ((bar0 & 0x06) == 0x04) {
|
||||
uint32_t bar1 = Pci::LegacyRead32(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_BAR1);
|
||||
mmioPhys |= ((uint64_t)bar1 << 32);
|
||||
}
|
||||
|
||||
KernelLogStream(INFO, "E1000E") << "BAR0 physical: " << base::hex << mmioPhys;
|
||||
|
||||
// Map the MMIO region (128KB = 32 pages)
|
||||
constexpr uint64_t MmioSize = 0x20000;
|
||||
for (uint64_t offset = 0; offset < MmioSize; offset += 0x1000) {
|
||||
Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset));
|
||||
}
|
||||
|
||||
g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys);
|
||||
|
||||
// Enable bus mastering and memory space in PCI command register
|
||||
uint16_t pciCmd = Pci::LegacyRead16(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_COMMAND);
|
||||
pciCmd |= PCI_CMD_BUS_MASTER | PCI_CMD_MEM_SPACE;
|
||||
Pci::LegacyWrite16(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_COMMAND, pciCmd);
|
||||
|
||||
KernelLogStream(OK, "E1000E") << "Bus mastering enabled";
|
||||
|
||||
// Read interrupt line from PCI config (used for legacy IRQ fallback)
|
||||
g_irqLine = Pci::LegacyRead8(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_INTERRUPT);
|
||||
KernelLogStream(INFO, "E1000E") << "PCI IRQ line: " << base::dec << (uint64_t)g_irqLine;
|
||||
|
||||
// --- ICH/PCH reset sequence ---
|
||||
|
||||
// 1. Disable interrupts, flush ICR
|
||||
KernelLogStream(INFO, "E1000E") << "Disabling interrupts...";
|
||||
WriteReg(REG_IMC, 0xFFFFFFFF);
|
||||
ReadReg(REG_ICR);
|
||||
|
||||
// 2. Acquire SW/FW semaphore (non-fatal if it fails)
|
||||
KernelLogStream(INFO, "E1000E") << "Acquiring semaphore...";
|
||||
AcquireSwFwSync();
|
||||
|
||||
// 3. Reset the device
|
||||
KernelLogStream(INFO, "E1000E") << "Resetting device...";
|
||||
uint32_t ctrl = ReadReg(REG_CTRL);
|
||||
WriteReg(REG_CTRL, ctrl | CTRL_RST);
|
||||
|
||||
// Post-reset settling delay (give hardware time before polling)
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
asm volatile("" ::: "memory");
|
||||
}
|
||||
|
||||
// Poll for reset completion
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
if (!(ReadReg(REG_CTRL) & CTRL_RST)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Release semaphore
|
||||
ReleaseSwFwSync();
|
||||
|
||||
// 5. Disable interrupts again after reset
|
||||
WriteReg(REG_IMC, 0xFFFFFFFF);
|
||||
ReadReg(REG_ICR);
|
||||
|
||||
KernelLogStream(OK, "E1000E") << "Reset complete";
|
||||
|
||||
// Set link up: SLU on, but let auto-negotiation decide speed/duplex
|
||||
ctrl = ReadReg(REG_CTRL);
|
||||
ctrl |= CTRL_SLU;
|
||||
ctrl &= ~CTRL_FRCSPD;
|
||||
ctrl &= ~CTRL_FRCDPLX;
|
||||
ctrl &= ~(1u << 3); // Clear LRST
|
||||
ctrl &= ~(1u << 31); // Clear PHY_RST
|
||||
ctrl &= ~(1u << 7); // Clear ILOS
|
||||
WriteReg(REG_CTRL, ctrl);
|
||||
|
||||
// Initialize PHY
|
||||
InitPhy();
|
||||
|
||||
// Read MAC address
|
||||
ReadMacAddress();
|
||||
|
||||
KernelLogStream(OK, "E1000E") << "MAC: "
|
||||
<< base::hex
|
||||
<< (uint64_t)g_macAddress[0] << ":"
|
||||
<< (uint64_t)g_macAddress[1] << ":"
|
||||
<< (uint64_t)g_macAddress[2] << ":"
|
||||
<< (uint64_t)g_macAddress[3] << ":"
|
||||
<< (uint64_t)g_macAddress[4] << ":"
|
||||
<< (uint64_t)g_macAddress[5];
|
||||
|
||||
// Zero out the Multicast Table Array (128 entries)
|
||||
for (uint32_t i = 0; i < 128; i++) {
|
||||
WriteReg(REG_MTA + (i * 4), 0);
|
||||
}
|
||||
|
||||
// Set up RX and TX descriptor rings
|
||||
SetupRx();
|
||||
SetupTx();
|
||||
|
||||
// Three-tier interrupt strategy: MSI → legacy IRQ → polling
|
||||
if (SetupMsi(foundDev->Bus, foundDev->Device, foundDev->Function)) {
|
||||
// MSI configured — enable NIC interrupt causes
|
||||
WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0);
|
||||
} else if (g_irqLine != 0xFF) {
|
||||
// Legacy IRQ fallback
|
||||
KernelLogStream(INFO, "E1000E") << "Falling back to legacy IRQ " << base::dec << (uint64_t)g_irqLine;
|
||||
Hal::RegisterIrqHandler(g_irqLine, HandleInterrupt);
|
||||
Hal::IoApic::UnmaskIrq(Hal::IoApic::GetGsiForIrq(g_irqLine));
|
||||
WriteReg(REG_IMS, ICR_RXT0 | ICR_TXDW | ICR_TXQE | ICR_LSC | ICR_RXDMT0);
|
||||
} else {
|
||||
// Polling as last resort
|
||||
KernelLogStream(WARNING, "E1000E") << "No MSI or legacy IRQ available, using polling mode";
|
||||
g_pollingMode = true;
|
||||
}
|
||||
|
||||
g_initialized = true;
|
||||
|
||||
uint32_t status = ReadReg(REG_STATUS);
|
||||
bool linkUp = (status & (1 << 1)) != 0;
|
||||
KernelLogStream(OK, "E1000E") << "Initialization complete, link: " << (linkUp ? "UP" : "DOWN");
|
||||
InitDevice(foundDev->Bus, foundDev->Device, foundDev->Function, foundName);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <Pci/Pci.hpp>
|
||||
|
||||
namespace Drivers::Net::E1000E {
|
||||
|
||||
@@ -134,6 +135,9 @@ namespace Drivers::Net::E1000E {
|
||||
// Initialize the E1000E driver (scans PCI for the device)
|
||||
void Initialize();
|
||||
|
||||
// Probe a specific PCI device (called by PCI driver matching framework)
|
||||
bool Probe(const Pci::PciDevice& dev);
|
||||
|
||||
// Send a raw Ethernet frame
|
||||
bool SendPacket(const uint8_t* data, uint16_t length);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "HidKeyboard.hpp"
|
||||
#include <Drivers/PS2/Keyboard.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <Timekeeping/ApicTimer.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
@@ -171,13 +172,13 @@ namespace Drivers::USB::HidKeyboard {
|
||||
static uint8_t g_PrevKeys[6] = {};
|
||||
static uint8_t g_PrevModifiers = 0;
|
||||
|
||||
// Typematic repeat state
|
||||
static uint8_t g_RepeatKey = 0; // HID usage ID currently repeating
|
||||
static uint16_t g_HoldCount = 0; // Reports since key was first held
|
||||
// Typematic repeat state (timestamp-based, independent of report rate)
|
||||
static uint8_t g_RepeatKey = 0; // HID usage ID currently repeating
|
||||
static uint64_t g_RepeatStartMs = 0; // Timestamp when key was first held
|
||||
static uint64_t g_LastRepeatMs = 0; // Timestamp of last repeat event
|
||||
|
||||
// Tuned for ~16ms report interval (SET_IDLE(4))
|
||||
constexpr uint16_t TYPEMATIC_DELAY = 31; // ~500ms before repeat starts
|
||||
constexpr uint16_t TYPEMATIC_PERIOD = 2; // ~32ms between repeats (~31 chars/sec)
|
||||
constexpr uint64_t TYPEMATIC_DELAY_MS = 500; // ms before repeat starts
|
||||
constexpr uint64_t TYPEMATIC_PERIOD_MS = 32; // ms between repeats (~31 chars/sec)
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
@@ -252,7 +253,8 @@ namespace Drivers::USB::HidKeyboard {
|
||||
for (int i = 0; i < 6; i++) g_PrevKeys[i] = 0;
|
||||
g_PrevModifiers = 0;
|
||||
g_RepeatKey = 0;
|
||||
g_HoldCount = 0;
|
||||
g_RepeatStartMs = 0;
|
||||
g_LastRepeatMs = 0;
|
||||
|
||||
KernelLogStream(OK, "USB/KB") << "Registered HID keyboard on slot " << (uint64_t)slotId;
|
||||
}
|
||||
@@ -264,6 +266,16 @@ namespace Drivers::USB::HidKeyboard {
|
||||
// data[1] is reserved
|
||||
const uint8_t* keys = &data[2];
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Detect ErrorRollOver (phantom keys) — ignore entire report
|
||||
// When the keyboard can't reliably report all pressed keys, it
|
||||
// fills key slots with 0x01. Updating state from this would
|
||||
// falsely release all held keys, so we skip the report entirely.
|
||||
// -----------------------------------------------------------------
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (keys[i] == 0x01) return;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Handle modifier changes
|
||||
// -----------------------------------------------------------------
|
||||
@@ -291,6 +303,7 @@ namespace Drivers::USB::HidKeyboard {
|
||||
// -----------------------------------------------------------------
|
||||
// Detect newly pressed keys (in current but not in previous)
|
||||
// -----------------------------------------------------------------
|
||||
uint64_t now = Timekeeping::GetMilliseconds();
|
||||
bool newKeyPressed = false;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
uint8_t key = keys[i];
|
||||
@@ -301,7 +314,8 @@ namespace Drivers::USB::HidKeyboard {
|
||||
if (scancode != 0) {
|
||||
InjectKey(scancode, true, modifiers, IsNonCharKey(key));
|
||||
g_RepeatKey = key;
|
||||
g_HoldCount = 0;
|
||||
g_RepeatStartMs = now;
|
||||
g_LastRepeatMs = 0;
|
||||
newKeyPressed = true;
|
||||
}
|
||||
}
|
||||
@@ -321,21 +335,6 @@ namespace Drivers::USB::HidKeyboard {
|
||||
}
|
||||
if (key == g_RepeatKey) {
|
||||
g_RepeatKey = 0;
|
||||
g_HoldCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Typematic repeat for held keys
|
||||
// -----------------------------------------------------------------
|
||||
if (!newKeyPressed && g_RepeatKey != 0 && KeyInArray(g_RepeatKey, keys)) {
|
||||
g_HoldCount++;
|
||||
if (g_HoldCount >= TYPEMATIC_DELAY &&
|
||||
(g_HoldCount - TYPEMATIC_DELAY) % TYPEMATIC_PERIOD == 0) {
|
||||
uint8_t scancode = g_HidToScancode[g_RepeatKey];
|
||||
if (scancode != 0) {
|
||||
InjectKey(scancode, true, modifiers, IsNonCharKey(g_RepeatKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,4 +346,27 @@ namespace Drivers::USB::HidKeyboard {
|
||||
g_PrevModifiers = modifiers;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tick — called from periodic timer (1ms) to drive typematic repeat
|
||||
// With SET_IDLE(0) the keyboard only reports on changes, so we need
|
||||
// an external clock to fire repeats while a key is held unchanged.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void Tick() {
|
||||
if (g_RepeatKey == 0) return;
|
||||
|
||||
uint64_t now = Timekeeping::GetMilliseconds();
|
||||
uint64_t held = now - g_RepeatStartMs;
|
||||
if (held < TYPEMATIC_DELAY_MS) return;
|
||||
|
||||
if (g_LastRepeatMs == 0 ||
|
||||
(now - g_LastRepeatMs) >= TYPEMATIC_PERIOD_MS) {
|
||||
uint8_t scancode = g_HidToScancode[g_RepeatKey];
|
||||
if (scancode != 0) {
|
||||
InjectKey(scancode, true, g_PrevModifiers, IsNonCharKey(g_RepeatKey));
|
||||
}
|
||||
g_LastRepeatMs = now;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,4 +15,7 @@ namespace Drivers::USB::HidKeyboard {
|
||||
// Process an 8-byte boot protocol keyboard report
|
||||
void ProcessReport(const uint8_t* data, uint16_t length);
|
||||
|
||||
// Timer tick for typematic repeat (call from periodic timer, e.g. 1ms)
|
||||
void Tick();
|
||||
|
||||
};
|
||||
|
||||
@@ -501,14 +501,14 @@ namespace Drivers::USB::UsbDevice {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 11: SET_IDLE(4) -- 16ms idle rate for software typematic
|
||||
// Step 11: SET_IDLE(0) -- only report on changes (no idle reports)
|
||||
// -----------------------------------------------------------------
|
||||
if (foundEp && dev->InterfaceProtocol == PROTOCOL_KEYBOARD) {
|
||||
// wValue upper byte = duration in 4ms units, lower byte = report ID
|
||||
// wValue upper byte = duration (0 = indefinite), lower byte = report ID
|
||||
cc = Xhci::ControlTransfer(slotId, REQTYPE_CLASS_IFACE, REQ_SET_IDLE,
|
||||
(4 << 8), 0, 0, nullptr, false);
|
||||
(0 << 8), 0, 0, nullptr, false);
|
||||
if (cc != Xhci::CC_SUCCESS) {
|
||||
KernelLogStream(WARNING, "USB") << "SET_IDLE(4) failed, cc=" << (uint64_t)cc;
|
||||
KernelLogStream(WARNING, "USB") << "SET_IDLE(0) failed, cc=" << (uint64_t)cc;
|
||||
// Non-fatal: not all devices support SET_IDLE
|
||||
}
|
||||
}
|
||||
|
||||
+226
-19
@@ -216,9 +216,9 @@ namespace Drivers::USB::Xhci {
|
||||
Pci::LegacyWrite16(bus, dev, func, cap + 2, msgCtrl);
|
||||
|
||||
// Disable legacy INTx in PCI command register
|
||||
uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, PCI_REG_COMMAND);
|
||||
pciCmd |= PCI_CMD_INTX_DISABLE;
|
||||
Pci::LegacyWrite16(bus, dev, func, PCI_REG_COMMAND, pciCmd);
|
||||
uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, (uint8_t)Pci::PCI_REG_COMMAND);
|
||||
pciCmd |= Pci::PCI_CMD_INTX_DISABLE;
|
||||
Pci::LegacyWrite16(bus, dev, func, (uint8_t)Pci::PCI_REG_COMMAND, pciCmd);
|
||||
|
||||
// Register the interrupt handler for MSI vector
|
||||
Hal::RegisterIrqHandler(MSI_IRQ, HandleInterrupt);
|
||||
@@ -299,15 +299,17 @@ namespace Drivers::USB::Xhci {
|
||||
HidMouse::ProcessReport(g_interruptDataBuf[slotId], len);
|
||||
}
|
||||
}
|
||||
|
||||
// Only re-queue on success — re-queuing on error would
|
||||
// create an infinite loop of failed transfers.
|
||||
QueueInterruptTransfer(slotId);
|
||||
} else {
|
||||
KernelLogStream(WARNING, "xHCI") << "Transfer error on slot "
|
||||
<< base::dec << (uint64_t)slotId << " ep " << (uint64_t)epDci
|
||||
<< " cc=" << (uint64_t)completionCode;
|
||||
}
|
||||
|
||||
// Always re-queue the next interrupt transfer so the
|
||||
// device can continue delivering reports. Transient
|
||||
// errors (stall, babble, etc.) must not permanently
|
||||
// kill the transfer chain.
|
||||
QueueInterruptTransfer(slotId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -645,7 +647,221 @@ namespace Drivers::USB::Xhci {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Initialize
|
||||
// Probe (called by PCI driver matching framework)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
bool Probe(const Pci::PciDevice& dev) {
|
||||
if (g_initialized) return false;
|
||||
|
||||
KernelLogStream(OK, "xHCI") << "Found controller at PCI "
|
||||
<< base::hex << (uint64_t)dev.Bus << ":"
|
||||
<< (uint64_t)dev.Device << "." << (uint64_t)dev.Function;
|
||||
|
||||
// Read BAR0 and map MMIO region
|
||||
uint64_t mmioPhys = Pci::ReadBar0(dev.Bus, dev.Device, dev.Function);
|
||||
|
||||
KernelLogStream(INFO, "xHCI") << "BAR0 physical: " << base::hex << mmioPhys;
|
||||
|
||||
constexpr uint64_t MmioSize = 0x10000;
|
||||
for (uint64_t offset = 0; offset < MmioSize; offset += 0x1000) {
|
||||
Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset));
|
||||
}
|
||||
|
||||
g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys);
|
||||
|
||||
Pci::EnableBusMaster(dev.Bus, dev.Device, dev.Function);
|
||||
KernelLogStream(OK, "xHCI") << "Bus mastering enabled";
|
||||
|
||||
// Parse capability registers
|
||||
g_capLength = *(volatile uint8_t*)(g_mmioBase + CAP_CAPLENGTH);
|
||||
|
||||
uint32_t hciVersion = *(volatile uint16_t*)(g_mmioBase + CAP_HCIVERSION);
|
||||
KernelLogStream(INFO, "xHCI") << "Version: " << base::hex << (uint64_t)hciVersion
|
||||
<< ", CapLength: " << (uint64_t)g_capLength;
|
||||
|
||||
uint32_t hcsParams1 = ReadCap(CAP_HCSPARAMS1);
|
||||
g_maxSlots = hcsParams1 & 0xFF;
|
||||
g_maxPorts = (hcsParams1 >> 24) & 0xFF;
|
||||
|
||||
uint32_t hcsParams2 = ReadCap(CAP_HCSPARAMS2);
|
||||
uint32_t scratchpadBufsHi = (hcsParams2 >> 21) & 0x1F;
|
||||
uint32_t scratchpadBufsLo = (hcsParams2 >> 27) & 0x1F;
|
||||
uint32_t maxScratchpadBufs = (scratchpadBufsHi << 5) | scratchpadBufsLo;
|
||||
|
||||
uint32_t dbOff = ReadCap(CAP_DBOFF) & ~0x3u;
|
||||
uint32_t rtsOff = ReadCap(CAP_RTSOFF) & ~0x1Fu;
|
||||
|
||||
g_opBase = g_mmioBase + g_capLength;
|
||||
g_rtBase = g_mmioBase + rtsOff;
|
||||
g_dbBase = g_mmioBase + dbOff;
|
||||
|
||||
KernelLogStream(INFO, "xHCI") << "MaxSlots: " << base::dec << (uint64_t)g_maxSlots
|
||||
<< ", MaxPorts: " << (uint64_t)g_maxPorts
|
||||
<< ", ScratchpadBufs: " << (uint64_t)maxScratchpadBufs;
|
||||
|
||||
if (g_maxSlots > MAX_SLOTS) g_maxSlots = MAX_SLOTS;
|
||||
if (g_maxPorts > MAX_PORTS) g_maxPorts = MAX_PORTS;
|
||||
|
||||
// Halt controller
|
||||
uint32_t usbcmd = ReadOp(OP_USBCMD);
|
||||
usbcmd &= ~USBCMD_RS;
|
||||
WriteOp(OP_USBCMD, usbcmd);
|
||||
|
||||
for (uint32_t i = 0; i < 100000; i++) {
|
||||
if (ReadOp(OP_USBSTS) & USBSTS_HCH) break;
|
||||
for (int j = 0; j < 10; j++) asm volatile("" ::: "memory");
|
||||
}
|
||||
|
||||
if (!(ReadOp(OP_USBSTS) & USBSTS_HCH)) {
|
||||
KernelLogStream(WARNING, "xHCI") << "Controller failed to halt";
|
||||
}
|
||||
KernelLogStream(OK, "xHCI") << "Controller halted";
|
||||
|
||||
// Reset controller
|
||||
WriteOp(OP_USBCMD, USBCMD_HCRST);
|
||||
for (uint32_t i = 0; i < 100000; i++) {
|
||||
if (!(ReadOp(OP_USBCMD) & USBCMD_HCRST)) break;
|
||||
for (int j = 0; j < 10; j++) asm volatile("" ::: "memory");
|
||||
}
|
||||
for (uint32_t i = 0; i < 100000; i++) {
|
||||
if (!(ReadOp(OP_USBSTS) & USBSTS_CNR)) break;
|
||||
for (int j = 0; j < 10; j++) asm volatile("" ::: "memory");
|
||||
}
|
||||
if (ReadOp(OP_USBSTS) & USBSTS_CNR) {
|
||||
KernelLogStream(WARNING, "xHCI") << "Controller not ready after reset";
|
||||
}
|
||||
KernelLogStream(OK, "xHCI") << "Controller reset complete";
|
||||
|
||||
// Program CONFIG
|
||||
WriteOp(OP_CONFIG, g_maxSlots);
|
||||
|
||||
// Allocate DCBAA
|
||||
g_dcbaa = (uint64_t*)AllocateDmaBuffer(g_dcbaaPhys);
|
||||
WriteOp(OP_DCBAAP, (uint32_t)(g_dcbaaPhys & 0xFFFFFFFF));
|
||||
WriteOp(OP_DCBAAP + 4, (uint32_t)(g_dcbaaPhys >> 32));
|
||||
KernelLogStream(OK, "xHCI") << "DCBAA at phys " << base::hex << g_dcbaaPhys;
|
||||
|
||||
// Scratchpad buffers
|
||||
if (maxScratchpadBufs > 0) {
|
||||
uint64_t spArrayPhys;
|
||||
g_scratchpadBufs = (uint64_t*)AllocateDmaBuffer(spArrayPhys);
|
||||
for (uint32_t i = 0; i < maxScratchpadBufs; i++) {
|
||||
uint64_t bufPhys;
|
||||
AllocateDmaBuffer(bufPhys);
|
||||
g_scratchpadBufs[i] = bufPhys;
|
||||
}
|
||||
g_dcbaa[0] = spArrayPhys;
|
||||
KernelLogStream(OK, "xHCI") << "Allocated " << base::dec << (uint64_t)maxScratchpadBufs << " scratchpad buffers";
|
||||
}
|
||||
|
||||
// Command ring
|
||||
g_cmdRing = (TRB*)AllocateDmaBuffer(g_cmdRingPhys);
|
||||
TRB& linkTrb = g_cmdRing[CMD_RING_SIZE - 1];
|
||||
linkTrb.Parameter0 = (uint32_t)(g_cmdRingPhys & 0xFFFFFFFF);
|
||||
linkTrb.Parameter1 = (uint32_t)(g_cmdRingPhys >> 32);
|
||||
linkTrb.Status = 0;
|
||||
linkTrb.Control = (TRB_LINK << TRB_TYPE_SHIFT) | TRB_ENT;
|
||||
uint64_t crcr = g_cmdRingPhys | TRB_CYCLE_BIT;
|
||||
WriteOp(OP_CRCR, (uint32_t)(crcr & 0xFFFFFFFF));
|
||||
WriteOp(OP_CRCR + 4, (uint32_t)(crcr >> 32));
|
||||
g_cmdRingCCS = true;
|
||||
g_cmdRingEnqueue = 0;
|
||||
KernelLogStream(OK, "xHCI") << "Command ring at phys " << base::hex << g_cmdRingPhys;
|
||||
|
||||
// Event ring + ERST
|
||||
g_evtRing = (TRB*)AllocateDmaBuffer(g_evtRingPhys);
|
||||
g_erst = (ERSTEntry*)AllocateDmaBuffer(g_erstPhys);
|
||||
g_erst[0].RingSegmentBase = g_evtRingPhys;
|
||||
g_erst[0].RingSegmentSize = EVT_RING_SIZE;
|
||||
g_erst[0].Reserved = 0;
|
||||
WriteRt(IR0_ERSTSZ, 1);
|
||||
WriteRt(IR0_ERDP, (uint32_t)(g_evtRingPhys & 0xFFFFFFFF));
|
||||
WriteRt(IR0_ERDP + 4, (uint32_t)(g_evtRingPhys >> 32));
|
||||
WriteRt(IR0_ERSTBA, (uint32_t)(g_erstPhys & 0xFFFFFFFF));
|
||||
WriteRt(IR0_ERSTBA + 4, (uint32_t)(g_erstPhys >> 32));
|
||||
g_evtRingCCS = true;
|
||||
g_evtRingDequeue = 0;
|
||||
KernelLogStream(OK, "xHCI") << "Event ring at phys " << base::hex << g_evtRingPhys;
|
||||
|
||||
// MSI setup
|
||||
if (!SetupMsi(dev.Bus, dev.Device, dev.Function)) {
|
||||
KernelLogStream(WARNING, "xHCI") << "MSI not available, using poll mode";
|
||||
}
|
||||
|
||||
// Enable interrupter 0
|
||||
WriteRt(IR0_IMAN, IMAN_IE);
|
||||
WriteRt(IR0_IMOD, 0);
|
||||
|
||||
// Start controller
|
||||
WriteOp(OP_USBCMD, USBCMD_RS | USBCMD_INTE | USBCMD_HSEE);
|
||||
for (uint32_t i = 0; i < 100000; i++) {
|
||||
if (!(ReadOp(OP_USBSTS) & USBSTS_HCH)) break;
|
||||
for (int j = 0; j < 10; j++) asm volatile("" ::: "memory");
|
||||
}
|
||||
KernelLogStream(OK, "xHCI") << "Controller started";
|
||||
g_initialized = true;
|
||||
|
||||
// Power on all ports
|
||||
for (uint32_t port = 0; port < g_maxPorts; port++) {
|
||||
uint32_t portsc = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE);
|
||||
if (!(portsc & PORTSC_PP)) {
|
||||
WriteOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE, PORTSC_PP);
|
||||
}
|
||||
}
|
||||
BusyWaitMs(20);
|
||||
KernelLogStream(OK, "xHCI") << "All ports powered";
|
||||
|
||||
// Port scanning
|
||||
for (uint32_t port = 0; port < g_maxPorts; port++) {
|
||||
uint32_t portsc = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE);
|
||||
if (!(portsc & PORTSC_CCS)) continue;
|
||||
|
||||
KernelLogStream(INFO, "xHCI") << "Port " << base::dec << (uint64_t)(port + 1)
|
||||
<< ": device connected, PORTSC=" << base::hex << (uint64_t)portsc;
|
||||
|
||||
WriteOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE,
|
||||
(portsc & PORTSC_PRESERVE) | PORTSC_PR | PORTSC_CHANGE_BITS);
|
||||
|
||||
bool resetDone = false;
|
||||
for (uint32_t i = 0; i < 100000; i++) {
|
||||
PollEvents();
|
||||
uint32_t ps = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE);
|
||||
if (ps & PORTSC_PRC) { resetDone = true; break; }
|
||||
for (int j = 0; j < 100; j++) asm volatile("" ::: "memory");
|
||||
}
|
||||
|
||||
if (!resetDone) {
|
||||
KernelLogStream(WARNING, "xHCI") << "Port " << base::dec << (uint64_t)(port + 1) << " reset timeout";
|
||||
continue;
|
||||
}
|
||||
|
||||
portsc = ReadOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE);
|
||||
uint32_t speed = (portsc >> 10) & 0xF;
|
||||
WriteOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE,
|
||||
(portsc & PORTSC_PRESERVE) | PORTSC_CHANGE_BITS);
|
||||
|
||||
const char* speedStr = "Unknown";
|
||||
switch (speed) {
|
||||
case SPEED_FULL: speedStr = "Full (12 Mbps)"; break;
|
||||
case SPEED_LOW: speedStr = "Low (1.5 Mbps)"; break;
|
||||
case SPEED_HIGH: speedStr = "High (480 Mbps)"; break;
|
||||
case SPEED_SUPER: speedStr = "Super (5 Gbps)"; break;
|
||||
}
|
||||
|
||||
KernelLogStream(OK, "xHCI") << "Port " << base::dec << (uint64_t)(port + 1)
|
||||
<< ": reset complete, speed=" << speedStr;
|
||||
|
||||
BusyWaitMs(10);
|
||||
UsbDevice::EnumerateDevice(port + 1, speed);
|
||||
}
|
||||
|
||||
g_bootScanComplete = true;
|
||||
KernelLogStream(OK, "xHCI") << "Initialization complete";
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Initialize (standalone fallback path)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void Initialize() {
|
||||
@@ -678,14 +894,7 @@ namespace Drivers::USB::Xhci {
|
||||
// -----------------------------------------------------------------
|
||||
// Step 2: Read BAR0 and map MMIO region
|
||||
// -----------------------------------------------------------------
|
||||
uint32_t bar0 = Pci::LegacyRead32(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_BAR0);
|
||||
uint64_t mmioPhys = bar0 & 0xFFFFFFF0;
|
||||
|
||||
// Check if 64-bit BAR (type field bits 2:1 == 0b10)
|
||||
if ((bar0 & 0x06) == 0x04) {
|
||||
uint32_t bar1 = Pci::LegacyRead32(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_BAR1);
|
||||
mmioPhys |= ((uint64_t)bar1 << 32);
|
||||
}
|
||||
uint64_t mmioPhys = Pci::ReadBar0(foundDev->Bus, foundDev->Device, foundDev->Function);
|
||||
|
||||
KernelLogStream(INFO, "xHCI") << "BAR0 physical: " << base::hex << mmioPhys;
|
||||
|
||||
@@ -700,9 +909,7 @@ namespace Drivers::USB::Xhci {
|
||||
// -----------------------------------------------------------------
|
||||
// Step 3: Enable PCI bus master and memory space
|
||||
// -----------------------------------------------------------------
|
||||
uint16_t pciCmd = Pci::LegacyRead16(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_COMMAND);
|
||||
pciCmd |= PCI_CMD_BUS_MASTER | PCI_CMD_MEM_SPACE;
|
||||
Pci::LegacyWrite16(foundDev->Bus, foundDev->Device, foundDev->Function, PCI_REG_COMMAND, pciCmd);
|
||||
Pci::EnableBusMaster(foundDev->Bus, foundDev->Device, foundDev->Function);
|
||||
|
||||
KernelLogStream(OK, "xHCI") << "Bus mastering enabled";
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <Pci/Pci.hpp>
|
||||
|
||||
namespace Drivers::USB::Xhci {
|
||||
|
||||
@@ -29,16 +30,6 @@ namespace Drivers::USB::Xhci {
|
||||
constexpr uint8_t PCI_SUBCLASS_USB = 0x03;
|
||||
constexpr uint8_t PCI_PROGIF_XHCI = 0x30;
|
||||
|
||||
// PCI config space offsets
|
||||
constexpr uint8_t PCI_REG_BAR0 = 0x10;
|
||||
constexpr uint8_t PCI_REG_BAR1 = 0x14;
|
||||
constexpr uint8_t PCI_REG_COMMAND = 0x04;
|
||||
|
||||
// PCI command register bits
|
||||
constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2);
|
||||
constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1);
|
||||
constexpr uint16_t PCI_CMD_INTX_DISABLE = (1 << 10);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// xHCI Capability Register offsets (from BAR0)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -272,6 +263,7 @@ namespace Drivers::USB::Xhci {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Initialize();
|
||||
bool Probe(const Pci::PciDevice& dev);
|
||||
bool IsInitialized();
|
||||
|
||||
// Deferred hot-plug processing (call from timer tick, not interrupt context)
|
||||
|
||||
Reference in New Issue
Block a user