From 5502e596dc065a20d10a8a53e73c146a9812ace1 Mon Sep 17 00:00:00 2001 From: Hugo Santos Date: Sun, 6 May 2007 15:07:56 +0000 Subject: [PATCH] added io_interrupt_handler methods for dynamic handling of IO interrupts, including enabling/disabling without allocation and in interrupt context. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21040 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/drivers/KernelExport.h | 12 +++ src/system/kernel/int.c | 130 ++++++++++++++++++++++-------- 2 files changed, 109 insertions(+), 33 deletions(-) diff --git a/headers/os/drivers/KernelExport.h b/headers/os/drivers/KernelExport.h index 076821ff7f..c54033dbef 100644 --- a/headers/os/drivers/KernelExport.h +++ b/headers/os/drivers/KernelExport.h @@ -54,6 +54,18 @@ extern status_t remove_io_interrupt_handler(long interrupt_number, interrupt_handler handler, void *data); +/* Private, Haiku specific */ + +/* Flags that can be passwd to disable_io_interrupt_handle() */ +#define B_IN_INTERRUPT_CONTEXT 1 + +extern status_t create_io_interrupt_handler(long interrupt_number, + interrupt_handler handler, void *data, void **context); +void enable_io_interrupt_handler(void *context); +void disable_io_interrupt_handler(void *context, uint32 flags); +void delete_io_interrupt_handler(void *context); + + /*-------------------------------------------------------------*/ /* timer interrupts services */ diff --git a/src/system/kernel/int.c b/src/system/kernel/int.c index cb76711752..33e1380498 100644 --- a/src/system/kernel/int.c +++ b/src/system/kernel/int.c @@ -31,6 +31,7 @@ struct io_handler { struct io_handler *next; struct io_handler *prev; + long vector; interrupt_handler func; void *data; bool use_enable_counter; @@ -144,40 +145,16 @@ int_init_post_device_manager(kernel_args *args) status_t install_io_interrupt_handler(long vector, interrupt_handler handler, void *data, ulong flags) { - struct io_handler *io = NULL; - cpu_status state; + struct io_handler *io; + status_t status; - if (vector < 0 || vector >= NUM_IO_VECTORS) - return B_BAD_VALUE; + status = create_io_interrupt_handler(vector, handler, data, (void **)&io); + if (status < B_OK) + return status; - io = (struct io_handler *)malloc(sizeof(struct io_handler)); - if (io == NULL) - return B_NO_MEMORY; - - arch_debug_remove_interrupt_handler(vector); - // There might be a temporary debug interrupt installed on this - // vector that should be removed now. - - io->func = handler; - io->data = data; io->use_enable_counter = (flags & B_NO_ENABLE_COUNTER) == 0; - // Disable the interrupts, get the spinlock for this irq only - // and then insert the handler - state = disable_interrupts(); - acquire_spinlock(&io_vectors[vector].vector_lock); - - insque(io, &io_vectors[vector].handler_list); - - // If B_NO_ENABLE_COUNTER is set, we're being asked to not alter - // whether the interrupt should be enabled or not - if (io->use_enable_counter) { - if (io_vectors[vector].enable_count++ == 0) - arch_int_enable_io_interrupt(vector); - } - - release_spinlock(&io_vectors[vector].vector_lock); - restore_interrupts(state); + enable_io_interrupt_handler(io); return B_OK; } @@ -230,6 +207,90 @@ remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data) } +status_t +create_io_interrupt_handler(long vector, interrupt_handler handler, void *data, + void **context) +{ + struct io_handler *io = NULL; + + if (vector < 0 || vector >= NUM_IO_VECTORS) + return B_BAD_VALUE; + + io = (struct io_handler *)malloc(sizeof(struct io_handler)); + if (io == NULL) + return B_NO_MEMORY; + + io->vector = vector; + io->func = handler; + io->data = data; + io->use_enable_counter = true; + + *context = io; + return B_OK; +} + + +void +enable_io_interrupt_handler(void *context) +{ + struct io_handler *io = context; + long vector = io->vector; + cpu_status state; + + // Disable the interrupts, get the spinlock for this irq only + // and then insert the handler + state = disable_interrupts(); + acquire_spinlock(&io_vectors[vector].vector_lock); + + insque(io, &io_vectors[vector].handler_list); + + // If B_NO_ENABLE_COUNTER is set, we're being asked to not alter + // whether the interrupt should be enabled or not + if (io->use_enable_counter) { + if (io_vectors[vector].enable_count++ == 0) + arch_int_enable_io_interrupt(vector); + } + + release_spinlock(&io_vectors[vector].vector_lock); + restore_interrupts(state); +} + + +void +disable_io_interrupt_handler(void *context, uint32 flags) +{ + struct io_handler *io = context; + long vector = io->vector; + cpu_status state = 0; + + if ((flags & B_IN_INTERRUPT_CONTEXT) == 0) { + /* lock the structures down so it is not modified while we search */ + state = disable_interrupts(); + acquire_spinlock(&io_vectors[vector].vector_lock); + } + + remque(io); + + // Check if we need to disable the interrupt + if (io->use_enable_counter && --io_vectors[vector].enable_count == 0) + arch_int_disable_io_interrupt(vector); + + if ((flags & B_IN_INTERRUPT_CONTEXT) == 0) { + release_spinlock(&io_vectors[vector].vector_lock); + restore_interrupts(state); + } +} + + +void +delete_io_interrupt_handler(void *context) +{ + struct io_handler *io = context; + + free(io); +} + + /** actually process an interrupt via the handlers registered for that * vector (irq) */ @@ -259,9 +320,10 @@ int_io_interrupt_handler(int vector, bool levelTriggered) * whatever the driver thought would be useful (ie. B_INVOKE_SCHEDULER) */ - for (io = io_vectors[vector].handler_list.next; - io != &io_vectors[vector].handler_list; // Are we already at the end of the list? - io = io->next) { + io = io_vectors[vector].handler_list.next; + while (io != &io_vectors[vector].handler_list) { + struct io_handler *next = io->next; + status = io->func(io->data); if (levelTriggered && status != B_UNHANDLED_INTERRUPT) @@ -271,6 +333,8 @@ int_io_interrupt_handler(int vector, bool levelTriggered) handled = true; else if (status == B_INVOKE_SCHEDULER) invokeScheduler = true; + + io = next; } #ifdef DEBUG_INT