116 lines
2.8 KiB
C++
116 lines
2.8 KiB
C++
/*
|
|
* interrupt_manager.hpp
|
|
*
|
|
* Interrupt handling for PuzzleFW firmware.
|
|
*
|
|
* Joris van Rantwijk 2024
|
|
*/
|
|
|
|
#ifndef PUZZLEFW_INTERRUPT_MANAGER_H_
|
|
#define PUZZLEFW_INTERRUPT_MANAGER_H_
|
|
|
|
#include <functional>
|
|
#include <initializer_list>
|
|
#include <system_error>
|
|
#include <vector>
|
|
|
|
#include <boost/asio.hpp>
|
|
#include <boost/system/error_code.hpp>
|
|
|
|
#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 <typename Func>
|
|
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<std::function<void()>> m_callbacks;
|
|
};
|
|
|
|
|
|
} // namespace puzzlefw
|
|
|
|
#endif // PUZZLEFW_INTERRUPT_MANAGER_H_
|