EFI USB Keyboard Driver Setup - RCS Problem

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
gomidas
Posts: 17
Joined: Wed Nov 25, 2020 9:28 pm

EFI USB Keyboard Driver Setup - RCS Problem

Post by gomidas »

I am trying to make USB keyboard driver. Here is my driver setup (CSM disabled at bios):

Code: Select all

#include <efi.h>
#include <efilib.h>

#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif

#pragma pack(push, 1)
typedef struct {
    uint32_t info[8];      // Slot Context
    uint32_t ep0[8];       // Endpoint 0 Context
    uint32_t reserved[8 * 30];
} DeviceContext;

typedef struct {
    uint32_t drop_flags;
    uint32_t add_flags;    // Slot (bit 0) + EP0 (bit 1) = 3
    uint32_t reserved[6];
    DeviceContext dev;
} InputContext;
#pragma pack(pop)

// --- xHCI Register ve TRB Yapıları ---
#pragma pack(push, 1)
typedef struct {
    uint64_t parameter;
    uint32_t status;
    uint32_t control;
} TRB;

typedef struct {
    uint64_t base_address;
    uint32_t size;
    uint32_t reserved;
} ERST_Entry;
#pragma pack(pop)
static TRB* kbd_transfer_ring = NULL;
// --- Global Değişkenler ---
static uintptr_t xhci_mmio = 0;
#define cap_base xhci_mmio
static uintptr_t op_base = 0;
static uintptr_t rt_base = 0;
static uintptr_t db_base = 0;
static uint32_t  cmd_idx = 0;
static TRB* cmd_ring = NULL;
static TRB* ev_ring = NULL;
static ERST_Entry* erst = NULL;
static uint64_t* dcbaa = NULL;
static uint32_t g_rcs_bit = 0;

// --- Temel MMIO ve PCI Araçları ---
static inline void mmio_w32(uintptr_t a, uint32_t v) { *(volatile uint32_t*)a = v; }
static inline uint32_t mmio_r32(uintptr_t a) { return *(volatile uint32_t*)a; }

// Fonksiyon prototipleri
bool xhci_initialize_full(void);
int find_and_reset_connected_port(uintptr_t cap_base, uintptr_t op_base);
bool address_device(uint8_t slot_id, uint32_t port_idx);
void* alloc_pages_aligned(UINTN pages);
uint8_t do_enable_slot_sync(void);
void configure_command_ring(void);
void update_erdp(uintptr_t rt_base, uintptr_t event_trb_ptr);
void check_xhci_status(void);

void xhci_bios_handover(uintptr_t xhci_mmio);

uint32_t pci_readd(uint8_t b, uint8_t d, uint8_t f, uint8_t off) {
    uint32_t addr = (uint32_t)((1u << 31) | (b << 16) | (d << 11) | (f << 8) | (off & 0xFC));
    __outdword(0xCF8, addr);
    return __indword(0xCFC);
}

void pci_writed(uint8_t b, uint8_t d, uint8_t f, uint8_t off, uint32_t val) {
    uint32_t addr = (uint32_t)((1u << 31) | (b << 16) | (d << 11) | (f << 8) | (off & 0xFC));
    __outdword(0xCF8, addr);
    __outdword(0xCFC, val);
}

void* alloc_pages_aligned(UINTN pages) {
    EFI_PHYSICAL_ADDRESS addr = 0;
    EFI_STATUS status;

    Print(L"\n  [alloc] %d sayfa isteniyor...", pages);
    status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages, &addr);

    if (EFI_ERROR(status)) {
        Print(L" HATA: %r", status);
        return NULL;
    }

    if (addr < 0x1000) {
        Print(L" GECERSIZ ADRES: %016llx", addr);
        return NULL;
    }

    Print(L" adres: %016llx", addr);

    char* ptr = (char*)addr;
    for (UINTN i = 0; i < pages * 4096; i++) {
        ptr[i] = 0;
    }

    Print(L" (sifirlandi)");
    return (void*)addr;
}

bool wait_reg(uintptr_t addr, uint32_t mask, uint32_t target, uint32_t retry_ms) {
    for (uint32_t i = 0; i < retry_ms; i++) {
        uint32_t val = mmio_r32(addr) & mask;
        if (val == target) {
            return true;
        }
        gBS->Stall(1000);
    }
    return false;
}

// --- 8-bit PCI okuma/yazma fonksiyonları (eklendi) ---
uint8_t pci_readb(uint8_t b, uint8_t d, uint8_t f, uint8_t off) {
    uint32_t addr = (uint32_t)((1u << 31) | (b << 16) | (d << 11) | (f << 8) | (off & 0xFC));
    __outdword(0xCF8, addr);
    return (uint8_t)(__indword(0xCFC) >> ((off & 3) * 8));
}

void pci_writeb(uint8_t b, uint8_t d, uint8_t f, uint8_t off, uint8_t val) {
    uint32_t addr = (uint32_t)((1u << 31) | (b << 16) | (d << 11) | (f << 8) | (off & 0xFC));
    uint32_t reg = __indword(0xCFC);
    uint32_t shift = (off & 3) * 8;
    reg &= ~(0xFF << shift);
    reg |= (val << shift);
    __outdword(0xCF8, addr);
    __outdword(0xCFC, reg);
}

// --- xHCI Fonksiyonları ---

bool xhci_initialize_full() {
    uint8_t bus, dev, func;
    bool found = false;

    Print(L"\n=== XHCI BASLATILIYOR ===");

    // 1. PCI Tarama
    for (int b = 0; b < 256 && !found; b++) {
        for (int d = 0; d < 32 && !found; d++) {
            for (int f = 0; f < 8; f++) {
                uint32_t id = pci_readd(b, d, f, 0x08);
                if ((id >> 8) == 0x0C0330) {
                    bus = b; dev = d; func = f; found = true;
                    Print(L"\n[xHCI] PCI'da bulundu: %02x:%02x.%x", b, d, f);
                    uint16_t vendor = id & 0xFFFF;
                    uint16_t device = (id >> 16) & 0xFFFF;
                    Print(L" (Vendor:%04x Device:%04x)", vendor, device);
                    break;
                }
            }
        }
    }
    if (!found) {
        Print(L"\n[xHCI] HATA: Denetleyici bulunamadı!");
        return false;
    }

    // 2. PCI Command Register
    uint32_t pci_cmd = pci_readd(bus, dev, func, 0x04);
    Print(L"\n[xHCI] PCI Command (once): %04x", pci_cmd & 0xFFFF);

    // TÜM bitleri set et (IO, MMIO, Bus Master)
    pci_writed(bus, dev, func, 0x04, pci_cmd | 0x07);

    // OKU ve doğrula
    pci_cmd = pci_readd(bus, dev, func, 0x04);
    Print(L"\n[xHCI] PCI Command (sonra): %04x", pci_cmd & 0xFFFF);

    // Bus Master hala 0 ise, dene
    if (!(pci_cmd & 0x04)) {
        Print(L"\n[xHCI] Bus Master set edilemedi! Tekrar deneniyor...");
        pci_writed(bus, dev, func, 0x04, pci_cmd | 0x04);
        pci_cmd = pci_readd(bus, dev, func, 0x04);
        Print(L"\n[xHCI] PCI Command (tekrar): %04x", pci_cmd & 0xFFFF);
    }

    // 3. BAR
    uint32_t bar0 = pci_readd(bus, dev, func, 0x10);
    uint32_t bar2 = pci_readd(bus, dev, func, 0x14);
    Print(L"\n[xHCI] BAR0: %08x BAR2: %08x", bar0, bar2);


    if ((bar0 & 0x06) == 0x04) {
        Print(L"\n[xHCI] 64-bit MMIO");
        xhci_mmio = bar0 & ~0xF;
        xhci_mmio |= ((uint64_t)bar2 << 32);
    }
    else {
        Print(L"\n[xHCI] 32-bit MMIO");
        xhci_mmio = bar0 & ~0xF;
    }
    Print(L"\n[xHCI] MMIO base: %016llx", (uint64_t)xhci_mmio);



    // MMIO'ya basit bir test yaz
    Print(L"\n[xHCI] MMIO test yazılıyor...");
    // Yazmak yerine sadece oku ve doğrula
    uint32_t cap_reg = mmio_r32(xhci_mmio);
    uint8_t len = cap_reg & 0xFF;
    if (len < 0x20 || len > 0x100) { // xHCI için makul sınırlar
        Print(L"\n[HATA] Gecersiz CAPLENGTH: %02x", len);
        return false;
    }

    uint32_t test_read = mmio_r32(xhci_mmio);
    Print(L"\n[xHCI] MMIO test okuma: %08x", test_read);

    if (test_read != 0x12345678) {
        Print(L"\n[xHCI] HATA: MMIO yazma/okuma basarisiz!");
        Print(L"\n[xHCI] BAR yanlis olabilir!");
        return false;
    }


    xhci_bios_handover(xhci_mmio);

    // 4. Capability registers
    uint32_t caplength = mmio_r32(xhci_mmio);
    uint8_t cap_length = caplength & 0xFF;
    uint32_t hcsparams1 = mmio_r32(xhci_mmio + 0x04);
    uint32_t max_slots = (hcsparams1 >> 8) & 0xFF;
    uint32_t max_ports = (hcsparams1 >> 24) & 0xFF;

    Print(L"\n[xHCI] CAPLENGTH: %08x", caplength);
    Print(L"\n[xHCI] Max Slots: %d", max_slots);
    Print(L"\n[xHCI] Max Ports: %d", max_ports);

    // 5. Base registers
    op_base = xhci_mmio + cap_length;
    db_base = xhci_mmio + (mmio_r32(xhci_mmio + 0x14) & ~0x3);
    rt_base = xhci_mmio + (mmio_r32(xhci_mmio + 0x18) & ~0x1F);

    // 6. Stop controller
    Print(L"\n[xHCI] Denetleyici durduruluyor...");
    mmio_w32(op_base + 0x00, 0);

    int wait_halt = 0;
    while (wait_halt < 100) {
        if (mmio_r32(op_base + 0x04) & 0x01) {
            Print(L"\n  [%d] HALTED", wait_halt);
            break;
        }
        for (volatile int w = 0; w < 100000; w++);
        wait_halt++;
    }

    // 7. Reset
    Print(L"\n[xHCI] Controller Reset...");
    mmio_w32(op_base + 0x00, 0x02);

    int wait_reset = 0;
    while (wait_reset < 1000) {
        if ((mmio_r32(op_base + 0x00) & 0x02) == 0) {
            Print(L"\n  [%d] Reset tamam", wait_reset);
            break;
        }
        for (volatile int w = 0; w < 100000; w++);
        wait_reset++;
    }

    // 7.1 CNR (Controller Not Ready) bitinin temizlenmesini bekle
    Print(L"\n[xHCI] Donanimin hazir olmasi bekleniyor (CNR)...");
    int wait_cnr = 0;
    while (wait_cnr < 1000) {
        uint32_t status = mmio_r32(op_base + 0x04);
        if (!(status & (1 << 11))) { // Bit 11 = CNR
            Print(L" Hazir!");
            break;
        }
        gBS->Stall(1000);
        wait_cnr++;
    }

    if (wait_cnr >= 1000) {
        Print(L"\n[HATA] Denetleyici CNR bitini temizlemedi!");
        return false;
    }

    // 8. Hata durumunu temizle
    uint32_t usbsts = mmio_r32(op_base + 0x04);
    if (usbsts & 0x10) {  // HSE biti
        Print(L"\n[xHCI] HSE biti temizleniyor...");
        mmio_w32(op_base + 0x04, 0x10);  // Write 1 to clear
    }

    // 9. Memory allocation
    Print(L"\n[xHCI] Bellek tahsis ediliyor...");

    Print(L"\n  Command ring icin:");
    cmd_ring = (TRB*)alloc_pages_aligned(1);
    if (!cmd_ring) return false;

    Print(L"\n  Event ring icin:");
    ev_ring = (TRB*)alloc_pages_aligned(1);
    if (!ev_ring) return false;

    Print(L"\n  ERST icin:");
    erst = (ERST_Entry*)alloc_pages_aligned(1);
    if (!erst) return false;

    Print(L"\n  DCBAA icin:");
    dcbaa = (uint64_t*)alloc_pages_aligned(1);
    if (!dcbaa) return false;

    // 10. DCBAA
    mmio_w32(op_base + 0x30, (uint32_t)(uintptr_t)dcbaa);
    mmio_w32(op_base + 0x34, (uint32_t)((uintptr_t)dcbaa >> 32));

    // 11. Command Ring - first TRB
    volatile uint32_t* first_cmd = (volatile uint32_t*)cmd_ring;
    first_cmd[0] = 0;
    first_cmd[1] = 0;
    first_cmd[2] = 0;
    first_cmd[3] = 1;
    _mm_mfence();

    // Command Ring'i disable et (CRCR'nin en düşük bitini 0 yap)
    uint64_t crcr_disable = (uintptr_t)cmd_ring & ~0x01;
    mmio_w32(op_base + 0x18, (uint32_t)crcr_disable);
    mmio_w32(op_base + 0x1C, (uint32_t)(crcr_disable >> 32));
    for (volatile int w = 0; w < 100000; w++);

    // Sonra enable et
    uint64_t crcr_enable = (uintptr_t)cmd_ring | 0x01;
    mmio_w32(op_base + 0x18, (uint32_t)crcr_enable);
    mmio_w32(op_base + 0x1C, (uint32_t)(crcr_enable >> 32));
    uint32_t read_low = mmio_r32(op_base + 0x18);
    uint32_t read_high = mmio_r32(op_base + 0x1C);
    Print(L"\n[xHCI] Okuma: %08x %08x", read_high, read_low);

    mmio_w32(op_base + 0x18, 1);
    read_low = mmio_r32(op_base + 0x18);
    Print(L"\n[xHCI] CRCR=1 okuma: %08x", read_low);

    // 12. Configure command ring
    configure_command_ring();

    // 13. Event Ring
    Print(L"\n[xHCI] Event ring yapilandiriliyor...");
    SetMem(ev_ring, 4096, 0);

    erst[0].base_address = (uintptr_t)ev_ring;
    erst[0].size = 256;
    erst[0].reserved = 0;

    uintptr_t int0 = rt_base + 0x20;
    mmio_w32(int0 + 0x00, 0);
    mmio_w32(int0 + 0x08, 1);
    mmio_w32(int0 + 0x10, (uint32_t)(uintptr_t)erst);
    mmio_w32(int0 + 0x14, (uint32_t)((uintptr_t)erst >> 32));

    uint64_t erdp = ((uintptr_t)ev_ring & ~0xF) | 0x08;
    mmio_w32(int0 + 0x18, (uint32_t)erdp);
    mmio_w32(int0 + 0x1C, (uint32_t)(erdp >> 32));
    mmio_w32(int0 + 0x00, 0x03);

    // 14. Max slots
    mmio_w32(op_base + 0x38, max_slots);

    // 15. Start controller
    Print(L"\n[xHCI] Denetleyici baslatiliyor...");
    mmio_w32(op_base + 0x00, 0x01 | 0x04 | 0x08);

    // HSE bitini temizle
   usbsts = mmio_r32(op_base + 0x04);
    if (usbsts & 0x10) {
        Print(L"\n[xHCI] HSE biti temizleniyor...");
        mmio_w32(op_base + 0x04, 0x10);  // Write 1 to clear
        usbsts = mmio_r32(op_base + 0x04);
        Print(L"\n[xHCI] Yeni USBSTS: %08x", usbsts);
    }

    int wait_run = 0;
    while (wait_run < 1000) {
        usbsts = mmio_r32(op_base + 0x04);
        if ((usbsts & 0x01) == 0) {
            Print(L"\n  [%d] CALISIYOR! USBSTS: %08x", wait_run, usbsts);
            break;
        }
        for (volatile int w = 0; w < 100000; w++);
        wait_run++;
    }

    Print(L"\n=== XHCI BASARIYLA BASLATILDI ===");
    return true;
}

void update_erdp(uintptr_t rt_base, uintptr_t event_trb_ptr) {
    uint64_t val = (event_trb_ptr & ~0xF) | 0x08;
    uintptr_t erdp_low = rt_base + 0x20 + 0x18;
    uintptr_t erdp_high = rt_base + 0x20 + 0x1C;

    mmio_w32(erdp_high, (uint32_t)(val >> 32));
    mmio_w32(erdp_low, (uint32_t)val);
}

void configure_command_ring() {
    Print(L"\n[xHCI] Command Ring yapilandiriliyor (SON DENEME)...");

    uint64_t crcr_value = (uintptr_t)cmd_ring | 0x01;
    uint32_t low = (uint32_t)crcr_value;
    uint32_t high = (uint32_t)(crcr_value >> 32);

    Print(L"\n[xHCI] CRCR degeri: %016llx", crcr_value);
    Print(L"\n[xHCI] Low: %08x, High: %08x", low, high);

    // Önce disable
    uint64_t crcr_disable = (uintptr_t)cmd_ring & ~0x01;
    mmio_w32(op_base + 0x18, (uint32_t)crcr_disable);
    mmio_w32(op_base + 0x1C, (uint32_t)(crcr_disable >> 32));
    for (volatile int w = 0; w < 1000000; w++);

    // Şimdi enable - farklı sıra dene
        mmio_w32(op_base + 0x18, low);
        mmio_w32(op_base + 0x1C, high);
    // CRCR yazarken önce High sonra Low yazmayı deneyin
    //mmio_w32(op_base + 0x1C, high); // Önce yüksek 32 bit
    //mmio_w32(op_base + 0x18, low | 0x01);   // Sonra düşük 32 bit + RCS

    // Hemen oku
    uint32_t read_low = mmio_r32(op_base + 0x18);
    uint32_t read_high = mmio_r32(op_base + 0x1C);
    Print(L"\n[xHCI] Okuma: %08x %08x", read_high, read_low);

    if ((read_low & 1) == 1) {
        Print(L"\n[xHCI] BASARILI! RCS=1");
        g_rcs_bit = 1;
    }
    else {
        Print(L"\n[xHCI] HATA: RCS hala 0");
        // Test: sadece 1 yaz
        mmio_w32(op_base + 0x18, 1);
        read_low = mmio_r32(op_base + 0x18);
        Print(L"\n[xHCI] CRCR=1 okuma: %08x", read_low);
        g_rcs_bit = 0;
    }
}

uint8_t do_enable_slot_sync() {
    Print(L"\n\n=== SLOT ALMA ISLEMI ===");

    uint32_t crcr_low = mmio_r32(op_base + 0x18);
    uint32_t rcs = crcr_low & 1;
    Print(L"\nCRCR: %08x (RCS=%d)", crcr_low, rcs);

    if (rcs == 0) {
        Print(L"\nHATA: Command Ring calismiyor! (RCS=0)");
        configure_command_ring();
        crcr_low = mmio_r32(op_base + 0x18);
        rcs = crcr_low & 1;
        if (rcs == 0) {
            return 0;
        }
    }

    SetMem(ev_ring, 4096, 0);

    uint64_t erdp = ((uintptr_t)ev_ring & ~0xF) | 0x08;
    mmio_w32(rt_base + 0x20 + 0x18, (uint32_t)erdp);
    mmio_w32(rt_base + 0x20 + 0x1C, (uint32_t)(erdp >> 32));

    volatile uint32_t* cmd = (volatile uint32_t*)cmd_ring;
    cmd[0] = 0;
    cmd[1] = 0;
    cmd[2] = 0;
    cmd[3] = (9 << 10) | rcs;

    Print(L"\nEnable Slot TRB: %08x %08x %08x %08x", cmd[0], cmd[1], cmd[2], cmd[3]);
    Print(L"\nDoorbell caliniyor...");
    mmio_w32(db_base, 0);

    Print(L"\nCommand Completion bekleniyor...");

    for (int timeout = 0; timeout < 5000; timeout++) {
        for (int j = 0; j < 16; j++) {
            volatile uint32_t* ev = (volatile uint32_t*)&ev_ring[j];
            if (ev[3] & 1) {
                uint32_t type = (ev[3] >> 10) & 0x3F;
                if (type == 33) {
                    uint8_t slot = (ev[3] >> 24) & 0xFF;
                    uint32_t code = (ev[2] >> 24) & 0xFF;
                    Print(L"\n>>> Slot:%d Kod:%d", slot, code);
                    if (code == 1) {
                        Print(L"\n*** SLOT %d ALINDI! ***", slot);
                        return slot;
                    }
                }
            }
        }
        if (timeout % 500 == 0) Print(L".");
        for (volatile int w = 0; w < 100000; w++);
    }

    Print(L"\n[HATA] Command Completion gelmedi!");
    return 0;
}

int find_and_reset_connected_port(uintptr_t cap_base, uintptr_t op_base) {
    uint32_t hcs1 = mmio_r32(cap_base + 0x04);
    uint32_t max_ports = (hcs1 >> 24) & 0xFF;

    Print(L"\n[xHCI] %d port taranıyor...", max_ports);

    for (uint32_t i = 0; i < max_ports; i++) {
        uintptr_t port_reg = op_base + 0x400 + (i * 0x10);
        uint32_t status = mmio_r32(port_reg);
        Print(L"\n[PORT %d] Status: %08x", i + 1, status);

        if (status & 0x01) {
            Print(L" CIHAZ VAR!");

            uint32_t portsc = status;
            portsc |= (1 << 4);
            portsc &= ~(1 << 9);
            mmio_w32(port_reg, portsc);
            Print(L" Reset basladi");

            int reset_timeout = 0;
            while (reset_timeout < 1000) {
                status = mmio_r32(port_reg);
                if (!(status & (1 << 4))) {
                    Print(L" Tamam (%d)", reset_timeout);
                    break;
                }
                for (volatile int w = 0; w < 100000; w++);
                reset_timeout++;
            }

            int enable_timeout = 0;
            while (enable_timeout < 500) {
                status = mmio_r32(port_reg);
                if (status & (1 << 9)) {
                    Print(L" ENABLED");
                    break;
                }
                for (volatile int w = 0; w < 100000; w++);
                enable_timeout++;
            }
            return i;
        }
    }
    return -1;
}

bool address_device(uint8_t slot_id, uint32_t port_idx) {
    Print(L"\n\n=== ADDRESS DEVICE ISLEMI ===");

    InputContext* input = (InputContext*)alloc_pages_aligned(1);
    DeviceContext* output = (DeviceContext*)alloc_pages_aligned(1);

    if (!input || !output) return false;

    dcbaa[slot_id] = (uintptr_t)output;
    input->add_flags = 3;

    uint32_t port_status = mmio_r32(op_base + 0x400 + (port_idx * 0x10));
    uint32_t speed = (port_status >> 10) & 0x0F;
    uint32_t portno = port_idx + 1;

    input->dev.info[0] = (1 << 27);
    input->dev.info[1] = (portno << 16);
    input->dev.info[2] = (speed << 20);
    input->dev.ep0[1] = (2 << 3) | (8 << 16);

    volatile TRB* cmd = &cmd_ring[cmd_idx];
    cmd->parameter = (uintptr_t)input;
    cmd->status = 0;
    cmd->control = (11 << 10) | (slot_id << 24) | 1;

    Print(L"\nAddress Device TRB gonderildi");
    mmio_w32(db_base, 0);

    for (int timeout = 0; timeout < 30000; timeout++) {
        for (int j = 0; j < 16; j++) {
            volatile uint32_t* ev = (volatile uint32_t*)&ev_ring[j];
            if (ev[3] & 1) {
                uint32_t type = (ev[3] >> 10) & 0x3F;
                if (type == 33) {
                    uint32_t completion = (ev[2] >> 24) & 0xFF;
                    ev[3] &= ~1;
                    update_erdp(rt_base, (uintptr_t)&ev_ring[j]);
                    if (completion == 1) {
                        Print(L"\n*** ADDRESS DEVICE BASARILI! ***");
                        cmd_idx++;
                        return true;
                    }
                }
            }
        }
        if (timeout % 1000 == 0) Print(L".");
        for (volatile int w = 0; w < 10000; w++);
    }
    return false;
}

void check_xhci_status() {
    Print(L"\n\n=== XHCI DURUM KONTROLÜ ===");
    Print(L"\nUSBCMD: %08x", mmio_r32(op_base + 0x00));
    Print(L"\nUSBSTS: %08x", mmio_r32(op_base + 0x04));
    Print(L"\nCRCR:   %08x %08x",
        mmio_r32(op_base + 0x1C), mmio_r32(op_base + 0x18));
}

// Global buffer - HID raporu için
static uint8_t keyboard_hid_buffer[8] __attribute__((aligned(64)));

// Interrupt TRB ile klavye verisi okuma
void setup_keyboard_interrupt(uint8_t slot_id) {
    Print(L"\n\n=== KLAVYE INTERRUPT AYARLANIYOR ===");

    // 1. Input Context hazırla
    InputContext* input = (InputContext*)alloc_pages_aligned(1);
    if (!input) return;

    // 2. Klavye için Transfer Ring (Halka) oluştur
    // Bu global değişken olmalı ki read_keyboard içinde erişebilesin
    kbd_transfer_ring = (TRB*)alloc_pages_aligned(1);

    // 3. Add Context Flags Ayarı
    // Bit 0: Slot Context güncellenecek
    // Bit 3: EP1-IN konfigüre edilecek (Klavye genelde EP1-IN kullanır)
    input->add_flags = (1 << 0) | (1 << 3);

    // 4. Slot Context Ayarları (Mevcut slot bilgilerini korumak için)
    // Önemli: Context Entries sayısını EP1-IN'i kapsayacak şekilde (3) güncellemelisin
    input->dev.info[0] = (3 << 27); // Context Entries = 3 (Slot + EP0 + EP1-OUT + EP1-IN)

    // 5. EP1-IN Context (Index 3) Doldurma
    // Senin struct yapında reserved[8 * 30] olduğu için:
    // reserved[0..7]   -> Index 2 (EP1-OUT)
    // reserved[8..15]  -> Index 3 (EP1-IN)
    uint32_t* ep1_in_ctx = &input->dev.reserved[8];

    // EP State = 0, Mult = 0, MaxPStreams = 0, LSA = 0, Interval = 1ms (genelde 7 veya 3)
    ep1_in_ctx[1] = (5 << 3) | (3 << 1) | (8 << 16) | (7 << 24);
    // Type: 5 (Interrupt IN), Error Count: 3, Max Packet Size: 8, Interval: 1ms (2^(7-3))

    // Transfer Ring Adresi + DCS (Dequeue Cycle State)
    uint64_t tr_ptr = (uintptr_t)kbd_transfer_ring | 1; // Başlangıç Cycle Bit = 1
    ep1_in_ctx[2] = (uint32_t)tr_ptr;
    ep1_in_ctx[3] = (uint32_t)(tr_ptr >> 32);

    ep1_in_ctx[4] = 8; // Average TRB Length (Klavye için 8 byte)

    // 6. Configure Endpoint Komutunu Gönder (Type 12)
    volatile TRB* cmd = &cmd_ring[cmd_idx];
    cmd->parameter = (uintptr_t)input;
    cmd->status = 0;
    cmd->control = (12 << 10) | (slot_id << 24) | 1; // 12: Configure Endpoint

    Print(L"\n[xHCI] Configure Endpoint (EP1-IN) TRB gonderildi. Slot: %d", slot_id);
    mmio_w32(db_base, 0); // Command Ring Doorbell

    // Command Completion bekleme döngüsü (Mevcut mantığın doğru)
    // ... (Wait loop) ...
    // ... (Configure Endpoint gönderildikten sonra)
    bool success = false;
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 16; j++) {
            volatile uint32_t* ev = (volatile uint32_t*)&ev_ring[j];
            if (ev[3] & 1) { // Cycle bit kontrolü
                uint32_t type = (ev[3] >> 10) & 0x3F;
                if (type == 33) { // Command Completion Event
                    uint32_t code = (ev[2] >> 24) & 0xFF;
                    if (code == 1) success = true; // 1 = Success
                }
                ev[3] &= ~1; // Event'i işlemiş say
                update_erdp(rt_base, (uintptr_t)&ev_ring[j]);
            }
        }
        if (success) break;
        gBS->Stall(1000);
    }
    if (!success) Print(L"\n[HATA] Endpoint konf. basarisiz!");
    cmd_idx++; // Komut indeksini artırmayı unutma!
}

static uint8_t kbd_cycle = 1; // Global takip

char read_keyboard(uint8_t slot_id) {
    if (!kbd_transfer_ring) return 0;

    // 0. indeksi kullanıyoruz (basitlik için)
    volatile TRB* trb = &kbd_transfer_ring[0];

    trb->parameter = (uintptr_t)keyboard_hid_buffer;
    trb->status = 8; // 8 byte bekle

    // IOC=1 (Bit 5), Type=1 (Normal, Bit 10-15), Cycle Bit
    trb->control = (1 << 10) | (1 << 5) | kbd_cycle;

    // Doorbell: Slot x, Target 3 (EP1-IN)
    // db_base genelde uint32_t pointer gibi davranır
    ((uint32_t*)db_base)[slot_id] = 3;

    // Event bekleme
    for (int timeout = 0; timeout < 100000; timeout++) {
        for (int j = 0; j < 256; j++) { // Event ring boyutu kadar tara
            volatile uint32_t* ev = (volatile uint32_t*)&ev_ring[j];

            // Cycle bit kontrolü (Donanım burayı 1 yaptı mı?)
            if ((ev[3] & 1) == 1) {
                uint32_t type = (ev[3] >> 10) & 0x3F;

                if (type == 32) { // Transfer Event
                    uint8_t keycode = keyboard_hid_buffer[2];

                    // ERDP Güncelle (Okuduğumuz TRB'nin adresi)
                    uintptr_t erdp_val = (uintptr_t)ev | 0x08;
                    mmio_w32(rt_base + 0x20 + 0x18, (uint32_t)erdp_val);
                    mmio_w32(rt_base + 0x20 + 0x1C, (uint32_t)(erdp_val >> 32));

                    // Temizlik ve bir sonraki sefer için cycle bit hazırlığı (basit mantık)
                    // Gerçek sürücüde burada enqueue/dequeue yönetilir

                    if (keycode >= 4 && keycode <= 29) return 'a' + (keycode - 4);
                }
            }
        }
        gBS->Stall(1);
    }
    return 0;
}

void xhci_bios_handover(uintptr_t xhci_mmio) {
    uint32_t hccparams1 = mmio_r32(xhci_mmio + 0x10);
    uint32_t xecp = (hccparams1 >> 16) << 2; // Dword offset to byte offset

    while (xecp) {
        uintptr_t addr = xhci_mmio + xecp;
        uint32_t cap = mmio_r32(addr);
        uint8_t id = cap & 0xFF;

        if (id == 1) { // USB Legacy Support Capability
            uint32_t bios_owned = mmio_r32(addr);
            if (bios_owned & (1 << 16)) { // BIOS Semaphore bit
                Print(L"\n[xHCI] BIOS mülkiyeti bırakıyor...");
                mmio_w32(addr, bios_owned | (1 << 24)); // OS Semaphore bit set

                // BIOS'un mülkiyeti bırakmasını bekle (bit 16 sıfırlanmalı)
                for (int i = 0; i < 1000; i++) {
                    if (!(mmio_r32(addr) & (1 << 16))) break;
                    gBS->Stall(1000);
                }
            }
        }

        uint8_t next = (cap >> 8) & 0xFF;
        if (!next) break;
        xecp += (next << 2);
    }
}

static uint32_t kbd_enq_idx = 0;   // Bizim yazacağımız yer
static uint8_t  kbd_pcs = 1;       // Producer Cycle State (Donanımın beklediği bit)

// Klavye TRB'sini halkaya ekleyen fonksiyon
void enqueue_kbd_trb(uint8_t slot_id) {
    volatile TRB* trb = &kbd_transfer_ring[kbd_enq_idx];

    trb->parameter = (uintptr_t)keyboard_hid_buffer;
    trb->status = 8; // Max Packet Size

    // IOC (Interrupt on Completion) + TRB Type (1 = Normal) + Cycle Bit
    trb->control = (1 << 5) | (1 << 10) | kbd_pcs;

    // Doorbell çal: Target 3 (EP1-IN)
    uint32_t* db = (uint32_t*)db_base;
    db[slot_id] = 3;

    // İndeksi ilerlet
    kbd_enq_idx++;

    // Ring sonuna geldik mi? (Genelde 256 TRB'lik bir sayfa ayırdık)
    if (kbd_enq_idx >= 255) { // Son TRB Link TRB olmalı ama basitlik için başa dönüyoruz
        // Gerçek bir sürücüde buraya "Link TRB" konur. 
        // Şimdilik halkayı manuel sıfırlıyoruz:
        kbd_enq_idx = 0;
        kbd_pcs ^= 1; // Tur bittiğinde Cycle Bit tersine döner
    }
}

static uint32_t ev_deq_idx = 0;
static uint8_t  ev_ccs = 1; // Consumer Cycle State (Donanımın yazdığı bit)

char poll_keyboard_event(uint8_t slot_id) {
    volatile TRB* ev = &ev_ring[ev_deq_idx];

    // Donanım bu indekse bir şey yazdı mı? (Cycle Bit kontrolü)
    if ((ev->control & 1) == ev_ccs) {
        uint32_t type = (ev->control >> 10) & 0x3F;
        char result = 0;

        if (type == 32) { // Transfer Event
            uint32_t completion_code = (ev->status >> 24) & 0xFF;
            
            if (completion_code == 1) { // Success
                uint8_t keycode = keyboard_hid_buffer[2];
                if (keycode >= 4 && keycode <= 29) {
                    result = 'a' + (keycode - 4);
                }
            }
            
            // Yeni bir transfer isteği gönder (Klavye sürekli dinlenmeli)
            enqueue_kbd_trb(slot_id);
        }

        // Event işlendi, Dequeue Pointer'ı ilerlet
        ev_deq_idx++;
        if (ev_deq_idx >= 256) {
            ev_deq_idx = 0;
            ev_ccs ^= 1;
        }

        // ERDP Güncelle (Donanıma "buraya kadar okudum" de)
        uintptr_t erdp_val = (uintptr_t)&ev_ring[ev_deq_idx] | 0x08;
        mmio_w32(rt_base + 0x20 + 0x18, (uint32_t)erdp_val);
        mmio_w32(rt_base + 0x20 + 0x1C, (uint32_t)(erdp_val >> 32));

        return result;
    }
    return 0;
}
main efi loop:

Code: Select all

//...
					Rnder = false; //Normally I exit boot services and do the OS loop somewhere else...
					Rnder_OS = true;

					Print(L"\n=== USB KLAVYE BASLATILIYOR ===");

					if (xhci_initialize_full()) {
						// Durum kontrolü
						uint32_t usbsts = mmio_r32(op_base + 0x04);
						Print(L"\nUSBSTS: %08x", usbsts);

						// Portları tara ve klavyeyi bul
						int device_port = find_and_reset_connected_port(cap_base, op_base);

						if (device_port != -1) {
							Print(L"\nKlavye port %d'de bulundu!", device_port + 1);

							// Slot al
							uint8_t slot = do_enable_slot_sync();

							// Slot alındıktan ve device address yapıldıktan sonra:
							if (slot > 0 && address_device(slot, device_port)) {
								Print(L"\n✓✓✓ KLAVYE HAZIR! ✓✓✓");

								// Interrupt endpoint'i yapılandır
								setup_keyboard_interrupt(slot);

								// Klavye okuma döngüsü
								while (Rnder_OS) {
									char key = read_keyboard(slot);

									if (key >= 'a' && key <= 'z') {
										Print(L"\nTuş: %c", key);  // A'ya basınca 'a' yazacak
									}

									// ESC tuşu için (HID Usage ID 41 = ESC)
									// Basit bekleme
									for (volatile int w = 0; w < 1000000; w++);
								}
							}
						}
					}
//...
Result:
Image

Problem is RCS always comes zero.

How to solve this problem? [-o<
Octocontrabass
Member
Member
Posts: 6249
Joined: Mon Mar 25, 2013 7:01 pm

Re: EFI USB Keyboard Driver Setup - RCS Problem

Post by Octocontrabass »

If you want your driver to work with boot services, you must rewrite your driver to follow the UEFI driver model.

If you want your driver to work without boot services, you must exit boot services before you start your driver.
gomidas
Posts: 17
Joined: Wed Nov 25, 2020 9:28 pm

Re: EFI USB Keyboard Driver Setup - RCS Problem

Post by gomidas »

Octocontrabass wrote: Tue Mar 03, 2026 10:10 am If you want your driver to work with boot services, you must rewrite your driver to follow the UEFI driver model.

If you want your driver to work without boot services, you must exit boot services before you start your driver.
Thank you, you were correct. However, I had to fall back to PS/2 with CSM support in order to make a text renderer. Now, I exit boot services and read output from ps/2 keyboard and mouse:
Image
Additionally, works on real hardware (did not test the latest one but generally works).

Next, I will replace the prints with my DrawString(...) retry for xHCI. :mrgreen:
User avatar
bellezzasolo
Member
Member
Posts: 163
Joined: Sun Feb 20, 2011 2:01 pm

Re: EFI USB Keyboard Driver Setup - RCS Problem

Post by bellezzasolo »

gomidas wrote: Thu Mar 05, 2026 6:45 am
Octocontrabass wrote: Tue Mar 03, 2026 10:10 am If you want your driver to work with boot services, you must rewrite your driver to follow the UEFI driver model.

If you want your driver to work without boot services, you must exit boot services before you start your driver.
Thank you, you were correct. However, I had to fall back to PS/2 with CSM support in order to make a text renderer. Now, I exit boot services and read output from ps/2 keyboard and mouse:
Image
Additionally, works on real hardware (did not test the latest one but generally works).

Next, I will replace the prints with my DrawString(...) retry for xHCI. :mrgreen:
If you want a sample that got to a basic xHCI keyboard input, I did this one:
https://github.com/ChaiSoft/ChaiOS/blob ... l/xhci.cpp
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS
Post Reply