/* * interrupt_manager.hpp * * Interrupt handling for PuzzleFW firmware. * * Joris van Rantwijk 2024 */ #ifndef PUZZLEFW_INTERRUPT_MANAGER_H_ #define PUZZLEFW_INTERRUPT_MANAGER_H_ #include #include #include #include #include #include #include "puzzlefw.hpp" namespace puzzlefw { namespace asio = boost::asio; /** * Handle FPGA interrupts via the UIO file descriptor. * * When an interrupt occurs, a list of callback functions are invoked. */ class InterruptManager { public: /** Start handling interrupts. */ InterruptManager(asio::io_context& io, PuzzleFwDevice& device) : m_device(device) , m_uio_fd(io, device.uio_fd()) { // Enable FPGA interrupts. m_device.enable_irq(); // Start asynchronous wait on the UIO file descriptor. m_uio_fd.async_wait(asio::posix::descriptor_base::wait_read, [this](auto& ec){ handle_wait(ec); }); } // Delete copy constructor and assignment operator. InterruptManager(const InterruptManager&) = delete; InterruptManager& operator=(const InterruptManager&) = delete; /** Destructor. */ ~InterruptManager() { // Cancel asynchronous wait. m_uio_fd.cancel(); // Release UIO file descriptor (without closing it). m_uio_fd.release(); } /** * Add callback function to invoke when an interrupt occurs. * * The callback functions are (together) responsible for disabling * and clearing all pending interrupts. */ template void add_callback(Func&& func) { m_callbacks.emplace_back(func); } private: /** Called when an FPGA interrupt occurs. */ void handle_wait(boost::system::error_code error) { if (error) { // Ignore error due to cancellation. if (error == asio::error::operation_aborted) { return; } // Raise exception on unexpected error. throw std::system_error(error); } // Read from file descriptor to clear interrupt. char buf[4]; ::read(m_uio_fd.native_handle(), buf, 4); // Invoke callbacks. for (const auto& callback : m_callbacks) { callback(); } // Re-enable FPGA interrupts. m_device.enable_irq(); // Wait for next interrupt. m_uio_fd.async_wait(asio::posix::descriptor_base::wait_read, [this](auto ec){ handle_wait(ec); }); } PuzzleFwDevice& m_device; asio::posix::stream_descriptor m_uio_fd; std::vector> m_callbacks; }; } // namespace puzzlefw #endif // PUZZLEFW_INTERRUPT_MANAGER_H_