// HAUKI_MODULE: BCMINIT
// HAUKI_MODULE: BCMSEND
// HAUKI_MODULE: BCMRECV

#include "../hauki.h"

#define BROADCOM_VENDOR 0x14E4

// Broadcom Tigon3 MMIO Offsets
#define TG3_MAC_ADDR_HIGH      0x0410
#define TG3_MAC_ADDR_LOW       0x0414
#define TG3_SEND_MAILBOX       0x0300 // TX BD Producer Index
#define TG3_RECV_MAILBOX       0x0200 // RX BD Return Index

// Safe DMA Memory Regions
#define TX_RING_ADDR           0x220000
#define TX_BUFS_ADDR           0x230000
#define RX_RING_ADDR           0x240000
#define RX_BUFS_ADDR           0x250000
#define NUM_DESC               16

// Broadcom TX Buffer Descriptor (BD) Structure (16 bytes)
struct tg3_tx_desc {
    unsigned int addr_high;
    unsigned int addr_low;
    unsigned short len;
    unsigned short flags;
    unsigned int vlan_tag; // Also used for opaque data
} __attribute__((packed));

// Broadcom RX Buffer Descriptor (BD) Structure (16 bytes)
struct tg3_rx_desc {
    unsigned int addr_high;
    unsigned int addr_low;
    unsigned short len;
    unsigned short idx_flags;
    unsigned short type_flags;
    unsigned short ip_tcp_csum;
    unsigned int err_vlan;
} __attribute__((packed));

static unsigned int bcm_mmio_base = 0;
static struct tg3_tx_desc* tx_ring;
static unsigned char* tx_bufs;
static int tx_idx = 0;

static struct tg3_rx_desc* rx_ring;
static unsigned char* rx_bufs;
static int rx_idx = 0;

static unsigned char my_mac[6] = {0};

static unsigned int pci_read_bcm(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset) {
    unsigned int address = (unsigned int)((bus << 16) | (slot << 11) | (func << 8) | (offset & 0xFC) | 0x80000000);
    outl(0xCF8, address); return inl(0xCFC);
}

static void pci_write_bcm(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset, unsigned int data) {
    unsigned int address = (unsigned int)((bus << 16) | (slot << 11) | (func << 8) | (offset & 0xFC) | 0x80000000);
    outl(0xCF8, address); outl(0xCFC, data);
}

static inline void mmio_write32(unsigned int reg, unsigned int val) {
    *((volatile unsigned int*)(bcm_mmio_base + reg)) = val;
}

static inline unsigned int mmio_read32(unsigned int reg) {
    return *((volatile unsigned int*)(bcm_mmio_base + reg));
}

void mod_BCMINIT(char* args) {
    kprint("--- BROADCOM TIGON3 PHYSICAL INITIALIZATION ---\r\n");
    int found = 0;

    // Broadcom cards are sometimes pushed to higher buses on physical motherboards
    for (int bus = 0; bus < 128; bus++) { // Extended scan for weird bridges
        for (int slot = 0; slot < 32; slot++) {
            unsigned int vendor_device = pci_read_bcm(bus, slot, 0, 0);
            if ((vendor_device & 0xFFFF) == BROADCOM_VENDOR) {
                found = 1;

                // 1. Enable Bus Mastering & Memory Space
                unsigned int cmd = pci_read_bcm(bus, slot, 0, 0x04);
                pci_write_bcm(bus, slot, 0, 0x04, cmd | 0x06);

                // 2. Lock MMIO Base
                bcm_mmio_base = pci_read_bcm(bus, slot, 0, 0x10) & 0xFFFFFFF0;
                kprint("[+] BCM MMIO Locked: 0x"); print_hex32(bcm_mmio_base); kprint("\r\n");

                // 3. Extract Physical MAC
                unsigned int mac_high = mmio_read32(TG3_MAC_ADDR_HIGH);
                unsigned int mac_low  = mmio_read32(TG3_MAC_ADDR_LOW);

                my_mac[0] = (mac_high >> 8) & 0xFF;
                my_mac[1] = mac_high & 0xFF;
                my_mac[2] = (mac_low >> 24) & 0xFF;
                my_mac[3] = (mac_low >> 16) & 0xFF;
                my_mac[4] = (mac_low >> 8) & 0xFF;
                my_mac[5] = mac_low & 0xFF;

                kprint("[+] Physical MAC Address: ");
                for(int i=0; i<6; i++) {
                    char hex[] = "0123456789ABCDEF";
                    char str[3] = { hex[(my_mac[i] >> 4) & 0xF], hex[my_mac[i] & 0xF], '\0' };
                    kprint(str); if (i < 5) kprint(":");
                }
                kprint("\r\n");

                // 4. Map TX Descriptor Ring in RAM
                tx_ring = (struct tg3_tx_desc*)TX_RING_ADDR;
                tx_bufs = (unsigned char*)TX_BUFS_ADDR;
                for(int i=0; i<NUM_DESC; i++) {
                    tx_ring[i].addr_high = 0; // 32-bit OS
                    tx_ring[i].addr_low = (unsigned int)(tx_bufs + (i * 2048));
                    tx_ring[i].len = 0;
                    tx_ring[i].flags = 0;
                    tx_ring[i].vlan_tag = 0;
                }
                tx_idx = 0;

                // 5. Map RX Descriptor Ring in RAM
                rx_ring = (struct tg3_rx_desc*)RX_RING_ADDR;
                rx_bufs = (unsigned char*)RX_BUFS_ADDR;
                for(int i=0; i<NUM_DESC; i++) {
                    rx_ring[i].addr_high = 0;
                    rx_ring[i].addr_low = (unsigned int)(rx_bufs + (i * 2048));
                    rx_ring[i].len = 0;
                    rx_ring[i].idx_flags = 0;
                }
                rx_idx = 0;

                kprint("[+] Tigon3 Mailbox DMA Rings Mapped in RAM.\r\n");
                return;
            }
        }
    }
    if (!found) kprint("[-] Broadcom silicon not found.\r\n");
}

void mod_BCMSEND(char* args) {
    if (bcm_mmio_base == 0) { kprint("?RUN BCMINIT FIRST\r\n"); return; }
    while (*args == ' ' || *args == '"') args++;

    unsigned char* buf = tx_bufs + (tx_idx * 2048);

    // 1. Clean the buffer to remove the "Ghost in the RAM" garbage!
    for(int i=0; i<2048; i++) buf[i] = 0;

    // 2. Build Ethernet Header
    for(int i=0; i<6; i++) buf[i] = 0xFF; // Broadcast MAC
    for(int i=0; i<6; i++) buf[6+i] = my_mac[i]; // Source Physical MAC
    buf[12] = 0x88; buf[13] = 0xB5; // Custom Bare Metal EtherType

    // 3. Copy Payload
    int payload_len = 0;
    while(args[payload_len] && args[payload_len] != '"' && payload_len < 1500) {
        buf[14 + payload_len] = args[payload_len];
        payload_len++;
    }

    int total_len = 14 + payload_len;
    if (total_len < 60) total_len = 60; // Min Ethernet frame size

    // 4. Configure Broadcom TX Buffer Descriptor
    tx_ring[tx_idx].len = total_len;
    // Packet End Flag (0x0004) tells the ASIC this BD is the end of the frame
    tx_ring[tx_idx].flags = 0x0004;

    // 5. Strike the Send Mailbox!
    tx_idx = (tx_idx + 1) % NUM_DESC;
    mmio_write32(TG3_SEND_MAILBOX, tx_idx);

    kprint("[+] Payload dropped in TX Mailbox. ASIC Notified.\r\n");
}

void mod_BCMRECV(char* args) {
    kprint("BCMRECV: Pending physical hardware validation.\r\n");
}