#pragma once /// \file device_manager.h /// The device manager definition #include "kutil/vector.h" #include "apic.h" #include "hpet.h" #include "pci.h" struct acpi_table_header; class block_device; class endpoint; using irq_callback = void (*)(void *); /// Manager for all system hardware devices class device_manager { public: /// Constructor. device_manager(); /// Get the system global device manager. /// \returns A reference to the system device manager static device_manager & get() { return s_instance; } /// Get the LAPIC /// \returns An object representing the local APIC lapic * get_lapic() { return m_lapic; } /// Get an IOAPIC /// \arg i Index of the requested IOAPIC /// \returns An object representing the given IOAPIC if it exists, /// otherwise nullptr. ioapic * get_ioapic(int i); /// Parse ACPI tables. /// \arg root_table Pointer to the ACPI RSDP void parse_acpi(const void *root_table); /// Intialize drivers for the current device list. void init_drivers(); /// Bind an IRQ to an endpoint /// \arg irq The IRQ number to bind /// \arg target The endpoint to recieve messages when the IRQ is signalled /// \returns True on success bool bind_irq(unsigned irq, endpoint *target); /// Remove IRQ bindings for an endpoint /// \arg target The endpoint to remove void unbind_irqs(endpoint *target); /// Allocate an MSI IRQ for a device /// \arg name Name of the interrupt, for display to user /// \arg device Device this MSI is being allocated for /// \arg cb Callback to call when the interrupt is received /// \arg data Data to pass to the callback /// \returns True if an interrupt was allocated successfully bool allocate_msi( const char *name, pci_device &device, irq_callback cb, void *data); /// Dispatch an IRQ interrupt /// \arg irq The irq number of the interrupt /// \returns True if the interrupt was handled bool dispatch_irq(unsigned irq); /// Register the existance of a block device. /// \arg blockdev Pointer to the block device void register_block_device(block_device *blockdev); /// Get the number of block devices in the system /// \returns A count of devices inline unsigned get_num_block_devices() const { return m_blockdevs.count(); } /// Get a block device /// \arg i Index of the device to get /// \returns A pointer to the requested device, or nullptr inline block_device * get_block_device(unsigned i) { return i < m_blockdevs.count() ? m_blockdevs[i] : nullptr; } /// Get an HPET device /// \arg i Index of the device to get /// \returns A pointer to the requested device, or nullptr inline hpet * get_hpet(unsigned i) { return i < m_hpets.count() ? &m_hpets[i] : nullptr; } private: /// Parse the ACPI XSDT and load relevant sub-tables. /// \arg xsdt Pointer to the XSDT from the firmware void load_xsdt(const acpi_table_header *xsdt); /// Parse the ACPI MADT and initialize APICs from it. /// \arg apic Pointer to the MADT from the XSDT void load_apic(const acpi_table_header *apic); /// Parse the ACPI MCFG and initialize PCIe from it. /// \arg mcfg Pointer to the MCFG from the XSDT void load_mcfg(const acpi_table_header *mcfg); /// Parse the ACPI HPET and initialize an HPET from it. /// \arg hpet Pointer to the HPET from the XSDT void load_hpet(const acpi_table_header *hpet); /// Probe the PCIe busses and add found devices to our /// device list. The device list is destroyed and rebuilt. void probe_pci(); /// Handle a bad IRQ. Called when an interrupt is dispatched /// that has no callback. void bad_irq(uint8_t irq); lapic *m_lapic; kutil::vector m_ioapics; kutil::vector m_hpets; kutil::vector m_pci; kutil::vector m_devices; kutil::vector m_irqs; kutil::vector m_blockdevs; static device_manager s_instance; device_manager(const device_manager &) = delete; device_manager operator=(const device_manager &) = delete; };