/*  Intel(R) E1000 Driver
    Author: TomAwezome

    Driver is based on:
    -   01000101's example i825xx driver
    -   OSDev Intel(R) 8254x documentation
    -   Intel(R) PCI/PCI-X Family of Gigabit Ethernet Controllers Software Developer's Manual
    -   Linux E1000 driver
    -   any other useful sources.

    Guidelines:
    -   Magic numbers are bad. #defines are good.
    -   Understandability over LOC.
    -   Clear documentation.
*/

// TODO: clean up entire driver

#define E1000_REG_CTRL  0x0000
#define E1000_REG_EERD  0x0014  // EEPROM Read
#define E1000_REG_IMS   0x00D0
#define E1000_REG_MTA   0x5200  // Multicast Table Array

#define E1000_CTRLf_SLU 6 // Set Link Up ?

#define E1000_CTRLF_SLU (1 << E1000_CTRLf_SLU) // Set Link Up ?

class CE1000
{
    CPCIDev *pci;

    U8  mac_address[6];
    U64 mmio_address;

} e1000; // e1000 is the global variable we store all of this into.

CPCIDev *E1000PCIDevFind()
{// Find and return E1000 card as a CPCIDev pointer.

    CPCIDev *pci = PCIDevFind(PCIC_NETWORK,, PCIV_E1000);

    if (!pci)
        return NULL;

    ClassRep(pci);

    switch (pci->device_id)
    {
        case PCID_82545EM:
            break;

        default:
            pci = NULL;
    }

    return pci;
}

U32 E1000MMIORead(U32 offset)
{
    U32 *val = e1000.mmio_address + offset;

    return *val;
}

U0 E1000MMIOWrite(U32 offset, U32 val)
{
    U32 *addr = e1000.mmio_address + offset;

    *addr = val;
}

U16 E1000EEPROMRead(U8 word)
{ // word arg is which U16 to read
    U16 data;
    U32 temp;

    E1000MMIOWrite(E1000_REG_EERD, 1 | word << 8);

    while (!((temp = E1000MMIORead(E1000_REG_EERD)) & (1 << 4)))
        Sleep(1);

    data = (temp >> 16) & 0xFFFF;
    return data;
}

U0 E1000MACGet()
{
    I64 i;
    U16 mac;

    NetLog("E1000 GET MAC: Getting VM MAC.");

    for (i = 0; i < 3; i++)
    {
        mac = E1000EEPROMRead(i);
        e1000.mac_address[2*i]   = mac & 0xFF;
        e1000.mac_address[2*i+1] = (mac >> 8) & 0xFF;
        NetLog(" %02X %02X", mac.u8[0], mac.u8[1]);
    }
    
}

U0 EthernetFrameFinish(I64 de_index)
{//Alias for driver Finish TX function.
    //E1000TransmitPacketFinish(de_index);
    no_warn de_index;
    NetErr("TODO E1000");
}

/*
U0 PCIInterruptsReroute(I64 base)
{ // todo: comments explaining process, maybe better var names
    I64  i;
    U8  *da = dev.uncached_alias + IOAPIC_REG;
    U32 *_d = dev.uncached_alias + IOAPIC_DATA;

    for (i = 0; i < 4; i++)
    {
        *da = IOREDTAB + i * 2 + 1;
        *_d = dev.mp_apic_ids[INT_DEST_CPU] << 24;
        *da = IOREDTAB + i * 2;
        *_d = 0x4000 + base + i;
    }
}


U0 E1000InterruptsSetup()
{
//  PCIInterruptsReroute(I_E1000);
    NetErr("TODO E1000");
}
*/

U0 E1000Init()
{
    I64 i;

    MemSet(&e1000, 0, sizeof(CE1000)); // e1000 global var will hold member data the driver uses often.
    "\nE1000 driver WIP\n\n";

    e1000.pci = E1000PCIDevFind;
    if (!e1000.pci)
        return; // if we don't find the card, quit.


    e1000.mmio_address = dev.uncached_alias + e1000.pci->base[0] & ~0xF;
    // Assuming card supports MMIO... lower 4 bits are hardwired zero (?)

    "\nMMIO address: 0x%0X\n", e1000.mmio_address;

    // init rx/tx addrs? (linux)

    // eeprom? MAC ?
    E1000MACGet;

    // setup link? (01000101's driver)
    E1000MMIOWrite(E1000_REG_CTRL, E1000MMIORead(E1000_REG_CTRL) | E1000_CTRLF_SLU);

    // zero out multicast hash? (linux)
    // zero out multicast table array (01000101's driver)
    for (i = 0; i < 128; i++)
        E1000MMIOWrite(E1000_REG_MTA + i*4, 0);

    // setup link? (linux)

    // clear all statistics regs after link establish attempt (linux)

    // enable & clear existing interupts (01000101's driver)
    E1000MMIOWrite(E1000_REG_IMS, 0x1F6DC); // TODO: WHAT IS THIS
    E1000MMIORead(0xC0); // TODO : WHAT IS THIS ???

    // start rx tx?


    NetErr("TODO E1000");
}

I64 EthernetFrameAllocate(U8 **packet_buffer_out,
                          U8 *source_address,
                          U8 *destination_address,
                          U16 ethertype,
                          I64 packet_length)
{
    no_warn packet_buffer_out, source_address, destination_address, ethertype, packet_length;
    NetErr("TODO E1000");
    return -1;
}

U8 *EthernetMACGet()
{
    return e1000.mac_address;
}

U0 NetStop()
{

}

U0 NetStart()
{

}

E1000Init;