blob: fb19303f9b3ae6d7a3f56ee489cba5b096d72284 [file] [log] [blame]
// 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
#include <object/interrupt_event_dispatcher.h>
#include <kernel/auto_lock.h>
#include <dev/interrupt.h>
#include <zircon/rights.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include <err.h>
// Static storage
fbl::Mutex InterruptEventDispatcher::vectors_lock_;
InterruptEventDispatcher::VectorCollection InterruptEventDispatcher::vectors_;
// static
zx_status_t InterruptEventDispatcher::Create(uint32_t vector,
uint32_t flags,
fbl::RefPtr<Dispatcher>* dispatcher,
zx_rights_t* rights) {
// Remap the vector if we have been asked to do so.
if (flags & ZX_FLAG_REMAP_IRQ)
vector = remap_interrupt(vector);
// If this is not a valid interrupt vector, fail.
if (!is_valid_interrupt(vector, 0))
return ZX_ERR_INVALID_ARGS;
// Attempt to construct the dispatcher.
fbl::AllocChecker ac;
InterruptEventDispatcher* disp = new (&ac) InterruptEventDispatcher(vector);
if (!ac.check())
return ZX_ERR_NO_MEMORY;
// Hold a ref while we check to see if someone else owns this vector or not.
// If things go wrong, this ref will be released and the IED will get
// cleaned up automatically.
auto disp_ref = fbl::AdoptRef<Dispatcher>(disp);
// Attempt to add ourselves to the vector collection.
{
fbl::AutoLock lock(&vectors_lock_);
if (!vectors_.insert_or_find(disp))
return ZX_ERR_ALREADY_EXISTS;
}
// Looks like things went well. Register our callback and unmask our
// interrupt.
register_int_handler(vector, IrqHandler, disp);
unmask_interrupt(vector);
// Transfer control of the new dispatcher to the creator and we are done.
*rights = ZX_DEFAULT_INTERRUPT_RIGHTS;
*dispatcher = fbl::move(disp_ref);
return ZX_OK;
}
InterruptEventDispatcher::~InterruptEventDispatcher() {
// If we were successfully instantiated, then we must exist in the vector
// collection. Unconditionally mask our vector, clear out our handler and
// remove ourselves from the collection bookkeeping (allowing others to
// claim the vector if they desire).
if (wavl_node_state_.InContainer()) {
mask_interrupt(vector_);
register_int_handler(vector_, nullptr, nullptr);
{
fbl::AutoLock lock(&vectors_lock_);
vectors_.erase(*this);
}
}
}
zx_status_t InterruptEventDispatcher::InterruptComplete() {
canary_.Assert();
unsignal();
unmask_interrupt(vector_);
return ZX_OK;
}
zx_status_t InterruptEventDispatcher::UserSignal() {
canary_.Assert();
mask_interrupt(vector_);
signal(true);
return ZX_OK;
}
enum handler_return InterruptEventDispatcher::IrqHandler(void* ctx) {
InterruptEventDispatcher* thiz = reinterpret_cast<InterruptEventDispatcher*>(ctx);
// TODO(johngro): make sure that this is safe to do from an IRQ.
mask_interrupt(thiz->vector_);
if (thiz->signal() > 0) {
return INT_RESCHEDULE;
} else {
return INT_NO_RESCHEDULE;
}
}