2002-07-09 16:24:59 +04:00
|
|
|
/*
|
|
|
|
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
|
|
|
** Distributed under the terms of the NewOS License.
|
|
|
|
*/
|
2002-07-10 19:48:19 +04:00
|
|
|
|
2002-07-10 17:08:47 +04:00
|
|
|
#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>
|
2002-07-09 16:24:59 +04:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#define NUM_IO_VECTORS 256
|
|
|
|
|
|
|
|
struct io_handler {
|
2002-07-19 20:07:36 +04:00
|
|
|
struct io_handler *next;
|
|
|
|
struct io_handler *prev;
|
|
|
|
interrupt_handler func;
|
2002-07-18 19:03:13 +04:00
|
|
|
void* data;
|
2002-07-09 16:24:59 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct io_vector {
|
2002-07-19 20:07:36 +04:00
|
|
|
struct io_handler handler_list;
|
|
|
|
spinlock_t vector_lock;
|
2002-07-09 16:24:59 +04:00
|
|
|
};
|
|
|
|
|
2002-07-19 20:07:36 +04:00
|
|
|
static struct io_vector *io_vectors = NULL;
|
2002-07-18 19:03:13 +04:00
|
|
|
|
|
|
|
int
|
|
|
|
int_init(kernel_args *ka)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-07-18 19:03:13 +04:00
|
|
|
dprintf("init_int_handlers: entry\n");
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-18 19:03:13 +04:00
|
|
|
return arch_int_init(ka);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-18 19:03:13 +04:00
|
|
|
|
|
|
|
int
|
|
|
|
int_init2(kernel_args *ka)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-07-18 19:03:13 +04:00
|
|
|
io_vectors = (struct io_vector *)kmalloc(sizeof(struct io_vectors *) * NUM_IO_VECTORS);
|
|
|
|
if (io_vectors == NULL)
|
|
|
|
panic("int_init2: could not create io vector table!\n");
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-18 19:03:13 +04:00
|
|
|
memset(io_vectors, 0, sizeof(struct io_vector *) * NUM_IO_VECTORS);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-18 19:03:13 +04:00
|
|
|
return arch_int_init2(ka);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
/* 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)
|
|
|
|
{
|
2002-07-19 20:07:36 +04:00
|
|
|
struct io_handler *io = NULL;
|
2002-07-19 22:33:41 +04:00
|
|
|
int state;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
dprintf("install_interrupt_handler: vector %ld\n", vector);
|
|
|
|
|
2002-07-19 20:07:36 +04:00
|
|
|
/* 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.
|
|
|
|
*/
|
2002-07-18 19:03:13 +04:00
|
|
|
io = (struct io_handler *)kmalloc(sizeof(struct io_handler));
|
|
|
|
if (io == NULL)
|
|
|
|
return ENOMEM;
|
2002-07-19 20:07:36 +04:00
|
|
|
io->func = handler;
|
2002-07-18 19:03:13 +04:00
|
|
|
io->data = data;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-19 20:07:36 +04:00
|
|
|
/* Make sure our list is init'd or bad things will happen */
|
2002-07-19 22:33:41 +04:00
|
|
|
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;
|
2002-07-19 20:07:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable the interrupts, get the spinlock for this irq only
|
|
|
|
* and then insert the handler */
|
|
|
|
state = int_disable_interrupts();
|
2002-07-19 22:33:41 +04:00
|
|
|
acquire_spinlock(&io_vectors[vector].vector_lock);
|
|
|
|
insque(io, &io_vectors[vector].handler_list);
|
|
|
|
release_spinlock(&io_vectors[vector].vector_lock);
|
2002-07-18 19:03:13 +04:00
|
|
|
int_restore_interrupts(state);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
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;
|
|
|
|
dprintf("install_io_interrupt_handler: irq %ld\n", irq);
|
|
|
|
rv = install_interrupt_handler(vector, handler, data);
|
|
|
|
|
|
|
|
if (rv != 0)
|
|
|
|
return rv;
|
|
|
|
|
2002-07-19 20:07:36 +04:00
|
|
|
/* 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);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-18 19:03:13 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
/* remove_interrupt_handler
|
|
|
|
* read notes for install_interrupt_handler
|
2002-07-19 20:07:36 +04:00
|
|
|
*/
|
2002-07-19 22:33:41 +04:00
|
|
|
long remove_interrupt_handler(long vector, interrupt_handler handler,
|
|
|
|
void* data)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-07-19 20:07:36 +04:00
|
|
|
struct io_handler *io = NULL;
|
2002-07-18 19:03:13 +04:00
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
/* lock the structures down so it is not modified while we search */
|
|
|
|
int state = int_disable_interrupts();
|
|
|
|
acquire_spinlock(&io_vectors[vector].vector_lock);
|
2002-07-19 20:07:36 +04:00
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
2002-07-19 22:33:41 +04:00
|
|
|
io = io_vectors[vector].handler_list.next;
|
|
|
|
for (io = io_vectors[vector].handler_list.next;
|
|
|
|
io != &io_vectors[vector].handler_list;
|
|
|
|
io = io->next) {
|
2002-07-19 20:07:36 +04:00
|
|
|
/* we have to match both function and data */
|
|
|
|
if (io->func == handler && io->data == data)
|
2002-07-18 19:03:13 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
if (io) {
|
2002-07-19 20:07:36 +04:00
|
|
|
remque(io);
|
2002-07-19 22:33:41 +04:00
|
|
|
kfree(io);
|
|
|
|
}
|
2002-07-19 20:07:36 +04:00
|
|
|
|
2002-07-18 19:03:13 +04:00
|
|
|
// release our lock as we're done with the vector
|
2002-07-19 22:33:41 +04:00
|
|
|
release_spinlock(&io_vectors[vector].vector_lock);
|
2002-07-18 19:03:13 +04:00
|
|
|
int_restore_interrupts(state);
|
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
return (io != NULL) ? 0 : EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
2002-07-18 19:03:13 +04:00
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
if (rv < 0)
|
|
|
|
return rv;
|
2002-07-18 19:03:13 +04:00
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
/* 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;
|
2002-07-18 19:03:13 +04:00
|
|
|
}
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-19 20:07:36 +04:00
|
|
|
/* int_io_interrupt_handler
|
|
|
|
* actually process an interrupt via the handlers registered for that
|
|
|
|
* vector (irq)
|
|
|
|
*/
|
|
|
|
int int_io_interrupt_handler(int vector)
|
2002-07-18 19:03:13 +04:00
|
|
|
{
|
2002-07-19 20:07:36 +04:00
|
|
|
int ret = B_UNHANDLED_INTERRUPT;
|
2002-07-18 19:03:13 +04:00
|
|
|
|
|
|
|
acquire_spinlock(&io_vectors[vector].vector_lock);
|
|
|
|
|
2002-07-19 20:07:36 +04:00
|
|
|
if (io_vectors[vector].handler_list.next == &io_vectors[vector].handler_list) {
|
2002-07-18 19:03:13 +04:00
|
|
|
dprintf("unhandled io interrupt %d\n", vector);
|
|
|
|
} else {
|
|
|
|
struct io_handler *io;
|
2002-07-19 20:07:36 +04:00
|
|
|
/* 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
|
2002-07-19 22:33:41 +04:00
|
|
|
*
|
|
|
|
* 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.
|
2002-07-19 20:07:36 +04:00
|
|
|
*/
|
|
|
|
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;
|
2002-07-18 19:03:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
release_spinlock(&io_vectors[vector].vector_lock);
|
|
|
|
|
|
|
|
return ret;
|
2002-07-10 19:48:19 +04:00
|
|
|
}
|
|
|
|
|