blob: 70fb4e8b8bda977157eefb54c7f3cce0bbe6957e [file] [edit]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_INTERRUPT_DISPATCHER_H_
#define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_INTERRUPT_DISPATCHER_H_
#include <lib/wake-vector.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <sys/types.h>
#include <zircon/rights.h>
#include <zircon/types.h>
#include <kernel/event.h>
#include <kernel/spinlock.h>
#include <object/dispatcher.h>
#include <object/port_dispatcher.h>
enum class InterruptState {
WAITING = 0,
DESTROYED = 1,
TRIGGERED = 2,
NEEDACK = 3,
IDLE = 4,
};
// Note that unlike most Dispatcher subclasses, this one is further
// subclassed, and so cannot be final.
class InterruptDispatcher : public SoloDispatcher<InterruptDispatcher, ZX_DEFAULT_INTERRUPT_RIGHTS>,
public wake_vector::WakeVector {
public:
InterruptDispatcher& operator=(const InterruptDispatcher&) = delete;
zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_INTERRUPT; }
bool is_wake_vector() const { return flags_ & INTERRUPT_WAKE_VECTOR; }
virtual zx_status_t WaitForInterrupt(zx_time_t* out_timestamp);
virtual zx_status_t Trigger(zx_time_t timestamp);
virtual zx_status_t Ack();
virtual zx_status_t Destroy();
void InterruptHandler();
zx_status_t Bind(fbl::RefPtr<PortDispatcher> port_dispatcher, uint64_t key);
zx_status_t Unbind(fbl::RefPtr<PortDispatcher> port_dispatcher);
void on_zero_handles() final;
// Default wake vector diagnostics for interrupt dispatchers.
void GetDiagnostics(WakeVector::Diagnostics& diagnostics_out) const override {
diagnostics_out.enabled = false;
diagnostics_out.koid = get_koid();
}
// Returns information about this interrupt in a zx_object_get_info call.
zx_info_interrupt_t GetInfo() const;
protected:
virtual void MaskInterrupt() = 0;
virtual void UnmaskInterrupt() = 0;
virtual void DeactivateInterrupt() = 0;
virtual void UnregisterInterruptHandler() = 0;
enum Flags : uint32_t {
// The interrupt is virtual.
INTERRUPT_VIRTUAL = (1u << 0),
// The interrupt should be unmasked before waiting on the event.
//
// Mutually exclusive with INTERRUPT_UNMASK_PREWAIT_UNLOCKED.
INTERRUPT_UNMASK_PREWAIT = (1u << 1),
// The same as |INTERRUPT_UNMASK_PREWAIT| except release the dispatcher
// spinlock before waiting.
//
// Mutually exclusive with INTERRUPT_UNMASK_PREWAIT.
INTERRUPT_UNMASK_PREWAIT_UNLOCKED = (1u << 2),
// The interrupt should be masked following waiting.
INTERRUPT_MASK_POSTWAIT = (1u << 4),
// The interrupt may wake the system from suspend.
INTERRUPT_WAKE_VECTOR = (1u << 5),
// Allow kernel tests to call Ack() without binding to a port.
INTERRUPT_ALLOW_ACK_WITHOUT_PORT_FOR_TEST = (1u << 6),
// The interrupt should use monotonic timestamps instead of the default,
// which is boot timestamps.
INTERRUPT_TIMESTAMP_MONO = (1u << 7),
};
enum PostAckState {
FullyAcked, // The interrupt was acked, and there was no other interrupt pending afterwards.
Retriggered, // The interrupt was acked, but immediately re-triggered due to a pending irq.
};
// It is an error to specify both INTERRUPT_UNMASK_PREWAIT and INTERRUPT_UNMASK_PREWAIT_UNLOCKED.
explicit InterruptDispatcher(Flags flags) : InterruptDispatcher(flags, 0) {}
explicit InterruptDispatcher(Flags flags, uint32_t options);
void Signal() { event_.Signal(); }
bool SendPacketLocked(zx_time_t timestamp) TA_REQ(spinlock_);
zx::result<InterruptDispatcher::PostAckState> AckInternal() TA_EXCL(spinlock_);
// Allow subclasses to add/remove the wake event instance from the global diagnostics list at the
// appropriate times during initialization and teardown. These methods do not need to be called
// unless the subclass needs to be able to wake the system from suspend. See lib/wake-vector.h for
// the specific requirements regarding when to initialize and destroy the wake event.
void InitializeWakeEvent() TA_REQ(spinlock_) { wake_event_.Initialize(); }
void DestroyWakeEvent() TA_REQ(spinlock_) { wake_event_.Destroy(); }
// The zx_interrupt_wait operation is broken down into two phases. First, we
// BeginWaitForInterrupt, which will handle internal bookkeeping and either return a status code
// if the wait operation is finished (success or failure), or it will return nothing if we need to
// block and try again later.
//
// Afterwards, if we need to block, we will call DoWaitForInterruptBlock and try again if the
// block operation succeeds.
ktl::optional<zx_status_t> BeginWaitForInterrupt(zx_time_t* out_timestamp) TA_EXCL(spinlock_);
zx_status_t DoWaitForInterruptBlock() TA_EXCL(spinlock_);
private:
AutounsignalEvent event_;
zx_time_t timestamp_ TA_GUARDED(spinlock_);
const Flags flags_;
const uint32_t options_;
// Current state of the interrupt object
InterruptState state_ TA_GUARDED(spinlock_);
PortInterruptPacket port_packet_ TA_GUARDED(spinlock_) = {};
fbl::RefPtr<PortDispatcher> port_dispatcher_ TA_GUARDED(spinlock_);
wake_vector::WakeEvent wake_event_ TA_GUARDED(spinlock_);
// Controls the access to Interrupt properties
DECLARE_SPINLOCK(InterruptDispatcher) spinlock_;
};
#endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_INTERRUPT_DISPATCHER_H_