haiku/src/kernel/core/int.c
beveloper 8271683e44 backport a bugfix from newos:
another terrible bug that managed to exist this long and not clobber everything.
  The io vector table was being created with an incorrect size, so all sorts of garbage was getting written to whomever was allocated after it.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@1112 a95241bf-73f2-0310-859d-f6bbb57e9c96
2002-09-22 01:00:26 +00:00

222 lines
6.4 KiB
C

/*
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
#include <kernel.h>
#include <int.h>
#include <debug.h>
#include <memheap.h>
#include <smp.h>
#include <arch/int.h>
#include <errno.h>
#include <stage2.h>
#include <string.h>
#include <stdio.h>
#define NUM_IO_VECTORS 256
struct io_handler {
struct io_handler *next;
struct io_handler *prev;
interrupt_handler func;
void* data;
};
struct io_vector {
struct io_handler handler_list;
spinlock_t vector_lock;
};
static struct io_vector *io_vectors = NULL;
int
int_init(kernel_args *ka)
{
dprintf("init_int_handlers: entry\n");
return arch_int_init(ka);
}
int
int_init2(kernel_args *ka)
{
io_vectors = (struct io_vector *)kmalloc(sizeof(struct io_vector) * NUM_IO_VECTORS);
if (io_vectors == NULL)
panic("int_init2: could not create io vector table!\n");
memset(io_vectors, 0, sizeof(struct io_vector) * NUM_IO_VECTORS);
return arch_int_init2(ka);
}
/* install_interrupt_handler
* This function is used internally to install a handler on the given vector.
* NB this does NOT take an IRQ, but a system interrupt value.
* As this is intended for system use this function doe NOT call
* arch_int_enable_io_interrupt() as it only works for IRQ values
*/
long install_interrupt_handler(long vector, interrupt_handler handler,
void* data)
{
struct io_handler *io = NULL;
int state;
/* find the chain of handlers for this irq.
* NB there can be multiple handlers for the same IRQ, especially for
* PCI drivers. Where we have multiple handlers we will call each in turn
* until one returns a value other than B_UNHANDLED_INTERRUPT.
*/
io = (struct io_handler *)kmalloc(sizeof(struct io_handler));
if (io == NULL)
return ENOMEM;
io->func = handler;
io->data = data;
/* Make sure our list is init'd or bad things will happen */
if (io_vectors[vector].handler_list.next == NULL) {
io_vectors[vector].handler_list.next = &io_vectors[vector].handler_list;
io_vectors[vector].handler_list.prev = &io_vectors[vector].handler_list;
}
/* 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);
release_spinlock(&io_vectors[vector].vector_lock);
restore_interrupts(state);
return 0;
}
/* install_io_interrupt_handler
* install a handler to be called when an interrupt is triggered
* for the given irq with data as the argument
*/
long install_io_interrupt_handler(long irq, interrupt_handler handler,
void* data, ulong flags)
{
long vector = irq + 0x20;
long rv = install_interrupt_handler(vector, handler, data);
if (rv != 0)
return rv;
/* If we were passed the bit-flag B_NO_ENABLE_COUNTER then
* we're being asked to not alter whether the interrupt is set
* regardless of setting.
*/
if ((flags & B_NO_ENABLE_COUNTER) == 0)
arch_int_enable_io_interrupt(irq);
return 0;
}
/* remove_interrupt_handler
* read notes for install_interrupt_handler
*/
long remove_interrupt_handler(long vector, interrupt_handler handler,
void* data)
{
struct io_handler *io = NULL;
long rv = EINVAL;
/* lock the structures down so it is not modified while we search */
int 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);
kfree(io);
rv = 0;
break;
}
}
/* to finish we need to release our locks and return
* the value rv
*/
release_spinlock(&io_vectors[vector].vector_lock);
restore_interrupts(state);
return rv;
}
/* remove_io_interrupt_handler
* remove an interrupt handler previously inserted
*/
long remove_io_interrupt_handler(long irq, interrupt_handler handler,
void* data)
{
long vector = irq + 0x20;
long rv = remove_interrupt_handler(vector, handler, data);
if (rv < 0)
return rv;
/* Check if we need to disable interrupts... */
if (io_vectors[vector].handler_list.next != &io_vectors[vector].handler_list)
arch_int_disable_io_interrupt(irq);
return 0;
}
/* int_io_interrupt_handler
* actually process an interrupt via the handlers registered for that
* vector (irq)
*/
int int_io_interrupt_handler(int vector)
{
int ret = B_UNHANDLED_INTERRUPT;
acquire_spinlock(&io_vectors[vector].vector_lock);
if (io_vectors[vector].handler_list.next == &io_vectors[vector].handler_list) {
dprintf("unhandled io interrupt %d\n", vector);
} else {
struct io_handler *io;
/* 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
*
* XXX - 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.
*/
for (io = io_vectors[vector].handler_list.next;
io != &io_vectors[vector].handler_list;
io = io->next) {
if ((ret = io->func(io->data)) != B_UNHANDLED_INTERRUPT)
break;
}
}
release_spinlock(&io_vectors[vector].vector_lock);
return ret;
}