#include "wifi.h"
#include "dma.h"
#include "net.h"
#include "arch.h"
#include "dbg.h"
#include "fb.h"
#include "fs.h"
#include "idt.h"
#include "ioapic.h"
#include "lapic.h"
#include "paging.h"

struct interrupt_frame;

/* Forward declarations for MediaTek functions */
static int mt_mcu_send(uint8_t cmd, const void *data, uint16_t len, int wait_resp);
static int mt_fw_scatter(uint32_t addr, const uint8_t *data, uint32_t len);
static int mt_load_firmware(void);
static int wifi_poll(uint32_t reg, uint32_t mask, uint32_t val, int timeout_ms);

/* Forward declarations for Intel functions */
static int iwl_fw_start(void);

/* Forward declarations for MMIO */
static inline uint32_t iwl_rr(uint32_t reg);
static inline void iwl_wr(uint32_t reg, uint32_t val);
static inline uint32_t mt_rr(uint32_t reg);
static inline void mt_wr(uint32_t reg, uint32_t val);
static uint32_t wpci_read(uint8_t bus, uint8_t dev, uint8_t func, uint8_t off);

/* Global WiFi device (declared early for IRQ handlers) */
static struct wifi_dev wifi;

/* Forward declaration for interrupt handler */
static void wifi_irq_handler_impl(void);

/* ══════════════════════════════════════════════
   WiFi Interrupt Handler
   ══════════════════════════════════════════════ */
#define WIFI_IRQ_VECTOR  0x40  /* IDT vector for WiFi */

volatile uint32_t wifi_irq_count = 0;
volatile uint32_t wifi_irq_alive = 0;

/* ISR wrapper — called from IDT */
__attribute__((interrupt))
static void wifi_irq_isr(struct interrupt_frame *frame)
{
    (void)frame;
    wifi_irq_count++;

    /* Read interrupt cause */
    uint32_t cause = iwl_rr(IWL_CSR_INT);
    iwl_wr(IWL_CSR_INT, cause);  /* ack */

    /* Check for ALIVE notification */
    if(cause & IWL_CSR_INT_BIT_ALIVE) {
        wifi_irq_alive = 1;
    }

    /* Check for FH RX (firmware data) */
    if(cause & IWL_CSR_INT_BIT_FH_RX) {
        /* TODO: process RX data */
    }

    /* Check for FH TX done */
    if(cause & IWL_CSR_INT_BIT_FH_TX) {
        /* TODO: process TX reclaim */
    }

    /* Check for errors */
    if(cause & IWL_CSR_INT_BIT_HW_ERROR) {
        dbg_printf("[wifi IRQ] HW error! cause=0x%x\n", cause);
    }

    /* Check for RF kill */
    if(cause & IWL_CSR_INT_BIT_RF_KILL) {
        dbg_printf("[wifi IRQ] RF kill\n");
    }

    /* Check for wakeup */
    if(cause & IWL_CSR_INT_BIT_WAKEUP) {
        /* TODO: handle wakeup */
    }

    /* Re-enable interrupts */
    iwl_wr(IWL_CSR_INT_MASK, IWL_MSK_INT_MASK_DEF);

    /* Send EOI */
    lapic_eoi();
}

/* Install WiFi IRQ handler */
static int wifi_install_irq(void)
{
    /* Read IRQ from PCI config space (offset 0x3C) */
    uint32_t irq_line = wpci_read(wifi.bus, wifi.dev, 0, 0x3C);
    uint8_t irq = irq_line & 0xFF;
    uint8_t pin = (irq_line >> 8) & 0xFF;

    dbg_printf("[wifi] PCI IRQ: line=%u pin=%u\n", irq, pin);

    if(irq == 0 || irq == 0xFF) {
        dbg_printf("[wifi] No IRQ assigned, using polling\n");
        return -1;
    }

    /* Install ISR in IDT */
    idt_set_entry(WIFI_IRQ_VECTOR, wifi_irq_isr, 0x8E, 0);
    dbg_printf("[wifi] ISR installed at vector 0x%x\n", WIFI_IRQ_VECTOR);

    /* Configure IOAPIC: route IRQ to our vector */
    /* flags: level triggered, active low, physical destination */
    uint64_t flags = (1 << 15) |  /* level triggered */
                     (1 << 13) |  /* active low */
                     (0 << 11);   /* physical destination */
    ioapic_redirect_irq(irq, WIFI_IRQ_VECTOR, flags);
    dbg_printf("[wifi] IOAPIC: IRQ %d -> vector 0x%x\n", irq, WIFI_IRQ_VECTOR);

    /* Unmask the IRQ */
    ioapic_unmask_irq(irq);
    dbg_printf("[wifi] IRQ %d unmasked\n", irq);

    return 0;
}

/* ── MediaTek WFDMA IRQ handler ── */
#define MT7922_IRQ_VECTOR  0x41  /* separate vector for MT7922 */

volatile uint32_t mt7922_irq_count = 0;

__attribute__((interrupt))
static void mt7922_irq_isr(struct interrupt_frame *frame)
{
    (void)frame;
    mt7922_irq_count++;

    /* Read WFDMA0 host interrupt status */
    uint32_t cause = mt_rr(0xd4000 + 0x200);  /* HOST_INT_STA */
    mt_wr(0xd4000 + 0x200, cause);  /* ack */

    /* Handle MCU RX complete */
    if(cause & MT7922_INT_RX_DONE_WM) {
        /* TODO: process MCU response */
    }

    /* Handle data RX complete */
    if(cause & MT7922_INT_RX_DONE_DATA) {
        /* TODO: process 802.11 data */
    }

    /* Handle TX complete */
    if(cause & MT7922_INT_TX_DONE_BAND0) {
        /* TODO: reclaim TX descriptors */
    }

    /* Re-enable interrupts */
    mt_wr(0xd4000 + 0x204, 0xFFFFFFFF);

    lapic_eoi();
}

/* Install MT7922 IRQ handler */
static int mt7922_install_irq(void)
{
    uint32_t irq_line = wpci_read(wifi.bus, wifi.dev, 0, 0x3C);
    uint8_t irq = irq_line & 0xFF;

    dbg_printf("[MT7922] PCI IRQ: line=%u\n", irq);

    if(irq == 0 || irq == 0xFF) {
        dbg_printf("[MT7922] No IRQ assigned\n");
        return -1;
    }

    /* Install ISR */
    idt_set_entry(MT7922_IRQ_VECTOR, mt7922_irq_isr, 0x8E, 0);

    /* Route via IOAPIC */
    uint64_t flags = (1 << 15) | (1 << 13) | (0 << 11);
    ioapic_redirect_irq(irq, MT7922_IRQ_VECTOR, flags);
    ioapic_unmask_irq(irq);

    /* Enable WFDMA0 interrupts */
    mt_wr(0xd4000 + 0x204, 0xFFFFFFFF);

    dbg_printf("[MT7922] IRQ installed\n");
    return 0;
}
static void *memset(void *s, int c, uint32_t n)
{
    uint8_t *p = s;
    while(n--) *p++ = (uint8_t)c;
    return s;
}

static void *memcpy(void *d, const void *s, uint32_t n)
{
    uint8_t *dd = d;
    const uint8_t *ss = s;
    while(n--) *dd++ = *ss++;
    return d;
}

/* ══════════════════════════════════════════════
   PCI helpers
   ══════════════════════════════════════════════ */
static uint32_t wpci_read(uint8_t bus, uint8_t dev, uint8_t func, uint8_t off)
{
    uint32_t addr = (1u << 31) | ((uint32_t)bus << 16) |
                    ((uint32_t)dev << 11) | ((uint32_t)func << 8) | (off & ~3);
    outl(0xCF8, addr);
    return inl(0xCFC);
}

static void wpci_write(uint8_t bus, uint8_t dev, uint8_t func, uint8_t off, uint32_t val)
{
    uint32_t addr = (1u << 31) | ((uint32_t)bus << 16) |
                    ((uint32_t)dev << 11) | ((uint32_t)func << 8) | (off & ~3);
    outl(0xCF8, addr);
    outl(0xCFC, val);
}

/* ══════════════════════════════════════════════
   MediaTek MT7922 register map
   ══════════════════════════════════════════════ */
struct mt7922_reg_map { uint32_t phys, maps, size; };

static const struct mt7922_reg_map mt_fixed_map[] = {
    { 0x820d0000, 0x30000, 0x10000 },
    { 0x820ed000, 0x24800, 0x00800 },
    { 0x820e4000, 0x21000, 0x00400 },
    { 0x820e7000, 0x21e00, 0x00200 },
    { 0x820eb000, 0x24200, 0x00400 },
    { 0x820e2000, 0x20800, 0x00400 },
    { 0x820e3000, 0x20c00, 0x00400 },
    { 0x820e5000, 0x21400, 0x00800 },
    { 0x00400000, 0x80000, 0x10000 },
    { 0x00410000, 0x90000, 0x10000 },
    { 0x40000000, 0x70000, 0x10000 },
    { 0x54000000, 0x02000, 0x01000 },
    { 0x55000000, 0x03000, 0x01000 },
    { 0x58000000, 0x06000, 0x01000 },
    { 0x59000000, 0x07000, 0x01000 },
    { 0x7c000000, 0xf0000, 0x10000 },
    { 0x7c020000, 0xd0000, 0x10000 },
    { 0x7c060000, 0xe0000, 0x10000 },
    { 0x80020000, 0xb0000, 0x10000 },
    { 0x81020000, 0xc0000, 0x10000 },
    { 0x820c0000, 0x08000, 0x04000 },
    { 0x820c8000, 0x0c000, 0x02000 },
    { 0x820cc000, 0x0e000, 0x01000 },
    { 0x820cd000, 0x0f000, 0x01000 },
    { 0x74030000, 0x10000, 0x10000 },
    { 0x820ce000, 0x21c00, 0x00200 },
    { 0x820cf000, 0x22000, 0x01000 },
    { 0x820e0000, 0x20000, 0x00400 },
    { 0x820e1000, 0x20400, 0x00200 },
    { 0x820e9000, 0x23400, 0x00200 },
    { 0x820ea000, 0x24000, 0x00200 },
    { 0x820ec000, 0x24600, 0x00200 },
    { 0x820f0000, 0xa0000, 0x00400 },
    { 0x820f1000, 0xa0600, 0x00200 },
    { 0x820f2000, 0xa0800, 0x00400 },
    { 0x820f3000, 0xa0c00, 0x00400 },
    { 0x820f4000, 0xa1000, 0x00400 },
    { 0x820f5000, 0xa1400, 0x00800 },
    { 0x820f7000, 0xa1e00, 0x00200 },
    { 0x820f9000, 0xa3400, 0x00200 },
    { 0x820fa000, 0xa4000, 0x00200 },
    { 0x820fb000, 0xa4200, 0x00400 },
    { 0x820fc000, 0xa4600, 0x00200 },
    { 0x820fd000, 0xa4800, 0x00800 },
};

/* ── MT7922 MMIO ── */
static uint32_t mt_reg_map(uint32_t addr)
{
    if(addr < 0x100000) return addr;
    for(int i = 0; i < (int)(sizeof(mt_fixed_map)/sizeof(mt_fixed_map[0])); i++) {
        if(addr < mt_fixed_map[i].phys) continue;
        uint32_t ofs = addr - mt_fixed_map[i].phys;
        if(ofs >= mt_fixed_map[i].size) continue;
        return mt_fixed_map[i].maps + ofs;
    }
    dbg_printf("[wifi] unsupported MT reg 0x%x\n", addr);
    return 0;
}

static inline uint32_t mt_rr(uint32_t reg)
{
    volatile uint32_t *mmio = (volatile uint32_t *)(uintptr_t)wifi.mmio;
    return mmio[mt_reg_map(reg) / 4];
}

static inline void mt_wr(uint32_t reg, uint32_t val)
{
    volatile uint32_t *mmio = (volatile uint32_t *)(uintptr_t)wifi.mmio;
    mmio[mt_reg_map(reg) / 4] = val;
}

/* ══════════════════════════════════════════════
   Intel MMIO (direct CSR offsets)
   ══════════════════════════════════════════════ */
static inline uint32_t iwl_rr(uint32_t reg)
{
    volatile uint32_t *mmio = (volatile uint32_t *)(uintptr_t)wifi.mmio;
    return mmio[reg / 4];
}

static inline void iwl_wr(uint32_t reg, uint32_t val)
{
    volatile uint32_t *mmio = (volatile uint32_t *)(uintptr_t)wifi.mmio;
    mmio[reg / 4] = val;
}

static inline void iwl_set(uint32_t reg, uint32_t bits)
{
    iwl_wr(reg, iwl_rr(reg) | bits);
}

static inline void iwl_clear(uint32_t reg, uint32_t bits)
{
    iwl_wr(reg, iwl_rr(reg) & ~bits);
}

static inline void iwl_write64(uint32_t reg, uint64_t val)
{
    iwl_wr(reg, (uint32_t)val);
    iwl_wr(reg + 4, (uint32_t)(val >> 32));
}

/* ── Delay ── */
static void wifi_delay_ms(int ms)
{
    for(int i = 0; i < ms; i++)
        for(volatile int j = 0; j < 100000; j++);
}

/* ── Poll ── */
static int wifi_poll(uint32_t reg, uint32_t mask, uint32_t val, int timeout_ms)
{
    for(int i = 0; i < timeout_ms; i++) {
        if((iwl_rr(reg) & mask) == val) return 1;
        wifi_delay_ms(1);
    }
    return 0;
}

/* ══════════════════════════════════════════════
   PCI probe — detect both MT7922 and Intel
   ══════════════════════════════════════════════ */
static int wifi_pci_probe(void)
{
    dbg_printf("[wifi] Scanning PCI buses...\n");
    for(uint16_t b = 0; b < 256; b++) {
        if((b & 31) == 0) { dbg_render(); fb_printf("[wifi] scanning bus %d...\n", b); }
        for(uint8_t d = 0; d < 32; d++) {
            for(uint8_t f = 0; f < 8; f++) {
                uint32_t id = wpci_read(b, d, f, 0);
                uint16_t ven = id & 0xFFFF;
                uint16_t dev_id = (id >> 16) & 0xFFFF;
                if(ven == 0xFFFF || ven == 0) continue;

                int found_chip = 0;

                if(ven == MT7922_VENDOR_ID && dev_id == MT7922_DEVICE_ID) {
                    wifi.chip = WIFI_CHIP_MT7922;
                    wifi.device_id = dev_id;
                    dbg_printf("[wifi] Found MediaTek MT7922 at PCI %d:%d.%d\n", b, d, f);
                    fb_printf("[wifi] Found MT7922 at %d:%d.%d\n", b, d, f);
                    found_chip = 1;
                }

                if(ven == PCI_VENDOR_ID_INTEL) {
                    switch(dev_id) {
                    case INTEL_AX200_DEVICE:
                        wifi.chip = WIFI_CHIP_INTEL_AX200;
                        dbg_printf("[wifi] Found Intel AX200 at PCI %d:%d.%d\n", b, d, f);
                        fb_printf("[wifi] Found AX200 at %d:%d.%d\n", b, d, f);
                        found_chip = 1; break;
                    case INTEL_AX201_DEVICE:
                    case INTEL_AX201_DEVICE_2:
                        wifi.chip = WIFI_CHIP_INTEL_AX201;
                        dbg_printf("[wifi] Found Intel AX201 at PCI %d:%d.%d\n", b, d, f);
                        fb_printf("[wifi] Found AX201 at %d:%d.%d\n", b, d, f);
                        found_chip = 1; break;
                    case INTEL_AX210_DEVICE:
                        wifi.chip = WIFI_CHIP_INTEL_AX210;
                        dbg_printf("[wifi] Found Intel AX210 at PCI %d:%d.%d\n", b, d, f);
                        fb_printf("[wifi] Found AX210 at %d:%d.%d\n", b, d, f);
                        found_chip = 1; break;
                    case INTEL_CNVI_DEVICE:
                    case INTEL_CNVI_DEVICE_2:
                    case INTEL_CNVI_DEVICE_3:
                        wifi.chip = WIFI_CHIP_INTEL_CNVI;
                        dbg_printf("[wifi] Found Intel CNVi at PCI %d:%d.%d\n", b, d, f);
                        fb_printf("[wifi] Found CNVi at %d:%d.%d\n", b, d, f);
                        found_chip = 1; break;
                    default:
                        break;
                    }
                }

                if(found_chip) {
                    /* Enable bus master + memory space + disable INTx */
                    uint32_t cmd = wpci_read(b, d, f, 0x04);
                    wpci_write(b, d, f, 0x04, cmd | 0x04 | 0x02 | 0x40000);

                    /* Map BAR0 — handle 32-bit and 64-bit BARs */
                    uint32_t bar0_lo = wpci_read(b, d, f, 0x10);
                    uint32_t bar0_hi = 0;
                    uint32_t bar_type = (bar0_lo >> 1) & 3;

                    if(bar_type == 2) {
                        /* 64-bit BAR: also read upper 32 bits from BAR1 (offset 0x0C) */
                        bar0_hi = wpci_read(b, d, f, 0x0C);
                    }

                    uint64_t bar0_phys = ((uint64_t)bar0_hi << 32) | (bar0_lo & ~0xFULL);
                    dbg_printf("[wifi] BAR0 raw=0x%08x%08x phys=0x%llx type=%d\n",
                               bar0_hi, bar0_lo, (unsigned long long)bar0_phys, bar_type);

                    if(bar0_phys == 0 || bar0_phys == 0xFFFFFFFFULL) {
                        dbg_printf("[wifi] BAR0 invalid, skipping\n");
                        fb_printf("[wifi] chip at %d:%d.%d BAR0=invalid, skip\n", b, d, f);
                        dbg_render();
                        found_chip = 0;
                        continue;
                    }

                    wifi.bus = b;
                    wifi.dev = d;
                    wifi.device_id = dev_id;

                    fb_printf("[wifi] BAR0=0x%llx OK\n", (unsigned long long)bar0_phys);

                    paging_map_mmio_range(bar0_phys, 0x100000);
                    uint64_t mmio_virt = bar0_phys + 0xFFFF800000000000ULL;
                    wifi.mmio = (volatile uint8_t *)(uintptr_t)mmio_virt;
                    dbg_printf("[wifi] MMIO at virt=0x%llx\n", (unsigned long long)mmio_virt);

                    /* Verify MMIO works — read HW_REV should not be 0 */
                    uint32_t hwrev = iwl_rr(IWL_CSR_HW_REV);
                    uint32_t gp = iwl_rr(IWL_CSR_GP_CNTRL);
                    dbg_printf("[wifi] MMIO test: HW_REV=0x%08x GP=0x%08x\n", hwrev, gp);
                    fb_printf("[wifi] HW_REV=0x%x GP=0x%x\n", hwrev, gp);
                    dbg_render();

                    wifi.state = WIFI_STATE_PCI_FOUND;
                    return 0;
                }
            }
        }
    }
    dbg_printf("[wifi] No WiFi chip found\n");
    fb_printf("[wifi] No WiFi chip found on PCI bus\n");
    dbg_render();
    return -1;
}

/* ══════════════════════════════════════════════
   MediaTek MT7922 init
   ══════════════════════════════════════════════ */

static int mt_wfsys_reset(void)
{
    dbg_printf("[MT7922] WFSYS reset...\n");
    mt_wr(0x18060010, PCIE_LPCR_HOST_SET_OWN);
    wifi_delay_ms(50);
    mt_wr(0x18000140, 0);
    wifi_delay_ms(50);
    mt_wr(0x18000140, WFSYS_SW_RST_B);
    if(!wifi_poll(0x18000140, WFSYS_SW_INIT_DONE, WFSYS_SW_INIT_DONE, 500)) {
        dbg_printf("[MT7922] WFSYS reset timeout!\n");
        return -1;
    }
    dbg_printf("[MT7922] WFSYS reset OK\n");
    return 0;
}

static int mt_drv_own(void)
{
    for(int i = 0; i < 10; i++) {
        mt_wr(0x7c060010, PCIE_LPCR_HOST_CLR_OWN);
        wifi_delay_ms(2);
        if(wifi_poll(0x7c060010, PCIE_LPCR_HOST_OWN_SYNC, 0, 50))
            return 0;
    }
    dbg_printf("[MT7922] drv_own timeout\n");
    return -1;
}

static void mt_dma_init(void)
{
    dbg_printf("[MT7922] DMA init (using dma.c allocator)...\n");

    /* Disable TX/RX DMA */
    uint32_t glo = mt_rr(0xd4000 + 0x208);
    glo &= ~(WFDMA_GLO_CFG_TX_DMA_EN | WFDMA_GLO_CFG_RX_DMA_EN);
    mt_wr(0xd4000 + 0x208, glo);

    /* Allocate TX data ring (qid=0) */
    uint32_t phys;
    wifi.mt_tx_data.qid = 0;
    wifi.mt_tx_data.ring_size = MT7922_TX_RING_SIZE;
    wifi.mt_tx_data.tx = dma_alloc(MT7922_TX_RING_SIZE * sizeof(struct wfdma_tx_desc), &phys);
    wifi.mt_tx_data.phys = phys;
    wifi.mt_tx_data.buf = dma_alloc(MT7922_TX_RING_SIZE * MT_RX_BUF_SIZE, &wifi.mt_tx_data.phys);
    wifi.mt_tx_data.head = 0;
    wifi.mt_tx_data.tail = 0;
    mt_wr(0xd4000 + 0x300 + 0x000, phys);
    mt_wr(0xd4000 + 0x300 + 0x004, MT7922_TX_RING_SIZE);

    /* MCU TX ring (qid=1) */
    wifi.mt_tx_mcu.qid = 1;
    wifi.mt_tx_mcu.ring_size = MT7922_TX_MCU_RING_SIZE;
    wifi.mt_tx_mcu.tx = dma_alloc(MT7922_TX_MCU_RING_SIZE * sizeof(struct wfdma_tx_desc), &phys);
    wifi.mt_tx_mcu.phys = phys;
    wifi.mt_tx_mcu.buf = dma_alloc(MT7922_TX_MCU_RING_SIZE * MT_RX_BUF_SIZE, &wifi.mt_tx_mcu.phys);
    wifi.mt_tx_mcu.head = 0;
    wifi.mt_tx_mcu.tail = 0;
    mt_wr(0xd4000 + 0x400 + 0x000, phys);
    mt_wr(0xd4000 + 0x400 + 0x004, MT7922_TX_MCU_RING_SIZE);

    /* FWDL TX ring (qid=2) */
    wifi.mt_tx_fwdl.qid = 2;
    wifi.mt_tx_fwdl.ring_size = MT7922_TX_FWDL_RING_SIZE;
    wifi.mt_tx_fwdl.tx = dma_alloc(MT7922_TX_FWDL_RING_SIZE * sizeof(struct wfdma_tx_desc), &phys);
    wifi.mt_tx_fwdl.phys = phys;
    wifi.mt_tx_fwdl.buf = dma_alloc(MT7922_TX_FWDL_RING_SIZE * 4096, &wifi.mt_tx_fwdl.phys);
    wifi.mt_tx_fwdl.head = 0;
    wifi.mt_tx_fwdl.tail = 0;
    mt_wr(0xd4000 + 0x500 + 0x000, phys);
    mt_wr(0xd4000 + 0x500 + 0x004, MT7922_TX_FWDL_RING_SIZE);

    /* RX data ring */
    wifi.mt_rx_data.qid = 0;
    wifi.mt_rx_data.ring_size = MT7922_RX_RING_SIZE;
    wifi.mt_rx_data.rx = dma_alloc(MT7922_RX_RING_SIZE * sizeof(struct wfdma_rx_desc), &phys);
    wifi.mt_rx_data.phys = phys;
    wifi.mt_rx_data.buf = dma_alloc(MT7922_RX_RING_SIZE * MT_RX_BUF_SIZE, &wifi.mt_rx_data.phys);
    wifi.mt_rx_data.head = 0;
    wifi.mt_rx_data.tail = 0;
    mt_wr(0xd4000 + 0x540 + 0x000, phys);
    mt_wr(0xd4000 + 0x540 + 0x004, MT7922_RX_RING_SIZE);

    /* MCU RX ring */
    wifi.mt_rx_mcu.qid = 1;
    wifi.mt_rx_mcu.ring_size = MT7922_RX_MCU_RING_SIZE;
    wifi.mt_rx_mcu.rx = dma_alloc(MT7922_RX_MCU_RING_SIZE * sizeof(struct wfdma_rx_desc), &phys);
    wifi.mt_rx_mcu.phys = phys;
    wifi.mt_rx_mcu.buf = dma_alloc(MT7922_RX_MCU_RING_SIZE * MT_RX_BUF_SIZE, &wifi.mt_rx_mcu.phys);
    wifi.mt_rx_mcu.head = 0;
    wifi.mt_rx_mcu.tail = 0;
    mt_wr(0xd4000 + 0x580 + 0x000, phys);
    mt_wr(0xd4000 + 0x580 + 0x004, MT7922_RX_MCU_RING_SIZE);

    /* Reset DMA pointers */
    mt_wr(0xd4000 + 0x20c, ~0);
    mt_wr(0xd4000 + 0x210, ~0);
    mt_wr(0xd4000 + 0x214, 0);

    /* Enable WFDMA globally */
    glo = mt_rr(0xd4000 + 0x208);
    glo |= WFDMA_GLO_CFG_TX_WB_DDONE | WFDMA_GLO_CFG_RX_WB_DDONE |
           WFDMA_GLO_CFG_CLK_GAT_DIS | WFDMA_GLO_CFG_OMIT_TX_INFO |
           WFDMA_GLO_CFG_FIFO_DIS_CHECK | WFDMA_GLO_CFG_OMIT_RX_INFO_PFET2 |
           WFDMA_GLO_CFG_TX_DMA_EN | WFDMA_GLO_CFG_RX_DMA_EN;
    mt_wr(0xd4000 + 0x208, glo);

    dbg_printf("[MT7922] DMA rings initialized\n");
}

static int mt_mcu_send(uint8_t cmd, const void *data, uint16_t len, int wait_resp)
{
    volatile struct wfdma_tx_desc *desc;
    struct mt76_connac2_mcu_txd *txd;

    uint16_t idx = wifi.mt_tx_mcu.head;
    desc = &wifi.mt_tx_mcu.tx[idx];

    uint32_t buf_phys = wifi.mt_tx_mcu.phys + idx * MT_RX_BUF_SIZE;
    txd = (struct mt76_connac2_mcu_txd *)(wifi.mt_tx_mcu.buf + idx * MT_RX_BUF_SIZE);
    memset((void*)txd, 0, sizeof(*txd));

    txd->txd[0] = (1 << 0) | (1 << 30);
    txd->len = len;
    txd->pq_id = 0x8000;
    txd->cid = cmd;
    txd->pkt_type = MCU_PKT_ID;
    txd->seq = wifi.mcu_seq++;
    txd->ext_cid_ack = 1;
    txd->s2d_index = S2D_INDEX_H2N;

    if(data && len > 0) {
        uint8_t *dst = (uint8_t *)txd + sizeof(*txd);
        const uint8_t *src = data;
        for(uint16_t i = 0; i < len; i++) dst[i] = src[i];
    }

    desc->sdl = (len + sizeof(*txd)) | (0 << 16);
    desc->addr = buf_phys;

    wifi.mt_tx_mcu.head = (idx + 1) % wifi.mt_tx_mcu.ring_size;
    mt_wr(0xd4000 + 0x400 + 0x0c, wifi.mt_tx_mcu.head);

    dbg_printf("[MT7922] MCU cmd=0x%02x seq=%d len=%d sent\n", cmd, txd->seq, len);

    if(wait_resp) {
        for(int i = 0; i < 500; i++) {
            wifi_delay_ms(1);
            uint16_t rx_idx = wifi.mt_rx_mcu.tail;
            volatile struct wfdma_rx_desc *rx_desc = &wifi.mt_rx_mcu.rx[rx_idx];
            if(rx_desc->sdl != 0) {
                struct mt76_connac2_mcu_rxd *rxd =
                    (struct mt76_connac2_mcu_rxd *)(wifi.mt_rx_mcu.buf + rx_idx * MT_RX_BUF_SIZE);
                dbg_printf("[MT7922] MCU resp eid=0x%02x seq=%d len=%d\n",
                           rxd->eid, rxd->seq, rxd->len);
                wifi.mt_rx_mcu.tail = (rx_idx + 1) % wifi.mt_rx_mcu.ring_size;
                return 0;
            }
        }
        dbg_printf("[MT7922] MCU cmd=0x%02x timeout\n", cmd);
    }
    return 0;
}

/* MediaTek firmware scatter upload */
static int mt_fw_scatter(uint32_t addr, const uint8_t *data, uint32_t len)
{
    struct __attribute__((packed)) { uint32_t addr; uint32_t len; } hdr;
    hdr.addr = addr;
    hdr.len = len;

    uint8_t cmd_buf[8 + 8 + 4096];
    struct mt76_connac2_mcu_txd *txd = (void *)cmd_buf;
    memset(txd, 0, sizeof(*txd));
    txd->txd[0] = (1 << 0) | (1 << 30);
    txd->len = 8 + len;
    txd->pq_id = 0x8000;
    txd->cid = MCU_CMD_FW_SCATTER;
    txd->pkt_type = MCU_PKT_ID;
    txd->seq = wifi.mcu_seq++;
    txd->ext_cid_ack = 1;
    txd->s2d_index = S2D_INDEX_H2N;

    uint8_t *p = cmd_buf + sizeof(*txd);
    p[0] = hdr.addr; p[1] = hdr.addr>>8; p[2] = hdr.addr>>16; p[3] = hdr.addr>>24;
    p[4] = hdr.len;  p[5] = hdr.len>>8;  p[6] = hdr.len>>16;  p[7] = hdr.len>>24;
    p += 8;
    for(uint32_t i = 0; i < len; i++) p[i] = data[i];

    volatile struct wfdma_tx_desc *desc = &wifi.mt_tx_fwdl.tx[wifi.mt_tx_fwdl.head];
    uint32_t buf_phys = wifi.mt_tx_fwdl.phys + wifi.mt_tx_fwdl.head * 4096;
    uint8_t *dma_buf = wifi.mt_tx_fwdl.buf + wifi.mt_tx_fwdl.head * 4096;
    for(uint32_t i = 0; i < sizeof(*txd) + 8 + len; i++) dma_buf[i] = cmd_buf[i];

    desc->sdl = (sizeof(*txd) + 8 + len) | (0 << 16);
    desc->addr = buf_phys;
    wifi.mt_tx_fwdl.head = (wifi.mt_tx_fwdl.head + 1) % wifi.mt_tx_fwdl.ring_size;
    mt_wr(0xd4000 + 0x500 + 0x0c, wifi.mt_tx_fwdl.head);
    return 0;
}

static int mt_load_firmware(void)
{
    dbg_printf("[MT7922] Loading firmware...\n");
    mt_mcu_send(MCU_CMD_RESTART_DL_REQ, 0, 0, 0);
    wifi_delay_ms(100);

    if(!wifi_poll(0x7c000 + 0x10, MT_TOP_MISC2_FW_PWR_ON, MT_TOP_MISC2_FW_PWR_ON, 1000)) {
        dbg_printf("[MT7922] MCU not ready for firmware\n");
        return -1;
    }
    dbg_printf("[MT7922] MCU ready for firmware\n");

    /* TODO: Read firmware from FAT and upload via scatter */

    uint32_t fw_start_arg = 0x01 | 0x04;
    mt_mcu_send(MCU_CMD_FW_START_REQ, &fw_start_arg, sizeof(fw_start_arg), 1);

    if(!wifi_poll(0x7c000 + 0x10, MT_TOP_MISC2_FW_N9_RDY, MT_TOP_MISC2_FW_N9_RDY, 1500)) {
        dbg_printf("[MT7922] Firmware init timeout\n");
        return -1;
    }

    dbg_printf("[MT7922] Firmware loaded!\n");
    wifi.state = WIFI_STATE_FW_LOADED;
    return 0;
}

static int mt_init(void)
{
    dbg_printf("[MT7922] Init...\n");
    wifi.hw_rev = (mt_rr(0x70010200) << 16) | (mt_rr(0x70010204) & 0xff);
    dbg_printf("[MT7922] ASIC 0x%08x\n", wifi.hw_rev);

    if(mt_drv_own() < 0) return -1;
    if(mt_wfsys_reset() < 0) return -1;

    mt_wr(0xd4000 + 0x204, 0);
    mt_wr(0xd4000 + 0x188, 0xff);

    mt_dma_init();

    /* Install IRQ handler */
    mt7922_install_irq();

    if(mt_load_firmware() < 0) return -1;

    wifi.state = WIFI_STATE_READY;
    dbg_printf("[MT7922] Init complete!\n");
    return 0;
}

/* ══════════════════════════════════════════════
   Intel iwlwifi init (AX200/AX201/AX210/CNVi)
   ══════════════════════════════════════════════ */

/* Write peripheral register via HBUS */
static void iwl_write_prph(uint32_t addr, uint32_t val)
{
    iwl_wr(IWL_HBUS_TARG_PRPH_RADDR, addr | (1 << 31));
    iwl_wr(IWL_HBUS_TARG_PRPH_WDAT, val);
}

static uint32_t iwl_read_prph(uint32_t addr)
{
    iwl_wr(IWL_HBUS_TARG_PRPH_RADDR, addr | (1 << 31));
    return iwl_rr(IWL_HBUS_TARG_PRPH_RDAT);
}

/* Disable interrupts */
static void iwl_disable_interrupts(void)
{
    iwl_wr(IWL_CSR_INT_MASK, 0);
}

/* Enable interrupts */
static void iwl_enable_interrupts(void)
{
    iwl_wr(IWL_CSR_INT_MASK, IWL_MSK_INT_MASK_DEF);
}

/* APM config — wait for MAC_CLOCK_READY + set INIT_DONE */
static int iwl_apm_config(void)
{
    dbg_printf("[Intel] APM config...\n");

    /* SW reset first — required for CNVi */
    dbg_printf("[Intel] SW reset...\n");
    iwl_wr(IWL_CSR_RESET, IWL_CSR_RESET_REG_FLAG_SW_RESET);
    wifi_delay_ms(10);
    iwl_wr(IWL_CSR_RESET, 0);
    wifi_delay_ms(10);

    /* Disable L0s */
    iwl_set(IWL_CSR_GIO_CHICKEN_BITS, IWL_CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);

    /* Set FH wait threshold */
    iwl_set(IWL_CSR_DBG_HPET_MEM_REG, 1 << 9);

    /* Enable HAP INTA */
    iwl_set(IWL_CSR_HW_IF_CONFIG_REG, IWL_CSR_HW_IF_CONFIG_REG_HAP_WAKE);

    /* For CNVi: set INIT_DONE + MAC_CLOCK_READY together */
    iwl_set(IWL_CSR_GP_CNTRL,
            IWL_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
            IWL_CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
    wifi_delay_ms(10);

    /* Verify */
    uint32_t gp = iwl_rr(IWL_CSR_GP_CNTRL);
    dbg_printf("[Intel] GP_CNTRL=0x%08x\n", gp);

    /* Wait MAC_CLOCK_READY */
    if(!wifi_poll(IWL_CSR_GP_CNTRL, IWL_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
                  IWL_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 2500)) {
        dbg_printf("[Intel] MAC clock not ready, GP=0x%08x\n", iwl_rr(IWL_CSR_GP_CNTRL));
        return -1;
    }
    dbg_printf("[Intel] MAC clock ready\n");

    return 0;
}

/* APM stop */
static void iwl_apm_stop(void)
{
    iwl_set(IWL_CSR_RESET, IWL_CSR_RESET_REG_FLAG_SW_RESET);
    wifi_delay_ms(5);
    iwl_clear(IWL_CSR_RESET, IWL_CSR_RESET_REG_FLAG_SW_RESET);
}

/* Stop FH DMA */
static void iwl_fh_stop(void)
{
    /* Disable RX */
    iwl_wr(IWL_FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);

    /* Disable TX */
    for(int i = 0; i < 16; i++) {
        iwl_wr(IWL_FH_MEM_CBBC_0_15_LOWER + i * 8, 0);
        iwl_wr(IWL_FH_MEM_CBBC_0_15_UPPER + i * 8, 0);
    }

    /* Reset FH */
    iwl_set(IWL_CSR_RESET, IWL_CSR_RESET_REG_FLAG_SW_RESET);
    wifi_delay_ms(10);
    iwl_clear(IWL_CSR_RESET, IWL_CSR_RESET_REG_FLAG_SW_RESET);
}

/* Start hardware: disable interrupts + APM config */
static int iwl_start_hw(void)
{
    dbg_printf("[Intel] Starting HW...\n");

    /* Disable all interrupts */
    iwl_disable_interrupts();

    /* Clear pending interrupts */
    iwl_wr(IWL_CSR_INT, 0xFFFFFFFF);

    /* APM config */
    if(iwl_apm_config() < 0)
        return -1;

    /* Stop FH */
    iwl_fh_stop();

    return 0;
}

/* ══════════════════════════════════════════════
   Intel FH DMA init (RX/TX rings)
   ══════════════════════════════════════════════ */

/* Allocate RX buffer (one big buffer for all RX descriptors) */
static int iwl_rx_init(void)
{
    dbg_printf("[Intel] RX init...\n");

    /* Allocate RX buffer pool (256 * 4KB = 1MB) */
    wifi.rx_buf_count = IWL_RX_QUEUE_SIZE;
    wifi.rx_buf = dma_alloc(wifi.rx_buf_count * 4096, &wifi.rx_buf_dma);
    if(!wifi.rx_buf) {
        dbg_printf("[Intel] RX alloc failed\n");
        return -1;
    }
    dbg_printf("[Intel] RX buf: %u pages, phys=0x%x\n", wifi.rx_buf_count, wifi.rx_buf_dma);

    /* Allocate RB status structure */
    wifi.rb_status = dma_alloc(sizeof(struct iwl_rb_status), &wifi.rb_status_dma);
    if(!wifi.rb_status) return -1;
    memset(wifi.rb_status, 0, sizeof(struct iwl_rb_status));

    wifi.rx_read_ptr = 0;

    return 0;
}

/* Allocate TX command queue */
static int iwl_tx_init(void)
{
    dbg_printf("[Intel] TX init...\n");

    /* Allocate TX command queue (TFDs) */
    wifi.tx_cmd_buf = dma_alloc(IWL_CMD_QUEUE_SIZE * sizeof(struct iwl_tfh_tfd), &wifi.tx_cmd_dma);
    if(!wifi.tx_cmd_buf) {
        dbg_printf("[Intel] TX alloc failed\n");
        return -1;
    }
    memset(wifi.tx_cmd_buf, 0, IWL_CMD_QUEUE_SIZE * sizeof(struct iwl_tfh_tfd));

    wifi.tx_write_ptr = 0;
    wifi.tx_read_ptr = 0;

    return 0;
}

/* Allocate Keep-Warm buffer */
static int iwl_kw_init(void)
{
    wifi.kw_buf = dma_alloc(4096, &wifi.kw_dma);
    if(!wifi.kw_buf) return -1;
    memset(wifi.kw_buf, 0, 4096);
    return 0;
}

/* Initialize FH (Flow Handler) */
static int iwl_fh_init(void)
{
    dbg_printf("[Intel] FH init...\n");

    /* Allocate and init RX */
    if(iwl_rx_init() < 0) return -1;

    /* Allocate and init TX */
    if(iwl_tx_init() < 0) return -1;

    /* Allocate KW buffer */
    if(iwl_kw_init() < 0) return -1;

    /* Configure RX channel 0 */
    iwl_wr(IWL_FH_RCSR_CHNL0_RBDCB_BASE_REG, wifi.rb_status_dma);
    iwl_wr(IWL_FH_RCSR_CHNL0_RBDCB_WPTR_REG, 0);

    /* Configure RX status ring */
    iwl_wr(IWL_FH_RSCSR_CHNL0_STTS_WPTR_REG, wifi.rb_status_dma);

    /* Enable RX channel: 4KB RB size, DMA enable */
    iwl_wr(IWL_FH_MEM_RCSR_CHNL0_CONFIG_REG,
           IWL_FH_RCSR_RX_CONFIG_RB_SIZE_4K |
           IWL_FH_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN |
           IWL_FH_RCSR_RX_CONFIG_REG_VAL_RDRB_EN);

    /* Set FH RX read/write pointers */
    iwl_wr(IWL_FH_RCSR_CHNL0_RDPTR_REG, 0);

    /* Set Keep-Warm buffer */
    iwl_wr(IWL_FH_MEM_KW_BASE, wifi.kw_dma);

    dbg_printf("[Intel] FH init complete\n");
    return 0;
}

/* ══════════════════════════════════════════════
   Intel Context Info v2
   ══════════════════════════════════════════════ */

static int iwl_ctxt_info_v2_init(void)
{
    dbg_printf("[Intel] Context Info v2 init...\n");

    /* Allocate prph scratch */
    wifi.prph_scratch = dma_alloc(sizeof(struct iwl_prph_scratch), &wifi.prph_scratch_dma);
    if(!wifi.prph_scratch) return -1;
    memset(wifi.prph_scratch, 0, sizeof(struct iwl_prph_scratch));

    /* Fill scratch config */
    wifi.prph_scratch->ctrl_cfg.version.mac_id = (uint16_t)wifi.hw_rev;
    wifi.prph_scratch->ctrl_cfg.version.size = sizeof(struct iwl_prph_scratch) / 4;

    uint32_t flags = IWL_PRPH_SCRATCH_MTR_MODE |
                     IWL_PRPH_SCRATCH_MTR_FORMAT_256B |
                     IWL_PRPH_SCRATCH_RB_SIZE_4K;
    wifi.prph_scratch->ctrl_cfg.control.control_flags = flags;

    /* Set free RBD address */
    wifi.prph_scratch->ctrl_cfg.free_rbd_addr = wifi.rb_status_dma;

    /* Allocate prph info */
    wifi.prph_info = dma_alloc(4096, &wifi.prph_info_dma);
    if(!wifi.prph_info) return -1;
    memset(wifi.prph_info, 0, 4096);

    /* Allocate context info */
    wifi.ctxt_info_v2 = dma_alloc(sizeof(struct iwl_context_info_v2), &wifi.ctxt_info_dma);
    if(!wifi.ctxt_info_v2) return -1;
    memset(wifi.ctxt_info_v2, 0, sizeof(struct iwl_context_info_v2));

    /* Fill context info */
    wifi.ctxt_info_v2->version = 2;
    wifi.ctxt_info_v2->size = sizeof(struct iwl_context_info_v2) / 4;
    wifi.ctxt_info_v2->config = IWL_CTXT_INFO_AUTO_FUNC_ENABLE;

    wifi.ctxt_info_v2->prph_info_base_addr = wifi.prph_info_dma;
    wifi.ctxt_info_v2->prph_scratch_base_addr = wifi.prph_scratch_dma;
    wifi.ctxt_info_v2->prph_scratch_size = sizeof(struct iwl_prph_scratch);

    /* RX ring (CR head index arr) */
    wifi.ctxt_info_v2->cr_head_idx_arr_base_addr = wifi.rb_status_dma;
    wifi.ctxt_info_v2->cr_idx_arr_size = IWL_RX_QUEUE_SIZE;

    /* TX ring (TR tail index arr) */
    wifi.ctxt_info_v2->tr_tail_idx_arr_base_addr = wifi.prph_info_dma + 2048;
    wifi.ctxt_info_v2->tr_idx_arr_size = IWL_CMD_QUEUE_SIZE;

    /* CR tail (at offset 3/4 of prph_info) */
    wifi.ctxt_info_v2->cr_tail_idx_arr_base_addr = wifi.prph_info_dma + 3072;

    /* TR head */
    wifi.ctxt_info_v2->tr_head_idx_arr_base_addr = wifi.prph_info_dma + 1024;

    /* MTR (TX) and MCR (RX) base addresses */
    wifi.ctxt_info_v2->mtr_base_addr = wifi.tx_cmd_dma;
    wifi.ctxt_info_v2->mcr_base_addr = wifi.rb_status_dma;
    wifi.ctxt_info_v2->mtr_size = IWL_CMD_QUEUE_SIZE;
    wifi.ctxt_info_v2->mcr_size = IWL_RX_QUEUE_SIZE;

    dbg_printf("[Intel] Context Info: ctxt=0x%x scratch=0x%x info=0x%x\n",
               wifi.ctxt_info_dma, wifi.prph_scratch_dma, wifi.prph_info_dma);

    return 0;
}

/* ══════════════════════════════════════════════
   Intel FW start
   ══════════════════════════════════════════════ */

static int iwl_fw_start(void)
{
    dbg_printf("[Intel] Firmware start...\n");

    /* Write context info address to hardware */
    iwl_write64(IWL_CSR_CTXT_INFO_ADDR, wifi.ctxt_info_dma);
    iwl_write64(IWL_CSR_IML_DATA_ADDR, wifi.iml_dma);
    iwl_wr(IWL_CSR_IML_SIZE_ADDR, wifi.iml_len);

    /* Enable auto function boot */
    iwl_set(IWL_CSR_CTXT_INFO_BOOT_CTRL, IWL_CSR_AUTO_FUNC_BOOT_ENA);

    /* Wait for ALIVE notification */
    dbg_printf("[Intel] Waiting for ALIVE...\n");
    for(int i = 0; i < 3000; i++) {
        wifi_delay_ms(1);
        uint32_t intr = iwl_rr(IWL_CSR_INT);
        if(intr & IWL_CSR_INT_BIT_ALIVE) {
            dbg_printf("[Intel] ALIVE interrupt! (INT=0x%x)\n", intr);
            /* Clear interrupt */
            iwl_wr(IWL_CSR_INT, IWL_CSR_INT_BIT_ALIVE);

            /* Read alive notification from RX ring */
            /* TODO: Parse iwl_alive_ntf */
            dbg_printf("[Intel] Firmware alive!\n");
            wifi.alive_received = 1;
            return 0;
        }
        if(intr & IWL_CSR_INT_BIT_HW_ERROR) {
            dbg_printf("[Intel] HW error! (INT=0x%x)\n", intr);
            return -1;
        }
    }

    dbg_printf("[Intel] ALIVE timeout\n");
    return -1;
}

/* ══════════════════════════════════════════════
   Firmware loading from filesystem
   ══════════════════════════════════════════════ */

/* Read file from filesystem into DMA buffer.
 * Returns file size or -1 on error.
 * Caller must provide DMA buffer large enough. */
static int wifi_read_file(const char *name, void *dma_buf, uint32_t max_size)
{
    /* Find file in current directory */
    int inum = fs_find(name, fs_get_current());
    if(inum < 0) {
        dbg_printf("[wifi] File '%s' not found\n", name);
        return -1;
    }

    uint8_t type;
    fs_stat(inum, 0, &type, 0);
    if(type != FS_TYPE_FILE) {
        dbg_printf("[wifi] '%s' is not a file\n", name);
        return -1;
    }

    uint32_t fsize = fs_get_size(inum);
    if(fsize > max_size) {
        dbg_printf("[wifi] File '%s' too large: %u > %u\n", name, fsize, max_size);
        return -1;
    }

    /* Read in chunks */
    uint32_t off = 0;
    while(off < fsize) {
        uint32_t chunk = fsize - off;
        if(chunk > 4096) chunk = 4096;
        int n = fs_read(inum, (uint8_t *)dma_buf + off, chunk, off);
        if(n <= 0) break;
        off += n;
    }

    dbg_printf("[wifi] Read '%s': %u bytes\n", name, off);
    return (int)off;
}

/* Intel: Load IML from file and start firmware */
int wifi_load_intel_fw(const char *iml_name)
{
    if(wifi.chip == WIFI_CHIP_MT7922) {
        dbg_printf("[wifi] Not an Intel chip\n");
        return -1;
    }

    dbg_printf("[wifi] Loading Intel firmware: %s\n", iml_name);

    /* IML (Init Memory Load) — loaded via Context Info v2 kick */
    int iml_size = wifi_read_file(iml_name, wifi.iml_buf, wifi.iml_len);
    if(iml_size < 0) {
        dbg_printf("[wifi] IML load failed\n");
        return -1;
    }
    wifi.iml_len = iml_size;
    dbg_printf("[wifi] IML loaded: %u bytes\n", iml_size);

    /* Start firmware */
    if(iwl_fw_start() < 0) {
        dbg_printf("[wifi] FW start failed\n");
        return -1;
    }

    dbg_printf("[wifi] Intel firmware loaded and running!\n");
    return 0;
}

/* MediaTek: Load firmware from file via MCU scatter upload */
int wifi_load_mt_fw(const char *fw_name, const char *patch_name)
{
    if(wifi.chip != WIFI_CHIP_MT7922) {
        dbg_printf("[wifi] Not a MediaTek chip\n");
        return -1;
    }

    dbg_printf("[wifi] Loading MediaTek firmware: %s\n", fw_name);

    /* Send restart command */
    mt_mcu_send(MCU_CMD_RESTART_DL_REQ, 0, 0, 0);
    wifi_delay_ms(100);

    /* Wait for MCU ready */
    if(!wifi_poll(0x7c000 + 0x10, MT_TOP_MISC2_FW_PWR_ON, MT_TOP_MISC2_FW_PWR_ON, 1000)) {
        dbg_printf("[wifi] MCU not ready for firmware\n");
        return -1;
    }
    dbg_printf("[wifi] MCU ready for firmware\n");

    /* Load ROM patch first (if specified) */
    if(patch_name) {
        uint8_t patch_buf[256 * 1024];
        int patch_size = wifi_read_file(patch_name, patch_buf, sizeof(patch_buf));
        if(patch_size > 0) {
            dbg_printf("[wifi] Uploading ROM patch: %u bytes\n", patch_size);
            /* Upload in 4KB chunks via scatter */
            uint32_t addr = 0;
            const uint8_t *p = patch_buf;
            while(patch_size > 0) {
                uint32_t chunk = patch_size > 4000 ? 4000 : patch_size;
                mt_fw_scatter(addr, p, chunk);
                addr += chunk;
                p += chunk;
                patch_size -= chunk;
                wifi_delay_ms(10);
            }
            dbg_printf("[wifi] ROM patch uploaded\n");
        }
    }

    /* Load RAM firmware */
    uint8_t fw_buf[256 * 1024];
    int fw_size = wifi_read_file(fw_name, fw_buf, sizeof(fw_buf));
    if(fw_size < 0) {
        dbg_printf("[wifi] FW file not found\n");
        return -1;
    }

    dbg_printf("[wifi] Uploading RAM firmware: %u bytes\n", fw_size);

    /* Upload firmware via scatter */
    uint32_t addr = 0;
    const uint8_t *p = fw_buf;
    int remaining = fw_size;
    while(remaining > 0) {
        uint32_t chunk = remaining > 4000 ? 4000 : remaining;
        mt_fw_scatter(addr, p, chunk);
        addr += chunk;
        p += chunk;
        remaining -= chunk;
        wifi_delay_ms(10);
    }
    dbg_printf("[wifi] Firmware uploaded\n");

    /* Start firmware */
    uint32_t fw_start_arg = 0x01 | 0x04;
    mt_mcu_send(MCU_CMD_FW_START_REQ, &fw_start_arg, sizeof(fw_start_arg), 1);

    /* Wait for N9 firmware ready */
    if(!wifi_poll(0x7c000 + 0x10, MT_TOP_MISC2_FW_N9_RDY, MT_TOP_MISC2_FW_N9_RDY, 1500)) {
        dbg_printf("[wifi] Firmware init timeout\n");
        return -1;
    }

    dbg_printf("[wifi] MediaTek firmware loaded and running!\n");
    wifi.state = WIFI_STATE_FW_LOADED;
    return 0;
}

/* Generic firmware load (auto-detect chip) */
int wifi_load_fw(const char *fw_name)
{
    if(wifi.state == WIFI_STATE_NONE || wifi.state == WIFI_STATE_PCI_FOUND) {
        dbg_printf("[wifi] WiFi not initialized, run 'wifi' first\n");
        return -1;
    }

    if(wifi.chip == WIFI_CHIP_MT7922)
        return wifi_load_mt_fw(fw_name, 0);
    else
        return wifi_load_intel_fw(fw_name);
}

/* ══════════════════════════════════════════════
   Intel main init
   ══════════════════════════════════════════════ */

static int intel_init(void)
{
    const char *chip_name = "unknown";
    switch(wifi.chip) {
    case WIFI_CHIP_INTEL_AX200: chip_name = "AX200"; break;
    case WIFI_CHIP_INTEL_AX201: chip_name = "AX201"; break;
    case WIFI_CHIP_INTEL_AX210: chip_name = "AX210"; break;
    case WIFI_CHIP_INTEL_CNVI:  chip_name = "CNVi (AC 9560/9260)"; break;
    default: break;
    }
    dbg_printf("[Intel] Init %s (device=0x%04x)...\n", chip_name, wifi.device_id);

    /* Read HW revision */
    wifi.hw_rev = iwl_rr(IWL_CSR_HW_REV);
    dbg_printf("[Intel] HW revision: 0x%08x\n", wifi.hw_rev);

    /* Start HW */
    if(iwl_start_hw() < 0) return -1;

    /* Install IRQ handler */
    wifi_install_irq();

    /* Init FH (RX/TX rings) */
    if(iwl_fh_init() < 0) return -1;

    /* Init Context Info v2 */
    if(iwl_ctxt_info_v2_init() < 0) return -1;

    /* Allocate IML buffer (max 2MB) */
    wifi.iml_len = 2 * 1024 * 1024;
    wifi.iml_buf = dma_alloc(wifi.iml_len, &wifi.iml_dma);
    if(!wifi.iml_buf) {
        dbg_printf("[Intel] IML alloc failed\n");
        return -1;
    }
    dbg_printf("[Intel] IML buffer: %u bytes, phys=0x%x\n", wifi.iml_len, wifi.iml_dma);

    /* TODO: Load firmware IML from FAT — run 'wifi-load <file>' */
    dbg_printf("[Intel] Ready for firmware. Run 'wifi-load <iml_file>'\n");

    wifi.state = WIFI_STATE_READY;
    dbg_printf("[Intel] Init complete!\n");
    return 0;
}

/* ══════════════════════════════════════════════
   Main init
   ══════════════════════════════════════════════ */
int wifi_init(void)
{
    dbg_printf("[wifi] === WiFi driver ===\n");

    /* Init DMA allocator */
    dma_init();

    if(wifi_pci_probe() < 0)
        return -1;

    if(wifi.chip == WIFI_CHIP_MT7922)
        return mt_init();
    else
        return intel_init();
}

int wifi_scan(void)
{
    dbg_printf("[wifi] Scan: not yet implemented\n");
    return -1;
}
