/* * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. * * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. * Distributed under the terms of the NewOS License. */ #include #include #include #include #include #include #include #include //#define TRACE_INT #ifdef TRACE_INT # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif struct io_handler { struct io_handler *next; struct io_handler *prev; interrupt_handler func; void *data; bool use_enable_counter; }; struct io_vector { struct io_handler handler_list; spinlock vector_lock; int32 enable_count; }; static struct io_vector io_vectors[NUM_IO_VECTORS]; cpu_status disable_interrupts(void) { return arch_int_disable_interrupts(); } void restore_interrupts(cpu_status status) { arch_int_restore_interrupts(status); } bool interrupts_enabled(void) { return arch_int_are_interrupts_enabled(); } status_t int_init(kernel_args *args) { dprintf("init_int_handlers: entry\n"); return arch_int_init(args); } status_t int_init_post_vm(kernel_args *args) { int i; /* initialize the vector list */ for (i = 0; i < NUM_IO_VECTORS; i++) { io_vectors[i].vector_lock = 0; /* initialize spinlock */ io_vectors[i].enable_count = 0; initque(&io_vectors[i].handler_list); /* initialize handler queue */ } return arch_int_init_post_vm(args); } /** Install a handler to be called when an interrupt is triggered * for the given interrupt number with \a data as the argument. */ status_t install_io_interrupt_handler(long vector, interrupt_handler handler, void *data, ulong flags) { struct io_handler *io = NULL; cpu_status state; 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->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); return B_OK; } /** Remove a previously installed interrupt handler */ status_t remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data) { status_t status = B_BAD_VALUE; struct io_handler *io = NULL; cpu_status state; if (vector < 0 || vector >= NUM_IO_VECTORS) return B_BAD_VALUE; /* lock the structures down so it is not modified while we search */ state = disable_interrupts(); acquire_spinlock(&io_vectors[vector].vector_lock); /* loop through the available handlers and try to find a match. * We go forward through the list but this means we start with the * most recently added handlers. */ for (io = io_vectors[vector].handler_list.next; io != &io_vectors[vector].handler_list; io = io->next) { /* we have to match both function and data */ if (io->func == handler && io->data == data) { 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); status = B_OK; break; } } release_spinlock(&io_vectors[vector].vector_lock); restore_interrupts(state); // if the handler could be found and removed, we still have to free it if (status == B_OK) free(io); return status; } /** actually process an interrupt via the handlers registered for that * vector (irq) */ int int_io_interrupt_handler(int vector) { int status = B_UNHANDLED_INTERRUPT; struct io_handler *io; acquire_spinlock(&io_vectors[vector].vector_lock); // The list can be empty at this place if (io_vectors[vector].handler_list.next == &io_vectors[vector].handler_list) { dprintf("unhandled io interrupt %d\n", vector); release_spinlock(&io_vectors[vector].vector_lock); return B_UNHANDLED_INTERRUPT; } /* Loop through the list of handlers. * each handler returns as follows... * - B_UNHANDLED_INTERRUPT, the interrupt wasn't processed by the * fucntion, so try the next available. * - B_HANDLED_INTERRUPT, the interrupt has been handled and no further * attention is required * - B_INVOKE_SCHEDULER, the interrupt has been handled, but the function wants * the scheduler to be invoked * * This is a change of behaviour from newos where every handler registered * be called, even if the interrupt had been "handled" by a previous * function. * The logic now is that if there are no handlers then we return * B_UNHANDLED_INTERRUPT and let the system do as it will. * When we have the first function that claims to have "handled" the * interrupt, by returning B_HANDLED_... or B_INVOKE_SCHEDULER we simply * stop calling further handlers and return the value from that * handler. * This may not be correct but appears to be what BeOS did and seems * right. * * ToDo: we might want to reenable calling all registered handlers depending * on a flag somewhere, so that we can deal with buggy drivers */ 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) { if ((status = io->func(io->data)) != B_UNHANDLED_INTERRUPT) break; } release_spinlock(&io_vectors[vector].vector_lock); return status; }