#ifndef __PCIDEVICE_H
#define __PCIDEVICE_H

#include "types.h" // u32
#include "list.h" // hlist_node

struct pci_device {
    u16 bdf;
    u8 rootbus;
    struct hlist_node node;
    struct pci_device *parent;

    // Configuration space device information
    u16 vendor, device;
    u16 class;
    u8 prog_if, revision;
    u8 header_type;
    u8 secondary_bus;

    // Local information on device.
    int have_driver;
};
extern struct hlist_head PCIDevices;
extern int MaxPCIBus;

static inline u32 pci_classprog(struct pci_device *pci) {
    return (pci->class << 8) | pci->prog_if;
}

#define foreachpci(PCI)                                 \
    hlist_for_each_entry(PCI, &PCIDevices, node)

#define PCI_ANY_ID      (~0)
struct pci_device_id {
    u32 vendid;
    u32 devid;
    u32 class;
    u32 class_mask;
    void (*func)(struct pci_device *pci, void *arg);
};

#define PCI_DEVICE(vendor_id, device_id, init_func)     \
    {                                                   \
        .vendid = (vendor_id),                          \
        .devid = (device_id),                           \
        .class = PCI_ANY_ID,                            \
        .class_mask = 0,                                \
        .func = (init_func)                             \
    }

#define PCI_DEVICE_CLASS(vendor_id, device_id, class_code, init_func)   \
    {                                                                   \
        .vendid = (vendor_id),                                          \
        .devid = (device_id),                                           \
        .class = (class_code),                                          \
        .class_mask = ~0,                                               \
        .func = (init_func)                                             \
    }

#define PCI_DEVICE_END                          \
    {                                           \
        .vendid = 0,                            \
    }

void pci_probe_devices(void);
struct pci_device *pci_find_device(u16 vendid, u16 devid);
struct pci_device *pci_find_class(u16 classid);
int pci_init_device(const struct pci_device_id *ids
                    , struct pci_device *pci, void *arg);
struct pci_device *pci_find_init_device(const struct pci_device_id *ids
                                        , void *arg);
void pci_enable_busmaster(struct pci_device *pci);
u16 pci_enable_iobar(struct pci_device *pci, u32 addr);
void *pci_enable_membar(struct pci_device *pci, u32 addr);

#endif // pcidevice.h
