| // 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; |
| } |
| } |