// Must be in same directory as driver files. (Uses __DIR__)
// Throws if stack has no driver for the PC's hardware.

#define PCIV_PCNET      0x1022
#define PCID_PCNET      0x2000

#define PCIV_E1000      0x8086
#define PCID_82545EM    0x100F

#define PCIV_VIRTIO     0x1AF4
#define PCID_VIRTIO_NET 0x1000

U0 NetDriverInclude(U8 *driver)
{
    U8 *filename = MStrPrint(__DIR__ "/%s", driver);

    ExeFile(filename);
    Free(filename);
}

class CNetDriver:CQueue
{
    U16 vendor_id;
    U16 device_id;
    U8 *filename; // relative to Drivers/ folder, not absolute
};

CQueue *net_drivers = CAlloc(sizeof(CQueue));
QueueInit(net_drivers);

U0 NetDriverRegister(U16 vendor_id=NULL, U16 device_id=NULL, U8 *filename)
{

    CNetDriver *driver;

    if (!vendor_id && !device_id)
        return;

    driver = CAlloc(sizeof(CNetDriver));

    driver->vendor_id = vendor_id;
    driver->device_id = device_id;
    driver->filename = StrNew(filename);

    QueueInsertRev(driver, net_drivers);
}


U0 NetDriverInit()
{
    Bool found = FALSE;
    CNetDriver *net_driver;
    CPCIDev *net_pci;

    // register NIC PCI details with driver (file)name
    NetDriverRegister(PCIV_VIRTIO,  PCID_VIRTIO_NET,    "VirtIONet");
    NetDriverRegister(PCIV_E1000,   PCID_82545EM,       "E1000");
    NetDriverRegister(PCIV_PCNET,   PCID_PCNET,         "PCNet");

    // iterate registered drivers until match is found, if any found
    net_driver = net_drivers->next;

    while (net_driver != net_drivers)
    {
        net_pci = PCIDevFind(,, net_driver->vendor_id, net_driver->device_id);
        if (net_pci)
        {
            found = TRUE;
            NetDriverInclude(net_driver->filename);
            break;
        }
        net_driver = net_driver->next;
    }

    if (!found)
    {
        ClassRep(net_pci);
        throw('NODRIVER');
    }
}

NetDriverInit;